Index: psi/tags/0.10-os2-b1/.darcs_binaries
===================================================================
--- psi/tags/0.10-os2-b1/.darcs_binaries	(revision 38)
+++ psi/tags/0.10-os2-b1/.darcs_binaries	(revision 38)
@@ -0,0 +1,41 @@
+# Binary file regexps:
+\.png$
+\.PNG$
+\.gz$
+\.GZ$
+\.pdf$
+\.PDF$
+\.jpg$
+\.JPG$
+\.gif$
+\.GIF$
+\.tar$
+\.TAR$
+\.bz2$
+\.BZ2$
+\.z$
+\.Z$
+\.zip$
+\.ZIP$
+\.jar$
+\.JAR$
+\.so$
+\.SO$
+\.a$
+\.A$
+\.tgz$
+\.TGZ$
+\.jpeg$
+\.JPEG$
+\.mpg$
+\.MPG$
+\.mpeg$
+\.MPEG$
+\.iso$
+\.ISO$
+\.exe$
+\.EXE$
+\.doc$
+\.DOC$
+\.jisp$
+\.JISP$
Index: psi/tags/0.10-os2-b1/.darcs_boring
===================================================================
--- psi/tags/0.10-os2-b1/.darcs_boring	(revision 38)
+++ psi/tags/0.10-os2-b1/.darcs_boring	(revision 38)
@@ -0,0 +1,64 @@
+# Boring file regexps:
+\.hi$
+#\.o$
+\.o\.cmd$
+\.ko$
+\.ko\.cmd$
+\.mod\.c$
+(^|/)\.tmp_versions($|/)
+#(^|/)CVS($|/)
+(^|/)RCS($|/)
+~$
+#(^|/)\.[^/]
+(^|/)_darcs($|/)
+\.bak$
+\.BAK$
+\.orig$
+(^|/)vssver\.scc$
+\.swp$
+(^|/)MT($|/)
+(^|/)\{arch\}($|/)
+(^|/).arch-ids($|/)
+(^|/),
+\.class$
+\.prof$
+(^|/)\.DS_Store$
+(^|/)BitKeeper($|/)
+(^|/)ChangeSet($|/)
+(^|/)\.svn($|/)
+\.py[co]$
+\#
+#\.cvsignore$
+(^|/)Thumbs\.db$
+
+# Binaries
+^src/psi$
+^src/psi.app$
+
+# Mac binaries
+^mac/disk
+^mac/Psi-.*
+^mac/(template|wc)\.dmg($|\.bz2$)
+
+# Windows
+^psiwin/.*$
+
+# libpsi
+^libpsi/psiwidgets/.*\.(o|moc|dylib)$
+^libpsi/psiwidgets/moc_.*\.cpp$
+
+# QMake makefiles
+^Makefile
+^src/Makefile
+^libpsi/psiwidgets/Makefile
+
+# QConf
+^conf.log
+^conf.pri
+^extra.pri
+^src/config.h
+
+# Qt dirs
+(^|/).ui($|/)
+(^|/).obj($|/)
+(^|/).moc($|/)
Index: psi/tags/0.10-os2-b1/COPYING
===================================================================
--- psi/tags/0.10-os2-b1/COPYING	(revision 38)
+++ psi/tags/0.10-os2-b1/COPYING	(revision 38)
@@ -0,0 +1,300 @@
+
+  As a special exception, the copyright holder(s) give permission to link
+  this program with the Qt Library (commercial or non-commercial edition),
+  and distribute the resulting executable, without including the source
+  code for the Qt library in the source distribution.
+
+  As a special exception, the copyright holder(s) give permission to link
+  this program with any other library, and distribute the resulting
+  executable, without including the source code for the library in the
+  source distribution, provided that the library interfaces with this
+  program only via the following plugin interfaces:
+
+    1. The Qt Plugin APIs, only as authored by Trolltech
+    2. The QCA Plugin API, only as authored by Justin Karneges
+
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, 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
Index: psi/tags/0.10-os2-b1/ChangeLog
===================================================================
--- psi/tags/0.10-os2-b1/ChangeLog	(revision 38)
+++ psi/tags/0.10-os2-b1/ChangeLog	(revision 38)
@@ -0,0 +1,678 @@
+
+2005-08-22, Kevin Smith <psi-changelog@kismith.co.uk>
+	Made the auto-expanding text input the default.
+
+2005-08-22, Remko Troncon <remko@psi-im.org>
+	Added a UI option for auto-resizing text input.
+	
+	Added a message dialog suggesting you should restart.
+
+	Fixed Mac OS X focus problem in GC dialog.
+	This is the same patch as the earlier one for the chat dialog.
+
+	Reverted Fran's checkboxes for every sound event. (due to bugs)
+
+	Agents now get other types of initial presence as well (FS#464).
+	Patch by IceRAM.
+
+	Fixed authorization ping-pong issue.
+	Upon automatically granting authorzation, Psi no longer requests auth
+	when the subscription is already To or Both.
+	
+2005-08-21, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed single-line edit. Now it should behave ok when pasting large chunks
+	of text at a time. 
+	
+2005-06-24, Remko Troncon <remko@psi-im.org>
+	Added an option to set roster & chat dialog opacity (Win & Mac)
+
+	Moved Misc. Appearance options to a separate tab
+
+2005-06-23, Remko Troncon <remko@psi-im.org>
+	Added new appearance tweak options:
+	The group headings and account headings can now be assigned separate 
+	foreground and background colours.
+	The group headings can now be set in 'slim' mode from the options dialog.
+	The outline of the headings can be toggled.
+
+2005-06-21, Remko Troncon <remko@psi-im.org>
+	Hide unavailable platform-specific options from the UI
+
+	Added 'Dock Bounce' option to the options dialog
+
+	Changed 'MacOS X' into 'Mac OS X' here and there
+
+2005-06-19, Remko Troncon <remko@psi-im.org>
+	Clarified the JID field of the 'Register Account' dialog
+
+2005-06-14, Remko Troncon <remko@psi-im.org>
+	Mac OS X Dock icon changes: 
+	Added an option to bounce the dock once/never/forever. 
+	Added an overlayed event count to the dock icon.
+
+2005-06-08, Remko Troncon <remko@psi-im.org>
+	Separate checkboxes for every sound event + global checkbox in options
+	dialog. Patch by Fran.
+
+2005-05-24, Kevin Smith <psi-changelog@kismith.co.uk>
+	Finally fixed that tabs bug that could cause segfaults and strange behaviours
+
+2005-05-14, Remko Troncon <remko@psi-im.org>
+	Updates to the About Dialog
+
+	Fixed a memory leak in the Growl notification popups
+
+	Change Gadu-Gadu version string to the registered one (patch by
+	Maciek Niedzielski)
+
+	Fixed Mac OS X focus problems in the Chat Dialog
+
+	Flattened account context menu
+
+	Moved 'Check Status' dialog into the 'User Info' (VCard) dialog
+
+	Merged all iconset tabs into the appearance tab
+	Applied Kris Vandenbroek's fix for the iconset tabs
+	Misc. iconset details dialog improvements
+
+	Added 'Chat' button to the event dialog
+
+	Cleaning up the Account Dialog
+
+2005-05-04, Kevin Smith <psi-changelog@kismith.co.uk>
+	Changed the default from off to on for the previous patch
+
+2005-05-01, Remko Troncon <remko@psi-im.org>
+	Added an option to be notified of receiving authorization. Turned off by
+	default (as in about every IM client out there). In combination with
+	'auto-authorize', this makes a temporary solution for clean registration
+	with transports.
+
+2005-04-16, Remko Troncon <remko@psi-im.org>
+	Support clicking on Growl notifications. Fix some possible memory leaks
+	in GrowlNotifier.
+
+2005-04-14, Remko Troncon <remko@psi-im.org>
+	Translated all the Growl strings.
+	
+2005-04-14, Remko Troncon <remko@psi-im.org>
+	Modified the VCard dialog to enable displaying and setting of the photo
+	field (patch by Fran Rodríguez)
+
+2005-04-12, Kevin Smith <psi-changelog@kismith.co.uk>
+	Commiting Remko's growl patch. Strings aren't properly translated, he'll 
+	have to fix this sometime
+
+2005-04-07, Kevin Smith <psi-changelog@kismith.co.uk>
+	Committed a Linux From Scratch detection patch by Christopher Beppler
+
+2005-04-07, Kevin Smith <psi-changelog@kismith.co.uk>
+	Committed Remko's avatars patch to enable people to work on a central 
+	codebase.
+
+2005-03-29, Kevin Smith <psi-changelog@kismith.co.uk>
+	Machekku's selection Auto-copy patch.
+
+2005-03-22, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Updated roster auto-resize code. Now it should work for toggling self
+	contact too.
+	
+2005-03-12, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed bug: when loading eventqueue. If account name for event is not 
+	specified or incorrect, it's unable to display that message.
+	
+2005-03-09, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now, appending lines to the history files would not require index.
+	Thus, major speedup.
+	
+2005-03-05, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Ctrl-I shortcut will now trigger VCard dialog in MainWin, ChatDlg and
+	EventDlg.
+
+	Single-line edit should now work fine in tabbed chats too.
+	
+2005-02-28, Kevin Smith <psichangelog@kismith.co.uk>
+	Added icons and close buttons to each tab, getting rid of the unsightly one
+	in the corner.
+	
+2005-02-28, Kevin Smith <psichangelog@kismith.co.uk>
+	Added the option to have tabs at the bottom of windows instead, and
+	added drag and drop between tab windows.
+
+2005-02-28, Kevin Smith <psichangelog@kismith.co.uk>
+	Patch by PSZ to make Psi remember the last-used paths for file dialogs
+	on exit.
+
+2005-02-24, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now Shift+Space and Ctrl+Shift+Space global shortcuts should work on 
+	Linux too. And the GlobalAccel feature should now be used by everyone ;)
+	
+2005-02-22, Kevin Smith <psichangelog@kismith.co.uk>
+	Remko's patch for the mac menu.
+
+2005-02-20, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Psi won't stick to hidden windows anymore.
+
+	Now, config.xml should not be saved when processing new events.
+
+	Slightly better auto-roster-resizing on account enabling/disabling.
+	
+2005-02-13, Michail Pishchagin <mblsha@users.sourceforge.net>	
+	New AdvancedWidget template class, which implements sticking to screen
+	edges, and to other top-level widgets of the same app. Also implements
+	taskbar flashing. Chat, GroupChat, MainWin, Message dialogs are now
+	AdvancedWidget-enabled.
+	
+2005-02-13, Kevin Smith <psi-changelog@kismith.co.uk>
+	Newer version of the tabs code. Hopefully now it'll compile on non-linux without problems
+
+2005-02-13, Kevin Smith <psi-changelog@kismith.co.uk>
+	Remko's menubar patch
+
+2005-01-23, Kevin Smith <psi-changelog@kismith.co.uk>
+	Remko's memory leak in eventdb patch
+
+2005-02-06, Michail Pishchagin <mblsha@users.sourceforge.net>	
+	New autoRosterSize option, which automatically resizes your roster window
+	to fit as many contacts as possible. Please test it well ;)
+
+	Automatically resolve names of newly added contacts, if they don't have
+	assigned name yet. Could be useful when ICQ Transport is configured to
+	automatically add ICQ users to your roster. Also it could save you some 
+	time, clicking the Resolve button if you want to keep default nickname.
+	
+2005-01-30, Michail Pishchagin <mblsha@users.sourceforge.net>	
+	Groupchat with single-line edit enabled would no longer be unneedlessly
+	resized during nick completion.
+	
+2005-01-29, Michail Pishchagin <mblsha@users.sourceforge.net>	
+	Small chats now have effect on groupchats too. As well as single-line edit.
+
+	Mac: In single-line enabled chats and groupchats, the lineedits would be
+	paned by empty space from left and right and also would be centered. 
+
+2005-01-27, Michail Pishchagin <mblsha@users.sourceforge.net>	
+	Now scrollbar in single-line chat edit should no longer flicker.
+	
+2005-01-26, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Reduced flickering in single-line edit. Removed horizontal scrollbar.
+	Does not resize the dialog when it takes more than 90% of desktop's
+	area.
+		
+2005-01-24, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Experimental single-line edit in chat dialog. Seems to be useable.
+	
+2005-01-17, Kevin Smith <psi-changelog@kismith.co.uk>
+	Basic tab support. DO NOT USE THIS YET.
+
+2005-01-09, Kevin Smith <psi-changelog@kismith.co.uk>
+	Added the SystemWatch stuff for power saving notification on windows and osx
+
+2005-01-09, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now IconButton class would try to specify icon using QIconSet whenever
+	possible, what should improve the looks of disabled buttons.
+
+	Now Process Next Event would process events from a non-DND account prior 
+	to the DND one.
+
+	On MacOS, Psi dock icon would no longer bounce on messages from DND accounts.
+
+	Global Shortcuts patch by GreyCat. At the moment, if you want it, you
+	should configure the keys by editing the config.xml file.
+
+---- Psi 0.9.3 Release ----
+
+2005-01-08, Kevin Smith <psi-changelog@kismith.co.uk>
+	Minor compiler error fix, and Release 0.9.3
+
+2004-12-28, Kevin Smith <psi-changelog@kismith.co.uk>
+	Two patches, one various x:data fixes, and the other a fix for the accounts 
+	combobox, which now only includes available accounts. 
+	Thanks to Remko for both.
+
+2004-11-20, Kevin Smith <psi-changelog@kismith.co.uk>
+	Minor change of defaults (particularly colour scheme on OSX) and removal of 
+	ugly bar when there's no scrollbars on OSX (Thanks Remko).
+
+2004-11-19, Kevin Smith <psi-changelog@kismith.co.uk>
+	GUI option for smallChats (thanks Pintail). Various string consistencies and 
+	clarifications (thanks Remko). New Mac build scripts (thanks Remko)
+
+2004-11-19, Kevin Smith <psi-changelog@kismith.co.uk>
+	Previously enabling "Ignore events from non-roster contacts" ignored private
+	groupchat messages, now it doesn't (although there's an xml option to revert)
+
+2004-10-31, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Corrected the name of AlertIcon class (it messed up the caching of
+	MTray icons).
+
+	QPixmapTray will be cleared on iconset change.
+
+	IceRAM's patch for beauty tray icons on Linux.
+	
+2004-10-29, Kevin Smith <psi-changelog@kismith.co.uk>
+	Committed Misha's fix to the MTray segfaults.
+	Fixed the Segfault when a contact went offline during regrouping.
+
+2004-10-28, Kevin Smith <psi-changelog@kismith.co.uk>
+	Windows 2003 detection code added. (MichalJ)
+	QT Translations (Skazi)
+
+2004-10-20, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Message events patch by Remko Troncon.
+
+	No toolbars on Mac by default by Remko Troncon.
+	
+2004-10-11, Michail Pishchagin <mblsha@users.sourceforge.net>
+	http://ftp.xiaoka.com/friends/skazi/psi/patches/
+	psi-tooltip_version_noslash-mod.diff
+	
+2004-10-03, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Another set of patches by Remko:
+	
+	* Ability to disable online accounts, in which case a dialog will pop
+	  up.
+	* Ability to enter empty status messages.
+	* Removed badly-looking icons on Mac.
+	* Some changes in menu layout on Mac.
+	* Modifications to system iconset, so there will be different icons
+	  for Roster and Advanced tabs in options.
+	
+2004-09-24, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Corrected 'Use Sounds' setting loading. Thanks to Marcel Naziri for
+	writing a fix!
+	
+2004-09-06, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Dynamic 'avatars', 'vcard' and 'history' directory recreation, and 
+	removal of unused 'info' dir.
+	
+2004-08-18, Michail Pishchagin <mblsha@users.sourceforge.net>
+	TrayIcon correction by William Waghorn, so it should work correctly
+	on Gnome from now on. But there'll be dependency on PsiApplication :-/
+
+	Disabled the unfinished avatar support in preparations for 0.9.3
+	release. Patch by Remko.
+
+2004-08-16, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Small fix for default profile by Remko.
+
+	Created general-like menu on MacOS by Remko.
+
+2004-08-12, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now tooltip of account will display number of online contacts and
+	total number of contacts in roster by Remko.
+
+	Fixes to the Mac menu by Remko.
+
+2004-08-09, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now IconAction should correctly remove unnecessary cached QPopupMenu 
+	pointers, and shouldn't crash.
+	
+2004-08-08, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Major speedup when parsing disco#items results in Service Discovery
+	dialog.
+	
+2004-08-05, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now MainWin uses new action customization framework. Test it, break it,
+	fix it :-)
+	
+2004-08-03, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now ShowSelf action will be shown in the mac menu too
+	(thanks to Remko, as usual)
+
+2004-08-02, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Ability to initiate file transfers by dropping files in the ChatDlg 
+	by Remko:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/filetrans-chatdlg_psi.diff
+
+	Fixed joining groupchat rooms by Remko:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/accountscombobox-fix_psi.diff
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/eventdlg-onlineonly_psi.diff
+
+	Selfcontact action + account tip by Remko:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/selfcontact+accounttip_psi.diff
+
+2004-07-25, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Changed the toolbars configuration format in config.xml. Do not use
+	older Psi's after upgrading to this one, or your toolbar configuation
+	will be lost!
+	
+2004-07-18, Michail Pishchagin <mblsha@users.sourceforge.net>
+	x:data support for iq:register dialog.
+	x:data support for iq:search dialog.
+
+	SkaZi's offline status message patch.
+
+	Default profile Remko's patch:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/defaultprofile_psi.diff
+
+	Select only online accounts in Join Groupchat dialog by Remko:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/groupchat-onlineonly_psi.diff
+	
+2004-07-14, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Avatar patch by Remko:
+	* Added new icons & updated others
+	* When a new VCard is retrieved, the corresponding avatar is updated
+	  automatically
+	* Added run-time options for avatars (enable switch + dimension)
+	* Added 'Clear  avatar' context menu. Grouped avatar-related context menu
+	  in 1 popup.
+	* When there is no avatar, show no avatar (instead of showing a default one)
+	* When a contact is removed, remove its associated avatar too.
+	* Comments are now Psi-style
+	
+2004-07-11, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Support for getting VCard image as avatar by Remko.
+	
+2004-07-10, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Improved the avatar in the tooltip appearance somewhat.
+	Thanks to IceRAM.
+
+	Now CVS builds will be timestamped.
+
+2004-07-09, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed crash in groupchat's rosters' tooltips. Thanks, Remko!
+	
+2004-07-08, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now status buttons should have effect on connecting accounts too.
+	
+2004-07-07, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now ChatDlg uses toolbar to display it's toolbuttons. Later it should
+	become customizeable!!
+	
+2004-07-05, Kevin Smith <psi-changelog@kismith.co.uk>
+	Added Passive Popup for file transfers:
+	ftp://ftp.patryk.one.pl/pub/windows/siec/psi/selfmade/patches/psi-popup_file.diff
+
+2004-07-05, Kevin Smith <psi-changelog@kismith.co.uk>
+	Added Skazi, Remko, Hal and IceRam to the thanks page of the about dialog. 
+	Thanks Guys.
+
+	Added support for static (locally defined) avatars. Haven't added a toggle
+	yet, I'm waiting to see if anyone actually *doesn't* want it.
+
+2004-07-01, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Show 'Insert Emoticon' toolbutton in chat dialog when it's not in compact
+	mode:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/chatdlg-emot_psi.diff
+
+	Smart change status button:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/statuschange_psi.diff
+
+	Now dragging multiple files at once will result in many FT dialogs.
+	Thanks to Remko Troncon ;-)
+
+	Now Service Disovery dialog uses PsiToolBar.
+	
+2004-06-30, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Added 'Get vCard' and 'Resolve Nick' buttons to the Add new user dialog.	
+	Removed 'XML Console' from 'Psi' menu.
+
+	Fixed translations of compact chat mode:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/chatdlg-menu_psi.diff
+
+	Drag-n-Drop support for File Transfer:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/dragdropfile_psi.diff
+
+	Mac menu:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/macmenu_psi.diff
+
+	Mac roster grip:
+	http://www.cs.kuleuven.ac.be/~remko/psi/patches/macstatusbottom_psi.diff
+	
+2004-06-14, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now Psi wouldn't load successfully with the absent default icons.
+	
+---- Psi 0.9.2 Release ----
+
+2004-06-05, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Added 'Psi Handbook' entry to the Help menu.
+	
+2004-05-29, Michail Pishchagin <mblsha@users.sourceforge.net>
+        Fixed Tip of The Day's What's This string.
+
+2004-05-26, Michail Pishchagin <mblsha@users.sourceforge.net>
+        Fixed tab order in Sound tab in Options dialog.
+
+2004-05-04, Justin Karneges <justin@affinix.com>
+        Added file transfer manager.
+
+2004-05-04, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed a bug, when you had some popups on the screen, and quitted psi,
+	it always segfaulted.
+	
+2004-05-01, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Added "Incoming File Transfer" and "File Transfer Complete" sounds to the
+	Sound tab in Options dialog and common.h. 
+	Now it's up to Justin to actually use them :)
+
+	Now sounds from iconsets shouldn't be played when sounds are disabled.
+
+	Hopefully fixed the 'next' button's icon on non-X11 platforms.
+
+	Fixed line endings in Export History dialog.
+	
+2004-04-30, Michail Pishchagin <mblsha@users.sourceforge.net>
+	ed2k:// patch by SkaZi.
+
+	Bug with IRC gateway icon and Service Discovery dialog.
+
+	Added selection of Popup Font.
+
+	New looks of Appearance tab in the Options dialog.
+	
+2004-04-18, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Added conference/irc type to the list of known disco identities.
+	Now the disco dialog should display IRC gate icon just ok.
+	
+2004-04-05, Michail Pishchagin <mblsha@users.sourceforge.net>
+	The bug-reporting patch by SkaZi.
+	(http://psi.affinix.com/flyspray/index.php?do=details&id=238)
+	
+2004-04-04, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Update the (C) year in About dialog to 2004.
+	
+2004-04-01, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Added the Transfer Manager action to the main window.
+	
+2004-03-30, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now chat and event dialogs do not linkify the status tooltips of the 
+	contact.
+
+	Now service discovery dialog should be able to search JUD on jabber.org
+	(since it started to support Service Discovery) when protocol is set 
+	to A (Auto) or D (Disco).
+
+	Now Psi shouldn't crash on exit with the ~PsiTextView as the cause.
+	
+2004-03-25, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed the slotScroll() slot in GroupChat dialog, and scrolling to bottom
+	should actually work now.
+	
+2004-03-18, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now type ahead in contact view will reset after 3 seconds of inactivity.
+	
+2004-03-16, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Bug: Groupchat dialog was not always scrolled to bottom. Fixed now.
+	
+2004-03-15, Justin Karneges <justin@affinix.com>
+        New history features: erase all and export to friendly text.
+
+2004-03-15, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Delete old settings in HKLM on Windows systems.
+	
+2004-03-12, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Updated default icons (they're now made smaller in size, all thanks to
+	Piotr Iwo Skrodzewicz).
+
+	Added --nocrash command-line option.
+	
+2004-03-10, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed silly bug when loading sort style for contacts in roster.
+
+	Added ArchLinux to the list of detectable Linux distrox (thanks to IceRAM).
+	
+2004-03-08, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now 'crash' support will not be built-in by default.
+
+	Fixed bug in disco dialog, when doing Refresh Item on parent item, its
+	child loses all features and identities.
+	
+2004-03-03, Justin Karneges <justin@affinix.com>
+        ContactView shortcuts were broken, fixed.
+	'configure' updated to not bundle qxml unless necessary.
+
+2004-03-03, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Small code cleanup by SkaZi
+
+2004-03-02, Justin Karneges <justin@affinix.com>
+	Data transfer settings
+	
+2004-02-29, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Added vertical splitter to the groupchat dialog (thanks to IceRAM for the 
+	hint)
+
+	Now Psi should compile with Qt 3.1.x on Windows again
+	
+2004-02-28, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now only one trackbar exists at a time in groupchat dialog (patch by
+	Bastian Hoyer)
+	
+2004-02-27, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Reverted the hide/show roster code to the old one, which was used in 0.8.7
+	
+2004-02-26, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed replying to replied plain messages. In 0.9.1 subject became empty
+	in that case.
+	
+2004-02-25, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Real fix for now: Now in nick-completion the space is not added when 
+	completion is not at the start of the string (thanks to IceRAM for the 
+	hint)
+	
+2004-02-23, Kevin Smith <psi-changelog@kismith.co.uk>
+	Added an xml option to have the xml console visible and enabled at login.
+	Some people wanted this for debugging.
+
+2004-02-23, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed the emoticonify function: now it will correctly handle this case:
+	";-)
+	:-P(blah-blah-blah)"
+
+	Now MainWin's position will not be saved, if the X or Y coordinate is 
+	fever than -30000 (I think that it should work, but it needs testing).
+
+	Now IconSelect can take up to 3/4 of the screen's 
+	MIN(screen.width, screen.height).
+	
+2004-02-21, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Now in nick-completion the space is not added when copletion is not at
+	the start of the string (thanks to IceRAM for the hint)
+
+	Now Psi should be able to load attachments from messages that it saved
+	to the event queue on the disk (thanks to SkaZi for the patch)
+
+2004-02-16, Kevin Smith <psi-changelog@kismith.co.uk>
+	Added a trackbar to the groupchat dialog
+
+2004-02-16, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fix for nick completion bug in groupchat dialog, which showed only
+	when there were people with identical-starting nicks, but in other
+	cases (ex: IceRAM vs. infiniti)
+
+	"Crash" patch by Juan F. Codagnone.
+	
+2004-02-13, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fix for type ahead by Juan F. Codagnone.
+	
+2004-02-11, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fix for crash on startup if iconset could not be loaded. (thx to SkaZi)
+	
+	Fix for multiline descriptions in url attachments by SkaZi.
+
+	Fix for text color in groupchat dialog by SkaZi.
+	
+2004-01-27, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Event queue sorting patch by SkaZi.
+	
+2004-01-26, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Received messages are now queued to disk 
+	(to PsiDataDir/profiles/profilename/events-accountname.xml)
+	
+2004-01-25, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Hidden group patch by SkaZi.
+	
+2004-01-25, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Updated EventQueue saving/loading code (but it's still not perfect, 
+	though).
+	
+2004-01-23, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed a bug in PsiIconset::Private::loadIconset(): If original icon have
+	Anim, and new icon doesn't have Anim, old Anim was not deleted, which
+	caused incorrect animations to appear (example: psi/chat icon in 
+	LightBulb iconset).
+	
+2004-01-21, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Small fix to the resource search path under Windows.
+	
+2004-01-21, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Patch by Juan F. Codagnone:
+	This patch was called originaly "miranda like type-ahead patch"  after the 
+	Mozilla's and Miranda IM's feature. It lets you go to any contact in your 
+	roster, typing his name. (at the moment, in psi, when you want to search for 
+	some one, you need to type the first character of the nickname you are 
+	searching for).
+	
+2004-01-19, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Moved Configure Toolbars out of main Psi menu, to the toolbars' own
+	context menus.
+	
+2004-01-17, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Tip of the Day is back! Please contribute your useful tips and tricks,
+	so they will be there in 0.9.2 ;)
+		
+2004-01-17, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Fixed grammar, spelling, and other errors/typos in translatable	
+	strings (in psi/src/options).
+
+2004-01-16, Michail Pishchagin <mblsha@users.sourceforge.net>
+	now Sound Player option is separated to tokens by the ' ' (space) char.
+	it is possible to use complex sound player commands now.
+	Patch by "wwp <subscript@free.fr>"
+	
+2004-01-11, Kevin Smith <psi-changelog@kismith.co.uk>
+	XML option to turn off the numbers in group headers.
+
+2004-01-10, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Added nick-completion in Groupchat dialog. Press Tab key to complete and
+	to iterate through the nicks
+	
+2004-01-06, Kevin Smith <psi-changelog@kismith.co.uk>
+	Accounts can now be enabled and disabled in account settings (Skazi's
+	patch)
+
+2004-01-05, Kevin Smith <psi-changelog@kismith.co.uk>
+	Emoticonified text now also shows *text* as bold, /text/ as italic,
+	and _text_ as underlined.
+
+2004-01-05, Kevin Smith <psi-changelog@kismith.co.uk>
+	Support for multiple contact adds from the services dialog (Mike
+	Fawcett (deviant))
+
+2004-01-04, Kevin Smith <psi-changelog@kismith.co.uk>
+	Started this ChangeLog.
+	
+	Presets in status dialog are now sorted alphabetically. (Skazi's patch)	
+	Status Dialog has a tickbox to save the status message as a preset (Remko's Patch)	
+	Seperated headlines from messages for sound and popups.
+
+2003-01-03, Kevin Smith <psi-changelog@kismith.co.uk>
+	Small chat support, there's a global default in the config (no option yet) and
+	right click per-dialog
+	
+2004-01-02, Michail Pishchagin <mblsha@users.sourceforge.net>
+	Service registration dialog corrections by SkaZi:
+	  - 'register' is the default button
+	  - first line in form is focused
+	
+---- Psi 0.9.1 Release ----
Index: psi/tags/0.10-os2-b1/INSTALL
===================================================================
--- psi/tags/0.10-os2-b1/INSTALL	(revision 38)
+++ psi/tags/0.10-os2-b1/INSTALL	(revision 38)
@@ -0,0 +1,43 @@
+Installation
+------------
+
+-=[ Linux / Unix ]=-
+
+  $ ./configure
+  $ make
+  # make install
+
+  Once installed, launch the program by running "psi".
+
+  For more details about compiling, refer to the "Compiling" section below. 
+
+-=[ Windows ]=-
+
+  No installation required for Windows.  You can simply just
+  run the Psi executable.  You might want to move everything into
+  C:\Program Files\Psi
+
+-=[ eComStation / OS/2 ]=-
+
+  For automatic installation, just use the provided WarpIn archive.
+  For manual installation instructions, refer to the Readme-OS2.txt file.
+
+-=[ MacOS X ]=-
+
+  Just run the Psi "executable package" thing.  No installation required.
+
+
+Compiling
+---------
+
+Requirements:
+  - Qt 3.1.0 *mulithreaded*, nothing less!
+  - QCA 1.0 or compatible
+  - zlib 1.1.4 or compatible
+
+You can obtain Qt at http://www.trolltech.com/
+You can obtain QCA at http://delta.affinix.com/qca/
+
+Note: For information about building Psi on Mac or Windows, see
+build-mac.txt or build-win.txt
+
Index: psi/tags/0.10-os2-b1/README
===================================================================
--- psi/tags/0.10-os2-b1/README	(revision 38)
+++ psi/tags/0.10-os2-b1/README	(revision 38)
@@ -0,0 +1,217 @@
+Psi 0.10
+---------
+Date: January 11th, 2006
+
+Project Lead/Maintainer (2004-current):
+  Kevin Smith
+  kismith@psi-im.org
+  http://www.kismith.co.uk/
+
+Original Author (2001-2004):
+  Justin Karneges
+  justin@affinix.com
+  http://psi-im.org/
+
+
+  For installation or compiling instructions, see the INSTALL file.
+  (On Windows, see install.txt)
+
+  This program is licensed under the GNU General Public License.  See
+  the COPYING file for more information.
+
+
+Notes
+-----
+  - You need the QCA-TLS plugin to utilize SSL connectivity.
+    See http://delta.affinix.com/qca/
+
+  - Upgrading to 0.10 --
+    * Users upgrading will not benefit from the new colour scheme. If they
+      want the new colours, they should quit Psi and edit their config.xml file
+      by hand. For details, please see the User Guide in the Help menu.
+    * Due to the increased bandwidth used by auto-version checks, they
+      have been disabled since 0.9 by default.  There is an option to
+      enable it.
+    * The self-contact is now only shown if there is more than one
+      resource logged in at once.  If you'd rather it always show, like
+      in 0.8.7, then there is a way to enable it.
+    * An XML format is now used for storing SSL certificates.  In the
+      future, Psi will support importing of PEM files.  For now, you
+      will have to create the XML by hand.  See certs/README.
+    * You must use the QCA-TLS plugin to utilize SSL.  The older 'QSSL' plugin
+      will not work.
+
+  - GnuPG tips --
+    * Always use the latest version of GnuPG.
+    * On Windows, use a native build instead of one based on cygwin, and make
+      sure it is properly installed in the registry.
+    * Ensure 'gpg' actually works, by testing it from the command-line.
+    * You must have a public and secret keyring.  Psi cannot operate
+      without them, and they must be there when you start it.
+    * If you experience fatal GnuPG incompatibilities, you can pass
+      the --no-gpg option to Psi and gpg will not be used.
+    * If you have gpg-agent set up, but you don't want Psi to use it, then
+      you can pass --no-gpg-agent to force the use of Psi's built-in prompt.
+
+
+Changes
+-------
+  New in 0.10
+  - Psi will now sign out before Windows or Mac OS X machines go to 'sleep'
+  - The default text input line in the chat dialogs starts at one line tall, and grows as necessary.
+  - The Roster can now be automatically resized to fit all contacts.
+  - The Roster now has a menu bar available on Windows and Linux
+  - Chat windows can be collected together using tabs
+  - On Windows and Mac OS X, selected text may be automatically copied to the clipboard.
+  - Mac OS X builds now enjoy Growl(.info) support
+  - Photos are now available in the VCard dialog
+  - New default colour scheme
+  - New default iconsets
+  - Roster and chat dialog opacity is now configurable on Windows and Mac OS X
+  - The colour scheme is now slightly more configurable
+  - Assorted minor feature additions and bug fixes
+
+  New in 0.9.3
+  - Notification when a user is typing
+  - Advanced Data Forms capability (x:data)
+  - Mac version uses the menu bar and also has a new color scheme
+  - Slightly improved new user dialog
+  - Drag and drop File Transfer
+  - Ability to specify a status messages when going offline
+  - The tray icon under X11 should now be more beautiful
+  - Show self-contact is now a toolbar button instead of a normal option
+  - There is now an option for all chat windows to be compact by default
+  - Bugfix: file transfer sometimes wouldn't send the whole file
+  - Fixed many other small bugs
+
+  New in 0.9.2
+  - File transfer
+  - Event queue is saved to disk at all times for fault protection
+  - Enable/disable accounts
+  - Groupchat nickname tab-completion
+  - Groupchat trackbar to mark last read text when unfocused
+  - *bold*, /italic/, _underline_
+  - Fixed bug where the roster window could get lost on Windows
+  - 'Compact' mode for chat window
+  - Tip of the day
+  - Hidden roster group
+  - Ability to export history as readable text
+  - Event queue sorting by type
+  - Type-ahead search in the roster
+  - External programs can now have parameters
+  - Toolbar configuration is now accessed from the right-click menu
+  - Show dates in local format
+  - Use the gnome-open utility for opening URLs with GNOME2
+  - Fixed "broken pipe" crash when receiving GnuPG errors
+
+  New in 0.9.1
+  - Service Discovery browser (JEP-0030)
+  - Popup notifications
+  - Redesigned Iconset & Emoticons system, based on JEP-0038
+  - Now possible to use service/transport icons for individual contacts
+  - Ability to use custom roster icons for contacts via RegExps
+  - Emoticon popup menu in chat window, for quick access
+  - Reorganized Options window
+  - Toolbar Editor
+  - GnuPG 1.2.3 compatibility
+  - New XMPP/Jabber engine (partially complete, XMPP 1.0 support deferred)
+  - SOCKS5 proxy support
+  - HTTP polling proxy support (JEP-0025)
+  - Roster groups open/closed state saved
+  - 'Free for Chat' status
+  - Character counter
+  - Selectable colors for roster 'blink' animation
+  - Copy Location in URL attachment context menu
+  - X11: WindowMaker docklet
+  - Windows: fixed idle detection on Windows 9x
+  - Mac: ability to hide/restore main window
+  - Option: ignore messages from addresses not in your roster
+  - Option: enter key sends chat messages
+  - Option: raise chat window on receiving new messages
+  - Option: auto-authorize contacts
+  - Option: 'small' group headings
+  - Option: treat incoming chats as messages unless chatting
+  - Option: show/hide subject line
+  - Option: enable/disable vCard check during login
+  - Account dialogs ensure the JID is a valid user-JID before accepting
+  - Bugfix: crash after using the Jabber browser
+  - Bugfix: crash when resolving DNS
+  - Bugfix: busy indicator sometimes stopped when it should still be running
+  - Fixed many other bugs
+
+
+Description
+-----------
+
+  Psi is a capable Jabber client aimed at experienced users.  Its design
+  goals are simplicity and stability.  Psi is highly portable and runs on
+  Windows, Unix/X11, and MacOS X.
+
+  The interface is very ICQ-like.  Alerts appear in the contact list when
+  events are received, and things like subscriptions requests appear as
+  "system messages" (ICQ users know these well).  Even "Chats" are
+  queued in the contact list.  Also, chats are "remembered" by default,
+  so that you don't have to keep a bunch of windows open for each person.
+  Just close the chat window.  If you open it again it will all be there.
+
+  Psi is minimal but powerful.  There are keybindings for just about
+  everything, Unicode is supported throughout, and contacts are cached
+  offline.  Security is also a major consideration, and Psi provides it for
+  both client-to-server (SSL) and client-to-client (GnuPG).
+
+  Plans for v1.0:
+   - Multi-User Chat (JEP-0045)
+   - Plugin system
+   - Other misc stuff
+   - Bugfixes
+
+
+How you can help
+----------------
+
+  Comments
+  --------
+  I like comments, even if they are a simple thanks.
+
+  Bug reports
+  -----------
+  If you want to help me out, email me a bug report.  Be as descriptive
+  as possible and tell me what exactly you were doing at the time
+  something went wrong.  If possible, send me a stack backtrace.
+
+  Wishes
+  ------
+  I can't possibly know what everyone wants, so I appreciate all feature
+  requests.
+
+  Porting
+  -------
+  Psi should be very portable.  Please e-mail me first if you want to
+  port Psi to another platform.
+
+  Graphics
+  --------
+  I am no artist.  If you think you can do a better job with any of the
+  Psi graphics, then go right ahead!
+
+  Sound
+  -----
+  Same goes for sound.  Start mixing!
+
+  Programming
+  -----------
+  Patches are welcome.  External tools would be useful as well, like Licq
+  conversion utilities (iconsets, message history, etc).  Get to work!
+
+  Packaging
+  ---------
+  Want to make an RPM or something?
+
+  Financial
+  ---------
+  You can send a paypal gift if you'd like, to justin@affinix.com
+
+
+Have fun,
+-Justin
+
Index: psi/tags/0.10-os2-b1/Readme-OS2.txt
===================================================================
--- psi/tags/0.10-os2-b1/Readme-OS2.txt	(revision 38)
+++ psi/tags/0.10-os2-b1/Readme-OS2.txt	(revision 38)
@@ -0,0 +1,166 @@
+Psi 0.10-os2-b1 (Beta 1)
+------------------------
+Date: August 18th, 2006
+
+
+OS/2 Port Creator/Maintainer:
+    Dmitry A. Kuminov
+    dmik-qt(at)hugaida.com
+    http://svn.netlabs.org/qt3/ticket/25
+
+Using Qt Toolkit Version 3 for OS/2:
+    Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
+    Copyright (C) 2004 Norman ASA.  Initial OS/2 Port.
+    Copyright (C) 2005-2006 netlabs.org.  Further OS/2 Development.
+    Ported to OS/2 by Dmitry A. Kuminov
+    http://svn.netlabs.org/qt3
+
+
+This readme contains information specific to the OS/2 version of Psi.
+For general information, please refer to Readme.txt, Install.txt and
+to the online documentation you can find at http://psi-im.org/.
+
+For the list of changes, see the bottom of this file. 
+
+
+Manual Installation
+-------------------
+
+1.  Make sure Innotek (Bird) LIBC 0.6.1 library (libc061.dll) is
+    installed on your system. Download the library from
+    ftp://ftp.netlabs.org/pub/gcc/libc-0.6.1-csd1.zip and unzip
+    the archive to the directory you plan to install Psi to or to
+    one of the directories listed in your LIBPATH environment variable.
+
+2.  Unzip the Psi archive containing this file to the directory of your
+    choice and run the Psi executable (psi.exe). No additional
+    installation is required.
+
+
+Configuration data
+------------------
+
+Psi stores all configuration files in the subdirectory /PsiData of
+your home directory (pointed to by the HOME environment variable).
+
+The name of the last used Psi profile is stored in the registry
+under the key HKEY_CURRENT_USER\Software\Affinix\psi.
+
+
+Current Limitations and Known Bugs
+----------------------------------
+
+1.  Using the PM Default Browser/Mail choice for opening links doesn't
+    work yet. The Custom browser setting requires a command like
+    'cmd /c cd D:\Apps\Firefox & D:\Apps\Firefox\Firefox.exe'.
+    
+2.  SSL connection is completely untested and will most likely not work.
+
+3.  PGP encryption is completely untested and probably will not work.
+
+4.  The global hot keys are not available. 
+ 
+5.  Idle (i.e. Auto-Away) detection may not work properly.
+
+6.  The directory watcher service is not available (though I don't yet know
+    what is it good for in Psi). 
+
+7.  There is no "tray icon" (xCenter widget) for Psi. It will be available
+    later.
+    
+8.  Power management events such as "sleep" or "wakeup" are not detected by Psi.
+
+9.  Drag and Drop support in Qt/OS2 is not yet finished, so there are few
+    known issues (for example, text in entry fields may be corrupted when the
+    drag object is moved over them).
+
+10. The "Roaster window always on top" option is temporarily removed from the
+    options dialog because this functionality is not yet available in Qt/OS2.
+
+
+Bug Reports
+-----------
+
+To submit a bug report, please visit http://svn.netlabs.org/qt3/ticket/25
+and add a comment describing the problem.
+
+Note that in order to add a comment, you'll need to login to the Trac system
+using the special name and the password presented on the start page
+(http://svn.netlabs.org/qt3/wiki).  
+
+
+Contributors
+------------
+
+Thanks to the following people who contributed to Psi/2:
+
+Ivan Smirnoff
+    www.sampo.ru/~bigvic 
+	OS/2 icon
+
+Juergen Ulbts
+    WarpIn scripts
+
+
+Changes
+-------
+
+Note: fixes marked as [Qt ...] are applied to the Qt library for OS/2,
+not to Psi itself.
+
+*   Version 0.10-os2-b1 (2006-08-18)
+    --------------------------------
+
+    - Built using Qt 3.3.1-RC7 SVN build r118.
+
+    - New: Chat tabs can be switched using Ctrl+Tab and Ctrl+Shift+Tab
+      combinations (together with cross-platform Ctrl+PgUp/Ctrl+PgDn).
+
+    - New: Pressing the Esc key in the Roster window minimizes it
+      if the Doclet widget (tray icon) is disabled (which is always true
+      on OS/2 since there is no xCenter/SysTray integration yet).
+
+    - Fixed: Removed the Options->Sound->Player entry field (not used in
+      OS/2).
+
+    - Fixed: Unpainted vertical strip in QToolBar (seen to the left from the
+      Clear chat window button) [Qt bug].
+
+    - Fixed: Pressing AltGr+key could produce valid characters in some
+      keyboard layouts (i.e. the German one) which were ignored in the chat
+      window [Qt bug].
+
+    - Fixed: In the roster: pressing the Status button right after pressing
+      the Psi button didn't unpress the latter (and vice versa) [Qt bug].
+
+    - Fixed: Notification popups didn't take the desktop work area size into
+      account (appeared over the xCenter widget) [Qt enhancement].
+
+    - Fixed: Playing too many sounds simultaneously could screw up a
+      single-streamed audio driver and cause the application hang during
+      termination or even a crash [Qt improvement].
+
+    - Fixed: All network activity (including file transfer) was suspended
+      during moving or resizing Psi windows using the mouse [Qt improvement].
+
+    - Fixed: Selecting 'Rerequest authorization from' in the context menu
+      of ICQ and Transport contacts in the Roster would crash Psi.
+
+    - Fixed: Opening the chat window could sometimes crash Psi (SIGFPE)
+      [Qt bug].
+
+    - Fixed: When both "Open chats in tabbed mode" and "Raise chat window on
+      receiving new messages" were set, receiving a new message would bring
+      the chat window to front but would not change the active tab to the one
+      corresponding to the chat window.
+
+    - Fixed: Closing Psi trying to connect (resolve the server name) when
+      there was no Internet connection hid all Psi windows but didn't exit
+      the process until the request timed out (that could take quite a
+      long and didn't allow to start another Psi instance).
+
+
+*   Version 0.10-os2-a1 (2006-07-25)
+    --------------------------------
+    
+    The first public alpha version.
Index: psi/tags/0.10-os2-b1/TODO
===================================================================
--- psi/tags/0.10-os2-b1/TODO	(revision 38)
+++ psi/tags/0.10-os2-b1/TODO	(revision 38)
@@ -0,0 +1,201 @@
+Required
+  File Transfer
+  XML Encryption
+  add status message presets from the statussetdlg
+  Ability to prompt for status message when choosing "Offline" (signoff status)
+  ability to flag accounts as enabled/disabled.  disabled means they are not usable and won't show in roster.
+  groupchat should log presence changes
+  gcuserview: proper sorting of contacts based on status / alphabetical (like ContactView)
+  message events (composing, delivery, etc)
+  misha: JEP-0038: emoticon sets
+  server info:
+    show server version in tooltip
+    ssl information dialog (show cert and maybe connection info)
+  separate message history / info for each account
+  eventQueue: toXml, fromXml (for saving unread messages)
+  local contact settings:
+    checkboxes for accepting various things: messages; files; URLs; chats \
+      [each has a checkbox option: only from my contact list, or... only from this GROUP on my contact list]
+    be alerted when the contact changes status or comes online (etc)
+    option: ignore headline messages (die MSN)
+    annotations (notes about the user)
+    log history or not
+  activity log window
+    high level logging of connection status, presence changes, messages
+    timestamps
+    "find" feature
+  Richtext messages
+  revamp history system: ability to restore entire chats, show in normal order, not reverse
+  "properties" dialog (could be last entry in cvlist context) for setting groups and subscription, etc. \
+    it could also show client time / version
+  file downloading
+    nice dialog
+    after file received, there should be the options: "Open" and "Open folder"
+    put each received file into a folder for the contact (optional)
+    if a specific directory is chosen, the dialog should have a history of the most recent download \
+      folders (10 maybe?)
+  Advanced Alerts (sound, popups, etc)
+  Ignore list
+  plugin system
+
+Important
+  clicking on a chatdlg when bringing focus should always set focus to the input widget (eat the click)
+  'find' feature for chatdlg
+  checkbox to keep eventdlg open (both send and recv).  for send, it should reset the content after sending.
+  statussetdlg should automatically click ok in 10 seconds (unless you press a key)
+  ability to flag which accounts are affected by the mass status change button (should be in account context menu)
+  cv: remember the open/closed state of groups
+  cv: group headings should have a better design than just a rectangle (gradient?)
+  add "Go to Website" and x:oob in presence
+  use arts for sound on unix (or custom, for things like "play")
+  ability to do various iq requests to arbitrary jids:  version, time, info
+  multi-monitor support: windows should pop up on the same display as the main window?
+  "last seen" time for contacts
+  emoticon menu button
+  merge all contacts into one column, rather than dividing by account
+  consolidated contacts
+  send contacts
+  save/load contacts (hint: use QCheckListItem for dialogs)
+    exporting
+      dialog for selecting contacts to export to file
+      write to file
+    importing
+      import a list from file
+      dialog for selecting contacts to import
+      tell the server about the new contacts
+      check for dups
+  offline animation (door slam)
+  chathistorydlg
+    a nice window to display chat history
+    maybe a menubar with some features like "save to file" ?
+    show/hide times
+    show/hide/rename other person (for privacy)
+  WM dock
+  floating contacts (controlled by cvlist?)
+  full message archive dialog (tree/explorer view for choosing contacts on left, display area on the right).
+  Update all (mass user info check)
+  Detecting network connection status.  option to auto-connect when network is available.
+  startup argument to select profile
+  ability to execute a shell command on event
+  option to display (in realtime) the cvlist as 'flat', ie without groups
+  voice chat
+  option to launch on startup (platform dependent, good luck)
+  different color choices for different group types
+  transport-specific roster icons
+  deal with presence errors when subscribing
+  command line args?  psi.exe --psi-data dir
+  have separate status presets depending on the status type
+
+Extra
+  there should be a way to copy a url to clipboard from the eventdlg attachment
+  ability to flag a contact or group to always be shown even if they would not be (due to visibility toggles)
+  how about adding "Add Contact", "New Blank Message", "Manage services" and "Join Groupchat" to the cv \
+    rightclick menu for each of the servers?
+  allow eventdlg and chatdlg inputs to drag / drop URLs (in both directions)
+  chatdlg separator:  "snap" to font height?
+  transport iconsets (misha?)
+  translation packs for the Qt library itself??
+  check to see if app is already running when launched.  if it is, then:
+    ask if they really want to open it again
+    if they say yes, then disable auto-open for profiles
+  save presence changes to history?
+  win32: docking (all optional)
+    grapple to edge of screen, like ICQ for windows
+    auto hide: after a certain period of inactivity, "slide" the window off the screen
+    auto hide delay: time to wait before sliding off
+  right-click in chat/eventdlg should have options to paste your current URL or IP address
+  Avatars (Buddy Icons)
+  KDE-enhanced mode
+  "previous" button in the eventdlg?
+  Autoresponses?  (with general and customized texts)
+  auto close chat windows that are not in focus after so many minutes of inactivity
+  The Main Window's Icon should change when new messages arrive, just like the dock icon does.
+  Ability to filter messages based on words (maybe even shell command filters)
+  Ability to compose messages and send at a later specific date (or when you or the recipient go online)
+  option to autohide main roster window after a set period of time
+  supporting BOLD and ITALIC in jabber msges by using * and / like you do in newsgroups ..
+  Licq supports replacing some %<letter>s with information on the sender of the
+    message and other things. (There's a list below) IT supports these in
+    OnEvent, auto responses (check status), and utilities
+    For example: sound player "~/scripts/say", on message "%a has just sent you an I.C.Q. message"
+      %a - user alias
+      %e - email
+      %f - first name
+      %h - phone number
+      %i - user ip
+      %l - last name
+      %m - # pending messages
+      %n - full name
+      %o - last seen online
+      %O - online since
+      %p - user port
+      %s - full status
+      %S - abbreviated status
+      %u - uin
+      %w - webpage
+  Ability to specify an arbitrary time for chatdlgs to stay active (x minutes/hours/days)
+  "small mode" - put mainwin buttons (and toolbuttons) into the cvlist right-click
+  option to have sounds not play when XA (but still play when Away)
+  option to have mainwin/dock status follow a specific account rather than "best choice"
+  option to show status message in parenthesis to the right of the contact (like yahoo)
+  ability to "auto-reconnect" to a transport?
+  cv: let the user choose how the list is sorted
+  Spell check
+  pictures for users
+  group ideas:
+    option: sort or not
+    choose order of groups and contacts with drag and drop
+    save order on the server
+    support empty groups that get removed on signoff
+  Have a way of marking some people as 'important' contacts, so they will always trigger sound
+  psuedo-chat support like Mirabilis ICQ / Licq (ie, split window, but still used like normal messages)
+  friendlier infodlg.  get rid of those lame tabs
+  maybe an XML-based skins support (not only colors, sounds, images, key \
+    bindings, but also window positions, shapes, etc) \
+    Microsoft Windows Media Player supports XML-based skinning.  Basically \
+    this consists of a file which has a bunch of JavaScript glueing the \
+    entire skin together with code, and several images for hilite graphics, \
+    background graphics, alpha-map graphics, etc.  Very flexible. \
+    Justin's opinion:  if you want to fork Psi to do this, be my guest..
+  cvlist sorting options
+    sort by group, online/offline split
+    sort by group, online/offline together
+    sort by group and by status
+    sort by group and alphanumerically
+    sort by status without groups
+    sort alphanumerically without groups
+      sort by online/offline
+      straight alphanumerical sort
+  chatdlg: optionally tabbed like GAIM?
+  cvlist select multiple?
+  right-clicking on status button should bring up a list of accounts, each with submenus that would set \
+    the status of the chosen account.
+  option to have psi remember your password for the running session
+  Remember last status when psi quits (even if improperly shut down).  restore this status when psi is run again?
+  if you get a msg from someone in a closed group, it should not expand the whole group
+  "diskless" mode
+
+Bugs
+  connection timeouts are reported as "connection refused" (qt)
+  slowdown when receiving a chat message?  or a problem with QTextEdit debug messages?
+
+Miscellaneous
+  change PsiIconSet::type2anim() to be const (and return const)
+  properly sort cvlist accounts if one of them gets renamed
+  class Options: cpp'ify (reset, toXml, fromXml)
+  convert some of the preferences into their own classes (or lists), like fonts, colors, sounds, etc
+  field for author/license information in lang packs (display in About box)
+  'find' function in historydlg should show the found result in the center of 50 results, rather than the top
+  Check for libqssl without needing a restart?
+  finish jidlink and formalize the linktest process (<linktest xmlns="adsfasd"/>)
+  replies should only close a window that it is associated with (use QGuardedPtr?)
+  custombrowser, mailer, and sound player should be called as a shell command, not arg0
+  don't allow send/recv of blank messages (all content is whitespace or empty)
+  allow blank password entry
+  don't prompt for password until actually connected.  reprompt if wrong??
+  iconset: universal anim support (so eventdlg / mainwin / contactview can share) (misha?)
+  proper dialog button order depending on platform
+  when entering a text string into "Server to browse", any whitespace before or after is not trimmed.  lots of \
+    fields like this should have some sort of auto-trim.
+  when opening a new chat window of a contact with pending events, the presence is logged to the chat window \
+    before the messages are, which have an earlier timestamp (looks weird)
Index: psi/tags/0.10-os2-b1/admin/update_iconsets.sh
===================================================================
--- psi/tags/0.10-os2-b1/admin/update_iconsets.sh	(revision 38)
+++ psi/tags/0.10-os2-b1/admin/update_iconsets.sh	(revision 38)
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+SOURCE_DIR=../../iconsets
+TARGET_DIR=../iconsets
+
+ROSTER_DEFAULT='crystal-roster'
+ROSTER_EXTRAS='
+	crystal-aim crystal-icq crystal-msn crystal-service crystal-yahoo 
+	crystal-gadu crystal-sms crystal-roster
+	'
+
+SYSTEM_DEFAULT='crystal-system'
+SYSTEM_EXTRAS=''
+
+################################################################################
+
+if test ! -d $SOURCE_DIR; then
+	echo "Cannot find source dir $SOURCE_DIR"
+	exit
+fi
+
+if test ! -d $TARGET_DIR; then
+	echo "Cannot find target dir $TARGET_DIR"
+	exit
+fi
+
+################################################################################
+
+# Roster iconsets
+echo '*** Updating Roster iconsets ***'
+#cp -R $SOURCE_DIR/roster/$ROSTER_DEFAULT/* $TARGET_DIR/roster/default
+rm -f $TARGET_DIR/roster/default/Makefile
+for i in $ROSTER_EXTRAS; do
+	make -C $SOURCE_DIR/roster $i.jisp
+	cp $SOURCE_DIR/roster/$i.jisp $TARGET_DIR/roster
+done
+
+################################################################################
+
+# System iconsets
+echo '*** Updating System iconsets ***'
+#cp -R $SOURCE_DIR/system/$SYSTEM_DEFAULT/* $TARGET_DIR/system/default
+rm -f $TARGET_DIR/system/default/Makefile
+for i in $SYSTEM_EXTRAS; do
+	make -C $SOURCE_DIR/system $i.jisp
+	cp $SOURCE_DIR/system/$i.jisp $TARGET_DIR/system
+done
Index: psi/tags/0.10-os2-b1/build-mac.txt
===================================================================
--- psi/tags/0.10-os2-b1/build-mac.txt	(revision 38)
+++ psi/tags/0.10-os2-b1/build-mac.txt	(revision 38)
@@ -0,0 +1,21 @@
+Building Psi on Mac OS X
+------------------------
+
+To simply compile Psi, the same instructions apply as for other *nix 
+platforms:
+1. First, configure the build using the 'configure' script, optionally
+   adding parameters to specify where to find certain dependencies:
+	./configure
+2. Build the binary:
+	make
+After this, the built binary 'psi.app' will be available in src/.
+
+To make a distributable copy of your binary, do the following:
+1. As above, use configure to configure the build
+2. Alter the first lines of 'mac/Makefile' to reflect your setting
+3. Run 'make -C mac/'. 
+   This will create a distributable binary 'Psi.app' in 'mac/disk/'.
+4. (OPTIONAL) Create a DMG image by running 'make -C mac/ dmg'. 
+   This will create a 'Psi-<VERSION>.dmg' file in 'mac/'.
+   You can create your own DMG template to make the resulting DMG
+   look different. For more instructions on this, see 'mac/Makefile'.
Index: psi/tags/0.10-os2-b1/build-win.txt
===================================================================
--- psi/tags/0.10-os2-b1/build-win.txt	(revision 38)
+++ psi/tags/0.10-os2-b1/build-win.txt	(revision 38)
@@ -0,0 +1,13 @@
+Building Psi on Windows
+-----------------------
+
+With MSVC++ 6.0:
+
+* Go to a command prompt where Psi is unpacked, and type:
+
+  makewin
+
+  The result will be placed in the 'psiwin' folder.
+
+* Zip and enjoy.
+
Index: psi/tags/0.10-os2-b1/certs/README
===================================================================
--- psi/tags/0.10-os2-b1/certs/README	(revision 38)
+++ psi/tags/0.10-os2-b1/certs/README	(revision 38)
@@ -0,0 +1,21 @@
+This directory contains XML files that have SSL certificates in them.
+There should be at least a rootcert.xml file, but you can add more
+files if you want.
+
+The format is simple, there is one <store> element in each file, with
+any number of <certificate> child elements.  In each certificate, there
+is a <data> tag to contain the certificate data.  Example:
+
+  <store>
+    <certificate>
+      <data>Lots of nice data here</data>
+    </certificate>
+    <certificate>
+      <data>Data of another cert</data>
+    </certificate>
+    ...
+  </store>
+
+The data is DER+Base64 format, which is basically PEM but without the
+header, footer, or newlines.
+
Index: psi/tags/0.10-os2-b1/certs/rootcert.xml
===================================================================
--- psi/tags/0.10-os2-b1/certs/rootcert.xml	(revision 38)
+++ psi/tags/0.10-os2-b1/certs/rootcert.xml	(revision 38)
@@ -0,0 +1,344 @@
+<store>
+ <certificate>
+  <data>MIIDETCCAfmgAwIBAgIBADANBgkqhkiG9w0BAQQFADApMQswCQYDVQQGEwJhdTEaMBgGA1UEChMRU2VjdXJlTmV0IENBIFJvb3QwJhcROTkwNjMwMDAwMDAwKzEwMDAXETEwMTAxNTIzNTkwMCsxMDAwMCkxCzAJBgNVBAYTAmF1MRowGAYDVQQKExFTZWN1cmVOZXQgQ0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKcotNg89b96BpG9pUJMs3yeiGaZxdsX1EVUw3vA8CRWZpi/ROB5E3Zort2adM+i6ZfmwcCpw/cs1TY7c52vgtrcDl9Tr+tuUNeo8KqOq2PSm3X+VQp80bPAWZNRKodpihnt5NfXjhSrtRlYs85s4E2C0wOEwasAGHpJqBV9CMkBYCeU6PsJYJ7RP07NE6N4/WX3EV/2mwAwjCLtKIllG9MfYXvDI6PjxoD5g/P3ut6GeF/2lcOMakgiZYXTInn2/UQW6A9hBDYqdcbb4mUUJ2LcglyWEjMOTBjV9jydsc1hHLGF+QPRuvZM1u6ffdGSIfD2ChakNIf3Vo+TvpevUqcCAwEAAaM8MDowCwYDVR0PBAQDAgHEMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFPjNzO5Zx9l6SQKcbWNh7+dtGe0HMA0GCSqGSIb3DQEBBAUAA4IBAQCDld3QdwdCZqXldjxNjr2xNO+kYt3LGyaiOl9QmEe7VvrFOYZt2yPntFEK4RBaPUDkohrrF4MiOCXm5u0+q4WNg4SQS104Jf32nmNuOUjKbVOXoB3SEVuPx5z0B+s7QkHtkQp+9H0wkE4f/cU2bMtLT0M29yfJxO2x6PwOepUR5w4xzKYfQrcUaYyjmg5Myzqtl4weqt2iUpdUB9E+yALbM8wHv3rxXoAr9r3vcAyJeMl7gNfZ8W0Rk53qjZcC9l4DLXLcOplp/L5JeS5R5dcxD44BBIeEksmmYlIZhueGmrLLYu22xCln/RMUPDCBC5nFDqG+sBr7jVC6+MemssBp</data>
+ </certificate>
+ <certificate>
+  <data>MIIC2TCCAcGgAwIBAAIBADANBgkqhkiG9w0BAQQFADAsMQswCQYDVQQGEwJhdTEdMBsGA1UEChMUU2VjdXJlTmV0IENBIENsYXNzIEEwJhcROTkwNjMwMDAwMDAwKzEwMDAXETA5MTAxNTIzNTkwMCsxMDAwMCwxCzAJBgNVBAYTAmF1MR0wGwYDVQQKExRTZWN1cmVOZXQgQ0EgQ2xhc3MgQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKmjfPiQs46ETOAKvqsSKo0MPi+13ZsQiLlqt4d1ILdL7yIjBuvwVjL3yy1wxQNvt8HQw2YTAr1VSYeH6S2epeV2NqviZCVYiMMbGwLwXbGPnxNNwlIptBA1TzaIrwstgQgkYcNojVptMehUGVMX/acT9kCZpqgpwJ9Ceycpwe7b740DZCWBsf6VGh7aEEFiG1Z/GBhImNJNS8EYaLdpZ2tkTG6PAr8UaXv2RCMQ6S68XmLNW5zMo46MUYG0oqKxvKjJ6CqYqrtLFaHh6ZB6nn/oZQXoEXF6b+N13lU/y9jqyADNJZRyZB7vfRA+TS+Reb0C6teQwUloblkUJeLd3QkCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAIFOs7wKZu938ZA7BUVaUurKFhqzNefQYphR6ZpRnpriYHpw1nx+fN4ethR4YlBGtieD1XBMk06m1/7ls71YY8fiAKAiVPKr0LQpZhxHhT9JnHxFwwQrlSsKiSMAZR8gFuOb9KLZvfAPwzr6fy8K+cnCL0A/Ciqth2LNucT8v2A/512iNXQ2EzbTHcfJqu4AKqC0IVf9XrKmrykV7my3URV4AAjnSpb8H9a6Bg2xzFJOKeoWfyumkaRqlryhZNxwPtQ38GYtjTBtkuhUpo5/Rlc7GakJow14c9SfOn6n015rSEW083pdnCPMYutYzI84mgx7+DVFYYeyAJdO0NjO4ug==</data>
+ </certificate>
+ <certificate>
+  <data>MIIC2TCCAcGgAwIBAAIBADANBgkqhkiG9w0BAQQFADAsMQswCQYDVQQGEwJhdTEdMBsGA1UEChMUU2VjdXJlTmV0IENBIENsYXNzIEIwJhcROTkwNjMwMDAwMDAwKzEwMDAXETA5MTAxNTIzNTkwMCsxMDAwMCwxCzAJBgNVBAYTAmF1MR0wGwYDVQQKExRTZWN1cmVOZXQgQ0EgQ2xhc3MgQjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKZj2cYVWnbnRmXHNJH5e91Wu/iWNS4pyqd9Ch73MVLoWsjTzzkBTlvwH7G4StKzR0NmPwFATjBJ4Q/96xJjeMz2FmIY9W9p/X5uM3Yho8CID+rmJfypl8wYcscxtgubzcLFeGFjRaw0pid4aMS9UmjOhE3SJKUobIDmyubP5pqeUHjO8zbSo0NaHAavyyDu0CzERBiQbLGRZGW5t4WDS6iRmcXgnPe4UNfTsE+a7WP7TfTUKCQ6gFt7k4zFBYSvO4b+gTHHtp1fGuqQe7BGXrIIUgBXDVVSg3iVSv1oj57ZULZLU/ryhQKVTjj1g6eW1DE/P6fKN1PcxS9tGnSmzL8CAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAS0amIlrp0aXkEI9MU2DjTVSG3rocqipaNggdSgtJoe+qvbwmMqT/c0AwJA/sqxHVy6/ES5vnHhzDUIW6TwgrLz8UI6r2g5mGvkZ4ERUrmuSjv33pjX75Z98+f+atRudgZWdf5YDzLLtTZ3Nv4K6XEm1JGBxoTLMs81rBNTH27Ok+p8vNVFCHWEXAFNVItvMojjoYKeiamfHg9aPLCLR9eCUT4TTOaJvatyIhGzRSy+9T4vPXDGLasUVBRbiXWCwIYkUjDlmVNRoORMdRcE4/BK0mwh3tD0gtVRMdcaKnDrMvh2+w7uXHNosscVFAhxrOVhQ5Ke8lCoAKGZ31Vp/dig==</data>
+ </certificate>
+ <certificate>
+  <data>MIIC2zCCAcOgAwIBAAIBADANBgkqhkiG9w0BAQQFADAtMQswCQYDVQQGEwJhdTEeMBwGA1UEChMVU2VjdXJlTmV0IENBIFNHQyBSb290MCYXETk5MDYzMDAwMDAwMCsxMDAwFxEwOTEwMTUyMzU5MDArMTAwMDAtMQswCQYDVQQGEwJhdTEeMBwGA1UEChMVU2VjdXJlTmV0IENBIFNHQyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1uxDYpTIbpSiDiQQmVE/Vbrc8WF8wYx5Qj8jLHVescLIwq8WgkiAfinwN5XdDGLrTbMXnP39kTwMcr1LKIF8wocMHqGM+JGU/Zk1kersVOUY3fEYtMvC+pfsHUCXvgrzybz3tKt62V/vC5BhPyZmumBG6ecZsf49bKEGyB1ciHHhP8CRswPpmmFfVkh1Q6nXVYVT8wfQSx/Zhuv691Bo+yp5lZK/h6nxFwiny/gC3QBcMhzgwoHpGie5FEOjXQxL6LG2ggQK+8lPmyGtUbnl4PAq96wrgYa58j7736tjrCaRfGb9bHoMbtkAL9/kWbNqK+V6hM6Akxb68CT5EH8rQIDAQABMA0GCSqGSIb3DQEBBAUAA4IBAQAsoVzmIpjX7tSlQaP8a2WC6kBEwHwAgOPhv4Z1ds9iI/dgT+rPlRS6caN4jr833n1tb3WbydmOcwh0WNRxLFvWpF13CDWAAC3S2KjQcKhsJ+uppa6awG0vm/XB3EkQrNUrgL/AQZ25nZy9X6CEAg6llDG1ZHbCtbH/7/AeBOI1wGMCLgWVP5cVsBhnZc3XxRZ/P9PaYyC0C9zgvYz27X8rr0cZ0AvA99PIo7+BruTnNqdTGOHBYiJP+Isx44OEx47hjF98O3w5CJWhFuolG2X/1olQmOKxmmeo+S0tK/u0ZuuIT28KPhafD0ZUVpa7xnTZLm/HmYWR+OVy7Hg74fTJ</data>
+ </certificate>
+ <certificate>
+  <data>MIIDITCCAgmgAwIBAgIBADANBgkqhkiG9w0BAQQFADAxMQswCQYDVQQGEwJoazEiMCAGA1UEChMZQyZXIEhLVCBTZWN1cmVOZXQgQ0EgUm9vdDAmFxE5OTA2MzAwMDAwMDArMTAwMBcRMTAxMDE1MjM1OTAwKzEwMDAwMTELMAkGA1UEBhMCaGsxIjAgBgNVBAoTGUMmVyBIS1QgU2VjdXJlTmV0IENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0GKKQVozWXb9GWJEn6p114KTrolBwh0VJSZJrtzkAS3uo2kDh5g2i4DcBSE3OcuFck9PD+ZINJO9Izkcqy62SOqrWlbLyGZ4+lXyPYAJ0kxYoaf477l14kxx6PCPpYTlsxTAtH4SSBXHndMFCR+kwsaMXDDZjz1DQMqiHpUBiFBE4+84vjTOwi+mOcPuwnsddA+725Y1yPMi8VJLPypDfyNuvBfHrUMrRBawFtAEVlvXrOKFp94ND6DymXaUsuiQWL/sOMnCmEtaZNvdrG6Pn/McbyNgvddE1s+nC1o8EHcG1fgQ7OInkoyPw/cq9zOudFwYVcu3Q5vvWb7PxnT67AgMBAAGjPDA6MAsGA1UdDwQEAwIBxDAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTQY5TtgAkLJ5/gT7b7XCeyBlewhDANBgkqhkiG9w0BAQQFAAOCAQEAZjQ0EyeH4aRtS0hfsskxBGTzCjvVnUPM/EpokhvvCHbCKE5Acp9AHQSvo1C3GPwD1m7M2aUNu/XtsVJAawBm6cqN6TbeHx3/aVXzRw9eExI3ITa4+CSw7F4o9dmjqAt02YBNDtRiy+wyxFRQU9hUhnNZINC638Yo4lUJlvcb9p24y43UMAcv0R1n8JE1ftJBT7s50WTFlluN2AV1gXHNPSCE5K2dONjqZgy8GXIqyDBTzHognWP8K+1G3ARTtQ7NrY7kK8Tvj+K7UmYlO5Ye4aSCbuunnMiEouxjKWwSulcngMsPsH514+mkK34DOm444OzUQfzOEb6roeB+ZZ/NQA==</data>
+ </certificate>
+ <certificate>
+  <data>MIICqjCCAhOgAwIBAgIBADANBgkqhkiG9w0BAQUFADBEMRwwGgYDVQQKExNFVW5ldCBJbnRlcm5hdGlvbmFsMSQwIgYDVQQDExtFVW5ldCBJbnRlcm5hdGlvbmFsIFJvb3QgQ0EwHhcNOTkwMTE1MTUwMDU0WhcNMTgxMDAyMTUwMDU0WjBEMRwwGgYDVQQKExNFVW5ldCBJbnRlcm5hdGlvbmFsMSQwIgYDVQQDExtFVW5ldCBJbnRlcm5hdGlvbmFsIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ5BO9lBSYsonFkU91BoKHtNCFxn/4UeC6bPOjICsHx8Sp+2iQIhDEqZi8tkUX54WrZeqVO37+/H36UhWZdO4gflLXwZ1SrKyDPTlhAu3aAAn/3K6GGpU8q2HRg+nBHgY+cqZ1vIRJXwE1VIu75VuGc3zcsAEw38T2kUalGU7LwnAgMBAAGjgaswgagwHQYDVR0OBBYEFPfn3cvO2zj5trJnDlTQWQwyP929MGwGA1UdIwRlMGOAFPfn3cvO2zj5trJnDlTQWQwyP929oUikRjBEMRwwGgYDVQQKExNFVW5ldCBJbnRlcm5hdGlvbmFsMSQwIgYDVQQDExtFVW5ldCBJbnRlcm5hdGlvbmFsIFJvb3QgQ0GCAQAwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEFBQADgYEAXpHCGSGnCQkOM8Gomr2Y+bLysWDElw2lMJtfOwnfTXSsMhZ2C9PcIZ8iDwl2B8ulk0L4tG5cEo8Ur46QG2oD/ykxHjNKFxz3N40fgMCDZsQAplYV+qAyVROqS/5J8Nqa09a+pahKzWjs/3O2qwuj22FEmmRjCbEnB7VAjxcALlA=</data>
+ </certificate>
+ <certificate>
+  <data>MIIC6TCCAdGgAwIBAAIBADANBgkqhkiG9w0BAQQFADA0MQswCQYDVQQGEwJoazElMCMGA1UEChMcQyZXIEhLVCBTZWN1cmVOZXQgQ0EgQ2xhc3MgQTAmFxE5OTA2MzAwMDAwMDArMTAwMBcRMDkxMDE1MjM1OTAwKzEwMDAwNDELMAkGA1UEBhMCaGsxJTAjBgNVBAoTHEMmVyBIS1QgU2VjdXJlTmV0IENBIENsYXNzIEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0G6IKpUxzY0ZRSHRjqLSMFtv0yGfeoUgC/8ujz6ff2bEx4OMuKHEEpmRaxlz+0Ke46j4K2vsRf38kKF7IiqxueKrIohg5nkxQ0fCYte+kshtga7/5c7QLdfkiu+nlgaCV0nZMfNOn/l2VFe0H1MfeYDAA7vIulPGGMuVZUejfgKIqAO88iF0R4wBfVOQMRxBssPX4M3YcNGZb1Ls0YznTZpQ67M0iKfP2ErvUISk3pURN34kMBAx9QXuH8xbBQylktNZFZ6uGTGWPAbfqY4xOeFRERxpThoxrGCchC8VWZg4l9uB5rGgAdOmaZDxJTp7gHciIG1AG910J+I2LE/1dAgMBAAEwDQYJKoZIhvcNAQEEBQADggEBAF6wPCJpW/GYRwxjRwnhcHVXEXEyV5jHX9Vd/sbz6094HaQXcN3oQ6y2ZuftHGa+l+u0tcvXI6Y7IH/NLRVvfil7SE2j4+vRmu1OY3Rc5c7U7VSEFmJdpqFC558EwwrZkeXVpgy4EVnNrhRGNP/916hpFlZe6sm3J0t+sOJrsplE5szmQ/n2bVbvCQdITdMpt6SQHkXL55WQHAIplQgPiYNx2ZEA5LLqjG0qd1A2YC3v11XgY2wSQARsFPaGuWiNEC0Z6h+peZVH4LyBeT34BWLB76baeIuoInhE2v5gyyK7VaWPWpG69vNstR1pYIzy/JCoG/AqOHtljDBWaWlGf1A=</data>
+ </certificate>
+ <certificate>
+  <data>MIIC6TCCAdGgAwIBAAIBADANBgkqhkiG9w0BAQQFADA0MQswCQYDVQQGEwJoazElMCMGA1UEChMcQyZXIEhLVCBTZWN1cmVOZXQgQ0EgQ2xhc3MgQjAmFxE5OTA2MzAwMDAwMDArMTAwMBcRMDkxMDE1MjM1OTAwKzEwMDAwNDELMAkGA1UEBhMCaGsxJTAjBgNVBAoTHEMmVyBIS1QgU2VjdXJlTmV0IENBIENsYXNzIEIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCf4CWRDwRXwsdcsWUCZg+o/dgxDm+CbPnDHrDjoL04RFLHjHYSeEx+wiuMiuT53ArglUXf6x5+0kT4AxUA7MSxPHq2TbaWCne8B2Dv8Lc3/on9stgHq1L+gj+8NWcz2fss3LVYYaIMsX2U0aiEaSQ9l82bCExQiYGaH9LCIG/MPw5lxOFISdw7+eUqMQdupmtHqBlartOhdLW8wts5PfqrsPDJIgnUPEZobC4uH/iojBGhdjVSkteriCHcvemc2fg+3jAxTAIiL1dmu1ZyOTdQma9X0PRL1CntqW/G07KGkYsdEG1oFmTxDm2/Xmf9so5wZedvcrxEdasBdv83ZEUhAgMBAAEwDQYJKoZIhvcNAQEEBQADggEBADyBtF+Nd+/imA31qToeRtyJQcbakpNL/IJAdM6aeOZ2zWpSk0NJT+lOKslLYxpHlRvlG9qgLOLu27IQWl9HHhwlfS2YPJx5A28lLovH7D2lr3uRadRnSI+ASd2DbHQ6oGx/cVbmidvwZoNlRtL8yxdCxcSdU865EZSutC9mG2USgdopVyRie5lS7+ICfWMH91q8642tCzajGavNIWDCo3ad+qDORcwDpx/e9zoIl0sWTCBjQu/BEg0YKIZ2B74f2bUCtKgs1R+kStjS2huIKO8M5TXC9fTUPx0g9wPTri1PTpU1AfjGzDVaYuSZPvwz4uzqT6HExPQHg1XKjZg05hk=</data>
+ </certificate>
+ <certificate>
+  <data>MIIC6zCCAdOgAwIBAAIBADANBgkqhkiG9w0BAQQFADA1MQswCQYDVQQGEwJoazEmMCQGA1UEChMdQyZXIEhLVCBTZWN1cmVOZXQgQ0EgU0dDIFJvb3QwJhcROTkwNjMwMDAwMDAwKzEwMDAXETA5MTAxNTIzNTkwMCsxMDAwMDUxCzAJBgNVBAYTAmhrMSYwJAYDVQQKEx1DJlcgSEtUIFNlY3VyZU5ldCBDQSBTR0MgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKhTT49D3a/swbQNG1+3CKKayELjbdclGt/gyrwvIiT5aSK9Gq1R53H3tXbU8z7IPhHQTXuZHhi3ns0+YRtFEUtbJtwpPABNEdodj54fdN4VQLwwyQ/f+C182I2doOCfsM8nYM84EQjvNi98TjYdJgKn9NRQQ4FJ6923lhQwYzLRA80hvDeRHj5lrhaC9hVgS1E7luUZ6ML3pgWBOqh/GFg+d8NZAIP09XlJYKcuAz4B/YXi36Gaidn4ZAorhM1DzSU1AF4wZnShxwh1rEkngtewlTVuHZmA65KgLVrWOLu1RyerGsy/U1PBRrJv6NdNVxHdsDVIH6sm6lYNuAowttsCAwEAATANBgkqhkiG9w0BAQQFAAOCAQEAR5UV2Olmm+jWoeFv136OPNl3mE/gimc4zF0uEPwWcwSMstO5vW5tn8dqvBNMLdMT8vdtP/Qal9WGt6+ZSVU8XieOMD55JJp7GGJoUDc6KUGq0F1l4JPcn55NT5SepS0j3XuGF5XAgFc2jU/BrNQuWwTel7zX7TVFJLWOio5mWqXGhJBJGCINnCO1bJSDu15wdLcgCPilDCTidxuFPl5ZkWYWm3Iy5LEIEMRIx10WRzqBF7UpE5z2ZN+LCDLMZJB6lWjKYiLnx0NLBws6MeAoey0o9zK0kFGaOh8gBYj1Xo6okQTq1fQyGQHbBpxYVP31PCvUbVBfb/Gt9yPP/DVCyQ==</data>
+ </certificate>
+ <certificate>
+  <data>MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJENySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lNQseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6w4pl</data>
+ </certificate>
+ <certificate>
+  <data>MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQLEwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6CTShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHRxdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlihw6ID</data>
+ </certificate>
+ <certificate>
+  <data>MIIC+TCCAmKgAwIBAgIENvEbGTANBgkqhkiG9w0BAQUFADA2MQswCQYDVQQGEwJFUzENMAsGA1UEChMERk5NVDEYMBYGA1UECxMPRk5NVCBDbGFzZSAyIENBMB4XDTk5MDMxODE0NTYxOVoXDTE5MDMxODE1MjYxOVowNjELMAkGA1UEBhMCRVMxDTALBgNVBAoTBEZOTVQxGDAWBgNVBAsTD0ZOTVQgQ2xhc2UgMiBDQTCBnTANBgkqhkiG9w0BAQEFAAOBiwAwgYcCgYEAmD+tGTaTPT7+dkIU/TVv8fqtInpY40bQXcZa+WItjzFe/rQw/lB0rNadHeBixkndFBJ9cQusBsE/1waH4JCJ1uXjA7LyJ7GfM8iqazZKo8Q/eUGdiUYvKz5j1DhWkaodsQ1CdU3zh07jD03MtGy/YhOH6tCbjrbi/xn0lAnVlmECAQOjggEUMIIBEDARBglghkgBhvhCAQEEBAMCAAcwWAYDVR0fBFEwTzBNoEugSaRHMEUxCzAJBgNVBAYTAkVTMQ0wCwYDVQQKEwRGTk1UMRgwFgYDVQQLEw9GTk1UIENsYXNlIDIgQ0ExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5OTAzMTgxNDU2MTlagQ8yMDE5MDMxODE0NTYxOVowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFECadkSXdAfErBTLHo1POkV8MNdhMB0GA1UdDgQWBBRAmnZEl3QHxKwUyx6NTzpFfDDXYTAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GBAGFMoHxZY1tm+O5lE85DgEe5sjXJyITHa3NgReSdN531jiW5+aqqyuP4Q5wvoIkFsUUylCoeA41dpt7PV5Xa3yZgX8vflR64zgjY+IrJT6lodZPjLwVMZGACokIeb4ZoZVUO2ENv8pExPqNHPCgFr0W2nSJMJntLfVsV+RlG3whd</data>
+ </certificate>
+ <certificate>
+  <data>MIIDkzCCAnugAwIBAgIEAgAAADANBgkqhkiG9w0BAQQFADA/MQswCQYDVQQGEwJGUjETMBEGA1UEChMKQ2VydGlwb3N0ZTEbMBkGA1UEAxMSQ2VydGlwb3N0ZSBTZXJ2ZXVyMB4XDTk4MDYyNDA4MDAwMFoXDTE4MDYyNDA4MDAwMFowPzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpcG9zdGUxGzAZBgNVBAMTEkNlcnRpcG9zdGUgU2VydmV1cjCCASEwDQYJKoZIhvcNAQEBBQADggEOADCCAQkCggEAPqd4MziR4spWldGRRrIj3KVWfw/Hl7jinpEuKBYujvNHtlyDxPDm/mcak2R9QAnYHWgA7NQR7thjiQMj6YRfx1UOSuJyuynBVWR2qOOSzff2bkJWGFIzifmAFKgZUX7Hd796qDHAfRduQ07VZskFPkvi5eJVG3SqUR2qzJgRd6Tr2yoOJ8fVzUjs3H5OSedBzZkL0UteGBsNOgqbLZs5x0AM0HYHfHyPflxyjofVvvKmud6/3Z8SeUd8xcmXQEYAktMTWpNPnX7YX0zBXUhJ2S2YPPzMp4ch8D6Ocn1ZODYWGgxIvqHy1T6oFgwgWc/ODzODPa2r9f/d1/1pCIgA7wIDAQABo4GXMIGUMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEDMDUGA1UdDgQuBCxDPSBGUiwgTz0gQ2VydGlwb3N0ZSwgQ049IENlcnRpcG9zdGUgU2VydmV1cjA3BgNVHSMEMDAugCxDPSBGUiwgTz0gQ2VydGlwb3N0ZSwgQ049IENlcnRpcG9zdGUgU2VydmV1cjANBgkqhkiG9w0BAQQFAAOCAQEACArMs7ciiZrvVKv/PBKQ9O5+gcQAL27NobHE2ryy3rJBQPwsB57vpKMnv2ZUtka+hG+ri2JaNBQLqDmijh/dGYmvUYPTX+n76X1IhlkqcNHtN3Bwec3iOvtqjtVf9665TMoUTs/f123tx8bQrhZkonWnNm+Yhb7+4Q67ecf1janH7GcN/SXsfx7jJpreWULf7v7Cvpr4R7qIJcmdHIntmf7JPM5n6cDBv17uSW63er7NkVnMFHwK1QaGFLMybFkzaeGrvFm4k3lRefiXDmuOe+FhJgHYezYHf44LvPRPwhSrzi9+Aq3o8pWDguJuZDIUP1F1jMa+LPwvREXfFcUW+w==</data>
+ </certificate>
+ <certificate>
+  <data>MIIDkjCCAnqgAwIBAgIRAIb+HV/DgfhH1zMsc5R1ezcwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAxIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwMTAwWhcNMjAwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNzIDEgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMNrKcgux658UgXpk4lWClp5yczcJe+Hii0F2IEXBBs6Rfh5Dg1J1IFByKtF+3XIFfLi0LzZYWyEpBMZp9n58kncTA/GfVe0E9weCXEpNp2AWPQb2IkU69PSk0sq8XfnamW9GeWq/QxKY+HCmbQPBLZaGzY8Nzy/xeVe8V9XDh/H/Szn5G9qm2KWORGbZvAstlLc4Sf/223+66tjFB1jiGPiFgz5IFQPm90VanaDT70ni1OmN9KH8kdQUjtm6dF4GhuyO2nwWTw4W9EqTDNeDspWpe0tW4KDTdY4TopWtmovJGsSMKYq3q8Mu6pEEhwTxWUP4/3YGjFL5D8MfEXoFjMCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUIy7f6YG00IT9jrup3fkMo+PzSVMwEQYJYIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29tL0NSTC9jbGFzczEuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC+JlPWXp+QPwGUAuwIwYia0Kf6UA3a/JQdqNRQI9CfHIthsbKqRA+s2BMN1dah6DF40O8eg0Ca5f1B6ZL97qOVkPPAuG9+9zSlod88895WssffhyZEx+KDuRNvMEVxKSIVq8TbP0/zd/mZGDT1c5J9N2ZnSW4zmk0plIu9UDljZQ8Q53sQatFH+Z4lDCcpwtsIiOeqiTpFqp9Jsn3qaIpA/pHBwkn/IdFnDhoSHUouHPuU+W07eTL5HimRCkKWRfHvylgPcp0MqYO8Mt2YBNFZ4WQXvcyBowNelmDlfxY4CmvR+rOEnSrKPvtC+E/op1soGRit4KZmHrApIfK4mY//</data>
+ </certificate>
+ <certificate>
+  <data>MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7l7+ijrRU</data>
+ </certificate>
+ <certificate>
+  <data>MIIDkTCCAnmgAwIBAgIQWd+sfopmFRHliCTO76m8qTANBgkqhkiG9w0BAQUFADA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNzIDMgUHJpbWFyeSBDQTAeFw05OTA3MDcxNzA4MDBaFw0xOTA3MDYyMzU5NTlaMD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xhc3MgMyBQcmltYXJ5IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt5QwbtBM2X5eYOVvuybKUm9rbIWZpQvvABpvG01YtRgH+t17+MssUt38nLiNUum5sgt89Y9bmUgJWN7NSJWGJYkFNCcwC1e2DHdjKctsqj65mVESDZhwdkdM+UmWIgj5a+qqADajFaccHp+Mylp5eyHaiR9jfnmSUAkEKK3O420ESUqZsT9FCXhYIO+N/oDIBO0pLKBYjYQCJZc/oBPXe4sj45+4x7hCQDgbkkq9SpRVx1YVDYF3zJ+iN4krW4UNi3f4xIv7EMuUx+kaVhKXZhTEu9d9bQIbv3FiJhjpSYr6o97hhK2AykriIoxqCGGDsiLHCYg4Vl3RMavwCZ8TWQIDAQABo4GMMIGJMA8GA1UdEwQIMAYBAf8CAQowCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBRqZDZ2UJxO9PdPFfUN7TMlYOzLIDARBglghkgBhvhCAQEEBAMCAQYwNwYDVR0fBDAwLjAsoCqgKIYmaHR0cDovL3d3dy5jZXJ0cGx1cy5jb20vQ1JML2NsYXNzMy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAJqhPjiu4VLSRQ3R6YNjrYyA9JPDuMI6IpcnslGGPtLyl958u9eWQFPKUqqKbGPMunGK4vDwEIoXsE+Pxu8OMTBaja0Ex6uMXxoIz5oXmUa+jmtKan8BR2klGPhOPEoEKwbyEAA9/z6l6A7PcDiGu8WODNbiS0tm83NfgLU78RIp6nO8aJ4cYPfzqkLYFsho3nMLP0DBd3dUHZRXKQGTW5jDawT6fCmNrEfw/lUVS69xI/XDzgFnkyaArPSCisQbQErcxjkKbmO2ZE16zpCqmb02/Vn8L/xFvt6OM1n6igsPMDZN41u11pZQ17/+hdzatIIrzBsIP/GaTrXhjwhhJBY=</data>
+ </certificate>
+ <certificate>
+  <data>MIIDRzCCArCgAwIBAgIENm3FGDANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRswGQYDVQQLExJEU1QtRW50cnVzdCBHVEkgQ0EwHhcNOTgxMjA5MDAwMjI0WhcNMTgxMjA5MDAzMjI0WjBQMQswCQYDVQQGEwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRswGQYDVQQLExJEU1QtRW50cnVzdCBHVEkgQ0EwgZ0wDQYJKoZIhvcNAQEBBQADgYsAMIGHAoGBALYd90uNDxPjEvUJ/gYyDq9MQfV91Ec9KgrfgwXe3n3mAxb2UTrLRxpKrX7E/R20vnSKeN0Lg460hBPE+/htKa6h4Q8PQ+O1XmBp+oOU/Hnm3Hbt0UQrjv0Su/4XdxcMie2n71F9xO04wzujevviTaBgtfL9E2XTxuw/vjWcPSLvAgEDo4IBLjCCASowEQYJYIZIAYb4QgEBBAQDAgAHMHIGA1UdHwRrMGkwZ6BloGOkYTBfMQswCQYDVQQGEwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRswGQYDVQQLExJEU1QtRW50cnVzdCBHVEkgQ0ExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkwMDAyMjRagQ8yMDE4MTIwOTAwMDIyNFowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFJOaRMrQeFOAKUkE38evMz+ZdV+uMB0GA1UdDgQWBBSTmkTK0HhTgClJBN/HrzM/mXVfrjAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GBAGSJzAOn3AryWCDn/RegKHLNh7DNmLUkR2MzMRAQsu+KV3KuTAPgZ5+sYEOEIsGpo+Wxp94J1M8NeEYjW49Je/4TIpeU6nJI4SwgeJbpZkUZywllY2E/0UmYsXYQVdVjSmZLpAdr3nt/ueaTWxoCW4AO3Y0Y1Iqjwmjxo+AY0U5M</data>
+ </certificate>
+ <certificate>
+  <data>MIIDKTCCAhECCF9gWF8AAAAAMA0GCSqGSIb3DQEBBQUAMFcxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRswGQYDVQQDExJTZWN1cmVTaWduIFJvb3RDQTEwHhcNOTkwOTE1MTUwMDAxWhcNMjAwOTE1MTQ1OTU5WjBXMQswCQYDVQQGEwJKUDErMCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEbMBkGA1UEAxMSU2VjdXJlU2lnbiBSb290Q0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlJAMS3EpHNr2aHl6pLrn0syNr+hHkJkfxirql2PoH84XV8Yas6jHfIftNTWAurpubb4X/swtG2zvigBJFuHuBl5KB12rPdFQuJFG1NTaFdiUXA7K19q/oPdJPMi7zuomgQoULZwNN0VrQcpXizjwJh8x/M80jo93wT/jq1Q8J7TOMkxVE2L8/joWJc8ba6Ijt+DqAmm79yJxbXwLGZOhl5zjkWkfaOQvfRBtj2euwRCisF5jSpf35niprSa7VMnftO7FntMl3RNoU/mP6Ozl3oHWeD7uUEC0ATysFcGCOy5/8VIni3Lg59v5iynDw0orM4mrXCoH/HwjHitPCCL+wQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBV7W97k+VFMU5o1VWBoggfbN0JxXgacFfI3wiBrmZ3xnUP5O9JiwNcbP8ckKRystMWErIG+EaGrr+nFduFTfrCLU2ztbBD73x+B9tfs1dGUXYHhkT9B+rxy0tFTWanMybE+UOqjRKz1I1otvcCebQtWtcDmAQsaZmv9GY7ZKyywCvIaVSeTE5IGI3OV7U7UeUb1/o5YNtWRRO+52bVI/Z8SACwTO80jSKssi7RTDjN+lgDBu46c4cKBTrK5K/Uwe4chX8lFs8nAR+EincI0NNG6CDsn6SM8bzNxBI2gB7HCSiv6Ai+wNOyPtcuZz2jzrs0+uKFzazOVR1FW3iF04V6</data>
+ </certificate>
+ <certificate>
+  <data>MIIDKTCCAhECCF9gWF8AAAAAMA0GCSqGSIb3DQEBBQUAMFcxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRswGQYDVQQDExJTZWN1cmVTaWduIFJvb3RDQTIwHhcNOTkwOTE1MTUwMDAxWhcNMjAwOTE1MTQ1OTU5WjBXMQswCQYDVQQGEwJKUDErMCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEbMBkGA1UEAxMSU2VjdXJlU2lnbiBSb290Q0EyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlnuSIz9g3wk8WIAI42MJl+jkC3Vh1M0Oo/LjHkO6g/+6gVwvyN6Qi0wOLyn5B9aOs6Yor4Iqe8K0Zkxx9Ax0GrjbGuhoN6n5oaJuHCjNbCY8jyoznp3LtHnE2WQ9lcYzqEf75QcJ3PZtuCVCTMP7Su1bLtQHqOWTECSTWG59wdAez+kp19C8X0zwFRbD2MLO41sXW5SLKGsUZyQ79FLsDW58TrSZAtvJ8w+CqwH0jN4WcMa8Fwdh/xFAhOosG3o6sANhB6qWjdDauYOO5J1RaXVxZIG0iFXcEIPOLaX1MJZhLjsK/IdfnFyCdRMe05jR7cntchYcDAbcWSB+8F3v9wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBp4FZw/zNayOHI06wwIXNITiLiMijLvBc6jX440fm3PzCFMo1X/B9Afz96dOwSCWTB/dz8HDW88Qu06qFc3ispkIIDFsqexLuwko2VVlKZMsCQm1VD5PNNdlGtbDxcncJBfY1M8KmsOr4gJwWXcEZotmT4JZnplcOOOunB4hrAIrFtrbDunyzsdMPBZCySb5ZoCwFX6IaZr+eIyFlgbrJWBKtUuF0YFnbN2h50kEubRhGXCYHzzvJhmQPz6W5ITD/bvIDbgSD+vVxG/07nr03+vESA4wluZ3ol+1fnPOZVpaIBhig/f1/r9OsahFUxnyTwyfZpTBRSE7P5JBoX2Rhw</data>
+ </certificate>
+ <certificate>
+  <data>MIIDKTCCAhECCF9gWF8AAAAAMA0GCSqGSIb3DQEBBQUAMFcxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRswGQYDVQQDExJTZWN1cmVTaWduIFJvb3RDQTMwHhcNOTkwOTE1MTUwMDAxWhcNMjAwOTE1MTQ1OTU5WjBXMQswCQYDVQQGEwJKUDErMCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEbMBkGA1UEAxMSU2VjdXJlU2lnbiBSb290Q0EzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmV4egJZmI2TOnIwAPgqvCOm4BOCEuG1TdU02qLXg14xOYFW2A5ebWhqn87o92ZqUMXZ0I8n37BJd2CDUHekbojd2BA8+rBZpO+H/EC9WJeQzUBMJzE4Oq/Dkddtx1fxKze3bDzUFFdWwZntCeyblWeK1x8Cyx6FD/Q8vC4MlJVeBu7vRNTB0kZCyj59o1dJDt7JFqSPAVtiHEtNz/stZ6q/85x9eVEUcqm2Vk2JHQkFeT+s2Bw4oeFQKfMDDJBOGAwK5rHaSSlrdxdzs+LPbK7UbNud4gkyVfiBWsnUcfZfvf5Q4KaIA4tHqseM0NjFAWLiqt86BGgwXgQ3967jTvQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCOmITWG42BrNADeug5vE3lEmqvqnJgMX8aLHphfjxMnBxqXEzRPZZZCy+Rsa3z4ajSn8GLdtyPrDCpsCvaZdajjtxQS1ZNQkVKEh1hlSuanYJkFV31+8AWszcPA6obgFU5AEbSlIguLLTNq1u16cub5l8wvtB0XpsQL4B1Pwn5LuFmH1ls7W8uPjunJJA0u+SQp0vIL3kpXzZoHh0aFWrg3yuBQcY7SkaRv8qTe+GRLC08B18CrHueTg2tPlzkHGgin8+BsXtvmM2hOPPUiSVDhE9O4m5eJCRdlMI5z6X5cWAY+krJ5AB8Xv2JUrdtgnd4b61FxnLRgE2lBUPty+Kz</data>
+ </certificate>
+ <certificate>
+  <data>MIIDlTCCAn2gAwIBAgIRAL9c27byHG7ATet6Ajs26HkwDQYJKoZIhvcNAQEFBQAwPjELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRwwGgYDVQQDExNDbGFzcyAzUCBQcmltYXJ5IENBMB4XDTk5MDcwNzE3MTAwMFoXDTE5MDcwNjIzNTk1OVowPjELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRwwGgYDVQQDExNDbGFzcyAzUCBQcmltYXJ5IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqzf/62CbQXhp9UlYsN4fcWmmK+OuUMapvJPpIL7kxBOCVu/wQzIJypt1A498T+HgT3aeC61kehQ6mp2/LxYLZRyp7py84xply0+F6pJWdWbWVUDv+8zWOD+rHO9CjRmJ9reVhsKnHen3KfEq2WV5/Cv1jsoad36e6Kz5Zr9F++gTnV+2c+V9e477EnRdHwZehRumXhhEALq8027RUg4GrevutbTBu7zrOA9IIpHHb9K4cju6f8CNbLe8R3MhKoX/rNYoohnVl2o6uaxtRezmTcPbqF3FXYKYrEpaquYrCAwQdLxi9jpJBGbYURwmpth1n5y/rmBRPVy8ok97iWfNUwIDAQABo4GNMIGKMA8GA1UdEwQIMAYBAf8CAQowCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBSG4eGBcb9qEvEK8gHkyPtAzmiAiTARBglghkgBhvhCAQEEBAMCAAEwOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL3d3dy5jZXJ0cGx1cy5jb20vQ1JML2NsYXNzM1AuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQAlquEiQMKkgDy3ol2ZjR96QjU4ZhcR372v/BURmBkz5gVChFSoS+uwnds32hZSQBF0aL/pybIQhLcdRAB5Jxz1WAYXGDI1tjCXY8amORvI7kYXYsUu5wqjmoowY3OqFKVNCqhyk/BJERCQfBh9qCAFxMJ6NbocWgrgLnjIiLHPVwHsPeIGEzTAqNz6gIAF7gV2vZ0ryJ1Qb2vFQFCE/V0d5pCcENOkxrkoGt61+Apwqs7eUD0DgNvYiMVIBuQDc90WzjbW5Zvqd9qylrVlpwRdI673k7JeilFkX9rPjD1BW975o+kqfEcQH/YyPH5w6d+h1S4NsRpFtLwS7SgX6R4C</data>
+ </certificate>
+ <certificate>
+  <data>MIIB+jCCAWMCAgGjMA0GCSqGSIb3DQEBBAUAMEUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xHDAaBgNVBAMTE0dURSBDeWJlclRydXN0IFJvb3QwHhcNOTYwMjIzMjMwMTAwWhcNMDYwMjIzMjM1OTAwWjBFMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMRwwGgYDVQQDExNHVEUgQ3liZXJUcnVzdCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC45k+625h8cXyvRLfTD0bZZOWTwUKOx7pJjTUteueLveUFMVnGsS8KDPufpz+iCWaEVh43KRuH6X4MypqfpX/1FZSj1aJGgthoTNE3FQZor734sLPwKfWVWgkWYXcKIiXUT0Wqx73llt/51KiOQswkwB6RJ0q1bQaAYznEol44AwIDAQABMA0GCSqGSIb3DQEBBAUAA4GBABKzdcZfHeFhVYAA1IFLezEPI2PnPfMD+fQ2qLvZ46WXTeorKeDWanOB5sCJo9Px4KWlIjeaY8JIILTbcuPI9tl8vrGvU9oUtCG41tWW4/5ODFlitppK+ULdjG+BqXH/9ApybW1EDp3zdHSo1TRJ6V6e6bR64eVaH4QwnNOfpSXY</data>
+ </certificate>
+ <certificate>
+  <data>MIIDgzCCAmugAwIBAgIRAK8/ZGtWAoZmxthD+Ut9ls8wDQYJKoZIhvcNAQEFBQAwPzELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMR0wGwYDVQQDExRDbGFzcyAzVFMgUHJpbWFyeSBDQTAeFw05OTA3MDcxNzE0MDBaFw0xOTA3MDYyMzU5NTlaMD8xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEdMBsGA1UEAxMUQ2xhc3MgM1RTIFByaW1hcnkgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9ZZojQwA8+SUBQ5hbRjQ3Zzf0W1eOo97NjC9BsxU95ZiL8sxcp8evNcezp0/HEClX6QOJg3IGdlLBcAI3QO3wzouxsYmsAqhb3dzVQ0UG3Ci26VEbau7ze0jFnRGujaZyk8AlQpGoPkbiaEyBQLBC03q+e0fSR1AMpBKmyuQS0MB8OjwBqcoClt6CWsX7Gbo8nNgPdB+YGK6UUDIHUhARSkvsWFUs1J5LlHsF0nLfBvCNywie5O60RK2C8RthavGz+BYxNkIkupC/tX5hbsJZDFR5tEyIKB7MG9+EiyPIvq8H8pGy2o3KjF98KDx8iWwGWb0+zsWC02rJrGhV7sWvAgMBAAGjejB4MA8GA1UdEwQIMAYBAf8CAQowCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBSidg45eGuPui1xSgj3ontuAKJkJTA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vd3d3LmNlcnRwbHVzLmNvbS9DUkwvY2xhc3MzVFMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC1n2icxJLwd3wTwhW8HNQObnQoofqG0ggbJfzke9l90wJYZ28EblDue0oBAmO/QjbWvgz+IgyZPY7zrh2U1qrqpj3jQ55HuwnJy+7OoSwPqnzffHOS5Uu5BMQ72yD+YPkMbngaPUjXJ0iYtZGspNwis+edX+zlQGImj3oXxur7CtZDW5DbNhbtmC6eX6wUB4IqUL+mv2+mBzRytmFo7FGYGVN36Mi/9iF7vLDef3Q6+RU0OJr7iTPwEg2f6PGWbfYdSH4+Eu/6BmdZ3rOy1pcLO0BXQrLBe8Zt2CZSNCXShbahCgrdqpSqTnsgk92kgENWz7bE4FjpbJWPYm5YBq94</data>
+ </certificate>
+ <certificate>
+  <data>MIIDTTCCAragAwIBAgIENm6ibzANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMR0wGwYDVQQLExREU1QgKEFOWCBOZXR3b3JrKSBDQTAeFw05ODEyMDkxNTQ2NDhaFw0xODEyMDkxNjE2NDhaMFIxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xHTAbBgNVBAsTFERTVCAoQU5YIE5ldHdvcmspIENBMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC0SBGAWKDVpZkP9jcsRLZu0XzzKmueEbaIIwRccSWeahJ3EW6/aDllqPay9qIYsokVoGe3eowiSGv2hDQftsr3G3LL8ltI04ceInYTBLSsbJZ/5w4IyTJRMC3VgOghZ7rzXggkLAdZnZAa7kbJtaQelrRBkdR/0o04JrBvQ24JfQIBA6OCATAwggEsMBEGCWCGSAGG+EIBAQQEAwIABzB0BgNVHR8EbTBrMGmgZ6BlpGMwYTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEdMBsGA1UECxMURFNUIChBTlggTmV0d29yaykgQ0ExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxNTQ2NDhagQ8yMDE4MTIwOTE1NDY0OFowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFIwWVXDMFgpTZMKlhKqzZBdDP4I2MB0GA1UdDgQWBBSMFlVwzBYKU2TCpYSqs2QXQz+CNjAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GBAEklyWCxDF+pORDTxTRVfc95wynr3vnCQPnoVsXwL+z02exIUbhjOF6TbhiWhbnKUJykuOpmJmiThW9vTHHQvnoLPDG5975pnhDX0UDorBZxq66rOOFwscqSFuBdhaYYgAYAnOGmGEJRp2hoWe8mlF+tMQz+KR4XAYQ3W+gSMqNd</data>
+ </certificate>
+ <certificate>
+  <data>MIIEEjCCAvqgAwIBAgIPAMEAizw8iBHRPvZj7N9AMA0GCSqGSIb3DQEBBAUAMHAxKzApBgNVBAsTIkNvcHlyaWdodCAoYykgMTk5NyBNaWNyb3NvZnQgQ29ycC4xHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFJvb3QgQXV0aG9yaXR5MB4XDTk3MDExMDA3MDAwMFoXDTIwMTIzMTA3MDAwMFowcDErMCkGA1UECxMiQ29weXJpZ2h0IChjKSAxOTk3IE1pY3Jvc29mdCBDb3JwLjEeMBwGA1UECxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgUm9vdCBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpAr3BcOY78k4bKJ+XeF4w6qKpjSVf+P6VTKO3/p2iID58UaKboo9gMmvRQmR57qx2yVTa8uuchhyPn4Rms8VremIj1h083g8BkuiWxL8tZpqaaCaZ0Dosvwy1WCbBRucKPjiWLKkoOajsSYNC44QPu5psVWGsgnyhYC13TOmZtGQ7mlAcMQgkFJ+p55ErGOY9mGMUYFgFZZ8dN1KH96fvlALGG9O/VUWziYC/OuxUlE6u/ad6bXROrxjMlgkoIQBXkGBpN7tLEgc8Vv9b+6RmCgim0oFWV++2O14WgXcE2va+roCV/rDNf9anGnJcPMq88AijIjCzBoXJsyB3E4XfAgMBAAGjgagwgaUwgaIGA1UdAQSBmjCBl4AQW9Bw72lyniNRfhSyTY7/y6FyMHAxKzApBgNVBAsTIkNvcHlyaWdodCAoYykgMTk5NyBNaWNyb3NvZnQgQ29ycC4xHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFJvb3QgQXV0aG9yaXR5gg8AwQCLPDyIEdE+9mPs30AwDQYJKoZIhvcNAQEEBQADggEBAJXoC8CN85cYNe24ASTYdxHzXGAyn54Lyz4FkYiPyTrmIfLwV5MstaBHyGLv/NfMOztaqTZUaf4kbT/JzKreBXzdMY09nxBwarv+Ek8YacD80EPjEVogT+pie6+qGcgrNyUtvmWhEoolD2Oj91Qc+SHJ1hXzUqxuQzIH/YIX+OVnbA1R9r3xUse958Qw/CAxCYgdlSkaTdUdAqXxgOADtFv0sd3IV+5lScdSVLa0AygS/5DW8AiPfriXxas3LOR65Kh343agANBqP8HSNorgQRKoNWobats14dQcBOSoRQTIWjM4bk0cDWK3CqKM09VUP0bNHFWmcNsSOoeTdZ+n0qA=</data>
+ </certificate>
+ <certificate>
+  <data>MIIDuzCCAqOgAwIBAgIEAQAAADANBgkqhkiG9w0BAQQFADBJMQswCQYDVQQGEwJGUjETMBEGA1UEChMKQ2VydGlwb3N0ZTElMCMGA1UEAxMcQ2VydGlwb3N0ZSBDbGFzc2UgQSBQZXJzb25uZTAeFw05ODA2MjQwODAwMDBaFw0xODA2MjQwODAwMDBaMEkxCzAJBgNVBAYTAkZSMRMwEQYDVQQKEwpDZXJ0aXBvc3RlMSUwIwYDVQQDExxDZXJ0aXBvc3RlIENsYXNzZSBBIFBlcnNvbm5lMIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQAox3xaJSN48rMAR0Biy2+MQlCfnl7UXA5lC1hWlSvjRtBhNuAtRpuCy5Hu0pV8mpKvBAp+pp/g17HDRfmYQRs5redW19m2f867OS4sO8+2cwODzhNdMmpjottb+Esz6FBsy6gX7J6TuWwGSyYLdx6e+eWMiTfS0bv9qYwrLJwQMdhLjM23cX44LCnjF7JP6FK245I80v3hAtphEHTSGvPI0dFmB1/EhGNpva5s3GUjHLf798YTLoN+P6nlCyBtAQo34lzait4icOkN4HQ9xOtxm2Eq4g0Ui0xGN0wm0mjWVsNXqqJgN69fnaCzgILmQypMgAAJUNmoanNtA/5ec5LlAgMBAAGjgaswgagwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQMwPwYDVR0OBDgENkM9IEZSLCBPPSBDZXJ0aXBvc3RlLCBDTj0gQ2VydGlwb3N0ZSBDbGFzc2UgQSBQZXJzb25uZTBBBgNVHSMEOjA4gDZDPSBGUiwgTz0gQ2VydGlwb3N0ZSwgQ049IENlcnRpcG9zdGUgQ2xhc3NlIEEgUGVyc29ubmUwDQYJKoZIhvcNAQEEBQADggEBAB6Ii98OcWRdSMSQRBLLJ12Pk+NzhQvMQFtX86A3bxlIo5S7WiZVDySOeOeeBILseNsJyuGFg+9xWWG6Jqq6bn0d84gBsrKKiTx2s2KmXsLsZFuCvf+wlDp2LPl+KYZKWXpeKLFE2L1GGKmnbDC5CkfnqRyQKKfySVQ2sjPHBrFObIc5QHodnI9tozSj3dq7Dw5YhDc2yeMxBUhdY0lMMnB9xdgqqowKMGDdNASa4ULYyaphuZe820rESDThsBNYk4jA/6jUKmrIiu0nl/ehe6TseZhXYVPsg2C/d3po0N7adbPuY+b7sEiRagvprQqTrDyJM6qkRhdU1Z2FTXvKOvY=</data>
+ </certificate>
+ <certificate>
+  <data>MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ==</data>
+ </certificate>
+ <certificate>
+  <data>MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXggU2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBqy/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN</data>
+ </certificate>
+ <certificate>
+  <data>MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4</data>
+ </certificate>
+ <certificate>
+  <data>MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV</data>
+ </certificate>
+ <certificate>
+  <data>MIIFmTCCA4GgAwIBAgIQea0WoUqgpa1Mc1j0BxMuZTANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDEwNTA5MjMxOTIyWhcNMjEwNTA5MjMyODEzWjBfMRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDzXfqAZ9Rap6kMLJAg0DUIPHWEzbcHiZyJ2t7Ow2D6kWhanpRxKRh2fMLgyCV2lA5Y+gQ0Nubfr/eAuulYCyuT5Z0F43cikfc0ZDwikR1e4QmQvBT+/HVYGeF5tweSo66IWQjYnwfKA1j8aCltMtfSqMtL/OELSDJP5uu4rU/kXG8TlJnbldV126gat5SRtHdb9UgMj2p5fRRwBH1tr5D12nDYR7e/my9s5wW34RFgrHmRFHzF1qbk4X7Vw37lktI8ALU2gt554W3ztW74nzPJy1J9c5g224uha6KVl5uj3sJNJv8GlmclBsjnrOTuEjOVMZnINQhONMp5U9W1vmMyWUA2wKVOBE0921sHM+RYv+8/U2TYQlk1V/0PRXwkBE2e1jh0EZcikM5oRHSSb9VLb7CG48c2QqDQ/MHAWvmjYbkwR3GWChawkcBCle8Qfyhq4yofseTNAz93cQTHIPxJDx1FiKTXy36IrY4t7EXbxFEEySr87IaemhGXW97OU4jm4rf9rJXCKEDb7wSQ34EzOdmyRaUjhwalVYkxuwYtYA5BGH0fLrWXyxHrFdUkpZTvFRSJ/Utz+jJb/NEzAPlZYnAHMuouq0Ate8rdIWcbMJmPFqojqEHRsG4RmzbE3kB0nOFYZcFgHnpbOMiPuwQmfNQWQOW2a2yqhv0Av87BNQIDAQABo1EwTzALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUDqyCYEBWJ5flJRP8KuEKU5VZ5KQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggIBAMURTQM6YN1dUhF3j7K7NsiyBb+0t6jYIJ1cEwO2HCL6BhM1tshj1JpHbyZX0lXxBLEmX9apUGigvNK4bszD6azfGc14rFl0rGY0NsQbPmw4TDMOMBINoyb+UVMA/69aToQNDx/kbQUuToVLjWwzb1TSZKu/UK99ejmgN+1jAw/8EwbOFjbUVDuVG1FiOuVNF9QFOZKaJ6hbqr3su77jIIlgcWxWs6UT0G0OI36VA+1oPfLYY7hrTbboMLXhypRL96KqXZkwsj2nwlFsKCABJCcrSwC3nRFrcL6yEIK8DJto0I07JIeqmShynTNfWZC99d6TnjpiWjQ54ohVHbkGsMGJay3XacMZEjaE0Mmg2v8vaXiy5Xra69cMwPe9Yxe4ORM4ojZbe/KFVmodZGLBOOKqv1FmopT1EpxmIhBr8rcwki3yKfA9OxRDaKLxnCk3y844ICVtfGfzfiQSJAMIgUfspZ6X9RjXz7vV73aW7/3O21adlaBC+ZdY4dcxItNfWeY+biIA6kOEtiXb2fMIVmjAZGsdfOy2k6JiV24u2OdYj8QxSSbd3ik1h/UwcXBbFDxpvYkSfesuo/7Yf56CWlIKK8FDK9kwiJ/IEPuJjeahhXUzfmye23MTZGJppS99ypZtn/gETTCSPW4hFCHJPeDD/YprnUr90aGdmUN3P7Da</data>
+ </certificate>
+ <certificate>
+  <data>MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk0MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGbMA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6zV4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXATcXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZIhvcNAQECBQADfgBl3X7hsuyw4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3YQO2WxZpO8ZECAyIUwxrl0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc1/p3yjkWWW8O6tO1g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==</data>
+ </certificate>
+ <certificate>
+  <data>MIICZDCCAc2gAwIBAgIBBTANBgkqhkiG9w0BAQQFADBvMQswCQYDVQQGEwJCUjEuMCwGA1UEChMlQ2VydGlzaWduIENlcnRpZmljYWRvcmEgRGlnaXRhbCBMdGRhLjEwMC4GA1UECxMnQ2VydGlzaWduIEF1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSBBQzFTMB4XDTk5MDYyNzAwMDAwMFoXDTE4MDYyNzAwMDAwMFowbzELMAkGA1UEBhMCQlIxLjAsBgNVBAoTJUNlcnRpc2lnbiBDZXJ0aWZpY2Fkb3JhIERpZ2l0YWwgTHRkYS4xMDAuBgNVBAsTJ0NlcnRpc2lnbiBBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgQUMxUzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsMCV66PFQfibb3FjjpERV/kDx2iaGpxh8MMPDJdiiH4mATV9/fFvTxeVqn7yu7zv4nZzYZU2UYM+hOg/ulc2N5QTbJJkH5QN61QvUVrlKF7wkobn5G0hel83C+dbvS0CqIH5P8sUyLykxePRyzfcq+WHqvILmC56z8wewn/o/QECAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAbrqnYdrx6nviLGuf7RdNeMA6TvfipXQNCm/Bkxnk9Q2k8QKE2RkiCVuiI8DqV5r7oobZAvf/WLK2hMwZzeAJl6e17e0mEbWTT852bbJ/hQwOYYMC10+XrSKlJYuK5zswkCtgiwT03MAScdf6IugSleqIA3u0BHaY5E6Rs2LWaog==</data>
+ </certificate>
+ <certificate>
+  <data>MIID1jCCAr6gAwIBAgIBATANBgkqhkiG9w0BAQQFADBQMQswCQYDVQQGEwJVUzENMAsGA1UEChMETVNGVDEyMDAGA1UEAxMpTWljcm9zb2Z0IEF1dGhlbnRpY29kZSh0bSkgUm9vdCBBdXRob3JpdHkwHhcNOTUwMTAxMDgwMDAxWhcNOTkxMjMxMjM1OTU5WjBQMQswCQYDVQQGEwJVUzENMAsGA1UEChMETVNGVDEyMDAGA1UEAxMpTWljcm9zb2Z0IEF1dGhlbnRpY29kZSh0bSkgUm9vdCBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDfCLrjP25km/WJryiWSgePGy6LPh38uIBpo6HO29+wjmyJdilPymA1Oa1yMuALrik9TBbZSzyd2sXT0QnJLG+mwmBTRd1L0VXNAxzSWVYk8+V42AfM2LMfkD/AGnFQHS2nEghtfLCGbMe6hTIH4WFvrwPFbeXWoY829sEL0T5pl0hyyX+kyMJKTH6h0ZSm19zrBUYuuBi0Vx2GSdtpSiwh9V4PVC1aQ6l6fmqOUE0lV6G/GxUFQ3ssBY29PQOMkyJ9Y+oKVwUGCtthmGUtR0mo5+ZWdVy4ZAhjqTBAZrL5tuM06Gcw4UMLh//JvnIQXiPwm6dIZb8JiHvNcrwueZt7AgMBAAGjgbowgbcwDQYDVR0KBAYwBAMCB4AwMgYDVQQDBCsTKU1pY3Jvc29mdCBBdXRoZW50aWNvZGUodG0pIFJvb3QgQXV0aG9yaXR5MHIGA1UdAQRrMGmAEBob51uf/YwqwzmuDGIuUzKhUjBQMQswCQYDVQQGEwJVUzENMAsGA1UEChMETVNGVDEyMDAGA1UEAxMpTWljcm9zb2Z0IEF1dGhlbnRpY29kZSh0bSkgUm9vdCBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEEBQADggEBAC3J4vYSnl1WZ/r6S5p+3ClWXIAUAiiFbibzzVjaUIDF+BmzpnzinWtfO48idOYYBPxHQNh6PzBm8BKk0esd57b0mKtTIoZRWO4jCXbkHUVcS/9M4wJQARPMQaRSl9SG1cT+g4Nlfeq+omg7wbEpmL+ipfyd04TucBdQ8wv6PO+pJ4uRtEjIRaDhAUJLRHYEHMIZoo5rIJjE3QKstNKiDo1duTaOShtdbBriywB/EPSyle/j6P+hc1ipdSyiSZWF/szaRIrCEkTSRMiloh+pWo5WwsN7z0Jg3IIf+850Bn7W8awZak90XMUVZjFswWJxkQ9ZW30qghrfsbTYHTfeDQ8=</data>
+ </certificate>
+ <certificate>
+  <data>MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQQFADByMQswCQYDVQQGEwJCUjEuMCwGA1UEChMlQ2VydGlzaWduIENlcnRpZmljYWRvcmEgRGlnaXRhbCBMdGRhLjEzMDEGA1UECxMqQ2VydGlzaWduIC0gQXV0b3JpZGFkZSBDZXJ0aWZpY2Fkb3JhIC0gQUMyMB4XDTk5MDYyNzAwMDAwMFoXDTE4MDYyNzAwMDAwMFowcjELMAkGA1UEBhMCQlIxLjAsBgNVBAoTJUNlcnRpc2lnbiBDZXJ0aWZpY2Fkb3JhIERpZ2l0YWwgTHRkYS4xMzAxBgNVBAsTKkNlcnRpc2lnbiAtIEF1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSAtIEFDMjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuTDMpeuQ1laUZzOJg21GvnCXkxdOCidwzsnF8sXSLdERnHcybHTTkm/66dW4YZqqTBM+NTrw7gk22dGXMcderEFJv0htTiBTyXtqo5Yxa7JuqM8vckwGrTLPWgjzfRCcqru6c/nz8Lgkb6hltiL+BVsQVUTCAx2t0SLMCG37jMsCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBIa969g3LQgWoJEgXcYwOVaoZILqriJUtiZDyHhKQwpL2lHotWEldDZE/H6Y+9qvEadXmKU5H2Q10Cmg/XQhQ6go30aR6wrFHOydXbYE4ocChRSerdgyZhnAv1jqoVp7UKvg+4jQLU5RFAPG7HDGN8UWPa4lIQcj1NsyPRTQVBxA==</data>
+ </certificate>
+ <certificate>
+  <data>MIICajCCAdOgAwIBAgIBADANBgkqhkiG9w0BAQQFADByMQswCQYDVQQGEwJCUjEuMCwGA1UEChMlQ2VydGlzaWduIENlcnRpZmljYWRvcmEgRGlnaXRhbCBMdGRhLjEzMDEGA1UECxMqQ2VydGlzaWduIC0gQXV0b3JpZGFkZSBDZXJ0aWZpY2Fkb3JhIC0gQUM0MB4XDTk5MDYyNzAwMDAwMFoXDTE4MDYyNzAwMDAwMFowcjELMAkGA1UEBhMCQlIxLjAsBgNVBAoTJUNlcnRpc2lnbiBDZXJ0aWZpY2Fkb3JhIERpZ2l0YWwgTHRkYS4xMzAxBgNVBAsTKkNlcnRpc2lnbiAtIEF1dG9yaWRhZGUgQ2VydGlmaWNhZG9yYSAtIEFDNDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA7IPUzIOQP1/svsHp0MdYRlkf8ZNKshcTC88pmasERTPj0V6+vI4ld2Ldm5KbMQpARXnV8rPdbLlA/Zh/QKIt6bKTvUvVwd7p0fpx9z9BvPQUr81ryDlKnWBltITQakHpY66baeq5RhzbGAg7YkzeQdeehn8TchHU3OreYKW73RsCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQApXY3251O97KOBFkGxHvgkPaW3Dj7zf/uOyJ6jVucUQd8uJd1O7RJA39lCGcQwmJSZUoojwGm1moJ5Q9yJBB8yv2uNvihzKUyj+1TNY/tfVGtSFRig6pXHDwmp74++q2q81qgGpOLSfePyThC5wDwuJaHyk7qhjbavh8aZ0z6hHw==</data>
+ </certificate>
+ <certificate>
+  <data>MIICQDCCAakCEAPHjzfbkijfPLsarYL6ZxAwDQYJKoZIhvcNAQECBQAwYTERMA8GA1UEBxMISW50ZXJuZXQxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTMwMQYDVQQLEypWZXJpU2lnbiBDb21tZXJjaWFsIFNvZnR3YXJlIFB1Ymxpc2hlcnMgQ0EwHhcNOTYwNDA5MDAwMDAwWhcNMDQwMTA3MjM1OTU5WjBhMREwDwYDVQQHEwhJbnRlcm5ldDEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xMzAxBgNVBAsTKlZlcmlTaWduIENvbW1lcmNpYWwgU29mdHdhcmUgUHVibGlzaGVycyBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAw9NpZVIBlFSrKMZiGLNUVcVEh0VKO8J+2NPXyICGjdgM8RaczGupKbKPdnOSyMVipjztHgV18BMAbBRN1JiQB75pc4G4Yk4xHtH8yQzrfZC/rrRHUexvzmQ1AtZ9ZwV34o/ZUdf7lxm8Ptd3gcZD3fLd38qjg4vLQcE9IkhIphkCAwEAATANBgkqhkiG9w0BAQIFAAOBgQC1vLB1aomihr1keMOnMnVyEaomAhdgMEzjSDQZuVJKURiA/lMte9UxjMVlmUFBL/KuY3roc5kVkBofeotB0I460M04NETQdfjqccSBGTgXNUquxT4y5iG4BcCT4cc4XNj3kzhkkO1UzsrT09Bf7wSb3gKC3YgpscNPpc1xZDE8PA==</data>
+ </certificate>
+ <certificate>
+  <data>MIICNTCCAZ4CBQK0AAABMA0GCSqGSIb3DQEBAgUAMGExETAPBgNVBAcTCEludGVybmV0MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEzMDEGA1UECxMqVmVyaVNpZ24gQ29tbWVyY2lhbCBTb2Z0d2FyZSBQdWJsaXNoZXJzIENBMB4XDTk2MDQwOTA5MzU1OVoXDTk5MTIzMTA5MzU1OFowYTERMA8GA1UEBxMISW50ZXJuZXQxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTMwMQYDVQQLEypWZXJpU2lnbiBDb21tZXJjaWFsIFNvZnR3YXJlIFB1Ymxpc2hlcnMgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPTaWVSAZRUqyjGYhizVFXFRIdFSjvCftjT18iAho3YDPEWnMxrqSmyj3ZzksjFYqY87R4FdfATAGwUTdSYkAe+aXOBuGJOMR7R/MkM632Qv660R1Hsb85kNQLWfWcFd+KP2VHX+5cZvD7Xd4HGQ93y3d/Ko4OLy0HBPSJISKYZAgMBAAEwDQYJKoZIhvcNAQECBQADgYEAMbswxW+nviMmbaWZdmjFKgMoS/OJsJkDMluUoXvByBnX9JVsrHMkCstEBX147vr2p5+HpH/o80tPMjAwFQgXAbKA/KHZJIelAF/N3SnIoaXKWHU5YEUf3o3WVwjTwBuBwtniAIzsCpECxp02dJqDa+98jNKlKmrJftupvSsi/xw=</data>
+ </certificate>
+ <certificate>
+  <data>MIICQTCCAaoCEQDK9sH1cOLpA2tz/AaSHCrxMA0GCSqGSIb3DQEBAgUAMGExETAPBgNVBAcTCEludGVybmV0MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEzMDEGA1UECxMqVmVyaVNpZ24gSW5kaXZpZHVhbCBTb2Z0d2FyZSBQdWJsaXNoZXJzIENBMB4XDTk2MDQwOTAwMDAwMFoXDTA0MDEwNzIzNTk1OVowYTERMA8GA1UEBxMISW50ZXJuZXQxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTMwMQYDVQQLEypWZXJpU2lnbiBJbmRpdmlkdWFsIFNvZnR3YXJlIFB1Ymxpc2hlcnMgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMF6ymWnLdUPT2xHMvhzd4blPfJveZC13k/SFHkzS7IORv2IcirC/GSeYY9rwFvo8BEE2nq6cvbsXa/78ZfxFNIo4yiFXHu9iqJ/wjO1bQuSeAs4cXSFfz26uS/vvidIDTgiyVYwjXf6XSxanJfOcDDpUV+mi+SVlqWgF3fxk7gpAgMBAAEwDQYJKoZIhvcNAQECBQADgYEAwI+f138OJvdH/WV7Y5IGRszgXkuBWLOX7AUYExXWlxAqf/hWrdgOer8nSEUHnyUirHASBmk3gFhY2Io0WgfGJ9rG+56xfWQggE/0oRV7GWGvxqvAJmX+jAeHPxrEjrJARBB+LGF6/q2jyL7MvV6qr6zFvxFnWCoy8yXdtnRDwHw=</data>
+ </certificate>
+ <certificate>
+  <data>MIICNTCCAZ4CBQK1AAACMA0GCSqGSIb3DQEBAgUAMGExETAPBgNVBAcTCEludGVybmV0MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEzMDEGA1UECxMqVmVyaVNpZ24gSW5kaXZpZHVhbCBTb2Z0d2FyZSBQdWJsaXNoZXJzIENBMB4XDTk2MDQwOTA5Mzc0OVoXDTk5MTIzMTA5Mzc0OFowYTERMA8GA1UEBxMISW50ZXJuZXQxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTMwMQYDVQQLEypWZXJpU2lnbiBJbmRpdmlkdWFsIFNvZnR3YXJlIFB1Ymxpc2hlcnMgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMF6ymWnLdUPT2xHMvhzd4blPfJveZC13k/SFHkzS7IORv2IcirC/GSeYY9rwFvo8BEE2nq6cvbsXa/78ZfxFNIo4yiFXHu9iqJ/wjO1bQuSeAs4cXSFfz26uS/vvidIDTgiyVYwjXf6XSxanJfOcDDpUV+mi+SVlqWgF3fxk7gpAgMBAAEwDQYJKoZIhvcNAQECBQADgYEAiNZWQ5sz3FzRQ+TxOs8REzlLLgYcaIGAypzJ/JMWmgUPd3JdbI2GrwqXwjKSx1z5EAPkhECvSWvhcfpljd2yOP0Frm7I4moxePcNSkrvGDutKTjO+GK93ZwjupveI1fJUj54+7N4ZGpFsmh+0bKZYera5OMwN0Vk5+o7WjrIFOM=</data>
+ </certificate>
+ <certificate>
+  <data>MIICPDCCAaUCEDJQM89Q0VbzXIGtZVxPyCUwDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTIwMDEwNzIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlGb9to1ZhLZlIcfZn3rmN67eehoAKkQ76OCWvRoiC5XOooJskXQ0fzGVuDLDQVoQYh5oGmxChc9+0WDlrbsH2FdWoqD+qEgaNMax/sDTXjzRniAnNFBHiTkVWaR94AoDa3EeRKbs2yWNcxeDXLYd7obcysHswuiovMaruo2fa2wIDAQABMA0GCSqGSIb3DQEBAgUAA4GBAEtEZmBoZOSYG/OwcuaViXzde7OVwB0u2NgZ0C00PcZQmhCGjKo/O6gE/DdSlcPZydvN8oYGxLEb8IKIMEKOF1AcZHq4PplJdJf8rAJD+5YMVgQlDHx8h50kp9jwMim1pN9dokzFFjKoQvZFprY2ueC/ZTaTwtLXa9zeWdaiNfhF</data>
+ </certificate>
+ <certificate>
+  <data>MIICPTCCAaYCEQDNun9W8N/kvFT+IqyzcqpVMA0GCSqGSIb3DQEBAgUAMF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05NjAxMjkwMDAwMDBaFw0yODA4MDEyMzU5NTlaMF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Rm/baNWYS2ZSHH2Z965jeu3noaACpEO+jglr0aIguVzqKCbJF0NH8xlbgyw0FaEGIeaBpsQoXPftFg5a27B9hXVqKg/qhIGjTGsf7A01480Z4gJzRQR4k5FVmkfeAKA2txHkSm7NsljXMXg1y2He6G3MrB7MLoqLzGq7qNn2tsCAwEAATANBgkqhkiG9w0BAQIFAAOBgQBMP7iLxmjf7kMzDl3ppssHhE16M/+SG/Q2rdiVIjZoEWx8QszznC7EBz8UsA9P/5CSdvnivErpj82ggAr3xSnxgiJduLHdgSOjeyUVRjB5FvjqBUuUfx3CHMjjt/QQQDwTw18fU+hI5Ia0e6E1sHslurjTjqs/OJ0ANACY89FxlA==</data>
+ </certificate>
+ <certificate>
+  <data>MIICPDCCAaUCEC0b/EoXjaOR6+f/9YtFvgswDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2WoujDWojg4BrzzmH9CETMwZMJaLtVRKXxaeAufqDwSCg+i8VDXyhYGt+eSz6Bg86rvYbb7HS/y8oUl+DfUvEerf4Zh+AVPy3wo5ZShRXRtGak75BkQO7FYCTXOvnzAhsPz6zSvz/S2wj1VCCJkQZjiPDceoZJEcEnnW/yKYAHwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBAIobK/o5wXTXXtgZZKJYSi034DNHD6zt96rbHuSLBlxgJ8pFUs4W7z8GZOeUaHxgMxURaa+dYo2jA1Rrpr7l7gUYYAS/QoD90KioHgE796Ncr6Pc5iaAIzy4RHT3Cq5Ji2F4zCS/iIqnDupzGUH9TQPwiNHleI2lKk/2lw0Xd8rY</data>
+ </certificate>
+ <certificate>
+  <data>MIICPTCCAaYCEQC6WslMBTuS1qe2307QU5INMA0GCSqGSIb3DQEBAgUAMF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05NjAxMjkwMDAwMDBaFw0wNDAxMDcyMzU5NTlaMF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtlqLow1qI4OAa885h/QhEzMGTCWi7VUSl8WngLn6g8EgoPovFQ18oWBrfnks+gYPOq72G2+x0v8vKFJfg31LxHq3+GYfgFT8t8KOWUoUV0bRmpO+QZEDuxWAk1zr58wIbD8+s0r8/0tsI9VQgiZEGY4jw3HqGSRHBJ51v8imAB8CAwEAATANBgkqhkiG9w0BAQIFAAOBgQC2AB+TV6QHp0DOZUA/VV7t7/pUSaUw1iF8YYfug5MLv7Qz8pisnwa/TqjOFIFMywROWMPPX+5815pvy0GKt3+BuP+EYcYnQ2UdDOyxAArdG6S7x3ggKLKi3TaVLuFUT79guXdoEZkj6OpS6KoATmdOu5C1RZtG644W78QzWzM91Q==</data>
+ </certificate>
+ <certificate>
+  <data>MIICPTCCAaYCEQDknv3zOugOz6URPhmkJAIyMA0GCSqGSIb3DQEBAgUAMF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05NjAxMjkwMDAwMDBaFw0wNDAxMDcyMzU5NTlaMF8xCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyVxZnvIbigEUtBDfBEDb41evakVAj4QMC9Ez2dkRz+4CWB8l9yqoRAWq7AMfeH+ek7maAKojfdashaJjRcdyJ8z0TMZ1cdI5709C8HXfCpDGjiBvmA/4rCNfcCk2pMmG57GaIMtTpYXnPb59mv4kRTPcdhXtD6JxZExlLoFoRacCAwEAATANBgkqhkiG9w0BAQIFAAOBgQBhcOwvP579K+ZoVCGwZ3kIDCCWMYoNer62Jt95LCJpSTbjl3diYaIy13pUITa6Ask05yXaRDWw0lyAXbOU+Pms7qRgdSoflUkjsUp89LNHciFbfperVKxi513srpvSybIk+4Kt6WcVS7qqpvCXoPawl1cAyAw8CaCCBLpB2veZpA==</data>
+ </certificate>
+ <certificate>
+  <data>MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k</data>
+ </certificate>
+ <certificate>
+  <data>MIIEQTCCAymgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBoTELMAkGA1UEBhMCTVgxVzBVBgNVBAMTTkF1dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhIGRlIGxhIEFzb2NpYWNpb24gTmFjaW9uYWwgZGVsIE5vdGFyaWFkbyBNZXhpY2FubywgQS5DLjE5MDcGA1UEChMwQXNvY2lhY2lvbiBOYWNpb25hbCBkZWwgTm90YXJpYWRvIE1leGljYW5vLCBBLkMuMB4XDTk5MDYyODE4NTMwMFoXDTA5MDYyODE4NTMwMFowgaExCzAJBgNVBAYTAk1YMVcwVQYDVQQDE05BdXRvcmlkYWQgQ2VydGlmaWNhZG9yYSBkZSBsYSBBc29jaWFjaW9uIE5hY2lvbmFsIGRlbCBOb3RhcmlhZG8gTWV4aWNhbm8sIEEuQy4xOTA3BgNVBAoTMEFzb2NpYWNpb24gTmFjaW9uYWwgZGVsIE5vdGFyaWFkbyBNZXhpY2FubywgQS5DLjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO7Za1WEcSb2jq1Bu9bS3mPq8j9l8jsTwa5RJvS/eI/GTEmsv0He9SjHKOef14Dl1O4jK5OVPcmPavXKAOkQx4iCGm6jX/A0UiElaCj4lfCAlaIssT9q9ijW4oT9FmecJbl12+XGWQO0q0IHiNVcSEWiCQPJrmeSqKJTx8qrfpoSlVoFV40QZJYnEbDmMjBbzT0MoBsTLbsaq905zpJrogTUE19td2dUOah7aYrGX3jKBxhpbvnA09CKjq8eMGju+NioZgyEfSNqiTxiTRR0rNubnPywg/4HMPH2nLRIyOY6QiUJKPdWwrHXo5h3AofqisFd2sSKJweFXkhMVShA7QkCAwEAAaOBgTB/MEwGA1UdIARFMEMwQQYLYIZIAYb4RQEHAQEwMjAwBggrBgEFBQcCARYkd3d3Lk5vdGFyaWFkb01leGljYW5vLm9yZy5teC9SQ0QvZHBjMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEAdbdMMa8JkYWt+CbEUydF67H9kFGgUzxvTtHHbcXoAIFEVymjZQrCCHwzsrcs9yOzbA0kRzreeAQUcwFbaL+TwnZw1qwhh9mwmmesVrs2EddBgZFlDAoZEHtxtMVEVkwRPDL9fvSWslSRJ2tT6QFnYzjOQZ8Nr1MsQjYgXg2lkHYb6C2LSfACIsoYU00BEqoLOmKZEGZxPsm0mKDAN7Uj0IMXdBYIZj30gtuN51ow+mm0Wl/lveyeG6zlIubGmL5roF5aj/Z/VxW2mBhCeZuuzOMReo/vNKgn/SNeZDeqcoJYT2WJRlFObc9O9Bpez2pRo/QtrgjtbMKhqX56iV9e4g==</data>
+ </certificate>
+ <certificate>
+  <data>MIIEUTCCAzmgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UEBhMCTVgxWjBYBgNVBAMTUUF1dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhIGRlbCBDb2xlZ2lvIE5hY2lvbmFsIGRlIENvcnJlZHVyaWEgUHVibGljYSBNZXhpY2FuYSwgQS5DLjE+MDwGA1UEChM1Q29sZWdpbyBOYWNpb25hbCBkZSBDb3JyZWR1cmlhIFB1YmxpY2EgTWV4aWNhbmEsIEEuQy4wHhcNOTkwNjI5MTg1OTAwWhcNMDkwNjI5MTg1OTAwWjCBqTELMAkGA1UEBhMCTVgxWjBYBgNVBAMTUUF1dG9yaWRhZCBDZXJ0aWZpY2Fkb3JhIGRlbCBDb2xlZ2lvIE5hY2lvbmFsIGRlIENvcnJlZHVyaWEgUHVibGljYSBNZXhpY2FuYSwgQS5DLjE+MDwGA1UEChM1Q29sZWdpbyBOYWNpb25hbCBkZSBDb3JyZWR1cmlhIFB1YmxpY2EgTWV4aWNhbmEsIEEuQy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCY7R2Fgfv+N3S+I4AnXxtBfrznpYrYc12WSOqrWKp9pR3P/iAK2fJ1Ce2z1f34kyoZqV6osRTy1kKGQUFeT8VWp5qiQm8uKEjqLqvZwdma8KWei10RZ5o3N1NgZvEi/3d3R+nd3BhG7i7f4sGW4OpY3ofrk4hpmbA+bnYpQGHDKGZzf6wxdvQ4zdYyj3MyEBU00nxE0Px5DACdfQ/j4/dE9Z50SSetG9cyyFOubyqw17UtGCxuOzpNTNv1aLUvsAyQ+T3cdTaawuC/u5aIwmvuGNdOUD9Xi2dopL4DM11ZEjSJ/nbI2RV2AVRk/azjUO8QyNDuX+wd308FmDy0FF4BAgMBAAGjgYEwfzBMBgNVHSAERTBDMEEGC2CGSAGG+EUBBwEBMDIwMAYIKwYBBQUHAgEWJHd3dy5Db3JyZWR1cmlhUHVibGljYS5vcmcubXgvUkNEL2RwYzAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIABjARBglghkgBhvhCAQEEBAMCAAcwDQYJKoZIhvcNAQEFBQADggEBAD03xIEgZfSIcQZuAsQFHDMYOZp4RrC/vcOTflm0HZYvdIVdfLTVk3NYqAxxe/5eo6vBxuc+ngsoqlcVTOJhtxPqZc4b7tKx/ZGJau2g4E/QLYevYZZhiFNuF1kCtXnhH7gOA0acMmZV2htjuUMzXphpI03QygJ+qnavPuinx88YB2yD8NRfWM+nPhfBFqmqPknAyw2VV6vU45XHt34Aet9iml3f7VS+O9QITevk+Gun92ohquRidcm11z3mbiSc/ZUDoWN0HC9fkJVHGyr0tz0ZHLVhj9c7TbfsNsatp3qccK4tNsAwn+JVe+8CY64rPeRClElhUCDC1qgdpVNtgmQ=</data>
+ </certificate>
+ <certificate>
+  <data>MIIEJzCCA5CgAwIBAgIEN35hxjANBgkqhkiG9w0BAQQFADCBgzELMAkGA1UEBhMCVVMxLTArBgNVBAoTJEZpcnN0IERhdGEgRGlnaXRhbCBDZXJ0aWZpY2F0ZXMgSW5jLjFFMEMGA1UEAxM8Rmlyc3QgRGF0YSBEaWdpdGFsIENlcnRpZmljYXRlcyBJbmMuIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk5MDcwMzE4NDczNFoXDTE5MDcwMzE5MTczNFowgYMxCzAJBgNVBAYTAlVTMS0wKwYDVQQKEyRGaXJzdCBEYXRhIERpZ2l0YWwgQ2VydGlmaWNhdGVzIEluYy4xRTBDBgNVBAMTPEZpcnN0IERhdGEgRGlnaXRhbCBDZXJ0aWZpY2F0ZXMgSW5jLiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCBnTANBgkqhkiG9w0BAQEFAAOBiwAwgYcCgYEA3xwUHgm5v6RAciCZebaEIvTXhZLFBCToBy4C5BeVBTeVdj38seUPhw5iuSwwlybhCxVnAKYV3uiNy5XsAlhSwEdlM0xWnwofBMA3UIFXut/68mtn68vQgA/ZV5UQZXsGRVjrrrRe45MVK5m8tikv+0KfRysuTOs0KDKZDu//b6ECAQOjggGmMIIBojARBglghkgBhvhCAQEEBAMCAAcwgawGA1UdHwSBpDCBoTCBnqCBm6CBmKSBlTCBkjELMAkGA1UEBhMCVVMxLTArBgNVBAoTJEZpcnN0IERhdGEgRGlnaXRhbCBDZXJ0aWZpY2F0ZXMgSW5jLjFFMEMGA1UEAxM8Rmlyc3QgRGF0YSBEaWdpdGFsIENlcnRpZmljYXRlcyBJbmMuIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCsGA1UdEAQkMCKADzE5OTkwNzAzMTg0NzM0WoEPMjAxOTA3MDMxODQ3MzRaMAsGA1UdDwQEAwIBBjAfBgNVHSMEGDAWgBSmuCDJFkuPT1wMw8PumA0+fu5WVTAdBgNVHQ4EFgQUprggyRZLj09cDMPD7pgNPn7uVlUwDAYDVR0TBAUwAwEB/zA7BgNVHSUENDAyBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEEBQADgYEAEObEaCOpbLeXSbFzNp3+v3KiDhLCKlEGH2mTlDARNYVOqHkG43FVPBxWYx5Ee2qBwjB1bN7z8gzDTsp/ycbAX1/vxAZiqk/6EN4yzOAu/2rixcdFKXU5+YxZC8ZrmQSYWsy6v7F4ApGqtoeAO1cUWzz8zAPKhqGZqDpta2V+Ubg=</data>
+ </certificate>
+ <certificate>
+  <data>MIIDFzCCAoCgAwIBAgIENuetnDANBgkqhkiG9w0BAQUFADBAMQswCQYDVQQGEwJHQjEQMA4GA1UEChMHVmlhQ29kZTEQMA4GA1UECxMHQ0EgRGF0YTENMAsGA1UECxMEQ0EgMTAeFw05OTAzMTExMTE4NDhaFw0xOTAzMTExMTQ4NDhaMEAxCzAJBgNVBAYTAkdCMRAwDgYDVQQKEwdWaWFDb2RlMRAwDgYDVQQLEwdDQSBEYXRhMQ0wCwYDVQQLEwRDQSAxMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCws2enlrV2g+kWA9kaoHbVdOecBdJRP9tPGaGoOU7LJH1hxB5qIK4Pgd7quDn9Gx9rNkDtTSEl8qPZoVHYbMAblvjUQpTUp84bj9NUJqKE7zKFr0o/8TI2rz3mOifrA8IlfvRhK62KGkvmmzZo1C/l0oiU3Baq2sIVTGzD4RmRyQIBA6OCAR4wggEaMBEGCWCGSAGG+EIBAQQEAwIABzBiBgNVHR8EWzBZMFegVaBTpFEwTzELMAkGA1UEBhMCR0IxEDAOBgNVBAoTB1ZpYUNvZGUxEDAOBgNVBAsTB0NBIERhdGExDTALBgNVBAsTBENBIDExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5OTAzMTExMTE4NDhagQ8yMDE5MDMxMTExMTg0OFowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFCiHsaeIf9bdy2+hHA5a0C4VMZ3MMB0GA1UdDgQWBBQoh7GniH/W3ctvoRwOWtAuFTGdzDAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3DQEBBQUAA4GBAFPf0unw7uPaAGeP6hVHdh/gtaSgRwLCs9i0StT7rByBuSMgY2LYVG8JddY65jCrsXO8FKT+RC9SZNAWYy05tuk47mUzkNGBGIAFZyNvPB1CfNHx84ag+stWbL4b9TKcYZO73+3AT8HR5UXO40QViNfMw/yrGJZR9OCofMHLmLzB</data>
+ </certificate>
+ <certificate>
+  <data>MIIDdTCCAl2gAwIBAgILAgAAAAAA1ni3lAUwDQYJKoZIhvcNAQEEBQAwVzELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0xNDAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQUYHtmGkUNl8qJUC99BM00qP/8/UswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOCAQEArqqf/LfSyx9fOSkoGJ40yWxPbxrwZKJwSk8ThptgKJ7ogUmYfQq75bCdPTbbjwVR/wkxKh/diXeeDy5slQTthsu0AD+EAk2AaioteAuubyuig0SDH81Qgkwkr733pbTIWg/050deSY43lv6aiAU62cDbKYfmGZZHpzqmjIs8d/5GY6dT2iHRrH5Jokvmw2dZL7OKDrssvamqQnw1wdh/1acxOk5jQzmvCLBhNIzTmKlDNPYPhyk7ncJWWJh3w/cbrPad+D6qp1RF8PX51TFl/mtYnHGzHtdS6jIX/EBgHcl5JLL2bP2oZg6C3ZjL2sJETy6ge/L3ayx2EYRGinij4w==</data>
+ </certificate>
+ <certificate>
+  <data>MIIGujCCBaKgAwIBAgIEOaKT4zANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJJRTEQMA4GA1UEChMHQW4gUG9zdDEYMBYGA1UECxMPUG9zdC5UcnVzdCBMdGQuMRswGQYDVQQDExJQb3N0LlRydXN0IFJvb3QgQ0EwHhcNMDAwODIyMTQ1NjIxWhcNMTAwODIwMTM1NjIxWjBWMQswCQYDVQQGEwJJRTEQMA4GA1UEChMHQW4gUG9zdDEYMBYGA1UECxMPUG9zdC5UcnVzdCBMdGQuMRswGQYDVQQDExJQb3N0LlRydXN0IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWfxPkDSTY2PvptsOxUqNO3Lc74vcOX4HmAfCm5jZw2BNzof9SCJDyFbzyMhyo1+EGEHtBqJTHPz1EFdyAUecw/lNI9ZBfbW0l4GC7oPvWxin4TXBQGjegDs14KrZo85nUDI5eEmL/wl9amiuIR6q7odl18Cxc/m3CsVsxzIOHGqe+kA7oEFRTUgucSfOAAv5vJ6ajVzC427jQAAQD5OXUQwbrq2hNaCbSmTS54BKWf0u7a24hceYIRUodZg03yQPh0crcsS9w9reBYpYbZKf+u+GG60GfqJzYyqDh1i/4D+G6QDj3bdGlykwZyJbczEkcCrbTqJE6+drsHc1zSiQ/AgMBAAGjggOOMIIDijAPBgNVHRMBAf8EBTADAQH/MIIDTQYDVR0gBIIDRDCCA0AwggM8BgcqgnS76CIBMIIDLzCCAvQGCCsGAQUFBwICMIIC5hqCAuJUaGlzIGNlcnRpZmljYXRlIGlzIGlzc3VlZCBzdWJqZWN0IHRvIHRoZSBQb3N0LlRydXN0IENQUyB0ZXJtcyBhbmQgY29uZGl0aW9ucyB3aGljaCBvdXRsaW5lIHRoZSB3YXJyYW50aWVzIHByb3ZpZGVkIHdpdGggdGhpcyBjZXJ0aWZpY2F0ZSBhbmQgdGhlIGV4dGVudCBvZiB0aGUgbGlhYmlsaXR5IG9mIFBvc3QuVHJ1c3QgTGltaXRlZC4gIFBvc3QuVHJ1c3QgTGltaXRlZCBleHByZXNzbHkgZXhjbHVkZXMgY2VydGFpbiBleHByZXNzIGFuZCBpbXBsaWVkIHdhcnJhbnRpZXMgYW5kIGNvbmRpdGlvbnMsIGluY2x1ZGluZyB3YXJyYW50aWVzIGFzIHRvIGZpdG5lc3MgZm9yIHB1cnBvc2Ugb3IgbWVyY2hhbnRhYmlsaXR5LCBhbmQgd2lsbCBub3QgYmUgbGlhYmxlIGZvciBpbmRpcmVjdCBhbmQgY29uc2VxdWVudGlhbCB0eXBlIGxvc3Nlcywgb3IgZm9yIGxvc3MgaW4gZXhjZXNzIG9mIGEgc3BlY2lmaWVkIGFtb3VudC4gIEFjY29yZGluZ2x5LCB0aGUgQ1BTIHRlcm1zIGFuZCBjb25kaXRpb25zIG11c3QgYmUgcmVhZCBiZWZvcmUgYW55IHJlbGlhbmNlIGNhbiBiZSBwbGFjZWQgb24gdGhpcyBjZXJ0aWZpY2F0ZSwgYW5kIGJ5IGFjY2VwdGluZyBvbiB0aGlzIGNlcnRpZmljYXRlLCBhIHN1YnNjcmliZXIgYW5kL29yIHJlbHlpbmcgcGFydHkgaXMgYWNrbm93bGVkZ2luZyB0aGF0IGl0IGhhcyByZWFkLCB1bmRlcnN0b29kIGFuZCBhY2NlcHRlZCB0aGUgQ1BTIHRlcm1zIGFuZCBjb25kaXRpb25zLiAwNQYIKwYBBQUHAgEWKWh0dHA6Ly93d3cucG9zdC50cnVzdC5pZS9yZXBvc2l0L2Nwcy5odG1sMBEGA1UdDgQKBAhBGozjTz3v8TATBgNVHSMEDDAKgAhBGozjTz3v8TANBgkqhkiG9w0BAQUFAAOCAQEAZ42p46/48eFtLPvXDWJLrvBYO74yQ1Bb8kOOPhvwNzUSkDw2AR3/KfQIpn7usR5RuLkLXOu34g30ijD4qDUYC8eOc+oGmSsHJQcPc0v/uiRbhPzOlDZS+5Cceo2LoIh6RvyaKFjLjatU9O14Ovc702fjj5CDmaIIUoIWtYFcmwgx/OXWeK6A8UwCY2yOBJy5e5nxHDOhr3tDUYUHrt8uxQn8hKX+VbwK5Hhcptj3O48Kx275n44QD0IE/FdB/lI/sTGUb/Bt1E4F3Z4rAF/BEn5CZKdhFjFPfL4OyOMQ+y73FWWnVMpdpMwszHgAvQHXzd/Q2zPdiacU16IhvDXWDg==</data>
+ </certificate>
+ <certificate>
+  <data>MIIEAjCCAuqgAwIBAgIEN4JtSDANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRwwGgYDVQQDExNCYWx0aW1vcmUgRVogYnkgRFNUMSIwIAYKCZImiZPyLGQBAxQSY2FAZGlnc2lndHJ1c3QuY29tMB4XDTk5MDcwNjIwNTY1M1oXDTA5MDcwMzE5NTY1M1owdTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEcMBoGA1UEAxMTQmFsdGltb3JlIEVaIGJ5IERTVDEiMCAGCgmSJomT8ixkAQMUEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALzMsz1DebhH+RW3biaxTJKYee3FcPM66dXdA/QqP2CMluVHFgL3NrLxniaI9Z5CZZ4Prn/jzLFuIfXxiCJ01h+ydwQ5MrpHtTXAE10BcqMFQ8xO2iaah6hhY/Rmp8MVRurR0eoww7txapvBQ4bBOR/GVmACG/yIOqCZu4zXeGHQXlePkTuAi27/yWWBCjm7kBMgNUg/peG/LcFxg4Ug7SCHy7b+GYwE3e0pXd8KEs6JVvIfWhkxWLb9YzbqySPep5GXJ2VymWzHekTEX76bHx7KnlgcpqojauMGG5Ik2FciCVAVkEbwg162gEwSgT1mbwRVOohr0O9Wh1CgudZVG50CAwEAAaOBmTCBljAPBgNVHRMBAf8EBTADAQH/MFsGA1UdIARUMFIwUAYJYIZIAYb5LwAAMEMwQQYIKwYBBQUHAgEWNWh0dHA6Ly93d3cuZGlnc2lndHJ1c3QuY29tL0RTVF9UUlVTVF9DUFNfdjk5MDcwMS5odG1sMBEGA1UdDgQKBAhM12LOiU7eJDATBgNVHSMEDDAKgAhM12LOiU7eJDANBgkqhkiG9w0BAQUFAAOCAQEAfAKJFDhfgxvmxlhb80zCHPbbJ3ct3M4bE1AvEkeKypyzAmcnxB8y3+DCVESgJRjjL/SsLWbkzIdeqwl905Sp1X1ZSKvuKXwIewc43SuyLx7gzeeXEP/GCAZdP4H0Nv7GDg3Ba3pQnec3XKBZR12sGJU/hZ7cc1gEAKRM5CG37pC6KvxY4wmwcBe0qmaj+ok59ANDgyICQ3Sz3bXJBimWx7mTOF/gC2XifHwZw1Vz6MwOBggIyaQFcVEqLH0DFw7MjuBdotB3d3KxZht0je8Ic1NYvSDb+RsBohDRkv7DsqV0ci7m7D38Wi+oaYa7bkDhZleEI4mvMWTnj+F6ZaALhg==</data>
+ </certificate>
+ <certificate>
+  <data>MIICTDCCAbUCAgD9MA0GCSqGSIb3DQEBBAUAMG4xCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEcMBoGA1UEAxMTR1RFIEN5YmVyVHJ1c3QgUm9vdDAeFw05ODA0MDMxNDUyMDFaFw0wNDA0MDMyMzU5MDBaMG4xCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEcMBoGA1UEAxMTR1RFIEN5YmVyVHJ1c3QgUm9vdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuo69dZAS+uV5dLLZ9ybUDkEd6TYHVnLVuGnaVNSx1nhtquM7PaOENyXHr68E3LMiseltvoJt640vvbSucB6i1m4gMVHmVlxzUrIjUE68+5pnQMSGEUbHQyK66gBPbHLNYZzey/ruMNLm+ludxZncIPg5v2SMB1Y7vdxsXva/4dkCAwEAATANBgkqhkiG9w0BAQQFAAOBgQCWXx08xnWVtmuvhwDNpUEri7/QjTFbOWXT8dGPWB5SIsM9hvLCmvbdSF5xw0UcfEyiWe6hUZY999U0JDqq/YImZcVMQg8Mj2lgfOfW1RpLefruWrVMpsyja0bW0cgyVKAwZ9JdhhGRvMO6KpzVeFEa1F3ddpEiRWsxWT75hVlOyw==</data>
+ </certificate>
+ <certificate>
+  <data>MIICyDCCAjGgAwIBAgIBMDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJJVDETMBEGA1UEChMKU0lBIFMucC5BLjEPMA0GA1UEBxMGTWlsYW5vMR0wGwYDVQQDExRTSUEgU2VjdXJlIENsaWVudCBDQTAeFw05OTA3MDgxMjQ1MDBaFw0xOTA3MDgyMzU5MDBaMFIxCzAJBgNVBAYTAklUMRMwEQYDVQQKEwpTSUEgUy5wLkEuMQ8wDQYDVQQHEwZNaWxhbm8xHTAbBgNVBAMTFFNJQSBTZWN1cmUgQ2xpZW50IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDS/LBAYGpmY1Jm5mkJcY2BmB4dHfPgSQ3IK2/Qd1FFxZ1uo1xw3hV4Fh5f4MJi9H0yQ3cI19/S9X83glLGfpOd8U1naMIvwiWIHXHm2ArQeORRQjlVBvOAYv6WpW3FRsdB5QASm2bB4o2VPtXHDFj3yGCknHhxlYzeegm/HNX8owIDAQABo4GtMIGqMDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9jYS5zaWEuaXQvc2VjY2xpL3JlcG9zaXRvcnkvQ1JMLmRlcjBKBgNVHSAEQzBBMD8GBiuBBwEBAzA1MDMGCCsGAQUFBwIBFidodHRwczovL2NhLnNpYS5pdC9zZWNjbGkvcmVwb3NpdG9yeS9DUFMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADgYEAgwYCx20JM9pE1pHpf5g617koCBnXohH9HxL+hEoLc6ez1NtDer4kFKPWAIo6LRy4rYv815q2GEHk2SVW74HIFKRJDF13cBYFZyjhnt8a962rLlbu1yId7eh3Yb/cZ3AhkoN2dLHpA3KwKIh8wGTMxqw3Ea5NbH8xQGGQt9VSyEM=</data>
+ </certificate>
+ <certificate>
+  <data>MIIDzTCCArWgAwIBAgIBMTANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJJVDETMBEGA1UEChMKU0lBIFMucC5BLjEPMA0GA1UEBxMGTWlsYW5vMR0wGwYDVQQDExRTSUEgU2VjdXJlIFNlcnZlciBDQTAeFw05OTA3MDgxMzEzMDBaFw0xOTA3MDgyMzU5MDBaMFIxCzAJBgNVBAYTAklUMRMwEQYDVQQKEwpTSUEgUy5wLkEuMQ8wDQYDVQQHEwZNaWxhbm8xHTAbBgNVBAMTFFNJQSBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA28ELzCfTEiIuuWQWdKxZJ+IqkACSntWYXCtRbhsTb1RvShCihethC+ztnH7Of2WTbsxsQZzILarGs5v7THCcEXXzcom6iQCtxy5J53PagLIs/vKXmfQCGzQvOaqL5u8F/Ln1ulR/ob+OHkg2Mwl0Yac9x5skx8OJzcpOKDEjBhxiFY7fTxtrLUri9LDczvOQ/XmBE8E+Lma8+SJNCy9iM42oK+rpb3OnN5QEL+leTQ3p7XwyP3lK5jp2KSBQ84+CRHJsMDRIWKpdGz8B6yHs6n6oK4Rd9sExlU8pe7U1t/60BlewFNfyVVmMupu5MT/lqqrvJXCVkjZB8VWfwQhEnQIDAQABo4GtMIGqMDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9jYS5zaWEuaXQvc2Vjc3J2L3JlcG9zaXRvcnkvQ1JMLmRlcjBKBgNVHSAEQzBBMD8GBiuBBwEBAjA1MDMGCCsGAQUFBwIBFidodHRwczovL2NhLnNpYS5pdC9zZWNzcnYvcmVwb3NpdG9yeS9DUFMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAELhiyLoXBNRRduRw5CUv9Uh8RDgQjq3rmIlg+zh5caYbM7WbdbYCi74ug9utb7BjTJGkYiYcxXJqulbwPG4O4JUaTtRGPNtWUVkhqAiy7T+47VZGABuFoMy/KfT4tMzs6ufphZo3fH1rxfT4uwIMHudvCCqt2JmH8d6YA3vAB+NeOB4BJRgeF1aP6TagT1fZBaCL6nwOJ/bNG9YgCIqXpNTlnRPvoM3MjmuNbC71M9IaB7KxOxtuNWhCOz7CfnDuTAxD0NOCZta1ArzGbHDGhCzTofJp3Pk2Qt76yc/WNJ0DinIpxRIV2wqlTX9iPbmE/02WooRmzqWJkOnuY45sfA=</data>
+ </certificate>
+ <certificate>
+  <data>MIICmjCCAgOgAwIBAgIBJDANBgkqhkiG9w0BAQQFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDEwHhcNOTkwNzA5MTEzNDAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANDdmwygF0RED68hQHNnVvA+aWgRutk38oGuwySsaaHN/KYYVVb/i58ywdvneCw522CBQaXv082AjRg84lIMC5/3ZJ7loPC4YWL0v+Cj2lgr/RUEa706nnyd8j3Y5JXD7E7C8WWrDEvsR4Jb4uFQddj2YbQYXO0zoEseg/tPhLx5AgMBAAGjQjBAMB0GA1UdDgQWBBQUMeJ/nMoSlfvxcCDbTSgTcUJhxjAPBgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQQFAAOBgQCdHRD6k8gdZCBFXpxvBW2jUn+nqIdls2eaNoZ4chYV/9dFcygBhoidkere1ikLDD6jmXRADMzsEOBk/HC2ujkSJ/JeAFC1C9ZK36lv9LMJKID80h7E7HBGhUzeRbABlTisFp9GSu4ty73LZbE65aZLBEsOM/8ce8iEhEfhWmRGSg==</data>
+ </certificate>
+ <certificate>
+  <data>MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mUCm26OWMohpLzGITY+9HPBVZkVw==</data>
+ </certificate>
+ <certificate>
+  <data>MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/</data>
+ </certificate>
+ <certificate>
+  <data>MIICrTCCAhYCAQEwDQYJKoZIhvcNAQEEBQAwgZ4xIDAeBgNVBAoTF01pY3Jvc29mdCBUcnVzdCBOZXR3b3JrMR4wHAYDVQQLExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBUaW1lIFN0YW1waW5nIFNlcnZpY2UgUm9vdDErMCkGA1UECxMiQ29weXJpZ2h0IChjKSAxOTk3IE1pY3Jvc29mdCBDb3JwLjAeFw05NzA1MTMxNjEyNTlaFw05OTEyMzAyMzU5NTlaMIGeMSAwHgYDVQQKExdNaWNyb3NvZnQgVHJ1c3QgTmV0d29yazEeMBwGA1UECxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgVGltZSBTdGFtcGluZyBTZXJ2aWNlIFJvb3QxKzApBgNVBAsTIkNvcHlyaWdodCAoYykgMTk5NyBNaWNyb3NvZnQgQ29ycC4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALdaOPUfN8ypQ8TcJBi+8oVStB1bXxi5C49Lbaj/zUBQbNOg01xHwrn3huTNfTUFaTcfrz3dH/2PFTTCxHnMWXSKb4wOw+gR64Q4R5hT4fEMDeQBDPAbHiDaKno9whVSjor/ezK/WB4lmIMmy4rJxAcUJLxJntd6s4caJTO8bQhHAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAUFvFa2+NUlsNyb2sNHOYygqHyq5MSroUpOtScJUh4LWhYE73QwJRAa8eOnDOvxi2hiiZZ+sI6JdAXBaC/bgl+zZvb3Y+xUyO4qdR+srBY7pegyRHC5Ny9kSazqeVOo9QEJ4dsVkWq88+34OLx/rDa9xkn0ArH0UkBK5JL/bfDJE=</data>
+ </certificate>
+ <certificate>
+  <data>MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJFrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMooPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQh7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ==</data>
+ </certificate>
+ <certificate>
+  <data>MIICvDCCAiUCEEoZ0jiMglkcpV1zXxVd3KMwDQYJKoZIhvcNAQEEBQAwgZ4xHzAdBgNVBAoTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxFzAVBgNVBAsTDlZlcmlTaWduLCBJbmMuMSwwKgYDVQQLEyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIFNlcnZpY2UgUm9vdDE0MDIGA1UECxMrTk8gTElBQklMSVRZIEFDQ0VQVEVELCAoYyk5NyBWZXJpU2lnbiwgSW5jLjAeFw05NzA1MTIwMDAwMDBaFw0wNDAxMDcyMzU5NTlaMIGeMR8wHQYDVQQKExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMRcwFQYDVQQLEw5WZXJpU2lnbiwgSW5jLjEsMCoGA1UECxMjVmVyaVNpZ24gVGltZSBTdGFtcGluZyBTZXJ2aWNlIFJvb3QxNDAyBgNVBAsTK05PIExJQUJJTElUWSBBQ0NFUFRFRCwgKGMpOTcgVmVyaVNpZ24sIEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANMuIPBofCwtLoEcsQaypwu3EQ1X2lPYdePJMyqy1PYJWzTz6ZD+CQzQ2xtauc3n9oixncCHJet9WBBzanjLcRX9xlj2KatYXpYE/S1iEViBHMpxlNUiWC/VzBQFhDa6lKq0TUrp7jsirVaZfiGcbIbASkeXarSmNtX8CS3TtDmbAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAYVUOPnvHkhJ+ERCOIszUsxMrW+hE5At4nqR+86cHch7iWe/MhOOJlEzbTmHvs6T7Rj1QNAufcFb2jip/F87lY795aQdzLrCVKIr17aqp0l3NCsoQCY/Os68olsR5KYSS3P+6Z0JIppAQ5L9h+JxT5ZPRcz/4/Z1PhKxV0f0RY2M=</data>
+ </certificate>
+ <certificate>
+  <data>MIIDkzCCAnugAwIBAgIEN3uAZDANBgkqhkiG9w0BAQUFADBtMQswCQYDVQQGEwJOTDERMA8GA1UEChMIUFRUIFBvc3QxEDAOBgNVBAsTB0tleU1haWwxGTAXBgNVBAMTEFBUVCBQb3N0IFJvb3QgQ0ExHjAcBgoJkiaJk/IsZAEDFA5jYUBwdHQtcG9zdC5ubDAeFw05OTA3MDExNDUxNDhaFw0xOTA2MjYxMzUxNDhaMG0xCzAJBgNVBAYTAk5MMREwDwYDVQQKEwhQVFQgUG9zdDEQMA4GA1UECxMHS2V5TWFpbDEZMBcGA1UEAxMQUFRUIFBvc3QgUm9vdCBDQTEeMBwGCgmSJomT8ixkAQMUDmNhQHB0dC1wb3N0Lm5sMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsH7iOgHxSK1T1HHO276A4FCtmaKEeto6JyQ6EYE2Eg3mo5mOpMwmtQ5hxu4oq22G3y6XYfpAacmNjMQxe/pSXlZMIJ5gGl9sSnjJiTyflYasd2cOpg5C6CxiSTJLBD4yQ5AOCiLKyHQOhe+DgcVb8ttshQhvTialBqt245iiTl7EgODo+8zpMGzycmGuJ35T1BWUD9KPeYLZ9o+rxhPmHJh0SwBhDnlpVPKQsqMJAWX3BEdsTvopK/AOBheT3ILAEd6PsDBGWUhKZs42r8fPMdGSdBQj1aq64InbEtHs1GkjuAsWSTPOGvninF98aB13uwGqZ+Ixxv/WOmn9DBt8IwIDAQABozswOTAPBgNVHRMBAf8EBTADAQH/MBEGA1UdDgQKBAhKGyoEHidFnjATBgNVHSMEDDAKgAhKGyoEHidFnjANBgkqhkiG9w0BAQUFAAOCAQEAPfgyHnWiN2VoVxwCfWX8W4pvWb++Mis950uAF1HMVWtkvbcFOE4rqZ0F0hWHL0gZkzB1/UvmIIVAm8fsXUO3tQzH6ksard18mzGvI/HiBUDZ5b9Ef2PMvgUiMQvl8orwt692y+O0wbcMth0GEleMHsvDipGMsV9oF5ihMVWsp2Rq6TSrim7o1H8+WePowplh9K20Sw4L1ISoplzoYEO83X5I8sNpXOmuEoGmDELOEuRzb78zmjNSeJTz2M5BGYxQJd8SkikrU6m5ZcqPIleFWvxZ1siS0HASVP1kmbycBVxUMenEElQBxoE2Nqxbiw2Gidi5ClJNyJ6819t/gG3SrQ==</data>
+ </certificate>
+ <certificate>
+  <data>MIICiTCCAfKgAwIBAgIEN4dnrDANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJiZTERMA8GA1UEChMIQmVsZ2Fjb20xDDAKBgNVBAsTA01UTTEkMCIGA1UEAxMbQmVsZ2Fjb20gRS1UcnVzdCBQcmltYXJ5IENBMR8wHQYKCZImiZPyLGQBAxQPaW5mb0BlLXRydXN0LmJlMB4XDTk4MTEwNDEzMDQzOVoXDTEwMDEyMTEzMDQzOVowdTELMAkGA1UEBhMCYmUxETAPBgNVBAoTCEJlbGdhY29tMQwwCgYDVQQLEwNNVE0xJDAiBgNVBAMTG0JlbGdhY29tIEUtVHJ1c3QgUHJpbWFyeSBDQTEfMB0GCgmSJomT8ixkAQMUD2luZm9AZS10cnVzdC5iZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAqtm5s9VPak3FQdB7BGFqi3GBB9pk41huJ1XCrc4XsPz6ko0I8Bxy/7LDMf7gaoeXTMxDV6coeTq1g12kHWrxasU+FCIdWQZv8KYxd9ywSTjmywwP/qpyNIjaKDohWu50Kxuk21sTFrVzX8OujNLAPj2wy/Dsi4YLwsFEGFpjqNUCAwEAAaMmMCQwDwYDVR0TBAgwBgEB/wIBATARBglghkgBhvhCAQEEBAMCAAcwDQYJKoZIhvcNAQEFBQADgYEAerKxpbF9M+nC4RvO05OMfwH9Gx1amq6rB1Ev7Ymr3VBCux//SrWknLFhKQpM6oNZSY2vhmnXgaxHqqRxblnvynxqblSK2qiSyfVms3lf1IsBniFjRjWTpcJfImIDcB1jI+hrSB0jECfY9t9HorrsgFBKbMRwpnrkdCJ/9oRiMn8=</data>
+ </certificate>
+ <certificate>
+  <data>MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI</data>
+ </certificate>
+ <certificate>
+  <data>MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd</data>
+ </certificate>
+ <certificate>
+  <data>MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu</data>
+ </certificate>
+ <certificate>
+  <data>MIIDAjCCAmsCEDnKVIn+UCIy/jLZ2/sbhBkwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTE4MDUxODIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYKVdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSmFc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIv3GhDOdlwHq4OZ3BeAbzQ5XZg+a3Is4ceie0ApuXiIukzFo2penm574/ICQQxmvq37rqIUzpLzojSLtLK2JPLl1eDI5WJthHvLvrsDi3xXyvA3qZCviu4Dvh0onNkmdqDNxJ1O8K4HFtW+r1cIatCgQkJCHvQgzKV4gpUmOIpH</data>
+ </certificate>
+ <certificate>
+  <data>MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYKVdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSmFc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0Jh9ZrbWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2uluIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68DzFc6PLZ</data>
+ </certificate>
+ <certificate>
+  <data>MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAeFw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjMHiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjwDqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cCAwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9jinb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAXrXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnInjBJ7xUS0rg==</data>
+ </certificate>
+ <certificate>
+  <data>MIIDAjCCAmsCEB9CKF88iA+OPImzhLOrHxwwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTE4MDUxODIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCniAEhdCznGgPwmOGXPA8hCPGc25fpmvzCBAYTvl9SyMweLBJWLLgBaSzMmR+tsJaueQTyEznBe5i6CCzowoQTLKpp6Qn0x6kCpELCI09K2PAOovsxbMnmb5knB/Xm9Ex4nm3rRob6uYbJVPKyxK/URhxayRUw/w1s9S0Obc5/dwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABFFqKR/8eNzIMq97t/1hyORPY2sR0Ua3m3bVCHODoMO+NzlQ9XrLmGRI+JyADRV98TPETPdweQiI1xQUxn4ZOf3CQ9FUaBXK9+8Imb+MXB7JToPxYp+w7tyAczwvU1SgaQbWFhT1VM69Q5q2umvxOFY80JvVGJHrDGU0Q3O7x0x</data>
+ </certificate>
+ <certificate>
+  <data>MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9</data>
+ </certificate>
+ <certificate>
+  <data>MIIDAjCCAmsCEEakM712H2pJ5qjDp/WFQPUwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTE4MDUxODIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABB79Ik/3D0LuwBM6zQoy/0HqUNphvJLAKTH1diwgngO7ZY8ZnsHB+E+c/Z+csjFQd0pSFxj6zb0dS7FBI2qu7a3FKWAZkY9AQzSwAC1SBtLHfQpR6g8QhdYLXh7IFACJ0ubJwvt8y9UJnNI8CWpifefyaqKYbfKDD3WhHcGFOgV</data>
+ </certificate>
+ <certificate>
+  <data>MIIDAjCCAmsCEDKIjprS9esTR/h/xCA3JfgwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDMHO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtKqsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIWMEsGnuVAVess+rLhDityq3RS6iYF+ATwjcSGIL4LcY/oCRaxFWdcqWERbt5+BO5JoPeI3JPV7bI92NZYJqFmduc4jq3TWg/0ycyfYaT5DdPauxYma51N86Xv2S/PBZYPejYqcPIiNOVn8qj8ijaHBZlCBckztImRPT8qAkbYp</data>
+ </certificate>
+ <certificate>
+  <data>MIIDAjCCAmsCEH62EasyyEHbXqwB45We//0wDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTE4MDUxODIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDMHO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtKqsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAIpoYody54SQCQLbALEo/xjFtWNkmivB0vdf6B8Mr8nr9R2hBH4/46aOxx39KBB1fD/K+RcmJ28rwdvJw2t8psbc5VqN++9AyIfvGVA2V4fx8j/l6qOWmZetma87v4S31Z5siwqj7+di+6Z36C9Oht0xovPKVpjsh57Zgq+EUkHo</data>
+ </certificate>
+ <certificate>
+  <data>MIIEfzCCA2egAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCRkkxETAPBgNVBAcTCEhlbHNpbmtpMR8wHQYDVQQKExZTYXVuYWxhaGRlbiBTZXJ2ZXJpIE95MR8wHQYDVQQDExZTYXVuYWxhaGRlbiBTZXJ2ZXJpIENBMScwJQYJKoZIhvcNAQkBFhhnb2xkLWNlcnRzQHNhdW5hbGFodGkuZmkwHhcNOTkwNzAxMDQ1NzA4WhcNMTkwNjI2MDQ1NzA4WjCBizELMAkGA1UEBhMCRkkxETAPBgNVBAcTCEhlbHNpbmtpMR8wHQYDVQQKExZTYXVuYWxhaGRlbiBTZXJ2ZXJpIE95MR8wHQYDVQQDExZTYXVuYWxhaGRlbiBTZXJ2ZXJpIENBMScwJQYJKoZIhvcNAQkBFhhnb2xkLWNlcnRzQHNhdW5hbGFodGkuZmkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDnBCnc1uBS09ZPAK8cgYi/PFRy8555Y/3LYNoMfHIvyhup4hBpZ3o5n8KgJcyDg42DERy4/efbqYijDJIhV8Gq4uozfkpmA7xzjdIcKf3Hf0Ptj+tXe6FBv01zMUo3KLQYV1g8aHBQk7oQHXjNk8PCgu+43w8MWNUwV0M6FCUqOFkGtETN28leRvJLuBxDQYHSPkOyriGeG0qAfKbKs/QmqS4s+Lf3PBgPmzJvEQlzxM0v6NPsSTbzgY3Ywv/QGm3d2BS/MtDpww0+CK/t2kudas8Ti5CCafrXZyNhS1v9pYBkHXr3pdPadxx4u1H750aAkHFxthb4Q7QZvRir3nZ7AgMBAAGjgeswgegwHQYDVR0OBBYEFGTG3PcyaAuybOpKy15uU0N0S/6ZMIG4BgNVHSMEgbAwga2AFGTG3PcyaAuybOpKy15uU0N0S/6ZoYGRpIGOMIGLMQswCQYDVQQGEwJGSTERMA8GA1UEBxMISGVsc2lua2kxHzAdBgNVBAoTFlNhdW5hbGFoZGVuIFNlcnZlcmkgT3kxHzAdBgNVBAMTFlNhdW5hbGFoZGVuIFNlcnZlcmkgQ0ExJzAlBgkqhkiG9w0BCQEWGGdvbGQtY2VydHNAc2F1bmFsYWh0aS5maYIBADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQAX7dGjfePOY7y5Gpz2UaJg9h1huIfSeFrHyNJfGO+yXd1NnVjk6lrHIMe0+u7GzsMvv/qb3mgMuptHpOxZhBHfsa7+OPvugYEfEnTeBUqrRjjxFjqyyS/4f5xsU4midBiS3jmNCBdT+9mELsPKfQ9CT6dG3wr0QlkIglEJMix2UEtOtQkfgl1oRXlbKKwl6Q0851USpcNC9nS7+uA8zeiiaOjNg7fROje/sIgMTKVGHuXkdDC4ELIN1+CjsfS2XAL56UZqdIkmpwT9kJ1+1NhaT27CuOYmWojl8yaFSLvwyW92aLns0LlG3F02T7BEH6NexNciIXbskjS/yNkfaHki</data>
+ </certificate>
+ <certificate>
+  <data>MIID+zCCAuOgAwIBAgIENuWNnjANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCVVkxKzApBgNVBAoTIkFETUlOSVNUUkFDSU9OIE5BQ0lPTkFMIERFIENPUlJFT1MxHzAdBgNVBAsTFlNFUlZJQ0lPUyBFTEVDVFJPTklDT1MxLDAqBgNVBAMTI1NFUlZJQ0lPUyBERSBDRVJUSUZJQ0FDSU9OIC0gQS5OLkMuMSkwJwYKCZImiZPyLGQBAxQZY29ycmVvX2NlcnRAY29ycmVvLmNvbS51eTAeFw05OTAzMDkyMTA4MDdaFw0wOTAzMDkyMTA4MDdaMIG0MQswCQYDVQQGEwJVWTErMCkGA1UEChMiQURNSU5JU1RSQUNJT04gTkFDSU9OQUwgREUgQ09SUkVPUzEfMB0GA1UECxMWU0VSVklDSU9TIEVMRUNUUk9OSUNPUzEsMCoGA1UEAxMjU0VSVklDSU9TIERFIENFUlRJRklDQUNJT04gLSBBLk4uQy4xKTAnBgoJkiaJk/IsZAEDFBljb3JyZW9fY2VydEBjb3JyZW8uY29tLnV5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsiov7CtZakOTiUYqiuXs+gX64sjeQWuvA9sAWu9IN89XifvdyZIQ3ncDlRyQPse2ZyU7VZjv2Tz+JuSKO0SpdDeDCncndLipca3dlxPSyqIuuLqdyb5Z6Nly8oqFZhxHXrSHgtYP32cmpr02sfNdkFBRdjIsOy+qX2Fe41TVEl3/DY0Rx4J6Nt/hTBbEdN0tau/QsfAzp/6/N2dDEi55SpSvhPsHEQhOMJN16QFUzsXeFIbwrq6bciUPRHfi82yveZwuSceemHYyFpq8AN7gtCAFkRfdgBUU7jZBxCGP7tkAShnGcWGlEV0AO+SndGw6Sm6D4HoxXCFl+AiHQodn5QIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCZH0kiXPhlf4NukGYvAs+09lY6Zjaht5INNsNFWDZL5szxWsxzMp6aqK7Tduvv/oAs4xA0skefHCVq6ES1j0y+gfPQwMELzQMWoEJVVk7B47QgrV+btlVfSBAS7ySzVnzMBRuyySqpaBGSfi/pP0Tx0MaQG0vd92Ei7GmGFUCz4qVqd/Oz8lQn2kkqZBn0Od58dobZnHJXCjT19S/QOWgb20WlGOOAVDuTEJAUwhQBqqV2LX0P5Fbz/3Mqb47B2ID7u5AdGxpJnARjxEId82+7s0lK3UftSboCLSqMxwNWueJo0MGwhrWMSzqujRyIhEz9UK+ZJnYIwoCtKHlV0y3m</data>
+ </certificate>
+ <certificate>
+  <data>MIIEhTCCA22gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBjTELMAkGA1UEBhMCRkkxETAPBgNVBAcTCEhlbHNpbmtpMR8wHQYDVQQKExZTYXVuYWxhaGRlbiBTZXJ2ZXJpIE95MR8wHQYDVQQDExZTYXVuYWxhaGRlbiBTZXJ2ZXJpIENBMSkwJwYJKoZIhvcNAQkBFhpzaWx2ZXItY2VydHNAc2F1bmFsYWh0aS5maTAeFw05OTA3MDEwNDU1MDNaFw0xOTA2MjYwNDU1MDNaMIGNMQswCQYDVQQGEwJGSTERMA8GA1UEBxMISGVsc2lua2kxHzAdBgNVBAoTFlNhdW5hbGFoZGVuIFNlcnZlcmkgT3kxHzAdBgNVBAMTFlNhdW5hbGFoZGVuIFNlcnZlcmkgQ0ExKTAnBgkqhkiG9w0BCQEWGnNpbHZlci1jZXJ0c0BzYXVuYWxhaHRpLmZpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0neMvIdsNk5TqmhgRbgjd2fj7kmC5mx/XpJdtRxhUteoYEsW+ut5fp1MkulXe16GMKkoPH030SHidhoZw++q2u74AZ4aOSovk3UDZj9uKU2NhGOMpx8VlLQ0SbTk00GruvvEXLWecvUoyjKCY0zHRPi0HcSKCldVkK8wiVQOp2gm00AHIrPOPKP7mNckPN58gkm0NIx9JNtkbmSy6f+GyKx+q1Pk0kH0EYTuR0wIHUTmVk0AfNqJQjnveAjRhea+XJ4zuTX/HM70g7XyZMUxSKm0rMXYPIwabab/Qq3z+EvOrNrFirAPAyPB9fPHWX8w8d9mHVoxBaJGHTnkVbOtDwIDAQABo4HtMIHqMB0GA1UdDgQWBBTRsV9BeXN5IFjJL6KoR8dExmOEKDCBugYDVR0jBIGyMIGvgBTRsV9BeXN5IFjJL6KoR8dExmOEKKGBk6SBkDCBjTELMAkGA1UEBhMCRkkxETAPBgNVBAcTCEhlbHNpbmtpMR8wHQYDVQQKExZTYXVuYWxhaGRlbiBTZXJ2ZXJpIE95MR8wHQYDVQQDExZTYXVuYWxhaGRlbiBTZXJ2ZXJpIENBMSkwJwYJKoZIhvcNAQkBFhpzaWx2ZXItY2VydHNAc2F1bmFsYWh0aS5maYIBADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBfV7bHhYLgoISRnUCzjbA5//QSGYud2gRiRN5kTY0R3nlSy20Y8Z26B62GCaQaaqrClA5Z7BeKL0zjcc5wVp9bM0EUUFr2sntBZBntCBbWxhQcfEJCn+3RWQnUr+tHzNuveRS6PHgFELPX9w550KHNPEsyyM8PAYSeoEsw0MYAV0lXJvOvm7rXYKQYPrL18oh9/l0HYsmIqawim5hYs7s2wUMKOI+K+7zTjuALtdwDCfTMNdDFcs3dFlEjQg/c4lizQRs0hnqINIRkkAuuykmNtTkII11BtIv3GNCyS4/v712ex853T8iCQTDS/OZRYPxvaPT/lNuLYNGN02KKB2WR</data>
+ </certificate>
+ <certificate>
+  <data>MIICszCCAhwCAQAwDQYJKoZIhvcNAQEEBQAwgaExCzAJBgNVBAYTAkJSMRcwFQYDVQQIEw5SaW8gZGUgSmFuZWlybzEXMBUGA1UEBxMOUmlvIGRlIEphbmVpcm8xLjAsBgNVBAoTJUNlcnRpc2lnbiBDZXJ0aWZpY2Fkb3JhIERpZ2l0YWwgTHRkYS4xMDAuBgNVBAsTJ0NlcnRpc2lnbiBBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgQUMzUzAeFw05OTA3MDkyMDU2MzJaFw0xODA3MDkyMDU2MzJaMIGhMQswCQYDVQQGEwJCUjEXMBUGA1UECBMOUmlvIGRlIEphbmVpcm8xFzAVBgNVBAcTDlJpbyBkZSBKYW5laXJvMS4wLAYDVQQKEyVDZXJ0aXNpZ24gQ2VydGlmaWNhZG9yYSBEaWdpdGFsIEx0ZGEuMTAwLgYDVQQLEydDZXJ0aXNpZ24gQXV0b3JpZGFkZSBDZXJ0aWZpY2Fkb3JhIEFDM1MwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM5kTtbPrnyYJ5jvKO+6NMM1Gp3SZh5x8eSWLScXOZBFjy3v5tVHqeGDgXw+KHH6gLtd+ENrGRDBmG8Vni2uSWRE7IQaaVkWV7I9kthmS7+ZrUMYxzjJi44sBC6zS2uT9p0fhLLN5gDCvjalvFm1n4sZIP/lqCikVbaKtrLF10TjAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAy3XX5MH/8qBDjJ0upEOfrpFIuGq3/8gzjBPG0uQmcHq4re2sm8WlK6pXiwTXgc2qDTlv5ESpkGUC9/p41tRVd8VrQCR4VMvERrHl0oZAhZAI/NsVq84qPmi+F1/EstC8si3NTLcMxC341gtBg8rkdGkL4VWxkRzJSjw+8FIUHfQ=</data>
+ </certificate>
+ <certificate>
+  <data>MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI</data>
+ </certificate>
+ <certificate>
+  <data>MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UEBhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50cnVzdC5uZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBsaW1pdHMgbGlhYi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBaMIHJMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo6oT9n3V5z8GKUZSvx1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDeg7K6PvHViTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSCARkwggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCygKqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9DbGllbnQxLmNybDArBgNVHRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkxMDEyMTkyNDMwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW/O5bs8qZdIuV6kwwHQYDVR0OBBYEFMT7nCl7l81MlvzuW7PKmXSLlepMMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7pFuPeJoSSJn59DXeDDYHAmsQOokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzzwy5E97BnRqqS5TvaHBkUODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/aEkP/TOYGJqibGapEPHayXOw=</data>
+ </certificate>
+ <certificate>
+  <data>MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBsZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A==</data>
+ </certificate>
+ <certificate>
+  <data>MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=</data>
+ </certificate>
+ <certificate>
+  <data>MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjVojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjbPG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP26KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vrn5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1aqtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrspSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g==</data>
+ </certificate>
+ <certificate>
+  <data>MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWUJ92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDOJxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUYwZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9okoqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjNqWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/ESrg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfexbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKUsQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RIsH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTPcjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q</data>
+ </certificate>
+ <certificate>
+  <data>MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==</data>
+ </certificate>
+ <certificate>
+  <data>MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKmfjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg==</data>
+ </certificate>
+ <certificate>
+  <data>MIICjzCCAfgCAQAwDQYJKoZIhvcNAQEEBQAwgY8xCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEYMBYGA1UEChMPRnVuZGFjaW9uIEZFU1RFMR4wHAYDVQQDExVGRVNURSwgVmVyaWZpZWQgQ2VydHMxHjAcBgkqhkiG9w0BCQEWD2Zlc3RlQGZlc3RlLm9yZzAeFw05OTA1MTMxOTIzNTNaFw0yMDAxMDExOTIzNTNaMIGPMQswCQYDVQQGEwJFUzESMBAGA1UECBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmExGDAWBgNVBAoTD0Z1bmRhY2lvbiBGRVNURTEeMBwGA1UEAxMVRkVTVEUsIFZlcmlmaWVkIENlcnRzMR4wHAYJKoZIhvcNAQkBFg9mZXN0ZUBmZXN0ZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOpjnx84GoQEhLNLkxmEontiw6erLOxgTlUR4gQyJyA1+Rp4Hydnp+CCsAXoJqmbikRWv0uEU6Mipg4WU7l/rxDIMxhU2cZMfNFbVlJThqZbPxPywwHSMaEzXLxd5NxiPMojl09w+4GOsNmer5PqtrEkjQQtUUM8OOwnQ3GH6DzHAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAfvj3P7Lu3vohb4EUd4ae71xPjpbPfHXP1F1/cUbURQpgaiqW8dSIpI6j9xcwLsFK8Butk7JgKQzhxHWSZcOGzXLGMh+gVb485XfbHWQTmIslbgB/7KPhWgTYLKgSozCGRKBhN/4QMHk0zeX6AnU192Ft9R9kmwOQ0EyhTG4bO9I=</data>
+ </certificate>
+ <certificate>
+  <data>MIICmTCCAgICAQAwDQYJKoZIhvcNAQEEBQAwgZQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEYMBYGA1UEChMPRnVuZGFjaW9uIEZFU1RFMSMwIQYDVQQDExpGRVNURSwgUHVibGljIE5vdGFyeSBDZXJ0czEeMBwGCSqGSIb3DQEJARYPZmVzdGVAZmVzdGUub3JnMB4XDTk5MDUxMzE5MjEyOFoXDTIwMDEwMTE5MjEyOFowgZQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEYMBYGA1UEChMPRnVuZGFjaW9uIEZFU1RFMSMwIQYDVQQDExpGRVNURSwgUHVibGljIE5vdGFyeSBDZXJ0czEeMBwGCSqGSIb3DQEJARYPZmVzdGVAZmVzdGUub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDhg/ObLsnn4cf0VAXNdkD+tLMTvucVXoYm6EB3GlU/0QMmjPqHX6TF+f61MonGf0GR2BVATnBS8PHa+GI1mV4clFNhzD5iwINdWNH4SBFxbPewd+EYl7QHKDCRMcdPVPOEnsxZiUVtfrTJ245ClWbU3x4YTfylD9YahDnEyvK98wIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAKY/Lh5JIBFgcKST3mTjaHSKt7c8eyF6ouErXa4o68k3RdI1e1gX2hD2ruFWYrrtIXyum2F8pP7euO0Y7iKIaDAiAWhZYnq7c5mLywn1nfGBvvbUlYjtVQzFnCYVjPFDpQNhffVm6rWhMjuED+ykatF75/Mv3wRihCluNha9WID/</data>
+ </certificate>
+ <certificate>
+  <data>MIIDtzCCAp+gAwIBAgIBETANBgkqhkiG9w0BAQQFADCBjTELMAkGA1UEBhMCQ0gxFDASBgNVBAoTC1N3aXNza2V5IEFHMR4wHAYDVQQLExUwMDg1MTAwMDAwMDA1MDAwMDAxOTIxGzAZBgNVBAsTElB1YmxpYyBDQSBTZXJ2aWNlczEQMA4GA1UEBxMHWnVlcmljaDEZMBcGA1UEAxMQU3dpc3NrZXkgUm9vdCBDQTAeFw05OTA0MTUxMDM4MDBaFw0xNTEyMzEyMzU5MDBaMIGNMQswCQYDVQQGEwJDSDEUMBIGA1UEChMLU3dpc3NrZXkgQUcxHjAcBgNVBAsTFTAwODUxMDAwMDAwMDUwMDAwMDE5MjEbMBkGA1UECxMSUHVibGljIENBIFNlcnZpY2VzMRAwDgYDVQQHEwdadWVyaWNoMRkwFwYDVQQDExBTd2lzc2tleSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOeC2xUTrnnCtF+SjyO8uvfG0QCv1lRp8V2mYvhh0Zzeyjss6VwWJzTmuNHKdO8leGRt/hzoiXMxU2dnhsStamjnClZEgzpYR4l3Gtpv8vkHQMk9Ae9q0dlrhJ7FaytOtyz4pGpXq2gxuhlmuuwbV/vOStZLeMPBgT1LljCZqcMt4uQSJgqkYxIc1HfIgdSnVUMt/ARWndwLrrdsCtozkIgFyX5UgujSMtDXAUkqNZB5OXPWi7xhzYdtUBUFTKnoSkcxiwXM5flC1xJg+Do/o6k2GqWGNiymBIMJ9lLFsH0fiEGQmMVlaJYQshPJFkm9Kr6wSKfC/S1eVtA3TVhR+wIDAQABoyAwHjAPBgNVHRMECDAGAQH/AgEDMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQQFAAOCAQEAInV83Kdkxklhv3fSTerGh6d5fVBNBMawKuY2DkJrhu0NWIipYOWhIc7pSv9KJvsHGpl63+SWh8s30ltrY126U4qYGepm5bMIQmNJz5ppMwe8k9OENw7oOlSbJN1P13rdmC1swhZRYNy/esRenFSLRCths2LODYZ8x8XJY25RKfJg5eMlCJLzevBZCg8Ud2y0goPryjQHHh9+EdkHE8BEkMh3IiPLrgao8tS6QH/Pda/dVxk91mpFh4/XPtVMW9DaX48KCA3OwrgcKaWDRhMTqTg1f1utRO5dbRLdCTY+nFBPAnIlBb8D8XGobwgPN8ds6OrVo9mmjXRLmfgJDrAjmw==</data>
+ </certificate>
+ <certificate>
+  <data>MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowftGzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI</data>
+ </certificate>
+ <certificate>
+  <data>MIID+DCCAuCgAwIBAgIRANAeQJAAACdLAAAAAQAAAAQwDQYJKoZIhvcNAQEFBQAwgYwxCzAJBgNVBAYTAlVTMQ0wCwYDVQQIEwRVdGFoMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEYMBYGA1UEChMPWGNlcnQgRVogYnkgRFNUMRgwFgYDVQQDEw9YY2VydCBFWiBieSBEU1QxITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05OTA3MTQxNjE0MThaFw0wOTA3MTExNjE0MThaMIGMMQswCQYDVQQGEwJVUzENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxGDAWBgNVBAoTD1hjZXJ0IEVaIGJ5IERTVDEYMBYGA1UEAxMPWGNlcnQgRVogYnkgRFNUMSEwHwYJKoZIhvcNAQkBFhJjYUBkaWdzaWd0cnVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtVBjetL/3reh0qu2LfI/C1HUa1YS5tmL8ie/kl2GS+x244VpHNJ6eBiL70+o4y7iLB/caoBd3B1owHNQpOCDXJ0DYUJNDv9IYoil2BXKqa7ZpmKt5Hhxl9WqL/MUWqqJy2mDtTm4ZJXoKHTDjUJtCPETrobAgHtsCfv49H7/QAIrbQHamGKUVp1e2UsIBF5h3j4qBxhq0airmr6nWAKzP2BVJfNsbof6B+of505DBAsD50ELpkWglX8a/hznplQBgKL+DLMDnXrbXNhbnYId26OcnsiUNi3rlqh3lWc3OCw5vxsic4xDZhTnTt5v6xrp8dNJddVardKSiUb9SfO5xAgMBAAGjUzBRMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUCCBsZuuBCmxc1bWmPEHdHJaRJ3cwHQYDVR0OBBYEFAggbGbrgQpsXNW1pjxB3RyWkSd3MA0GCSqGSIb3DQEBBQUAA4IBAQBah1iPLat2IWtUDNnxQfZOzSue4x+boy1/2St9WMhnpCn16ezVvZY/o3P4xFs2fNBjLDQ5m0i4PW/2FMWeY+anNG7T6DOzxzwYbiOuQ5KZP5jFaTDxNjutuTCC1rZZFpYCCykSYbQRifcML5SQhZgonFNsfmPdc/QZ/0qB0bJSI/08SjTOWhvgUIrtT4GV2GDn5MQNu1g+WPdOaG8+Z8nLepcWJ+xCYRR2uwDF6wg9FX9LtiJdhzuQ9PPA/jez6dliDMDDWa9gvR8N26E0HzDEPYutsB0Ek+1f1eS/IDAE9EjpMwHRLpAnUrOb3jocq6mXf5vrwo3CbezcE9NGxXl8</data>
+ </certificate>
+ <certificate>
+  <data>MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3QtT2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAzNlowgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicPHxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLOKqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehbkkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUCAwEAAaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDApBgNVHSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQwDQYJKoZIhvcNAQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujwNTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXBmMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK581OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyRUh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g=</data>
+ </certificate>
+ <certificate>
+  <data>MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcwMTAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYrWHhhRYZT6jR7UZztsOYuGA7+4F+oJ9O0yeB8WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQaWt9MevPZQx08EHp5JduQ/vBR5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL8vg7ij5FrHGSALSQQZj7X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC9RAIDb/LogWK0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQpgCed/r8zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZCayJSdM=</data>
+ </certificate>
+ <certificate>
+  <data>MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA==</data>
+ </certificate>
+ <certificate>
+  <data>MIIDXDCCAsWgAwIBAgICA+kwDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAxIENBMSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMSBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALAp67R2s67Xtlu0Xue947GcSQRXW6Gr2X8TG/26YavY53HfLQCUXVFIfSPvdWKEkDwKH1kRdC+OgKX9MAI9KVLNchpJIZy8y1KOSKFjlsgQhTBpV3RFwFqGxtU94GhXfTFqJI1Flz4xfmhmMm4kbewyNslByvAxRMijYcoboDYfAgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0GCSqGSIb3DQEBBAUAA4GBAE+ZWYXIZFaCxW892EYJLzxRwadwWIGSEur01BYAll5yKOfWNl8anK8fwoMatAVVmaZYXDco8lce612/sdNFD3IcA9IAxyxV2v5fiXaL4tR39U0JF6/EuqswK0+4HerZ/1nwUHRGul7qNrDrknsPWNoy4VK9IzcP9fMASq6wXt5u</data>
+ </certificate>
+ <certificate>
+  <data>MIIDXDCCAsWgAwIBAgICA+owDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAyIENBMSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANo46O0yAClxgwENv4wB3NrGrTmkqYov1YtcaF9QxmL1Zr3KkSLsqh1R1z2zUbKDTl3LSbDwTFXlay3HhQswHJJOgtTKAu33b77c4OMUuAVT8pr0VotanoWT0bSCVq5Nu6hLVxa8/vhYnvgpjbB7zXjJT6yLZwzxnPv8V5tXXE8NAgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0GCSqGSIb3DQEBBAUAA4GBAIRS+yjf/x91AbwBvgRWl2p0QiQxg/lGsQaKic+WLDO/jLVfenKhhQbOhvgFjuj5Jcrag4wGrOs2bYWRNAQ29ELw+HkuCkhcq8xRT3h2oNmsGb0q0WkEKJHKNhAngFdb0lz1wlurZIFjdFH0l7/NEij3TWZ/p/AcASZ4smZHcFFk</data>
+ </certificate>
+ <certificate>
+  <data>MIIDXDCCAsWgAwIBAgICA+swDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAzIENBMSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALa0wTUFLg2N7KBAahwOJ6ZQkmtQGwfeLud2zODa/ISoXoxjaitN2U4CdhHBC/KNecoAtvGwDtf7pBc9r6tpepYnv68zoZoqWarEtTcI8hKlMbZD9TKWcSgoq40oht+77uMMfTDWw1Krj10nnGvAo+cFa1dJRLNu6mTP0o56UHd3AgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0GCSqGSIb3DQEBBAUAA4GBABY9xs3Bu4VxhUafPiCPUSiZ7C1FIWMjWwS7TJC4iJIETb19AaM/9uzO8d7+feXhPrvGq14L3T2WxMup1Pkm5gZOngylerpuw3yCGdHHsbHD2w2Om0B8NwvxXej9H5CIpQ5ON2QhqE6NtJ/x3kit1VYYUimLRzQSCdS7kjXvD9s0</data>
+ </certificate>
+ <certificate>
+  <data>MIIDXDCCAsWgAwIBAgICA+wwDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYTAkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyA0IENBMSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAeFw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJERTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UEChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29ya3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgNCBDQTEpMCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL8vY9Y2e7IN01X1ZGzmJV3GtMgUuiU4g+tWYqVVqWWj9COZwku50M1UZ6ajoKOpMyt25L2td7LtXBJ0w8W2D1KacpNDkGJmFQ9Fpd3g3bhvQG5XwXlyo2CqunYdEolTWvwCvuEJE8VKL9w9ixmt14skRftM9M1cNR0pTFHz8mxVAgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0GCSqGSIb3DQEBBAUAA4GBAHIR5ZVBRTK6HPiAFPtmt+uums51g1HAroq7F9Eo53YfE8YrRnGmFXcEmedespEkbwMMc+cjnnbKvgzFy8SQGPxtOm7gVoAbw9+MNhNH+WXBg1LVXFy92UJm4TUhaBIQpGCQPj+B6MOMobAVBFrO6yxUVkv5BHktneqMWS+teb1I</data>
+ </certificate>
+ <certificate>
+  <data>MIIDQzCCAqygAwIBAgICA/EwDQYJKoZIhvcNAQEEBQAwgcExCzAJBgNVBAYTAkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJIMSgwJgYDVQQLEx9UQyBUcnVzdENlbnRlciBUaW1lIFN0YW1waW5nIENBMSgwJgYDVQQDEx9UQyBUcnVzdENlbnRlciBUaW1lIFN0YW1waW5nIENBMB4XDTk4MDMwOTExNTk1OVoXDTExMDEwMTExNTk1OVowgcExCzAJBgNVBAYTAkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYDVQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3b3JrcyBHbWJIMSgwJgYDVQQLEx9UQyBUcnVzdENlbnRlciBUaW1lIFN0YW1waW5nIENBMSgwJgYDVQQDEx9UQyBUcnVzdENlbnRlciBUaW1lIFN0YW1waW5nIENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2S+Q2apwDjOkZb76H+fcwjD4vGE1U3ujMLAg9fEYvhAyd6+/7EZRj5+y0zRP9mvYwZcWKfciC0aO9EXsefr8v3GeBBFtwS+rhs7aYPbW+cNM+eV0LN5hYisP6mSiPAQRjHoB/d3LEXX//T1f/qslWd0Ot/BY3ajgvdEEZN6f/wwIDAQABo0gwRjAPBgNVHRMBAf8EBTADAQH/MDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwDQYJKoZIhvcNAQEEBQADgYEALqyPthmgpIxeAbsJadYuBft2K2k118hvBqgb8tVfC8xL88FT9JW/nI5ss197C8bmnKfQLAM+1TnhnG7rQfjJZEO4PaJK4R5PhZLXG0duPxfar+wWPo4aiS1BidZpL0OqXS7y6NBU7g0Wxdpw2BJ0RK4WS3TtjAurNQpIaOxpAyk=</data>
+ </certificate>
+ <certificate>
+  <data>MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUFADCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0ODM5WhcNMTkwNzA5MTg1NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz+5Gh5DZVhawGNFugmliy+LUPBXeDrjKxdpJo7CNKyXY/45y2N3kDuatpjQclthln5LAbGHNhSuh+zdMvZOOmfAz6F4CjDUeJT1FxL+78P/m4FoCHiZMlIJpDgmkkdihZNaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXuOzr0hAReYFmnjDRy7rh4xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1axwiP8vv/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6gyN7igEL66S/ozjIEj3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPqGydvguul49Uuo1hXf8NPhahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXhi6r/fWRRzwr/vH3YIWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUqf9FuVSTiuwL7MT++6LzsQCv4AdRWOOTKRIK1YSAhZ2X28AvnNPilwpyjXEAfhZOVBt5P1CeptqX8Fs1zMT+4ZSfP1FMa8Kxun08FDAOBp4QpxFq9ZFdyrTvPNximmMatBrTcCKME1SmklpoSZ0qMYEWd8SOasACcaLWYUNPvji6SZbFIPiG+FTAqDbUMo2s/rn9X9R+WfN9v3YIwLGUbQErNaLly7HF27FSOH4UMAWr6pjisH8SE</data>
+ </certificate>
+ <certificate>
+  <data>MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI</data>
+ </certificate>
+ <certificate>
+  <data>MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCBrjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3QtQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBaFw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWlsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3BYHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6lL8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLmSGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQrfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAimeOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gkUSeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ=</data>
+ </certificate>
+ <certificate>
+  <data>MIICtzCCAiACAQAwDQYJKoZIhvcNAQEEBQAwgaMxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCQVJDRUxPTkExEjAQBgNVBAcTCUJBUkNFTE9OQTEZMBcGA1UEChMQSVBTIFNlZ3VyaWRhZCBDQTEYMBYGA1UECxMPQ2VydGlmaWNhY2lvbmVzMRcwFQYDVQQDEw5JUFMgU0VSVklET1JFUzEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVzMB4XDTk4MDEwMTIzMjEwN1oXDTA5MTIyOTIzMjEwN1owgaMxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCQVJDRUxPTkExEjAQBgNVBAcTCUJBUkNFTE9OQTEZMBcGA1UEChMQSVBTIFNlZ3VyaWRhZCBDQTEYMBYGA1UECxMPQ2VydGlmaWNhY2lvbmVzMRcwFQYDVQQDEw5JUFMgU0VSVklET1JFUzEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsT1J0nznqjtwlxLyYXZhkJAk8IbPMGbWOlI6H0fg3PqHILVikgDVboXVsHUUMH2Fjal5vmwpMwci4YSM1gf/+rHhwLWjhOgeYlQJU3c0jt4BT18g3RXIGJBK6E2Ehim51KODFDzT9NthFf+G4Nu+z4cYgjui0OLzhPvYR3oydAQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBACzzw3lYJN7GO9HgQmm47mSzPWIBubOE3yN93ZjPEKn+ANgilgUTB1RXxafey9m4iEL2mdsUdx+2/iU94aI+A6mB0i1sR/WWRowiq8jMDQ6XXotBtDvECgZAHd1G9AHduoIuPD14cJ58GNCr+Lh3B0Zx8coLY1xq+XKU1QFPoNtC</data>
+ </certificate>
+ <certificate>
+  <data>MIID2DCCAsACEQDQHkCLAAACfAAAAAIAAAABMA0GCSqGSIb3DQEBBQUAMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDExITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODEyMDExODE4NTVaFw0wODExMjgxODE4NTVaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDExITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANLGJrbnpT3BxGjVUG9TxW9JEwm4ryxIjRRqoxdfWvnTLnUv2Chi0ZMv/E3Uq4flCMeZ55I/db3rJbQVwZsZPdJEjdd0IG03Ao9pk1uKxBmd9LIO/BZsubEFkoPRhSxglD5FVaDZqwgh5mDoO3TymVBRaNADLbGAvqPYUrBEzUNKcI5YhZXhTizWLUFv1oTnyJhEykfbLCSlaSbPa7gnYsP0yXqSI+0TZ4KuRS5F5X5yP4WdlGIQ5jyRoa13AOAV7POEgHJ6jm5gl8ckWRA0g1vhpaRptlc1HHhZxtMvOnNn7pTKBBMFYgZwI7P0fO5F2WQLW0mqpEPOJsREEmy43XkCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAojeyP2n714Z5VEkxlTMr89EJFEliYIalsBHiUMIdBlc+LegzZL6bqq1fG03UmZWii5rJYnK1aerZWKs17RWiQ9a2vAd5ZWRzfdd5ynvVWlHG4VMElo04z6MXrDlxawHDi1M8Y+nuecDkvpIyZHqzH5eUYr3qsiAVlfuX8ngvYzZAOONGDx3drJXK50uQe7FLqdTF65raqtWjlBRGjS0f8zrWkzr2Pnn86Oawde3uPclwx12qgUtGJRzHbBXjlU4PqjI3lAoXJJIThFjSY28r9+ZbYgsTF7ANUkz+/m9c4pFuHf2kYtdo+o56T9II2pPc8JIRetDccpMMc5NihWjQ9A==</data>
+ </certificate>
+ <certificate>
+  <data>MIID2DCCAsACEQDQHkCLAAB3bQAAAAEAAAAEMA0GCSqGSIb3DQEBBQUAMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIxITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODExMzAyMjQ2MTZaFw0wODExMjcyMjQ2MTZaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIxITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANx18IzAdZaawGIfJvfE4Zrq4FZzW5nNAUSoCLbVp9oaBBg5kkp4o4HC9Xd6ULRw/5qrxsfKboNPQpj7Jgva3G3WqZlVUmfpKAOS3OWwBZoPFflrWXJW8vo5/Kpo7g8fEIMv/J36F5bdguPmRX3AS4BEH+0s4IT9kVySVGkl5WJp3OXuAFK9MwutdQKFp2RQLcUZGTDAJtvJ0/0uma1ZtQtN1EGuhUhDWdy3qOKi3sOP17ihYqZoUFLkzzGnlIXan0YyF1bl8utmPRL/Q9uY73fPy4GNNLHGUEom0eQ+QVCvbK4iNC7Va26Dunm4dmVI2gkpZGMiuftHdoWMhkTLCdsCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAtTYOXeFhKFoRZcA/gwN5Tb4opgsHAlKFzfiR0BBstWogWxyQ2TA8xkieil5k+aFxd+8EJx8H6+Qm93N0yUQYGmbT4EOvkTvRyyzYdFQ6HE3K1GjNI3wdEJ5F6fYAbqbNGf9PLCmPV03Ed5K+4EwJ+11EhmYhqLkyolbV6YyDfFk/xPEL553snr2cGA4+wjl5KLcDDQjLxufZATdQEOzMYRZA1K8xdHv8PzGn0EdzMzkbzE5q10mDEQb+64JYMzJM8FasHpwvVpp7wUocpf1VNs78lk30sPDst2yC7S8xmUJMqbINuBVd8d+6ybVK1GSYsyapMMj9puyrliGtf8J4tg==</data>
+ </certificate>
+ <certificate>
+  <data>MIIEAjCCAuoCEQDQHkCKAAACfAAAAAMAAAABMA0GCSqGSIb3DQEBBQUAMIG+MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEjMCEGA1UECxMaTmF0aW9uYWwgUmV0YWlsIEZlZGVyYXRpb24xGTAXBgNVBAMTEERTVCAoTlJGKSBSb290Q0ExITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODEyMTExNjE0MTZaFw0wODEyMDgxNjE0MTZaMIG+MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEjMCEGA1UECxMaTmF0aW9uYWwgUmV0YWlsIEZlZGVyYXRpb24xGTAXBgNVBAMTEERTVCAoTlJGKSBSb290Q0ExITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANmsm3f6UNPM3LlArLlyagCHI/wPliHQJq/k4rVf+tOmfSEwLswXgo+YdPxnpKbfiJeiQin1p9sRk/teIzDCqrwi50Eb5e0l3sg/295XRXhARoOy1Ro93w9FbdVjAnXYL8Zuq5WRdDcNy00JXNHUWzra3Q7Ia5nY1TnM34VVxJJTAqPh94DJcKPa3DPEf6JHCBw1lh+hAxwwg/TEzP+Yw7BGRKLAv63b0oH2TJgsp14k84bKY9W6ffCawErQG1ju7Klnz2kCbCLAYCws0cgg6sgt+92cu8tRTNznVwQ7VJsRpTJ07HQB85AVWy98LJNluWZntIGINeWekRh/gahByMsCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAhF4LO+ygjRyb0DwdcWnkGn9kvoFlYcWMatd8AHTgemJV7SR84GHj8t0U5hFugw7h6qmegK2aIL/gV37V0LWEYy3ZGOS9GzUsXq5hdqpnhTs44TGBHzF/5tf4W9K7Y3mGxIzF3gqu19H8AXT/trYNYoFnHLsm+CSA4Fxe2KSKOo99y/+So/18qTJpB1hYYUKZUgOxOD3GcW9s8uh9BqrBfFPLGi2IT8mpp6xpb/ekH9h0gfVKv7FVt9N3OKdvwkrI4nOJ01dy4UMvcjz2H7f4BEpuwemUF+SXF/QOE4ZvjavoXy20/2zWorQf7LmUaqoSTxrd9Xe1JYzyigrx/FJbWA==</data>
+ </certificate>
+ <certificate>
+  <data>MIID+DCCAuACEQDQHkCLAAACfAAAAAcAAAABMA0GCSqGSIb3DQEBBQUAMIG5MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjEeMBwGA1UECxMVVW5pdGVkIFBhcmNlbCBTZXJ2aWNlMRkwFwYDVQQDExBEU1QgKFVQUykgUm9vdENBMSEwHwYJKoZIhvcNAQkBFhJjYUBkaWdzaWd0cnVzdC5jb20wHhcNOTgxMjEwMDAyNTQ2WhcNMDgxMjA3MDAyNTQ2WjCBuTELMAkGA1UEBhMCdXMxDTALBgNVBAgTBFV0YWgxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xHjAcBgNVBAsTFVVuaXRlZCBQYXJjZWwgU2VydmljZTEZMBcGA1UEAxMQRFNUIChVUFMpIFJvb3RDQTEhMB8GCSqGSIb3DQEJARYSY2FAZGlnc2lndHJ1c3QuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7xfsrynm2SsnwNt7JJ9m9ASjwq0KyrDNhCuqN/OAoWDvQo/lXXdfV0JU3SvbYbJxXpN7b1/rJCvnpPLr8XOzC431Wdcy36yQjk4xuiVNtgym8eWvDOHlb1IDFcHfvn5KpqYYRnA/76dNqNz1dNlhekA8oZQo6sKUiMs3FQUZPJViuhwt+yiM0ciekjxbEVQ7eNlHO5stSuY+e2vf9PYFzyj2upg2AJ48N4UKnN63pIXFY/23YhRtFx7MioCFQjIRsCHinXfJgBZBnuvlFIl/t8O8T8Gfh5uW7GP2+ZBWDpWjIwqMZNqbuxx3sExd5sjo9X15LVckP8zjPSyYzxKfFwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQC7OI4EIiZYDiFEVsy9WXwpaMtcD8iGVD+BeKetj8xG9xxUuHktW3IFaugh0OwdHf6kNFG+7u3OzJwWaOJddXMIQzGRahArEMJLafjJrZio/bjv9qvwXyHvy4VrCe0vSGa1YHLA6KDHmNsO9xtzjTQICnvFd2KqMCObsB6LgJhU3AWHs6liWfyLtxWarETszzUa9w8uXZJLAch77qA37eQdgg2ZQUMXrdTVyuP5fReiAdAwD0C53LkEgmmDtvkP+gaS96j01hcc8F5/xCnI5uHi/zZoIVGu/6m6hJKtinsz2JDSwXltMzM5dKwbOHGfLAeQ6h3g04lfy+8UjSdUpb1G</data>
+ </certificate>
+ <certificate>
+  <data>MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=</data>
+ </certificate>
+ <certificate>
+  <data>MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBlcnNvbmFsIEJhc2ljIENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNpY0B0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgcsxCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBDQTEoMCYGCSqGSIb3DQEJARYZcGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53dXLdjUmbllegeNTKP1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdKwPQIcOk8RHtQfmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7G1sY0b8jkyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95B21P9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ==</data>
+ </certificate>
+ <certificate>
+  <data>MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==</data>
+ </certificate>
+ <certificate>
+  <data>MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBlcnNvbmFsIFByZW1pdW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXByZW1pdW1AdGhhd3RlLmNvbTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5NTlaMIHPMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRvd24xGjAYBgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5nMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMSMwIQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJlbWl1bSBDQTEqMCgGCSqGSIb3DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0VsBd/eJxZRNkERbGw77f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWIEt12TfIa/G8jHnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYDZicRFTuqW/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIHb4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVxeTBhKXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1KzGJ</data>
+ </certificate>
+ <certificate>
+  <data>MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBlcnNvbmFsIEZyZWVtYWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1mcmVlbWFpbEB0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgdExCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xJDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBDQTErMCkGCSqGSIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfYDFG26nKRsIRefS0Nj3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5ErHzmj+hND3EfQDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVquzgkCGqYx7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjPMPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgCneSa/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr5PjRzneigQ==</data>
+ </certificate>
+ <certificate>
+  <data>MIIDtTCCAp2gAwIBAgIRANAeQJAAAEZSAAAAAQAAAAQwDQYJKoZIhvcNAQEFBQAwgYkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJEQzETMBEGA1UEBxMKV2FzaGluZ3RvbjEXMBUGA1UEChMOQUJBLkVDT00sIElOQy4xGTAXBgNVBAMTEEFCQS5FQ09NIFJvb3QgQ0ExJDAiBgkqhkiG9w0BCQEWFWFkbWluQGRpZ3NpZ3RydXN0LmNvbTAeFw05OTA3MTIxNzMzNTNaFw0wOTA3MDkxNzMzNTNaMIGJMQswCQYDVQQGEwJVUzELMAkGA1UECBMCREMxEzARBgNVBAcTCldhc2hpbmd0b24xFzAVBgNVBAoTDkFCQS5FQ09NLCBJTkMuMRkwFwYDVQQDExBBQkEuRUNPTSBSb290IENBMSQwIgYJKoZIhvcNAQkBFhVhZG1pbkBkaWdzaWd0cnVzdC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx0xHgeVVDBwhMywVCAOINg0Y95JO6tgbTDVm9PsHOQ2cBiiGo77zM0KLMsFWWU4RmBQDaREmA2FQKpSWGlO1jVv9wbKOhGdJ4vmgqRF4vz8wYXke8OrFGPR7wuSw0X4x8TAgpnUBV6zx9g9618PeKgw6hTLQ6pbNfWiKX7BmbwQVo/ea3qZGULOR4SCQaJRk665WcOQqKz0Ky8BzVX/tr7WhWezkscjiw7pOp03t3POtxA6k4ShZsiSrK2jMTecJVjO2cu/LLWxD4LmE1xilMKtAqY9FlWbT4zfn0AIS2V0KFnTKo+SpU+/94Qby9cSj0u5C8/5Y0BONFnqFGKECBAgMBAAGjFjAUMBIGA1UdEwEB/wQIMAYBAf8CAQgwDQYJKoZIhvcNAQEFBQADggEBAARvJYbk5pYntNlCwNDJALF/VD6Hsm0kqS8Kfv2kRLD4VAe9G52dyntQJHsRW0mjpr8SdNWJt7cvmGQlFLdh6X9ggGvTZOirvRrWUfrAtF13Gn9kCF55xgVM8XrdTX3O5kh7VNJhkoHWG9YA8A6eKHegTYjHInYZw8eeG6Z3ePhfm1bR8PIXrI6dWeYf/le22V7hXZ9F7GFoGUHhsiAm/lowdiT/QHI8eZ98IkirRs3bs4Ysj78FQdPB4xTjQRcm0HyncUwZ6EoPclgxfexgeqMiKL0ZJGA/O4dzwGvky663qyVDslUte6sGDnVdNOVdc22esnVApVnJTzFxiNmIf1Q=</data>
+ </certificate>
+</store>
Index: psi/tags/0.10-os2-b1/conf_os2.pri
===================================================================
--- psi/tags/0.10-os2-b1/conf_os2.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/conf_os2.pri	(revision 38)
@@ -0,0 +1,7 @@
+# OS/2 build parameters
+# Change these variables to reflect your environment
+
+# QCA
+QCA_PATH = ../../qca
+INCLUDEPATH += $$QCA_PATH/src
+LIBS += $$QCA_PATH/qca.lib
Index: psi/tags/0.10-os2-b1/conf_win32.pri
===================================================================
--- psi/tags/0.10-os2-b1/conf_win32.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/conf_win32.pri	(revision 38)
@@ -0,0 +1,7 @@
+# Win32 build parameters
+# Change these variables to reflect your environment
+
+# QCA
+QCA_PATH = ../../qca
+INCLUDEPATH += $$QCA_PATH/src
+LIBS += $$QCA_PATH/qca.lib
Index: psi/tags/0.10-os2-b1/configure
===================================================================
--- psi/tags/0.10-os2-b1/configure	(revision 38)
+++ psi/tags/0.10-os2-b1/configure	(revision 38)
@@ -0,0 +1,1301 @@
+#!/bin/sh
+#
+# Generated by qconf 1.2-CVS ( http://delta.affinix.com/qconf/ )
+#
+
+show_usage() {
+cat <<EOT
+Usage: $0 [OPTION]...
+
+This script creates necessary configuration files to build/install.
+
+Main options:
+  --prefix=[path]     Base path for build/install.  Default: /usr/local
+  --bindir=[path]     Directory for binaries.  Default: PREFIX/bin
+  --datadir=[path]    Directory for data.  Default: PREFIX/share
+  --qtdir=[path]      Directory where Qt is installed.
+  --debug             Enable debug output.
+  --help              This help text.
+
+Dependency options:
+  --with-qca-inc=[path]     Path to QCA include files
+  --with-qca-lib=[path]     Path to QCA library files
+  --with-zlib-inc=[path]    Path to zlib include files
+  --with-zlib-lib=[path]    Path to zlib library files
+  --disable-growl           Disable use of Growl
+  --with-growl=[path]       Path to the Growl framework
+  --disable-xss             Disable use of the XScreenSaver extension
+  --disable-dnotify         Disable use of Linux Directory Notification
+  --disable-ghbnr           Disable use of gethostbyname_r()
+
+EOT
+}
+
+# which/make detection adapted from Qt
+which_command() {
+	OLD_HOME=$HOME
+	HOME=/dev/null
+	export HOME
+
+	WHICH=`which which 2>/dev/null`
+	if echo $WHICH | grep 'shell built-in command' >/dev/null 2>&1; then
+		WHICH=which
+	elif [ -z "$WHICH" ]; then
+		if which which >/dev/null 2>&1; then
+			WHICH=which
+		else
+			for a in /usr/ucb /usr/bin /bin /usr/local/bin; do
+				if [ -x $a/which ]; then
+					WHICH=$a/which
+					break;
+				fi
+			done
+		fi
+	fi
+
+	if [ -z "$WHICH" ]; then
+		OLD_IFS=$IFS
+		IFS=:
+		for a in $PATH; do
+			if [ -x $a/$1 ]; then
+				echo "$a/$1"
+				IFS=$OLD_IFS
+				export IFS
+				HOME=$OLD_HOME
+				export HOME
+				return 0
+			fi
+		done
+		IFS=$OLD_IFS
+		export IFS
+	else
+		a=`"$WHICH" "$1" 2>/dev/null`
+		if [ ! -z "$a" -a -x "$a" ]; then
+			echo "$a"
+			HOME=$OLD_HOME
+			export HOME
+			return 0
+		fi
+	fi
+	HOME=$OLD_HOME
+	export HOME
+	return 1
+}
+WHICH=which_command
+
+# find a make command
+if [ -z "$MAKE" ]; then
+	MAKE=
+	for mk in gmake make; do
+		if $WHICH $mk >/dev/null 2>&1; then
+			MAKE=`$WHICH $mk`
+			break
+		fi
+	done
+	if [ -z "$MAKE" ]; then
+		echo "You don't seem to have 'make' or 'gmake' in your PATH."
+		echo "Cannot proceed."
+		exit 1
+	fi
+fi
+
+show_qt_info() {
+	printf "Be sure you have a proper Qt 4.0 build environment set up.  This means not\n"
+	printf "just Qt, but also a C++ compiler, a make tool, and any other packages\n"
+	printf "necessary for compiling C++ programs.\n"
+	printf "\n"
+	printf "If you are certain everything is installed, then it could be that Qt 4 is not\n"
+	printf "being recognized or that a different version of Qt is being detected by\n"
+	printf "mistake (for example, this could happen if \$QTDIR is pointing to a Qt 3\n"
+	printf "installation).  At least one of the following conditions must be satisfied:\n"
+	printf "\n"
+	printf " 1) --qtdir is set to the location of Qt\n"
+	printf " 2) \$QTDIR is set to the location of Qt\n"
+	printf " 3) QtCore is in the pkg-config database\n"
+	printf " 4) qmake is in the \$PATH\n"
+	printf "\n"
+	printf "This script will use the first one it finds to be true, checked in the above\n"
+	printf "order.  #3 and #4 are the recommended options.  #1 and #2 are mainly for\n"
+	printf "overriding the system configuration.\n"
+	printf "\n"
+}
+
+while [ $# -gt 0 ]; do
+	case "$1" in
+		--prefix=*)
+			PREFIX="${1#--prefix=}"
+			shift
+			;;
+
+		--bindir=*)
+			BINDIR="${1#--bindir=}"
+			shift
+			;;
+
+		--datadir=*)
+			DATADIR="${1#--datadir=}"
+			shift
+			;;
+
+		--qtdir=*)
+			EX_QTDIR="${1#--qtdir=}"
+			shift
+			;;
+
+		--with-qca-inc=*)
+			QC_WITH_QCA_INC="${1#--with-qca-inc=}"
+			shift
+			;;
+
+		--with-qca-lib=*)
+			QC_WITH_QCA_LIB="${1#--with-qca-lib=}"
+			shift
+			;;
+
+		--with-zlib-inc=*)
+			QC_WITH_ZLIB_INC="${1#--with-zlib-inc=}"
+			shift
+			;;
+
+		--with-zlib-lib=*)
+			QC_WITH_ZLIB_LIB="${1#--with-zlib-lib=}"
+			shift
+			;;
+
+		--disable-growl)
+			QC_DISABLE_growl="Y"
+			shift
+			;;
+
+		--with-growl=*)
+			QC_WITH_GROWL="${1#--with-growl=}"
+			shift
+			;;
+
+		--disable-xss)
+			QC_DISABLE_xss="Y"
+			shift
+			;;
+
+		--disable-dnotify)
+			QC_DISABLE_dnotify="Y"
+			shift
+			;;
+
+		--disable-ghbnr)
+			QC_DISABLE_ghbnr="Y"
+			shift
+			;;
+
+		--debug)
+			QC_DEBUG="Y"
+			shift
+			;;
+		--help) show_usage; exit ;;
+		*) show_usage; exit ;;
+	esac
+done
+
+PREFIX=${PREFIX:-/usr/local}
+BINDIR=${BINDIR:-$PREFIX/bin}
+DATADIR=${DATADIR:-$PREFIX/share}
+
+echo "Configuring Psi ..."
+
+if [ "$QC_DEBUG" = "Y" ]; then
+echo
+echo PREFIX=$PREFIX
+echo BINDIR=$BINDIR
+echo DATADIR=$DATADIR
+echo EX_QTDIR=$EX_QTDIR
+echo QC_WITH_QCA_INC=$QC_WITH_QCA_INC
+echo QC_WITH_QCA_LIB=$QC_WITH_QCA_LIB
+echo QC_WITH_ZLIB_INC=$QC_WITH_ZLIB_INC
+echo QC_WITH_ZLIB_LIB=$QC_WITH_ZLIB_LIB
+echo QC_DISABLE_growl=$QC_DISABLE_growl
+echo QC_WITH_GROWL=$QC_WITH_GROWL
+echo QC_DISABLE_xss=$QC_DISABLE_xss
+echo QC_DISABLE_dnotify=$QC_DISABLE_dnotify
+echo QC_DISABLE_ghbnr=$QC_DISABLE_ghbnr
+echo
+fi
+
+printf "Verifying Qt 3.x Multithreaded (MT) build environment ... "
+
+if [ -z "$QTDIR" ]; then
+	if [ "$QC_DEBUG" = "Y" ]; then
+		echo \$QTDIR not set... trying to find Qt manually
+	fi
+	for p in /usr/lib/qt /usr/share/qt /usr/share/qt3 /usr/local/lib/qt /usr/local/share/qt /usr/lib/qt3 /usr/local/lib/qt3 /usr/X11R6/share/qt /usr/qt/3 ; do
+		if [ -d "$p/mkspecs" ]; then
+			QTDIR=$p
+			break;
+		fi;
+	done
+	if [ -z "$QTDIR" ]; then
+		echo fail
+		echo
+		echo "Unable to find Qt 'mkspecs'.  Perhaps you need to"
+		echo "install the Qt 3 development utilities.  You may download"
+		echo "them either from the vendor of your operating system"
+		echo "or from http://www.trolltech.com/"
+		echo
+		echo "If you're sure you have the Qt development utilities"
+		echo "installed, you might try using the --qtdir option."
+		echo
+		exit 1;
+	fi
+	if [ ! -x "$QTDIR/bin/moc" ]; then
+		m=`which moc 2>/dev/null`
+		if [ ! -x "$m" ]; then
+			echo fail
+			echo
+			echo "We found Qt in $QTDIR, but we were unable to locate"
+			echo "the moc utility.  It was not found in $QTDIR/bin"
+			echo "nor in PATH.  This seems to be a very unusual setup."
+			echo "You might try using the --qtdir option."
+			echo
+			exit 1;
+		fi
+		qtpre=`echo $m | awk '{ n = index($0, "/bin/moc"); if (!n) { exit 1; } print substr($0, 0, n-1); exit 0; }' 2>/dev/null`
+		ret="$?"
+		if [ "$ret" != "0" ]; then
+			echo fail
+			echo
+			echo "We found Qt in $QTDIR, but the location of moc"
+			echo "($m) is not suitable for use with this build system."
+			echo "This is a VERY unusual and likely-broken setup.  You"
+			echo "should contact the maintainer of your Qt package."
+			echo
+			exit 1;
+		fi
+		QTDIR=$qtpre
+	fi
+fi
+
+if [ ! -x "$QTDIR/bin/qmake" ]; then
+	if [ "$QC_DEBUG" = "Y" ]; then
+		echo Warning: qmake not in \$QTDIR/bin/qmake
+		echo trying to find it in \$PATH
+	fi
+	qm=`which qmake 2>/dev/null`
+	if [ -x "$qm" ]; then
+		if [ "$QC_DEBUG" = "Y" ]; then
+			echo qmake found in $qm
+		fi
+	else
+		echo fail
+		echo
+		echo Sorry, you seem to have a very unusual setup,
+		echo or I missdetected \$QTDIR=$QTDIR
+		echo
+		echo Please set \$QTDIR manually and make sure that
+		echo \$QTDIR/bin/qmake exists.
+		echo
+		exit 1;
+	fi
+else
+	qm=$QTDIR/bin/qmake
+fi
+
+gen_files() {
+cat >$1/modules.cpp <<EOT
+#line 1 "qt31.qcm"
+/*
+-----BEGIN QCMOD-----
+name: Qt >= 3.1
+-----END QCMOD-----
+*/
+class qc_qt31 : public ConfObj
+{
+public:
+	qc_qt31(Conf *c) : ConfObj(c) {}
+	QString name() const { return "Qt >= 3.1"; }
+	QString shortname() const { return "qt31"; }
+	bool exec()
+	{
+		if(QT_VERSION >= 0x030100) {
+			if(QT_VERSION < 0x030300)
+				conf->addExtra("QXML_STATIC = Y");
+			return true;
+		}
+		else
+			return false;
+	}
+};
+#line 1 "qca.qcm"
+/*
+-----BEGIN QCMOD-----
+name: QCA 1.0
+arg: with-qca-inc=[path],Path to QCA include files
+arg: with-qca-lib=[path],Path to QCA library files
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_qca
+//----------------------------------------------------------------------------
+class qc_qca : public ConfObj
+{
+public:
+	qc_qca(Conf *c) : ConfObj(c) {}
+	QString name() const { return "QCA 1.0"; }
+	QString shortname() const { return "qca"; }
+	bool exec()
+	{
+		QString inc, lib;
+		QString s;
+
+		s = conf->getenv("QC_WITH_QCA_INC");
+		if(!s.isEmpty()) {
+			if(!conf->checkHeader(s, "qca.h"))
+				return false;
+			inc = s;
+		}
+		else {
+			if(!conf->findHeader("qca.h", QStringList(), &s))
+				return false;
+			inc = s;
+		}
+
+		s = conf->getenv("QC_WITH_QCA_LIB");
+		if(!s.isEmpty()) {
+			if(!conf->checkLibrary(s, "qca"))
+				return false;
+			lib = s;
+		}
+		else {
+			if(!conf->findLibrary("qca", &s))
+				return false;
+			lib = s;
+		}
+
+		if(!inc.isEmpty())
+			conf->addIncludePath(inc);
+		if(!lib.isEmpty())
+			conf->addLib(QString("-L") + s);
+		conf->addLib("-lqca");
+
+		return true;
+	}
+};
+#line 1 "zlib.qcm"
+/*
+-----BEGIN QCMOD-----
+name: zlib
+arg: with-zlib-inc=[path],Path to zlib include files
+arg: with-zlib-lib=[path],Path to zlib library files
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_zlib
+//----------------------------------------------------------------------------
+class qc_zlib : public ConfObj
+{
+public:
+	qc_zlib(Conf *c) : ConfObj(c) {}
+	QString name() const { return "zlib"; }
+	QString shortname() const { return "zlib"; }
+	bool exec()
+	{
+		QString inc, lib;
+		QString s;
+
+		s = conf->getenv("QC_WITH_ZLIB_INC");
+		if(!s.isEmpty()) {
+			if(!conf->checkHeader(s, "zlib.h"))
+				return false;
+			inc = s;
+		}
+		else {
+			if(!conf->findHeader("zlib.h", QStringList(), &s))
+				return false;
+			inc = s;
+		}
+
+		s = conf->getenv("QC_WITH_ZLIB_LIB");
+		if(!s.isEmpty()) {
+			if(!conf->checkLibrary(s, "z"))
+				return false;
+			lib = s;
+		}
+		else {
+			if(!conf->findLibrary("z", &s))
+				return false;
+			lib = s;
+		}
+
+		if(!inc.isEmpty())
+			conf->addIncludePath(inc);
+		if(!lib.isEmpty())
+			conf->addLib(QString("-L") + s);
+		conf->addLib("-lz");
+
+		return true;
+	}
+};
+#line 1 "growl.qcm"
+/*
+-----BEGIN QCMOD-----
+name: Growl
+arg: with-growl=[path],Path to the Growl framework
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_growl
+//----------------------------------------------------------------------------
+class qc_growl : public ConfObj
+{
+public:
+	qc_growl(Conf *c) : ConfObj(c) {}
+	QString name() const { return "Growl"; }
+	QString shortname() const { return "growl"; }
+
+	// TODO: This should go into ConfObj
+    bool checkFramework(const QString &path, const QString &name)
+    {
+        QString str =
+            "int main()\n"
+            "{\n"
+            "    return 0;\n"
+            "}\n";
+
+        QString extra;
+        if(!path.isEmpty())
+            extra += QString("-F") + path + ' ';
+        extra += QString("-framework ") + name;
+        if(!conf->doCompileAndLink(str, extra))
+            return false;
+        return true;
+    }
+	
+	// TODO: This should go into ConfObj
+	void addFrameworkPath(const QString& str)
+	{
+		conf->addExtra("QMAKE_CXXFLAGS += -F" + str);
+		conf->addLib("-F" + str);
+	}
+
+	bool exec()
+	{
+#ifdef Q_WS_MAC
+		QString growl_path = conf->getenv("QC_WITH_GROWL");
+		if(!checkFramework(growl_path, "Growl"))
+			return false;
+
+		if(!growl_path.isEmpty()) 
+			addFrameworkPath(growl_path);
+		conf->addLib("-framework Growl");
+		conf->addDefine("HAVE_GROWL");
+
+		return true;
+#else
+		return false;
+#endif
+	}
+};
+#line 1 "xss.qcm"
+/*
+-----BEGIN QCMOD-----
+name: the XScreenSaver extension
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_xss
+//----------------------------------------------------------------------------
+class qc_xss : public ConfObj
+{
+public:
+	enum { OK, NEEDLIB, FAIL };
+	qc_xss(Conf *c) : ConfObj(c)
+	{
+	}
+
+	~qc_xss()
+	{
+		remove("xssprobe_test.c");
+		remove("xssprobe_test.o");
+		remove("xssprobe_test");
+	}
+
+	QString name() const
+	{
+		return "the XScreenSaver extension";
+	}
+
+	QString shortname() const { return "xss"; }
+
+	int do_write()
+	{
+		char *xsstest =
+			"#include<X11/Xlib.h>\n"
+			"#include<X11/Xutil.h>\n"
+			"#include<X11/extensions/scrnsaver.h>\n"
+			"\n"
+			"int main()\n"
+			"{\n"
+			"    XScreenSaverQueryExtension(NULL, NULL, NULL);\n"
+			"    return 0;\n"
+			"}\n";
+
+		FILE *f;
+		f = fopen("xssprobe_test.c", "w");
+		if(!f)
+			return 0;
+		fwrite(xsstest, strlen(xsstest), 1, f);
+		fclose(f);
+
+		return 1;
+	}
+
+	int do_compile()
+	{
+		QString inc = conf->expandIncludes(conf->qvar("QMAKE_INCDIR_X11"));
+		QString str = conf->qvar("QMAKE_CC") + " -c " + inc + " xssprobe_test.c -o xssprobe_test.o";
+		int r = conf->doCommand(str);
+		if(r == 0)
+			return 1;
+		else
+			return 0;
+	}
+
+	int do_link()
+	{
+		QString lib = conf->expandLibs(conf->qvar("QMAKE_LIBDIR_X11"));
+		QString inc = conf->expandIncludes(conf->qvar("QMAKE_INCDIR_X11"));
+		QString str = conf->qvar("QMAKE_CC") + " xssprobe_test.o -o xssprobe_test " + lib + ' ' + conf->qvar("QMAKE_LIBS_X11");
+		int r = conf->doCommand(str);
+		if(r == 0)
+			return 1;
+		else
+			return 0;
+	}
+
+	int do_linkLib()
+	{
+		QString lib = conf->expandLibs(conf->qvar("QMAKE_LIBDIR_X11"));
+		QString inc = conf->expandIncludes(conf->qvar("QMAKE_INCDIR_X11"));
+		QString str = conf->qvar("QMAKE_CC") + " xssprobe_test.o -o xssprobe_test " + lib + ' ' + conf->qvar("QMAKE_LIBS_X11") + " -lXss";
+		int r = conf->doCommand(str);
+		if(r == 0)
+			return 1;
+		else
+			return 0;
+	}
+
+	int do_all()
+	{
+		if(!do_write())
+			return FAIL;
+		if(!do_compile())
+			return FAIL;
+		if(do_link())
+			return OK;
+		if(do_linkLib())
+			return NEEDLIB;
+		return FAIL;
+	}
+
+	bool exec()
+	{
+		int r = do_all();
+		if(r == OK) {
+			conf->addDefine("HAVE_XSS");
+			return true;
+		}
+		else if(r == NEEDLIB) {
+			conf->addDefine("HAVE_XSS");
+			conf->addLib("-lXss");
+			return true;
+		}
+	}
+};
+#line 1 "dnotify.qcm"
+/*
+-----BEGIN QCMOD-----
+name: Linux Directory Notification
+-----END QCMOD-----
+*/
+
+#include<unistd.h>
+#include<fcntl.h>
+#include<signal.h>
+#include<sys/utsname.h>
+
+//----------------------------------------------------------------------------
+// qc_dnotify
+//----------------------------------------------------------------------------
+class qc_dnotify : public ConfObj
+{
+public:
+	qc_dnotify(Conf *c) : ConfObj(c)
+	{
+	}
+
+	~qc_dnotify()
+	{
+		remove("ftest.c");
+		remove("ftest.o");
+	}
+
+	QString name() const { return "Linux Directory Notification"; }
+	QString shortname() const { return "dnotify"; }
+
+	bool do_write()
+	{
+		char *fdata =
+			"#define _GNU_SOURCE\n"
+			"#include<unistd.h>\n"
+			"#include<fcntl.h>\n"
+			"#include<signal.h>\n"
+			"#include<sys/utsname.h>\n"
+			"\n"
+			"int main()\n"
+			"{\n"
+			"    DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT|DN_MODIFY|DN_ATTRIB;\n"
+			"    return 0;\n"
+			"}\n";
+
+		FILE *f;
+		f = fopen("ftest.c", "w");
+		if(!f)
+			return false;
+		fwrite(fdata, strlen(fdata), 1, f);
+		fclose(f);
+
+		return true;
+	}
+
+	bool do_compile()
+	{
+		QString str = conf->qvar("QMAKE_CC") + " -c ftest.c -o ftest.o";
+		int r = conf->doCommand(str);
+		if(r == 0)
+			return true;
+		else
+			return false;
+	}
+
+	bool exec()
+	{
+		// taken from KDE
+		bool supports_dnotify = true; // not guilty until proven guilty
+		struct utsname uts;
+		int major, minor, patch;
+		if(uname(&uts) < 0)
+			supports_dnotify = false; // *shrug*
+		else if(sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
+			supports_dnotify = false; // *shrug*
+		else if( major * 1000000 + minor * 1000 + patch < 2004019 ) // <2.4.19
+			supports_dnotify = false;
+		if(!supports_dnotify)
+			return false;
+
+		if(!do_write())
+			return false;
+		if(!do_compile())
+			return false;
+
+		conf->addDefine("HAVE_DNOTIFY");
+		return true;
+	}
+};
+#line 1 "ghbnr.qcm"
+/*
+-----BEGIN QCMOD-----
+name: gethostbyname_r()
+-----END QCMOD-----
+*/
+class qc_ghbnr : public ConfObj
+{
+public:
+	qc_ghbnr(Conf *c) : ConfObj(c)
+	{
+	}
+
+	~qc_ghbnr()
+	{
+		remove("ftest.c");
+		remove("ftest");
+	}
+
+	QString name() const { return "gethostbyname_r()"; }
+	QString shortname() const { return "ghbnr"; }
+
+	bool exec()
+	{
+		char *fdata =
+			"#include<netdb.h>\n"
+			"\n"
+			"int main()\n"
+			"{\n"
+			"    gethostbyname_r(\"\", 0, 0, 0, 0, 0);\n"
+			"    return 0;\n"
+			"}\n";
+
+		FILE *f;
+		f = fopen("ftest.c", "w");	
+		if(!f)
+			return false;
+		fwrite(fdata, strlen(fdata), 1, f);
+		fclose(f);
+
+		QString str = conf->qvar("QMAKE_CC") + " ftest.c -o ftest";
+		int r = conf->doCommand(str);
+		if(r == 0) {
+			conf->addDefine("HAVE_GETHOSTBYNAME_R");
+			return true;
+		}
+		else
+			return false;
+	}
+};
+#line 1 "conf.qcm"
+/*
+-----BEGIN QCMOD-----
+name: Psi Configuration
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_conf
+//----------------------------------------------------------------------------
+class qc_conf : public ConfObj
+{
+public:
+	qc_conf(Conf *c) : ConfObj(c) {}
+	QString name() const { return "Psi Configuration"; }
+	QString shortname() const { return "conf"; }
+	QString checkString() const { return "Writing configuration ..."; }
+	bool exec()
+	{
+		conf->addExtra(QString("PREFIX=%1").arg(conf->getenv("PREFIX")));
+		conf->addExtra(QString("DATADIR=%1/psi").arg(conf->getenv("DATADIR")));
+
+		QFile file("src/config.h");
+		if ( file.open( IO_WriteOnly ) ) {
+			QTextStream stream( &file );
+			stream << "#define PSI_DATADIR \"" << conf->getenv("DATADIR") << "/psi\"" << endl;
+		}
+		
+		conf->addDefine("HAVE_CONFIG");
+		
+		return true;
+	}
+};
+
+EOT
+cat >$1/modules_new.cpp <<EOT
+    o = new qc_qt31(conf);
+    o->required = true;
+    o->disabled = false;
+    o = new qc_qca(conf);
+    o->required = true;
+    o->disabled = false;
+    o = new qc_zlib(conf);
+    o->required = true;
+    o->disabled = false;
+    o = new qc_growl(conf);
+    o->required = false;
+    o->disabled = false;
+    o = new qc_xss(conf);
+    o->required = false;
+    o->disabled = false;
+    o = new qc_dnotify(conf);
+    o->required = false;
+    o->disabled = false;
+    o = new qc_ghbnr(conf);
+    o->required = false;
+    o->disabled = false;
+    o = new qc_conf(conf);
+    o->required = true;
+    o->disabled = false;
+
+EOT
+cat >$1/conf.h <<EOT
+
+EOT
+cat >$1/conf.cpp <<EOT
+#include<stdio.h>
+#include<stdlib.h>
+#include<qstring.h>
+#include<qdict.h>
+#include<qptrlist.h>
+#include<qfileinfo.h>
+#include<qfile.h>
+#include<qdir.h>
+#include<qstringlist.h>
+#include<qobject.h>
+
+class MocTestObject : public QObject
+{
+	Q_OBJECT
+public:
+	MocTestObject() {}
+};
+
+class Conf;
+
+class ConfObj
+{
+public:
+	ConfObj(Conf *c);
+	virtual ~ConfObj();
+
+	virtual QString name() const=0;
+	virtual QString shortname() const=0;
+	virtual QString checkString() const;
+	virtual QString resultString() const;
+	virtual bool exec()=0;
+
+	Conf *conf;
+	bool required;
+	bool disabled;
+};
+
+typedef QPtrList<ConfObj> ConfObjList;
+typedef QPtrListIterator<ConfObj> ConfObjListIt;
+
+class Conf
+{
+public:
+	Conf() : vars(17)
+	{
+		list.setAutoDelete(true);
+		vars.setAutoDelete(true);
+
+		vars.insert("QMAKE_INCDIR_X11", new QString(X11_INC));
+		vars.insert("QMAKE_LIBDIR_X11", new QString(X11_LIBDIR));
+		vars.insert("QMAKE_LIBS_X11",   new QString(X11_LIB));
+		vars.insert("QMAKE_CC",         new QString(CC));
+		vars.insert("QMAKE_CXX",        new QString(CXX));
+
+		do_debug = false;
+		done_debug = false;
+	}
+
+	~Conf()
+	{
+	}
+
+	void added(ConfObj *o)
+	{
+		list.append(o);
+	}
+
+	QString getenv(const QString &var)
+	{
+		char *p = ::getenv(var.latin1());
+		if(!p)
+			return QString::null;
+		return QString(p);
+	}
+
+	void debug(const QString &s)
+	{
+		if(do_debug) {
+			if(!done_debug)
+				printf("\n");
+			done_debug = true;
+			printf(" * %s\n", s.latin1());
+		}
+	}
+
+	bool exec()
+	{
+		if(getenv("QC_DEBUG") == "Y")
+			do_debug = true;
+
+		ConfObjListIt it(list);
+		for(ConfObj *o; (o = it.current()); ++it) {
+			// if this was a disabled-by-default option, check if it was enabled
+			if(o->disabled) {
+				QString v = QString("QC_ENABLE_") + o->shortname();
+				if(getenv(v) != "Y")
+					continue;
+			}
+			// and the opposite?
+			else {
+				QString v = QString("QC_DISABLE_") + o->shortname();
+				if(getenv(v) == "Y")
+					continue;
+			}
+
+			QString check = o->checkString();
+			if(check.isEmpty())
+				check = QString("Checking for %1 ...").arg(o->name());
+			printf("%s", check.latin1());
+			fflush(stdout);
+
+			done_debug = false;
+			bool ok = o->exec();
+
+			QString result = o->resultString();
+			if(result.isEmpty()) {
+				if(ok)
+					result = "yes";
+				else
+					result = "no";
+			}
+			if(done_debug)
+				printf(" -> %s\n", result.latin1());
+			else
+				printf(" %s\n", result.latin1());
+
+			if(!ok && o->required) {
+				printf("\nError: need %s!\n", o->name().latin1());
+				return false;
+			}
+		}
+		return true;
+	}
+
+	const QString & qvar(const QString &s)
+	{
+		QString *p = vars.find(s);
+		if(p)
+			return *p;
+		else
+			return blank;
+	}
+
+	QString expandIncludes(const QString &inc)
+	{
+		return QString("-I") + inc;
+	}
+
+	QString expandLibs(const QString &lib)
+	{
+		return QString("-L") + lib;
+	}
+
+	int doCommand(const QString &s)
+	{
+		debug(QString("[%1]").arg(s));
+		QString fullcmd;
+		if(do_debug)
+			fullcmd = s;
+		else
+			fullcmd = s + " 1>/dev/null 2>/dev/null";
+		int r = system(fullcmd.latin1());
+		debug(QString("returned: %1").arg(r));
+		return r;
+	}
+
+	bool doCompileAndLink(const QString &filedata, const QString &flags, int *retcode=0)
+	{
+		QDir dir(".");
+		QString fname = "atest.c";
+		QString out = "atest";
+		QFile f(fname);
+		QCString cs = filedata.latin1();
+		if(!f.open(IO_WriteOnly | IO_Truncate)) {
+			debug("unable to open atest.c for writing");
+			return false;
+		}
+		if(f.writeBlock(cs.data(), cs.length()) == -1) {
+			debug("error writing to atest.c");
+			return false;
+		}
+		f.close();
+
+		debug(QString("Wrote atest.c:\n%1").arg(filedata));
+
+		QString str = qvar("QMAKE_CXX") + ' ' + fname + " -o " + out;
+		if(!flags.isEmpty()) {
+			str += ' ';
+			str += flags;
+		}
+
+		int r = doCommand(str);
+		if(r == 0 && retcode)
+			*retcode = doCommand(QString("./") + out);
+		dir.remove(fname);
+		dir.remove(out);
+		if(r != 0)
+			return false;
+		return true;
+	}
+
+	bool checkHeader(const QString &path, const QString &h)
+	{
+		QFileInfo fi(path + '/' + h);
+		if(fi.exists())
+			return true;
+		return false;
+	}
+
+	bool findHeader(const QString &h, const QStringList &ext, QString *inc)
+	{
+		if(checkHeader("/usr/include", h)) {
+			*inc = "";
+			return true;
+		}
+		QStringList dirs;
+		dirs += "/usr/local/include";
+		dirs += ext;
+		for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
+			if(checkHeader(*it, h)) {
+				*inc = *it;
+				return true;
+			}
+		}
+		return false;
+	}
+
+	bool checkLibrary(const QString &path, const QString &name)
+	{
+		QString str =
+			"int main()\n"
+			"{\n"
+			"    return 0;\n"
+			"}\n";
+
+		QString extra;
+		if(!path.isEmpty())
+			extra += QString("-L") + path + ' ';
+		extra += QString("-l") + name;
+		if(!doCompileAndLink(str, extra))
+			return false;
+		return true;
+	}
+
+	bool findLibrary(const QString &name, QString *lib)
+	{
+		if(checkLibrary("", name)) {
+			*lib = "";
+			return true;
+		}
+		if(checkLibrary("/usr/local/lib", name)) {
+			*lib = "/usr/local/lib";
+			return true;
+		}
+		return false;
+	}
+
+	void addDefine(const QString &str)
+	{
+		if(DEFINES.isEmpty())
+			DEFINES = str;
+		else
+			DEFINES += QString(" ") + str;
+		debug(QString("DEFINES += %1").arg(str));
+	}
+
+	void addLib(const QString &str)
+	{
+		if(LIBS.isEmpty())
+			LIBS = str;
+		else
+			LIBS += QString(" ") + str;
+		debug(QString("LIBS += %1").arg(str));
+	}
+
+	void addIncludePath(const QString &str)
+	{
+		if(INCLUDEPATH.isEmpty())
+			INCLUDEPATH = str;
+		else
+			INCLUDEPATH += QString(" ") + str;
+		debug(QString("INCLUDEPATH += %1").arg(str));
+	}
+
+	void addExtra(const QString &str)
+	{
+		extra += str + '\n';
+		debug(QString("extra += %1").arg(str));
+	}
+
+	QString DEFINES;
+	QString INCLUDEPATH;
+	QString LIBS;
+	QString extra;
+
+private:
+	ConfObjList list;
+	QDict<QString> vars;
+	QString blank;
+	bool do_debug, done_debug;
+};
+
+ConfObj::ConfObj(Conf *c)
+{
+	conf = c;
+	conf->added(this);
+	required = false;
+	disabled = false;
+}
+
+ConfObj::~ConfObj()
+{
+}
+
+QString ConfObj::checkString() const
+{
+	return QString();
+}
+
+QString ConfObj::resultString() const
+{
+	return QString();
+}
+
+#include"modules.cpp"
+
+//----------------------------------------------------------------------------
+// main
+//----------------------------------------------------------------------------
+int main()
+{
+	Conf *conf = new Conf;
+	ConfObj *o;
+	o = 0;
+#include"modules_new.cpp"
+
+	printf("ok\n");
+	bool success = false;
+	if(conf->exec()) {
+		QFile f("conf.pri");
+		if(!f.open(IO_WriteOnly | IO_Truncate)) {
+			printf("Error writing %s\n", f.name().latin1());
+			return 1;
+		}
+
+		QString str;
+		str += "# qconf\n";
+		str += "QT_PATH_PLUGINS = " + QString(qInstallPathPlugins()) + '\n';
+		if(!conf->DEFINES.isEmpty())
+			str += "DEFINES += " + conf->DEFINES + '\n';
+		if(!conf->INCLUDEPATH.isEmpty())
+			str += "INCLUDEPATH += " + conf->INCLUDEPATH + '\n';
+		if(!conf->LIBS.isEmpty())
+			str += "LIBS += " + conf->LIBS + '\n';
+		if(!conf->extra.isEmpty())
+			str += conf->extra;
+		str += '\n';
+
+		char *p = getenv("BINDIR");
+		if(p) {
+			str += QString("target.path = ") + p + '\n';
+			str += "INSTALLS += target\n";
+		}
+
+		QCString cs = str.latin1();
+		f.writeBlock(cs.data(), cs.length());
+		f.close();
+		success = true;
+	}
+	delete conf;
+
+	if(success)
+		return 0;
+	else
+		return 1;
+}
+
+#include"conf.moc"
+
+
+EOT
+cat >$1/conf.pro <<EOT
+TEMPLATE = app
+CONFIG += qt x11 thread console
+TARGET = conf
+
+DEFINES += X11_INC='"\$\$QMAKE_INCDIR_X11"'
+DEFINES += X11_LIBDIR='"\$\$QMAKE_LIBDIR_X11"'
+DEFINES += X11_LIB='"\$\$QMAKE_LIBS_X11"'
+DEFINES += CC='"\$\$QMAKE_CC"'
+DEFINES += CXX='"\$\$QMAKE_CXX"'
+
+SOURCES += conf.cpp
+
+DEFINES += HAVE_MODULES
+
+EOT
+}
+
+export PREFIX
+export BINDIR
+export DATADIR
+export EX_QTDIR
+export QC_WITH_QCA_INC
+export QC_WITH_QCA_LIB
+export QC_WITH_ZLIB_INC
+export QC_WITH_ZLIB_LIB
+export QC_DISABLE_growl
+export QC_WITH_GROWL
+export QC_DISABLE_xss
+export QC_DISABLE_dnotify
+export QC_DISABLE_ghbnr
+export QC_DEBUG
+rm -rf .qconftemp
+(
+	mkdir .qconftemp
+	gen_files .qconftemp
+	cd .qconftemp
+	$qm conf.pro >/dev/null
+	QTDIR=$QTDIR make clean >/dev/null 2>&1
+	QTDIR=$QTDIR make >../conf.log 2>&1
+)
+
+if [ "$?" != "0" ]; then
+	rm -rf .qconftemp
+	echo fail
+	echo
+	echo "There was an error compiling 'conf'.  Be sure you have a proper"
+	echo "Qt 3.x Multithreaded (MT) build environment set up.  This"
+	echo "means not just Qt, but also a C++ compiler, the 'make' command,"
+	echo "and any other packages necessary to compile C++ programs."
+	echo "See conf.log for details."
+	if [ ! -f "$QTDIR/lib/libqt-mt.so.3" ]; then
+		echo
+		echo "One possible reason is that you don't have"
+		echo "libqt-mt.so.3 installed in $QTDIR/lib/."
+	fi
+	echo
+	exit 1;
+fi
+
+.qconftemp/conf
+ret="$?"
+if [ "$ret" = "1" ]; then
+	rm -rf .qconftemp
+	echo
+	exit 1;
+else
+	if [ "$ret" != "0" ]; then
+		rm -rf .qconftemp
+		echo fail
+		echo
+		echo "Unexpected error launching 'conf'"
+		echo
+		exit 1;
+	fi
+fi
+rm -rf .qconftemp
+
+if [ -x "./qcextra" ]; then
+	./qcextra
+fi
+# run qmake
+$qm psi.pro
+if [ "$?" != "0" ]; then
+	echo
+	exit 1;
+fi
+cat >Makefile.tmp <<EOT
+export QTDIR = $QTDIR
+export PATH = $QTDIR/bin:$PATH
+EOT
+cat Makefile >> Makefile.tmp
+rm -f Makefile
+cp -f Makefile.tmp Makefile
+rm -f Makefile.tmp
+
+echo
+echo "Good, your configure finished.  Now run 'make'."
+echo
Index: psi/tags/0.10-os2-b1/cutestuff/Doxygen
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/Doxygen	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/Doxygen	(revision 38)
@@ -0,0 +1,946 @@
+# Doxyfile 1.2.16
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "CuteStuff"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = "docs"
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, 
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Korean, 
+# Norwegian, Polish, Portuguese, Romanian, Russian, Slovak, Slovene, 
+# Spanish, Swedish and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these class will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = NO
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = NO
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH        = 
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower case letters. If set to YES upper case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments  will behave just like the Qt-style comments (thus requiring an 
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consist of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = util/base64.h util/base64.cpp util/bytestream.h util/bytestream.cpp  network/ndns.h network/ndns.cpp
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS          = 
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = 
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
+INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse.
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, 
+# or Internet explorer 4.0+). Note that for large projects the tree generation 
+# can take a very long time. In such cases it is better to disable this feature. 
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimised for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assigments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_XML           = NO
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed.
+
+PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line and do not end with a semicolon. Such function macros are typically 
+# used for boiler-plate code, and will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermedate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
+
+# The CGI_NAME tag should be the name of the CGI script that 
+# starts the search engine (doxysearch) with the correct parameters. 
+# A script with this name will be generated by doxygen.
+
+CGI_NAME               = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the 
+# cgi binaries are located. See the documentation of your http daemon for 
+# details.
+
+CGI_URL                = 
+
+# The DOC_URL tag should be the absolute URL to the directory where the 
+# documentation is located. If left blank the absolute path to the 
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL                = 
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the 
+# documentation is located. If left blank the directory on the local machine 
+# will be used.
+
+DOC_ABSPATH            = 
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary 
+# is installed.
+
+BIN_ABSPATH            = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to 
+# documentation generated for other projects. This allows doxysearch to search 
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS          = 
Index: psi/tags/0.10-os2-b1/cutestuff/README
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/README	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/README	(revision 38)
@@ -0,0 +1,14 @@
+iconset     - generic classes for handling iconsets / animations
+idle        - detecting desktop idle
+input       - making life easier with text input (including richtext)
+openpgp     - pgp/gpg classes
+richtext    - richtext parsing function, xhtml conversion
+ssl         - SSL
+tray        - desktop tray icon
+util        - various things, see util/TODO
+globalaccel - global hotkeys
+network     - sockets, servers, dns, and proxies
+sasl        - SASL library
+xmlsec      - XML Encryption
+crash       - generates some (hopefully useful) feedback when program crashes
+systemwatch - Watches changes in the system state (such as sleep mode, ...)
Index: psi/tags/0.10-os2-b1/cutestuff/TODO
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/TODO	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/TODO	(revision 38)
@@ -0,0 +1,25 @@
+test:
+  httppoll
+  sasl
+  xmlenc
+
+code:
+  bsocket: 'maintain' internal sockets even after destruct (till flush)
+  qssl: server support
+  securestream: wrap QSSLFilter as ByteStream
+  qrandom: better randomness (use /dev/urandom on unix, srand on windows)
+  floating TODOs in gnupg, gpgproc ?
+  import misha's code
+  finish globalaccel
+  finish dirwatch
+  trayicon?
+
+port:
+  win32: bconsole
+
+document:
+  sha1
+  servsock
+  srvresolver
+  bsocket
+
Index: psi/tags/0.10-os2-b1/cutestuff/adconn.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/adconn.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/adconn.cpp	(revision 38)
@@ -0,0 +1,262 @@
+#include"adconn.h"
+
+#include<qapplication.h>
+#include"bconsole.h"
+#include"bsocket.h"
+#include"httpconnect.h"
+#include"socks.h"
+#include"httppoll.h"
+
+#include<stdio.h>
+
+class App::Private
+{
+public:
+	Private() {}
+
+	ByteStream *bs;
+	BConsole *c;
+	int mode;
+};
+
+App::App(int mode, const QString &host, int port, const QString &serv, const QString &proxy_host, int proxy_port, const QString &proxy_user, const QString &proxy_pass)
+:QObject(0)
+{
+	d = new Private;
+	d->c = new BConsole;
+	connect(d->c, SIGNAL(connectionClosed()), SLOT(con_connectionClosed()));
+	connect(d->c, SIGNAL(readyRead()), SLOT(con_readyRead()));
+	d->bs = 0;
+
+	d->mode = mode;
+
+	if(mode == 0 || mode == 1) {
+		BSocket *s = new BSocket;
+		d->bs = s;
+		connect(s, SIGNAL(connected()), SLOT(st_connected()));
+		connect(s, SIGNAL(error(int)), SLOT(st_error(int)));
+		if(mode == 0) {
+			fprintf(stderr, "adconn: Connecting to %s:%d ...\n", host.latin1(), port);
+			s->connectToHost(host, port);
+		}
+		else {
+			fprintf(stderr, "adconn: Connecting to '%s' server at %s ...\n", serv.latin1(), host.latin1());
+			s->connectToServer(host, serv);
+		}
+	}
+	else if(mode == 2) {
+		HttpConnect *s = new HttpConnect;
+		d->bs = s;
+		connect(s, SIGNAL(connected()), SLOT(st_connected()));
+		connect(s, SIGNAL(error(int)), SLOT(st_error(int)));
+		fprintf(stderr, "adconn: Connecting to %s:%d via %s:%d (https)\n", host.latin1(), port, proxy_host.latin1(), proxy_port);
+		if(!proxy_user.isEmpty())
+			s->setAuth(proxy_user, proxy_pass);
+		s->connectToHost(proxy_host, proxy_port, host, port);
+	}
+	else if(mode == 3) {
+		SocksClient *s = new SocksClient;
+		d->bs = s;
+		connect(s, SIGNAL(connected()), SLOT(st_connected()));
+		connect(s, SIGNAL(error(int)), SLOT(st_error(int)));
+		fprintf(stderr, "adconn: Connecting to %s:%d via %s:%d (socks)\n", host.latin1(), port, proxy_host.latin1(), proxy_port);
+		if(!proxy_user.isEmpty())
+			s->setAuth(proxy_user, proxy_pass);
+		s->connectToHost(proxy_host, proxy_port, host, port);
+	}
+	else if(mode == 4) {
+		HttpPoll *s = new HttpPoll;
+		d->bs = s;
+		connect(s, SIGNAL(connected()), SLOT(st_connected()));
+		connect(s, SIGNAL(error(int)), SLOT(st_error(int)));
+		fprintf(stderr, "adconn: Connecting to %s via %s:%d (poll)\n", host.latin1(), proxy_host.latin1(), proxy_port);
+		if(!proxy_user.isEmpty())
+			s->setAuth(proxy_user, proxy_pass);
+		s->connectToHost(proxy_host, proxy_port, host);
+	}
+}
+
+App::~App()
+{
+	delete d->bs;
+	delete d->c;
+	delete d;
+}
+
+void App::st_connected()
+{
+	fprintf(stderr, "adconn: Connected\n");
+
+	// map the signals
+	connect(d->bs, SIGNAL(connectionClosed()), SLOT(st_connectionClosed()));
+	connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(st_delayedCloseFinished()));
+	connect(d->bs, SIGNAL(readyRead()), SLOT(st_readyRead()));
+}
+
+void App::st_connectionClosed()
+{
+	fprintf(stderr, "adconn: Connection closed by foreign host.\n");
+	quit();
+}
+
+void App::st_delayedCloseFinished()
+{
+	quit();
+}
+
+void App::st_readyRead()
+{
+	QByteArray a = d->bs->read();
+	d->c->write(a);
+}
+
+void App::st_error(int x)
+{
+	fprintf(stderr, "adconn: Stream error [%d].\n", x);
+	quit();
+}
+
+void App::con_connectionClosed()
+{
+	fprintf(stderr, "adconn: Closing.\n");
+	d->bs->close();
+	if(d->bs->bytesToWrite() == 0)
+		quit();
+}
+
+void App::con_readyRead()
+{
+	QByteArray a = d->c->read();
+	d->bs->write(a);
+}
+
+
+int main(int argc, char **argv)
+{
+	QApplication app(argc, argv, false);
+
+	if(argc < 2) {
+		printf("usage: adconn [options] [host]\n");
+		printf("   [host] must be in the form 'host:port' or 'domain#server'\n");
+		printf("   When using proxy 'poll', [host] must be a URL\n");
+		printf("   Options:\n");
+		printf("     --proxy=[https|poll|socks],host:port\n");
+		printf("     --proxy-auth=user,pass\n");
+		printf("\n");
+		return 0;
+	}
+
+	int mode = 0;
+	QString proxy_host;
+	int proxy_port=0;
+	QString proxy_user, proxy_pass;
+
+	for(int at = 1; at < argc; ++at) {
+		QString s = argv[at];
+
+		// is it an option?
+		if(s.left(2) == "--") {
+			QString name;
+			QStringList args;
+			int n = s.find('=', 2);
+			if(n != -1) {
+				name = s.mid(2, n-2);
+				++n;
+				args = QStringList::split(',', s.mid(n), true);
+			}
+			else {
+				name = s.mid(2);
+				args.clear();
+			}
+
+			// eat the arg
+			--argc;
+			for(int x = at; x < argc; ++x)
+				argv[x] = argv[x+1];
+			--at; // don't advance
+
+			// process option
+			if(name == "proxy") {
+				QString type = args[0];
+				QString s = args[1];
+				int n = s.find(':');
+				if(n == -1) {
+					printf("invalid host:port for proxy\n");
+					return 0;
+				}
+				proxy_host = s.mid(0, n);
+				++n;
+				proxy_port = s.mid(n).toInt();
+
+				if(type == "https") {
+					mode = 2;
+				}
+				else if(type == "poll") {
+					mode = 4;
+				}
+				else if(type == "socks") {
+					mode = 3;
+				}
+				else {
+					printf("no such proxy type '%s'\n", type.latin1());
+					return 0;
+				}
+			}
+			else if(name == "proxy-auth") {
+				proxy_user = args[0];
+				proxy_pass = args[1];
+			}
+			else {
+				printf("Unknown option '%s'\n", name.latin1());
+				return 0;
+			}
+		}
+	}
+
+	if(argc < 2) {
+		if(mode == 4)
+			printf("No URL specified\n");
+		else
+			printf("No host specified!\n");
+		return 0;
+	}
+
+	QString host, serv;
+	int port=0;
+
+	if(mode == 4) {
+		host = argv[1];
+	}
+	else {
+		QString s = argv[1];
+		int n = s.find(':');
+		if(n == -1) {
+			n = s.find('#');
+			if(n == -1) {
+				printf("No port or server specified!\n");
+				return 0;
+			}
+
+			if(mode >= 2) {
+				printf("Can't use domain#server with proxy!\n");
+				return 0;
+			}
+			host = s.mid(0, n);
+			++n;
+			serv = s.mid(n);
+			mode = 1;
+		}
+		else {
+			host = s.mid(0, n);
+			++n;
+			port = s.mid(n).toInt();
+		}
+	}
+
+	App *a = new App(mode, host, port, serv, proxy_host, proxy_port, proxy_user, proxy_pass);
+	QObject::connect(a, SIGNAL(quit()), &app, SLOT(quit()));
+	app.exec();
+	delete a;
+
+	return 0;
+}
Index: psi/tags/0.10-os2-b1/cutestuff/adconn.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/adconn.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/adconn.h	(revision 38)
@@ -0,0 +1,31 @@
+#ifndef TEST_H
+#define TEST_H
+
+#include<qobject.h>
+
+class App : public QObject
+{
+	Q_OBJECT
+public:
+	App(int mode, const QString &host, int port, const QString &serv, const QString &proxy_host, int proxy_port, const QString &proxy_user, const QString &proxy_pass);
+	~App();
+
+signals:
+	void quit();
+
+private slots:
+	void st_connected();
+	void st_connectionClosed();
+	void st_delayedCloseFinished();
+	void st_readyRead();
+	void st_error(int);
+
+	void con_connectionClosed();
+	void con_readyRead();
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/adconn.pro
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/adconn.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/adconn.pro	(revision 38)
@@ -0,0 +1,41 @@
+CONFIG += thread
+TARGET  = adconn
+
+DEFINES += PROX_DEBUG
+
+INCLUDEPATH += util network sasl
+
+HEADERS = \
+	util/bytestream.h \
+	util/base64.h \
+	util/sha1.h \
+	util/bconsole.h \
+	util/qrandom.h \
+	network/ndns.h \
+	network/srvresolver.h \
+	network/bsocket.h \
+	network/httpconnect.h \
+	network/httppoll.h \
+	network/servsock.h \
+	network/socks.h \
+	sasl/qsasl.h \
+	adconn.h
+
+SOURCES = \
+	util/bytestream.cpp \
+	util/base64.cpp \
+	util/sha1.cpp \
+	util/bconsole.cpp \
+	util/qrandom.cpp \
+	network/ndns.cpp \
+	network/srvresolver.cpp \
+	network/bsocket.cpp \
+	network/httpconnect.cpp \
+	network/httppoll.cpp \
+	network/servsock.cpp \
+	network/socks.cpp \
+	sasl/qsasl.cpp \
+	adconn.cpp
+
+LIBS += -lsasl2
+
Index: psi/tags/0.10-os2-b1/cutestuff/csnsproc
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/csnsproc	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/csnsproc	(revision 38)
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+if [ $# -lt 2 ]; then
+	echo "Usage: $0 {file} {namespace}"
+	exit
+fi
+
+sed -e "s/\/\/.*CS_NAMESPACE_BEGIN/namespace $2\ {/" -e "s/\/\/.*CS_NAMESPACE_END/}/" $1
+
Index: psi/tags/0.10-os2-b1/cutestuff/enctest.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/enctest.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/enctest.cpp	(revision 38)
@@ -0,0 +1,94 @@
+#include<qdom.h>
+#include<qtextstream.h>
+#include<stdio.h>
+
+#include"util/cipher.h"
+#include"xmlsec/xmlenc.h"
+
+static QCString elemToString(const QDomElement &e)
+{
+	QString out;
+	QTextStream ts(&out, IO_WriteOnly);
+	e.save(ts, 1);
+	return out.utf8();
+}
+
+int main()
+{
+	QDomDocument doc;
+	QDomElement order = doc.createElement("order");
+	doc.appendChild(order);
+
+	QDomElement e = doc.createElement("creditcard");
+	e.appendChild(doc.createTextNode("1234 5678 9012 3456"));
+	order.appendChild(e);
+	printf("---- Original\n%s----\n\n", elemToString(order).data());
+
+	Cipher::Type mainKeyType = Cipher::TripleDES;
+	Cipher::Type subKeyType = Cipher::AES_256;
+
+	RSAKey rsa = generateRSAKey();
+	if(rsa.isNull()) {
+		printf("RSA key generation failed!\n");
+		return 0;
+	}
+	bool ok;
+	QCString cs = "Hello.";
+	printf("cs[%d]\n", cs.size());
+	QByteArray result = encryptRSA2(cs, rsa, &ok);
+	if(!ok) {
+		printf("Unable to RSA encrypt!\n");
+		return 0;
+	}
+	printf("result[%d]\n", result.size());
+	QByteArray result2 = decryptRSA2(result, rsa, &ok);
+	if(!ok) {
+		printf("Unable to RSA decrypt!\n");
+		return 0;
+	}
+	printf("result2[%d]\n", result2.size());
+	printf("{%s}\n", result2.data());
+
+	QByteArray iv = Cipher::generateIV(mainKeyType);
+	printf("ivsize=[%d]\n", iv.size());
+	Cipher::Key key1 = Cipher::generateKey(mainKeyType);
+	printf("keysize=[%d]\n", key1.data().size());
+	Cipher::Key key = Cipher::generateKey(subKeyType);
+	printf("Key: { ");
+	for(int n = 0; n < (int)key.data().size(); ++n)
+		printf("%02x", (unsigned char)key.data()[n]);
+	printf(" }\n\n");
+
+	XmlEnc::Encrypted dat;
+	dat.encryptKey(key, key1);
+	QDomElement encKey = dat.toXml(&doc);
+
+	//printf("---- Encrypted Key\n%s----\n\n", elemToString(encKey).data());
+
+	XmlEnc::KeyInfo ki;
+	ki.setName("My Key");
+	//ki.attachEncryptedKey(encKey);
+	ki.setValue(key.data());
+
+	dat.setKeyInfo(ki);
+	dat.encryptElement(e, key);
+	QDomElement enc = dat.toXml(&doc);
+	order.replaceChild(enc, e);
+	printf("---- Encrypt\n%s----\n\n", elemToString(order).data());
+
+	XmlEnc::Encrypted de;
+	if(!de.fromXml(encKey))
+		printf("Error: could not read encrypted xml!\n");
+	QByteArray r = de.decryptKey(key1);
+
+	Cipher::Key realKey;
+	realKey.setData(r);
+	realKey.setType(subKeyType);
+
+	if(!de.fromXml(enc))
+		printf("Error: could not read encrypted xml!\n");
+
+	QDomElement dec = de.decryptElement(&doc, realKey);
+	order.replaceChild(dec, enc);
+	printf("---- Decrypt\n%s----\n\n", elemToString(order).data());
+}
Index: psi/tags/0.10-os2-b1/cutestuff/enctest.pro
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/enctest.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/enctest.pro	(revision 38)
@@ -0,0 +1,26 @@
+CONFIG += thread
+TARGET  = enctest
+
+INCLUDEPATH += /usr/local
+
+HEADERS = \
+	util/bytestream.h \
+	util/base64.h \
+	util/qrandom.h \
+	util/cipher.h \
+	util/sha1.h \
+	xmlsec/keyops.h \
+	xmlsec/xmlenc.h
+
+SOURCES = \
+	util/bytestream.cpp \
+	util/base64.cpp \
+	util/qrandom.cpp \
+	util/cipher.cpp \
+	util/sha1.cpp \
+	xmlsec/keyops.cpp \
+	xmlsec/xmlenc.cpp \
+	enctest.cpp
+
+LIBS += -L/usr/local/lib -lcrypto
+
Index: psi/tags/0.10-os2-b1/cutestuff/network/bsocket.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/bsocket.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/bsocket.cpp	(revision 38)
@@ -0,0 +1,392 @@
+/*
+ * bsocket.cpp - QSocket wrapper based on Bytestream with SRV DNS support
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"bsocket.h"
+
+#include<qcstring.h>
+#include<qsocket.h>
+#include<qdns.h>
+#include<qguardedptr.h>
+#include"safedelete.h"
+#ifndef NO_NDNS
+#include"ndns.h"
+#endif
+#include"srvresolver.h"
+
+#ifdef BS_DEBUG
+#include<stdio.h>
+#endif
+
+#define READBUFSIZE 65536
+
+// CS_NAMESPACE_BEGIN
+
+class BSocket::Private
+{
+public:
+	Private()
+	{
+		qsock = 0;
+	}
+
+	QSocket *qsock;
+	int state;
+
+#ifndef NO_NDNS
+	NDns ndns;
+#endif
+	SrvResolver srv;
+	QString host;
+	int port;
+	SafeDelete sd;
+};
+
+BSocket::BSocket(QObject *parent)
+:ByteStream(parent)
+{
+	d = new Private;
+#ifndef NO_NDNS
+	connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+#endif
+	connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done()));
+
+	reset();
+}
+
+BSocket::~BSocket()
+{
+	reset(true);
+	delete d;
+}
+
+void BSocket::reset(bool clear)
+{
+	if(d->qsock) {
+		d->qsock->disconnect(this);
+
+		if(!clear && d->qsock->isOpen()) {
+			// move remaining into the local queue
+			QByteArray block(d->qsock->bytesAvailable());
+			d->qsock->readBlock(block.data(), block.size());
+			appendRead(block);
+		}
+
+		d->sd.deleteLater(d->qsock);
+		d->qsock = 0;
+	}
+	else {
+		if(clear)
+			clearReadBuffer();
+	}
+
+	if(d->srv.isBusy())
+		d->srv.stop();
+#ifndef NO_NDNS
+	if(d->ndns.isBusy())
+		d->ndns.stop();
+#endif
+	d->state = Idle;
+}
+
+void BSocket::ensureSocket()
+{
+	if(!d->qsock) {
+		d->qsock = new QSocket;
+#if QT_VERSION >= 0x030200
+		d->qsock->setReadBufferSize(READBUFSIZE);
+#endif
+		connect(d->qsock, SIGNAL(hostFound()), SLOT(qs_hostFound()));
+		connect(d->qsock, SIGNAL(connected()), SLOT(qs_connected()));
+		connect(d->qsock, SIGNAL(connectionClosed()), SLOT(qs_connectionClosed()));
+		connect(d->qsock, SIGNAL(delayedCloseFinished()), SLOT(qs_delayedCloseFinished()));
+		connect(d->qsock, SIGNAL(readyRead()), SLOT(qs_readyRead()));
+		connect(d->qsock, SIGNAL(bytesWritten(int)), SLOT(qs_bytesWritten(int)));
+		connect(d->qsock, SIGNAL(error(int)), SLOT(qs_error(int)));
+	}
+}
+
+void BSocket::connectToHost(const QString &host, Q_UINT16 port)
+{
+	reset(true);
+	d->host = host;
+	d->port = port;
+#ifdef NO_NDNS
+	d->state = Connecting;
+	do_connect();
+#else
+	d->state = HostLookup;
+	d->ndns.resolve(d->host);
+#endif
+}
+
+void BSocket::connectToServer(const QString &srv, const QString &type)
+{
+	reset(true);
+	d->state = HostLookup;
+	d->srv.resolve(srv, type, "tcp");
+}
+
+int BSocket::socket() const
+{
+	if(d->qsock)
+		return d->qsock->socket();
+	else
+		return -1;
+}
+
+void BSocket::setSocket(int s)
+{
+	reset(true);
+	ensureSocket();
+	d->state = Connected;
+	d->qsock->setSocket(s);
+}
+
+int BSocket::state() const
+{
+	return d->state;
+}
+
+bool BSocket::isOpen() const
+{
+	if(d->state == Connected)
+		return true;
+	else
+		return false;
+}
+
+void BSocket::close()
+{
+	if(d->state == Idle)
+		return;
+
+	if(d->qsock) {
+		d->qsock->close();
+		d->state = Closing;
+		if(d->qsock->bytesToWrite() == 0)
+			reset();
+	}
+	else {
+		reset();
+	}
+}
+
+void BSocket::write(const QByteArray &a)
+{
+	if(d->state != Connected)
+		return;
+#ifdef BS_DEBUG
+	QCString cs;
+	cs.resize(a.size()+1);
+	memcpy(cs.data(), a.data(), a.size());
+	QString s = QString::fromUtf8(cs);
+	fprintf(stderr, "BSocket: writing [%d]: {%s}\n", a.size(), cs.data());
+#endif
+	d->qsock->writeBlock(a.data(), a.size());
+}
+
+QByteArray BSocket::read(int bytes)
+{
+	QByteArray block;
+	if(d->qsock) {
+		int max = bytesAvailable();
+		if(bytes <= 0 || bytes > max)
+			bytes = max;
+		block.resize(bytes);
+		d->qsock->readBlock(block.data(), block.size());
+	}
+	else
+		block = ByteStream::read(bytes);
+
+#ifdef BS_DEBUG
+	QCString cs;
+	cs.resize(block.size()+1);
+	memcpy(cs.data(), block.data(), block.size());
+	QString s = QString::fromUtf8(cs);
+	fprintf(stderr, "BSocket: read [%d]: {%s}\n", block.size(), s.latin1());
+#endif
+	return block;
+}
+
+int BSocket::bytesAvailable() const
+{
+	if(d->qsock)
+		return d->qsock->bytesAvailable();
+	else
+		return ByteStream::bytesAvailable();
+}
+
+int BSocket::bytesToWrite() const
+{
+	if(!d->qsock)
+		return 0;
+	return d->qsock->bytesToWrite();
+}
+
+QHostAddress BSocket::address() const
+{
+	if(d->qsock)
+		return d->qsock->address();
+	else
+		return QHostAddress();
+}
+
+Q_UINT16 BSocket::port() const
+{
+	if(d->qsock)
+		return d->qsock->port();
+	else
+		return 0;
+}
+
+QHostAddress BSocket::peerAddress() const
+{
+	if(d->qsock)
+		return d->qsock->peerAddress();
+	else
+		return QHostAddress();
+}
+
+Q_UINT16 BSocket::peerPort() const
+{
+	if(d->qsock)
+		return d->qsock->port();
+	else
+		return 0;
+}
+
+void BSocket::srv_done()
+{
+	if(d->srv.failed()) {
+#ifdef BS_DEBUG
+		fprintf(stderr, "BSocket: Error resolving hostname.\n");
+#endif
+		error(ErrHostNotFound);
+		return;
+	}
+
+	d->host = d->srv.resultAddress().toString();
+	d->port = d->srv.resultPort();
+	do_connect();
+	//QTimer::singleShot(0, this, SLOT(do_connect()));
+	//hostFound();
+}
+
+void BSocket::ndns_done()
+{
+#ifndef NO_NDNS
+	if(d->ndns.result()) {
+		d->host = d->ndns.resultString();
+		d->state = Connecting;
+		do_connect();
+		//QTimer::singleShot(0, this, SLOT(do_connect()));
+		//hostFound();
+	}
+	else {
+#ifdef BS_DEBUG
+		fprintf(stderr, "BSocket: Error resolving hostname.\n");
+#endif
+		error(ErrHostNotFound);
+	}
+#endif
+}
+
+void BSocket::do_connect()
+{
+#ifdef BS_DEBUG
+	fprintf(stderr, "BSocket: Connecting to %s:%d\n", d->host.latin1(), d->port);
+#endif
+	ensureSocket();
+	d->qsock->connectToHost(d->host, d->port);
+}
+
+void BSocket::qs_hostFound()
+{
+	//SafeDeleteLock s(&d->sd);
+}
+
+void BSocket::qs_connected()
+{
+	d->state = Connected;
+#ifdef BS_DEBUG
+	fprintf(stderr, "BSocket: Connected.\n");
+#endif
+	SafeDeleteLock s(&d->sd);
+	connected();
+}
+
+void BSocket::qs_connectionClosed()
+{
+#ifdef BS_DEBUG
+	fprintf(stderr, "BSocket: Connection Closed.\n");
+#endif
+	SafeDeleteLock s(&d->sd);
+	reset();
+	connectionClosed();
+}
+
+void BSocket::qs_delayedCloseFinished()
+{
+#ifdef BS_DEBUG
+	fprintf(stderr, "BSocket: Delayed Close Finished.\n");
+#endif
+	SafeDeleteLock s(&d->sd);
+	reset();
+	delayedCloseFinished();
+}
+
+void BSocket::qs_readyRead()
+{
+	SafeDeleteLock s(&d->sd);
+	readyRead();
+}
+
+void BSocket::qs_bytesWritten(int x)
+{
+#ifdef BS_DEBUG
+	fprintf(stderr, "BSocket: BytesWritten [%d].\n", x);
+#endif
+	SafeDeleteLock s(&d->sd);
+	bytesWritten(x);
+}
+
+void BSocket::qs_error(int x)
+{
+#ifdef BS_DEBUG
+	fprintf(stderr, "BSocket: Error.\n");
+#endif
+	SafeDeleteLock s(&d->sd);
+
+	// connection error during SRV host connect?  try next
+	if(d->state == HostLookup && (x == QSocket::ErrConnectionRefused || x == QSocket::ErrHostNotFound)) {
+		d->srv.next();
+		return;
+	}
+
+	reset();
+	if(x == QSocket::ErrConnectionRefused)
+		error(ErrConnectionRefused);
+	else if(x == QSocket::ErrHostNotFound)
+		error(ErrHostNotFound);
+	else if(x == QSocket::ErrSocketRead)
+		error(ErrRead);
+}
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/network/bsocket.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/bsocket.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/bsocket.h	(revision 38)
@@ -0,0 +1,87 @@
+/*
+ * bsocket.h - QSocket wrapper based on Bytestream with SRV DNS support
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_BSOCKET_H
+#define CS_BSOCKET_H
+
+#include<qobject.h>
+#include<qhostaddress.h>
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class BSocket : public ByteStream
+{
+	Q_OBJECT
+public:
+	enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound };
+	enum State { Idle, HostLookup, Connecting, Connected, Closing };
+	BSocket(QObject *parent=0);
+	~BSocket();
+
+	void connectToHost(const QString &host, Q_UINT16 port);
+	void connectToServer(const QString &srv, const QString &type);
+	int socket() const;
+	void setSocket(int);
+	int state() const;
+
+	// from ByteStream
+	bool isOpen() const;
+	void close();
+	void write(const QByteArray &);
+	QByteArray read(int bytes=0);
+	int bytesAvailable() const;
+	int bytesToWrite() const;
+
+	// local
+	QHostAddress address() const;
+	Q_UINT16 port() const;
+
+	// remote
+	QHostAddress peerAddress() const;
+	Q_UINT16 peerPort() const;
+
+signals:
+	void hostFound();
+	void connected();
+
+private slots:
+	void qs_hostFound();
+	void qs_connected();
+	void qs_connectionClosed();
+	void qs_delayedCloseFinished();
+	void qs_readyRead();
+	void qs_bytesWritten(int);
+	void qs_error(int);
+	void srv_done();
+	void ndns_done();
+	void do_connect();
+
+private:
+	class Private;
+	Private *d;
+
+	void reset(bool clear=false);
+	void ensureSocket();
+};
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/network/httpconnect.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/httpconnect.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/httpconnect.cpp	(revision 38)
@@ -0,0 +1,366 @@
+/*
+ * httpconnect.cpp - HTTP "CONNECT" proxy
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"httpconnect.h"
+
+#include<qstringlist.h>
+#include"bsocket.h"
+#include"base64.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+static QString extractLine(QByteArray *buf, bool *found)
+{
+	// scan for newline
+	int n;
+	for(n = 0; n < (int)buf->size()-1; ++n) {
+		if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
+			QCString cstr;
+			cstr.resize(n+1);
+			memcpy(cstr.data(), buf->data(), n);
+			n += 2; // hack off CR/LF
+
+			memmove(buf->data(), buf->data() + n, buf->size() - n);
+			buf->resize(buf->size() - n);
+			QString s = QString::fromUtf8(cstr);
+
+			if(found)
+				*found = true;
+			return s;
+		}
+	}
+
+	if(found)
+		*found = false;
+	return "";
+}
+
+static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
+{
+	int n = line.find(' ');
+	if(n == -1)
+		return false;
+	if(proto)
+		*proto = line.mid(0, n);
+	++n;
+	int n2 = line.find(' ', n);
+	if(n2 == -1)
+		return false;
+	if(code)
+		*code = line.mid(n, n2-n).toInt();
+	n = n2+1;
+	if(msg)
+		*msg = line.mid(n);
+	return true;
+}
+
+class HttpConnect::Private
+{
+public:
+	Private() {}
+
+	BSocket sock;
+	QString host;
+	int port;
+	QString user, pass;
+	QString real_host;
+	int real_port;
+
+	QByteArray recvBuf;
+
+	bool inHeader;
+	QStringList headerLines;
+
+	int toWrite;
+	bool active;
+};
+
+HttpConnect::HttpConnect(QObject *parent)
+:ByteStream(parent)
+{
+	d = new Private;
+	connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+	connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+	connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished()));
+	connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+	connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int)));
+	connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+
+	reset(true);
+}
+
+HttpConnect::~HttpConnect()
+{
+	reset(true);
+	delete d;
+}
+
+void HttpConnect::reset(bool clear)
+{
+	if(d->sock.state() != BSocket::Idle)
+		d->sock.close();
+	if(clear) {
+		clearReadBuffer();
+		d->recvBuf.resize(0);
+	}
+	d->active = false;
+}
+
+void HttpConnect::setAuth(const QString &user, const QString &pass)
+{
+	d->user = user;
+	d->pass = pass;
+}
+
+void HttpConnect::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port)
+{
+	reset(true);
+
+	d->host = proxyHost;
+	d->port = proxyPort;
+	d->real_host = host;
+	d->real_port = port;
+
+#ifdef PROX_DEBUG
+	fprintf(stderr, "HttpConnect: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+	if(d->user.isEmpty())
+		fprintf(stderr, "\n");
+	else
+		fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+	d->sock.connectToHost(d->host, d->port);
+}
+
+bool HttpConnect::isOpen() const
+{
+	return d->active;
+}
+
+void HttpConnect::close()
+{
+	d->sock.close();
+	if(d->sock.bytesToWrite() == 0)
+		reset();
+}
+
+void HttpConnect::write(const QByteArray &buf)
+{
+	if(d->active)
+		d->sock.write(buf);
+}
+
+QByteArray HttpConnect::read(int bytes)
+{
+	return ByteStream::read(bytes);
+}
+
+int HttpConnect::bytesAvailable() const
+{
+	return ByteStream::bytesAvailable();
+}
+
+int HttpConnect::bytesToWrite() const
+{
+	if(d->active)
+		return d->sock.bytesToWrite();
+	else
+		return 0;
+}
+
+void HttpConnect::sock_connected()
+{
+#ifdef PROX_DEBUG
+	fprintf(stderr, "HttpConnect: Connected\n");
+#endif
+	d->inHeader = true;
+	d->headerLines.clear();
+
+	// connected, now send the request
+	QString s;
+	s += QString("CONNECT ") + d->real_host + ':' + QString::number(d->real_port) + " HTTP/1.0\r\n";
+	if(!d->user.isEmpty()) {
+		QString str = d->user + ':' + d->pass;
+		s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
+	}
+	s += "Pragma: no-cache\r\n";
+	s += "\r\n";
+
+	QCString cs = s.utf8();
+	QByteArray block(cs.length());
+	memcpy(block.data(), cs.data(), block.size());
+	d->toWrite = block.size();
+	d->sock.write(block);
+}
+
+void HttpConnect::sock_connectionClosed()
+{
+	if(d->active) {
+		reset();
+		connectionClosed();
+	}
+	else {
+		error(ErrProxyNeg);
+	}
+}
+
+void HttpConnect::sock_delayedCloseFinished()
+{
+	if(d->active) {
+		reset();
+		delayedCloseFinished();
+	}
+}
+
+void HttpConnect::sock_readyRead()
+{
+	QByteArray block = d->sock.read();
+
+	if(!d->active) {
+		ByteStream::appendArray(&d->recvBuf, block);
+
+		if(d->inHeader) {
+			// grab available lines
+			while(1) {
+				bool found;
+				QString line = extractLine(&d->recvBuf, &found);
+				if(!found)
+					break;
+				if(line.isEmpty()) {
+					d->inHeader = false;
+					break;
+				}
+				d->headerLines += line;
+			}
+
+			// done with grabbing the header?
+			if(!d->inHeader) {
+				QString str = d->headerLines.first();
+				d->headerLines.remove(d->headerLines.begin());
+
+				QString proto;
+				int code;
+				QString msg;
+				if(!extractMainHeader(str, &proto, &code, &msg)) {
+#ifdef PROX_DEBUG
+					fprintf(stderr, "HttpConnect: invalid header!\n");
+#endif
+					reset(true);
+					error(ErrProxyNeg);
+					return;
+				}
+				else {
+#ifdef PROX_DEBUG
+					fprintf(stderr, "HttpConnect: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
+					for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
+						fprintf(stderr, "HttpConnect: * [%s]\n", (*it).latin1());
+#endif
+				}
+
+				if(code == 200) { // OK
+#ifdef PROX_DEBUG
+					fprintf(stderr, "HttpConnect: << Success >>\n");
+#endif
+					d->active = true;
+					connected();
+
+					if(!d->recvBuf.isEmpty()) {
+						appendRead(d->recvBuf);
+						d->recvBuf.resize(0);
+						readyRead();
+						return;
+					}
+				}
+				else {
+					int err;
+					QString errStr;
+					if(code == 407) { // Authentication failed
+						err = ErrProxyAuth;
+						errStr = tr("Authentication failed");
+					}
+					else if(code == 404) { // Host not found
+						err = ErrHostNotFound;
+						errStr = tr("Host not found");
+					}
+					else if(code == 403) { // Access denied
+						err = ErrProxyNeg;
+						errStr = tr("Access denied");
+					}
+					else if(code == 503) { // Connection refused
+						err = ErrConnectionRefused;
+						errStr = tr("Connection refused");
+					}
+					else { // invalid reply
+						err = ErrProxyNeg;
+						errStr = tr("Invalid reply");
+					}
+
+#ifdef PROX_DEBUG
+					fprintf(stderr, "HttpConnect: << Error >> [%s]\n", errStr.latin1());
+#endif
+					reset(true);
+					error(err);
+					return;
+				}
+			}
+		}
+	}
+	else {
+		appendRead(block);
+		readyRead();
+		return;
+	}
+}
+
+void HttpConnect::sock_bytesWritten(int x)
+{
+	if(d->toWrite > 0) {
+		int size = x;
+		if(d->toWrite < x)
+			size = d->toWrite;
+		d->toWrite -= size;
+		x -= size;
+	}
+
+	if(d->active && x > 0)
+		bytesWritten(x);
+}
+
+void HttpConnect::sock_error(int x)
+{
+	if(d->active) {
+		reset();
+		error(ErrRead);
+	}
+	else {
+		reset(true);
+		if(x == BSocket::ErrHostNotFound)
+			error(ErrProxyConnect);
+		else if(x == BSocket::ErrConnectionRefused)
+			error(ErrProxyConnect);
+		else if(x == BSocket::ErrRead)
+			error(ErrProxyNeg);
+	}
+}
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/network/httpconnect.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/httpconnect.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/httpconnect.h	(revision 38)
@@ -0,0 +1,67 @@
+/*
+ * httpconnect.h - HTTP "CONNECT" proxy
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_HTTPCONNECT_H
+#define CS_HTTPCONNECT_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class HttpConnect : public ByteStream
+{
+	Q_OBJECT
+public:
+	enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+	HttpConnect(QObject *parent=0);
+	~HttpConnect();
+
+	void setAuth(const QString &user, const QString &pass="");
+	void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port);
+
+	// from ByteStream
+	bool isOpen() const;
+	void close();
+	void write(const QByteArray &);
+	QByteArray read(int bytes=0);
+	int bytesAvailable() const;
+	int bytesToWrite() const;
+
+signals:
+	void connected();
+
+private slots:
+	void sock_connected();
+	void sock_connectionClosed();
+	void sock_delayedCloseFinished();
+	void sock_readyRead();
+	void sock_bytesWritten(int);
+	void sock_error(int);
+
+private:
+	class Private;
+	Private *d;
+
+	void reset(bool clear=false);
+};
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/network/httppoll.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/httppoll.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/httppoll.cpp	(revision 38)
@@ -0,0 +1,663 @@
+/*
+ * httppoll.cpp - HTTP polling proxy
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"httppoll.h"
+
+#include<qstringlist.h>
+#include<qurl.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qca.h>
+#include<stdlib.h>
+#include"bsocket.h"
+#include"base64.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+#define POLL_KEYS 64
+
+// CS_NAMESPACE_BEGIN
+
+static QByteArray randomArray(int size)
+{
+	QByteArray a(size);
+	for(int n = 0; n < size; ++n)
+		a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
+	return a;
+}
+
+//----------------------------------------------------------------------------
+// HttpPoll
+//----------------------------------------------------------------------------
+static QString hpk(int n, const QString &s)
+{
+	if(n == 0)
+		return s;
+	else
+		return Base64::arrayToString( QCA::SHA1::hash( QCString(hpk(n - 1, s).latin1()) ) );
+}
+
+class HttpPoll::Private
+{
+public:
+	Private() {}
+
+	HttpProxyPost http;
+	QString host;
+	int port;
+	QString user, pass;
+	QString url;
+	bool use_proxy;
+
+	QByteArray out;
+
+	int state;
+	bool closing;
+	QString ident;
+
+	QTimer *t;
+
+	QString key[POLL_KEYS];
+	int key_n;
+
+	int polltime;
+};
+
+HttpPoll::HttpPoll(QObject *parent)
+:ByteStream(parent)
+{
+	d = new Private;
+
+	d->polltime = 30;
+	d->t = new QTimer;
+	connect(d->t, SIGNAL(timeout()), SLOT(do_sync()));
+
+	connect(&d->http, SIGNAL(result()), SLOT(http_result()));
+	connect(&d->http, SIGNAL(error(int)), SLOT(http_error(int)));
+
+	reset(true);
+}
+
+HttpPoll::~HttpPoll()
+{
+	reset(true);
+	delete d->t;
+	delete d;
+}
+
+void HttpPoll::reset(bool clear)
+{
+	if(d->http.isActive())
+		d->http.stop();
+	if(clear)
+		clearReadBuffer();
+	clearWriteBuffer();
+	d->out.resize(0);
+	d->state = 0;
+	d->closing = false;
+	d->t->stop();
+}
+
+void HttpPoll::setAuth(const QString &user, const QString &pass)
+{
+	d->user = user;
+	d->pass = pass;
+}
+
+void HttpPoll::connectToUrl(const QString &url)
+{
+	connectToHost("", 0, url);
+}
+
+void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QString &url)
+{
+	reset(true);
+
+	// using proxy?
+	if(!proxyHost.isEmpty()) {
+		d->host = proxyHost;
+		d->port = proxyPort;
+		d->url = url;
+		d->use_proxy = true;
+	}
+	else {
+		QUrl u = url;
+		d->host = u.host();
+		if(u.hasPort())
+			d->port = u.port();
+		else
+			d->port = 80;
+		d->url = u.encodedPathAndQuery();
+		d->use_proxy = false;
+	}
+
+	resetKey();
+	bool last;
+	QString key = getKey(&last);
+
+#ifdef PROX_DEBUG
+	fprintf(stderr, "HttpPoll: Connecting to %s:%d [%s]", d->host.latin1(), d->port, d->url.latin1());
+	if(d->user.isEmpty())
+		fprintf(stderr, "\n");
+	else
+		fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+	QGuardedPtr<QObject> self = this;
+	syncStarted();
+	if(!self)
+		return;
+
+	d->state = 1;
+	d->http.setAuth(d->user, d->pass);
+	d->http.post(d->host, d->port, d->url, makePacket("0", key, "", QByteArray()), d->use_proxy);
+}
+
+QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block)
+{
+	QString str = ident;
+	if(!key.isEmpty()) {
+		str += ';';
+		str += key;
+	}
+	if(!newkey.isEmpty()) {
+		str += ';';
+		str += newkey;
+	}
+	str += ',';
+	QCString cs = str.latin1();
+	int len = cs.length();
+
+	QByteArray a(len + block.size());
+	memcpy(a.data(), cs.data(), len);
+	memcpy(a.data() + len, block.data(), block.size());
+	return a;
+}
+
+int HttpPoll::pollInterval() const
+{
+	return d->polltime;
+}
+
+void HttpPoll::setPollInterval(int seconds)
+{
+	d->polltime = seconds;
+}
+
+bool HttpPoll::isOpen() const
+{
+	return (d->state == 2 ? true: false);
+}
+
+void HttpPoll::close()
+{
+	if(d->state == 0 || d->closing)
+		return;
+
+	if(bytesToWrite() == 0)
+		reset();
+	else
+		d->closing = true;
+}
+
+void HttpPoll::http_result()
+{
+	// check for death :)
+	QGuardedPtr<QObject> self = this;
+	syncFinished();
+	if(!self)
+		return;
+
+	// get id and packet
+	QString id;
+	QString cookie = d->http.getHeader("Set-Cookie");
+	int n = cookie.find("ID=");
+	if(n == -1) {
+		reset();
+		error(ErrRead);
+		return;
+	}
+	n += 3;
+	int n2 = cookie.find(';', n);
+	if(n2 != -1)
+		id = cookie.mid(n, n2-n);
+	else
+		id = cookie.mid(n);
+	QByteArray block = d->http.body();
+
+	// session error?
+	if(id.right(2) == ":0") {
+		if(id == "0:0" && d->state == 2) {
+			reset();
+			connectionClosed();
+			return;
+		}
+		else {
+			reset();
+			error(ErrRead);
+			return;
+		}
+	}
+
+	d->ident = id;
+	bool justNowConnected = false;
+	if(d->state == 1) {
+		d->state = 2;
+		justNowConnected = true;
+	}
+
+	// sync up again soon
+	if(bytesToWrite() > 0 || !d->closing)
+		d->t->start(d->polltime * 1000, true);
+
+	// connecting
+	if(justNowConnected) {
+		connected();
+	}
+	else {
+		if(!d->out.isEmpty()) {
+			int x = d->out.size();
+			d->out.resize(0);
+			takeWrite(x);
+			bytesWritten(x);
+		}
+	}
+
+	if(!self)
+		return;
+
+	if(!block.isEmpty()) {
+		appendRead(block);
+		readyRead();
+	}
+
+	if(!self)
+		return;
+
+	if(bytesToWrite() > 0) {
+		do_sync();
+	}
+	else {
+		if(d->closing) {
+			reset();
+			delayedCloseFinished();
+			return;
+		}
+	}
+}
+
+void HttpPoll::http_error(int x)
+{
+	reset();
+	if(x == HttpProxyPost::ErrConnectionRefused)
+		error(ErrConnectionRefused);
+	else if(x == HttpProxyPost::ErrHostNotFound)
+		error(ErrHostNotFound);
+	else if(x == HttpProxyPost::ErrSocket)
+		error(ErrRead);
+	else if(x == HttpProxyPost::ErrProxyConnect)
+		error(ErrProxyConnect);
+	else if(x == HttpProxyPost::ErrProxyNeg)
+		error(ErrProxyNeg);
+	else if(x == HttpProxyPost::ErrProxyAuth)
+		error(ErrProxyAuth);
+}
+
+int HttpPoll::tryWrite()
+{
+	if(!d->http.isActive())
+		do_sync();
+	return 0;
+}
+
+void HttpPoll::do_sync()
+{
+	if(d->http.isActive())
+		return;
+
+	d->t->stop();
+	d->out = takeWrite(0, false);
+
+	bool last;
+	QString key = getKey(&last);
+	QString newkey;
+	if(last) {
+		resetKey();
+		newkey = getKey(&last);
+	}
+
+	QGuardedPtr<QObject> self = this;
+	syncStarted();
+	if(!self)
+		return;
+
+	d->http.post(d->host, d->port, d->url, makePacket(d->ident, key, newkey, d->out), d->use_proxy);
+}
+
+void HttpPoll::resetKey()
+{
+#ifdef PROX_DEBUG
+	fprintf(stderr, "HttpPoll: reset key!\n");
+#endif
+	QByteArray a = randomArray(64);
+	QString str = QString::fromLatin1(a.data(), a.size());
+
+	d->key_n = POLL_KEYS;
+	for(int n = 0; n < POLL_KEYS; ++n)
+		d->key[n] = hpk(n+1, str);
+}
+
+const QString & HttpPoll::getKey(bool *last)
+{
+	*last = false;
+	--(d->key_n);
+	if(d->key_n == 0)
+		*last = true;
+	return d->key[d->key_n];
+}
+
+
+//----------------------------------------------------------------------------
+// HttpProxyPost
+//----------------------------------------------------------------------------
+static QString extractLine(QByteArray *buf, bool *found)
+{
+	// scan for newline
+	int n;
+	for(n = 0; n < (int)buf->size()-1; ++n) {
+		if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
+			QCString cstr;
+			cstr.resize(n+1);
+			memcpy(cstr.data(), buf->data(), n);
+			n += 2; // hack off CR/LF
+
+			memmove(buf->data(), buf->data() + n, buf->size() - n);
+			buf->resize(buf->size() - n);
+			QString s = QString::fromUtf8(cstr);
+
+			if(found)
+				*found = true;
+			return s;
+		}
+	}
+
+	if(found)
+		*found = false;
+	return "";
+}
+
+static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
+{
+	int n = line.find(' ');
+	if(n == -1)
+		return false;
+	if(proto)
+		*proto = line.mid(0, n);
+	++n;
+	int n2 = line.find(' ', n);
+	if(n2 == -1)
+		return false;
+	if(code)
+		*code = line.mid(n, n2-n).toInt();
+	n = n2+1;
+	if(msg)
+		*msg = line.mid(n);
+	return true;
+}
+
+class HttpProxyPost::Private
+{
+public:
+	Private() {}
+
+	BSocket sock;
+	QByteArray postdata, recvBuf, body;
+	QString url;
+	QString user, pass;
+	bool inHeader;
+	QStringList headerLines;
+	bool asProxy;
+	QString host;
+};
+
+HttpProxyPost::HttpProxyPost(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+	connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+	connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+	connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+	reset(true);
+}
+
+HttpProxyPost::~HttpProxyPost()
+{
+	reset(true);
+	delete d;
+}
+
+void HttpProxyPost::reset(bool clear)
+{
+	if(d->sock.state() != BSocket::Idle)
+		d->sock.close();
+	d->recvBuf.resize(0);
+	if(clear)
+		d->body.resize(0);
+}
+
+void HttpProxyPost::setAuth(const QString &user, const QString &pass)
+{
+	d->user = user;
+	d->pass = pass;
+}
+
+bool HttpProxyPost::isActive() const
+{
+	return (d->sock.state() == BSocket::Idle ? false: true);
+}
+
+void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy)
+{
+	reset(true);
+
+	d->host = proxyHost;
+	d->url = url;
+	d->postdata = data;
+	d->asProxy = asProxy;
+
+#ifdef PROX_DEBUG
+	fprintf(stderr, "HttpProxyPost: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+	if(d->user.isEmpty())
+		fprintf(stderr, "\n");
+	else
+		fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+	d->sock.connectToHost(proxyHost, proxyPort);
+}
+
+void HttpProxyPost::stop()
+{
+	reset();
+}
+
+QByteArray HttpProxyPost::body() const
+{
+	return d->body;
+}
+
+QString HttpProxyPost::getHeader(const QString &var) const
+{
+	for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) {
+		const QString &s = *it;
+		int n = s.find(": ");
+		if(n == -1)
+			continue;
+		QString v = s.mid(0, n);
+		if(v == var)
+			return s.mid(n+2);
+	}
+	return "";
+}
+
+void HttpProxyPost::sock_connected()
+{
+#ifdef PROX_DEBUG
+	fprintf(stderr, "HttpProxyPost: Connected\n");
+#endif
+	d->inHeader = true;
+	d->headerLines.clear();
+
+	QUrl u = d->url;
+
+	// connected, now send the request
+	QString s;
+	s += QString("POST ") + d->url + " HTTP/1.0\r\n";
+	if(d->asProxy) {
+		if(!d->user.isEmpty()) {
+			QString str = d->user + ':' + d->pass;
+			s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
+		}
+		s += "Pragma: no-cache\r\n";
+		s += QString("Host: ") + u.host() + "\r\n";
+	}
+	else {
+		s += QString("Host: ") + d->host + "\r\n";
+	}
+	s += "Content-Type: application/x-www-form-urlencoded\r\n";
+	s += QString("Content-Length: ") + QString::number(d->postdata.size()) + "\r\n";
+	s += "\r\n";
+
+	// write request
+	QCString cs = s.utf8();
+	QByteArray block(cs.length());
+	memcpy(block.data(), cs.data(), block.size());
+	d->sock.write(block);
+
+	// write postdata
+	d->sock.write(d->postdata);
+}
+
+void HttpProxyPost::sock_connectionClosed()
+{
+	d->body = d->recvBuf.copy();
+	reset();
+	result();
+}
+
+void HttpProxyPost::sock_readyRead()
+{
+	QByteArray block = d->sock.read();
+	ByteStream::appendArray(&d->recvBuf, block);
+
+	if(d->inHeader) {
+		// grab available lines
+		while(1) {
+			bool found;
+			QString line = extractLine(&d->recvBuf, &found);
+			if(!found)
+				break;
+			if(line.isEmpty()) {
+				d->inHeader = false;
+				break;
+			}
+			d->headerLines += line;
+		}
+
+		// done with grabbing the header?
+		if(!d->inHeader) {
+			QString str = d->headerLines.first();
+			d->headerLines.remove(d->headerLines.begin());
+
+			QString proto;
+			int code;
+			QString msg;
+			if(!extractMainHeader(str, &proto, &code, &msg)) {
+#ifdef PROX_DEBUG
+				fprintf(stderr, "HttpProxyPost: invalid header!\n");
+#endif
+				reset(true);
+				error(ErrProxyNeg);
+				return;
+			}
+			else {
+#ifdef PROX_DEBUG
+				fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
+				for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
+					fprintf(stderr, "HttpProxyPost: * [%s]\n", (*it).latin1());
+#endif
+			}
+
+			if(code == 200) { // OK
+#ifdef PROX_DEBUG
+				fprintf(stderr, "HttpProxyPost: << Success >>\n");
+#endif
+			}
+			else {
+				int err;
+				QString errStr;
+				if(code == 407) { // Authentication failed
+					err = ErrProxyAuth;
+					errStr = tr("Authentication failed");
+				}
+				else if(code == 404) { // Host not found
+					err = ErrHostNotFound;
+					errStr = tr("Host not found");
+				}
+				else if(code == 403) { // Access denied
+					err = ErrProxyNeg;
+					errStr = tr("Access denied");
+				}
+				else if(code == 503) { // Connection refused
+					err = ErrConnectionRefused;
+					errStr = tr("Connection refused");
+				}
+				else { // invalid reply
+					err = ErrProxyNeg;
+					errStr = tr("Invalid reply");
+				}
+
+#ifdef PROX_DEBUG
+				fprintf(stderr, "HttpProxyPost: << Error >> [%s]\n", errStr.latin1());
+#endif
+				reset(true);
+				error(err);
+				return;
+			}
+		}
+	}
+}
+
+void HttpProxyPost::sock_error(int x)
+{
+#ifdef PROX_DEBUG
+	fprintf(stderr, "HttpProxyPost: socket error: %d\n", x);
+#endif
+	reset(true);
+	if(x == BSocket::ErrHostNotFound)
+		error(ErrProxyConnect);
+	else if(x == BSocket::ErrConnectionRefused)
+		error(ErrProxyConnect);
+	else if(x == BSocket::ErrRead)
+		error(ErrProxyNeg);
+}
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/network/httppoll.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/httppoll.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/httppoll.h	(revision 38)
@@ -0,0 +1,104 @@
+/*
+ * httppoll.h - HTTP polling proxy
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_HTTPPOLL_H
+#define CS_HTTPPOLL_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class HttpPoll : public ByteStream
+{
+	Q_OBJECT
+public:
+	enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+	HttpPoll(QObject *parent=0);
+	~HttpPoll();
+
+	void setAuth(const QString &user, const QString &pass="");
+	void connectToUrl(const QString &url);
+	void connectToHost(const QString &proxyHost, int proxyPort, const QString &url);
+
+	int pollInterval() const;
+	void setPollInterval(int seconds);
+
+	// from ByteStream
+	bool isOpen() const;
+	void close();
+
+signals:
+	void connected();
+	void syncStarted();
+	void syncFinished();
+
+protected:
+	int tryWrite();
+
+private slots:
+	void http_result();
+	void http_error(int);
+	void do_sync();
+
+private:
+	class Private;
+	Private *d;
+
+	void reset(bool clear=false);
+	QByteArray makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block);
+	void resetKey();
+	const QString & getKey(bool *);
+};
+
+class HttpProxyPost : public QObject
+{
+	Q_OBJECT
+public:
+	enum Error { ErrConnectionRefused, ErrHostNotFound, ErrSocket, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+	HttpProxyPost(QObject *parent=0);
+	~HttpProxyPost();
+
+	void setAuth(const QString &user, const QString &pass="");
+	bool isActive() const;
+	void post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy=true);
+	void stop();
+	QByteArray body() const;
+	QString getHeader(const QString &) const;
+
+signals:
+	void result();
+	void error(int);
+
+private slots:
+	void sock_connected();
+	void sock_connectionClosed();
+	void sock_readyRead();
+	void sock_error(int);
+
+private:
+	class Private;
+	Private *d;
+
+	void reset(bool clear=false);
+};
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/network/ndns.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/ndns.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/ndns.cpp	(revision 38)
@@ -0,0 +1,443 @@
+/*
+ * ndns.cpp - native DNS resolution
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+//! \class NDns ndns.h
+//! \brief Simple DNS resolution using native system calls
+//!
+//! This class is to be used when Qt's QDns is not good enough.  Because QDns
+//! does not use threads, it cannot make a system call asyncronously.  Thus,
+//! QDns tries to imitate the behavior of each platform's native behavior, and
+//! generally falls short.
+//!
+//! NDns uses a thread to make the system call happen in the background.  This
+//! gives your program native DNS behavior, at the cost of requiring threads
+//! to build.
+//!
+//! \code
+//! #include "ndns.h"
+//!
+//! ...
+//!
+//! NDns dns;
+//! dns.resolve("psi.affinix.com");
+//!
+//! // The class will emit the resultsReady() signal when the resolution
+//! // is finished. You may then retrieve the results:
+//!
+//! uint ip_address = dns.result();
+//!
+//! // or if you want to get the IP address as a string:
+//!
+//! QString ip_address = dns.resultString();
+//! \endcode
+//!
+//! \note The MAP_NDNS_TO_QDNS macro can be used to use QDns to implement the
+//! NDns functionality. This is a but stupid, because it is supposed that if
+//! NO_NDNS macro is defined, QDns is directly used instead of NDns in the
+//! relevant sources. However, I (dmik) couln't get the DNS resolver working
+//! when NO_NDNS is defined and was too lazy to debug why. The reason for using
+//! QDns instead of NDns is pretty simple: it's impossible to cancel the async
+//! gethostbyname() request executed on a worker thread (for example, during
+//! application termination). As a result, in case of a lost connection (or
+//! whatever), Psi won't close until the gethostbyname() timeout expires which
+//! can take quite a while on OS/2. On the other hand, QDns is fully asynchronus
+//! there, so NDns is absolutely not necessary.
+
+#include"ndns.h"
+
+#include<qapplication.h>
+#include<qsocketdevice.h>
+#include<qptrlist.h>
+#include<qeventloop.h>
+
+#if !defined(MAP_NDNS_TO_QDNS)
+
+#if defined(Q_OS_UNIX) || defined(Q_OS_OS2)
+#include<netdb.h>
+#include<sys/types.h>
+#include<netinet/in.h>
+#include<arpa/inet.h>
+#endif
+
+#ifdef Q_OS_WIN32
+#include<windows.h>
+#endif
+
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+#if defined(MAP_NDNS_TO_QDNS)
+
+// dummy methods necessary because MOC doesn't understand #ifdefs and such
+NDnsManager::NDnsManager() {}
+NDnsManager::~NDnsManager() {}
+bool NDnsManager::event(QEvent *e) { return false; }
+void NDnsManager::app_aboutToQuit() {}
+
+#else // !MAP_NDNS_TO_QDNS
+
+//! \if _hide_doc_
+class NDnsWorkerEvent : public QCustomEvent
+{
+public:
+	enum Type { WorkerEvent = QEvent::User + 100 };
+	NDnsWorkerEvent(NDnsWorker *);
+
+	NDnsWorker *worker;
+};
+
+class NDnsWorker : public QThread
+{
+public:
+	NDnsWorker(QObject *, const QCString &);
+
+	bool success;
+	bool cancelled;
+	QHostAddress addr;
+
+protected:
+	void run();
+
+private:
+	QCString host;
+	QObject *par;
+};
+//! \endif
+
+//----------------------------------------------------------------------------
+// NDnsManager
+//----------------------------------------------------------------------------
+#ifndef HAVE_GETHOSTBYNAME_R
+static QMutex *workerMutex = 0;
+static QMutex *workerCancelled = 0;
+#endif
+static NDnsManager *man = 0;
+bool winsock_init = false;
+
+class NDnsManager::Item
+{
+public:
+	NDns *ndns;
+	NDnsWorker *worker;
+};
+
+class NDnsManager::Private
+{
+public:
+	Item *find(const NDns *n)
+	{
+		QPtrListIterator<Item> it(list);
+		for(Item *i; (i = it.current()); ++it) {
+			if(i->ndns == n)
+				return i;
+		}
+		return 0;
+	}
+
+	Item *find(const NDnsWorker *w)
+	{
+		QPtrListIterator<Item> it(list);
+		for(Item *i; (i = it.current()); ++it) {
+			if(i->worker == w)
+				return i;
+		}
+		return 0;
+	}
+
+	QPtrList<Item> list;
+};
+
+NDnsManager::NDnsManager()
+{
+#ifndef HAVE_GETHOSTBYNAME_R
+	workerMutex = new QMutex;
+	workerCancelled = new QMutex;
+#endif
+
+#ifdef Q_OS_WIN32
+	if(!winsock_init) {
+		winsock_init = true;
+		QSocketDevice *sd = new QSocketDevice;
+		delete sd;
+	}
+#endif
+
+	d = new Private;
+	d->list.setAutoDelete(true);
+
+	connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit()));
+}
+
+NDnsManager::~NDnsManager()
+{
+	delete d;
+
+#ifndef HAVE_GETHOSTBYNAME_R
+	delete workerMutex;
+	workerMutex = 0;
+	delete workerCancelled;
+	workerCancelled = 0;
+#endif
+}
+
+void NDnsManager::resolve(NDns *self, const QString &name)
+{
+	Item *i = new Item;
+	i->ndns = self;
+	i->worker = new NDnsWorker(this, name.utf8());
+	d->list.append(i);
+
+	i->worker->start();
+}
+
+void NDnsManager::stop(NDns *self)
+{
+	Item *i = d->find(self);
+	if(!i)
+		return;
+	// disassociate
+	i->ndns = 0;
+
+#ifndef HAVE_GETHOSTBYNAME_R
+	// cancel
+	workerCancelled->lock();
+	i->worker->cancelled = true;
+	workerCancelled->unlock();
+#endif
+}
+
+bool NDnsManager::isBusy(const NDns *self) const
+{
+	Item *i = d->find(self);
+	return (i ? true: false);
+}
+
+bool NDnsManager::event(QEvent *e)
+{
+	if((int)e->type() == (int)NDnsWorkerEvent::WorkerEvent) {
+		NDnsWorkerEvent *we = static_cast<NDnsWorkerEvent*>(e);
+		we->worker->wait(); // ensure that the thread is terminated
+
+		Item *i = d->find(we->worker);
+		if(!i) {
+			// should NOT happen
+			return true;
+		}
+		QHostAddress addr = i->worker->addr;
+		NDns *ndns = i->ndns;
+		delete i->worker;
+		d->list.removeRef(i);
+
+		// nuke manager if no longer needed (code that follows MUST BE SAFE!)
+		tryDestroy();
+
+		// requestor still around?
+		if(ndns)
+			ndns->finished(addr);
+		return true;
+	}
+	return false;
+}
+
+void NDnsManager::tryDestroy()
+{
+	if(d->list.isEmpty()) {
+		man = 0;
+		delete this;
+	}
+}
+
+void NDnsManager::app_aboutToQuit()
+{
+	while(man) {
+		QEventLoop *e = qApp->eventLoop();
+		e->processEvents(QEventLoop::WaitForMore);
+	}
+}
+
+#endif // !MAP_NDNS_TO_QDNS
+
+
+//----------------------------------------------------------------------------
+// NDns
+//----------------------------------------------------------------------------
+
+//! \fn void NDns::resultsReady()
+//! This signal is emitted when the DNS resolution succeeds or fails.
+
+//!
+//! Constructs an NDns object with parent \a parent.
+NDns::NDns(QObject *parent)
+:QObject(parent)
+{
+#if defined(MAP_NDNS_TO_QDNS)
+	connect(&qdns, SIGNAL(resultsReady()), this, SIGNAL(resultsReady())); 
+#endif	
+}
+
+//!
+//! Destroys the object and frees allocated resources.
+NDns::~NDns()
+{
+#if !defined(MAP_NDNS_TO_QDNS)
+	stop();
+#endif	
+}
+
+//!
+//! Resolves hostname \a host (eg. psi.affinix.com)
+void NDns::resolve(const QString &host)
+{
+#if defined(MAP_NDNS_TO_QDNS)
+	qdns.setLabel(host);
+	if (qdns.recordType() != QDns::A) 
+		qdns.setRecordType(QDns::A);
+#else
+	stop();
+	if(!man)
+		man = new NDnsManager;
+	man->resolve(this, host);
+#endif	
+}
+
+//!
+//! Cancels the lookup action.
+//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed.
+void NDns::stop()
+{
+#if defined(MAP_NDNS_TO_QDNS)
+	qdns.setRecordType(QDns::None);
+#else
+	if(man)
+		man->stop(this);
+#endif	
+}
+
+//!
+//! Returns the IP address as a 32-bit integer in host-byte-order.  This will be 0 if the lookup failed.
+//! \sa resultsReady()
+uint NDns::result() const
+{
+#if defined(MAP_NDNS_TO_QDNS)
+	QHostAddress addr;
+	QValueList<QHostAddress> list = qdns.addresses();
+	if (!list.isEmpty())
+		addr = list.first();
+	return addr.ip4Addr();
+#else
+	return addr.ip4Addr();
+#endif	
+}
+
+//!
+//! Returns the IP address as a string.  This will be an empty string if the lookup failed.
+//! \sa resultsReady()
+QString NDns::resultString() const
+{
+#if defined(MAP_NDNS_TO_QDNS)
+	QHostAddress addr;
+	QValueList<QHostAddress> list = qdns.addresses();
+	if (!list.isEmpty())
+		addr = list.first();
+	return addr.toString();
+#else
+	return addr.toString();
+#endif	
+}
+
+//!
+//! Returns TRUE if busy resolving a hostname.
+bool NDns::isBusy() const
+{
+#if defined(MAP_NDNS_TO_QDNS)
+	return qdns.isWorking();
+#else
+	if(!man)
+		return false;
+	return man->isBusy(this);
+#endif	
+}
+
+#if !defined(MAP_NDNS_TO_QDNS)
+
+void NDns::finished(const QHostAddress &a)
+{
+	addr = a;
+	resultsReady();
+}
+
+//----------------------------------------------------------------------------
+// NDnsWorkerEvent
+//----------------------------------------------------------------------------
+NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p)
+:QCustomEvent(WorkerEvent)
+{
+	worker = p;
+}
+
+//----------------------------------------------------------------------------
+// NDnsWorker
+//----------------------------------------------------------------------------
+NDnsWorker::NDnsWorker(QObject *_par, const QCString &_host)
+{
+	success = cancelled = false;
+	par = _par;
+	host = _host.copy(); // do we need this to avoid sharing across threads?
+}
+
+void NDnsWorker::run()
+{
+	hostent *h = 0;
+
+#ifdef HAVE_GETHOSTBYNAME_R
+	hostent buf;
+	char char_buf[1024];
+	int err;
+	gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err);
+#else
+	// lock for gethostbyname
+	QMutexLocker locker(workerMutex);
+
+	// check for cancel
+	workerCancelled->lock();
+	bool cancel = cancelled;
+	workerCancelled->unlock();
+
+	if(!cancel)
+		h = gethostbyname(host.data());
+#endif
+
+	if(!h) {
+		success = false;
+		QApplication::postEvent(par, new NDnsWorkerEvent(this));
+		return;
+	}
+
+	in_addr a = *((struct in_addr *)h->h_addr_list[0]);
+	addr.setAddress(ntohl(a.s_addr));
+	success = true;
+
+	QApplication::postEvent(par, new NDnsWorkerEvent(this));
+}
+
+#endif // !MAP_NDNS_TO_QDNS
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/network/ndns.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/ndns.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/ndns.h	(revision 38)
@@ -0,0 +1,98 @@
+/*
+ * ndns.h - native DNS resolution
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_NDNS_H
+#define CS_NDNS_H
+
+#include<qobject.h>
+#include<qcstring.h>
+#include<qthread.h>
+#include<qmutex.h>
+#include<qhostaddress.h>
+
+#if defined(MAP_NDNS_TO_QDNS)
+#include <qdns.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+#if !defined(MAP_NDNS_TO_QDNS)
+class NDnsWorker;
+class NDnsManager;
+#endif
+
+class NDns : public QObject
+{
+	Q_OBJECT
+public:
+	NDns(QObject *parent=0);
+	~NDns();
+
+	void resolve(const QString &);
+	void stop();
+	bool isBusy() const;
+
+	uint result() const;
+	QString resultString() const;
+
+signals:
+	void resultsReady();
+
+private:
+#if defined(MAP_NDNS_TO_QDNS)
+	QDns qdns;
+#else
+	QHostAddress addr;
+
+	friend class NDnsManager;
+	void finished(const QHostAddress &);
+#endif	
+};
+
+class NDnsManager : public QObject
+{
+	Q_OBJECT
+public:
+	~NDnsManager();
+	class Item;
+
+//! \if _hide_doc_
+protected:
+	bool event(QEvent *);
+//! \endif
+
+private slots:
+	void app_aboutToQuit();
+
+private:
+	class Private;
+	Private *d;
+
+	friend class NDns;
+	NDnsManager();
+	void resolve(NDns *self, const QString &name);
+	void stop(NDns *self);
+	bool isBusy(const NDns *self) const;
+	void tryDestroy();
+};
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/network/servsock.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/servsock.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/servsock.cpp	(revision 38)
@@ -0,0 +1,110 @@
+/*
+ * servsock.cpp - simple wrapper to QServerSocket
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"servsock.h"
+
+// CS_NAMESPACE_BEGIN
+
+//----------------------------------------------------------------------------
+// ServSock
+//----------------------------------------------------------------------------
+class ServSock::Private
+{
+public:
+	Private() {}
+
+	ServSockSignal *serv;
+};
+
+ServSock::ServSock(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->serv = 0;
+}
+
+ServSock::~ServSock()
+{
+	stop();
+	delete d;
+}
+
+bool ServSock::isActive() const
+{
+	return (d->serv ? true: false);
+}
+
+bool ServSock::listen(Q_UINT16 port)
+{
+	stop();
+
+	d->serv = new ServSockSignal(port);
+	if(!d->serv->ok()) {
+		delete d->serv;
+		d->serv = 0;
+		return false;
+	}
+	connect(d->serv, SIGNAL(connectionReady(int)), SLOT(sss_connectionReady(int)));
+
+	return true;
+}
+
+void ServSock::stop()
+{
+	delete d->serv;
+	d->serv = 0;
+}
+
+int ServSock::port() const
+{
+	if(d->serv)
+		return d->serv->port();
+	else
+		return -1;
+}
+
+QHostAddress ServSock::address() const
+{
+	if(d->serv)
+		return d->serv->address();
+	else
+		return QHostAddress();
+}
+
+void ServSock::sss_connectionReady(int s)
+{
+	connectionReady(s);
+}
+
+
+//----------------------------------------------------------------------------
+// ServSockSignal
+//----------------------------------------------------------------------------
+ServSockSignal::ServSockSignal(int port)
+:QServerSocket(port, 16)
+{
+}
+
+void ServSockSignal::newConnection(int x)
+{
+	connectionReady(x);
+}
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/network/servsock.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/servsock.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/servsock.h	(revision 38)
@@ -0,0 +1,68 @@
+/*
+ * servsock.h - simple wrapper to QServerSocket
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_SERVSOCK_H
+#define CS_SERVSOCK_H
+
+#include<qserversocket.h>
+
+// CS_NAMESPACE_BEGIN
+
+class ServSock : public QObject
+{
+	Q_OBJECT
+public:
+	ServSock(QObject *parent=0);
+	~ServSock();
+
+	bool isActive() const;
+	bool listen(Q_UINT16 port);
+	void stop();
+	int port() const;
+	QHostAddress address() const;
+
+signals:
+	void connectionReady(int);
+
+private slots:
+	void sss_connectionReady(int);
+
+private:
+	class Private;
+	Private *d;
+};
+
+class ServSockSignal : public QServerSocket
+{
+	Q_OBJECT
+public:
+	ServSockSignal(int port);
+
+signals:
+	void connectionReady(int);
+
+protected:
+	// reimplemented
+	void newConnection(int);
+};
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/network/socks.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/socks.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/socks.cpp	(revision 38)
@@ -0,0 +1,1221 @@
+/*
+ * socks.cpp - SOCKS5 TCP proxy client/server
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"socks.h"
+
+#include<qhostaddress.h>
+#include<qstringlist.h>
+#include<qptrlist.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qsocketdevice.h>
+#include<qsocketnotifier.h>
+
+#if defined(Q_OS_UNIX) || defined(Q_OS_OS2)
+#include<sys/types.h>
+#include<netinet/in.h>
+#endif
+
+#ifdef Q_OS_WIN32
+#include<windows.h>
+#endif
+
+#include"servsock.h"
+#include"bsocket.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+//----------------------------------------------------------------------------
+// SocksUDP
+//----------------------------------------------------------------------------
+static QByteArray sp_create_udp(const QString &host, Q_UINT16 port, const QByteArray &buf)
+{
+	// detect for IP addresses
+	//QHostAddress addr;
+	//if(addr.setAddress(host))
+	//	return sp_set_request(addr, port, cmd1);
+
+	QCString h = host.utf8();
+	h.truncate(255);
+	h = QString::fromUtf8(h).utf8(); // delete any partial characters?
+	int hlen = h.length();
+
+	int at = 0;
+	QByteArray a(4);
+	a[at++] = 0x00; // reserved
+	a[at++] = 0x00; // reserved
+	a[at++] = 0x00; // frag
+	a[at++] = 0x03; // address type = domain
+
+	// host
+	a.resize(at+hlen+1);
+	a[at++] = hlen;
+	memcpy(a.data() + at, h.data(), hlen);
+	at += hlen;
+
+	// port
+	a.resize(at+2);
+	unsigned short p = htons(port);
+	memcpy(a.data() + at, &p, 2);
+	at += 2;
+
+	a.resize(at+buf.size());
+	memcpy(a.data() + at, buf.data(), buf.size());
+
+	return a;
+}
+
+struct SPS_UDP
+{
+	QString host;
+	Q_UINT16 port;
+	QByteArray data;
+};
+
+static int sp_read_udp(QByteArray *from, SPS_UDP *s)
+{
+	int full_len = 4;
+	if((int)from->size() < full_len)
+		return 0;
+
+	QString host;
+	QHostAddress addr;
+	unsigned char atype = from->at(3);
+
+	if(atype == 0x01) {
+		full_len += 4;
+		if((int)from->size() < full_len)
+			return 0;
+		Q_UINT32 ip4;
+		memcpy(&ip4, from->data() + 4, 4);
+		addr.setAddress(ntohl(ip4));
+		host = addr.toString();
+	}
+	else if(atype == 0x03) {
+		++full_len;
+		if((int)from->size() < full_len)
+			return 0;
+		unsigned char host_len = from->at(4);
+		full_len += host_len;
+		if((int)from->size() < full_len)
+			return 0;
+		QCString cs(host_len+1);
+		memcpy(cs.data(), from->data() + 5, host_len);
+		host = QString::fromLatin1(cs);
+	}
+	else if(atype == 0x04) {
+		full_len += 16;
+		if((int)from->size() < full_len)
+			return 0;
+		Q_UINT8 a6[16];
+		memcpy(a6, from->data() + 4, 16);
+		addr.setAddress(a6);
+		host = addr.toString();
+	}
+
+	full_len += 2;
+	if((int)from->size() < full_len)
+		return 0;
+
+	Q_UINT16 p;
+	memcpy(&p, from->data() + full_len - 2, 2);
+
+	s->host = host;
+	s->port = ntohs(p);
+	s->data.resize(from->size() - full_len);
+	memcpy(s->data.data(), from->data() + full_len, s->data.size());
+
+	return 1;
+}
+
+class SocksUDP::Private
+{
+public:
+	QSocketDevice *sd;
+	QSocketNotifier *sn;
+	SocksClient *sc;
+	QHostAddress routeAddr;
+	int routePort;
+	QString host;
+	int port;
+};
+
+SocksUDP::SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort)
+:QObject(sc)
+{
+	d = new Private;
+	d->sc = sc;
+	d->sd = new QSocketDevice(QSocketDevice::Datagram);
+	d->sd->setBlocking(false);
+	d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read);
+	connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+	d->host = host;
+	d->port = port;
+	d->routeAddr = routeAddr;
+	d->routePort = routePort;
+}
+
+SocksUDP::~SocksUDP()
+{
+	delete d->sn;
+	delete d->sd;
+	delete d;
+}
+
+void SocksUDP::change(const QString &host, int port)
+{
+	d->host = host;
+	d->port = port;
+}
+
+void SocksUDP::write(const QByteArray &data)
+{
+	QByteArray buf = sp_create_udp(d->host, d->port, data);
+	d->sd->setBlocking(true);
+	d->sd->writeBlock(buf.data(), buf.size(), d->routeAddr, d->routePort);
+	d->sd->setBlocking(false);
+}
+
+void SocksUDP::sn_activated(int)
+{
+	QByteArray buf(8192);
+	int actual = d->sd->readBlock(buf.data(), buf.size());
+	buf.resize(actual);
+	packetReady(buf);
+}
+
+//----------------------------------------------------------------------------
+// SocksClient
+//----------------------------------------------------------------------------
+#define REQ_CONNECT      0x01
+#define REQ_BIND         0x02
+#define REQ_UDPASSOCIATE 0x03
+
+#define RET_SUCCESS      0x00
+#define RET_UNREACHABLE  0x04
+#define RET_CONNREFUSED  0x05
+
+// spc = socks packet client
+// sps = socks packet server
+// SPCS = socks packet client struct
+// SPSS = socks packet server struct
+
+// Version
+static QByteArray spc_set_version()
+{
+	QByteArray ver(4);
+	ver[0] = 0x05; // socks version 5
+	ver[1] = 0x02; // number of methods
+	ver[2] = 0x00; // no-auth
+	ver[3] = 0x02; // username
+	return ver;
+}
+
+static QByteArray sps_set_version(int method)
+{
+	QByteArray ver(2);
+	ver[0] = 0x05;
+	ver[1] = method;
+	return ver;
+}
+
+struct SPCS_VERSION
+{
+	unsigned char version;
+	QByteArray methodList;
+};
+
+static int spc_get_version(QByteArray *from, SPCS_VERSION *s)
+{
+	if(from->size() < 1)
+		return 0;
+	if(from->at(0) != 0x05) // only SOCKS5 supported
+		return -1;
+	if(from->size() < 2)
+		return 0;
+	uint num = from->at(1);
+	if(num > 16) // who the heck has over 16 auth methods??
+		return -1;
+	if(from->size() < 2 + num)
+		return 0;
+	QByteArray a = ByteStream::takeArray(from, 2+num);
+	s->version = a[0];
+	s->methodList.resize(num);
+	memcpy(s->methodList.data(), a.data() + 2, num);
+	return 1;
+}
+
+struct SPSS_VERSION
+{
+	unsigned char version;
+	unsigned char method;
+};
+
+static int sps_get_version(QByteArray *from, SPSS_VERSION *s)
+{
+	if(from->size() < 2)
+		return 0;
+	QByteArray a = ByteStream::takeArray(from, 2);
+	s->version = a[0];
+	s->method = a[1];
+	return 1;
+}
+
+// authUsername
+static QByteArray spc_set_authUsername(const QCString &user, const QCString &pass)
+{
+	int len1 = user.length();
+	int len2 = pass.length();
+	if(len1 > 255)
+		len1 = 255;
+	if(len2 > 255)
+		len2 = 255;
+	QByteArray a(1+1+len1+1+len2);
+	a[0] = 0x01; // username auth version 1
+	a[1] = len1;
+	memcpy(a.data() + 2, user.data(), len1);
+	a[2+len1] = len2;
+	memcpy(a.data() + 3 + len1, pass.data(), len2);
+	return a;
+}
+
+static QByteArray sps_set_authUsername(bool success)
+{
+	QByteArray a(2);
+	a[0] = 0x01;
+	a[1] = success ? 0x00 : 0xff;
+	return a;
+}
+
+struct SPCS_AUTHUSERNAME
+{
+	QString user, pass;
+};
+
+static int spc_get_authUsername(QByteArray *from, SPCS_AUTHUSERNAME *s)
+{
+	if(from->size() < 1)
+		return 0;
+	unsigned char ver = from->at(0);
+	if(ver != 0x01)
+		return -1;
+	if(from->size() < 2)
+		return 0;
+	unsigned char ulen = from->at(1);
+	if((int)from->size() < ulen + 3)
+		return 0;
+	unsigned char plen = from->at(ulen+2);
+	if((int)from->size() < ulen + plen + 3)
+		return 0;
+	QByteArray a = ByteStream::takeArray(from, ulen + plen + 3);
+
+	QCString user, pass;
+	user.resize(ulen+1);
+	pass.resize(plen+1);
+	memcpy(user.data(), a.data()+2, ulen);
+	memcpy(pass.data(), a.data()+ulen+3, plen);
+	s->user = QString::fromUtf8(user);
+	s->pass = QString::fromUtf8(pass);
+	return 1;
+}
+
+struct SPSS_AUTHUSERNAME
+{
+	unsigned char version;
+	bool success;
+};
+
+static int sps_get_authUsername(QByteArray *from, SPSS_AUTHUSERNAME *s)
+{
+	if(from->size() < 2)
+		return 0;
+	QByteArray a = ByteStream::takeArray(from, 2);
+	s->version = a[0];
+	s->success = a[1] == 0 ? true: false;
+	return 1;
+}
+
+// connectRequest
+static QByteArray sp_set_request(const QHostAddress &addr, unsigned short port, unsigned char cmd1)
+{
+	int at = 0;
+	QByteArray a(4);
+	a[at++] = 0x05; // socks version 5
+	a[at++] = cmd1;
+	a[at++] = 0x00; // reserved
+	if(addr.isIp4Addr()) {
+		a[at++] = 0x01; // address type = ipv4
+		Q_UINT32 ip4 = htonl(addr.ip4Addr());
+		a.resize(at+4);
+		memcpy(a.data() + at, &ip4, 4);
+		at += 4;
+	}
+	else {
+		a[at++] = 0x04;
+		Q_UINT8 a6[16];
+		QStringList s6 = QStringList::split(':', addr.toString(), true);
+		int at = 0;
+		Q_UINT16 c;
+		bool ok;
+		for(QStringList::ConstIterator it = s6.begin(); it != s6.end(); ++it) {
+			c = (*it).toInt(&ok, 16);
+			a6[at++] = (c >> 8);
+			a6[at++] = c & 0xff;
+		}
+		a.resize(at+16);
+		memcpy(a.data() + at, a6, 16);
+		at += 16;
+	}
+
+	// port
+	a.resize(at+2);
+	unsigned short p = htons(port);
+	memcpy(a.data() + at, &p, 2);
+
+	return a;
+}
+
+static QByteArray sp_set_request(const QString &host, Q_UINT16 port, unsigned char cmd1)
+{
+	// detect for IP addresses
+	QHostAddress addr;
+	if(addr.setAddress(host))
+		return sp_set_request(addr, port, cmd1);
+
+	QCString h = host.utf8();
+	h.truncate(255);
+	h = QString::fromUtf8(h).utf8(); // delete any partial characters?
+	int hlen = h.length();
+
+	int at = 0;
+	QByteArray a(4);
+	a[at++] = 0x05; // socks version 5
+	a[at++] = cmd1;
+	a[at++] = 0x00; // reserved
+	a[at++] = 0x03; // address type = domain
+
+	// host
+	a.resize(at+hlen+1);
+	a[at++] = hlen;
+	memcpy(a.data() + at, h.data(), hlen);
+	at += hlen;
+
+	// port
+	a.resize(at+2);
+	unsigned short p = htons(port);
+	memcpy(a.data() + at, &p, 2);
+
+	return a;
+}
+
+struct SPS_CONNREQ
+{
+	unsigned char version;
+	unsigned char cmd;
+	int address_type;
+	QString host;
+	QHostAddress addr;
+	Q_UINT16 port;
+};
+
+static int sp_get_request(QByteArray *from, SPS_CONNREQ *s)
+{
+	int full_len = 4;
+	if((int)from->size() < full_len)
+		return 0;
+
+	QString host;
+	QHostAddress addr;
+	unsigned char atype = from->at(3);
+
+	if(atype == 0x01) {
+		full_len += 4;
+		if((int)from->size() < full_len)
+			return 0;
+		Q_UINT32 ip4;
+		memcpy(&ip4, from->data() + 4, 4);
+		addr.setAddress(ntohl(ip4));
+	}
+	else if(atype == 0x03) {
+		++full_len;
+		if((int)from->size() < full_len)
+			return 0;
+		unsigned char host_len = from->at(4);
+		full_len += host_len;
+		if((int)from->size() < full_len)
+			return 0;
+		QCString cs(host_len+1);
+		memcpy(cs.data(), from->data() + 5, host_len);
+		host = QString::fromLatin1(cs);
+	}
+	else if(atype == 0x04) {
+		full_len += 16;
+		if((int)from->size() < full_len)
+			return 0;
+		Q_UINT8 a6[16];
+		memcpy(a6, from->data() + 4, 16);
+		addr.setAddress(a6);
+	}
+
+	full_len += 2;
+	if((int)from->size() < full_len)
+		return 0;
+
+	QByteArray a = ByteStream::takeArray(from, full_len);
+
+	Q_UINT16 p;
+	memcpy(&p, a.data() + full_len - 2, 2);
+
+	s->version = a[0];
+	s->cmd = a[1];
+	s->address_type = atype;
+	s->host = host;
+	s->addr = addr;
+	s->port = ntohs(p);
+
+	return 1;
+}
+
+enum { StepVersion, StepAuth, StepRequest };
+
+class SocksClient::Private
+{
+public:
+	Private() {}
+
+	BSocket sock;
+	QString host;
+	int port;
+	QString user, pass;
+	QString real_host;
+	int real_port;
+
+	QByteArray recvBuf;
+	bool active;
+	int step;
+	int authMethod;
+	bool incoming, waiting;
+
+	QString rhost;
+	int rport;
+
+	int pending;
+
+	bool udp;
+	QString udpAddr;
+	int udpPort;
+};
+
+SocksClient::SocksClient(QObject *parent)
+:ByteStream(parent)
+{
+	init();
+
+	d->incoming = false;
+}
+
+SocksClient::SocksClient(int s, QObject *parent)
+:ByteStream(parent)
+{
+	init();
+
+	d->incoming = true;
+	d->waiting = true;
+	d->sock.setSocket(s);
+}
+
+void SocksClient::init()
+{
+	d = new Private;
+	connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+	connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+	connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished()));
+	connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+	connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int)));
+	connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+
+	reset(true);
+}
+
+SocksClient::~SocksClient()
+{
+	reset(true);
+	delete d;
+}
+
+void SocksClient::reset(bool clear)
+{
+	if(d->sock.state() != BSocket::Idle)
+		d->sock.close();
+	if(clear)
+		clearReadBuffer();
+	d->recvBuf.resize(0);
+	d->active = false;
+	d->waiting = false;
+	d->udp = false;
+	d->pending = 0;
+}
+
+bool SocksClient::isIncoming() const
+{
+	return d->incoming;
+}
+
+void SocksClient::setAuth(const QString &user, const QString &pass)
+{
+	d->user = user;
+	d->pass = pass;
+}
+
+void SocksClient::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode)
+{
+	reset(true);
+
+	d->host = proxyHost;
+	d->port = proxyPort;
+	d->real_host = host;
+	d->real_port = port;
+	d->udp = udpMode;
+
+#ifdef PROX_DEBUG
+	fprintf(stderr, "SocksClient: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+	if(d->user.isEmpty())
+		fprintf(stderr, "\n");
+	else
+		fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+	d->sock.connectToHost(d->host, d->port);
+}
+
+bool SocksClient::isOpen() const
+{
+	return d->active;
+}
+
+void SocksClient::close()
+{
+	d->sock.close();
+	if(d->sock.bytesToWrite() == 0)
+		reset();
+}
+
+void SocksClient::writeData(const QByteArray &buf)
+{
+	d->pending += buf.size();
+	d->sock.write(buf);
+}
+
+void SocksClient::write(const QByteArray &buf)
+{
+	if(d->active && !d->udp)
+		d->sock.write(buf);
+}
+
+QByteArray SocksClient::read(int bytes)
+{
+	return ByteStream::read(bytes);
+}
+
+int SocksClient::bytesAvailable() const
+{
+	return ByteStream::bytesAvailable();
+}
+
+int SocksClient::bytesToWrite() const
+{
+	if(d->active)
+		return d->sock.bytesToWrite();
+	else
+		return 0;
+}
+
+void SocksClient::sock_connected()
+{
+#ifdef PROX_DEBUG
+	fprintf(stderr, "SocksClient: Connected\n");
+#endif
+
+	d->step = StepVersion;
+	writeData(spc_set_version());
+}
+
+void SocksClient::sock_connectionClosed()
+{
+	if(d->active) {
+		reset();
+		connectionClosed();
+	}
+	else {
+		error(ErrProxyNeg);
+	}
+}
+
+void SocksClient::sock_delayedCloseFinished()
+{
+	if(d->active) {
+		reset();
+		delayedCloseFinished();
+	}
+}
+
+void SocksClient::sock_readyRead()
+{
+	QByteArray block = d->sock.read();
+
+	if(!d->active) {
+		if(d->incoming)
+			processIncoming(block);
+		else
+			processOutgoing(block);
+	}
+	else {
+		if(!d->udp) {
+			appendRead(block);
+			readyRead();
+		}
+	}
+}
+
+void SocksClient::processOutgoing(const QByteArray &block)
+{
+#ifdef PROX_DEBUG
+	// show hex
+	fprintf(stderr, "SocksClient: client recv { ");
+	for(int n = 0; n < (int)block.size(); ++n)
+		fprintf(stderr, "%02X ", (unsigned char)block[n]);
+	fprintf(stderr, " } \n");
+#endif
+	ByteStream::appendArray(&d->recvBuf, block);
+
+	if(d->step == StepVersion) {
+		SPSS_VERSION s;
+		int r = sps_get_version(&d->recvBuf, &s);
+		if(r == -1) {
+			reset(true);
+			error(ErrProxyNeg);
+			return;
+		}
+		else if(r == 1) {
+			if(s.version != 0x05 || s.method == 0xff) {
+#ifdef PROX_DEBUG
+				fprintf(stderr, "SocksClient: Method selection failed\n");
+#endif
+				reset(true);
+				error(ErrProxyNeg);
+				return;
+			}
+
+			QString str;
+			if(s.method == 0x00) {
+				str = "None";
+				d->authMethod = AuthNone;
+			}
+			else if(s.method == 0x02) {
+				str = "Username/Password";
+				d->authMethod = AuthUsername;
+			}
+			else {
+#ifdef PROX_DEBUG
+				fprintf(stderr, "SocksClient: Server wants to use unknown method '%02x'\n", s.method);
+#endif
+				reset(true);
+				error(ErrProxyNeg);
+				return;
+			}
+
+			if(d->authMethod == AuthNone) {
+				// no auth, go straight to the request
+				do_request();
+			}
+			else if(d->authMethod == AuthUsername) {
+				d->step = StepAuth;
+#ifdef PROX_DEBUG
+				fprintf(stderr, "SocksClient: Authenticating [Username] ...\n");
+#endif
+				writeData(spc_set_authUsername(d->user.latin1(), d->pass.latin1()));
+			}
+		}
+	}
+	if(d->step == StepAuth) {
+		if(d->authMethod == AuthUsername) {
+			SPSS_AUTHUSERNAME s;
+			int r = sps_get_authUsername(&d->recvBuf, &s);
+			if(r == -1) {
+				reset(true);
+				error(ErrProxyNeg);
+				return;
+			}
+			else if(r == 1) {
+				if(s.version != 0x01) {
+					reset(true);
+					error(ErrProxyNeg);
+					return;
+				}
+				if(!s.success) {
+					reset(true);
+					error(ErrProxyAuth);
+					return;
+				}
+
+				do_request();
+			}
+		}
+	}
+	else if(d->step == StepRequest) {
+		SPS_CONNREQ s;
+		int r = sp_get_request(&d->recvBuf, &s);
+		if(r == -1) {
+			reset(true);
+			error(ErrProxyNeg);
+			return;
+		}
+		else if(r == 1) {
+			if(s.cmd != RET_SUCCESS) {
+#ifdef PROX_DEBUG
+				fprintf(stderr, "SocksClient: client << Error >> [%02x]\n", s.cmd);
+#endif
+				reset(true);
+				if(s.cmd == RET_UNREACHABLE)
+					error(ErrHostNotFound);
+				else if(s.cmd == RET_CONNREFUSED)
+					error(ErrConnectionRefused);
+				else
+					error(ErrProxyNeg);
+				return;
+			}
+
+#ifdef PROX_DEBUG
+			fprintf(stderr, "SocksClient: client << Success >>\n");
+#endif
+			if(d->udp) {
+				if(s.address_type == 0x03)
+					d->udpAddr = s.host;
+				else
+					d->udpAddr = s.addr.toString();
+				d->udpPort = s.port;
+			}
+
+			d->active = true;
+
+			QGuardedPtr<QObject> self = this;
+			connected();
+			if(!self)
+				return;
+
+			if(!d->recvBuf.isEmpty()) {
+				appendRead(d->recvBuf);
+				d->recvBuf.resize(0);
+				readyRead();
+			}
+		}
+	}
+}
+
+void SocksClient::do_request()
+{
+#ifdef PROX_DEBUG
+	fprintf(stderr, "SocksClient: Requesting ...\n");
+#endif
+	d->step = StepRequest;
+	int cmd = d->udp ? REQ_UDPASSOCIATE : REQ_CONNECT;
+	QByteArray buf;
+	if(!d->real_host.isEmpty())
+		buf = sp_set_request(d->real_host, d->real_port, cmd);
+	else
+		buf = sp_set_request(QHostAddress(), 0, cmd);
+	writeData(buf);
+}
+
+void SocksClient::sock_bytesWritten(int x)
+{
+	int bytes = x;
+	if(d->pending >= bytes) {
+		d->pending -= bytes;
+		bytes = 0;
+	}
+	else {
+		bytes -= d->pending;
+		d->pending = 0;
+	}
+	if(bytes > 0)
+		bytesWritten(bytes);
+}
+
+void SocksClient::sock_error(int x)
+{
+	if(d->active) {
+		reset();
+		error(ErrRead);
+	}
+	else {
+		reset(true);
+		if(x == BSocket::ErrHostNotFound)
+			error(ErrProxyConnect);
+		else if(x == BSocket::ErrConnectionRefused)
+			error(ErrProxyConnect);
+		else if(x == BSocket::ErrRead)
+			error(ErrProxyNeg);
+	}
+}
+
+void SocksClient::serve()
+{
+	d->waiting = false;
+	d->step = StepVersion;
+	continueIncoming();
+}
+
+void SocksClient::processIncoming(const QByteArray &block)
+{
+#ifdef PROX_DEBUG
+	// show hex
+	fprintf(stderr, "SocksClient: server recv { ");
+	for(int n = 0; n < (int)block.size(); ++n)
+		fprintf(stderr, "%02X ", (unsigned char)block[n]);
+	fprintf(stderr, " } \n");
+#endif
+	ByteStream::appendArray(&d->recvBuf, block);
+
+	if(!d->waiting)
+		continueIncoming();
+}
+
+void SocksClient::continueIncoming()
+{
+	if(d->recvBuf.isEmpty())
+		return;
+
+	if(d->step == StepVersion) {
+		SPCS_VERSION s;
+		int r = spc_get_version(&d->recvBuf, &s);
+		if(r == -1) {
+			reset(true);
+			error(ErrProxyNeg);
+			return;
+		}
+		else if(r == 1) {
+			if(s.version != 0x05) {
+				reset(true);
+				error(ErrProxyNeg);
+				return;
+			}
+
+			int methods = 0;
+			for(int n = 0; n < (int)s.methodList.size(); ++n) {
+				unsigned char c = s.methodList[n];
+				if(c == 0x00)
+					methods |= AuthNone;
+				else if(c == 0x02)
+					methods |= AuthUsername;
+			}
+			d->waiting = true;
+			incomingMethods(methods);
+		}
+	}
+	else if(d->step == StepAuth) {
+		SPCS_AUTHUSERNAME s;
+		int r = spc_get_authUsername(&d->recvBuf, &s);
+		if(r == -1) {
+			reset(true);
+			error(ErrProxyNeg);
+			return;
+		}
+		else if(r == 1) {
+			d->waiting = true;
+			incomingAuth(s.user, s.pass);
+		}
+	}
+	else if(d->step == StepRequest) {
+		SPS_CONNREQ s;
+		int r = sp_get_request(&d->recvBuf, &s);
+		if(r == -1) {
+			reset(true);
+			error(ErrProxyNeg);
+			return;
+		}
+		else if(r == 1) {
+			d->waiting = true;
+			if(s.cmd == REQ_CONNECT) {
+				if(!s.host.isEmpty())
+					d->rhost = s.host;
+				else
+					d->rhost = s.addr.toString();
+				d->rport = s.port;
+				incomingConnectRequest(d->rhost, d->rport);
+			}
+			else if(s.cmd == REQ_UDPASSOCIATE) {
+				incomingUDPAssociateRequest();
+			}
+			else {
+				requestDeny();
+				return;
+			}
+		}
+	}
+}
+
+void SocksClient::chooseMethod(int method)
+{
+	if(d->step != StepVersion || !d->waiting)
+		return;
+
+	unsigned char c;
+	if(method == AuthNone) {
+		d->step = StepRequest;
+		c = 0x00;
+	}
+	else {
+		d->step = StepAuth;
+		c = 0x02;
+	}
+
+	// version response
+	d->waiting = false;
+	writeData(sps_set_version(c));
+	continueIncoming();
+}
+
+void SocksClient::authGrant(bool b)
+{
+	if(d->step != StepAuth || !d->waiting)
+		return;
+
+	if(b)
+		d->step = StepRequest;
+
+	// auth response
+	d->waiting = false;
+	writeData(sps_set_authUsername(b));
+	if(!b) {
+		reset(true);
+		return;
+	}
+	continueIncoming();
+}
+
+void SocksClient::requestDeny()
+{
+	if(d->step != StepRequest || !d->waiting)
+		return;
+
+	// response
+	d->waiting = false;
+	writeData(sp_set_request(d->rhost, d->rport, RET_UNREACHABLE));
+	reset(true);
+}
+
+void SocksClient::grantConnect()
+{
+	if(d->step != StepRequest || !d->waiting)
+		return;
+
+	// response
+	d->waiting = false;
+	writeData(sp_set_request(d->rhost, d->rport, RET_SUCCESS));
+	d->active = true;
+#ifdef PROX_DEBUG
+	fprintf(stderr, "SocksClient: server << Success >>\n");
+#endif
+
+	if(!d->recvBuf.isEmpty()) {
+		appendRead(d->recvBuf);
+		d->recvBuf.resize(0);
+		readyRead();
+	}
+}
+
+void SocksClient::grantUDPAssociate(const QString &relayHost, int relayPort)
+{
+	if(d->step != StepRequest || !d->waiting)
+		return;
+
+	// response
+	d->waiting = false;
+	writeData(sp_set_request(relayHost, relayPort, RET_SUCCESS));
+	d->udp = true;
+	d->active = true;
+#ifdef PROX_DEBUG
+	fprintf(stderr, "SocksClient: server << Success >>\n");
+#endif
+
+	if(!d->recvBuf.isEmpty())
+		d->recvBuf.resize(0);
+}
+
+QHostAddress SocksClient::peerAddress() const
+{
+	return d->sock.peerAddress();
+}
+
+Q_UINT16 SocksClient::peerPort() const
+{
+	return d->sock.peerPort();
+}
+
+QString SocksClient::udpAddress() const
+{
+	return d->udpAddr;
+}
+
+Q_UINT16 SocksClient::udpPort() const
+{
+	return d->udpPort;
+}
+
+SocksUDP *SocksClient::createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort)
+{
+	return new SocksUDP(this, host, port, routeAddr, routePort);
+}
+
+//----------------------------------------------------------------------------
+// SocksServer
+//----------------------------------------------------------------------------
+class SocksServer::Private
+{
+public:
+	Private() {}
+
+	ServSock serv;
+	QPtrList<SocksClient> incomingConns;
+	QSocketDevice *sd;
+	QSocketNotifier *sn;
+};
+
+SocksServer::SocksServer(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->sd = 0;
+	d->sn = 0;
+	connect(&d->serv, SIGNAL(connectionReady(int)), SLOT(connectionReady(int)));
+}
+
+SocksServer::~SocksServer()
+{
+	stop();
+	d->incomingConns.setAutoDelete(true);
+	d->incomingConns.clear();
+	delete d;
+}
+
+bool SocksServer::isActive() const
+{
+	return d->serv.isActive();
+}
+
+bool SocksServer::listen(Q_UINT16 port, bool udp)
+{
+	stop();
+	if(!d->serv.listen(port))
+		return false;
+	if(udp) {
+		d->sd = new QSocketDevice(QSocketDevice::Datagram);
+		d->sd->setBlocking(false);
+		if(!d->sd->bind(QHostAddress(), port)) {
+			delete d->sd;
+			d->sd = 0;
+			d->serv.stop();
+			return false;
+		}
+		d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read);
+		connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+	}
+	return true;
+}
+
+void SocksServer::stop()
+{
+	delete d->sn;
+	d->sn = 0;
+	delete d->sd;
+	d->sd = 0;
+	d->serv.stop();
+}
+
+int SocksServer::port() const
+{
+	return d->serv.port();
+}
+
+QHostAddress SocksServer::address() const
+{
+	return d->serv.address();
+}
+
+SocksClient *SocksServer::takeIncoming()
+{
+	if(d->incomingConns.isEmpty())
+		return 0;
+
+	SocksClient *c = d->incomingConns.getFirst();
+	d->incomingConns.removeRef(c);
+
+	// we don't care about errors anymore
+	disconnect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+
+	// don't serve the connection until the event loop, to give the caller a chance to map signals
+	QTimer::singleShot(0, c, SLOT(serve()));
+
+	return c;
+}
+
+void SocksServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
+{
+	if(d->sd) {
+		d->sd->setBlocking(true);
+		d->sd->writeBlock(data.data(), data.size(), addr, port);
+		d->sd->setBlocking(false);
+	}
+}
+
+void SocksServer::connectionReady(int s)
+{
+	SocksClient *c = new SocksClient(s, this);
+	connect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+	d->incomingConns.append(c);
+	incomingReady();
+}
+
+void SocksServer::connectionError()
+{
+	SocksClient *c = (SocksClient *)sender();
+	d->incomingConns.removeRef(c);
+	c->deleteLater();
+}
+
+void SocksServer::sn_activated(int)
+{
+	QByteArray buf(8192);
+	int actual = d->sd->readBlock(buf.data(), buf.size());
+	buf.resize(actual);
+	QHostAddress pa = d->sd->peerAddress();
+	int pp = d->sd->peerPort();
+	SPS_UDP s;
+	int r = sp_read_udp(&buf, &s);
+	if(r != 1)
+		return;
+	incomingUDP(s.host, s.port, pa, pp, s.data);
+}
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/network/socks.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/socks.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/socks.h	(revision 38)
@@ -0,0 +1,160 @@
+/*
+ * socks.h - SOCKS5 TCP proxy client/server
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_SOCKS_H
+#define CS_SOCKS_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class QHostAddress;
+class SocksClient;
+class SocksServer;
+
+class SocksUDP : public QObject
+{
+	Q_OBJECT
+public:
+	~SocksUDP();
+
+	void change(const QString &host, int port);
+	void write(const QByteArray &data);
+
+signals:
+	void packetReady(const QByteArray &data);
+
+private slots:
+	void sn_activated(int);
+
+private:
+	class Private;
+	Private *d;
+
+	friend class SocksClient;
+	SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort);
+};
+
+class SocksClient : public ByteStream
+{
+	Q_OBJECT
+public:
+	enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+	enum Method { AuthNone=0x0001, AuthUsername=0x0002 };
+	enum Request { ReqConnect, ReqUDPAssociate };
+	SocksClient(QObject *parent=0);
+	SocksClient(int, QObject *parent=0);
+	~SocksClient();
+
+	bool isIncoming() const;
+
+	// outgoing
+	void setAuth(const QString &user, const QString &pass="");
+	void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode=false);
+
+	// incoming
+	void chooseMethod(int);
+	void authGrant(bool);
+	void requestDeny();
+	void grantConnect();
+	void grantUDPAssociate(const QString &relayHost, int relayPort);
+
+	// from ByteStream
+	bool isOpen() const;
+	void close();
+	void write(const QByteArray &);
+	QByteArray read(int bytes=0);
+	int bytesAvailable() const;
+	int bytesToWrite() const;
+
+	// remote address
+	QHostAddress peerAddress() const;
+	Q_UINT16 peerPort() const;
+
+	// udp
+	QString udpAddress() const;
+	Q_UINT16 udpPort() const;
+	SocksUDP *createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort);
+
+signals:
+	// outgoing
+	void connected();
+
+	// incoming
+	void incomingMethods(int);
+	void incomingAuth(const QString &user, const QString &pass);
+	void incomingConnectRequest(const QString &host, int port);
+	void incomingUDPAssociateRequest();
+
+private slots:
+	void sock_connected();
+	void sock_connectionClosed();
+	void sock_delayedCloseFinished();
+	void sock_readyRead();
+	void sock_bytesWritten(int);
+	void sock_error(int);
+	void serve();
+
+private:
+	class Private;
+	Private *d;
+
+	void init();
+	void reset(bool clear=false);
+	void do_request();
+	void processOutgoing(const QByteArray &);
+	void processIncoming(const QByteArray &);
+	void continueIncoming();
+	void writeData(const QByteArray &a);
+};
+
+class SocksServer : public QObject
+{
+	Q_OBJECT
+public:
+	SocksServer(QObject *parent=0);
+	~SocksServer();
+
+	bool isActive() const;
+	bool listen(Q_UINT16 port, bool udp=false);
+	void stop();
+	int port() const;
+	QHostAddress address() const;
+	SocksClient *takeIncoming();
+
+	void writeUDP(const QHostAddress &addr, int port, const QByteArray &data);
+
+signals:
+	void incomingReady();
+	void incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data);
+
+private slots:
+	void connectionReady(int);
+	void connectionError();
+	void sn_activated(int);
+
+private:
+	class Private;
+	Private *d;
+};
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/network/srvresolver.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/srvresolver.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/srvresolver.cpp	(revision 38)
@@ -0,0 +1,322 @@
+/*
+ * srvresolver.cpp - class to simplify SRV lookups
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"srvresolver.h"
+
+#include<qcstring.h>
+#include<qtimer.h>
+#include<qdns.h>
+#include"safedelete.h"
+
+#ifndef NO_NDNS
+#include"ndns.h"
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+static void sortSRVList(QValueList<QDns::Server> &list)
+{
+	QValueList<QDns::Server> tmp = list;
+	list.clear();
+
+	while(!tmp.isEmpty()) {
+		QValueList<QDns::Server>::Iterator p = tmp.end();
+		for(QValueList<QDns::Server>::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
+			if(p == tmp.end())
+				p = it;
+			else {
+				int a = (*it).priority;
+				int b = (*p).priority;
+				int j = (*it).weight;
+				int k = (*p).weight;
+				if(a < b || (a == b && j < k))
+					p = it;
+			}
+		}
+		list.append(*p);
+		tmp.remove(p);
+	}
+}
+
+class SrvResolver::Private
+{
+public:
+	Private() {}
+
+	QDns *qdns;
+#ifndef NO_NDNS
+	NDns ndns;
+#endif
+
+	bool failed;
+	QHostAddress resultAddress;
+	Q_UINT16 resultPort;
+
+	bool srvonly;
+	QString srv;
+	QValueList<QDns::Server> servers;
+	bool aaaa;
+
+	QTimer t;
+	SafeDelete sd;
+};
+
+SrvResolver::SrvResolver(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->qdns = 0;
+
+#ifndef NO_NDNS
+	connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+#endif
+	connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+	stop();
+}
+
+SrvResolver::~SrvResolver()
+{
+	stop();
+	delete d;
+}
+
+void SrvResolver::resolve(const QString &server, const QString &type, const QString &proto)
+{
+	stop();
+
+	d->failed = false;
+	d->srvonly = false;
+	d->srv = QString("_") + type + "._" + proto + '.' + server;
+	d->t.start(15000, true);
+	d->qdns = new QDns;
+	connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done()));
+	d->qdns->setRecordType(QDns::Srv);
+	d->qdns->setLabel(d->srv);
+}
+
+void SrvResolver::resolveSrvOnly(const QString &server, const QString &type, const QString &proto)
+{
+	stop();
+
+	d->failed = false;
+	d->srvonly = true;
+	d->srv = QString("_") + type + "._" + proto + '.' + server;
+	d->t.start(15000, true);
+	d->qdns = new QDns;
+	connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done()));
+	d->qdns->setRecordType(QDns::Srv);
+	d->qdns->setLabel(d->srv);
+}
+
+void SrvResolver::next()
+{
+	if(d->servers.isEmpty())
+		return;
+
+	tryNext();
+}
+
+void SrvResolver::stop()
+{
+	if(d->t.isActive())
+		d->t.stop();
+	if(d->qdns) {
+		d->qdns->disconnect(this);
+		d->sd.deleteLater(d->qdns);
+		d->qdns = 0;
+	}
+#ifndef NO_NDNS
+	if(d->ndns.isBusy())
+		d->ndns.stop();
+#endif
+	d->resultAddress = QHostAddress();
+	d->resultPort = 0;
+	d->servers.clear();
+	d->srv = "";
+	d->failed = true;
+}
+
+bool SrvResolver::isBusy() const
+{
+#ifndef NO_NDNS
+	if(d->qdns || d->ndns.isBusy())
+#else
+	if(d->qdns)
+#endif
+		return true;
+	else
+		return false;
+}
+
+QValueList<QDns::Server> SrvResolver::servers() const
+{
+	return d->servers;
+}
+
+bool SrvResolver::failed() const
+{
+	return d->failed;
+}
+
+QHostAddress SrvResolver::resultAddress() const
+{
+	return d->resultAddress;
+}
+
+Q_UINT16 SrvResolver::resultPort() const
+{
+	return d->resultPort;
+}
+
+void SrvResolver::tryNext()
+{
+#ifndef NO_NDNS
+	d->ndns.resolve(d->servers.first().name);
+#else
+	d->qdns = new QDns;
+	connect(d->qdns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+	if(d->aaaa)
+		d->qdns->setRecordType(QDns::Aaaa); // IPv6
+	else
+		d->qdns->setRecordType(QDns::A); // IPv4
+	d->qdns->setLabel(d->servers.first().name);
+#endif
+}
+
+void SrvResolver::qdns_done()
+{
+	if(!d->qdns)
+		return;
+
+	// apparently we sometimes get this signal even though the results aren't ready
+	if(d->qdns->isWorking())
+		return;
+	d->t.stop();
+
+	SafeDeleteLock s(&d->sd);
+
+	// grab the server list and destroy the qdns object
+	QValueList<QDns::Server> list;
+	if(d->qdns->recordType() == QDns::Srv)
+		list = d->qdns->servers();
+	d->qdns->disconnect(this);
+	d->sd.deleteLater(d->qdns);
+	d->qdns = 0;
+
+	if(list.isEmpty()) {
+		stop();
+		resultsReady();
+		return;
+	}
+	sortSRVList(list);
+	d->servers = list;
+
+	if(d->srvonly)
+		resultsReady();
+	else {
+		// kick it off
+		d->aaaa = true;
+		tryNext();
+	}
+}
+
+void SrvResolver::ndns_done()
+{
+#ifndef NO_NDNS
+	SafeDeleteLock s(&d->sd);
+
+	// ignore unexpected messages (may happen when MAP_NDNS_TO_QDNS is defined)
+	if (d->servers.isEmpty())
+		return;
+	
+	uint r = d->ndns.result();
+	int port = d->servers.first().port;
+	d->servers.remove(d->servers.begin());
+
+	if(r) {
+		d->resultAddress = QHostAddress(d->ndns.result());
+		d->resultPort = port;
+		resultsReady();
+	}
+	else {
+		// failed?  bail if last one
+		if(d->servers.isEmpty()) {
+			stop();
+			resultsReady();
+			return;
+		}
+
+		// otherwise try the next
+		tryNext();
+	}
+#else
+	if(!d->qdns)
+		return;
+
+	// apparently we sometimes get this signal even though the results aren't ready
+	if(d->qdns->isWorking())
+		return;
+
+	SafeDeleteLock s(&d->sd);
+
+	// grab the address list and destroy the qdns object
+	QValueList<QHostAddress> list;
+	if(d->qdns->recordType() == QDns::A || d->qdns->recordType() == QDns::Aaaa)
+		list = d->qdns->addresses();
+	d->qdns->disconnect(this);
+	d->sd.deleteLater(d->qdns);
+	d->qdns = 0;
+
+	if(!list.isEmpty()) {
+		int port = d->servers.first().port;
+		d->servers.remove(d->servers.begin());
+		d->aaaa = true;
+
+		d->resultAddress = list.first();
+		d->resultPort = port;
+		resultsReady();
+	}
+	else {
+		if(!d->aaaa)
+			d->servers.remove(d->servers.begin());
+		d->aaaa = !d->aaaa;
+
+		// failed?  bail if last one
+		if(d->servers.isEmpty()) {
+			stop();
+			resultsReady();
+			return;
+		}
+
+		// otherwise try the next
+		tryNext();
+	}
+#endif
+}
+
+void SrvResolver::t_timeout()
+{
+	SafeDeleteLock s(&d->sd);
+
+	stop();
+	resultsReady();
+}
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/network/srvresolver.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/network/srvresolver.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/network/srvresolver.h	(revision 38)
@@ -0,0 +1,65 @@
+/*
+ * srvresolver.h - class to simplify SRV lookups
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_SRVRESOLVER_H
+#define CS_SRVRESOLVER_H
+
+#include<qvaluelist.h>
+#include<qdns.h>
+
+// CS_NAMESPACE_BEGIN
+
+class SrvResolver : public QObject
+{
+	Q_OBJECT
+public:
+	SrvResolver(QObject *parent=0);
+	~SrvResolver();
+
+	void resolve(const QString &server, const QString &type, const QString &proto);
+	void resolveSrvOnly(const QString &server, const QString &type, const QString &proto);
+	void next();
+	void stop();
+	bool isBusy() const;
+
+	QValueList<QDns::Server> servers() const;
+
+	bool failed() const;
+	QHostAddress resultAddress() const;
+	Q_UINT16 resultPort() const;
+
+signals:
+	void resultsReady();
+
+private slots:
+	void qdns_done();
+	void ndns_done();
+	void t_timeout();
+
+private:
+	class Private;
+	Private *d;
+
+	void tryNext();
+};
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/rtsp/altports.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/rtsp/altports.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/rtsp/altports.cpp	(revision 38)
@@ -0,0 +1,443 @@
+/*
+ * altports.cpp - manage alternative UDP port ranges
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "altports.h"
+
+#include <qsocketdevice.h>
+#include <qsocketnotifier.h>
+#include <qptrlist.h>
+
+//----------------------------------------------------------------------------
+// PortRange
+//----------------------------------------------------------------------------
+PortRange::PortRange()
+{
+	base = 0;
+	count = 0;
+}
+
+QString PortRange::toString() const
+{
+	QString s;
+	if(count > 0)
+	{
+		s += QString::number(base);
+		if(count > 1)
+		{
+			s += '-';
+			s += QString::number(base + count - 1);
+		}
+	}
+	return s;
+}
+
+bool PortRange::fromString(const QString &s)
+{
+	if(s.isEmpty())
+		return false;
+
+	int n = s.find('-');
+	if(n == -1)
+	{
+		// single port
+		base = s.toInt();
+		count = 1;
+	}
+	else
+	{
+		// port range
+		int first = s.mid(0, n).toInt();
+		int last = s.mid(n + 1).toInt();
+		if(first < 1 || first > 65535 || last < first || last > 65535)
+			return false;
+
+		base = first;
+		count = last - first + 1;
+	}
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// PortRangeList
+//----------------------------------------------------------------------------
+void PortRangeList::merge(const PortRange &r)
+{
+	// see if we have this base already
+	for(Iterator it = begin(); it != end(); ++it)
+	{
+		PortRange &pr = *it;
+		if(pr.base == r.base)
+		{
+			pr.count = QMAX(pr.count, r.count);
+			return;
+		}
+	}
+
+	// else, just append it
+	append(r);
+}
+
+int PortRangeList::findByBase(int base) const
+{
+	int index = 0;
+	for(ConstIterator it = begin(); it != end(); ++it)
+	{
+		if((*it).base == base)
+			return index;
+		++index;
+	}
+	return -1;
+}
+
+//----------------------------------------------------------------------------
+// UDPItem
+//----------------------------------------------------------------------------
+class UDPItem : public QObject
+{
+	Q_OBJECT
+public:
+	static UDPItem *create(const QHostAddress &addr, int port)
+	{
+		QSocketDevice *sd = new QSocketDevice(QSocketDevice::Datagram);
+		sd->setBlocking(false);
+		if(!sd->bind(addr, port))
+			return 0;
+		UDPItem *i = new UDPItem;
+		i->sd = sd;
+		i->sn = new QSocketNotifier(i->sd->socket(), QSocketNotifier::Read);
+		i->connect(i->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+		i->_port = port;
+		//printf("UDP BIND: [%d]\n", port);
+		return i;
+	}
+
+	~UDPItem()
+	{
+		delete sn;
+		delete sd;
+		//printf("UDP UNBIND: [%d]\n", _port);
+	}
+
+	int port() const
+	{
+		return _port;
+	}
+
+	void write(const QByteArray &buf, const QHostAddress &addr, int port)
+	{
+		sd->setBlocking(true);
+		sd->writeBlock(buf.data(), buf.size(), addr, port);
+		sd->setBlocking(false);
+	}
+
+signals:
+	void packetReady(const QByteArray &buf, const QHostAddress &addr, int port);
+
+private slots:
+	void sn_activated(int)
+	{
+		QByteArray buf(8192);
+		int actual = sd->readBlock(buf.data(), buf.size());
+		buf.resize(actual);
+		QHostAddress pa = sd->peerAddress();
+		int pp = sd->peerPort();
+		packetReady(buf, pa, pp);
+	}
+
+private:
+	UDPItem()
+	{
+	}
+
+	QSocketDevice *sd;
+	QSocketNotifier *sn;
+	int _port;
+};
+
+//----------------------------------------------------------------------------
+// PortRange
+//----------------------------------------------------------------------------
+#define PORT_ALLOC_BASE 16000
+#define PORT_ALLOC_MAX  65535
+
+class PortSequence : public QObject
+{
+	Q_OBJECT
+public:
+	PortSequence()
+	{
+		list.setAutoDelete(true);
+	}
+
+	void reset()
+	{
+		list.clear();
+	}
+
+	bool allocate(const QHostAddress &address, int count, int base=-1)
+	{
+		return try_allocate(address, count, base);
+	}
+
+	bool resize(int count)
+	{
+		// don't allow growing
+		if(count > (int)list.count())
+			return false;
+
+		QPtrListIterator<UDPItem> it(list);
+		it += count;
+		int del = list.count() - count;
+		for(int n = 0; n < del; ++n)
+			list.removeRef(it.current());
+		return true;
+	}
+
+	void send(int index, const QHostAddress &addr, int destPort, const QByteArray &buf)
+	{
+		if(index >= 0 && index < (int)list.count())
+			list.at(index)->write(buf, addr, destPort);
+	}
+
+	QHostAddress address() const
+	{
+		return addr;
+	}
+
+	int base() const
+	{
+		if(!list.isEmpty())
+			return list.getFirst()->port();
+		else
+			return -1;
+	}
+
+	int count() const
+	{
+		return list.count();
+	}
+
+signals:
+	void packetReady(int index, const QHostAddress &addr, int port, const QByteArray &buf);
+
+private slots:
+	void udp_packetReady(const QByteArray &buf, const QHostAddress &addr, int port)
+	{
+		UDPItem *su = (UDPItem *)sender();
+		bool found = false;
+		int index = 0;
+		QPtrListIterator<UDPItem> it(list);
+		for(UDPItem *u; (u = it.current()); ++it)
+		{
+			if(u == su)
+			{
+				found = true;
+				break;
+			}
+			++index;
+		}
+		if(!found)
+			return;
+
+		packetReady(index, addr, port, buf);
+	}
+
+private:
+	QHostAddress addr;
+	QPtrList<UDPItem> list;
+
+	bool try_allocate(const QHostAddress &address, int count, int base)
+	{
+		reset();
+		if(count < 1)
+			return true;
+
+		int start = base != -1 ? base : PORT_ALLOC_BASE;
+		while(start + (count - 1) <= PORT_ALLOC_MAX)
+		{
+			QPtrList<UDPItem> udplist;
+			bool ok = true;
+			for(int n = 0; n < count; ++n)
+			{
+				UDPItem *i = UDPItem::create(address, start + n);
+				if(!i)
+				{
+					ok = false;
+					break;
+				}
+				udplist.append(i);
+			}
+			if(ok)
+			{
+				list = udplist;
+				break;
+			}
+			udplist.setAutoDelete(true);
+
+			// if using 'start', we only get one chance
+			if(base != -1)
+				break;
+
+			start += udplist.count() + 1;
+		}
+
+		if(list.isEmpty())
+			return false;
+
+		addr = address;
+
+		QPtrListIterator<UDPItem> it(list);
+		for(UDPItem *u; (u = it.current()); ++it)
+			connect(u, SIGNAL(packetReady(const QByteArray &, const QHostAddress &, int)), SLOT(udp_packetReady(const QByteArray &, const QHostAddress &, int)));
+
+		return true;
+	}
+};
+
+//----------------------------------------------------------------------------
+// AltPorts
+//----------------------------------------------------------------------------
+class AltPorts::Private : public QObject
+{
+	Q_OBJECT
+public:
+	AltPorts *par;
+	QPtrList<PortSequence> list;
+	PortSequence *ports;
+	PortRangeList orig;
+
+	Private(AltPorts *_par)
+	{
+		par = _par;
+		ports = 0;
+	}
+
+public slots:
+	void range_packetReady(int index, const QHostAddress &addr, int port, const QByteArray &buf)
+	{
+		par->packetReady(index, addr, port, buf);
+	}
+};
+
+AltPorts::AltPorts()
+{
+	d = new Private(this);
+}
+
+AltPorts::~AltPorts()
+{
+	delete d;
+}
+
+void AltPorts::reset()
+{
+	d->list.clear();
+	delete d->ports;
+	d->ports = 0;
+	d->orig.clear();
+}
+
+bool AltPorts::isEmpty() const
+{
+	if(!d->ports && d->list.isEmpty())
+		return true;
+	return false;
+}
+
+bool AltPorts::allocate(const PortRange &real, PortRange *alt)
+{
+	if(!isEmpty())
+		return false;
+
+	PortRangeList in, out;
+	in.append(real);
+	if(!reserve(in, &out))
+		return false;
+	keep(real);
+	*alt = out.first();
+	return true;
+}
+
+bool AltPorts::reserve(const PortRangeList &real, PortRangeList *alt)
+{
+	if(!isEmpty())
+		return false;
+
+	PortRangeList out;
+	QPtrList<PortSequence> list;
+	for(PortRangeList::ConstIterator it = real.begin(); it != real.end(); ++it)
+	{
+		PortSequence *s = new PortSequence;
+		if(!s->allocate(QHostAddress(), (*it).count))
+		{
+			delete s;
+			list.setAutoDelete(true);
+			return false;
+		}
+		PortRange r;
+		r.base = s->base();
+		r.count = s->count();
+		out.append(r);
+		list.append(s);
+	}
+
+	d->list = list;
+	d->orig = real;
+	*alt = out;
+	return true;
+}
+
+void AltPorts::keep(const PortRange &r)
+{
+	if(d->ports)
+		return;
+
+	int n = d->orig.findByBase(r.base);
+	if(n == -1)
+		return;
+
+	PortSequence *s = d->list.at(n);
+	s->resize(r.count);
+	d->ports = s;
+	d->list.removeRef(s);
+	d->list.setAutoDelete(true);
+	d->list.clear();
+	d->list.setAutoDelete(false);
+	connect(d->ports, SIGNAL(packetReady(int, const QHostAddress &, int, const QByteArray &)), d, SLOT(range_packetReady(int, const QHostAddress &, int, const QByteArray &)));
+}
+
+PortRange AltPorts::range() const
+{
+	PortRange r;
+	if(d->ports)
+	{
+		r.base = d->ports->base();
+		r.count = d->ports->count();
+	}
+	return r;
+}
+
+void AltPorts::send(int index, const QHostAddress &addr, int destPort, const QByteArray &buf)
+{
+	if(d->ports)
+		d->ports->send(index, addr, destPort, buf);
+}
+
+#include "altports.moc"
Index: psi/tags/0.10-os2-b1/cutestuff/rtsp/altports.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/rtsp/altports.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/rtsp/altports.h	(revision 38)
@@ -0,0 +1,73 @@
+/*
+ * altports.h - manage alternative UDP port ranges
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ALTPORTS_H
+#define ALTPORTS_H
+
+#include <qobject.h>
+#include <qcstring.h>
+#include <qvaluelist.h>
+#include <qhostaddress.h>
+
+class PortRange
+{
+public:
+	PortRange();
+
+	int base, count;
+
+	QString toString() const;
+	bool fromString(const QString &s);
+};
+
+class PortRangeList : public QValueList<PortRange>
+{
+public:
+	void merge(const PortRange &r);
+	int findByBase(int base) const;
+};
+
+class AltPorts : public QObject
+{
+	Q_OBJECT
+public:
+	AltPorts();
+	~AltPorts();
+
+	void reset();
+	bool isEmpty() const;
+
+	bool allocate(const PortRange &real, PortRange *alt);
+	bool reserve(const PortRangeList &real, PortRangeList *alt);
+	void keep(const PortRange &r);
+	PortRange range() const;
+	void send(int index, const QHostAddress &addr, int destPort, const QByteArray &buf);
+
+signals:
+	void packetReady(int index, const QHostAddress &addr, int sourcePort, const QByteArray &buf);
+
+public:
+	class Private;
+private:
+	friend class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspbase.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspbase.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspbase.cpp	(revision 38)
@@ -0,0 +1,1028 @@
+/*
+ * rtspbase.cpp - very basic RTSP client/server
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "rtspbase.h"
+
+#include <qtimer.h>
+#include <qguardedptr.h>
+#include "bsocket.h"
+#include "servsock.h"
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+#else
+# include <netinet/in.h>
+#endif
+
+#define MAX_CONTENT_LENGTH 32767
+
+namespace RTSP {
+
+//----------------------------------------------------------------------------
+// HeaderList
+//----------------------------------------------------------------------------
+HeaderList::HeaderList()
+:QValueList<Var>()
+{
+}
+
+bool HeaderList::has(const QString &var) const
+{
+	QString low = var.lower();
+	for(ConstIterator it = begin(); it != end(); ++it)
+	{
+		if((*it).name.lower() == low)
+			return true;
+	}
+	return false;
+}
+
+QString HeaderList::get(const QString &var) const
+{
+	QString low = var.lower();
+	for(ConstIterator it = begin(); it != end(); ++it)
+	{
+		if((*it).name.lower() == low)
+			return (*it).value;
+	}
+	return QString::null;
+}
+
+void HeaderList::set(const QString &var, const QString &val)
+{
+	QString low = var.lower();
+	for(Iterator it = begin(); it != end(); ++it)
+	{
+		if((*it).name.lower() == low)
+		{
+			(*it).value = val;
+			return;
+		}
+	}
+	Var v;
+	v.name = var;
+	v.value = val;
+	append(v);
+}
+
+void HeaderList::remove(const QString &var)
+{
+	QString low = var.lower();
+	for(Iterator it = begin(); it != end(); ++it)
+	{
+		if((*it).name.lower() == low)
+		{
+			QValueList<Var>::remove(it);
+			return;
+		}
+	}
+}
+
+//----------------------------------------------------------------------------
+// Transport
+//----------------------------------------------------------------------------
+static int findOneOf(const QString &s, const QString &list, int i, QChar *found)
+{
+	for(int n = i; n < (int)s.length(); ++n)
+	{
+		for(int k = 0; k < (int)list.length(); ++k)
+		{
+			if(s[n] == list[k])
+			{
+				if(found)
+					*found = list[k];
+				return n;
+			}
+		}
+	}
+	return -1;
+}
+
+Transport::Transport()
+{
+}
+
+QString Transport::name() const
+{
+	return _name;
+}
+
+TransportArgs Transport::arguments() const
+{
+	return _args;
+}
+
+QString Transport::argument(const QString &name) const
+{
+	for(TransportArgs::ConstIterator it = _args.begin(); it != _args.end(); ++it)
+	{
+		const Var &v = *it;
+		if(v.name == name)
+			return v.value;
+	}
+	return QString::null;
+}
+
+void Transport::setArguments(const TransportArgs &list)
+{
+	_args = list;
+}
+
+void Transport::setArgument(const QString &name, const QString &value)
+{
+	for(TransportArgs::Iterator it = _args.begin(); it != _args.end(); ++it)
+	{
+		Var &v = *it;
+		if(v.name == name)
+		{
+			v.value = value;
+			return;
+		}
+	}
+}
+
+QString Transport::toString() const
+{
+	QString s;
+	s += _name;
+	if(!_args.isEmpty())
+	{
+		for(TransportArgs::ConstIterator it = _args.begin(); it != _args.end(); ++it)
+		{
+			s += ';';
+			s += (*it).name;
+			if(!(*it).value.isNull())
+			{
+				s += '=';
+				s += (*it).value;
+			}
+		}
+	}
+	return s;
+}
+
+Transport::ReadStatus Transport::readFromString(QString *sp)
+{
+	const QString &s = *sp;
+	QString tname;
+	QValueList<Var> targs;
+
+	// first read the name
+	int at = 0;
+	QChar c;
+	int n = findOneOf(s, ",;", at, &c);
+	if(n == -1)
+	{
+		tname = s.mid(at);
+		// empty at end means nothing left
+		if(tname.isEmpty())
+			return Done;
+
+		at = s.length();
+	}
+	else if(c == ',')
+	{
+		tname = s.mid(at, n - at);
+		// empty with separator is bad
+		if(tname.isEmpty())
+			return Error;
+
+		at = n + 1;
+	}
+	else if(c == ';')
+	{
+		tname = s.mid(at, n - at);
+		// empty with separator is bad
+		if(tname.isEmpty())
+			return Error;
+
+		at = n + 1;
+
+		// read args
+		QString targ, tval;
+		bool last = false;
+		while(!last)
+		{
+			// get arg
+			n = findOneOf(s, ",;=", at, &c);
+			if(n == -1)
+			{
+				targ = s.mid(at);
+				// empty at end means nothing left
+				if(targ.isEmpty())
+					break;
+
+				at = s.length();
+				last = true;
+			}
+			else if(c == ',')
+			{
+				targ = s.mid(at, n - at);
+				// empty with separator is bad
+				if(targ.isEmpty())
+					return Error;
+
+				at = n + 1;
+				last = true;
+			}
+			else if(c == ';')
+			{
+				targ = s.mid(at, n - at);
+				// empty with separator is bad
+				if(targ.isEmpty())
+					return Error;
+
+				at = n + 1;
+			}
+			else if(c == '=')
+			{
+				// this arg has a value
+				targ = s.mid(at, n - at);
+				// empty with value is bad
+				if(targ.isEmpty())
+					return Error;
+
+				at = n + 1;
+
+				// is next char a quote?
+				if(at < (int)s.length() && s[at] == '\"')
+				{
+					n = s.find('\"', at + 1);
+					if(n == -1)
+						return Error;
+					tval = s.mid(at, n - at + 1);
+					at = n + 1;
+
+					if(at < (int)s.length())
+					{
+						// if not at end, the next char better be a separator
+						if(s[at] != ',' && s[at] != ';')
+							return Error;
+
+						if(s[at] == ',')
+							last = true;
+
+						// skip over it
+						++at;
+					}
+					else
+						last = true;
+				}
+				else
+				{
+					// find next separator
+					n = findOneOf(s, ",;", at, &c);
+					if(n == -1)
+					{
+						tval = s.mid(at);
+						at = s.length();
+						last = true;
+					}
+					else if(c == ',')
+					{
+						tval = s.mid(at, n - at);
+						at = n + 1;
+						last = true;
+					}
+					else if(c == ';')
+					{
+						tval = s.mid(at, n - at);
+						at = n + 1;
+					}
+				}
+			}
+
+			Var v;
+			v.name = targ;
+			v.value = tval;
+			targs.append(v);
+		}
+	}
+
+	_name = tname;
+	_args = targs;
+
+	// remove what we just parsed
+	(*sp) = sp->mid(at);
+	return Ok;
+}
+
+//----------------------------------------------------------------------------
+// TransportList
+//----------------------------------------------------------------------------
+TransportList::TransportList()
+:QValueList<Transport>()
+{
+}
+
+QString TransportList::toString() const
+{
+	QStringList list;
+	for(ConstIterator it = begin(); it != end(); ++it)
+		list.append((*it).toString());
+	return list.join(",");
+}
+
+bool TransportList::fromString(const QString &s)
+{
+	QString tmp = s;
+	clear();
+
+	while(1)
+	{
+		Transport t;
+		Transport::ReadStatus r = t.readFromString(&tmp);
+		if(r == Transport::Error)
+			return false;
+		if(r == Transport::Done)
+			break;
+		append(t);
+	}
+
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// Packet
+//----------------------------------------------------------------------------
+Packet::Packet()
+{
+	t = Empty;
+}
+
+Packet::Packet(const QString &command, const QString &resource, const HeaderList &headers)
+{
+	cmd = command;
+	res = resource;
+	_headers = headers;
+	ver = "1.0";
+}
+
+Packet::Packet(int responseCode, const QString &responseString, const HeaderList &headers)
+{
+	rcode = responseCode;
+	rstr = responseString;
+	_headers = headers;
+	ver = "1.0";
+}
+
+Packet::~Packet()
+{
+}
+
+bool Packet::isNull() const
+{
+	return (t == Empty);
+}
+
+Packet::Type Packet::type() const
+{
+	return t;
+}
+
+QString Packet::version() const
+{
+	return ver;
+}
+
+QString Packet::command() const
+{
+	return cmd;
+}
+
+QString Packet::resource() const
+{
+	return res;
+}
+
+int Packet::responseCode() const
+{
+	return rcode;
+}
+
+QString Packet::responseString() const
+{
+	return rstr;
+}
+
+int Packet::channel() const
+{
+	return chan;
+}
+
+QByteArray Packet::data() const
+{
+	return _data;
+}
+
+HeaderList & Packet::headers()
+{
+	return _headers;
+}
+
+const HeaderList & Packet::headers() const
+{
+	return _headers;
+}
+
+TransportList Packet::transports() const
+{
+	TransportList list;
+	list.fromString(_headers.get("Transport"));
+	return list;
+}
+
+void Packet::setTransports(const TransportList &list)
+{
+	_headers.set("Transport", list.toString());
+}
+
+void Packet::setResource(const QString &s)
+{
+	res = s;
+}
+
+QByteArray Packet::toArray() const
+{
+	QByteArray buf;
+
+	if(t == Data)
+	{
+		buf.resize(4 + _data.size());
+
+		buf[0] = '$';
+		buf[1] = chan;
+		ushort ssa = _data.size();
+		ushort ssb = htons(ssa);
+		memcpy(buf.data() + 2, &ssb, 2);
+		memcpy(buf.data() + 4, _data.data(), _data.size());
+	}
+	else
+	{
+		QString str;
+
+		if(t == Request)
+		{
+			str += cmd + ' ' + res + " RTSP/" + ver + '\n';
+			for(HeaderList::ConstIterator it = _headers.begin(); it != _headers.end(); ++it)
+				str += (*it).name + ": " + (*it).value + '\n';
+			str += '\n';
+		}
+		else if(t == Response)
+		{
+			str += "RTSP/" + ver + ' ' + QString::number(rcode) + ' ' + rstr + '\n';
+			for(HeaderList::ConstIterator it = _headers.begin(); it != _headers.end(); ++it)
+				str += (*it).name + ": " + (*it).value + '\n';
+			str += '\n';
+		}
+
+		QCString cs = str.utf8();
+		buf.resize(cs.length());
+		memcpy(buf.data(), cs.data(), buf.size());
+		ByteStream::appendArray(&buf, _data);
+	}
+
+	return buf;
+}
+
+//----------------------------------------------------------------------------
+// Parser
+//----------------------------------------------------------------------------
+Parser::Parser()
+{
+}
+
+void Parser::reset(Mode _mode)
+{
+	mode = _mode;
+	in.resize(0);
+	cur.clear();
+	tmp = Packet();
+	list.clear();
+}
+
+void Parser::appendData(const QByteArray &a)
+{
+	ByteStream::appendArray(&in, a);
+}
+
+Packet Parser::read(bool *ok)
+{
+	if(ok)
+		*ok = true;
+
+	if(list.isEmpty())
+	{
+		if(!readPacket())
+		{
+			if(ok)
+				*ok = false;
+		}
+	}
+
+	Packet p;
+	if(!list.isEmpty())
+	{
+		p = list.first();
+		list.remove(list.begin());
+	}
+	return p;
+}
+
+bool Parser::readPacket()
+{
+	bool interleaved = false;
+	bool done = false;
+
+	// don't know what it is yet?  let's have a look
+	if(tmp.t == Packet::Empty)
+	{
+		// need at least 1 byte
+		if(in.isEmpty())
+			return true;
+
+		if(in[0] == '$')
+		{
+			// interleaved data
+			if(in.size() < 4)
+				return true;
+			ushort ss;
+			memcpy(&ss, in.data() + 2, 2);
+			int size = ntohs(ss);
+			if((int)in.size() < 4 + size)
+				return true;
+
+			QByteArray buf = ByteStream::takeArray(&in, 4 + size);
+			tmp.t = Packet::Data;
+			tmp.chan = buf[1];
+			tmp._data.resize(size);
+			memcpy(tmp._data.data(), buf.data() + 4, size);
+
+			interleaved = true;
+			done = true;
+		}
+		else
+		{
+			// regular request/response
+			if(mode == Client)
+				tmp.t = Packet::Request;
+			else
+				tmp.t = Packet::Response;
+			readingContent = false;
+		}
+	}
+
+	if(!interleaved)
+	{
+		if(!readingContent)
+		{
+			// read the lines
+			QStringList lines = readPacketLines();
+			if(lines.isEmpty())
+				return true;
+			if(!packetFromLines(lines))
+				return false;
+			if(tmp._headers.has("Content-length"))
+			{
+				clen = tmp._headers.get("Content-length").toInt();
+				if(clen > MAX_CONTENT_LENGTH)
+					return false;
+			}
+			else
+				clen = 0;
+			readingContent = true;
+		}
+
+		if(readingContent)
+		{
+			if(clen > 0)
+			{
+				int need = clen - tmp._data.size();
+				QByteArray a;
+				if((int)in.size() >= need)
+					a = ByteStream::takeArray(&in, need);
+				else
+					a = ByteStream::takeArray(&in);
+				ByteStream::appendArray(&tmp._data, a);
+			}
+
+			if(clen == 0 || (int)tmp._data.size() == clen)
+				done = true;
+		}
+	}
+
+	if(done)
+	{
+		list.append(tmp);
+		tmp = Packet();
+	}
+
+	return true;
+}
+
+bool Parser::packetFromLines(const QStringList &lines)
+{
+	if(mode == Client)
+	{
+		if(lines.isEmpty())
+			return false;
+		QString str = lines[0];
+		int n = str.find(' ');
+		if(n == -1)
+			return false;
+		QString c = str.mid(0, n);
+		int at = n + 1;
+		n = str.find(' ', at);
+		if(n == -1)
+			return false;
+		QString r = str.mid(at, n - at);
+		QString v = str.mid(n + 1);
+
+		if(v.left(5) != "RTSP/")
+			return false;
+
+		HeaderList headers;
+		if(!readHeaders(lines, &headers))
+			return false;
+
+		tmp.cmd = c;
+		tmp.res = r;
+		tmp.ver = v.mid(5);
+		tmp._headers = headers;
+		return true;
+	}
+	else
+	{
+		if(lines.isEmpty())
+			return false;
+		QString str = lines[0];
+		int n = str.find(' ');
+		if(n == -1)
+			return false;
+		QString v = str.mid(0, n);
+		int at = n + 1;
+		n = str.find(' ', at);
+		if(n == -1)
+			return false;
+		QString c = str.mid(at, n - at);
+		QString s = str.mid(n + 1);
+		if(v.left(5) != "RTSP/")
+			return false;
+
+		HeaderList headers;
+		if(!readHeaders(lines, &headers))
+			return false;
+
+		tmp.ver = v.mid(5);
+		tmp.rcode = c.toInt();
+		tmp.rstr = s;
+		tmp._headers = headers;
+		return true;
+	}
+}
+
+bool Parser::readHeaders(const QStringList &lines, HeaderList *headers)
+{
+	// skip the first line
+	QStringList::ConstIterator it = lines.begin();
+	++it;
+
+	headers->clear();
+	for(; it != lines.end(); ++it)
+	{
+		const QString &s = *it;
+		int n = s.find(':');
+		if(n == -1)
+			return false;
+		Var v;
+		v.name = s.mid(0, n).stripWhiteSpace();
+		v.value = s.mid(n + 1).stripWhiteSpace();
+		headers->append(v);
+	}
+	return true;
+}
+
+QStringList Parser::readPacketLines()
+{
+	QStringList lines;
+	while(1)
+	{
+		QString str = tryReadLine();
+		if(str.isNull())
+			break;
+
+		if(str.isEmpty())
+		{
+			lines = cur;
+			cur.clear();
+			break;
+		}
+
+		cur.append(str);
+	}
+	return lines;
+}
+
+QString Parser::tryReadLine()
+{
+	for(int n = 0; n < (int)in.size(); ++n)
+	{
+		if(in[n] == '\n')
+		{
+			int eat = n + 1;
+			if(n > 0 && in[n-1] == '\r')
+				--n;
+			QByteArray buf = ByteStream::takeArray(&in, eat);
+			QCString cs;
+			cs.resize(n + 1);
+			memcpy(cs.data(), buf.data(), n);
+			return QString::fromUtf8(cs);
+		}
+	}
+	return QString::null;
+}
+
+//----------------------------------------------------------------------------
+// Client
+//----------------------------------------------------------------------------
+class Client::Private
+{
+public:
+	Private()
+	{
+		bs = 0;
+		using_sock = false;
+		conn = false;
+		active = false;
+	}
+
+	ByteStream *bs;
+	bool using_sock;
+	bool conn;
+	bool active;
+	Parser parser;
+	QValueList<int> trackQueue;
+};
+
+Client::Client(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+}
+
+Client::Client(int socket)
+{
+	d = new Private;
+	d->using_sock = true;
+	BSocket *sock = new BSocket;
+	d->bs = sock;
+	d->conn = false;
+	hook();
+	d->parser.reset(Parser::Client); // as a server, we want to parse client requests
+	sock->setSocket(socket);
+}
+
+Client::~Client()
+{
+	delete d->bs;
+	delete d;
+}
+
+void Client::hook()
+{
+	connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+	connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+	connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+	connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int)));
+}
+
+void Client::processPackets()
+{
+	QGuardedPtr<QObject> self = this;
+	while(1)
+	{
+		bool ok;
+		Packet p = d->parser.read(&ok);
+		if(!ok)
+		{
+			delete d->bs;
+			d->bs = 0;
+			error(ErrParse);
+			return;
+		}
+
+		if(p.isNull())
+			break;
+
+		packetReady(p);
+		if(!self)
+			return;
+	}
+}
+
+void Client::connectToHost(const QString &host, int port)
+{
+	BSocket *sock = new BSocket;
+	connect(sock, SIGNAL(connected()), SLOT(sock_connected()));
+	d->bs = sock;
+	d->conn = true;
+	hook();
+	sock->connectToHost(host, port);
+}
+
+void Client::setByteStream(ByteStream *bs, Mode mode)
+{
+	d->bs = bs;
+	d->conn = false;
+	hook();
+	d->parser.reset(mode == MClient ? Parser::Server : Parser::Client);
+}
+
+void Client::close()
+{
+	if(d->bs)
+		d->bs->close();
+}
+
+void Client::write(const Packet &p)
+{
+	QByteArray buf = p.toArray();
+	d->trackQueue.append(buf.size());
+	d->bs->write(buf);
+}
+
+QHostAddress Client::peerAddress() const
+{
+	QHostAddress addr;
+	if(d->using_sock)
+		addr = ((BSocket *)d->bs)->peerAddress();
+	return addr;
+}
+
+void Client::sock_connected()
+{
+	d->using_sock = true;
+	d->active = true;
+	d->parser.reset(Parser::Server); // as a client, we want to parse server responses
+	connected();
+}
+
+void Client::bs_connectionClosed()
+{
+	connectionClosed();
+}
+
+void Client::bs_readyRead()
+{
+	QByteArray buf = d->bs->read();
+	d->parser.appendData(buf);
+	if(d->active)
+		processPackets();
+}
+
+void Client::bs_bytesWritten(int bytes)
+{
+	int written = 0;
+	for(QValueList<int>::Iterator it = d->trackQueue.begin(); it != d->trackQueue.end();)
+	{
+		int &i = *it;
+
+		// enough bytes?
+		if(bytes < i) {
+			i -= bytes;
+			break;
+		}
+		bytes -= i;
+		it = d->trackQueue.remove(it);
+		++written;
+	}
+
+	for(int n = 0; n < written; ++n)
+		packetWritten();
+}
+
+void Client::bs_error(int x)
+{
+	delete d->bs;
+	d->bs = 0;
+
+	if(d->conn)
+	{
+		if(x == BSocket::ErrConnectionRefused || x == BSocket::ErrHostNotFound)
+		{
+			error(ErrConnect);
+			return;
+		}
+	}
+
+	error(ErrStream);
+	return;
+}
+
+void Client::serve()
+{
+	d->active = true;
+	processPackets();
+}
+
+//----------------------------------------------------------------------------
+// Server
+//----------------------------------------------------------------------------
+class Server::Private
+{
+public:
+	Private() {}
+
+	ServSock serv;
+	QPtrList<Client> incomingConns;
+};
+
+Server::Server(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	connect(&d->serv, SIGNAL(connectionReady(int)), SLOT(connectionReady(int)));
+}
+
+Server::~Server()
+{
+	d->incomingConns.setAutoDelete(true);
+	d->incomingConns.clear();
+	delete d;
+}
+
+bool Server::isActive() const
+{
+	return d->serv.isActive();
+}
+
+bool Server::start(int port)
+{
+	return d->serv.listen(port);
+}
+
+void Server::stop()
+{
+	d->serv.stop();
+}
+
+int Server::port() const
+{
+	return d->serv.port();
+}
+
+QHostAddress Server::address() const
+{
+	return d->serv.address();
+}
+
+Client *Server::takeIncoming()
+{
+	if(d->incomingConns.isEmpty())
+		return 0;
+
+	Client *c = d->incomingConns.getFirst();
+	d->incomingConns.removeRef(c);
+
+	// we don't care about errors anymore
+	disconnect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+
+	// don't serve the connection until the event loop, to give the caller a chance to map signals
+	QTimer::singleShot(0, c, SLOT(serve()));
+
+	return c;
+}
+
+void Server::connectionReady(int s)
+{
+	Client *c = new Client(s);
+	connect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+	d->incomingConns.append(c);
+	incomingReady();
+}
+
+void Server::connectionError()
+{
+	Client *c = (Client *)sender();
+	d->incomingConns.removeRef(c);
+	c->deleteLater();
+}
+
+}
Index: psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspbase.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspbase.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspbase.h	(revision 38)
@@ -0,0 +1,226 @@
+/*
+ * rtspbase.h - very basic RTSP client/server
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+// TODO:
+//  - only listen on the local interface (or let the caller specify the interface)
+//  - client should not emit server replies unless the client actually sent a packet,
+//    to keep things in sync
+//  - if content-length maximum is exceeded, signal this fact?
+
+#ifndef RTSPBASE_H
+#define RTSPBASE_H
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+#include <qobject.h>
+#include <qhostaddress.h>
+
+class ByteStream;
+
+namespace RTSP
+{
+	class Parser;
+	class Server;
+
+	struct Var
+	{
+		QString name, value;
+	};
+
+	// This should probably be a QMap, but it isn't, so there.
+	class HeaderList : public QValueList<Var>
+	{
+	public:
+		HeaderList();
+
+		bool has(const QString &var) const;
+		QString get(const QString &var) const;
+		void set(const QString &var, const QString &val);
+		void remove(const QString &var);
+	};
+
+	typedef QValueList<Var> TransportArgs;
+	class Transport
+	{
+	public:
+		enum ReadStatus { Ok, Done, Error };
+		Transport();
+
+		QString name() const;
+		TransportArgs arguments() const;
+		QString argument(const QString &name) const;
+
+		void setArguments(const TransportArgs &list);
+		void setArgument(const QString &name, const QString &value);
+
+		QString toString() const;
+		ReadStatus readFromString(QString *s);
+
+	private:
+		QString _name;
+		TransportArgs _args;
+	};
+
+	class TransportList : public QValueList<Transport>
+	{
+	public:
+		TransportList();
+
+		QString toString() const;
+		bool fromString(const QString &s);
+	};
+
+	class Packet
+	{
+	public:
+		enum Type { Empty, Request, Response, Data };
+		Packet();
+		Packet(const QString &command, const QString &resource, const HeaderList &headers=HeaderList());
+		Packet(int responseCode, const QString &responseString, const HeaderList &headers=HeaderList());
+		~Packet();
+
+		bool isNull() const;
+		Type type() const;
+
+		QString version() const;
+		QString command() const;
+		QString resource() const;
+		int responseCode() const;
+		QString responseString() const;
+		int channel() const;
+		QByteArray data() const;
+
+		HeaderList & headers();
+		const HeaderList & headers() const;
+
+		TransportList transports() const;
+		void setTransports(const TransportList &list);
+
+		void setResource(const QString &s);
+
+		QByteArray toArray() const;
+
+	private:
+		friend class Parser;
+		Type t;
+		QString ver, cmd, res, rstr;
+		int rcode, chan;
+		QByteArray _data;
+		HeaderList _headers;
+	};
+
+	class Parser
+	{
+	public:
+		enum Mode { Client, Server };
+		Parser();
+
+		void reset(Mode mode);
+		void appendData(const QByteArray &a);
+		Packet read(bool *ok=0);
+
+	private:
+		Mode mode;
+		QByteArray in;
+		QStringList cur;
+		Packet tmp;
+		QValueList<Packet> list;
+		int clen;
+		bool readingContent;
+
+		bool readPacket();
+		bool packetFromLines(const QStringList &lines);
+		bool readHeaders(const QStringList &lines, HeaderList *headers);
+		QStringList readPacketLines();
+		QString tryReadLine();
+	};
+
+	class Client : public QObject
+	{
+		Q_OBJECT
+	public:
+		enum Mode { MClient, MServer };
+		enum Error { ErrConnect, ErrParse, ErrStream };
+		Client(QObject *parent=0);
+		~Client();
+
+		void connectToHost(const QString &host, int port);
+		void setByteStream(ByteStream *bs, Mode mode);
+		void close();
+
+		void write(const Packet &p);
+
+		QHostAddress peerAddress() const;
+
+	signals:
+		void connected();
+		void connectionClosed();
+		void packetReady(const Packet &p);
+		void packetWritten();
+		void error(int);
+
+	private slots:
+		void sock_connected();
+		void bs_connectionClosed();
+		void bs_readyRead();
+		void bs_bytesWritten(int);
+		void bs_error(int);
+		void serve();
+
+	private:
+		class Private;
+		Private *d;
+
+		friend class Server;
+		Client(int socket);
+		void hook();
+		void processPackets();
+	};
+
+	class Server : public QObject
+	{
+		Q_OBJECT
+	public:
+		Server(QObject *parent=0);
+		~Server();
+
+		bool isActive() const;
+		bool start(int port);
+		void stop();
+		int port() const;
+		QHostAddress address() const;
+		Client *takeIncoming();
+
+	signals:
+		void incomingReady();
+
+	private slots:
+		void connectionReady(int);
+		void connectionError();
+
+	private:
+		class Private;
+		Private *d;
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspproxy.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspproxy.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspproxy.cpp	(revision 38)
@@ -0,0 +1,707 @@
+/*
+ * rtspproxy.cpp - proxy for RTSP allowing direct and/or virtual endpoints
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "rtspproxy.h"
+
+#include <qurl.h>
+#include "servsock.h"
+#include "bsocket.h"
+#include "rtspbase.h"
+#include "altports.h"
+
+#define SERVER_ALLOC_BASE 16000
+#define SERVER_ALLOC_MAX  65535
+
+static bool try_serve(RTSP::Server *s)
+{
+	for(int n = SERVER_ALLOC_BASE; n <= SERVER_ALLOC_MAX; ++n)
+	{
+		if(s->start(n))
+			return true;
+	}
+	return false;
+}
+
+//----------------------------------------------------------------------------
+// PortMapper
+//----------------------------------------------------------------------------
+static PortRangeList create_virtual_ranges(const PortRangeList &in)
+{
+	PortRangeList out;
+	int base = 1;
+	for(PortRangeList::ConstIterator it = in.begin(); it != in.end(); ++it)
+	{
+		PortRange r;
+		r.base = base;
+		r.count = (*it).count;
+		base += r.count;
+		out.append(r);
+	}
+	return out;
+}
+
+class MapItem
+{
+public:
+	bool active;
+	bool virt;
+	QHostAddress host;
+	PortRange realPorts;
+	AltPorts altPorts;
+	PortRangeList realPortRanges, altPortRanges;
+
+	MapItem()
+	{
+		active = false;
+		virt = false;
+	}
+
+	void reset()
+	{
+		active = false;
+		virt = false;
+		host = QHostAddress();
+		altPorts.reset();
+	}
+};
+
+class PortMapper : public QObject
+{
+	Q_OBJECT
+public:
+	PortMapper();
+	~PortMapper();
+
+	void reset();
+
+	// setup
+	bool reserveDirectClient(const PortRangeList &clientRanges, bool virtServer=false);
+	bool reserveVirtualClient(const PortRangeList &clientRanges);
+	bool finalize(const QHostAddress &clientHost, const PortRange &clientPorts, const QHostAddress &serverHost, const PortRange &serverPorts);
+
+	PortRangeList clientAlternatePorts() const;
+	PortRangeList serverAlternatePorts() const;
+
+	void writeAsClient(int source, int dest, const QByteArray &buf);
+	void writeAsServer(int source, int dest, const QByteArray &buf);
+
+signals:
+	void packetFromClient(int source, int dest, const QByteArray &buf);
+	void packetFromServer(int source, int dest, const QByteArray &buf);
+
+private slots:
+	void client_packetReady(int index, const QHostAddress &addr, int sourcePort, const QByteArray &buf);
+	void server_packetReady(int index, const QHostAddress &addr, int sourcePort, const QByteArray &buf);
+
+private:
+	MapItem client, server;
+	bool ready;
+};
+
+PortMapper::PortMapper()
+{
+	connect(&client.altPorts, SIGNAL(packetReady(int, const QHostAddress &, int, const QByteArray &)), SLOT(client_packetReady(int, const QHostAddress &, int, const QByteArray &)));
+	connect(&server.altPorts, SIGNAL(packetReady(int, const QHostAddress &, int, const QByteArray &)), SLOT(server_packetReady(int, const QHostAddress &, int, const QByteArray &)));
+	ready = false;
+}
+
+PortMapper::~PortMapper()
+{
+}
+
+void PortMapper::reset()
+{
+	client.reset();
+	server.reset();
+	ready = false;
+}
+
+bool PortMapper::reserveDirectClient(const PortRangeList &clientRanges, bool virtServer)
+{
+	if(client.active)
+		return true;
+
+	client.realPortRanges = clientRanges;
+	if(virtServer)
+	{
+		client.virt = true; // virtual server means virtual client ports
+		client.altPortRanges = create_virtual_ranges(clientRanges);
+	}
+	else
+	{
+		if(!client.altPorts.reserve(clientRanges, &client.altPortRanges))
+			return false;
+	}
+	client.active = true;
+
+	return true;
+}
+
+bool PortMapper::reserveVirtualClient(const PortRangeList &clientRanges)
+{
+	if(client.active)
+		return false;
+
+	server.virt = true; // virtual client means virtual server ports
+	client.realPortRanges = clientRanges;
+	client.altPorts.reserve(clientRanges, &client.altPortRanges);
+	client.active = true;
+
+	return true;
+}
+
+bool PortMapper::finalize(const QHostAddress &clientHost, const PortRange &clientPorts, const QHostAddress &serverHost, const PortRange &serverPorts)
+{
+	if(ready)
+		return true;
+
+	int n = client.altPortRanges.findByBase(clientPorts.base);
+	if(n == -1)
+		return false;
+
+	client.host = clientHost;
+	client.realPorts = client.realPortRanges[n];
+	client.realPorts.count = clientPorts.count;
+
+	if(client.virt)
+	{
+		PortRange virtPorts = client.altPortRanges[n];
+		virtPorts.count = clientPorts.count;
+		client.altPortRanges.clear();
+		client.altPortRanges.append(virtPorts);
+	}
+	else
+	{
+		client.altPorts.keep(client.realPorts);
+		client.altPortRanges.clear();
+		client.altPortRanges.append(client.altPorts.range());
+	}
+
+	server.host = serverHost;
+	server.realPorts = serverPorts;
+
+	if(server.virt)
+	{
+		PortRangeList in;
+		in.append(server.realPorts);
+		server.altPortRanges = create_virtual_ranges(in);
+	}
+	else
+	{
+		PortRange r;
+		if(!server.altPorts.allocate(server.realPorts, &r))
+			return false;
+		server.altPortRanges.clear();
+		server.altPortRanges.append(r);
+	}
+	server.active = true;
+
+	ready = true;
+	return true;
+}
+
+PortRangeList PortMapper::clientAlternatePorts() const
+{
+	return client.altPortRanges;
+}
+
+PortRangeList PortMapper::serverAlternatePorts() const
+{
+	return server.altPortRanges;
+}
+
+void PortMapper::writeAsClient(int source, int, const QByteArray &buf)
+{
+	if(source < client.realPorts.base || source >= client.realPorts.base + client.realPorts.count)
+		return;
+	int index = source - client.realPorts.base;
+	client.altPorts.send(index, server.host, server.realPorts.base + index, buf);
+}
+
+void PortMapper::writeAsServer(int source, int, const QByteArray &buf)
+{
+	if(source < server.realPorts.base || source >= server.realPorts.base + server.realPorts.count)
+		return;
+	int index = source - server.realPorts.base;
+	server.altPorts.send(index, client.host, client.realPorts.base + index, buf);
+}
+
+void PortMapper::client_packetReady(int index, const QHostAddress &, int, const QByteArray &buf)
+{
+	if(server.virt)
+		emit packetFromServer(server.altPortRanges.first().base + index, client.realPorts.base + index, buf);
+	else
+		server.altPorts.send(index, client.host, client.realPorts.base + index, buf);
+}
+
+void PortMapper::server_packetReady(int index, const QHostAddress &, int, const QByteArray &buf)
+{
+	if(client.virt)
+		emit packetFromClient(client.altPortRanges.first().base + index, server.realPorts.base + index, buf);
+	else
+		client.altPorts.send(index, server.host, server.realPorts.base + index, buf);
+}
+
+//----------------------------------------------------------------------------
+// Session
+//----------------------------------------------------------------------------
+using namespace RTSP;
+
+static PortRangeList transport_get_ports(const TransportList &list, const QString &type)
+{
+	PortRangeList out;
+	for(TransportList::ConstIterator it = list.begin(); it != list.end(); ++it)
+	{
+		PortRange r;
+		if(!r.fromString((*it).argument(type)))
+			continue;
+		out.merge(r);
+	}
+	return out;
+}
+
+static TransportList transport_set_ports(const TransportList &_list, const QString &type, const PortRangeList &newpl)
+{
+	TransportList list = _list;
+	PortRangeList oldpl = transport_get_ports(_list, type);
+	for(TransportList::Iterator it = list.begin(); it != list.end(); ++it)
+	{
+		Transport &t = *it;
+		PortRange r;
+		if(!r.fromString(t.argument(type)))
+			continue;
+		int n = oldpl.findByBase(r.base);
+		if(n == -1)
+			continue;
+		r.base = newpl[n].base;
+		t.setArgument(type, r.toString());
+	}
+	return list;
+}
+
+static PortRangeList transport_get_client_ports(const TransportList &list)
+{
+	return transport_get_ports(list, "client_port");
+}
+
+static TransportList transport_set_client_ports(const TransportList &_list, const PortRangeList &newpl)
+{
+	return transport_set_ports(_list, "client_port", newpl);
+}
+
+static PortRangeList transport_get_server_ports(const TransportList &list)
+{
+	return transport_get_ports(list, "server_port");
+}
+
+static TransportList transport_set_server_ports(const TransportList &_list, const PortRangeList &newpl)
+{
+	return transport_set_ports(_list, "server_port", newpl);
+}
+
+static void showPacket(const RTSP::Packet &p)
+{
+	if(p.type() == RTSP::Packet::Request)
+	{
+		printf("--- RTSP Request     ---\n");
+		printf("Command:  [%s]\n", p.command().latin1());
+		printf("Resource: [%s]\n", p.resource().latin1());
+		printf("Version:  [%s]\n", p.version().latin1());
+		printf("Headers:\n");
+		HeaderList headers = p.headers();
+		for(HeaderList::ConstIterator it = headers.begin(); it != headers.end(); ++it)
+			printf("  [%s] = [%s]\n", (*it).name.latin1(), (*it).value.latin1());
+		QByteArray data = p.data();
+		if(!data.isEmpty())
+			printf("[%d bytes of attached content]\n", data.size());
+		printf("------------------------\n");
+	}
+	else if(p.type() == RTSP::Packet::Response)
+	{
+		printf("--- RTSP Response    ---\n");
+		printf("Code:     [%d]\n", p.responseCode());
+		printf("String:   [%s]\n", p.responseString().latin1());
+		printf("Version:  [%s]\n", p.version().latin1());
+		printf("Headers:\n");
+		HeaderList headers = p.headers();
+		for(HeaderList::ConstIterator it = headers.begin(); it != headers.end(); ++it)
+			printf("  [%s] = [%s]\n", (*it).name.latin1(), (*it).value.latin1());
+		QByteArray data = p.data();
+		if(!data.isEmpty())
+			printf("[%d bytes of attached content]\n", data.size());
+		printf("------------------------\n");
+	}
+	else if(p.type() == RTSP::Packet::Data)
+	{
+		printf("--- RTSP Interleaved ---\n");
+		printf("Channel:  [%d]\n", p.channel());
+		QByteArray data = p.data();
+		if(!data.isEmpty())
+			printf("[%d bytes of RTP content]\n", data.size());
+		printf("------------------------\n");
+	}
+}
+
+class Session : public QObject
+{
+	Q_OBJECT
+public:
+	Session()
+	{
+		client = 0;
+		server = 0;
+
+		connect(&local, SIGNAL(incomingReady()), SLOT(local_incomingReady()));
+		connect(&mapper, SIGNAL(packetFromClient(int, int, const QByteArray &)), SLOT(map_packetFromClient(int, int, const QByteArray &)));
+		connect(&mapper, SIGNAL(packetFromServer(int, int, const QByteArray &)), SLOT(map_packetFromServer(int, int, const QByteArray &)));
+	}
+
+	~Session()
+	{
+		reset();
+	}
+
+	void reset()
+	{
+		delete client;
+		client = 0;
+		delete server;
+		server = 0;
+	}
+
+	bool startIncoming(const QValueList<QUrl> &_urls, ByteStream *_server, int *incomingPort)
+	{
+		urls = _urls;
+		server = new RTSP::Client;
+		server->setByteStream(_server, RTSP::Client::MServer);
+
+		if(!try_serve(&local))
+			return false;
+
+		virtClient = false;
+		virtServer = true;
+		*incomingPort = local.port();
+		return true;
+	}
+
+	bool startIncoming(const QValueList<QUrl> &_urls, const QString &serverHost, int serverPort, int *incomingPort)
+	{
+		if(_urls.isEmpty())
+			return false;
+
+		urls = _urls;
+		shost = serverHost;
+		sport = serverPort;
+
+		if(!try_serve(&local))
+			return false;
+
+		virtClient = false;
+		virtServer = false;
+		*incomingPort = local.port();
+		return true;
+	}
+
+	bool startExisting(const QValueList<QUrl> &_urls, ByteStream *_client, const QString &serverHost, int serverPort)
+	{
+		urls = _urls;
+		client = new RTSP::Client;
+		client->setByteStream(_client, RTSP::Client::MClient);
+		shost = serverHost;
+		sport = serverPort;
+
+		virtClient = true;
+		virtServer = false;
+		return true;
+	}
+
+	void writeAsClient(int source, int dest, const QByteArray &buf)
+	{
+		mapper.writeAsClient(source, dest, buf);
+	}
+
+	void writeAsServer(int source, int dest, const QByteArray &buf)
+	{
+		mapper.writeAsServer(source, dest, buf);
+	}
+
+signals:
+	void packetFromClient(int source, int dest, const QByteArray &buf);
+	void packetFromServer(int source, int dest, const QByteArray &buf);
+
+private slots:
+	void local_incomingReady()
+	{
+		Client *c = local.takeIncoming();
+		if(!c)
+			return;
+
+		local.stop();
+
+		client = c;
+		connect(client, SIGNAL(connectionClosed()), SLOT(client_connectionClosed()));
+		connect(client, SIGNAL(packetReady(const Packet &)), SLOT(client_packetReady(const Packet &)));
+		connect(client, SIGNAL(packetWritten()), SLOT(client_packetWritten()));
+		connect(client, SIGNAL(error(int)), SLOT(client_error(int)));
+	}
+
+	void client_connectionClosed()
+	{
+		printf("Session: Client: connectionClosed\n");
+		delete client;
+		client = 0;
+	}
+
+	void client_packetReady(const Packet &p)
+	{
+		showPacket(p);
+
+		Packet m = p;
+		lastWasSetup = false;
+
+		// unmangle the url?
+		if(!virtClient)
+		{
+			QUrl u = urls.first();
+			int u_port = u.hasPort() ? u.port() : 554;
+
+			QUrl pu(m.resource());
+			pu.setHost(u.host());
+			pu.setPort(u_port == 554 ? -1 : u_port);
+
+			m.setResource(pu.toString());
+		}
+
+		QString cmd = m.command();
+		if(cmd == "SETUP")
+		{
+			TransportList list = m.transports();
+			PortRangeList pl = transport_get_client_ports(list);
+
+			/*printf("SETUP ports [%d]:\n", pl.count());
+			for(PortRangeList::ConstIterator it = pl.begin(); it != pl.end(); ++it)
+				printf("[%d-%d] ", (*it).base, (*it).count);
+			printf("\n");*/
+
+			if(virtClient)
+				mapper.reserveVirtualClient(pl);
+			else
+				mapper.reserveDirectClient(pl, virtServer);
+			PortRangeList altPorts = mapper.clientAlternatePorts();
+
+			/*printf("Alternate ports [%d]:\n", altPorts.count());
+			for(PortRangeList::ConstIterator it = altPorts.begin(); it != altPorts.end(); ++it)
+				printf("[%d-%d] ", (*it).base, (*it).count);
+			printf("\n");*/
+
+			m.setTransports(transport_set_client_ports(list, altPorts));
+			lastWasSetup = true;
+		}
+
+		cpackets.append(m);
+
+		// on receipt of first packet, connect to server if necessary
+		if(!server)
+		{
+			server = new Client;
+			connect(server, SIGNAL(connected()), SLOT(server_connected()));
+			connect(server, SIGNAL(connectionClosed()), SLOT(server_connectionClosed()));
+			connect(server, SIGNAL(packetReady(const Packet &)), SLOT(server_packetReady(const Packet &)));
+			connect(server, SIGNAL(packetWritten()), SLOT(server_packetWritten()));
+			connect(server, SIGNAL(error(int)), SLOT(server_error(int)));
+			printf("Session: Server: connecting to server\n");
+			server->connectToHost(shost, sport);
+			return;
+		}
+
+		sendPackets();
+	}
+
+	void client_packetWritten()
+	{
+		//printf("Session: Client: packetWritten\n");
+	}
+
+	void client_error(int x)
+	{
+		printf("Session: Client: error %d\n", x);
+		delete client;
+		client = 0;
+	}
+
+	void server_connected()
+	{
+		printf("Session: Server: connected\n");
+		sendPackets();
+	}
+
+	void server_connectionClosed()
+	{
+		printf("Session: Server: connectionClosed\n");
+		reset();
+	}
+
+	void server_packetReady(const Packet &p)
+	{
+		showPacket(p);
+		if(client)
+		{
+			Packet m = p;
+			if(lastWasSetup)
+			{
+				TransportList list = m.transports();
+				PortRangeList cpl = transport_get_client_ports(list);
+				PortRangeList spl = transport_get_server_ports(list);
+
+				/*printf("SETUP ports [%d]:\n", cpl.count());
+				for(PortRangeList::ConstIterator it = cpl.begin(); it != cpl.end(); ++it)
+					printf("[%d-%d] ", (*it).base, (*it).count);
+				printf("\n");
+				printf("Server SETUP ports [%d]:\n", spl.count());
+				for(PortRangeList::ConstIterator it = spl.begin(); it != spl.end(); ++it)
+					printf("[%d-%d] ", (*it).base, (*it).count);
+				printf("\n");*/
+
+				mapper.finalize(client->peerAddress(), cpl.first(), server->peerAddress(), spl.first());
+				PortRangeList altPorts = mapper.serverAlternatePorts();
+
+				/*printf("Alternate ports [%d]:\n", altPorts.count());
+				for(PortRangeList::ConstIterator it = altPorts.begin(); it != altPorts.end(); ++it)
+					printf("[%d-%d] ", (*it).base, (*it).count);
+				printf("\n");*/
+
+				m.setTransports(transport_set_server_ports(list, altPorts));
+			}
+			showPacket(m);
+			client->write(m);
+		}
+	}
+
+	void server_packetWritten()
+	{
+		//printf("Session: Server: packetWritten\n");
+	}
+
+	void server_error(int x)
+	{
+		printf("Session: Server: error %d\n", x);
+		reset();
+	}
+
+	void map_packetFromClient(int source, int dest, const QByteArray &buf)
+	{
+		packetFromClient(source, dest, buf);
+	}
+
+	void map_packetFromServer(int source, int dest, const QByteArray &buf)
+	{
+		packetFromServer(source, dest, buf);
+	}
+
+private:
+	void sendPackets()
+	{
+		for(QValueList<Packet>::Iterator it = cpackets.begin(); it != cpackets.end();)
+		{
+			showPacket(*it);
+			server->write(*it);
+			it = cpackets.remove(it);
+		}
+	}
+
+	bool virtClient, virtServer;
+	QValueList<Packet> cpackets;
+	Client *client, *server;
+	Server local;
+	QValueList<QUrl> urls;
+	QString shost;
+	int sport;
+	PortMapper mapper;
+	bool lastWasSetup;
+};
+
+//----------------------------------------------------------------------------
+// RTSPProxy
+//----------------------------------------------------------------------------
+class RTSPProxy::Private : public QObject
+{
+	Q_OBJECT
+public:
+	RTSPProxy *par;
+
+	Private(RTSPProxy *_par) : par(_par)
+	{
+	}
+};
+
+RTSPProxy::RTSPProxy(QObject *parent)
+:QObject(parent)
+{
+	d = new Private(this);
+}
+
+RTSPProxy::~RTSPProxy()
+{
+	delete d;
+}
+
+int RTSPProxy::startIncoming(const QStringList &urls, ByteStream *server, int *incomingPort)
+{
+}
+
+int RTSPProxy::startIncoming(const QStringList &urls, const QString &serverHost, int serverPort, int *incomingPort)
+{
+	QValueList<QUrl> list;
+	for(QStringList::ConstIterator it = urls.begin(); it != urls.end(); ++it)
+		list.append(QUrl(*it));
+	Session *s = new Session;
+	if(!s->startIncoming(list, serverHost, serverPort, incomingPort))
+	{
+		delete s;
+		return -1;
+	}
+	// TODO: add session to a list or something
+}
+
+int RTSPProxy::startExisting(const QStringList &urls, ByteStream *client, const QString &serverHost, int serverPort)
+{
+}
+
+void RTSPProxy::stop(int id)
+{
+}
+
+void RTSPProxy::writeAsClient(int id, int source, int dest, const QByteArray &buf)
+{
+}
+
+void RTSPProxy::writeAsServer(int id, int source, int dest, const QByteArray &buf)
+{
+}
+
+QString RTSPProxy::mangle(const QString &url, const QString &host, int port)
+{
+	QUrl u(url);
+	u.setHost(host);
+	u.setPort(port == 554 ? -1 : port);
+	return u.toString();
+}
+
+#include "rtspproxy.moc"
Index: psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspproxy.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspproxy.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/rtsp/rtspproxy.h	(revision 38)
@@ -0,0 +1,56 @@
+/*
+ * rtspproxy.h - proxy for RTSP allowing direct and/or virtual endpoints
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef RTSPPROXY_H
+#define RTSPPROXY_H
+
+#include <qobject.h>
+#include <qcstring.h>
+#include <qstringlist.h>
+#include "bytestream.h"
+
+class RTSPProxy : public QObject
+{
+	Q_OBJECT
+public:
+	RTSPProxy(QObject *parent=0);
+	~RTSPProxy();
+
+	int startIncoming(const QStringList &urls, ByteStream *server, int *incomingPort);
+	int startIncoming(const QStringList &urls, const QString &serverHost, int serverPort, int *incomingPort);
+	int startExisting(const QStringList &urls, ByteStream *client, const QString &serverHost, int serverPort);
+	void stop(int id);
+
+	void writeAsClient(int id, int source, int dest, const QByteArray &buf);
+	void writeAsServer(int id, int source, int dest, const QByteArray &buf);
+
+	static QString mangle(const QString &url, const QString &host, int port);
+
+signals:
+	void packetFromClient(int id, int source, int dest, const QByteArray &buf);
+	void packetFromServer(int id, int source, int dest, const QByteArray &buf);
+
+public:
+	class Private;
+private:
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/socksd.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/socksd.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/socksd.cpp	(revision 38)
@@ -0,0 +1,178 @@
+#include"socksd.h"
+
+#include<qapplication.h>
+#include"bconsole.h"
+#include"bsocket.h"
+#include"socks.h"
+
+#include<stdio.h>
+
+class App::Private
+{
+public:
+	Private() {}
+
+	ByteStream *bs;
+	BConsole *c;
+	SocksServer *serv;
+	SocksClient *client;
+
+	QString user, pass;
+};
+
+App::App(int port, const QString &user, const QString &pass)
+:QObject(0)
+{
+	d = new Private;
+	d->user = user;
+	d->pass = pass;
+	d->c = new BConsole;
+	connect(d->c, SIGNAL(connectionClosed()), SLOT(con_connectionClosed()));
+	connect(d->c, SIGNAL(readyRead()), SLOT(con_readyRead()));
+
+	d->bs = 0;
+	d->client = 0;
+	d->serv = new SocksServer;
+	connect(d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady()));
+	d->serv->listen(port);
+
+	fprintf(stderr, "socksd: listening on port %d\n", port);
+}
+
+App::~App()
+{
+	if(d->client)
+		d->client->deleteLater();
+	delete d->serv;
+	delete d->c;
+	delete d;
+}
+
+void App::st_connectionClosed()
+{
+	fprintf(stderr, "socksd: Connection closed by foreign host.\n");
+	quit();
+}
+
+void App::st_delayedCloseFinished()
+{
+	quit();
+}
+
+void App::st_readyRead()
+{
+	QByteArray a = d->bs->read();
+	d->c->write(a);
+}
+
+void App::st_error(int x)
+{
+	fprintf(stderr, "socksd: Stream error [%d].\n", x);
+	quit();
+}
+
+void App::con_connectionClosed()
+{
+	fprintf(stderr, "adconn: Closing.\n");
+	d->bs->close();
+	if(d->bs->bytesToWrite() == 0)
+		quit();
+}
+
+void App::con_readyRead()
+{
+	QByteArray a = d->c->read();
+	d->bs->write(a);
+}
+
+void App::ss_incomingReady()
+{
+	fprintf(stderr, "incoming connection!\n");
+	SocksClient *c = d->serv->takeIncoming();
+	if(!c)
+		return;
+	fprintf(stderr, "accepted\n");
+	connect(c, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int)));
+	connect(c, SIGNAL(incomingAuth(const QString &, const QString &)), SLOT(sc_incomingAuth(const QString &, const QString &)));
+	connect(c, SIGNAL(incomingRequest(const QString &, int)), SLOT(sc_incomingRequest(const QString &, int)));
+	connect(c, SIGNAL(error(int)), SLOT(sc_error(int)));
+	d->client = c;
+}
+
+void App::sc_incomingMethods(int m)
+{
+	fprintf(stderr, "m=%d\n", m);
+	if(d->user.isEmpty() && m & SocksClient::AuthNone)
+		d->client->chooseMethod(SocksClient::AuthNone);
+	else if(m & SocksClient::AuthUsername)
+		d->client->chooseMethod(SocksClient::AuthUsername);
+	else {
+		d->client->deleteLater();
+		d->client = 0;
+		fprintf(stderr, "unsupported method!\n");
+	}
+}
+
+void App::sc_incomingAuth(const QString &user, const QString &pass)
+{
+	fprintf(stderr, "incoming auth: user=[%s], pass=[%s]\n", user.latin1(), pass.latin1());
+	if(user == d->user && pass == d->pass) {
+		d->client->authGrant(true);
+	}
+	else {
+		d->client->authGrant(false);
+		d->client->deleteLater();
+		d->client = 0;
+	}
+}
+
+void App::sc_incomingRequest(const QString &host, int port)
+{
+	fprintf(stderr, "request: host=[%s], port=[%d]\n", host.latin1(), port);
+
+	disconnect(d->client, SIGNAL(error(int)), this, SLOT(sc_error(int)));
+	connect(d->client, SIGNAL(connectionClosed()), SLOT(st_connectionClosed()));
+	connect(d->client, SIGNAL(delayedCloseFinished()), SLOT(st_delayedCloseFinished()));
+	connect(d->client, SIGNAL(readyRead()), SLOT(st_readyRead()));
+	connect(d->client, SIGNAL(error(int)), SLOT(st_error(int)));
+	d->bs = d->client;
+
+	d->client->requestGrant(true);
+	fprintf(stderr, "<< Active >>\n");
+}
+
+void App::sc_error(int)
+{
+	d->client->deleteLater();
+	d->client = 0;
+
+	fprintf(stderr, "error!\n");
+}
+
+
+int main(int argc, char **argv)
+{
+	QApplication app(argc, argv, false);
+
+	if(argc < 2) {
+		printf("usage: socksd [port] [user] [pass]\n\n");
+		return 0;
+	}
+
+	QString p = argv[1];
+	int port = p.toInt();
+
+	QString user, pass;
+	if(argc >= 4) {
+		user = argv[2];
+		pass = argv[3];
+	}
+
+	App *a = new App(port, user, pass);
+	QObject::connect(a, SIGNAL(quit()), &app, SLOT(quit()));
+	app.exec();
+	delete a;
+
+	return 0;
+}
+
Index: psi/tags/0.10-os2-b1/cutestuff/socksd.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/socksd.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/socksd.h	(revision 38)
@@ -0,0 +1,36 @@
+#ifndef TEST_H
+#define TEST_H
+
+#include<qobject.h>
+
+class App : public QObject
+{
+	Q_OBJECT
+public:
+	App(int, const QString &, const QString &);
+	~App();
+
+signals:
+	void quit();
+
+private slots:
+	void st_connectionClosed();
+	void st_delayedCloseFinished();
+	void st_readyRead();
+	void st_error(int);
+
+	void con_connectionClosed();
+	void con_readyRead();
+
+	void ss_incomingReady();
+	void sc_incomingMethods(int);
+	void sc_incomingAuth(const QString &user, const QString &pass);
+	void sc_incomingRequest(const QString &host, int port);
+	void sc_error(int);
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/socksd.pro
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/socksd.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/socksd.pro	(revision 38)
@@ -0,0 +1,25 @@
+CONFIG += thread
+TARGET  = socksd
+
+DEFINES += PROX_DEBUG
+
+INCLUDEPATH += util network
+
+HEADERS = \
+	util/bytestream.h \
+	util/bconsole.h \
+	network/ndns.h \
+	network/bsocket.h \
+	network/servsock.h \
+	network/socks.h \
+	socksd.h
+
+SOURCES = \
+	util/bytestream.cpp \
+	util/bconsole.cpp \
+	network/ndns.cpp \
+	network/bsocket.cpp \
+	network/servsock.cpp \
+	network/socks.cpp \
+	socksd.cpp
+
Index: psi/tags/0.10-os2-b1/cutestuff/util/TODO
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/TODO	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/TODO	(revision 38)
@@ -0,0 +1,7 @@
+varlist
+common (opening urls)
+zip
+showtext
+format parsing
+xml handling, elem2string, etc
+
Index: psi/tags/0.10-os2-b1/cutestuff/util/base64.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/base64.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/base64.cpp	(revision 38)
@@ -0,0 +1,182 @@
+/*
+ * base64.cpp - Base64 converting functions
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"base64.h"
+
+// CS_NAMESPACE_BEGIN
+
+//! \class Base64 base64.h
+//! \brief Base64 conversion functions.
+//!
+//! Converts Base64 data between arrays and strings.
+//!
+//! \code
+//! #include "base64.h"
+//!
+//! ...
+//!
+//! // encode a block of data into base64
+//! QByteArray block(1024);
+//! QByteArray enc = Base64::encode(block);
+//!
+//!  \endcode
+
+//!
+//! Encodes array \a s and returns the result.
+QByteArray Base64::encode(const QByteArray &s)
+{
+	int i;
+	int len = s.size();
+	char tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+	int a, b, c;
+
+	QByteArray p((len+2)/3*4);
+	int at = 0;
+	for( i = 0; i < len; i += 3 ) {
+		a = ((unsigned char)s[i] & 3) << 4;
+		if(i + 1 < len) {
+			a += (unsigned char)s[i + 1] >> 4;
+			b = ((unsigned char)s[i + 1] & 0xF) << 2;
+			if(i + 2 < len) {
+				b += (unsigned char)s[i + 2] >> 6;
+				c = (unsigned char)s[i + 2] & 0x3F;
+			}
+			else
+				c = 64;
+		}
+		else
+			b = c = 64;
+
+		p[at++] = tbl[(unsigned char)s[i] >> 2];
+		p[at++] = tbl[a];
+		p[at++] = tbl[b];
+		p[at++] = tbl[c];
+	}
+	return p;
+}
+
+//!
+//! Decodes array \a s and returns the result.
+QByteArray Base64::decode(const QByteArray &s)
+{
+	// return value
+	QByteArray p;
+
+	// -1 specifies invalid
+	// 64 specifies eof
+	// everything else specifies data
+
+	char tbl[] = {
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
+		52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,
+		-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+		15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+		-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+		41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+	};
+
+	// this should be a multiple of 4
+	int len = s.size();
+
+	if(len % 4)
+		return p;
+
+	p.resize(len / 4 * 3);
+
+	int i;
+	int at = 0;
+
+	int a, b, c, d;
+	c = d = 0;
+
+	for( i = 0; i < len; i += 4 ) {
+		a = tbl[(int)s[i]];
+		b = tbl[(int)s[i + 1]];
+		c = tbl[(int)s[i + 2]];
+		d = tbl[(int)s[i + 3]];
+		if((a == 64 || b == 64) || (a < 0 || b < 0 || c < 0 || d < 0)) {
+			p.resize(0);
+			return p;
+		}
+		p[at++] = ((a & 0x3F) << 2) | ((b >> 4) & 0x03);
+		p[at++] = ((b & 0x0F) << 4) | ((c >> 2) & 0x0F);
+		p[at++] = ((c & 0x03) << 6) | ((d >> 0) & 0x3F);
+	}
+
+	if(c & 64)
+		p.resize(at - 2);
+	else if(d & 64)
+		p.resize(at - 1);
+
+	return p;
+}
+
+//!
+//! Encodes array \a a and returns the result as a string.
+QString Base64::arrayToString(const QByteArray &a)
+{
+	QByteArray b = encode(a);
+	QCString c;
+	c.resize(b.size()+1);
+	memcpy(c.data(), b.data(), b.size());
+	return QString::fromLatin1(c);
+}
+
+//!
+//! Decodes string \a s and returns the result as an array.
+QByteArray Base64::stringToArray(const QString &s)
+{
+	if(s.isEmpty())
+		return QByteArray();
+
+	// Unfold data
+	QString us(s);
+	us.remove('\n');
+
+	const char *c = us.latin1();
+	int len = strlen(c);
+	QByteArray b(len);
+	memcpy(b.data(), c, len);
+	QByteArray a = decode(b);
+	return a;
+}
+
+//!
+//! Encodes string \a s and returns the result as a string.
+QString Base64::encodeString(const QString &s)
+{
+	QCString c = s.utf8();
+	int len = c.length();
+	QByteArray b(len);
+	memcpy(b.data(), c.data(), len);
+	return arrayToString(b);
+}
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/util/base64.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/base64.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/base64.h	(revision 38)
@@ -0,0 +1,40 @@
+/*
+ * base64.h - Base64 converting functions
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_BASE64_H
+#define CS_BASE64_H
+
+#include<qstring.h>
+
+// CS_NAMESPACE_BEGIN
+
+class Base64
+{
+public:
+	static QByteArray encode(const QByteArray &);
+	static QByteArray decode(const QByteArray &);
+	static QString arrayToString(const QByteArray &);
+	static QByteArray stringToArray(const QString &);
+	static QString encodeString(const QString &);
+};
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/util/bconsole.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/bconsole.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/bconsole.cpp	(revision 38)
@@ -0,0 +1,130 @@
+/*
+ * bconsole.cpp - ByteStream wrapper for stdin/stdout
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"bconsole.h"
+
+#include<qsocketnotifier.h>
+#include<unistd.h>
+#include<fcntl.h>
+
+//----------------------------------------------------------------------------
+// BConsole
+//----------------------------------------------------------------------------
+class BConsole::Private
+{
+public:
+	Private() {}
+
+	QSocketNotifier *r, *w;
+	bool closing;
+	bool closed;
+};
+
+BConsole::BConsole(QObject *parent)
+:ByteStream(parent)
+{
+	d = new Private;
+	d->closing = false;
+	d->closed = false;
+
+	// set stdin/stdout to non-block
+	fcntl(0, F_SETFL, O_NONBLOCK);
+	fcntl(1, F_SETFL, O_NONBLOCK);
+
+	d->r = new QSocketNotifier(0, QSocketNotifier::Read);
+	connect(d->r, SIGNAL(activated(int)), SLOT(sn_read()));
+	d->w = 0;
+}
+
+BConsole::~BConsole()
+{
+	delete d->w;
+	delete d->r;
+	delete d;
+}
+
+bool BConsole::isOpen() const
+{
+	return (!d->closing && !d->closed);
+}
+
+void BConsole::close()
+{
+	if(d->closing || d->closed)
+		return;
+
+	if(bytesToWrite() > 0)
+		d->closing = true;
+	else {
+		::fclose(stdout);
+		d->closed = true;
+	}
+}
+
+void BConsole::sn_read()
+{
+	QByteArray a(1024);
+	int r = ::read(0, a.data(), a.size());
+	if(r < 0) {
+		error(ErrRead);
+	}
+	else if(r == 0) {
+		connectionClosed();
+	}
+	else {
+		a.resize(r);
+		appendRead(a);
+		readyRead();
+	}
+}
+
+void BConsole::sn_write()
+{
+	d->w->deleteLater();
+	d->w = 0;
+
+	if(bytesToWrite() > 0)
+		tryWrite();
+	if(bytesToWrite() == 0 && d->closing) {
+		d->closing = false;
+		d->closed = true;
+		delayedCloseFinished();
+	}
+}
+
+int BConsole::tryWrite()
+{
+	// try a section of the write buffer
+	QByteArray a = takeWrite(1024, false);
+
+	// write it
+	int r = ::write(1, a.data(), a.size());
+	if(r < 0) {
+		error(ErrWrite);
+		return -1;
+	}
+
+	d->w = new QSocketNotifier(1, QSocketNotifier::Write);
+	connect(d->w, SIGNAL(activated(int)), SLOT(sn_write()));
+
+	takeWrite(r);
+	bytesWritten(r);
+	return r;
+}
Index: psi/tags/0.10-os2-b1/cutestuff/util/bconsole.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/bconsole.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/bconsole.h	(revision 38)
@@ -0,0 +1,48 @@
+/*
+ * bconsole.h - ByteStream wrapper for stdin/stdout
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_BCONSOLE_H
+#define CS_BCONSOLE_H
+
+#include"bytestream.h"
+
+class BConsole : public ByteStream
+{
+	Q_OBJECT
+public:
+	BConsole(QObject *parent=0);
+	~BConsole();
+
+	bool isOpen() const;
+	void close();
+
+protected:
+	int tryWrite();
+
+private slots:
+	void sn_read();
+	void sn_write();
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/util/bytestream.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/bytestream.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/bytestream.cpp	(revision 38)
@@ -0,0 +1,267 @@
+/*
+ * bytestream.cpp - base class for bytestreams
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+//! \class ByteStream bytestream.h
+//! \brief Base class for "bytestreams"
+//! 
+//! This class provides a basic framework for a "bytestream", here defined
+//! as a bi-directional, asynchronous pipe of data.  It can be used to create
+//! several different kinds of bytestream-applications, such as a console or
+//! TCP connection, or something more abstract like a security layer or tunnel,
+//! all with the same interface.  The provided functions make creating such
+//! classes simpler.  ByteStream is a pure-virtual class, so you do not use it
+//! on its own, but instead through a subclass such as \a BSocket.
+//!
+//! The signals connectionClosed(), delayedCloseFinished(), readyRead(),
+//! bytesWritten(), and error() serve the exact same function as those from
+//! <A HREF="http://doc.trolltech.com/3.1/qsocket.html">QSocket</A>.
+//!
+//! The simplest way to create a ByteStream is to reimplement isOpen(), close(),
+//! and tryWrite().  Call appendRead() whenever you want to make data available for
+//! reading.  ByteStream will take care of the buffers with regards to the caller,
+//! and will call tryWrite() when the write buffer gains data.  It will be your
+//! job to call tryWrite() whenever it is acceptable to write more data to
+//! the underlying system.
+//!
+//! If you need more advanced control, reimplement read(), write(), bytesAvailable(),
+//! and/or bytesToWrite() as necessary.
+//!
+//! Use appendRead(), appendWrite(), takeRead(), and takeWrite() to modify the
+//! buffers.  If you have more advanced requirements, the buffers can be accessed
+//! directly with readBuf() and writeBuf().
+//!
+//! Also available are the static convenience functions ByteStream::appendArray()
+//! and ByteStream::takeArray(), which make dealing with byte queues very easy.
+
+class ByteStream::Private
+{
+public:
+	Private() {}
+
+	QByteArray readBuf, writeBuf;
+};
+
+//!
+//! Constructs a ByteStream object with parent \a parent.
+ByteStream::ByteStream(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+}
+
+//!
+//! Destroys the object and frees allocated resources.
+ByteStream::~ByteStream()
+{
+	delete d;
+}
+
+//!
+//! Returns TRUE if the stream is open, meaning that you can write to it.
+bool ByteStream::isOpen() const
+{
+	return false;
+}
+
+//!
+//! Closes the stream.  If there is data in the write buffer then it will be
+//! written before actually closing the stream.  Once all data has been written,
+//! the delayedCloseFinished() signal will be emitted.
+//! \sa delayedCloseFinished()
+void ByteStream::close()
+{
+}
+
+//!
+//! Writes array \a a to the stream.
+void ByteStream::write(const QByteArray &a)
+{
+	if(!isOpen())
+		return;
+
+	bool doWrite = bytesToWrite() == 0 ? true: false;
+	appendWrite(a);
+	if(doWrite)
+		tryWrite();
+}
+
+//!
+//! Reads bytes \a bytes of data from the stream and returns them as an array.  If \a bytes is 0, then
+//! \a read will return all available data.
+QByteArray ByteStream::read(int bytes)
+{
+	return takeRead(bytes);
+}
+
+//!
+//! Returns the number of bytes available for reading.
+int ByteStream::bytesAvailable() const
+{
+	return d->readBuf.size();
+}
+
+//!
+//! Returns the number of bytes that are waiting to be written.
+int ByteStream::bytesToWrite() const
+{
+	return d->writeBuf.size();
+}
+
+//!
+//! Writes string \a cs to the stream.
+void ByteStream::write(const QCString &cs)
+{
+	QByteArray block(cs.length());
+	memcpy(block.data(), cs.data(), block.size());
+	write(block);
+}
+
+//!
+//! Clears the read buffer.
+void ByteStream::clearReadBuffer()
+{
+	d->readBuf.resize(0);
+}
+
+//!
+//! Clears the write buffer.
+void ByteStream::clearWriteBuffer()
+{
+	d->writeBuf.resize(0);
+}
+
+//!
+//! Appends \a block to the end of the read buffer.
+void ByteStream::appendRead(const QByteArray &block)
+{
+	appendArray(&d->readBuf, block);
+}
+
+//!
+//! Appends \a block to the end of the write buffer.
+void ByteStream::appendWrite(const QByteArray &block)
+{
+	appendArray(&d->writeBuf, block);
+}
+
+//!
+//! Returns \a size bytes from the start of the read buffer.
+//! If \a size is 0, then all available data will be returned.
+//! If \a del is TRUE, then the bytes are also removed.
+QByteArray ByteStream::takeRead(int size, bool del)
+{
+	return takeArray(&d->readBuf, size, del);
+}
+
+//!
+//! Returns \a size bytes from the start of the write buffer.
+//! If \a size is 0, then all available data will be returned.
+//! If \a del is TRUE, then the bytes are also removed.
+QByteArray ByteStream::takeWrite(int size, bool del)
+{
+	return takeArray(&d->writeBuf, size, del);
+}
+
+//!
+//! Returns a reference to the read buffer.
+QByteArray & ByteStream::readBuf()
+{
+	return d->readBuf;
+}
+
+//!
+//! Returns a reference to the write buffer.
+QByteArray & ByteStream::writeBuf()
+{
+	return d->writeBuf;
+}
+
+//!
+//! Attempts to try and write some bytes from the write buffer, and returns the number
+//! successfully written or -1 on error.  The default implementation returns -1.
+int ByteStream::tryWrite()
+{
+	return -1;
+}
+
+//!
+//! Append array \a b to the end of the array pointed to by \a a.
+void ByteStream::appendArray(QByteArray *a, const QByteArray &b)
+{
+	int oldsize = a->size();
+	a->resize(oldsize + b.size());
+	memcpy(a->data() + oldsize, b.data(), b.size());
+}
+
+//!
+//! Returns \a size bytes from the start of the array pointed to by \a from.
+//! If \a size is 0, then all available data will be returned.
+//! If \a del is TRUE, then the bytes are also removed.
+QByteArray ByteStream::takeArray(QByteArray *from, int size, bool del)
+{
+	QByteArray a;
+	if(size == 0) {
+		a = from->copy();
+		if(del)
+			from->resize(0);
+	}
+	else {
+		if(size > (int)from->size())
+			size = from->size();
+		a.resize(size);
+		char *r = from->data();
+		memcpy(a.data(), r, size);
+		if(del) {
+			int newsize = from->size()-size;
+			memmove(r, r+size, newsize);
+			from->resize(newsize);
+		}
+	}
+	return a;
+}
+	void connectionClosed();
+	void delayedCloseFinished();
+	void readyRead();
+	void bytesWritten(int);
+	void error(int);
+
+//! \fn void ByteStream::connectionClosed()
+//! This signal is emitted when the remote end of the stream closes.
+
+//! \fn void ByteStream::delayedCloseFinished()
+//! This signal is emitted when all pending data has been written to the stream
+//! after an attempt to close.
+
+//! \fn void ByteStream::readyRead()
+//! This signal is emitted when data is available to be read.
+
+//! \fn void ByteStream::bytesWritten(int x);
+//! This signal is emitted when data has been successfully written to the stream.
+//! \a x is the number of bytes written.
+
+//! \fn void ByteStream::error(int code)
+//! This signal is emitted when an error occurs in the stream.  The reason for
+//! error is indicated by \a code.
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/util/bytestream.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/bytestream.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/bytestream.h	(revision 38)
@@ -0,0 +1,78 @@
+/*
+ * bytestream.h - base class for bytestreams
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_BYTESTREAM_H
+#define CS_BYTESTREAM_H
+
+#include<qobject.h>
+#include<qcstring.h>
+
+// CS_NAMESPACE_BEGIN
+
+// CS_EXPORT_BEGIN
+class ByteStream : public QObject
+{
+	Q_OBJECT
+public:
+	enum Error { ErrRead, ErrWrite, ErrCustom = 10 };
+	ByteStream(QObject *parent=0);
+	virtual ~ByteStream()=0;
+
+	virtual bool isOpen() const;
+	virtual void close();
+	virtual void write(const QByteArray &);
+	virtual QByteArray read(int bytes=0);
+	virtual int bytesAvailable() const;
+	virtual int bytesToWrite() const;
+
+	void write(const QCString &);
+
+	static void appendArray(QByteArray *a, const QByteArray &b);
+	static QByteArray takeArray(QByteArray *from, int size=0, bool del=true);
+
+signals:
+	void connectionClosed();
+	void delayedCloseFinished();
+	void readyRead();
+	void bytesWritten(int);
+	void error(int);
+
+protected:
+	void clearReadBuffer();
+	void clearWriteBuffer();
+	void appendRead(const QByteArray &);
+	void appendWrite(const QByteArray &);
+	QByteArray takeRead(int size=0, bool del=true);
+	QByteArray takeWrite(int size=0, bool del=true);
+	QByteArray & readBuf();
+	QByteArray & writeBuf();
+	virtual int tryWrite();
+
+private:
+//! \if _hide_doc_
+	class Private;
+	Private *d;
+//! \endif
+};
+// CS_EXPORT_END
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/util/cipher.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/cipher.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/cipher.cpp	(revision 38)
@@ -0,0 +1,357 @@
+/*
+ * cipher.cpp - Simple wrapper to 3DES,AES128/256 CBC ciphers
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"cipher.h"
+
+#include<openssl/evp.h>
+#include<openssl/rsa.h>
+#include"bytestream.h"
+#include"qrandom.h"
+
+static bool lib_encryptArray(const EVP_CIPHER *type, const QByteArray &buf, const QByteArray &key, const QByteArray &iv, bool pad, QByteArray *out)
+{
+	QByteArray result(buf.size()+type->block_size);
+	int len;
+	EVP_CIPHER_CTX c;
+
+	unsigned char *ivp = NULL;
+	if(!iv.isEmpty())
+		ivp = (unsigned char *)iv.data();
+	EVP_CIPHER_CTX_init(&c);
+	//EVP_CIPHER_CTX_set_padding(&c, pad ? 1: 0);
+	if(!EVP_EncryptInit_ex(&c, type, NULL, (unsigned char *)key.data(), ivp))
+		return false;
+	if(!EVP_EncryptUpdate(&c, (unsigned char *)result.data(), &len, (unsigned char *)buf.data(), buf.size()))
+		return false;
+	result.resize(len);
+	if(pad) {
+		QByteArray last(type->block_size);
+		if(!EVP_EncryptFinal_ex(&c, (unsigned char *)last.data(), &len))
+			return false;
+		last.resize(len);
+		ByteStream::appendArray(&result, last);
+	}
+
+	memset(&c, 0, sizeof(EVP_CIPHER_CTX));
+	*out = result;
+	return true;
+}
+
+static bool lib_decryptArray(const EVP_CIPHER *type, const QByteArray &buf, const QByteArray &key, const QByteArray &iv, bool pad, QByteArray *out)
+{
+	QByteArray result(buf.size()+type->block_size);
+	int len;
+	EVP_CIPHER_CTX c;
+
+	unsigned char *ivp = NULL;
+	if(!iv.isEmpty())
+		ivp = (unsigned char *)iv.data();
+	EVP_CIPHER_CTX_init(&c);
+	//EVP_CIPHER_CTX_set_padding(&c, pad ? 1: 0);
+	if(!EVP_DecryptInit_ex(&c, type, NULL, (unsigned char *)key.data(), ivp))
+		return false;
+	if(!pad) {
+		if(!EVP_EncryptUpdate(&c, (unsigned char *)result.data(), &len, (unsigned char *)buf.data(), buf.size()))
+			return false;
+	}
+	else {
+		if(!EVP_DecryptUpdate(&c, (unsigned char *)result.data(), &len, (unsigned char *)buf.data(), buf.size()))
+			return false;
+	}
+	result.resize(len);
+	if(pad) {
+		QByteArray last(type->block_size);
+		if(!EVP_DecryptFinal_ex(&c, (unsigned char *)last.data(), &len))
+			return false;
+		last.resize(len);
+		ByteStream::appendArray(&result, last);
+	}
+
+	memset(&c, 0, sizeof(EVP_CIPHER_CTX));
+	*out = result;
+	return true;
+}
+
+static bool lib_generateKeyIV(const EVP_CIPHER *type, const QByteArray &data, const QByteArray &salt, QByteArray *key, QByteArray *iv)
+{
+	QByteArray k, i;
+	unsigned char *kp = 0;
+	unsigned char *ip = 0;
+	if(key) {
+		k.resize(type->key_len);
+		kp = (unsigned char *)k.data();
+	}
+	if(iv) {
+		i.resize(type->iv_len);
+		ip = (unsigned char *)i.data();
+	}
+	if(!EVP_BytesToKey(type, EVP_sha1(), (unsigned char *)salt.data(), (unsigned char *)data.data(), data.size(), 1, kp, ip))
+		return false;
+	if(key)
+		*key = k;
+	if(iv)
+		*iv = i;
+	return true;
+}
+
+static const EVP_CIPHER * typeToCIPHER(Cipher::Type t)
+{
+	if(t == Cipher::TripleDES)
+		return EVP_des_ede3_cbc();
+	else if(t == Cipher::AES_128)
+		return EVP_aes_128_cbc();
+	else if(t == Cipher::AES_256)
+		return EVP_aes_256_cbc();
+	else
+		return 0;
+}
+
+Cipher::Key Cipher::generateKey(Type t)
+{
+	Key k;
+	const EVP_CIPHER *type = typeToCIPHER(t);
+	if(!type)
+		return k;
+	QByteArray out;
+	if(!lib_generateKeyIV(type, QRandom::randomArray(128), QRandom::randomArray(2), &out, 0))
+		return k;
+	k.setType(t);
+	k.setData(out);
+	return k;
+}
+
+QByteArray Cipher::generateIV(Type t)
+{
+	const EVP_CIPHER *type = typeToCIPHER(t);
+	if(!type)
+		return QByteArray();
+	QByteArray out;
+	if(!lib_generateKeyIV(type, QCString("Get this man an iv!"), QByteArray(), 0, &out))
+		return QByteArray();
+	return out;
+}
+
+int Cipher::ivSize(Type t)
+{
+	const EVP_CIPHER *type = typeToCIPHER(t);
+	if(!type)
+		return -1;
+	return type->iv_len;
+}
+
+QByteArray Cipher::encrypt(const QByteArray &buf, const Key &key, const QByteArray &iv, bool pad, bool *ok)
+{
+	if(ok)
+		*ok = false;
+	const EVP_CIPHER *type = typeToCIPHER(key.type());
+	if(!type)
+		return QByteArray();
+	QByteArray out;
+	if(!lib_encryptArray(type, buf, key.data(), iv, pad, &out))
+		return QByteArray();
+
+	if(ok)
+		*ok = true;
+	return out;
+}
+
+QByteArray Cipher::decrypt(const QByteArray &buf, const Key &key, const QByteArray &iv, bool pad, bool *ok)
+{
+	if(ok)
+		*ok = false;
+	const EVP_CIPHER *type = typeToCIPHER(key.type());
+	if(!type)
+		return QByteArray();
+	QByteArray out;
+	if(!lib_decryptArray(type, buf, key.data(), iv, pad, &out))
+		return QByteArray();
+
+	if(ok)
+		*ok = true;
+	return out;
+}
+
+
+class RSAKey::Private
+{
+public:
+	Private() {}
+
+	RSA *rsa;
+	int ref;
+};
+
+RSAKey::RSAKey()
+{
+	d = 0;
+}
+
+RSAKey::RSAKey(const RSAKey &from)
+{
+	d = 0;
+	*this = from;
+}
+
+RSAKey & RSAKey::operator=(const RSAKey &from)
+{
+	free();
+
+	if(from.d) {
+		d = from.d;
+		++d->ref;
+	}
+
+	return *this;
+}
+
+RSAKey::~RSAKey()
+{
+	free();
+}
+
+bool RSAKey::isNull() const
+{
+	return d ? false: true;
+}
+
+void * RSAKey::data() const
+{
+	if(d)
+		return (void *)d->rsa;
+	else
+		return 0;
+}
+
+void RSAKey::setData(void *p)
+{
+	free();
+
+	if(p) {
+		d = new Private;
+		d->ref = 1;
+		d->rsa = (RSA *)p;
+	}
+}
+
+void RSAKey::free()
+{
+	if(!d)
+		return;
+
+	--d->ref;
+	if(d->ref <= 0) {
+		RSA_free(d->rsa);
+		delete d;
+	}
+	d = 0;
+}
+
+RSAKey generateRSAKey()
+{
+	RSA *rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL);
+	RSAKey key;
+	if(rsa)
+		key.setData(rsa);
+	return key;
+}
+
+QByteArray encryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+	if(ok)
+		*ok = false;
+
+	int size = RSA_size((RSA *)key.data());
+	int flen = buf.size();
+	if(flen >= size - 11)
+		flen = size - 11;
+	QByteArray result(size);
+	unsigned char *from = (unsigned char *)buf.data();
+	unsigned char *to = (unsigned char *)result.data();
+	int r = RSA_public_encrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_PADDING);
+	if(r == -1)
+		return QByteArray();
+	result.resize(r);
+
+	if(ok)
+		*ok = true;
+	return result;
+}
+
+QByteArray decryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+	if(ok)
+		*ok = false;
+
+	int size = RSA_size((RSA *)key.data());
+	int flen = buf.size();
+	QByteArray result(size);
+	unsigned char *from = (unsigned char *)buf.data();
+	unsigned char *to = (unsigned char *)result.data();
+	int r = RSA_private_decrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_PADDING);
+	if(r == -1)
+		return QByteArray();
+	result.resize(r);
+
+	if(ok)
+		*ok = true;
+	return result;
+}
+
+QByteArray encryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+	if(ok)
+		*ok = false;
+
+	int size = RSA_size((RSA *)key.data());
+	int flen = buf.size();
+	if(flen >= size - 41)
+		flen = size - 41;
+	QByteArray result(size);
+	unsigned char *from = (unsigned char *)buf.data();
+	unsigned char *to = (unsigned char *)result.data();
+	int r = RSA_public_encrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_OAEP_PADDING);
+	if(r == -1)
+		return QByteArray();
+	result.resize(r);
+
+	if(ok)
+		*ok = true;
+	return result;
+}
+
+QByteArray decryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+	if(ok)
+		*ok = false;
+
+	int size = RSA_size((RSA *)key.data());
+	int flen = buf.size();
+	QByteArray result(size);
+	unsigned char *from = (unsigned char *)buf.data();
+	unsigned char *to = (unsigned char *)result.data();
+	int r = RSA_private_decrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_OAEP_PADDING);
+	if(r == -1)
+		return QByteArray();
+	result.resize(r);
+
+	if(ok)
+		*ok = true;
+	return result;
+}
Index: psi/tags/0.10-os2-b1/cutestuff/util/cipher.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/cipher.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/cipher.h	(revision 38)
@@ -0,0 +1,79 @@
+/*
+ * cipher.h - Simple wrapper to 3DES,AES128/256 CBC ciphers
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_CIPHER_H
+#define CS_CIPHER_H
+
+#include<qstring.h>
+#include<qcstring.h>
+
+namespace Cipher
+{
+	enum Type { None, TripleDES, AES_128, AES_256 };
+
+	class Key
+	{
+	public:
+		Key() { v_type = None; }
+
+		bool isValid() const { return (v_type == None ? false: true); }
+		void setType(Type x) { v_type = x; }
+		Type type() const { return v_type; }
+		void setData(const QByteArray &d) { v_data = d; }
+		const QByteArray & data() const { return v_data; }
+
+	private:
+		Type v_type;
+		QByteArray v_data;
+	};
+
+	Key generateKey(Type);
+	QByteArray generateIV(Type);
+	int ivSize(Type);
+	QByteArray encrypt(const QByteArray &, const Key &, const QByteArray &iv, bool pad, bool *ok=0);
+	QByteArray decrypt(const QByteArray &, const Key &, const QByteArray &iv, bool pad, bool *ok=0);
+}
+
+class RSAKey
+{
+public:
+	RSAKey();
+	RSAKey(const RSAKey &);
+	RSAKey & operator=(const RSAKey &);
+	~RSAKey();
+
+	bool isNull() const;
+	void *data() const;
+	void setData(void *);
+
+private:
+	class Private;
+	Private *d;
+
+	void free();
+};
+
+RSAKey generateRSAKey();
+QByteArray encryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+QByteArray decryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+QByteArray encryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+QByteArray decryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/util/qrandom.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/qrandom.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/qrandom.cpp	(revision 38)
@@ -0,0 +1,24 @@
+#include"qrandom.h"
+
+#include<stdlib.h>
+
+uchar QRandom::randomChar()
+{
+	return rand();
+}
+
+uint QRandom::randomInt()
+{
+	QByteArray a = randomArray(sizeof(uint));
+	uint x;
+	memcpy(&x, a.data(), a.size());
+	return x;
+}
+
+QByteArray QRandom::randomArray(uint size)
+{
+	QByteArray a(size);
+	for(uint n = 0; n < size; ++n)
+		a[n] = randomChar();
+	return a;
+}
Index: psi/tags/0.10-os2-b1/cutestuff/util/qrandom.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/qrandom.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/qrandom.h	(revision 38)
@@ -0,0 +1,14 @@
+#ifndef CS_QRANDOM_H
+#define CS_QRANDOM_H
+
+#include<qcstring.h>
+
+class QRandom
+{
+public:
+	static uchar randomChar();
+	static uint randomInt();
+	static QByteArray randomArray(uint size);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/util/safedelete.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/safedelete.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/safedelete.cpp	(revision 38)
@@ -0,0 +1,117 @@
+#include"safedelete.h"
+
+#include<qtimer.h>
+
+//----------------------------------------------------------------------------
+// SafeDelete
+//----------------------------------------------------------------------------
+SafeDelete::SafeDelete()
+{
+	lock = 0;
+}
+
+SafeDelete::~SafeDelete()
+{
+	if(lock)
+		lock->dying();
+}
+
+void SafeDelete::deleteLater(QObject *o)
+{
+	if(!lock)
+		deleteSingle(o);
+	else
+		list.append(o);
+}
+
+void SafeDelete::unlock()
+{
+	lock = 0;
+	deleteAll();
+}
+
+void SafeDelete::deleteAll()
+{
+	if(list.isEmpty())
+		return;
+
+	QObjectListIt it(list);
+	for(QObject *o; (o = it.current()); ++it)
+		deleteSingle(o);
+	list.clear();
+}
+
+void SafeDelete::deleteSingle(QObject *o)
+{
+#if QT_VERSION < 0x030000
+	// roll our own QObject::deleteLater()
+	SafeDeleteLater *sdl = SafeDeleteLater::ensureExists();
+	sdl->deleteItLater(o);
+#else
+	o->deleteLater();
+#endif
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLock
+//----------------------------------------------------------------------------
+SafeDeleteLock::SafeDeleteLock(SafeDelete *sd)
+{
+	own = false;
+	if(!sd->lock) {
+		_sd = sd;
+		_sd->lock = this;
+	}
+	else
+		_sd = 0;
+}
+
+SafeDeleteLock::~SafeDeleteLock()
+{
+	if(_sd) {
+		_sd->unlock();
+		if(own)
+			delete _sd;
+	}
+}
+
+void SafeDeleteLock::dying()
+{
+	_sd = new SafeDelete(*_sd);
+	own = true;
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLater
+//----------------------------------------------------------------------------
+SafeDeleteLater *SafeDeleteLater::self = 0;
+
+SafeDeleteLater *SafeDeleteLater::ensureExists()
+{
+	if(!self)
+		new SafeDeleteLater();
+	return self;
+}
+
+SafeDeleteLater::SafeDeleteLater()
+{
+	list.setAutoDelete(true);
+	self = this;
+	QTimer::singleShot(0, this, SLOT(explode()));
+}
+
+SafeDeleteLater::~SafeDeleteLater()
+{
+	list.clear();
+	self = 0;
+}
+
+void SafeDeleteLater::deleteItLater(QObject *o)
+{
+	list.append(o);
+}
+
+void SafeDeleteLater::explode()
+{
+	delete this;
+}
Index: psi/tags/0.10-os2-b1/cutestuff/util/safedelete.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/safedelete.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/safedelete.h	(revision 38)
@@ -0,0 +1,60 @@
+#ifndef SAFEDELETE_H
+#define SAFEDELETE_H
+
+#include<qobject.h>
+#include<qobjectlist.h>
+
+class SafeDelete;
+class SafeDeleteLock
+{
+public:
+	SafeDeleteLock(SafeDelete *sd);
+	~SafeDeleteLock();
+
+private:
+	SafeDelete *_sd;
+	bool own;
+	friend class SafeDelete;
+	void dying();
+};
+
+class SafeDelete
+{
+public:
+	SafeDelete();
+	~SafeDelete();
+
+	void deleteLater(QObject *o);
+
+	// same as QObject::deleteLater()
+	static void deleteSingle(QObject *o);
+
+private:
+	QObjectList list;
+	void deleteAll();
+
+	friend class SafeDeleteLock;
+	SafeDeleteLock *lock;
+	void unlock();
+};
+
+class SafeDeleteLater : public QObject
+{
+	Q_OBJECT
+public:
+	static SafeDeleteLater *ensureExists();
+	void deleteItLater(QObject *o);
+
+private slots:
+	void explode();
+
+private:
+	SafeDeleteLater();
+	~SafeDeleteLater();
+
+	QObjectList list;
+	friend class SafeDelete;
+	static SafeDeleteLater *self;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/util/sha1.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/sha1.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/sha1.cpp	(revision 38)
@@ -0,0 +1,196 @@
+/*
+ * sha1.cpp - Secure Hash Algorithm 1
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"sha1.h"
+
+// CS_NAMESPACE_BEGIN
+
+/****************************************************************************
+  SHA1 - from a public domain implementation by Steve Reid (steve@edmweb.com)
+****************************************************************************/
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15]^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+SHA1::SHA1()
+{
+	int wordSize;
+
+	qSysInfo(&wordSize, &bigEndian);
+}
+
+unsigned long SHA1::blk0(Q_UINT32 i)
+{
+	if(bigEndian)
+		return block->l[i];
+	else
+		return (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) | (rol(block->l[i],8)&0x00FF00FF));
+}
+
+// Hash a single 512-bit block. This is the core of the algorithm.
+void SHA1::transform(Q_UINT32 state[5], unsigned char buffer[64])
+{
+	Q_UINT32 a, b, c, d, e;
+
+	block = (CHAR64LONG16*)buffer;
+
+	// Copy context->state[] to working vars
+	a = state[0];
+	b = state[1];
+	c = state[2];
+	d = state[3];
+	e = state[4];
+
+	// 4 rounds of 20 operations each. Loop unrolled.
+	R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+	R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+	R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+	R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+	R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+	R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+	R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+	R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+	R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+	R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+	R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+	R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+	R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+	R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+	R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+	R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+	R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+	R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+	R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+	R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+	// Add the working vars back into context.state[]
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+	state[4] += e;
+
+	// Wipe variables
+	a = b = c = d = e = 0;
+}
+
+// SHA1Init - Initialize new context
+void SHA1::init(SHA1_CONTEXT* context)
+{
+	// SHA1 initialization constants
+	context->state[0] = 0x67452301;
+	context->state[1] = 0xEFCDAB89;
+	context->state[2] = 0x98BADCFE;
+	context->state[3] = 0x10325476;
+	context->state[4] = 0xC3D2E1F0;
+	context->count[0] = context->count[1] = 0;
+}
+
+// Run your data through this
+void SHA1::update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len)
+{
+	Q_UINT32 i, j;
+
+	j = (context->count[0] >> 3) & 63;
+	if((context->count[0] += len << 3) < (len << 3))
+		context->count[1]++;
+
+	context->count[1] += (len >> 29);
+
+	if((j + len) > 63) {
+		memcpy(&context->buffer[j], data, (i = 64-j));
+		transform(context->state, context->buffer);
+		for ( ; i + 63 < len; i += 64) {
+			transform(context->state, &data[i]);
+		}
+		j = 0;
+	}
+	else i = 0;
+		memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+// Add padding and return the message digest
+void SHA1::final(unsigned char digest[20], SHA1_CONTEXT* context)
+{
+	Q_UINT32 i, j;
+	unsigned char finalcount[8];
+
+	for (i = 0; i < 8; i++) {
+		finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+		>> ((3-(i & 3)) * 8) ) & 255);  // Endian independent
+	}
+	update(context, (unsigned char *)"\200", 1);
+	while ((context->count[0] & 504) != 448) {
+		update(context, (unsigned char *)"\0", 1);
+	}
+	update(context, finalcount, 8);  // Should cause a transform()
+	for (i = 0; i < 20; i++) {
+		digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+	}
+
+	// Wipe variables
+	i = j = 0;
+	memset(context->buffer, 0, 64);
+	memset(context->state, 0, 20);
+	memset(context->count, 0, 8);
+	memset(&finalcount, 0, 8);
+}
+
+QByteArray SHA1::hash(const QByteArray &a)
+{
+	SHA1_CONTEXT context;
+	QByteArray b(20);
+
+	SHA1 s;
+	s.init(&context);
+	s.update(&context, (unsigned char *)a.data(), (unsigned int)a.size());
+	s.final((unsigned char *)b.data(), &context);
+	return b;
+}
+
+QByteArray SHA1::hashString(const QCString &cs)
+{
+	QByteArray a(cs.length());
+	memcpy(a.data(), cs.data(), a.size());
+	return SHA1::hash(a);
+}
+
+QString SHA1::digest(const QString &in)
+{
+	QByteArray a = SHA1::hashString(in.utf8());
+	QString out;
+	for(int n = 0; n < (int)a.size(); ++n) {
+		QString str;
+		str.sprintf("%02x", (uchar)a[n]);
+		out.append(str);
+	}
+
+	return out;
+}
+
+// CS_NAMESPACE_END
Index: psi/tags/0.10-os2-b1/cutestuff/util/sha1.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/util/sha1.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/util/sha1.h	(revision 38)
@@ -0,0 +1,63 @@
+/*
+ * sha1.h - Secure Hash Algorithm 1
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_SHA1_H
+#define CS_SHA1_H
+
+#include<qstring.h>
+
+// CS_NAMESPACE_BEGIN
+
+class SHA1
+{
+public:
+	static QByteArray hash(const QByteArray &);
+	static QByteArray hashString(const QCString &);
+	static QString digest(const QString &);
+
+private:
+	SHA1();
+
+	struct SHA1_CONTEXT
+	{
+		Q_UINT32 state[5];
+		Q_UINT32 count[2];
+		unsigned char buffer[64];
+	};
+
+	typedef union {
+		unsigned char c[64];
+		Q_UINT32 l[16];
+	} CHAR64LONG16;
+
+	void transform(Q_UINT32 state[5], unsigned char buffer[64]);
+	void init(SHA1_CONTEXT* context);
+	void update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len);
+	void final(unsigned char digest[20], SHA1_CONTEXT* context);
+
+	unsigned long blk0(Q_UINT32 i);
+	bool bigEndian;
+
+	CHAR64LONG16* block;
+};
+
+// CS_NAMESPACE_END
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/xmlsec/keyops.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/xmlsec/keyops.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/xmlsec/keyops.cpp	(revision 38)
@@ -0,0 +1,264 @@
+#include"keyops.h"
+
+#include"../util/bytestream.h"
+#include"../util/base64.h"
+#include"../util/sha1.h"
+
+bool sym_encrypt(const QByteArray &data, const Cipher::Key &key, const QByteArray &iv, QString *out)
+{
+	QByteArray encData = iv.copy();
+	bool ok;
+	QByteArray a = Cipher::encrypt(data, key, iv, true, &ok);
+	if(!ok)
+		return false;
+	ByteStream::appendArray(&encData, a);
+
+	*out = Base64::arrayToString(encData);
+	return true;
+}
+
+bool sym_decrypt(const QString &str, const Cipher::Key &key, QByteArray *out)
+{
+	QByteArray data = Base64::stringToArray(str);
+	int r = Cipher::ivSize(key.type());
+	if(r == -1)
+		return false;
+	if((int)data.size() < r)
+		return false;
+	QByteArray iv = ByteStream::takeArray(&data, r);
+	bool ok;
+	QByteArray result = Cipher::decrypt(data, key, iv, true, &ok);
+	if(!ok)
+		return false;
+
+	*out = result;
+	return true;
+}
+
+static QByteArray calcCMS(const QByteArray &key)
+{
+	QByteArray a = SHA1::hash(key);
+	a.resize(8);
+	return a;
+}
+
+unsigned char sym_3des_fixed_iv[8] = { 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05 };
+unsigned char sym_aes_fixed_val[8] = { 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6 };
+
+static QByteArray cat64(const QByteArray &a1, const QByteArray &a2)
+{
+	QByteArray out = a1.copy();
+	ByteStream::appendArray(&out, a2);
+	return out;
+}
+
+static QByteArray xor64(const QByteArray &a1, const QByteArray &a2)
+{
+	QByteArray out(a1.size());
+	for(uint n = 0; n < a1.size(); ++n)
+		out[n] = a1[n] ^ a2[n];
+	return out;
+}
+
+static QByteArray msb64(const QByteArray &a)
+{
+	QByteArray out(8);
+	for(uint n = 0; n < 8; ++n)
+		out[n] = a[n];
+	return out;
+}
+
+static QByteArray lsb64(const QByteArray &a)
+{
+	QByteArray out(8);
+	for(uint n = 0; n < 8; ++n)
+		out[n] = a[n+8];
+	return out;
+}
+
+static QByteArray get64(const QByteArray &from, uint x)
+{
+	QByteArray out(8);
+	int base = (x-1) * 8;
+	for(uint n = 0; n < 8; ++n)
+		out[n] = from[base+n];
+	return out;
+}
+
+static void set64(QByteArray *from, uint x, const QByteArray &a)
+{
+	int base = (x-1) * 8;
+	for(uint n = 0; n < 8; ++n)
+		(*from)[base+n] = a[n];
+}
+
+static QByteArray uintTo64(uint x)
+{
+	QByteArray out(8);
+	out[0] = 0x00;
+	out[1] = 0x00;
+	out[2] = 0x00;
+	out[3] = 0x00;
+	out[4] = (x >> 24) & 0xff;
+	out[5] = (x >> 16) & 0xff;
+	out[6] = (x >>  8) & 0xff;
+	out[7] = (x >>  0) & 0xff;
+	return out;
+}
+
+bool sym_keywrap(const QByteArray &data, const Cipher::Key &key, QString *out)
+{
+	int x = key.type();
+	if(x == Cipher::TripleDES) {
+		QByteArray cks = calcCMS(data);
+		QByteArray wkcks = data.copy();
+		ByteStream::appendArray(&wkcks, cks);
+		QByteArray iv = Cipher::generateIV(key.type());
+		bool ok;
+		QByteArray temp1 = Cipher::encrypt(wkcks, key, iv, false, &ok);
+		if(!ok)
+			return false;
+		QByteArray temp2 = iv;
+		ByteStream::appendArray(&temp2, temp1);
+		QByteArray temp3(temp2.size());
+		int n2 = (int)temp2.size()-1;
+		for(int n = 0; n < (int)temp2.size(); ++n)
+			temp3[n2--] = temp2[n];
+		memcpy(iv.data(), sym_3des_fixed_iv, 8);
+		QByteArray final = Cipher::encrypt(temp3, key, iv, false, &ok);
+		if(!ok)
+			return false;
+
+		*out = Base64::arrayToString(final);
+		return true;
+	}
+	else if(x == Cipher::AES_128 || x == Cipher::AES_256) {
+		QByteArray c;
+		QByteArray work = data.copy();
+		int n = work.size() / 8;
+		if(work.size() % 8)
+			return false;
+		if(n < 1)
+			return false;
+
+		if(n == 1) {
+			QByteArray val(8);
+			memcpy(val.data(), sym_aes_fixed_val, 8);
+			bool ok;
+			c = Cipher::encrypt(cat64(val, work), key, QByteArray(), false, &ok);
+			if(!ok)
+				return false;
+		}
+		else {
+			QByteArray r = work;
+			QByteArray a(8);
+			memcpy(a.data(), sym_aes_fixed_val, 8);
+
+			for(int j = 0; j <= 5; ++j) {
+				for(int i = 1; i <= n; ++i) {
+					uint t = i + (j * n);
+					bool ok;
+					QByteArray b = Cipher::encrypt(cat64(a, get64(r, i)), key, QByteArray(), false, &ok);
+					if(!ok)
+						return false;
+					a = xor64(uintTo64(t), msb64(b));
+					set64(&r, i, lsb64(b));
+				}
+			}
+
+			c = a;
+			ByteStream::appendArray(&c, r);
+		}
+
+		*out = Base64::arrayToString(c);
+		return true;
+	}
+
+	return false;
+}
+
+bool sym_keyunwrap(const QString &str, const Cipher::Key &key, QByteArray *out)
+{
+	int x = key.type();
+	QByteArray data = Base64::stringToArray(str);
+	if(x == Cipher::TripleDES) {
+		QByteArray iv(8);
+		memcpy(iv.data(), sym_3des_fixed_iv, 8);
+		bool ok;
+		QByteArray temp3 = Cipher::decrypt(data, key, iv, false, &ok);
+		if(!ok)
+			return false;
+		QByteArray temp2(temp3.size());
+		int n2 = (int)temp3.size()-1;
+		for(int n = 0; n < (int)temp3.size(); ++n)
+			temp2[n2--] = temp3[n];
+		if((int)temp2.size() < 8)
+			return false;
+		iv = ByteStream::takeArray(&temp2, 8);
+		QByteArray temp1 = temp2;
+		QByteArray wkcks = Cipher::decrypt(temp1, key, iv, false, &ok);
+		if(!ok)
+			return false;
+		if((int)wkcks.size() < 8)
+			return false;
+		QByteArray wk = ByteStream::takeArray(&wkcks, wkcks.size() - 8);
+		QByteArray cks = wkcks;
+		QByteArray t = calcCMS(wk);
+
+		if(cks.size() != t.size())
+			return false;
+		for(int n = 0; n < (int)t.size(); ++n) {
+			if(t[n] != cks[n])
+				return false;
+		}
+		*out = wk;
+		return true;
+	}
+	else if(x == Cipher::AES_128 || x == Cipher::AES_256) {
+		QByteArray p;
+		QByteArray work = data.copy();
+		if(work.size() % 8)
+			return false;
+		int n = (work.size() / 8) - 1;
+		if(n < 1)
+			return false;
+
+		QByteArray a;
+		if(n == 1) {
+			bool ok;
+			QByteArray b = Cipher::decrypt(work, key, QByteArray(), false, &ok);
+			if(!ok)
+				return false;
+			ByteStream::appendArray(&p, lsb64(b));
+			a = msb64(b);
+		}
+		else {
+			a = ByteStream::takeArray(&work, 8);
+			QByteArray r = work;
+			for(int j = 5; j >= 0; --j) {
+				for(int i = n; i >= 1; --i) {
+					uint t = i + (j * n);
+					QByteArray ta = xor64(uintTo64(t), a);
+					bool ok;
+					QByteArray b = Cipher::decrypt(cat64(ta, get64(r, i)), key, QByteArray(), false, &ok);
+					if(!ok)
+						return false;
+					a = msb64(b);
+					set64(&r, i, lsb64(b));
+				}
+			}
+
+			p = r;
+		}
+
+		for(int i = 0; i < 8; ++i) {
+			if((unsigned char)a[i] != sym_aes_fixed_val[i])
+				return false;
+		}
+
+		*out = p;
+		return true;
+	}
+
+	return false;
+}
Index: psi/tags/0.10-os2-b1/cutestuff/xmlsec/keyops.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/xmlsec/keyops.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/xmlsec/keyops.h	(revision 38)
@@ -0,0 +1,20 @@
+#ifndef CS_KEYOPS_H
+#define CS_KEYOPS_H
+
+#include<qstring.h>
+#include<qcstring.h>
+#include"../util/cipher.h"
+
+// sym_encrypt - encrypt 'data' and return a base64 string of the result
+bool sym_encrypt(const QByteArray &data, const Cipher::Key &key, const QByteArray &iv, QString *out);
+
+// sym_decrypt - take a base64 string, decode and decrypt it, return the result
+bool sym_decrypt(const QString &str, const Cipher::Key &key, QByteArray *out);
+
+// sym_keywrap - encrypt key 'data' and return a base64 string of the result
+bool sym_keywrap(const QByteArray &data, const Cipher::Key &key, QString *out);
+
+// sym_keyunwrap - take a base64 string, decode and decrypt it, return the result (key data)
+bool sym_keyunwrap(const QString &str, const Cipher::Key &key, QByteArray *out);
+
+#endif
Index: psi/tags/0.10-os2-b1/cutestuff/xmlsec/xmlenc.cpp
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/xmlsec/xmlenc.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/xmlsec/xmlenc.cpp	(revision 38)
@@ -0,0 +1,762 @@
+/*
+ * xmlenc.cpp - XML Encryption
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"xmlenc.h"
+
+#include"../util/bytestream.h"
+#include"../util/base64.h"
+#include"keyops.h"
+
+static QByteArray nodeToArray(const QDomNode &e)
+{
+	QString out;
+	QTextStream ts(&out, IO_WriteOnly);
+	e.save(ts, 1);
+	QCString xmlToEnc = out.utf8();
+	int len = xmlToEnc.length();
+	QByteArray b(len);
+	memcpy(b.data(), xmlToEnc.data(), len);
+	return b;
+}
+
+static QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+	if(found)
+		*found = false;
+
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+		if(i.tagName() == name) {
+			if(found)
+				*found = true;
+			return i;
+		}
+	}
+
+	QDomElement tmp;
+	return tmp;
+}
+
+
+using namespace XmlEnc;
+
+
+//----------------------------------------------------------------------------
+// KeyInfo
+//----------------------------------------------------------------------------
+KeyInfo::KeyInfo()
+{
+}
+
+KeyInfo::~KeyInfo()
+{
+}
+
+bool KeyInfo::isEmpty() const
+{
+	if(v_name.isEmpty() && v_value.isEmpty() && v_rmethods.isEmpty() && v_key.isNull())
+		return true;
+	return false;
+}
+
+QString KeyInfo::name() const
+{
+	return v_name;
+}
+
+QByteArray KeyInfo::value () const
+{
+	return v_value;
+}
+
+QStringList KeyInfo::retrievalMethods() const
+{
+	return v_rmethods;
+}
+
+QDomElement KeyInfo::encryptedKey() const
+{
+	return v_key;
+}
+
+void KeyInfo::setName(const QString &s)
+{
+	v_name = s;
+}
+
+void KeyInfo::setValue(const QByteArray &d)
+{
+	v_value = d;
+}
+
+void KeyInfo::setRetrievalMethods(const QStringList &s)
+{
+	v_rmethods = s;
+}
+
+void KeyInfo::attachEncryptedKey(const QDomElement &e)
+{
+	v_key = e;
+}
+
+QDomElement KeyInfo::toXml(QDomDocument *doc) const
+{
+	QDomElement e = doc->createElement("ds:KeyInfo");
+	e.setAttribute("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#");
+
+	if(!v_value.isEmpty()) {
+		QDomElement v = doc->createElement("ds:KeyValue");
+		v.appendChild(doc->createTextNode(Base64::arrayToString(v_value)));
+		e.appendChild(v);
+	}
+
+	if(!v_name.isEmpty()) {
+		QDomElement n = doc->createElement("ds:KeyName");
+		n.appendChild(doc->createTextNode(v_name));
+		e.appendChild(n);
+	}
+
+	for(QStringList::ConstIterator it = v_rmethods.begin(); it != v_rmethods.end(); ++it) {
+		QDomElement r = doc->createElement("ds:RetrievalMethod");
+		r.setAttribute("Type", "http://www.w3.org/2001/04/xmlenc#EncryptedKey");
+		r.setAttribute("URI", *it);
+		e.appendChild(r);
+	}
+
+	if(!v_key.isNull())
+		e.appendChild(v_key);
+
+	return e;
+}
+
+bool KeyInfo::fromXml(const QDomElement &e)
+{
+	if(e.tagName() != "ds:KeyInfo" || e.attribute("xmlns:ds") != "http://www.w3.org/2000/09/xmldsig#")
+		return false;
+
+	QByteArray val;
+	QString n;
+	QStringList rml;
+	QDomElement ek;
+
+	bool found;
+	QDomElement i;
+
+	i = findSubTag(e, "ds:KeyValue", &found);
+	if(found)
+		val = Base64::stringToArray(i.text());
+	i = findSubTag(e, "ds:KeyName", &found);
+	if(found)
+		n = i.text();
+	QDomNodeList l = e.elementsByTagName("ds:RetrievalMethod");
+	for(int n = 0; n < (int)l.count(); ++n) {
+		QDomElement r = l.item(n).toElement();
+		if(r.attribute("Type") == "http://www.w3.org/2001/04/xmlenc#EncryptedKey")
+			rml += r.attribute("URI");
+	}
+	i = findSubTag(e, "EncryptedKey", &found);
+	if(found)
+		ek = i;
+
+	// all good
+	v_value = val;
+	v_name = n;
+	v_rmethods = rml;
+	v_key = ek;
+
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// EncryptionProperty
+//----------------------------------------------------------------------------
+EncryptionProperty::EncryptionProperty(const QString &target, const QString &id)
+{
+	v_target = target;
+	v_id = id;
+}
+
+QString EncryptionProperty::target() const
+{
+	return v_target;
+}
+
+QString EncryptionProperty::id() const
+{
+	return v_id;
+}
+
+QString EncryptionProperty::property(const QString &var) const
+{
+	QStringList::ConstIterator it = vars.begin();
+	QStringList::ConstIterator it2 = vals.begin();
+	while(it != vars.end()) {
+		if((*it) == var)
+			return *it2;
+		++it;
+		++it2;
+	}
+	return "";
+}
+
+void EncryptionProperty::setTarget(const QString &target)
+{
+	v_target = target;
+}
+
+void EncryptionProperty::setId(const QString &id)
+{
+	v_id = id;
+}
+
+void EncryptionProperty::setProperty(const QString &var, const QString &val)
+{
+	if(var == "Target" || var == "Id")
+		return;
+
+	// see if we have it already
+	QStringList::Iterator it = vars.begin();
+	QStringList::Iterator it2 = vals.begin();
+	while(it != vars.end()) {
+		if((*it) == var) {
+			*it2 = val;
+			return;
+		}
+		++it;
+		++it2;
+	}
+
+	vars += var;
+	vals += val;
+}
+
+QDomElement EncryptionProperty::toXml(QDomDocument *doc) const
+{
+	QDomElement e = doc->createElement("EncryptionProperty");
+	if(!v_target.isEmpty())
+		e.setAttribute("Target", v_target);
+	if(!v_id.isEmpty())
+		e.setAttribute("Id", v_id);
+
+	QStringList::ConstIterator it = vars.begin();
+	QStringList::ConstIterator it2 = vals.begin();
+	while(it != vars.end()) {
+		e.setAttribute(*it, *it2);
+		++it;
+		++it2;
+	}
+
+	return e;
+}
+
+bool EncryptionProperty::fromXml(QDomElement &e)
+{
+	if(e.tagName() != "EncryptionProperty")
+		return false;
+
+	v_target = e.attribute("Target");
+	v_id = e.attribute("Id");
+	vars.clear();
+	vals.clear();
+	QDomNamedNodeMap map = e.attributes();
+	for(int n = 0; n < (int)map.count(); ++n) {
+		QDomAttr a = map.item(n).toAttr();
+		QString n = a.name();
+		if(n == "Target" || n == "Id")
+			continue;
+		vars += n;
+		vals += a.value();
+	}
+
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// EncryptionProperties
+//----------------------------------------------------------------------------
+EncryptionProperties::EncryptionProperties(const QString &s)
+:QValueList<EncryptionProperty>()
+{
+	v_id = s;
+}
+
+QString EncryptionProperties::id() const
+{
+	return v_id;
+}
+
+void EncryptionProperties::setId(const QString &s)
+{
+	v_id = s;
+}
+
+QDomElement EncryptionProperties::toXml(QDomDocument *doc) const
+{
+	QDomElement e = doc->createElement("EncryptionProperties");
+	if(!v_id.isEmpty())
+		e.setAttribute("Id", v_id);
+	for(QValueList<EncryptionProperty>::ConstIterator it = begin(); it != end(); ++it) {
+		const EncryptionProperty &p = *it;
+		e.appendChild(p.toXml(doc));
+	}
+	return e;
+}
+
+bool EncryptionProperties::fromXml(QDomElement &e)
+{
+	if(e.tagName() != "EncryptionProperties")
+		return false;
+
+	clear();
+	v_id = e.attribute("Id");
+	QDomNodeList l = e.elementsByTagName("EncryptionProperty");
+	for(int n = 0; n < (int)l.count(); ++n) {
+		QDomElement r = l.item(n).toElement();
+		EncryptionProperty p;
+		if(!p.fromXml(r))
+			return false;
+		append(p);
+	}
+
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// Encrypted
+//----------------------------------------------------------------------------
+Encrypted::Encrypted()
+{
+	baseNS = "http://www.w3.org/2001/04/xmlenc#";
+	clear();
+}
+
+Encrypted::~Encrypted()
+{
+	clear();
+}
+
+void Encrypted::clear()
+{
+	v_method = None;
+	v_id = "";
+	v_dataType = Arbitrary;
+	v_type = Data;
+	v_mimeType = "";
+	v_keyInfo = KeyInfo();
+
+	v_cval = "";
+	v_cref = Reference();
+	v_carrykeyname = "";
+}
+
+Method Encrypted::cipherTypeToMethod(Cipher::Type t) const
+{
+	if(t == Cipher::TripleDES)
+		return TripleDES;
+	else if(t == Cipher::AES_128)
+		return AES_128;
+	else if(t == Cipher::AES_256)
+		return AES_256;
+	else
+		return None;
+}
+
+void Encrypted::setDataReference(const Reference &cref, Method m)
+{
+	v_method = m;
+	v_cref = cref;
+}
+
+bool Encrypted::encryptData(const QByteArray &data, const Cipher::Key &key)
+{
+	QByteArray iv = Cipher::generateIV(key.type());
+	if(!sym_encrypt(data, key, iv, &v_cval))
+		return false;
+	v_dataType = Arbitrary;
+	v_method = cipherTypeToMethod(key.type());
+	return true;
+}
+
+bool Encrypted::encryptElement(const QDomElement &data, const Cipher::Key &key)
+{
+	QByteArray iv = Cipher::generateIV(key.type());
+	if(!sym_encrypt(nodeToArray(data), key, iv, &v_cval))
+		return false;
+	v_type = Data;
+	v_dataType = Element;
+	v_method = cipherTypeToMethod(key.type());
+	return true;
+}
+
+bool Encrypted::encryptContent(const QDomElement &data, const Cipher::Key &key)
+{
+	// convert children to raw data
+	QByteArray a;
+	for(QDomNode n = data.firstChild(); !n.isNull(); n = n.nextSibling())
+		ByteStream::appendArray(&a, nodeToArray(n));
+
+	QByteArray iv = Cipher::generateIV(key.type());
+	if(!sym_encrypt(a, key, iv, &v_cval))
+		return false;
+	v_type = Data;
+	v_dataType = Content;
+	v_method = cipherTypeToMethod(key.type());
+	return true;
+}
+
+bool Encrypted::encryptKey(const Cipher::Key &data, const Cipher::Key &key)
+{
+	if(!sym_keywrap(data.data(), key, &v_cval))
+		return false;
+	v_type = Key;
+	v_dataType = Arbitrary;
+	v_method = cipherTypeToMethod(key.type());
+	return true;
+}
+
+bool Encrypted::encryptKey(const Cipher::Key &data, const RSAKey &key)
+{
+	bool useOAEP;
+	if(data.type() == Cipher::TripleDES)
+		useOAEP = false;
+	else
+		useOAEP = true;
+
+	bool ok;
+	QByteArray result;
+	if(useOAEP)
+		result = encryptRSA2(data.data(), key, &ok);
+	else
+		result = encryptRSA(data.data(), key, &ok);
+	if(!ok)
+		return false;
+
+	v_type = Key;
+	v_dataType = Arbitrary;
+	v_method = useOAEP ? RSA_OAEP : RSA_1_5;
+	v_cval = Base64::arrayToString(result);
+	return true;
+}
+
+QByteArray Encrypted::decryptData(const Cipher::Key &key) const
+{
+	QByteArray result;
+	if(!sym_decrypt(v_cval, key, &result))
+		return QByteArray();
+	return result;
+}
+
+QDomElement Encrypted::decryptElement(QDomDocument *doc, const Cipher::Key &key) const
+{
+	QByteArray result;
+	if(!sym_decrypt(v_cval, key, &result))
+		return QDomElement();
+
+	QDomDocument d;
+	if(!d.setContent(result))
+		return QDomElement();
+	return doc->importNode(d.documentElement(), true).toElement();
+}
+
+QDomNodeList Encrypted::decryptContent(QDomDocument *doc, const Cipher::Key &key) const
+{
+	QByteArray result;
+	if(!sym_decrypt(v_cval, key, &result))
+		return QDomNodeList();
+
+	// nul-terminate
+	result.resize(result.size()+1);
+	result[result.size()-1] = 0;
+
+	QCString cs = "<dummy>";
+	cs += (char *)result.data();
+	cs += "</dummy>";
+
+	QDomDocument d;
+	if(!d.setContent(cs))
+		return QDomNodeList();
+	QDomElement e = d.documentElement().firstChild().toElement();
+	if(e.isNull() || e.tagName() != "dummy")
+		return QDomNodeList();
+
+	return doc->importNode(e, true).childNodes();
+}
+
+QByteArray Encrypted::decryptKey(const Cipher::Key &key) const
+{
+	QByteArray result;
+	if(!sym_keyunwrap(v_cval, key, &result))
+		return QByteArray();
+
+	return result;
+}
+
+QByteArray Encrypted::decryptKey(const RSAKey &key) const
+{
+	QByteArray data = Base64::stringToArray(v_cval);
+	QByteArray result;
+	bool ok;
+	if(v_method == RSA_OAEP)
+		result = decryptRSA2(data, key, &ok);
+	else
+		result = decryptRSA(data, key, &ok);
+	if(!ok)
+		return QByteArray();
+
+	return result;
+}
+
+QDomElement Encrypted::toXml(QDomDocument *doc) const
+{
+	QString baseNS = "http://www.w3.org/2001/04/xmlenc#";
+	QDomElement enc;
+
+	// base xml
+	if(v_type == Data)
+		enc = doc->createElement("EncryptedData");
+	else
+		enc = doc->createElement("EncryptedKey");
+	enc.setAttribute("xmlns", baseNS);
+	if(!v_id.isEmpty())
+		enc.setAttribute("Id", v_id);
+	if(v_type == Data)
+		enc.setAttribute("Type", baseNS + "Element");
+
+	// method
+	QDomElement meth = doc->createElement("EncryptionMethod");
+	meth.setAttribute("Algorithm", methodToAlgorithm(v_method, v_type));
+	if(v_method == RSA_OAEP) {
+		// add digest method
+		QDomElement dm = doc->createElement("ds:DigestMethod");
+		dm.setAttribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
+		meth.appendChild(dm);
+	}
+	enc.appendChild(meth);
+
+	// keyinfo
+	if(!v_keyInfo.isEmpty())
+		enc.appendChild(v_keyInfo.toXml(doc));
+
+	// cipherdata
+	QDomElement cd = doc->createElement("CipherData");
+	if(!v_cref.uri().isEmpty()) {
+		QDomElement cr = doc->createElement("CipherReference");
+		cr.setAttribute("URI", v_cref.uri());
+		if(!v_cref.transforms().isNull())
+			cr.appendChild(v_cref.transforms());
+		cd.appendChild(cr);
+	}
+	else {
+		QDomElement cv = doc->createElement("CipherValue");
+		cv.appendChild(doc->createTextNode(v_cval));
+		cd.appendChild(cv);
+	}
+	enc.appendChild(cd);
+
+	// encryption properties
+	if(!v_props.isEmpty())
+		enc.appendChild(v_props.toXml(doc));
+
+	// reference list
+	if(!v_reflist.isEmpty()) {
+		QDomElement e = doc->createElement("ReferenceList");
+		for(QValueList<Reference>::ConstIterator it = v_reflist.begin(); it != v_reflist.end(); ++it) {
+			const Reference &r = *it;
+			QDomElement df = doc->createElement("DataReference");
+			df.setAttribute("URI", r.uri());
+			if(!r.transforms().isNull())
+				df.appendChild(r.transforms());
+			e.appendChild(df);
+		}
+		enc.appendChild(e);
+	}
+
+	// carry key name
+	if(!v_carrykeyname.isEmpty()) {
+		QDomElement e = doc->createElement("CarriedKeyName");
+		e.appendChild(doc->createTextNode(v_carrykeyname));
+		enc.appendChild(e);
+	}
+
+	return enc;
+}
+
+bool Encrypted::fromXml(const QDomElement &e)
+{
+	QString baseNS = "http://www.w3.org/2001/04/xmlenc#";
+	if(e.attribute("xmlns") != baseNS)
+		return false;
+	DataType dt = Arbitrary;
+	if(e.hasAttribute("Type")) {
+		QString str = e.attribute("Type");
+		int n = str.find('#');
+		if(n == -1)
+			return false;
+		++n;
+		if(str.mid(0, n) != baseNS)
+			return false;
+		str = str.mid(n);
+		if(str == "Element")
+			dt = Element;
+		else if(str == "Content")
+			dt = Content;
+	}
+
+	Type t;
+	if(e.tagName() == "EncryptedData")
+		t = Data;
+	else if(e.tagName() == "EncryptedKey")
+		t = Key;
+	else
+		return false;
+
+	// method
+	Method m = None;
+	bool found;
+	QDomElement i = findSubTag(e, "EncryptionMethod", &found);
+	if(!found)
+		return false;
+	QString str = i.attribute("Algorithm");
+	int n = str.find('#');
+	if(n == -1)
+		return false;
+	++n;
+	if(str.mid(0, n) != baseNS)
+		return false;
+	str = str.mid(n);
+
+	m = algorithmToMethod(str);
+	if(m == None)
+		return false;
+
+	// keyinfo
+	KeyInfo ki;
+	i = findSubTag(e, "ds:KeyInfo", &found);
+	if(found) {
+		if(!ki.fromXml(i))
+			return false;
+	}
+
+	// cipherdata
+	QString cval;
+	Reference cref;
+	i = findSubTag(e, "CipherData", &found);
+	if(!found)
+		return false;
+	i = findSubTag(i, "CipherValue", &found);
+	if(found)
+		cval = i.text();
+	else {
+		i = findSubTag(i, "CipherReference", &found);
+		if(!found)
+			return false;
+		cref.setURI(i.attribute("URI"));
+		QDomElement tf = i.firstChild().toElement();
+		if(!tf.isNull())
+			cref.setTransforms(tf);
+	}
+
+	// encryption properties
+	EncryptionProperties props;
+	i = findSubTag(e, "EncryptionProperties", &found);
+	if(found) {
+		if(!props.fromXml(i))
+			return false;
+	}
+
+	// reference list
+	ReferenceList rl;
+	i = findSubTag(e, "ReferenceList", &found);
+	if(found) {
+		QDomNodeList l = e.elementsByTagName("DataReference");
+		for(int n = 0; n < (int)l.count(); ++n) {
+			QDomElement dr = l.item(n).toElement();
+			Reference r;
+			r.setURI(dr.attribute("URI"));
+			QDomElement tf = i.firstChild().toElement();
+			if(!tf.isNull())
+				r.setTransforms(tf);
+			rl += r;
+		}
+	}
+
+	// carry key name
+	QString carrykey;
+	i = findSubTag(e, "CarriedKeyName", &found);
+	if(found)
+		carrykey = i.text();
+
+	// looks good, let's take it
+	clear();
+	v_type = t;
+	v_dataType = dt;
+	v_method = m;
+	v_cval = cval;
+	v_cref = cref;
+	v_keyInfo = ki;
+	v_props = props;
+	v_reflist = rl;
+	v_carrykeyname = carrykey;
+
+	return true;
+}
+
+QString Encrypted::methodToAlgorithm(Method m, Type t) const
+{
+	QString s;
+	if(m == TripleDES)
+		s = (t == Key ? "kw-tripledes": "tripledes-cbc");
+	else if(m == AES_128)
+		s = (t == Key ? "kw-aes128": "aes128-cbc");
+	else if(m == AES_256)
+		s = (t == Key ? "kw-aes256": "aes256-cbc");
+	else if(m == RSA_1_5)
+		s = "rsa-1_5";
+	else if(m == RSA_OAEP)
+		s = "rsa-oaep-mgf1p";
+	else
+		return "";
+
+	return (baseNS + s);
+}
+
+Method Encrypted::algorithmToMethod(const QString &s) const
+{
+	Method m;
+	if(s == "tripledes-cbc" || s == "kw-tripledes")
+		m = TripleDES;
+	else if(s == "aes128-cbc" || s == "kw-aes128")
+		m = AES_128;
+	else if(s == "aes256-cbc" || s == "kw-aes256")
+		m = AES_256;
+	else if(s == "rsa-1_5")
+		m = RSA_1_5;
+	else if(s == "rsa-oaep-mgf1p")
+		m = RSA_OAEP;
+	else
+		return None;
+
+	return m;
+}
Index: psi/tags/0.10-os2-b1/cutestuff/xmlsec/xmlenc.h
===================================================================
--- psi/tags/0.10-os2-b1/cutestuff/xmlsec/xmlenc.h	(revision 38)
+++ psi/tags/0.10-os2-b1/cutestuff/xmlsec/xmlenc.h	(revision 38)
@@ -0,0 +1,180 @@
+/*
+ * xmlenc.h - XML Encryption
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_XMLENC_H
+#define CS_XMLENC_H
+
+#include<qstring.h>
+#include<qcstring.h>
+#include<qstringlist.h>
+#include<qdom.h>
+#include<qvaluelist.h>
+#include"../util/cipher.h"
+
+namespace XmlEnc
+{
+	enum Method { None, TripleDES, AES_128, AES_256, RSA_1_5, RSA_OAEP };
+	enum DataType { Arbitrary, Element, Content };
+
+	class KeyInfo
+	{
+	public:
+		KeyInfo();
+		~KeyInfo();
+
+		bool isEmpty() const;
+		QString name() const;
+		QByteArray value () const;
+		QStringList retrievalMethods() const;
+		QDomElement encryptedKey() const;
+		void setName(const QString &);
+		void setValue(const QByteArray &);
+		void setRetrievalMethods(const QStringList &);
+		void attachEncryptedKey(const QDomElement &);
+
+		QDomElement toXml(QDomDocument *) const;
+		bool fromXml(const QDomElement &);
+
+	private:
+		QString v_name;
+		QByteArray v_value;
+		QStringList v_rmethods;
+		QDomElement v_key;
+	};
+
+	class Reference
+	{
+	public:
+		Reference() {}
+
+		QString uri() const { return v_uri; }
+		QDomElement transforms() const { return v_trans; }
+		void setURI(const QString &s) { v_uri = s; }
+		void setTransforms(const QDomElement &e) { v_trans = e; }
+
+	private:
+		QString v_uri;
+		QDomElement v_trans;
+	};
+	typedef QValueList<Reference> ReferenceList;
+
+	class EncryptionProperty
+	{
+	public:
+		EncryptionProperty(const QString &target="", const QString &id="");
+
+		QString target() const;
+		QString id() const;
+		QString property(const QString &var) const;
+		void setTarget(const QString &);
+		void setId(const QString &);
+		void setProperty(const QString &var, const QString &val);
+
+		QDomElement toXml(QDomDocument *) const;
+		bool fromXml(QDomElement &);
+
+	private:
+		QString v_target, v_id;
+		QStringList vars, vals;
+	};
+
+	class EncryptionProperties : public QValueList<EncryptionProperty>
+	{
+	public:
+		EncryptionProperties(const QString &s="");
+
+		QString id() const;
+		void setId(const QString &);
+
+		QDomElement toXml(QDomDocument *) const;
+		bool fromXml(QDomElement &);
+
+	private:
+		QString v_id;
+	};
+
+	class Encrypted
+	{
+	public:
+		enum Type { Key, Data };
+		Encrypted();
+		~Encrypted();
+
+		void clear();
+
+		Method method() const { return v_method; }
+		QString id() const { return v_id; }
+		DataType dataType() const { return v_dataType; }
+		Type type() const { return v_type; }
+		QString mimeType() const { return v_mimeType; }
+		const KeyInfo & keyInfo() const { return v_keyInfo; }
+		bool isReference() const { return (!v_cref.uri().isEmpty()); }
+		const Reference & dataReference() const { return v_cref; }
+		QString carriedKeyName() const { return v_carrykeyname; }
+		const ReferenceList & referenceList() const { return v_reflist; }
+		const EncryptionProperties & encryptionProperties() const { return v_props; }
+
+		void setId(const QString &id) { v_id = id; }
+		void setDataType(DataType t) { v_dataType = t; }
+		void setType(Type t) { v_type = t; }
+		void setMimeType(const QString &mime) { v_mimeType = mime; }
+		void setKeyInfo(const KeyInfo &info) { v_keyInfo = info; }
+		void setDataReference(const Reference &cref, Method m);
+		void setCarriedKeyName(const QString &s) { v_carrykeyname = s; }
+		void setReferenceList(const ReferenceList &rl) { v_reflist = rl; }
+		void setEncryptionProperties(const EncryptionProperties &p) { v_props = p; }
+
+		bool encryptData(const QByteArray &data, const Cipher::Key &key);
+		bool encryptElement(const QDomElement &data, const Cipher::Key &key);
+		bool encryptContent(const QDomElement &data, const Cipher::Key &key);
+		bool encryptKey(const Cipher::Key &data, const Cipher::Key &key);
+		bool encryptKey(const Cipher::Key &data, const RSAKey &key);
+
+		QByteArray decryptData(const Cipher::Key &key) const;
+		QDomElement decryptElement(QDomDocument *, const Cipher::Key &key) const;
+		QDomNodeList decryptContent(QDomDocument *, const Cipher::Key &key) const;
+		QByteArray decryptKey(const Cipher::Key &key) const;
+		QByteArray decryptKey(const RSAKey &key) const;
+
+		QDomElement toXml(QDomDocument *) const;
+		bool fromXml(const QDomElement &);
+
+	private:
+		Method v_method;
+		QString v_id;
+		DataType v_dataType;
+		Type v_type;
+		QString v_mimeType;
+		KeyInfo v_keyInfo;
+
+		QString v_cval;
+		Reference v_cref;
+		QString v_carrykeyname;
+		ReferenceList v_reflist;
+		EncryptionProperties v_props;
+
+		QString baseNS;
+		Method cipherTypeToMethod(Cipher::Type) const;
+		QString methodToAlgorithm(Method, Type) const;
+		Method algorithmToMethod(const QString &) const;
+	};
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/iconsets/emoticons/README
===================================================================
--- psi/tags/0.10-os2-b1/iconsets/emoticons/README	(revision 38)
+++ psi/tags/0.10-os2-b1/iconsets/emoticons/README	(revision 38)
@@ -0,0 +1,10 @@
+Emoticons iconset README
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+For general information about creation of iconsets, see psi/libpsi/iconset/ICONSET-HOWTO.
+This file contains only required icon names and some details about them.
+
+In is not necessary to give emoticons unique names. The exception is a special 'default'
+iconset.
+
+emoticons/smile	- Used in the profile creation dialog
Index: psi/tags/0.10-os2-b1/iconsets/emoticons/default/icondef.xml
===================================================================
--- psi/tags/0.10-os2-b1/iconsets/emoticons/default/icondef.xml	(revision 38)
+++ psi/tags/0.10-os2-b1/iconsets/emoticons/default/icondef.xml	(revision 38)
@@ -0,0 +1,280 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<icondef>
+	<meta>
+		<name>Stellar (default)</name>
+		<version>1.0</version>
+		<description>Default Psi 0.9.3 iconset</description>
+		<creation>2003-07-08</creation>
+		<home>http://psi.affinix.com</home>
+		<author email='jmkim@uci.edu'>Jason Kim</author>
+		<author jid='mblsha@jabber.ru' email='mblsha@users.sourceforge.net' www='http://maz.sf.net'>Michail Pishchagin (icondef.xml)</author>
+	</meta>
+
+	<!-- Normal icons -->
+	<icon>
+		<text>:-)</text>
+		<text>:)</text>
+
+		<object mime='image/png'>smile.png</object>
+	</icon>
+
+	<icon>
+		<text>;-)</text>
+		<text>;)</text>
+
+		<object mime='image/png'>wink.png</object>
+	</icon>
+
+	<icon>
+		<text>:-P</text>
+		<text>:-p</text>
+		<text>:P</text>
+		<text>:p</text>
+
+		<object mime='image/png'>tongue.png</object>
+	</icon>
+
+	<icon>
+		<text>:-D</text>
+		<text>:-d</text>
+		<text>:D</text>
+		<text>:d</text>
+		<text>:-></text>
+		<text>:></text>
+
+		<object mime='image/png'>biggrin.png</object>
+	</icon>
+
+	<icon>
+		<text>:-(</text>
+		<text>:(</text>
+
+		<object mime='image/png'>unhappy.png</object>
+	</icon>
+
+	<icon>
+		<text>:'-(</text>
+		<text>:'(</text>
+		<text>;-(</text>
+		<text>;(</text>
+
+		<object mime='image/png'>cry.png</object>
+	</icon>
+
+	<icon>
+		<text>:-O</text>
+		<text>:-o</text>
+		<text>:O</text>
+		<text>:o</text>
+
+		<object mime='image/png'>oh.png</object>
+	</icon>
+
+	<icon>
+		<text>:-@</text>
+		<text>:@</text>
+
+		<object mime='image/png'>angry.png</object>
+	</icon>
+
+	<icon>
+		<text>:-$</text>
+		<text>:$</text>
+
+		<object mime='image/png'>blush.png</object>
+	</icon>
+
+	<icon>
+		<text>:-|</text>
+		<text>:|</text>
+
+		<object mime='image/png'>stare.png</object>
+	</icon>
+
+	<icon>
+		<text>:-S</text>
+		<text>:-s</text>
+		<text>:S</text>
+		<text>:s</text>
+
+		<object mime='image/png'>frowning.png</object>
+	</icon>
+
+	<icon>
+		<text>B-)</text>
+		<text>B)</text>
+		<text>(H)</text>
+		<text>(h)</text>
+
+		<object mime='image/png'>coolglasses.png</object>
+	</icon>
+
+	<icon>
+		<text>:-[</text>
+		<text>:[</text>
+
+		<object mime='image/png'>bat.png</object>
+	</icon>
+
+	<!-- MSN icons -->
+	<icon>
+		<text>(L)</text>
+		<text>(l)</text>
+
+		<object mime='image/png'>heart.png</object>
+	</icon>
+
+	<icon>
+		<text>(U)</text>
+		<text>(u)</text>
+
+		<object mime='image/png'>brheart.png</object>
+	</icon>
+
+	<icon>
+		<text>(Y)</text>
+		<text>(y)</text>
+
+		<object mime='image/png'>yes.png</object>
+	</icon>
+
+	<icon>
+		<text>(N)</text>
+		<text>(n)</text>
+
+		<object mime='image/png'>no.png</object>
+	</icon>
+
+	<icon>
+		<text>(Z)</text>
+		<text>(z)</text>
+
+		<object mime='image/png'>boy.png</object>
+	</icon>
+
+	<icon>
+		<text>(X)</text>
+		<text>(x)</text>
+
+		<object mime='image/png'>girl.png</object>
+	</icon>
+
+	<icon>
+		<text>(@)</text>
+
+		<object mime='image/png'>pussy.png</object>
+	</icon>
+
+	<icon>
+		<text>(})</text>
+
+		<object mime='image/png'>hugleft.png</object>
+	</icon>
+
+	<icon>
+		<text>({)</text>
+
+		<object mime='image/png'>hugright.png</object>
+	</icon>
+
+	<icon>
+		<text>(6)</text>
+
+		<object mime='image/png'>devil.png</object>
+	</icon>
+
+	<icon>
+		<text>(R)</text>
+		<text>(r)</text>
+
+		<object mime='image/png'>rainbow.png</object>
+	</icon>
+
+	<icon>
+		<text>(W)</text>
+		<text>(w)</text>
+
+		<object mime='image/png'>brflower.png</object>
+	</icon>
+
+	<icon>
+		<text>(F)</text>
+		<text>(f)</text>
+
+		<object mime='image/png'>flower.png</object>
+	</icon>
+
+	<icon>
+		<text>(P)</text>
+		<text>(p)</text>
+
+		<object mime='image/png'>photo.png</object>
+	</icon>
+
+	<icon>
+		<text>(T)</text>
+		<text>(t)</text>
+
+		<object mime='image/png'>phone.png</object>
+	</icon>
+
+	<icon>
+		<text>(*)</text>
+
+		<object mime='image/png'>star.png</object>
+	</icon>
+
+	<icon>
+		<text>(8)</text>
+
+		<object mime='image/png'>music.png</object>
+	</icon>
+
+	<icon>
+		<text>(I)</text>
+		<text>(i)</text>
+
+		<object mime='image/png'>lamp.png</object>
+	</icon>
+
+	<icon>
+		<text>(B)</text>
+		<text>(b)</text>
+
+		<object mime='image/png'>beer.png</object>
+	</icon>
+
+	<icon>
+		<text>(D)</text>
+		<text>(d)</text>
+
+		<object mime='image/png'>drink.png</object>
+	</icon>
+
+	<icon>
+		<text>(C)</text>
+		<text>(c)</text>
+
+		<object mime='image/png'>coffee.png</object>
+	</icon>
+
+	<icon>
+		<text>(%)</text>
+
+		<object mime='image/png'>cuffs.png</object>
+	</icon>
+
+	<icon>
+		<text>(E)</text>
+		<text>(e)</text>
+
+		<object mime='image/png'>mail.png</object>
+	</icon>
+
+	<icon>
+		<text>(K)</text>
+		<text>(k)</text>
+
+		<object mime='image/png'>kiss.png</object>
+	</icon>
+</icondef>
Index: psi/tags/0.10-os2-b1/iconsets/roster/README
===================================================================
--- psi/tags/0.10-os2-b1/iconsets/roster/README	(revision 38)
+++ psi/tags/0.10-os2-b1/iconsets/roster/README	(revision 38)
@@ -0,0 +1,36 @@
+Roster iconset README
+~~~~~~~~~~~~~~~~~~~~~
+
+For general information about creation of iconsets, see psi/libpsi/iconset/ICONSET-HOWTO.
+This file contains only required icon names and some details about them.
+
+Read a little note about animations in the README for the system iconsets.
+
+Note: how to add service iconsets: modify src/psiiconset.cpp and add new service RegExp,
+add the default iconset name to profiles.cpp, and add your iconset to common.cpp's
+category2icon() (and don't forget to add entry to lv_isServices in ui_options.ui).
+
+status/online	 - Online
+status/offline	 - Offline
+status/away	 - Away
+status/xa	 - eXtended Away
+status/dnd	 - Do Not Disturb
+status/invisible - Invisible
+status/chat	 - Free For Chat
+
+// special statuses
+status/ask	 - We are asking for authorization
+status/noauth	 - No authorization
+status/error	 - Error status (contact is unavailable due to some reason)
+
+// special roster icons
+psi/chat	 - Received a chat message
+psi/message	 - Received a message
+psi/headline	 - Received a headline message
+psi/system	 - Received a system event
+psi/connect      - Connection to server in progress...
+
+// these icons make sense, only when used in default roster iconset
+psi/groupClosed	 - Closed group in roster
+psi/groupEmpty	 - Empty group in roster
+psi/groupOpen	 - Open group in roster
Index: psi/tags/0.10-os2-b1/iconsets/roster/default/icondef.xml
===================================================================
--- psi/tags/0.10-os2-b1/iconsets/roster/default/icondef.xml	(revision 38)
+++ psi/tags/0.10-os2-b1/iconsets/roster/default/icondef.xml	(revision 38)
@@ -0,0 +1,115 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<icondef>
+	<meta>
+		<name>Stellar (default)</name>
+		<version>1.0</version>
+		<description>Default Psi 0.9.1 iconset</description>
+		<creation>2003-07-08</creation>
+		<home>http://psi.affinix.com</home>
+		<author email='jmkim@uci.edu'>Jason Kim</author>
+		<author jid='mblsha@jabber.ru' email='mblsha@users.sourceforge.net' www='http://maz.sf.net'>Michail Pishchagin (icondef.xml)</author>
+	</meta>
+
+	<!-- Normal status icons -->
+	<icon>
+		<x xmlns='name'>status/online</x>
+		<object mime='image/png'>online.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>status/offline</x>
+		<object mime='image/png'>offline.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>status/away</x>
+		<object mime='image/png'>away.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>status/xa</x>
+		<object mime='image/png'>xa.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>status/dnd</x>
+		<object mime='image/png'>dnd.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>status/invisible</x>
+		<object mime='image/png'>invisible.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>status/chat</x>
+		<object mime='image/png'>ffc.png</object>
+	</icon>
+
+	<!-- Special statuses -->
+	<icon>
+		<x xmlns='name'>status/ask</x>
+		<object mime='image/png'>ask.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>status/noauth</x>
+		<object mime='image/png'>noauth.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>status/error</x>
+		<object mime='image/png'>perr.png</object>
+	</icon>
+
+	<!-- Special icons -->
+	<icon>
+		<x xmlns='name'>psi/chat</x>
+		<x xmlns='type'>animation</x>
+		<object mime='image/png'>chat.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/message</x>
+		<x xmlns='type'>animation</x>
+		<object mime='image/png'>message.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/headline</x>
+		<object mime='image/png'>headline.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/file</x>
+		<x xmlns='type'>animation</x>
+		<object mime='image/png'>file.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/system</x>
+		<x xmlns='type'>animation</x>
+		<object mime='image/png'>system.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/connect</x>
+		<x xmlns='type'>animation</x>
+		<object mime='image/png'>connect.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/groupClosed</x>
+		<object mime='image/png'>groupclose.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/groupEmpty</x>
+		<object mime='image/png'>groupempty.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/groupOpen</x>
+		<object mime='image/png'>groupopen.png</object>
+	</icon>
+</icondef>
Index: psi/tags/0.10-os2-b1/iconsets/system/README
===================================================================
--- psi/tags/0.10-os2-b1/iconsets/system/README	(revision 38)
+++ psi/tags/0.10-os2-b1/iconsets/system/README	(revision 38)
@@ -0,0 +1,79 @@
+System iconset README
+~~~~~~~~~~~~~~~~~~~~~
+
+For general information about creation of iconsets, see psi/libpsi/iconset/ICONSET-HOWTO.
+This file contains only required icon names and some details about them.
+
+System iconset, is a special iconset, that contains icons that are used in labels,
+buttons and other widgets in Psi.
+
+And a little note about animations: Icons could contain animations, and system Psi
+animations are a bit special. Their first frame is used only for creation of static
+images (such as in popup menus, or if Psi's alert style is set to 'blink'). This first
+frame is not used in animation (since it's stripped out of them).
+
+PS: If you would use Psi's .png-style animation don't forget to add <x xmlns='type'>animation</x>
+tag to the icon.
+
+psi/account	- Used in Modify accounts dialog
+psi/addContact	- Add contact
+psi/arrowUp	- Arrow up
+psi/arrowDown	- Arrow down
+psi/arrowLeft	- Arrow left
+psi/arrowRight	- Arrow right
+psi/profile	- Change account
+psi/clearChat	- Clear chat
+psi/groupChat	- GroupChat
+psi/help	- Help menu
+psi/history	- Chat history
+psi/vCard	- vCard/User info
+psi/info	- Info icon
+psi/jabber	- Jabber icon
+psi/options	- Options
+psi/toolbars	- Configure Toolbars
+psi/pgp		- PGP
+psi/keySingle	- Single (public) PGP key
+psi/keyBad	- Bad PGP key
+psi/keyDouble	- Double (public + private) PGP key
+psi/keyUnknown	- Unknown PGP key
+psi/playSounds	- Play sounds
+psi/psiMain	- Psi logo, used in roster and tray context menu
+psi/quit	- Quit
+psi/register	- Register service
+psi/reload	- Reload button
+psi/stop	- Stop button
+psi/remove	- Remove/Delete item
+psi/search	- Search service
+psi/sendMessage	- New plain message/Send message
+psi/cryptoYes	- Encryption enabled
+psi/cryptoNo	- Encryption disabled
+psi/time	- Time
+psi/www		- URL icon
+psi/email	- Email icon
+psi/xml		- XML icon
+psi/psi16	- 16x16 Psi icon
+psi/psi32	- 32x32 Psi icon
+psi/psi48	- 48x48 Psi icon
+psi/psiMac	- 128x128 Psi MacOS X icon
+psi/psiLogo	- Psi logo. Used in the first dialog. NOTE: the leftmost and the
+		  rightmost vertical lines of that icon are used for stretching
+psi/smile	- Used in the profile creation dialog
+psi/ok		- Check icon
+psi/cancel	- Cross icon
+psi/done	- Done icon -- TODO
+psi/close	- Close icon -- TODO: add to default iconset
+psi/apply	- Apply icon -- TODO: add
+psi/edit/clear	- Clear icon
+psi/edit/copy	- Copy icon
+psi/edit/cut	- Cut icon
+psi/edit/delete	- Delete icon
+psi/edit/paste	- Paste icon
+psi/edit	- Edit icon
+psi/edit/trash	- Trash icon
+psi/tip		- Tip of the Day
+psi/browse	- Browse file
+psi/play	- Play sound
+psi/eye		- Used to determine Hidden group visibility
+psi/upload	- Upload icon
+psi/download	- Download icon
+psi/filemanager - Transfer Manager
Index: psi/tags/0.10-os2-b1/iconsets/system/default/icondef.xml
===================================================================
--- psi/tags/0.10-os2-b1/iconsets/system/default/icondef.xml	(revision 38)
+++ psi/tags/0.10-os2-b1/iconsets/system/default/icondef.xml	(revision 38)
@@ -0,0 +1,430 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<icondef>
+	<meta>
+		<name>Crystal - System (default)</name>
+		<version>1.0</version>
+		<description>Crystal System Iconset</description>
+		<creation>2005-11-23</creation>
+		<home>http://psi.affinix.com</home>
+		<author email='jmkim@uci.edu'>Jason Kim (Logo and misc. icons)</author>
+		<author www='http://www.everaldo.com'>Everaldo Coelho (Crystal icons)</author>
+	</meta>
+
+	<icon>
+		<x xmlns='name'>psi/appearance</x>
+		<object mime='image/png'>appearance.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/account</x>
+		<object mime='image/png'>account.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/addContact</x>
+		<object mime='image/png'>add.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/arrowUp</x>
+		<object mime='image/png'>arrow_up.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/arrowDown</x>
+		<object mime='image/png'>arrow_down.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/arrowLeft</x>
+		<object mime='image/png'>arrow_left.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/arrowRight</x>
+		<object mime='image/png'>arrow_right.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/profile</x>
+		<object mime='image/png'>changeacc.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/clearChat</x>
+		<object mime='image/png'>chatclear.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/compact</x>
+		<object mime='image/png'>ok.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/disco</x>
+		<object mime='image/png'>disco.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/events</x>
+		<object mime='image/png'>events.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/groupChat</x>
+		<object mime='image/png'>groupchat.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/help</x>
+		<object mime='image/png'>help.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/history</x>
+		<object mime='image/png'>history.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/vCard</x>
+		<object mime='image/png'>vcard.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/jabber</x>
+		<object mime='image/png'>jabber.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/options</x>
+		<object mime='image/png'>options.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/toolbars</x>
+		<object mime='image/png'>configure_toolbars.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/pgp</x>
+		<object mime='image/png'>pgp.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/gpg-yes</x>
+		<object mime='image/png'>key.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/gpg-no</x>
+		<object mime='image/png'>key.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/keyBad</x>
+		<object mime='image/png'>key_bad.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/keyUnknown</x>
+		<object mime='image/png'>key_unknown.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/playSounds</x>
+		<object mime='image/png'>play_sounds.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/psiMain</x>
+		<object mime='image/png'>psimain.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/quit</x>
+		<object mime='image/png'>quit.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/register</x>
+		<object mime='image/png'>register.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/reload</x>
+		<object mime='image/png'>reload.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/stop</x>
+		<object mime='image/png'>stop.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/remove</x>
+		<object mime='image/png'>remove.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/search</x>
+		<object mime='image/png'>search.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/status</x>
+		<object mime='image/png'>status.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/sendMessage</x>
+		<object mime='image/png'>send.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/start-chat</x>
+		<object mime='image/png'>start-chat.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/cryptoYes</x>
+		<object mime='image/png'>ssl_yes.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/cryptoNo</x>
+		<object mime='image/png'>ssl_no.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/time</x>
+		<object mime='image/png'>time.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/www</x>
+		<object mime='image/png'>url.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/xml</x>
+		<object mime='image/png'>xml.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/psi16</x>
+		<object mime='image/png'>icon_16.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/psi32</x>
+		<object mime='image/png'>icon_32.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/psi48</x>
+		<object mime='image/png'>icon_48.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/psiMac</x>
+		<object mime='image/png'>icon_mac.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/psiLogo</x>
+		<object mime='image/png'>psilogo.png</object>
+	</icon>	
+
+	<icon>
+		<x xmlns='name'>psi/smile</x>
+		<object mime='image/png'>smile.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/email</x>
+		<object mime='image/png'>send.png</object> <!-- TODO: grab small KMail icon -->
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/ok</x>
+		<object mime='image/png'>ok.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/done</x>
+		<object mime='image/png'>close.png</object> <!-- TODO: change icon -->
+	</icon>
+
+  <icon>
+		<x xmlns='name'>psi/closetab</x>
+		<object mime='image/png'>closetab.png</object> 
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/cancel</x>
+		<object mime='image/png'>cancel.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/close</x>
+		<object mime='image/png'>close.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/apply</x>
+		<object mime='image/png'>ok.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/info</x>
+		<object mime='image/png'>info.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/tip</x>
+		<object mime='image/png'>tip.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/browse</x>
+		<object mime='image/png'>browse.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/play</x>
+		<object mime='image/png'>play.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/eye</x>
+		<object mime='image/png'>eye_blue.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/upload</x>
+		<object mime='image/png'>upload.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/download</x>
+		<object mime='image/png'>download.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>psi/filemanager</x>
+		<object mime='image/png'>filemanager.png</object>
+	</icon>
+
+	
+	<!-- FIXME -->
+	<icon>
+		<x xmlns='name'>psi/roster</x>
+		<object mime='image/png'>url.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/advanced</x>
+		<object mime='image/png'>advanced.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/show_self</x>
+		<object mime='image/png'>self.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/show_offline</x>
+		<object mime='image/png'>show_offline.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/show_away</x>
+		<object mime='image/png'>show_away.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>psi/show_hidden</x>
+		<object mime='image/png'>show_hidden.png</object>
+	</icon>
+
+	<!-- Avatars -->
+	<!--<icon>
+		<x xmlns='name'>client/unknown</x>
+		<object mime='image/png'>clients/unknown.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>client/gaim</x>
+		<object mime='image/png'>clients/gaim.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/gabber</x>
+		<object mime='image/png'>clients/gabber.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/gossip</x>
+		<object mime='image/png'>clients/gossip.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/exodus</x>
+		<object mime='image/png'>clients/exodus.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/pandion</x>
+		<object mime='image/png'>clients/pandion.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/nitro</x>
+		<object mime='image/png'>clients/nitro.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/kopete</x>
+		<object mime='image/png'>clients/kopete.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/tkabber</x>
+		<object mime='image/png'>clients/tkabber.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/jajc</x>
+		<object mime='image/png'>clients/jajc.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/trillian</x>
+		<object mime='image/png'>clients/trillian.png</object>
+	</icon>
+
+	<icon>
+		<x xmlns='name'>client/msn</x>
+		<object mime='image/png'>clients/msn.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/icq</x>
+		<object mime='image/png'>clients/icq.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/yahoo</x>
+		<object mime='image/png'>clients/yahoo.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/aim</x>
+		<object mime='image/png'>clients/aim.png</object>
+	</icon>
+	
+	<icon>
+		<x xmlns='name'>client/gadu</x>
+		<object mime='image/png'>clients/gadu.png</object>
+	</icon> -->
+</icondef>
Index: psi/tags/0.10-os2-b1/iris.pri
===================================================================
--- psi/tags/0.10-os2-b1/iris.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/iris.pri	(revision 38)
@@ -0,0 +1,64 @@
+# libidn
+LIBIDN_BASE = $$IRIS_BASE/libidn
+CONFIG += libidn
+include($$IRIS_BASE/libidn.pri)
+
+# qca
+#unix:{
+#	INCLUDEPATH += /usr/local/include
+#	LIBS += -L/usr/local/lib -lqca
+#}
+#win32:{
+#	INCLUDEPATH += c:\local\include
+#	LIBS += c:\local\lib\qca.lib
+#}
+
+#!exists(qcaprovider.h) {
+#	Q_PREFIX = $$IRIS_BASE/../qca/src
+#	INCLUDEPATH += $$Q_PREFIX
+#}
+#HEADERS += ($$Q_PREFIX)qcaprovider.h
+
+DEFINES += XMPP_TEST
+
+INCLUDEPATH += $$IRIS_BASE/include $$IRIS_BASE/xmpp-core $$IRIS_BASE/xmpp-im $$IRIS_BASE/jabber
+
+HEADERS += \
+	$$IRIS_BASE/xmpp-core/hash.h \
+	$$IRIS_BASE/xmpp-core/simplesasl.h \
+	$$IRIS_BASE/xmpp-core/securestream.h \
+	$$IRIS_BASE/xmpp-core/parser.h \
+	$$IRIS_BASE/xmpp-core/xmlprotocol.h \
+	$$IRIS_BASE/xmpp-core/protocol.h \
+	$$IRIS_BASE/xmpp-core/td.h \
+	$$IRIS_BASE/xmpp-im/xmpp_tasks.h \
+	$$IRIS_BASE/xmpp-im/xmpp_xmlcommon.h \
+	$$IRIS_BASE/xmpp-im/xmpp_vcard.h \
+	$$IRIS_BASE/jabber/s5b.h \
+	$$IRIS_BASE/jabber/xmpp_ibb.h \
+	$$IRIS_BASE/jabber/xmpp_jidlink.h \
+	$$IRIS_BASE/jabber/filetransfer.h \
+	$$IRIS_BASE/include/xmpp.h \
+	$$IRIS_BASE/include/im.h
+
+SOURCES += \
+	$$IRIS_BASE/xmpp-core/connector.cpp \
+	$$IRIS_BASE/xmpp-core/tlshandler.cpp \
+	$$IRIS_BASE/xmpp-core/jid.cpp \
+	$$IRIS_BASE/xmpp-core/hash.cpp \
+	$$IRIS_BASE/xmpp-core/simplesasl.cpp \
+	$$IRIS_BASE/xmpp-core/securestream.cpp \
+	$$IRIS_BASE/xmpp-core/parser.cpp \
+	$$IRIS_BASE/xmpp-core/xmlprotocol.cpp \
+	$$IRIS_BASE/xmpp-core/protocol.cpp \
+	$$IRIS_BASE/xmpp-core/stream.cpp \
+	$$IRIS_BASE/xmpp-im/types.cpp \
+	$$IRIS_BASE/xmpp-im/client.cpp \
+	$$IRIS_BASE/xmpp-im/xmpp_tasks.cpp \
+	$$IRIS_BASE/xmpp-im/xmpp_xmlcommon.cpp \
+	$$IRIS_BASE/xmpp-im/xmpp_vcard.cpp \
+	$$IRIS_BASE/jabber/s5b.cpp \
+	$$IRIS_BASE/jabber/xmpp_ibb.cpp \
+	$$IRIS_BASE/jabber/xmpp_jidlink.cpp \
+	$$IRIS_BASE/jabber/filetransfer.cpp
+
Index: psi/tags/0.10-os2-b1/iris/COPYING
===================================================================
--- psi/tags/0.10-os2-b1/iris/COPYING	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/COPYING	(revision 38)
@@ -0,0 +1,513 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 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.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, 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 library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+  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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+  If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be 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.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+  9. 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 Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+
+
+  11. 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 Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  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 library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; 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.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
Index: psi/tags/0.10-os2-b1/iris/TODO
===================================================================
--- psi/tags/0.10-os2-b1/iris/TODO	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/TODO	(revision 38)
@@ -0,0 +1,18 @@
+- Stream::id(), Stream::lang()
+- whitespace pings (but disable when using http poll)
+- make stanza error conditions work for both 1.0 and old
+
+- xmpp-im (messages, roster, subscriptions, presence, privacy)
+- document xmpp-core
+- provide complete support for xmpp-core.  this means all functionality from
+  the draft, and noting behavior issues (like IQ semantics) in the
+  library documentation.
+
+- SASL "EXTERNAL" w/ client certificate
+- SASL "ANONYMOUS" ?
+
+credits:
+  MD5 algorithm by Peter Deutsch (Aladdin Enterprises)
+
+- s5b: support multiple streamhosts in proxy reply
+
Index: psi/tags/0.10-os2-b1/iris/cs.pri
===================================================================
--- psi/tags/0.10-os2-b1/iris/cs.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/cs.pri	(revision 38)
@@ -0,0 +1,32 @@
+cutestuff {
+	INCLUDEPATH += \
+		$$CS_BASE/util \
+		$$CS_BASE/network
+
+	HEADERS += \
+		$$CS_BASE/util/base64.h \
+		$$CS_BASE/util/bytestream.h \
+		$$CS_BASE/util/bconsole.h \
+		$$CS_BASE/util/safedelete.h \
+		$$CS_BASE/network/ndns.h \
+		$$CS_BASE/network/srvresolver.h \
+		$$CS_BASE/network/bsocket.h \
+		$$CS_BASE/network/httpconnect.h \
+		$$CS_BASE/network/httppoll.h \
+		$$CS_BASE/network/servsock.h \
+		$$CS_BASE/network/socks.h
+
+	SOURCES += \
+		$$CS_BASE/util/base64.cpp \
+		$$CS_BASE/util/bytestream.cpp \
+		$$CS_BASE/util/bconsole.cpp \
+		$$CS_BASE/util/safedelete.cpp \
+		$$CS_BASE/network/ndns.cpp \
+		$$CS_BASE/network/srvresolver.cpp \
+		$$CS_BASE/network/bsocket.cpp \
+		$$CS_BASE/network/httpconnect.cpp \
+		$$CS_BASE/network/httppoll.cpp \
+		$$CS_BASE/network/servsock.cpp \
+		$$CS_BASE/network/socks.cpp
+}
+
Index: psi/tags/0.10-os2-b1/iris/example/conntest/configure
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/conntest/configure	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/conntest/configure	(revision 38)
@@ -0,0 +1,609 @@
+#!/bin/sh
+
+show_usage() {
+cat <<EOT
+Usage: ./configure [OPTION]...
+
+This script creates necessary configuration files to build/install.
+
+Main options:
+  --prefix=[path]    Base path for build/install.  Default: /usr/local
+  --bindir=[path]    Directory for binaries.  Default: PREFIX/bin
+  --qtdir=[path]     Directory where Qt is installed.
+  --debug            Enable debug output.
+  --help             This help text.
+
+EOT
+}
+
+while [ $# -gt 0 ]; do
+	case "$1" in
+		--prefix=*)
+			PREFIX="${1#--prefix=}"
+			shift
+			;;
+
+		--bindir=*)
+			BINDIR="${1#--bindir=}"
+			shift
+			;;
+
+		--qtdir=*)
+			QTDIR="${1#--qtdir=}"
+			shift
+			;;
+
+		--debug)
+			QC_DEBUG="Y"
+			shift
+			;;
+		--help) show_usage; exit ;;
+		*) show_usage; exit ;;
+	esac
+done
+
+PREFIX=${PREFIX:-/usr/local}
+BINDIR=${BINDIR:-$PREFIX/bin}
+
+echo "Configuring Iris Conntest ..."
+
+if [ "$QC_DEBUG" = "Y" ]; then
+echo
+echo PREFIX=$PREFIX
+echo BINDIR=$BINDIR
+echo QTDIR=$QTDIR
+echo
+fi
+
+printf "Verifying Qt 3.x Multithreaded (MT) build environment ... "
+
+if [ -z "$QTDIR" ]; then
+	if [ "$QC_DEBUG" = "Y" ]; then
+		echo \$QTDIR not set... trying to find Qt manually
+	fi
+	for p in /usr/lib/qt /usr/share/qt /usr/share/qt3 /usr/local/lib/qt /usr/local/share/qt /usr/lib/qt3 /usr/local/lib/qt3 /usr/X11R6/share/qt /usr/qt/3 ; do
+		if [ -d "$p/mkspecs" ]; then
+			QTDIR=$p
+			break;
+		fi;
+	done
+	if [ -z "$QTDIR" ]; then
+		echo fail
+		echo
+		echo "Unable to find Qt 'mkspecs'.  Perhaps you need to"
+		echo "install the Qt 3 development utilities.  You may download"
+		echo "them either from the vendor of your operating system"
+		echo "or from http://www.trolltech.com/"
+		echo
+		echo "If you're sure you have the Qt development utilities"
+		echo "installed, you might try using the --qtdir option."
+		echo
+		exit 1;
+	fi
+	if [ ! -x "$QTDIR/bin/moc" ]; then
+		m=`which moc 2>/dev/null`
+		if [ ! -x "$m" ]; then
+			echo fail
+			echo
+			echo "We found Qt in $QTDIR, but we were unable to locate"
+			echo "the moc utility.  It was not found in $QTDIR/bin"
+			echo "nor in PATH.  This seems to be a very unusual setup."
+			echo "You might try using the --qtdir option."
+			echo
+			exit 1;
+		fi
+		qtpre=`echo $m | awk '{ n = index($0, "/bin/moc"); if (!n) { exit 1; } print substr($0, 0, n-1); exit 0; }' 2>/dev/null`
+		ret="$?"
+		if [ "$ret" != "0" ]; then
+			echo fail
+			echo
+			echo "We found Qt in $QTDIR, but the location of moc"
+			echo "($m) is not suitable for use with this build system."
+			echo "This is a VERY unusual and likely-broken setup.  You"
+			echo "should contact the maintainer of your Qt package."
+			echo
+			exit 1;
+		fi
+		QTDIR=$qtpre
+	fi
+fi
+
+if [ ! -x "$QTDIR/bin/qmake" ]; then
+	if [ "$QC_DEBUG" = "Y" ]; then
+		echo Warning: qmake not in \$QTDIR/bin/qmake
+		echo trying to find it in \$PATH
+	fi
+	qm=`which qmake 2>/dev/null`
+	if [ -x "$qm" ]; then
+		if [ "$QC_DEBUG" = "Y" ]; then
+			echo qmake found in $qm
+		fi
+	else
+		echo fail
+		echo
+		echo Sorry, you seem to have a very unusual setup,
+		echo or I missdetected \$QTDIR=$QTDIR
+		echo
+		echo Please set \$QTDIR manually and make sure that
+		echo \$QTDIR/bin/qmake exists.
+		echo
+		exit 1;
+	fi
+else
+	qm=$QTDIR/bin/qmake
+fi
+
+gen_files() {
+cat >$1/modules.cpp <<EOT
+
+EOT
+cat >$1/modules_new.cpp <<EOT
+
+EOT
+cat >$1/conf.cpp <<EOT
+#include<stdio.h>
+#include<stdlib.h>
+#include<qstring.h>
+#include<qdict.h>
+#include<qptrlist.h>
+#include<qfileinfo.h>
+#include<qfile.h>
+#include<qdir.h>
+#include<qstringlist.h>
+#include<qobject.h>
+
+class MocTestObject : public QObject
+{
+	Q_OBJECT
+public:
+	MocTestObject() {}
+};
+
+class Conf;
+
+class ConfObj
+{
+public:
+	ConfObj(Conf *c);
+	virtual ~ConfObj();
+
+	virtual QString name() const=0;
+	virtual QString shortname() const=0;
+	virtual QString checkString() const;
+	virtual QString resultString() const;
+	virtual bool exec()=0;
+
+	Conf *conf;
+	bool required;
+	bool disabled;
+};
+
+typedef QPtrList<ConfObj> ConfObjList;
+typedef QPtrListIterator<ConfObj> ConfObjListIt;
+
+class Conf
+{
+public:
+	Conf() : vars(17)
+	{
+		list.setAutoDelete(true);
+		vars.setAutoDelete(true);
+
+		vars.insert("QMAKE_INCDIR_X11", new QString(X11_INC));
+		vars.insert("QMAKE_LIBDIR_X11", new QString(X11_LIBDIR));
+		vars.insert("QMAKE_LIBS_X11",   new QString(X11_LIB));
+		vars.insert("QMAKE_CC",         new QString(CC));
+
+		do_debug = false;
+		done_debug = false;
+	}
+
+	~Conf()
+	{
+	}
+
+	void added(ConfObj *o)
+	{
+		list.append(o);
+	}
+
+	QString getenv(const QString &var)
+	{
+		char *p = ::getenv(var.latin1());
+		if(!p)
+			return QString::null;
+		return QString(p);
+	}
+
+	void debug(const QString &s)
+	{
+		if(do_debug) {
+			if(!done_debug)
+				printf("\n");
+			done_debug = true;
+			printf(" * %s\n", s.latin1());
+		}
+	}
+
+	bool exec()
+	{
+		if(getenv("QC_DEBUG") == "Y")
+			do_debug = true;
+
+		ConfObjListIt it(list);
+		for(ConfObj *o; (o = it.current()); ++it) {
+			// if this was a disabled-by-default option, check if it was enabled
+			if(o->disabled) {
+				QString v = QString("QC_ENABLE_") + o->shortname();
+				if(getenv(v) != "Y")
+					continue;
+			}
+			// and the opposite?
+			else {
+				QString v = QString("QC_DISABLE_") + o->shortname();
+				if(getenv(v) == "Y")
+					continue;
+			}
+
+			QString check = o->checkString();
+			if(check.isEmpty())
+				check = QString("Checking for %1 ...").arg(o->name());
+			printf("%s", check.latin1());
+			fflush(stdout);
+
+			done_debug = false;
+			bool ok = o->exec();
+
+			QString result = o->resultString();
+			if(result.isEmpty()) {
+				if(ok)
+					result = "yes";
+				else
+					result = "no";
+			}
+			if(done_debug)
+				printf(" -> %s\n", result.latin1());
+			else
+				printf(" %s\n", result.latin1());
+
+			if(!ok && o->required) {
+				printf("\nError: need %s!\n", o->name().latin1());
+				return false;
+			}
+		}
+		return true;
+	}
+
+	const QString & qvar(const QString &s)
+	{
+		QString *p = vars.find(s);
+		if(p)
+			return *p;
+		else
+			return blank;
+	}
+
+	QString expandIncludes(const QString &inc)
+	{
+		return QString("-I") + inc;
+	}
+
+	QString expandLibs(const QString &lib)
+	{
+		return QString("-L") + lib;
+	}
+
+	int doCommand(const QString &s)
+	{
+		debug(QString("[%1]").arg(s));
+		QString fullcmd;
+		if(do_debug)
+			fullcmd = s;
+		else
+			fullcmd = s + " 1>/dev/null 2>/dev/null";
+		int r = system(fullcmd.latin1());
+		debug(QString("returned: %1").arg(r));
+		return r;
+	}
+
+	bool doCompileAndLink(const QString &filedata, const QString &flags, int *retcode=0)
+	{
+		QDir dir(".");
+		QString fname = "atest.c";
+		QString out = "atest";
+		QFile f(fname);
+		QCString cs = filedata.latin1();
+		if(!f.open(IO_WriteOnly | IO_Truncate)) {
+			debug("unable to open atest.c for writing");
+			return false;
+		}
+		if(f.writeBlock(cs.data(), cs.length()) == -1) {
+			debug("error writing to atest.c");
+			return false;
+		}
+		f.close();
+
+		debug(QString("Wrote atest.c:\n%1").arg(filedata));
+
+		QString str = qvar("QMAKE_CC") + ' ' + fname + " -o " + out;
+		if(!flags.isEmpty()) {
+			str += ' ';
+			str += flags;
+		}
+
+		int r = doCommand(str);
+		if(r == 0 && retcode)
+			*retcode = doCommand(QString("./") + out);
+		dir.remove(fname);
+		dir.remove(out);
+		if(r != 0)
+			return false;
+		return true;
+	}
+
+	bool checkHeader(const QString &path, const QString &h)
+	{
+		QFileInfo fi(path + '/' + h);
+		if(fi.exists())
+			return true;
+		return false;
+	}
+
+	bool findHeader(const QString &h, const QStringList &ext, QString *inc)
+	{
+		if(checkHeader("/usr/include", h)) {
+			*inc = "";
+			return true;
+		}
+		QStringList dirs;
+		dirs += "/usr/local/include";
+		dirs += ext;
+		for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
+			if(checkHeader(*it, h)) {
+				*inc = *it;
+				return true;
+			}
+		}
+		return false;
+	}
+
+	bool checkLibrary(const QString &path, const QString &name)
+	{
+		QString str =
+			"int main()\n"
+			"{\n"
+			"    return 0;\n"
+			"}\n";
+
+		QString extra;
+		if(!path.isEmpty())
+			extra += QString("-L") + path + ' ';
+		extra += QString("-l") + name;
+		if(!doCompileAndLink(str, extra))
+			return false;
+		return true;
+	}
+
+	bool findLibrary(const QString &name, QString *lib)
+	{
+		if(checkLibrary("", name)) {
+			*lib = "";
+			return true;
+		}
+		if(checkLibrary("/usr/local/lib", name)) {
+			*lib = "/usr/local/lib";
+			return true;
+		}
+		return false;
+	}
+
+	void addDefine(const QString &str)
+	{
+		if(DEFINES.isEmpty())
+			DEFINES = str;
+		else
+			DEFINES += QString(" ") + str;
+		debug(QString("DEFINES += %1").arg(str));
+	}
+
+	void addLib(const QString &str)
+	{
+		if(LIBS.isEmpty())
+			LIBS = str;
+		else
+			LIBS += QString(" ") + str;
+		debug(QString("LIBS += %1").arg(str));
+	}
+
+	void addIncludePath(const QString &str)
+	{
+		if(INCLUDEPATH.isEmpty())
+			INCLUDEPATH = str;
+		else
+			INCLUDEPATH += QString(" ") + str;
+		debug(QString("INCLUDEPATH += %1").arg(str));
+	}
+
+	void addExtra(const QString &str)
+	{
+		extra += str + '\n';
+		debug(QString("extra += %1").arg(str));
+	}
+
+	QString DEFINES;
+	QString INCLUDEPATH;
+	QString LIBS;
+	QString extra;
+
+private:
+	ConfObjList list;
+	QDict<QString> vars;
+	QString blank;
+	bool do_debug, done_debug;
+};
+
+ConfObj::ConfObj(Conf *c)
+{
+	conf = c;
+	conf->added(this);
+	required = false;
+	disabled = false;
+}
+
+ConfObj::~ConfObj()
+{
+}
+
+QString ConfObj::checkString() const
+{
+	return QString();
+}
+
+QString ConfObj::resultString() const
+{
+	return QString();
+}
+
+#include"modules.cpp"
+
+//----------------------------------------------------------------------------
+// main
+//----------------------------------------------------------------------------
+int main()
+{
+	Conf *conf = new Conf;
+	ConfObj *o;
+	o = 0;
+#include"modules_new.cpp"
+
+	printf("ok\n");
+	bool success = false;
+	if(conf->exec()) {
+		QFile f("conf.pri");
+		if(!f.open(IO_WriteOnly | IO_Truncate)) {
+			printf("Error writing %s\n", f.name().latin1());
+			return 1;
+		}
+
+		QString str;
+		str += "# qconf\n";
+		str += "QT_PATH_PLUGINS = " + QString(qInstallPathPlugins()) + '\n';
+		if(!conf->DEFINES.isEmpty())
+			str += "DEFINES += " + conf->DEFINES + '\n';
+		if(!conf->INCLUDEPATH.isEmpty())
+			str += "INCLUDEPATH += " + conf->INCLUDEPATH + '\n';
+		if(!conf->LIBS.isEmpty())
+			str += "LIBS += " + conf->LIBS + '\n';
+		if(!conf->extra.isEmpty())
+			str += conf->extra;
+		str += '\n';
+
+		char *p = getenv("BINDIR");
+		if(p) {
+			str += QString("target.path = ") + p + '\n';
+			str += "INSTALLS += target\n";
+		}
+
+		QCString cs = str.latin1();
+		f.writeBlock(cs.data(), cs.length());
+		f.close();
+		success = true;
+	}
+	delete conf;
+
+	if(success)
+		return 0;
+	else
+		return 1;
+}
+
+#include"conf.moc"
+
+
+EOT
+cat >$1/conf.pro <<EOT
+TEMPLATE = app
+CONFIG += qt x11 thread console
+TARGET = conf
+
+DEFINES += X11_INC='"\$\$QMAKE_INCDIR_X11"'
+DEFINES += X11_LIBDIR='"\$\$QMAKE_LIBDIR_X11"'
+DEFINES += X11_LIB='"\$\$QMAKE_LIBS_X11"'
+DEFINES += CC='"\$\$QMAKE_CC"'
+
+SOURCES += conf.cpp
+
+EOT
+}
+
+export PREFIX
+export BINDIR
+export QTDIR
+export QC_DEBUG
+rm -rf .qconftemp
+(
+	mkdir .qconftemp
+	gen_files .qconftemp
+	cd .qconftemp
+	$qm conf.pro >/dev/null
+	QTDIR=$QTDIR make clean >/dev/null 2>&1
+	QTDIR=$QTDIR make >../conf.log 2>&1
+)
+
+if [ "$?" != "0" ]; then
+	rm -rf .qconftemp
+	echo fail
+	echo
+	echo "There was an error compiling 'conf'.  Be sure you have a proper"
+	echo "Qt 3.x Multithreaded (MT) build environment set up.  This"
+	echo "means not just Qt, but also a C++ compiler, the 'make' command,"
+	echo "and any other packages necessary to compile C++ programs."
+	echo "See conf.log for details."
+	if [ ! -f "$QTDIR/lib/libqt-mt.so.3" ]; then
+		echo
+		echo "One possible reason is that you don't have"
+		echo "libqt-mt.so.3 installed in $QTDIR/lib/."
+	fi
+	echo
+	exit 1;
+fi
+
+.qconftemp/conf
+ret="$?"
+if [ "$ret" = "1" ]; then
+	rm -rf .qconftemp
+	echo
+	exit 1;
+else
+	if [ "$ret" != "0" ]; then
+		rm -rf .qconftemp
+		echo fail
+		echo
+		echo Unexpected error launching 'conf'
+		echo
+		exit 1;
+	fi
+fi
+rm -rf .qconftemp
+
+if [ -x "./qcextra" ]; then
+	./qcextra
+fi
+# run qmake
+$qm conntest.pro
+if [ "$?" != "0" ]; then
+	echo
+	exit 1;
+fi
+cat >Makefile.tmp <<EOT
+export QTDIR = $QTDIR
+export PATH = $QTDIR/bin:$PATH
+EOT
+cat Makefile >> Makefile.tmp
+rm -f Makefile
+cp -f Makefile.tmp Makefile
+rm -f Makefile.tmp
+
+echo
+echo Good, your configure finished.  Now run \'make\'.
+echo
Index: psi/tags/0.10-os2-b1/iris/example/conntest/conntest.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/conntest/conntest.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/conntest/conntest.cpp	(revision 38)
@@ -0,0 +1,579 @@
+#include<qapplication.h>
+#include"bconsole.h"
+#include"qca.h"
+#include"xmpp.h"
+#include"base64.h"
+
+#include<stdio.h>
+
+#define ROOTCERT_PATH "/usr/local/share/psi/certs/rootcert.xml"
+
+QCA::Cert readCertXml(const QDomElement &e)
+{
+	QCA::Cert cert;
+	// there should be one child data tag
+	QDomElement data = e.elementsByTagName("data").item(0).toElement();
+	if(!data.isNull())
+		cert.fromDER(Base64::stringToArray(data.text()));
+	return cert;
+}
+
+QPtrList<QCA::Cert> getRootCerts(const QString &store)
+{
+	QPtrList<QCA::Cert> list;
+
+	// open the Psi rootcerts file
+	QFile f(store);
+	if(!f.open(IO_ReadOnly)) {
+		printf("unable to open %s\n", f.name().latin1());
+		return list;
+	}
+	QDomDocument doc;
+	doc.setContent(&f);
+	f.close();
+
+	QDomElement base = doc.documentElement();
+	if(base.tagName() != "store") {
+		printf("wrong format of %s\n", f.name().latin1());
+		return list;
+	}
+	QDomNodeList cl = base.elementsByTagName("certificate");
+	if(cl.count() == 0) {
+		printf("no certs found in %s\n", f.name().latin1());
+		return list;
+	}
+
+	int num = 0;
+	for(int n = 0; n < (int)cl.count(); ++n) {
+		QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
+		if(cert->isNull()) {
+			printf("error reading cert\n");
+			delete cert;
+			continue;
+		}
+
+		++num;
+		list.append(cert);
+	}
+	printf("imported %d root certs\n", num);
+
+	return list;
+}
+
+static QString prompt(const QString &s)
+{
+	printf("* %s ", s.latin1());
+	fflush(stdout);
+	char line[256];
+	fgets(line, 255, stdin);
+	QString result = line;
+	if(result[result.length()-1] == '\n')
+		result.truncate(result.length()-1);
+	return result;
+}
+
+static void showCertInfo(const QCA::Cert &cert)
+{
+	fprintf(stderr, "-- Cert --\n");
+	fprintf(stderr, " CN: %s\n", cert.subject()["CN"].latin1());
+	fprintf(stderr, " Valid from: %s, until %s\n",
+		cert.notBefore().toString().latin1(),
+		cert.notAfter().toString().latin1());
+	fprintf(stderr, " PEM:\n%s\n", cert.toPEM().latin1());
+}
+
+static QString resultToString(int result)
+{
+	QString s;
+	switch(result) {
+		case QCA::TLS::NoCert:
+			s = QObject::tr("No certificate presented.");
+			break;
+		case QCA::TLS::Valid:
+			break;
+		case QCA::TLS::HostMismatch:
+			s = QObject::tr("Hostname mismatch.");
+			break;
+		case QCA::TLS::Rejected:
+			s = QObject::tr("Root CA rejects the specified purpose.");
+			break;
+		case QCA::TLS::Untrusted:
+			s = QObject::tr("Not trusted for the specified purpose.");
+			break;
+		case QCA::TLS::SignatureFailed:
+			s = QObject::tr("Invalid signature.");
+			break;
+		case QCA::TLS::InvalidCA:
+			s = QObject::tr("Invalid CA certificate.");
+			break;
+		case QCA::TLS::InvalidPurpose:
+			s = QObject::tr("Invalid certificate purpose.");
+			break;
+		case QCA::TLS::SelfSigned:
+			s = QObject::tr("Certificate is self-signed.");
+			break;
+		case QCA::TLS::Revoked:
+			s = QObject::tr("Certificate has been revoked.");
+			break;
+		case QCA::TLS::PathLengthExceeded:
+			s = QObject::tr("Maximum cert chain length exceeded.");
+			break;
+		case QCA::TLS::Expired:
+			s = QObject::tr("Certificate has expired.");
+			break;
+		case QCA::TLS::Unknown:
+		default:
+			s = QObject::tr("General validation error.");
+			break;
+	}
+	return s;
+}
+
+class App : public QObject
+{
+	Q_OBJECT
+public:
+	XMPP::AdvancedConnector *conn;
+	QCA::TLS *tls;
+	XMPP::QCATLSHandler *tlsHandler;
+	XMPP::ClientStream *stream;
+	BConsole *c;
+	XMPP::Jid jid;
+	QPtrList<QCA::Cert> rootCerts;
+
+	App(const XMPP::Jid &_jid, const XMPP::AdvancedConnector::Proxy &proxy, const QString &host, int port, bool opt_ssl, bool opt_probe)
+	:QObject(0)
+	{
+		c = 0;
+		jid = _jid;
+
+		// Connector
+		conn = new XMPP::AdvancedConnector;
+		conn->setProxy(proxy);
+		if(!host.isEmpty())
+			conn->setOptHostPort(host, port);
+		conn->setOptProbe(opt_probe);
+		conn->setOptSSL(opt_ssl);
+
+		// TLSHandler
+		tls = 0;
+		tlsHandler = 0;
+		rootCerts.setAutoDelete(true);
+		if(QCA::isSupported(QCA::CAP_TLS)) {
+			rootCerts = getRootCerts(ROOTCERT_PATH);
+			tls = new QCA::TLS;
+			tls->setCertificateStore(rootCerts);
+			tlsHandler = new XMPP::QCATLSHandler(tls);
+			connect(tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
+		}
+
+		// Stream
+		stream = new XMPP::ClientStream(conn, tlsHandler);
+		connect(stream, SIGNAL(connected()), SLOT(cs_connected()));
+		connect(stream, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated()));
+		connect(stream, SIGNAL(needAuthParams(bool, bool, bool)), SLOT(cs_needAuthParams(bool, bool, bool)));
+		connect(stream, SIGNAL(authenticated()), SLOT(cs_authenticated()));
+		connect(stream, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
+		connect(stream, SIGNAL(readyRead()), SLOT(cs_readyRead()));
+		connect(stream, SIGNAL(stanzaWritten()), SLOT(cs_stanzaWritten()));
+		connect(stream, SIGNAL(warning(int)), SLOT(cs_warning(int)));
+		connect(stream, SIGNAL(error(int)), SLOT(cs_error(int)));
+
+		fprintf(stderr, "conntest: Connecting ...\n");
+		stream->setSSFRange(0, 256);
+		stream->connectToServer(jid);
+	}
+
+	~App()
+	{
+		delete stream;
+		delete tls; // this destroys the TLSHandler also
+		delete conn;
+		delete c;
+	}
+
+signals:
+	void quit();
+
+private slots:
+	void tls_handshaken()
+	{
+		QCA::Cert cert = tls->peerCertificate();
+		int vr = tls->certificateValidityResult();
+
+		fprintf(stderr, "conntest: Successful TLS handshake.\n");
+		if(!cert.isNull())
+			showCertInfo(cert);
+		if(vr == QCA::TLS::Valid)
+			fprintf(stderr, "conntest: Valid certificate.\n");
+		else
+			fprintf(stderr, "conntest: Invalid certificate: %s\n", resultToString(vr).latin1());
+
+		tlsHandler->continueAfterHandshake();
+	}
+
+	void cs_connected()
+	{
+		fprintf(stderr, "conntest: Connected\n");
+	}
+
+	void cs_securityLayerActivated()
+	{
+		fprintf(stderr, "conntest: Security layer activated (%s)\n", tls->isHandshaken() ? "TLS": "SASL");
+	}
+
+	void cs_needAuthParams(bool user, bool pass, bool realm)
+	{
+		fprintf(stderr, "conntest: need auth params -");
+		if(user)
+			fprintf(stderr, " (user)");
+		if(pass)
+			fprintf(stderr, " (pass)");
+		if(realm)
+			fprintf(stderr, " (realm)");
+		fprintf(stderr, "\n");
+
+		if(user)
+			stream->setUsername(jid.node());
+		if(pass)
+			stream->setPassword(prompt("Password (not hidden!) :"));
+		stream->continueAfterParams();
+	}
+
+	void cs_authenticated()
+	{
+		fprintf(stderr, "conntest: <<< Authenticated >>>\n");
+
+		// console
+		c = new BConsole;
+		connect(c, SIGNAL(connectionClosed()), SLOT(con_connectionClosed()));
+		connect(c, SIGNAL(readyRead()), SLOT(con_readyRead()));
+	}
+
+	void cs_connectionClosed()
+	{
+		fprintf(stderr, "conntest: Disconnected by peer\n");
+		quit();
+	}
+
+	void cs_readyRead()
+	{
+		for(XMPP::Stanza s; !(s = stream->read()).isNull();) {
+			QString str = s.toString();
+			printf("%s\n", str.local8Bit().data());
+		}
+	}
+
+	void cs_stanzaWritten()
+	{
+		fprintf(stderr, "conntest: Stanza written\n");
+	}
+
+	void cs_warning(int warn)
+	{
+		if(warn == XMPP::ClientStream::WarnOldVersion) {
+			fprintf(stderr, "conntest: Warning: pre-1.0 protocol server\n");
+		}
+		else if(warn == XMPP::ClientStream::WarnNoTLS) {
+			fprintf(stderr, "conntest: Warning: TLS not available!\n");
+		}
+		stream->continueAfterWarning();
+	}
+
+	void cs_error(int err)
+	{
+		if(err == XMPP::ClientStream::ErrParse) {
+			fprintf(stderr, "conntest: XML parsing error\n");
+		}
+		else if(err == XMPP::ClientStream::ErrProtocol) {
+			fprintf(stderr, "conntest: XMPP protocol error\n");
+		}
+		else if(err == XMPP::ClientStream::ErrStream) {
+			int x = stream->errorCondition();
+			QString s;
+			if(x == XMPP::Stream::GenericStreamError)
+				s = "generic stream error";
+			else if(x == XMPP::ClientStream::Conflict)
+				s = "conflict (remote login replacing this one)";
+			else if(x == XMPP::ClientStream::ConnectionTimeout)
+				s = "timed out from inactivity";
+			else if(x == XMPP::ClientStream::InternalServerError)
+				s = "internal server error";
+			else if(x == XMPP::ClientStream::InvalidXml)
+				s = "invalid XML";
+			else if(x == XMPP::ClientStream::PolicyViolation)
+				s = "policy violation.  go to jail!";
+			else if(x == XMPP::ClientStream::ResourceConstraint)
+				s = "server out of resources";
+			else if(x == XMPP::ClientStream::SystemShutdown)
+				s = "system is shutting down NOW";
+			fprintf(stderr, "conntest: XMPP stream error: %s\n", s.latin1());
+		}
+		else if(err == XMPP::ClientStream::ErrConnection) {
+			int x = conn->errorCode();
+			QString s;
+			if(x == XMPP::AdvancedConnector::ErrConnectionRefused)
+				s = "unable to connect to server";
+			else if(x == XMPP::AdvancedConnector::ErrHostNotFound)
+				s = "host not found";
+			else if(x == XMPP::AdvancedConnector::ErrProxyConnect)
+				s = "proxy connect";
+			else if(x == XMPP::AdvancedConnector::ErrProxyNeg)
+				s = "proxy negotiating";
+			else if(x == XMPP::AdvancedConnector::ErrProxyAuth)
+				s = "proxy authorization";
+			else if(x == XMPP::AdvancedConnector::ErrStream)
+				s = "stream error";
+			fprintf(stderr, "conntest: connection error: %s\n", s.latin1());
+		}
+		else if(err == XMPP::ClientStream::ErrNeg) {
+			int x = stream->errorCondition();
+			QString s;
+			if(x == XMPP::ClientStream::HostGone)
+				s = "host no longer hosted";
+			else if(x == XMPP::ClientStream::HostUnknown)
+				s = "host unknown";
+			else if(x == XMPP::ClientStream::RemoteConnectionFailed)
+				s = "a required remote connection failed";
+			else if(x == XMPP::ClientStream::SeeOtherHost)
+				s = QString("see other host: [%1]").arg(stream->errorText());
+			else if(x == XMPP::ClientStream::UnsupportedVersion)
+				s = "server does not support proper xmpp version";
+			fprintf(stderr, "conntest: stream negotiation error: %s\n", s.latin1());
+		}
+		else if(err == XMPP::ClientStream::ErrTLS) {
+			int x = stream->errorCondition();
+			QString s;
+			if(x == XMPP::ClientStream::TLSStart)
+				s = "server rejected STARTTLS";
+			else if(x == XMPP::ClientStream::TLSFail) {
+				int t = tlsHandler->tlsError();
+				if(t == QCA::TLS::ErrHandshake)
+					s = "TLS handshake error";
+				else
+					s = "broken security layer (TLS)";
+			}
+			fprintf(stderr, "conntest: %s\n", s.latin1());
+		}
+		else if(err == XMPP::ClientStream::ErrAuth) {
+			int x = stream->errorCondition();
+			QString s;
+			if(x == XMPP::ClientStream::GenericAuthError)
+				s = "unable to login";
+			else if(x == XMPP::ClientStream::NoMech)
+				s = "no appropriate auth mechanism available for given security settings";
+			else if(x == XMPP::ClientStream::BadProto)
+				s = "bad server response";
+			else if(x == XMPP::ClientStream::BadServ)
+				s = "server failed mutual authentication";
+			else if(x == XMPP::ClientStream::EncryptionRequired)
+				s = "encryption required for chosen SASL mechanism";
+			else if(x == XMPP::ClientStream::InvalidAuthzid)
+				s = "invalid authzid";
+			else if(x == XMPP::ClientStream::InvalidMech)
+				s = "invalid SASL mechanism";
+			else if(x == XMPP::ClientStream::InvalidRealm)
+				s = "invalid realm";
+			else if(x == XMPP::ClientStream::MechTooWeak)
+				s = "SASL mechanism too weak for authzid";
+			else if(x == XMPP::ClientStream::NotAuthorized)
+				s = "not authorized";
+			else if(x == XMPP::ClientStream::TemporaryAuthFailure)
+				s = "temporary auth failure";
+			fprintf(stderr, "conntest: auth error: %s\n", s.latin1());
+		}
+		else if(err == XMPP::ClientStream::ErrSecurityLayer)
+			fprintf(stderr, "conntest: broken security layer (SASL)\n");
+		quit();
+	}
+
+	void con_connectionClosed()
+	{
+		fprintf(stderr, "conntest: Closing.\n");
+		stream->close();
+		quit();
+	}
+
+	void con_readyRead()
+	{
+		QByteArray a = c->read();
+		QCString cs;
+		cs.resize(a.size()+1);
+		memcpy(cs.data(), a.data(), a.size());
+		QString s = QString::fromLocal8Bit(cs);
+		stream->writeDirect(s);
+	}
+};
+
+#include"conntest.moc"
+
+int main(int argc, char **argv)
+{
+	QApplication app(argc, argv, false);
+
+	if(argc < 2) {
+		printf("usage: conntest [options] [jid]\n");
+		printf("   Options:\n");
+		printf("     --host=host:port\n");
+		printf("     --sslhost=host:port\n");
+		printf("     --probe\n");
+		printf("     --proxy=[https|poll|socks],host:port,url\n");
+		printf("     --proxy-auth=user,pass\n");
+		printf("\n");
+		return 0;
+	}
+
+	bool have_tls = QCA::isSupported(QCA::CAP_TLS);
+
+	XMPP::Jid jid;
+	XMPP::AdvancedConnector::Proxy proxy;
+	QString host;
+	int port = 0;
+	bool opt_ssl = false;
+	bool opt_probe = false;
+
+	for(int at = 1; at < argc; ++at) {
+		QString s = argv[at];
+
+		// is it an option?
+		if(s.left(2) == "--") {
+			QString name;
+			QStringList args;
+			int n = s.find('=', 2);
+			if(n != -1) {
+				name = s.mid(2, n-2);
+				++n;
+				args = QStringList::split(',', s.mid(n), true);
+			}
+			else {
+				name = s.mid(2);
+				args.clear();
+			}
+
+			// eat the arg
+			--argc;
+			for(int x = at; x < argc; ++x)
+				argv[x] = argv[x+1];
+			--at; // don't advance
+
+			// process option
+			if(name == "proxy") {
+				QString proxy_host;
+				int proxy_port = 0;
+				QString type = args[0];
+				QString s = args[1];
+				int n = s.find(':');
+				if(n == -1) {
+					if(type != "poll") {
+						printf("Invalid host:port for proxy\n");
+						return 0;
+					}
+				}
+				else {
+					proxy_host = s.mid(0, n);
+					++n;
+					proxy_port = s.mid(n).toInt();
+				}
+
+				if(type == "https") {
+					proxy.setHttpConnect(proxy_host, proxy_port);
+				}
+				else if(type == "poll") {
+					if(args.count() < 3) {
+						printf("poll needs more args\n");
+						return 0;
+					}
+					QString proxy_url = args[2];
+					proxy.setHttpPoll(proxy_host, proxy_port, proxy_url);
+				}
+				else if(type == "socks") {
+					proxy.setSocks(proxy_host, proxy_port);
+				}
+				else {
+					printf("No such proxy type '%s'\n", type.latin1());
+					return 0;
+				}
+			}
+			else if(name == "proxy-auth") {
+				proxy.setUserPass(args[0], args[1]);
+			}
+			else if(name == "host") {
+				QString s = args[0];
+				int n = s.find(':');
+				if(n == -1) {
+					printf("Invalid host:port for host\n");
+					return 0;
+				}
+				host = s.mid(0, n);
+				++n;
+				port = s.mid(n).toInt();
+			}
+			else if(name == "sslhost") {
+				QString s = args[0];
+				int n = s.find(':');
+				if(n == -1) {
+					printf("Invalid host:port for host\n");
+					return 0;
+				}
+				host = s.mid(0, n);
+				++n;
+				port = s.mid(n).toInt();
+				opt_ssl = true;
+			}
+			else if(name == "probe") {
+				opt_probe = true;
+			}
+			else {
+				printf("Unknown option '%s'\n", name.latin1());
+				return 0;
+			}
+		}
+	}
+
+	if(argc < 2) {
+		printf("No host specified!\n");
+		return 0;
+	}
+	jid = argv[1];
+
+	if((opt_ssl || opt_probe) && !have_tls) {
+		printf("TLS not supported, so the sslhost and probe options are not allowed.\n");
+		return 0;
+	}
+
+	printf("JID: %s\n", jid.full().latin1());
+	if(proxy.type() != XMPP::AdvancedConnector::Proxy::None) {
+		printf("Proxy: ");
+		if(proxy.type() == XMPP::AdvancedConnector::Proxy::HttpConnect)
+			printf("HttpConnect (%s:%d)", proxy.host().latin1(), proxy.port());
+		else if(proxy.type() == XMPP::AdvancedConnector::Proxy::HttpPoll) {
+			printf("HttpPoll {%s}", proxy.url().latin1());
+			if(!proxy.host().isEmpty()) {
+				printf(" (%s:%d)", proxy.host().latin1(), proxy.port());
+			}
+		}
+		else if(proxy.type() == XMPP::AdvancedConnector::Proxy::Socks)
+			printf("Socks (%s:%d)", proxy.host().latin1(), proxy.port());
+		printf("\n");
+	}
+	if(proxy.type() != XMPP::AdvancedConnector::Proxy::HttpPoll) {
+		if(!host.isEmpty()) {
+			printf("Host: %s:%d", host.latin1(), port);
+			if(opt_ssl)
+				printf(" (ssl)");
+			printf("\n");
+		}
+		else {
+			if(opt_probe)
+				printf("Probe active\n");
+		}
+	}
+	printf("----------\n");
+
+	App *a = new App(jid, proxy, host, port, opt_ssl, opt_probe);
+	QObject::connect(a, SIGNAL(quit()), &app, SLOT(quit()));
+	app.exec();
+	delete a;
+
+	return 0;
+}
Index: psi/tags/0.10-os2-b1/iris/example/conntest/conntest.pro
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/conntest/conntest.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/conntest/conntest.pro	(revision 38)
@@ -0,0 +1,13 @@
+TEMPLATE = app
+CONFIG += qt thread console
+TARGET = conntest
+
+exists(example.pri) {
+	include(example.pri)
+}
+!exists(example.pri) {
+	include(../example.pri)
+}
+
+SOURCES += conntest.cpp
+
Index: psi/tags/0.10-os2-b1/iris/example/conntest/conntest.qc
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/conntest/conntest.qc	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/conntest/conntest.qc	(revision 38)
@@ -0,0 +1,4 @@
+<qconf>
+  <name>Iris Conntest</name>
+  <profile>conntest.pro</profile>
+</qconf>
Index: psi/tags/0.10-os2-b1/iris/example/conntest/prepare
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/conntest/prepare	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/conntest/prepare	(revision 38)
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+CS_BASE=../../../cutestuff
+QCA_BASE=../../../qca
+IRIS_BASE=../..
+
+# import cutestuff
+mkdir cutestuff
+cp -a $CS_BASE/util cutestuff
+cp -a $CS_BASE/network cutestuff
+
+# import qca
+mkdir qca
+cp -a $QCA_BASE/src/* qca
+
+# import iris
+mkdir iris
+cp -a $IRIS_BASE/libidn iris
+cp -a $IRIS_BASE/libidn.pri iris
+cp -a $IRIS_BASE/include iris
+cp -a $IRIS_BASE/xmpp-core iris
+cp -a $IRIS_BASE/xmpp-im iris
+cp -a $IRIS_BASE/jabber iris
+cp -a $IRIS_BASE/iris.pri iris
+
+# other stuff
+cp -a $IRIS_BASE/cs.pri .
+cp -a $IRIS_BASE/example/example.pri .
+cp -a $IRIS_BASE/COPYING .
+
Index: psi/tags/0.10-os2-b1/iris/example/example.pri
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/example.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/example.pri	(revision 38)
@@ -0,0 +1,38 @@
+# stay clean
+MOC_DIR        = .moc
+OBJECTS_DIR    = .obj
+UI_DIR         = .ui
+
+# include some of cutestuff
+CONFIG += cutestuff
+CS_BASE = ../../../cutestuff
+exists(cutestuff) {
+	CS_BASE = cutestuff
+}
+exists(cs.pri) {
+	include(cs.pri)
+}
+!exists(cs.pri) {
+	include(../../cs.pri)
+}
+
+# qca
+#DEFINES += QCA_STATIC
+#QCA_PREFIX = qca
+#!exists(qca) {
+#	QCA_PREFIX = ../../../qca/src
+#}
+#INCLUDEPATH += $$QCA_PREFIX
+#HEADERS += $$QCA_PREFIX/qca.h $$QCA_PREFIX/qcaprovider.h
+#SOURCES += $$QCA_PREFIX/qca.cpp
+LIBS += -lqca
+
+# iris
+exists(iris) {
+	IRIS_BASE = iris
+}
+!exists(iris) {
+	IRIS_BASE = ../..
+}
+include($$IRIS_BASE/iris.pri)
+
Index: psi/tags/0.10-os2-b1/iris/example/server/server.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/server/server.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/server/server.cpp	(revision 38)
@@ -0,0 +1,217 @@
+#include<qapplication.h>
+#include<qsocket.h>
+#include<qserversocket.h>
+#include<qvaluelist.h>
+#include<qtimer.h>
+#include<qca.h>
+#include<stdlib.h>
+#include<time.h>
+#include"bsocket.h"
+#include"xmpp.h"
+
+#include<unistd.h>
+
+char pemdata_cert[] =
+	"-----BEGIN CERTIFICATE-----\n"
+	"MIIDbjCCAtegAwIBAgIBADANBgkqhkiG9w0BAQQFADCBhzELMAkGA1UEBhMCVVMx\n"
+	"EzARBgNVBAgTCkNhbGlmb3JuaWExDzANBgNVBAcTBklydmluZTEYMBYGA1UEChMP\n"
+	"RXhhbXBsZSBDb21wYW55MRQwEgYDVQQDEwtleGFtcGxlLmNvbTEiMCAGCSqGSIb3\n"
+	"DQEJARYTZXhhbXBsZUBleGFtcGxlLmNvbTAeFw0wMzA3MjQwNzMwMDBaFw0wMzA4\n"
+	"MjMwNzMwMDBaMIGHMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEP\n"
+	"MA0GA1UEBxMGSXJ2aW5lMRgwFgYDVQQKEw9FeGFtcGxlIENvbXBhbnkxFDASBgNV\n"
+	"BAMTC2V4YW1wbGUuY29tMSIwIAYJKoZIhvcNAQkBFhNleGFtcGxlQGV4YW1wbGUu\n"
+	"Y29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCobzCF268K2sRp473gvBTT\n"
+	"4AgSL1kjeF8N57vxS1P8zWrWMXNs4LuH0NRZmKTajeboy0br8xw+smIy3AbaKAwW\n"
+	"WZToesxebu3m9VeA8dqWyOaUMjoxAcgVYesgVaMpjRe7fcWdJnX1wJoVVPuIcO8m\n"
+	"a+AAPByfTORbzpSTmXAQAwIDAQABo4HnMIHkMB0GA1UdDgQWBBTvFierzLmmYMq0\n"
+	"cB/+5rK1bNR56zCBtAYDVR0jBIGsMIGpgBTvFierzLmmYMq0cB/+5rK1bNR566GB\n"
+	"jaSBijCBhzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDzANBgNV\n"
+	"BAcTBklydmluZTEYMBYGA1UEChMPRXhhbXBsZSBDb21wYW55MRQwEgYDVQQDEwtl\n"
+	"eGFtcGxlLmNvbTEiMCAGCSqGSIb3DQEJARYTZXhhbXBsZUBleGFtcGxlLmNvbYIB\n"
+	"ADAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4GBAGqGhXf7xNOnYNtFO7gz\n"
+	"K6RdZGHFI5q1DAEz4hhNBC9uElh32XGX4wN7giz3zLC8v9icL/W4ff/K5NDfv3Gf\n"
+	"gQe/+Wo9Be3H3ul6uwPPFnx4+PIOF2a5TW99H9smyxWdNjnFtcUte4al3RszcMWG\n"
+	"x3iqsWosGtj6F+ridmKoqKLu\n"
+	"-----END CERTIFICATE-----\n";
+
+char pemdata_privkey[] =
+	"-----BEGIN RSA PRIVATE KEY-----\n"
+	"MIICXAIBAAKBgQCobzCF268K2sRp473gvBTT4AgSL1kjeF8N57vxS1P8zWrWMXNs\n"
+	"4LuH0NRZmKTajeboy0br8xw+smIy3AbaKAwWWZToesxebu3m9VeA8dqWyOaUMjox\n"
+	"AcgVYesgVaMpjRe7fcWdJnX1wJoVVPuIcO8ma+AAPByfTORbzpSTmXAQAwIDAQAB\n"
+	"AoGAP83u+aYghuIcaWhmM03MLf69z/WztKYSi/fu0BcS977w67bL3MC9CVPoPRB/\n"
+	"0nLSt/jZIuRzHKUCYfXLerSU7v0oXDTy6GPzWMh/oXIrpF0tYNbwWF7LSq2O2gGZ\n"
+	"XtA9MSmUNNJaKzQQeXjqdVFOY8A0Pho+k2KByBiCi+ChkcECQQDRUuyX0+PKJtA2\n"
+	"M36BOTFpy61BAv+JRlXUnHuevOfQWl6NR6YGygqCyH1sWtP1sa9S4wWys3DFH+5A\n"
+	"DkuAqk7zAkEAzf4eUH2hp5CIMsXH+WpIzKj09oY1it2CAKjVq4rUELf8iXvmGoFl\n"
+	"000spua4MjHNUYm7LR0QaKesKrMyGZUesQJAL8aLdYPJI+SD9Tr/jqLtIkZ4frQe\n"
+	"eshw4pvsoyheiHF3zyshO791crAr4EVCx3sMlxB1xnmqLXPCPyCEHxO//QJBAIBY\n"
+	"IYkjDZJ6ofGIe1UyXJNvfdkPu9J+ut4wU5jjEcgs6mK62J6RGuFxhy2iOQfFMdjo\n"
+	"yL+OCUg7mDCun7uCxrECQAtSvnLOFMjO5qExRjFtwi+b1rcSekd3Osk/izyRFSzg\n"
+	"Or+AL56/EKfiogNnFipgaXIbb/xj785Cob6v96XoW1I=\n"
+	"-----END RSA PRIVATE KEY-----\n";
+
+QCA::Cert *cert;
+QCA::RSAKey *privkey;
+
+using namespace XMPP;
+
+int id_num = 0;
+
+class Session : public QObject
+{
+	Q_OBJECT
+public:
+	Session(const QString &host, const QString &defRealm, ByteStream *bs) : QObject(0)
+	{
+		id = id_num++;
+
+		tls = new QCA::TLS;
+		tls->setCertificate(*cert, *privkey);
+
+		cs = new ClientStream(host, defRealm, bs, tls);
+		connect(cs, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
+		connect(cs, SIGNAL(error(int)), SLOT(cs_error(int)));
+	}
+
+	~Session()
+	{
+		delete cs;
+		delete tls;
+		printf("[%d]: deleted\n", id);
+	}
+
+	void start()
+	{
+		printf("[%d]: New session!\n", id);
+		cs->accept();
+	}
+
+signals:
+	void done();
+
+private slots:
+	void cs_connectionClosed()
+	{
+		printf("[%d]: Connection closed by peer\n", id);
+		done();
+	}
+
+	void cs_error(int)
+	{
+		printf("[%d]: Error\n", id);
+		done();
+	}
+
+private:
+	int id;
+	ClientStream *cs;
+	QCA::TLS *tls;
+};
+
+class ServerTest : public QServerSocket
+{
+	Q_OBJECT
+public:
+	enum { Idle, Handshaking, Active, Closing };
+
+	ServerTest(const QString &_host, int _port) : QServerSocket(_port), host(_host), port(_port)
+	{
+		cert = new QCA::Cert;
+		privkey = new QCA::RSAKey;
+
+		cert->fromPEM(pemdata_cert);
+		privkey->fromPEM(pemdata_privkey);
+
+		list.setAutoDelete(true);
+	}
+
+	~ServerTest()
+	{
+	}
+
+	void start()
+	{
+		char buf[256];
+		int r = gethostname(buf, sizeof(buf)-1);
+		if(r == -1) {
+			printf("Error getting hostname!\n");
+			QTimer::singleShot(0, this, SIGNAL(quit()));
+			return;
+		}
+		QString myhostname = buf;
+
+		realm = myhostname;
+		if(host.isEmpty())
+			host = myhostname;
+
+		if(cert->isNull() || privkey->isNull()) {
+			printf("Error loading cert and/or private key!\n");
+			QTimer::singleShot(0, this, SIGNAL(quit()));
+			return;
+		}
+		if(!ok()) {
+			printf("Error binding to port %d!\n", port);
+			QTimer::singleShot(0, this, SIGNAL(quit()));
+			return;
+		}
+		printf("Listening on %s:%d ...\n", host.latin1(), port);
+	}
+
+	void newConnection(int s)
+	{
+		BSocket *bs = new BSocket;
+		bs->setSocket(s);
+		Session *sess = new Session(host, realm, bs);
+		list.append(sess);
+		connect(sess, SIGNAL(done()), SLOT(sess_done()));
+		sess->start();
+	}
+
+signals:
+	void quit();
+
+private slots:
+	void sess_done()
+	{
+		Session *sess = (Session *)sender();
+		list.removeRef(sess);
+	}
+
+private:
+	QString host, realm;
+	int port;
+	QPtrList<Session> list;
+};
+
+#include"server.moc"
+
+int main(int argc, char **argv)
+{
+	QApplication app(argc, argv, false);
+	QString host = argc > 1 ? QString(argv[1]) : QString();
+	int port = argc > 2 ? QString(argv[2]).toInt() : 5222;
+
+	if(!QCA::isSupported(QCA::CAP_TLS)) {
+		printf("TLS not supported!\n");
+		return 1;
+	}
+
+	if(!QCA::isSupported(QCA::CAP_SASL)) {
+		printf("SASL not supported!\n");
+		return 1;
+	}
+
+	srand(time(NULL));
+
+	ServerTest *s = new ServerTest(host, port);
+	QObject::connect(s, SIGNAL(quit()), &app, SLOT(quit()));
+	s->start();
+	app.exec();
+	delete s;
+
+	// clean up
+	QCA::unloadAllPlugins();
+
+	return 0;
+}
Index: psi/tags/0.10-os2-b1/iris/example/server/server.pro
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/server/server.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/server/server.pro	(revision 38)
@@ -0,0 +1,15 @@
+TEMPLATE = app
+CONFIG += qt thread console
+TARGET = server
+
+MOC_DIR        = .moc
+OBJECTS_DIR    = .obj
+UI_DIR         = .ui
+
+include(../xmpptest/iris.pri)
+
+SOURCES += server.cpp
+
+# gentoo hack?
+LIBS += -lcrypto
+
Index: psi/tags/0.10-os2-b1/iris/example/xmpptest/iris.pri
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/xmpptest/iris.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/xmpptest/iris.pri	(revision 38)
@@ -0,0 +1,75 @@
+IRIS_BASE = ../..
+
+# include some of cutestuff
+CONFIG += cutestuff
+CS_BASE = ../../../cutestuff
+include(../../cs.pri)
+
+# libidn
+LIBIDN_BASE = $$IRIS_BASE/libidn
+CONFIG += libidn
+include(../../libidn.pri)
+
+# qca
+#unix:{
+#	INCLUDEPATH += /usr/local/include
+#	LIBS += -L/usr/local/lib -lqca
+#}
+#win32:{
+#	INCLUDEPATH += c:\local\include
+#	LIBS += c:\local\lib\qca.lib
+#}
+#
+#!exists(qcaprovider.h) {
+#	Q_PREFIX = ../../../qca/src
+#	INCLUDEPATH += $${Q_PREFIX}
+#}
+#HEADERS += $${Q_PREFIX}/qcaprovider.h
+
+INCLUDEPATH += ../../../qca/src
+HEADERS += ../../../qca/src/qca.h ../../../qca/src/qcaprovider.h
+SOURCES += ../../../qca/src/qca.cpp
+
+DEFINES += XMPP_TEST
+
+INCLUDEPATH += $$IRIS_BASE/include $$IRIS_BASE/xmpp-core $$IRIS_BASE/xmpp-im $$IRIS_BASE/jabber
+
+HEADERS += \
+	$$IRIS_BASE/xmpp-core/hash.h \
+	$$IRIS_BASE/xmpp-core/simplesasl.h \
+	$$IRIS_BASE/xmpp-core/securestream.h \
+	$$IRIS_BASE/xmpp-core/parser.h \
+	$$IRIS_BASE/xmpp-core/xmlprotocol.h \
+	$$IRIS_BASE/xmpp-core/protocol.h \
+	$$IRIS_BASE/xmpp-core/td.h \
+	$$IRIS_BASE/xmpp-im/xmpp_tasks.h \
+	$$IRIS_BASE/xmpp-im/xmpp_xmlcommon.h \
+	$$IRIS_BASE/xmpp-im/xmpp_vcard.h \
+	$$IRIS_BASE/jabber/s5b.h \
+	$$IRIS_BASE/jabber/xmpp_ibb.h \
+	$$IRIS_BASE/jabber/xmpp_jidlink.h \
+	$$IRIS_BASE/jabber/filetransfer.h \
+	$$IRIS_BASE/include/xmpp.h \
+	$$IRIS_BASE/include/im.h
+
+SOURCES += \
+	$$IRIS_BASE/xmpp-core/connector.cpp \
+	$$IRIS_BASE/xmpp-core/tlshandler.cpp \
+	$$IRIS_BASE/xmpp-core/jid.cpp \
+	$$IRIS_BASE/xmpp-core/hash.cpp \
+	$$IRIS_BASE/xmpp-core/simplesasl.cpp \
+	$$IRIS_BASE/xmpp-core/securestream.cpp \
+	$$IRIS_BASE/xmpp-core/parser.cpp \
+	$$IRIS_BASE/xmpp-core/xmlprotocol.cpp \
+	$$IRIS_BASE/xmpp-core/protocol.cpp \
+	$$IRIS_BASE/xmpp-core/stream.cpp \
+	$$IRIS_BASE/xmpp-im/types.cpp \
+	$$IRIS_BASE/xmpp-im/client.cpp \
+	$$IRIS_BASE/xmpp-im/xmpp_tasks.cpp \
+	$$IRIS_BASE/xmpp-im/xmpp_xmlcommon.cpp \
+	$$IRIS_BASE/xmpp-im/xmpp_vcard.cpp \
+	$$IRIS_BASE/jabber/s5b.cpp \
+	$$IRIS_BASE/jabber/xmpp_ibb.cpp \
+	$$IRIS_BASE/jabber/xmpp_jidlink.cpp \
+	$$IRIS_BASE/jabber/filetransfer.cpp
+
Index: psi/tags/0.10-os2-b1/iris/example/xmpptest/ui_test.ui
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/xmpptest/ui_test.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/xmpptest/ui_test.ui	(revision 38)
@@ -0,0 +1,569 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>TestUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>TestUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>652</width>
+            <height>591</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Form1</string>
+    </property>
+    <hbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QTabWidget">
+            <property name="name">
+                <cstring>tb_main</cstring>
+            </property>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Core</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QGroupBox">
+                        <property name="name">
+                            <cstring>gb_server</cstring>
+                        </property>
+                        <property name="title">
+                            <string>Server</string>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout3</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QLabel">
+                                        <property name="name">
+                                            <cstring>textLabel1</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Full JID:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit">
+                                        <property name="name">
+                                            <cstring>le_jid</cstring>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout12</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QLabel">
+                                        <property name="name">
+                                            <cstring>textLabel5_2</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>User (if needed):</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit">
+                                        <property name="name">
+                                            <cstring>le_user</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string></string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLabel">
+                                        <property name="name">
+                                            <cstring>textLabel7_2</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Pass:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit">
+                                        <property name="name">
+                                            <cstring>le_pass</cstring>
+                                        </property>
+                                        <property name="echoMode">
+                                            <enum>Password</enum>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout2</cstring>
+                                </property>
+                                <grid>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QCheckBox" row="1" column="2">
+                                        <property name="name">
+                                            <cstring>ck_ssl</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>SSL</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QComboBox" row="2" column="1" rowspan="1" colspan="2">
+                                        <item>
+                                            <property name="text">
+                                                <string>None</string>
+                                            </property>
+                                        </item>
+                                        <item>
+                                            <property name="text">
+                                                <string>HTTP(S)</string>
+                                            </property>
+                                        </item>
+                                        <item>
+                                            <property name="text">
+                                                <string>SOCKS5</string>
+                                            </property>
+                                        </item>
+                                        <item>
+                                            <property name="text">
+                                                <string>HTTP Polling</string>
+                                            </property>
+                                        </item>
+                                        <property name="name">
+                                            <cstring>cb_proxy</cstring>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLabel" row="1" column="0">
+                                        <property name="name">
+                                            <cstring>lb_host</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Host:Port:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+                                        <property name="name">
+                                            <cstring>ck_probe</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Legacy SSL probe</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLabel" row="2" column="0">
+                                        <property name="name">
+                                            <cstring>textLabel4</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Proxy:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit" row="1" column="1">
+                                        <property name="name">
+                                            <cstring>le_host</cstring>
+                                        </property>
+                                    </widget>
+                                </grid>
+                            </widget>
+                            <widget class="QGroupBox">
+                                <property name="name">
+                                    <cstring>gb_proxy</cstring>
+                                </property>
+                                <property name="title">
+                                    <string>Proxy Settings</string>
+                                </property>
+                                <grid>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QLabel" row="0" column="0">
+                                        <property name="name">
+                                            <cstring>textLabel2_2</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Host:Port:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit" row="0" column="1" rowspan="1" colspan="3">
+                                        <property name="name">
+                                            <cstring>le_proxyhost</cstring>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLabel" row="1" column="0">
+                                        <property name="name">
+                                            <cstring>textLabel5</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>User/Pass:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit" row="1" column="1">
+                                        <property name="name">
+                                            <cstring>le_proxyuser</cstring>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLabel" row="1" column="2">
+                                        <property name="name">
+                                            <cstring>textLabel7</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>/</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit" row="1" column="3">
+                                        <property name="name">
+                                            <cstring>le_proxypass</cstring>
+                                        </property>
+                                        <property name="echoMode">
+                                            <enum>Password</enum>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLabel" row="2" column="0">
+                                        <property name="name">
+                                            <cstring>lb_proxyurl</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Polling URL:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit" row="2" column="1" rowspan="1" colspan="3">
+                                        <property name="name">
+                                            <cstring>le_proxyurl</cstring>
+                                        </property>
+                                    </widget>
+                                </grid>
+                            </widget>
+                            <widget class="QGroupBox">
+                                <property name="name">
+                                    <cstring>gb_security</cstring>
+                                </property>
+                                <property name="title">
+                                    <string>Security Settings</string>
+                                </property>
+                                <vbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QCheckBox">
+                                        <property name="name">
+                                            <cstring>ck_plain</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Allow plaintext login</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QCheckBox">
+                                        <property name="name">
+                                            <cstring>ck_mutual</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Require mutual authentication</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLayoutWidget">
+                                        <property name="name">
+                                            <cstring>layout8</cstring>
+                                        </property>
+                                        <hbox>
+                                            <property name="name">
+                                                <cstring>unnamed</cstring>
+                                            </property>
+                                            <widget class="QLabel">
+                                                <property name="name">
+                                                    <cstring>textLabel1_2</cstring>
+                                                </property>
+                                                <property name="text">
+                                                    <string>SASL SSF min/max:</string>
+                                                </property>
+                                            </widget>
+                                            <widget class="QSpinBox">
+                                                <property name="name">
+                                                    <cstring>sb_ssfmin</cstring>
+                                                </property>
+                                            </widget>
+                                            <widget class="QLabel">
+                                                <property name="name">
+                                                    <cstring>textLabel2</cstring>
+                                                </property>
+                                                <property name="text">
+                                                    <string>/</string>
+                                                </property>
+                                            </widget>
+                                            <widget class="QSpinBox">
+                                                <property name="name">
+                                                    <cstring>sb_ssfmax</cstring>
+                                                </property>
+                                            </widget>
+                                        </hbox>
+                                    </widget>
+                                </vbox>
+                            </widget>
+                        </vbox>
+                    </widget>
+                    <widget class="QLayoutWidget">
+                        <property name="name">
+                            <cstring>layout10</cstring>
+                        </property>
+                        <hbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QPushButton">
+                                <property name="name">
+                                    <cstring>pb_go</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>&amp;Connect</string>
+                                </property>
+                                <property name="accel">
+                                    <string>Alt+C</string>
+                                </property>
+                            </widget>
+                            <spacer>
+                                <property name="name">
+                                    <cstring>spacer5</cstring>
+                                </property>
+                                <property name="orientation">
+                                    <enum>Horizontal</enum>
+                                </property>
+                                <property name="sizeType">
+                                    <enum>Expanding</enum>
+                                </property>
+                                <property name="sizeHint">
+                                    <size>
+                                        <width>130</width>
+                                        <height>20</height>
+                                    </size>
+                                </property>
+                            </spacer>
+                            <widget class="QPushButton">
+                                <property name="name">
+                                    <cstring>pb_about</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>&amp;About</string>
+                                </property>
+                            </widget>
+                        </hbox>
+                    </widget>
+                    <spacer>
+                        <property name="name">
+                            <cstring>spacer3_2</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>20</width>
+                                <height>16</height>
+                            </size>
+                        </property>
+                    </spacer>
+                    <widget class="QGroupBox">
+                        <property name="name">
+                            <cstring>groupBox3</cstring>
+                        </property>
+                        <property name="title">
+                            <string>Quick XML &gt;&gt;</string>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout8</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QLabel">
+                                        <property name="name">
+                                            <cstring>textLabel8</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>To:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit">
+                                        <property name="name">
+                                            <cstring>le_to</cstring>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout9</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QPushButton">
+                                        <property name="name">
+                                            <cstring>pb_im</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>&amp;IM Session</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QPushButton">
+                                        <property name="name">
+                                            <cstring>pb_msg</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>&amp;Message</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QPushButton">
+                                        <property name="name">
+                                            <cstring>pb_iqv</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>IQ &amp;Version</string>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                        </vbox>
+                    </widget>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>IM</string>
+                </attribute>
+            </widget>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout8</cstring>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QTextEdit">
+                    <property name="name">
+                        <cstring>te_log</cstring>
+                    </property>
+                    <property name="minimumSize">
+                        <size>
+                            <width>300</width>
+                            <height>0</height>
+                        </size>
+                    </property>
+                    <property name="wrapPolicy">
+                        <enum>AtWordOrDocumentBoundary</enum>
+                    </property>
+                    <property name="readOnly">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+                <widget class="QTextEdit">
+                    <property name="name">
+                        <cstring>te_input</cstring>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>32767</width>
+                            <height>200</height>
+                        </size>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout7</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <spacer>
+                            <property name="name">
+                                <cstring>spacer3</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Horizontal</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>40</width>
+                                    <height>20</height>
+                                </size>
+                            </property>
+                        </spacer>
+                        <widget class="QPushButton">
+                            <property name="name">
+                                <cstring>pb_send</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Send</string>
+                            </property>
+                            <property name="accel">
+                                <string>Alt+S</string>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+            </vbox>
+        </widget>
+    </hbox>
+</widget>
+<tabstops>
+    <tabstop>le_jid</tabstop>
+    <tabstop>le_user</tabstop>
+    <tabstop>le_pass</tabstop>
+    <tabstop>ck_probe</tabstop>
+    <tabstop>le_host</tabstop>
+    <tabstop>ck_ssl</tabstop>
+    <tabstop>cb_proxy</tabstop>
+    <tabstop>le_proxyhost</tabstop>
+    <tabstop>le_proxyuser</tabstop>
+    <tabstop>le_proxypass</tabstop>
+    <tabstop>le_proxyurl</tabstop>
+    <tabstop>ck_plain</tabstop>
+    <tabstop>ck_mutual</tabstop>
+    <tabstop>sb_ssfmin</tabstop>
+    <tabstop>sb_ssfmax</tabstop>
+    <tabstop>pb_go</tabstop>
+    <tabstop>pb_about</tabstop>
+    <tabstop>le_to</tabstop>
+    <tabstop>pb_im</tabstop>
+    <tabstop>pb_msg</tabstop>
+    <tabstop>pb_iqv</tabstop>
+    <tabstop>te_input</tabstop>
+    <tabstop>pb_send</tabstop>
+    <tabstop>te_log</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/iris/example/xmpptest/xmpptest.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/xmpptest/xmpptest.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/xmpptest/xmpptest.cpp	(revision 38)
@@ -0,0 +1,898 @@
+#include<qapplication.h>
+#include<qtextedit.h>
+#include<qgroupbox.h>
+#include<qlineedit.h>
+#include<qlabel.h>
+#include<qcheckbox.h>
+#include<qtextedit.h>
+#include<qcombobox.h>
+#include<qpushbutton.h>
+#include<qmessagebox.h>
+#include<qinputdialog.h>
+#include<qspinbox.h>
+#include<qtimer.h>
+#include<qmenubar.h>
+#include<qpopupmenu.h>
+#include<qtabwidget.h>
+#include<qca.h>
+//#include<iris/xmpp.h>
+#include"xmpp.h"
+#include"im.h"
+
+#include<stdlib.h>
+#include<time.h>
+
+#include"ui_test.h"
+
+#include<stdio.h>
+
+#define AppName "xmpptest"
+
+static QString plain2rich(const QString &plain)
+{
+	QString rich;
+	int col = 0;
+
+	for(int i = 0; i < (int)plain.length(); ++i) {
+		if(plain[i] == '\n') {
+			rich += "<br>";
+			col = 0;
+		}
+		else if(plain[i] == '\t') {
+			rich += QChar::nbsp;
+			while(col % 4) {
+				rich += QChar::nbsp;
+				++col;
+			}
+		}
+		else if(plain[i].isSpace()) {
+			if(i > 0 && plain[i-1] == ' ')
+				rich += QChar::nbsp;
+			else
+				rich += ' ';
+		}
+		else if(plain[i] == '<')
+			rich += "&lt;";
+		else if(plain[i] == '>')
+			rich += "&gt;";
+		else if(plain[i] == '\"')
+			rich += "&quot;";
+		else if(plain[i] == '\'')
+			rich += "&apos;";
+		else if(plain[i] == '&')
+			rich += "&amp;";
+		else
+			rich += plain[i];
+		++col;
+	}
+
+	return rich;
+}
+
+/*static void showCertInfo(const QCA::Cert &cert)
+{
+	fprintf(stderr, "-- Cert --\n");
+	fprintf(stderr, " CN: %s\n", cert.subject()["CN"].latin1());
+	fprintf(stderr, " Valid from: %s, until %s\n",
+		cert.notBefore().toString().latin1(),
+		cert.notAfter().toString().latin1());
+	fprintf(stderr, " PEM:\n%s\n", cert.toPEM().latin1());
+}*/
+
+static QString resultToString(int result)
+{
+	QString s;
+	switch(result) {
+		case QCA::TLS::NoCert:
+			s = QObject::tr("No certificate presented.");
+			break;
+		case QCA::TLS::Valid:
+			break;
+		case QCA::TLS::HostMismatch:
+			s = QObject::tr("Hostname mismatch.");
+			break;
+		case QCA::TLS::Rejected:
+			s = QObject::tr("Root CA rejects the specified purpose.");
+			break;
+		case QCA::TLS::Untrusted:
+			s = QObject::tr("Not trusted for the specified purpose.");
+			break;
+		case QCA::TLS::SignatureFailed:
+			s = QObject::tr("Invalid signature.");
+			break;
+		case QCA::TLS::InvalidCA:
+			s = QObject::tr("Invalid CA certificate.");
+			break;
+		case QCA::TLS::InvalidPurpose:
+			s = QObject::tr("Invalid certificate purpose.");
+			break;
+		case QCA::TLS::SelfSigned:
+			s = QObject::tr("Certificate is self-signed.");
+			break;
+		case QCA::TLS::Revoked:
+			s = QObject::tr("Certificate has been revoked.");
+			break;
+		case QCA::TLS::PathLengthExceeded:
+			s = QObject::tr("Maximum cert chain length exceeded.");
+			break;
+		case QCA::TLS::Expired:
+			s = QObject::tr("Certificate has expired.");
+			break;
+		case QCA::TLS::Unknown:
+		default:
+			s = QObject::tr("General validation error.");
+			break;
+	}
+	return s;
+}
+
+class TestDebug : public XMPP::Debug
+{
+public:
+	void msg(const QString &);
+	void outgoingTag(const QString &);
+	void incomingTag(const QString &);
+	void outgoingXml(const QDomElement &);
+	void incomingXml(const QDomElement &);
+};
+
+class TestDlg : public TestUI
+{
+	Q_OBJECT
+public:
+	bool active, connected;
+	XMPP::AdvancedConnector *conn;
+	QCA::TLS *tls;
+	XMPP::QCATLSHandler *tlsHandler;
+	XMPP::ClientStream *stream;
+	XMPP::Jid jid;
+
+	TestDlg(QWidget *parent=0, const char *name=0) : TestUI(parent, name)
+	{
+		setCaption(tr("XMPP Test"));
+
+		connect(ck_probe, SIGNAL(toggled(bool)), SLOT(probe_toggled(bool)));
+		connect(cb_proxy, SIGNAL(activated(int)), SLOT(proxy_activated(int)));
+		connect(pb_go, SIGNAL(clicked()), SLOT(go()));
+		connect(pb_send, SIGNAL(clicked()), SLOT(send()));
+		connect(pb_im, SIGNAL(clicked()), SLOT(sc_im()));
+		connect(pb_msg, SIGNAL(clicked()), SLOT(sc_msg()));
+		connect(pb_iqv, SIGNAL(clicked()), SLOT(sc_iqv()));
+		connect(pb_about, SIGNAL(clicked()), SLOT(about()));
+
+		sb_ssfmin->setMinValue(0);
+		sb_ssfmin->setMaxValue(256);
+		sb_ssfmax->setMinValue(0);
+		sb_ssfmax->setMaxValue(256);
+
+		pb_send->setEnabled(false);
+		proxy_activated(0);
+		ck_probe->setChecked(true);
+		ck_mutual->setChecked(true);
+		pb_go->setText(tr("&Connect"));
+
+		//le_jid->setText("psitest@jabberd.jabberstudio.org/Test");
+		//ck_probe->setChecked(false);
+		//le_host->setText("jabberd.jabberstudio.org:15222");
+		//ck_mutual->setChecked(false);
+		//le_jid->setText("sasltest@e.jabber.ru/Test");
+		//le_jid->setText("psitest@jabber.cz/Test");
+		//le_pass->setText("psitest");
+		//cb_proxy->setCurrentItem(3);
+		//le_proxyurl->setText("http://connect.jabber.cz/");
+
+		// setup xmpp
+		conn = new XMPP::AdvancedConnector;
+		connect(conn, SIGNAL(srvLookup(const QString &)), SLOT(conn_srvLookup(const QString &)));
+		connect(conn, SIGNAL(srvResult(bool)), SLOT(conn_srvResult(bool)));
+		connect(conn, SIGNAL(httpSyncStarted()), SLOT(conn_httpSyncStarted()));
+		connect(conn, SIGNAL(httpSyncFinished()), SLOT(conn_httpSyncFinished()));
+
+		if(QCA::isSupported(QCA::CAP_TLS)) {
+			tls = new QCA::TLS;
+			tlsHandler = new XMPP::QCATLSHandler(tls);
+			connect(tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
+		}
+		else {
+			tls = 0;
+			tlsHandler = 0;
+		}
+
+		stream = new XMPP::ClientStream(conn, tlsHandler);
+		connect(stream, SIGNAL(connected()), SLOT(cs_connected()));
+		connect(stream, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated(int)));
+		connect(stream, SIGNAL(needAuthParams(bool, bool, bool)), SLOT(cs_needAuthParams(bool, bool, bool)));
+		connect(stream, SIGNAL(authenticated()), SLOT(cs_authenticated()));
+		connect(stream, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
+		connect(stream, SIGNAL(delayedCloseFinished()), SLOT(cs_delayedCloseFinished()));
+		connect(stream, SIGNAL(readyRead()), SLOT(cs_readyRead()));
+		connect(stream, SIGNAL(stanzaWritten()), SLOT(cs_stanzaWritten()));
+		connect(stream, SIGNAL(warning(int)), SLOT(cs_warning(int)));
+		connect(stream, SIGNAL(error(int)), SLOT(cs_error(int)));
+
+		QTimer::singleShot(0, this, SLOT(adjustLayout()));
+
+		le_jid->setFocus();
+		active = false;
+		connected = false;
+	}
+
+	~TestDlg()
+	{
+		delete stream;
+		delete tls; // this destroys the TLSHandler also
+		delete conn;
+	}
+
+private slots:
+	void adjustLayout()
+	{
+		tb_main->setFixedWidth(tb_main->minimumSizeHint().width());
+		resize(minimumSizeHint());
+		show();
+	}
+
+	void about()
+	{
+		QMessageBox::about(this, tr("About %1").arg(AppName), tr(
+			"%1 v1.0\n"
+			"\n"
+			"Utility to demonstrate the Iris XMPP library.\n"
+			"\n"
+			"Currently supports:\n"
+			"  draft-ietf-xmpp-core-21\n"
+			"  JEP-0025\n"
+			"\n"
+			"Copyright (C) 2003 Justin Karneges").arg(AppName));
+	}
+
+	void probe_toggled(bool)
+	{
+		setHostState();
+	}
+
+	void proxy_activated(int x)
+	{
+		bool ok = (x != 0);
+		bool okpoll = (x == 3);
+		gb_proxy->setEnabled(ok);
+		lb_proxyurl->setEnabled(okpoll);
+		le_proxyurl->setEnabled(okpoll);
+		ck_probe->setEnabled(!okpoll);
+		setHostState();
+	}
+
+	void cleanup()
+	{
+		pb_send->setEnabled(false);
+		pb_go->setEnabled(true);
+		pb_go->setText(tr("&Connect"));
+		pb_go->setFocus();
+		gb_server->setEnabled(true);
+		active = false;
+		connected = false;
+	}
+
+	void start()
+	{
+		if(active)
+			return;
+
+		jid = XMPP::Jid(le_jid->text());
+		if(jid.domain().isEmpty() || jid.node().isEmpty() || jid.resource().isEmpty()) {
+			QMessageBox::information(this, tr("Error"), tr("Please enter the Full JID to connect with."));
+			return;
+		}
+
+		int p = cb_proxy->currentItem();
+		XMPP::AdvancedConnector::Proxy proxy;
+		if(p > 0) {
+			QString s = le_proxyhost->text();
+			QString url = le_proxyurl->text();
+			if(p != 3 && s.isEmpty()) {
+				QMessageBox::information(this, tr("Error"), tr("You must specify a host:port for the proxy."));
+				return;
+			}
+			if(p == 3 && s.isEmpty() && url.isEmpty()) {
+				QMessageBox::information(this, tr("Error"), tr("You must at least enter a URL to use http poll."));
+				return;
+			}
+			QString host;
+			int port = 0;
+			if(!s.isEmpty()) {
+				int n = s.find(':');
+				if(n == -1) {
+					QMessageBox::information(this, tr("Error"), tr("Please enter the proxy host in the form 'host:port'."));
+					return;
+				}
+				host = s.mid(0, n);
+				port = s.mid(n+1).toInt();
+			}
+			if(p == 1)
+				proxy.setHttpConnect(host, port);
+			else if(p == 2)
+				proxy.setSocks(host, port);
+			else if(p == 3) {
+				proxy.setHttpPoll(host, port, url);
+				proxy.setPollInterval(2); // fast during login
+			}
+			proxy.setUserPass(le_proxyuser->text(), le_proxypass->text());
+		}
+		bool probe = (p != 3 && ck_probe->isChecked());
+		bool useHost = (!probe && !le_host->text().isEmpty());
+		QString host;
+		int port = 0;
+		bool ssl = false;
+		if(useHost) {
+			QString s = le_host->text();
+			int n = s.find(':');
+			if(n == -1) {
+				QMessageBox::information(this, tr("Error"), tr("Please enter the host in the form 'host:port'."));
+				return;
+			}
+			host = s.mid(0, n);
+			port = s.mid(n+1).toInt();
+
+			if(ck_ssl->isChecked())
+				ssl = true;
+		}
+		if(sb_ssfmin->value() > sb_ssfmax->value()) {
+			QMessageBox::information(this, tr("Error"), tr("Error: SSF Min is greater than SSF Max."));
+			return;
+		}
+
+		if((probe || ssl) && !tls) {
+			QMessageBox::information(this, tr("Error"), tr("Error: TLS not available.  Disable any TLS options."));
+			return;
+		}
+
+		// prepare
+		conn->setProxy(proxy);
+		if(useHost)
+			conn->setOptHostPort(host, port);
+		else
+			conn->setOptHostPort("", 0);
+		conn->setOptProbe(probe);
+		conn->setOptSSL(ssl);
+
+		if(tls) {
+			QPtrList<QCA::Cert> certStore;
+			tls->setCertificateStore(certStore);
+		}
+
+		stream->setNoopTime(55000); // every 55 seconds
+		stream->setAllowPlain(ck_plain->isChecked());
+		stream->setRequireMutualAuth(ck_mutual->isChecked());
+		stream->setSSFRange(sb_ssfmin->value(), sb_ssfmax->value());
+
+		gb_server->setEnabled(false);
+		pb_go->setText(tr("&Disconnect"));
+		pb_go->setFocus();
+		active = true;
+
+		appendSysMsg("Connecting...");
+		stream->connectToServer(jid);
+	}
+
+	void stop()
+	{
+		if(!active)
+			return;
+
+		if(connected) {
+			pb_go->setEnabled(false);
+			appendSysMsg("Disconnecting...");
+			stream->close();
+		}
+		else {
+			stream->close();
+			appendSysMsg("Disconnected");
+			cleanup();
+		}
+	}
+
+	void go()
+	{
+		if(active)
+			stop();
+		else
+			start();
+	}
+
+	void send()
+	{
+		if(te_input->text().isEmpty())
+			return;
+
+		// construct a "temporary" document to parse the input
+		QString str = "<stream xmlns=\"jabber:client\">\n";
+		str += te_input->text() + '\n';
+		str += "</stream>";
+
+		QDomDocument doc;
+		QString errMsg;
+		int errLine, errCol;
+		if(!doc.setContent(str, true, &errMsg, &errLine, &errCol)) {
+			int lines = QStringList::split('\n', str, true).count();
+			--errLine; // skip the first line
+			if(errLine == lines-1) {
+				errLine = lines-2;
+				errCol = te_input->paragraphLength(errLine-1)+1;
+				errMsg = "incomplete input";
+			}
+			te_input->setCursorPosition(errLine-1, errCol-1);
+			QMessageBox::information(this, tr("Error"), tr("Bad XML input (%1,%2): %3\nPlease correct and try again.").arg(errCol).arg(errLine).arg(errMsg));
+			return;
+		}
+		QDomElement e = doc.firstChild().toElement();
+
+		int num = 0;
+		QDomNodeList nl = e.childNodes();
+		QValueList<XMPP::Stanza> stanzaList;
+		for(uint x = 0; x < nl.count(); ++x) {
+			QDomNode n = nl.item(x);
+			if(n.isElement()) {
+				QDomElement e = n.toElement();
+				XMPP::Stanza s = stream->createStanza(e);
+				if(s.isNull()) {
+					QMessageBox::information(this, tr("Error"), tr("Bad Stanza '%1'.  Must be 'message', 'presence', or 'iq'").arg(e.tagName()));
+					return;
+				}
+				stanzaList += s;
+				++num;
+			}
+		}
+		if(num == 0) {
+			QMessageBox::information(this, tr("Error"), tr("You must enter at least one stanza!"));
+			return;
+		}
+
+		// out the door
+		for(QValueList<XMPP::Stanza>::ConstIterator it = stanzaList.begin(); it != stanzaList.end(); ++it) {
+			appendXmlOut(XMPP::Stream::xmlToString((*it).element(), true));
+			stream->write(*it);
+		}
+
+		te_input->setText("");
+	}
+
+	void sc_im()
+	{
+		/*XMPP::Message m("justin@andbit.net/Psi");
+		m.setSubject("Hi");
+		m.setBody("I send you this in order to have your advice.");
+		m.setBody("Escucha lechuga!", "es");
+		XMPP::Stanza stanza = m.toStanza(stream);
+		QString str = stanza.toString();
+		printf("[%s]\n", str.latin1());
+
+		XMPP::Message n;
+		n.fromStanza(stanza);
+		printf("subject: [%s]\n", n.subject().latin1());
+		printf("body: [%s]\n", n.body().latin1());
+		printf("body-es: [%s]\n", n.body("es").latin1());*/
+
+		QString s;
+		s += "<iq type='set' id='sess_1'>\n";
+		s += " <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>\n";
+		s += "</iq>";
+		te_input->setText(s);
+		te_input->setFocus();
+	}
+
+	void sc_msg()
+	{
+		QString to = le_to->text();
+		QString s;
+		if(!to.isEmpty())
+			s += QString("<message to=\"%1\">\n").arg(to);
+		else
+			s += QString("<message to=\"\">\n");
+		s += " <body>hello world</body>\n";
+		s += "</message>";
+		te_input->setText(s);
+		if(!to.isEmpty()) {
+			te_input->setCursorPosition(1, 7);
+			te_input->setSelection(1, 7, 1, 18);
+		}
+		else
+			te_input->setCursorPosition(0, 13);
+		te_input->setFocus();
+	}
+
+	void sc_iqv()
+	{
+		QString to = le_to->text();
+		QString s;
+		if(!to.isEmpty())
+			s += QString("<iq to=\"%1\" type=\"get\" id=\"abcde\">\n").arg(to);
+		else
+			s += QString("<iq to=\"\" type=\"get\" id=\"abcde\">\n");
+		s += " <query xmlns=\"jabber:iq:version\"/>\n";
+		s += "</iq>";
+		te_input->setText(s);
+		if(!to.isEmpty()) {
+			te_input->setCursorPosition(0, 8);
+			te_input->setSelection(0, 8, 0, 8 + to.length());
+		}
+		else
+			te_input->setCursorPosition(0, 8);
+		te_input->setFocus();
+	}
+
+	void conn_srvLookup(const QString &server)
+	{
+		appendLibMsg(QString("SRV lookup on [%1]").arg(server));
+	}
+
+	void conn_srvResult(bool b)
+	{
+		if(b)
+			appendLibMsg("SRV lookup success!");
+		else
+			appendLibMsg("SRV lookup failed");
+	}
+
+	void conn_httpSyncStarted()
+	{
+		appendLibMsg("HttpPoll: syncing");
+	}
+
+	void conn_httpSyncFinished()
+	{
+		appendLibMsg("HttpPoll: done");
+	}
+
+	void tls_handshaken()
+	{
+		QCA::Cert cert = tls->peerCertificate();
+		int vr = tls->certificateValidityResult();
+
+		appendSysMsg("Successful TLS handshake.");
+		if(vr == QCA::TLS::Valid)
+			appendSysMsg("Valid certificate.");
+		else {
+			appendSysMsg(QString("Invalid certificate: %1").arg(resultToString(vr)), Qt::red);
+			appendSysMsg("Continuing anyway");
+		}
+
+		tlsHandler->continueAfterHandshake();
+	}
+
+	void cs_connected()
+	{
+		QString s = "Connected";
+		if(conn->havePeerAddress())
+			s += QString(" (%1:%2)").arg(conn->peerAddress().toString()).arg(conn->peerPort());
+		if(conn->useSSL())
+			s += " [ssl]";
+		appendSysMsg(s);
+	}
+
+	void cs_securityLayerActivated(int type)
+	{
+		appendSysMsg(QString("Security layer activated (%1)").arg((type == XMPP::ClientStream::LayerTLS) ? "TLS": "SASL"));
+	}
+
+	void cs_needAuthParams(bool user, bool pass, bool realm)
+	{
+		QString s = "Need auth parameters -";
+		if(user)
+			s += " (Username)";
+		if(pass)
+			s += " (Password)";
+		if(realm)
+			s += " (Realm)";
+		appendSysMsg(s);
+
+		if(user) {
+			if(!le_user->text().isEmpty())
+				stream->setUsername(le_user->text());
+			else
+				stream->setUsername(jid.node());
+		}
+		if(pass) {
+			if(!le_pass->text().isEmpty())
+				stream->setPassword(le_pass->text());
+			else {
+				conn->changePollInterval(10); // slow down during prompt
+				bool ok;
+				QString s = QInputDialog::getText(tr("Password"), tr("Enter the password for %1").arg(jid.full()), QLineEdit::Password, QString::null, &ok, this);
+				if(!ok) {
+					stop();
+					return;
+				}
+				stream->setPassword(s);
+
+				conn->changePollInterval(2); // resume speed
+			}
+		}
+		if(realm)
+			stream->setRealm(jid.domain());
+
+		stream->continueAfterParams();
+	}
+
+	void cs_authenticated()
+	{
+		connected = true;
+		pb_send->setEnabled(true);
+		conn->changePollInterval(10); // slow down after login
+		appendSysMsg("Authenticated");
+	}
+
+	void cs_connectionClosed()
+	{
+		appendSysMsg("Disconnected by peer");
+		cleanup();
+	}
+
+	void cs_delayedCloseFinished()
+	{
+		appendSysMsg("Disconnected");
+		cleanup();
+	}
+
+	void cs_readyRead()
+	{
+		while(stream->stanzaAvailable()) {
+			XMPP::Stanza s = stream->read();
+			appendXmlIn(XMPP::Stream::xmlToString(s.element(), true));
+		}
+	}
+
+	void cs_stanzaWritten()
+	{
+		appendSysMsg("Stanza sent");
+	}
+
+	void cs_warning(int warn)
+	{
+		if(warn == XMPP::ClientStream::WarnOldVersion) {
+			appendSysMsg("Warning: pre-1.0 protocol server", Qt::red);
+		}
+		else if(warn == XMPP::ClientStream::WarnNoTLS) {
+			appendSysMsg("Warning: TLS not available!", Qt::red);
+		}
+		stream->continueAfterWarning();
+	}
+
+	void cs_error(int err)
+	{
+		if(err == XMPP::ClientStream::ErrParse) {
+			appendErrMsg("XML parsing error");
+		}
+		else if(err == XMPP::ClientStream::ErrProtocol) {
+			appendErrMsg("XMPP protocol error");
+		}
+		else if(err == XMPP::ClientStream::ErrStream) {
+			int x = stream->errorCondition();
+			QString s;
+			if(x == XMPP::Stream::GenericStreamError)
+				s = "generic stream error";
+			else if(x == XMPP::ClientStream::Conflict)
+				s = "conflict (remote login replacing this one)";
+			else if(x == XMPP::ClientStream::ConnectionTimeout)
+				s = "timed out from inactivity";
+			else if(x == XMPP::ClientStream::InternalServerError)
+				s = "internal server error";
+			else if(x == XMPP::ClientStream::InvalidFrom)
+				s = "invalid from address";
+			else if(x == XMPP::ClientStream::InvalidXml)
+				s = "invalid XML";
+			else if(x == XMPP::ClientStream::PolicyViolation)
+				s = "policy violation.  go to jail!";
+			else if(x == XMPP::ClientStream::ResourceConstraint)
+				s = "server out of resources";
+			else if(x == XMPP::ClientStream::SystemShutdown)
+				s = "system is shutting down NOW";
+			appendErrMsg(QString("XMPP stream error: %1").arg(s));
+		}
+		else if(err == XMPP::ClientStream::ErrConnection) {
+			int x = conn->errorCode();
+			QString s;
+			if(x == XMPP::AdvancedConnector::ErrConnectionRefused)
+				s = "unable to connect to server";
+			else if(x == XMPP::AdvancedConnector::ErrHostNotFound)
+				s = "host not found";
+			else if(x == XMPP::AdvancedConnector::ErrProxyConnect)
+				s = "proxy connect";
+			else if(x == XMPP::AdvancedConnector::ErrProxyNeg)
+				s = "proxy negotiating";
+			else if(x == XMPP::AdvancedConnector::ErrProxyAuth)
+				s = "proxy authorization";
+			else if(x == XMPP::AdvancedConnector::ErrStream)
+				s = "stream error";
+			appendErrMsg(QString("Connection error: %1").arg(s));
+		}
+		else if(err == XMPP::ClientStream::ErrNeg) {
+			int x = stream->errorCondition();
+			QString s;
+			if(x == XMPP::ClientStream::HostGone)
+				s = "host no longer hosted";
+			else if(x == XMPP::ClientStream::HostUnknown)
+				s = "host unknown";
+			else if(x == XMPP::ClientStream::RemoteConnectionFailed)
+				s = "a required remote connection failed";
+			else if(x == XMPP::ClientStream::SeeOtherHost)
+				s = QString("see other host: [%1]").arg(stream->errorText());
+			else if(x == XMPP::ClientStream::UnsupportedVersion)
+				s = "server does not support proper xmpp version";
+			appendErrMsg(QString("Stream negotiation error: %1").arg(s));
+		}
+		else if(err == XMPP::ClientStream::ErrTLS) {
+			int x = stream->errorCondition();
+			QString s;
+			if(x == XMPP::ClientStream::TLSStart)
+				s = "server rejected STARTTLS";
+			else if(x == XMPP::ClientStream::TLSFail) {
+				int t = tlsHandler->tlsError();
+				if(t == QCA::TLS::ErrHandshake)
+					s = "TLS handshake error";
+				else
+					s = "broken security layer (TLS)";
+			}
+			appendErrMsg(s);
+		}
+		else if(err == XMPP::ClientStream::ErrAuth) {
+			int x = stream->errorCondition();
+			QString s;
+			if(x == XMPP::ClientStream::GenericAuthError)
+				s = "unable to login";
+			else if(x == XMPP::ClientStream::NoMech)
+				s = "no appropriate auth mechanism available for given security settings";
+			else if(x == XMPP::ClientStream::BadProto)
+				s = "bad server response";
+			else if(x == XMPP::ClientStream::BadServ)
+				s = "server failed mutual authentication";
+			else if(x == XMPP::ClientStream::EncryptionRequired)
+				s = "encryption required for chosen SASL mechanism";
+			else if(x == XMPP::ClientStream::InvalidAuthzid)
+				s = "invalid authzid";
+			else if(x == XMPP::ClientStream::InvalidMech)
+				s = "invalid SASL mechanism";
+			else if(x == XMPP::ClientStream::InvalidRealm)
+				s = "invalid realm";
+			else if(x == XMPP::ClientStream::MechTooWeak)
+				s = "SASL mechanism too weak for authzid";
+			else if(x == XMPP::ClientStream::NotAuthorized)
+				s = "not authorized";
+			else if(x == XMPP::ClientStream::TemporaryAuthFailure)
+				s = "temporary auth failure";
+			appendErrMsg(QString("Auth error: %1").arg(s));
+		}
+		else if(err == XMPP::ClientStream::ErrSecurityLayer)
+			appendErrMsg("Broken security layer (SASL)");
+		cleanup();
+	}
+
+private:
+	void setHostState()
+	{
+		bool ok = false;
+		if(!ck_probe->isChecked() && cb_proxy->currentItem() != 3)
+			ok = true;
+		lb_host->setEnabled(ok);
+		le_host->setEnabled(ok);
+		ck_ssl->setEnabled(ok);
+	}
+
+	void appendSysMsg(const QString &s, const QColor &_c=QColor())
+	{
+		QString str;
+		QColor c;
+		if(_c.isValid())
+			c = _c;
+		else
+			c = Qt::blue;
+
+		if(c.isValid())
+			str += QString("<font color=\"%1\">").arg(c.name());
+		str += QString("*** %1").arg(s);
+		if(c.isValid())
+			str += QString("</font>");
+		te_log->append(str);
+	}
+
+public:
+	void appendLibMsg(const QString &s)
+	{
+		appendSysMsg(s, Qt::magenta);
+	}
+
+	void appendErrMsg(const QString &s)
+	{
+		appendSysMsg(s, Qt::red);
+	}
+
+	void appendXmlOut(const QString &s)
+	{
+		QStringList lines = QStringList::split('\n', s, true);
+		QString str;
+		bool first = true;
+		for(QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it) {
+			if(!first)
+				str += "<br>";
+			str += QString("<font color=\"%1\">%2</font>").arg(Qt::darkGreen.name()).arg(plain2rich(*it));
+			first = false;
+		}
+		te_log->append(str);
+	}
+
+	void appendXmlIn(const QString &s)
+	{
+		QStringList lines = QStringList::split('\n', s, true);
+		QString str;
+		bool first = true;
+		for(QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it) {
+			if(!first)
+				str += "<br>";
+			str += QString("<font color=\"%1\">%2</font>").arg(Qt::darkBlue.name()).arg(plain2rich(*it));
+			first = false;
+		}
+		te_log->append(str);
+	}
+};
+
+TestDlg *td_glob = 0;
+
+void TestDebug::msg(const QString &s)
+{
+	if(td_glob)
+		td_glob->appendLibMsg(s);
+}
+
+void TestDebug::outgoingTag(const QString &s)
+{
+	if(td_glob)
+		td_glob->appendXmlOut(s);
+}
+
+void TestDebug::incomingTag(const QString &s)
+{
+	if(td_glob)
+		td_glob->appendXmlIn(s);
+}
+
+void TestDebug::outgoingXml(const QDomElement &e)
+{
+	QString out = XMPP::Stream::xmlToString(e, true);
+	if(td_glob)
+		td_glob->appendXmlOut(out);
+}
+
+void TestDebug::incomingXml(const QDomElement &e)
+{
+	QString out = XMPP::Stream::xmlToString(e, true);
+	if(td_glob)
+		td_glob->appendXmlIn(out);
+}
+
+#include"xmpptest.moc"
+
+int main(int argc, char **argv)
+{
+#ifdef Q_OS_WIN32
+	QApplication::addLibraryPath(".");
+	putenv("SASL_PATH=.\\sasl");
+#endif
+	QApplication app(argc, argv);
+
+	// seed the random number generator (needed at least for HttpPoll)
+	srand(time(NULL));
+
+	TestDlg *w = new TestDlg(0);
+	td_glob = w;
+	TestDebug *td = new TestDebug;
+	XMPP::setDebug(td);
+	QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
+	app.exec();
+	XMPP::setDebug(0);
+	delete td;
+	delete w;
+
+	// we need this for a clean exit
+	QCA::unloadAllPlugins();
+
+	return 0;
+}
Index: psi/tags/0.10-os2-b1/iris/example/xmpptest/xmpptest.pro
===================================================================
--- psi/tags/0.10-os2-b1/iris/example/xmpptest/xmpptest.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/example/xmpptest/xmpptest.pro	(revision 38)
@@ -0,0 +1,22 @@
+TEMPLATE = app
+CONFIG += thread
+TARGET  = xmpptest
+
+MOC_DIR        = .moc
+OBJECTS_DIR    = .obj
+UI_DIR         = .ui
+
+#DEFINES += CS_XMPP
+
+#INCLUDEPATH += /usr/local/include ../../../qca/src
+
+include(iris.pri)
+
+SOURCES += xmpptest.cpp
+INTERFACES += ui_test.ui
+
+# iris
+#LIBS += -L/usr/local/lib -liris
+
+# stupid gentoo cyrus sasl hack
+#LIBS += -lcrypto
Index: psi/tags/0.10-os2-b1/iris/include/im.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/include/im.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/include/im.h	(revision 38)
@@ -0,0 +1,694 @@
+/*
+ * im.h - XMPP "IM" library API
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef XMPP_IM_H
+#define XMPP_IM_H
+
+#include<qdatetime.h>
+#include"xmpp.h"
+
+namespace XMPP
+{
+	class Url
+	{
+	public:
+		Url(const QString &url="", const QString &desc="");
+		Url(const Url &);
+		Url & operator=(const Url &);
+		~Url();
+
+		QString url() const;
+		QString desc() const;
+
+		void setUrl(const QString &);
+		void setDesc(const QString &);
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	typedef QValueList<Url> UrlList;
+	typedef QMap<QString, QString> StringMap;
+	typedef enum { OfflineEvent, DeliveredEvent, DisplayedEvent,
+			ComposingEvent, CancelEvent } MsgEvent;
+                                           
+	class Message
+	{
+	public:
+		Message(const Jid &to="");
+		Message(const Message &from);
+		Message & operator=(const Message &from);
+		~Message();
+
+		Jid to() const;
+		Jid from() const;
+		QString id() const;
+		QString type() const;
+		QString lang() const;
+		QString subject(const QString &lang="") const;
+		QString body(const QString &lang="") const;
+		QString thread() const;
+		Stanza::Error error() const;
+
+		void setTo(const Jid &j);
+		void setFrom(const Jid &j);
+		void setId(const QString &s);
+		void setType(const QString &s);
+		void setLang(const QString &s);
+		void setSubject(const QString &s, const QString &lang="");
+		void setBody(const QString &s, const QString &lang="");
+		void setThread(const QString &s);
+		void setError(const Stanza::Error &err);
+
+		// JEP-0091
+		QDateTime timeStamp() const;
+		void setTimeStamp(const QDateTime &ts);
+
+		// JEP-0066
+		UrlList urlList() const;
+		void urlAdd(const Url &u);
+		void urlsClear();
+		void setUrlList(const UrlList &list);
+
+		// JEP-0022
+		QString eventId() const;
+		void setEventId(const QString& id);
+		bool containsEvents() const;
+		bool containsEvent(MsgEvent e) const;
+		void addEvent(MsgEvent e);
+ 
+		// JEP-0027
+		QString xencrypted() const;
+		void setXEncrypted(const QString &s);
+
+		// Obsolete invitation
+		QString invite() const;
+		void setInvite(const QString &s);
+
+		// for compatibility.  delete me later
+		bool spooled() const;
+		void setSpooled(bool);
+		bool wasEncrypted() const;
+		void setWasEncrypted(bool);
+
+		Stanza toStanza(Stream *stream) const;
+		bool fromStanza(const Stanza &s, int tzoffset);
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	class Subscription
+	{
+	public:
+		enum SubType { None, To, From, Both, Remove };
+
+		Subscription(SubType type=None);
+
+		int type() const;
+
+		QString toString() const;
+		bool fromString(const QString &);
+
+	private:
+		SubType value;
+	};
+
+	class Status
+	{
+	public:
+		Status(const QString &show="", const QString &status="", int priority=0, bool available=true);
+		~Status();
+
+		int priority() const;
+		const QString & show() const;
+		const QString & status() const;
+		QDateTime timeStamp() const;
+		const QString & keyID() const;
+		bool isAvailable() const;
+		bool isAway() const;
+		bool isInvisible() const;
+		bool hasError() const;
+		int errorCode() const;
+		const QString & errorString() const;
+
+		const QString & xsigned() const;
+		const QString & songTitle() const;
+
+		void setPriority(int);
+		void setShow(const QString &);
+		void setStatus(const QString &);
+		void setTimeStamp(const QDateTime &);
+		void setKeyID(const QString &);
+		void setIsAvailable(bool);
+		void setIsInvisible(bool);
+		void setError(int, const QString &);
+
+		void setXSigned(const QString &);
+		void setSongTitle(const QString &);
+
+	private:
+		int v_priority;
+		QString v_show, v_status, v_key;
+		QDateTime v_timeStamp;
+		bool v_isAvailable;
+		bool v_isInvisible;
+
+		QString v_xsigned;
+		// gabber song extension
+		QString v_songTitle;
+
+		int ecode;
+		QString estr;
+
+		class Private;
+		Private *d;
+	};
+
+	class Resource
+	{
+	public:
+		Resource(const QString &name="", const Status &s=Status());
+		~Resource();
+
+		const QString & name() const;
+		int priority() const;
+		const Status & status() const;
+
+		void setName(const QString &);
+		void setStatus(const Status &);
+
+	private:
+		QString v_name;
+		Status v_status;
+
+		class ResourcePrivate *d;
+	};
+
+	class ResourceList : public QValueList<Resource>
+	{
+	public:
+		ResourceList();
+		~ResourceList();
+
+		ResourceList::Iterator find(const QString &);
+		ResourceList::Iterator priority();
+
+		ResourceList::ConstIterator find(const QString &) const;
+		ResourceList::ConstIterator priority() const;
+
+	private:
+		class ResourceListPrivate *d;
+	};
+
+	class RosterItem
+	{
+	public:
+		RosterItem(const Jid &jid="");
+		virtual ~RosterItem();
+
+		const Jid & jid() const;
+		const QString & name() const;
+		const QStringList & groups() const;
+		const Subscription & subscription() const;
+		const QString & ask() const;
+		bool isPush() const;
+		bool inGroup(const QString &) const;
+
+		virtual void setJid(const Jid &);
+		void setName(const QString &);
+		void setGroups(const QStringList &);
+		void setSubscription(const Subscription &);
+		void setAsk(const QString &);
+		void setIsPush(bool);
+		bool addGroup(const QString &);
+		bool removeGroup(const QString &);
+
+		QDomElement toXml(QDomDocument *) const;
+		bool fromXml(const QDomElement &);
+
+	private:
+		Jid v_jid;
+		QString v_name;
+		QStringList v_groups;
+		Subscription v_subscription;
+		QString v_ask;
+		bool v_push;
+
+		class RosterItemPrivate *d;
+	};
+
+	class Roster : public QValueList<RosterItem>
+	{
+	public:
+		Roster();
+		~Roster();
+
+		Roster::Iterator find(const Jid &);
+		Roster::ConstIterator find(const Jid &) const;
+
+	private:
+		class RosterPrivate *d;
+	};
+
+        class Features
+	{
+	public:
+		Features();
+		Features(const QStringList &);
+		Features(const QString &);
+		~Features();
+
+		QStringList list() const; // actual featurelist
+		void setList(const QStringList &);
+
+		// features
+		bool canRegister() const;
+		bool canSearch() const;
+		bool canGroupchat() const;
+		bool canDisco() const;
+		bool isGateway() const;
+		bool haveVCard() const;
+
+		enum FeatureID {
+			FID_Invalid = -1,
+			FID_None,
+			FID_Register,
+			FID_Search,
+			FID_Groupchat,
+			FID_Disco,
+			FID_Gateway,
+			FID_VCard,
+
+			// private Psi actions
+			FID_Add
+		};
+
+		// useful functions
+		bool test(const QStringList &) const;
+
+		QString name() const;
+		static QString name(long id);
+		static QString name(const QString &feature);
+
+		long id() const;
+		static long id(const QString &feature);
+		static QString feature(long id);
+
+		class FeatureName;
+	private:
+		QStringList _list;
+	};
+
+	class AgentItem
+	{
+	public:
+		AgentItem() { }
+
+		const Jid & jid() const { return v_jid; }
+		const QString & name() const { return v_name; }
+		const QString & category() const { return v_category; }
+		const QString & type() const { return v_type; }
+		const Features & features() const { return v_features; }
+
+		void setJid(const Jid &j) { v_jid = j; }
+		void setName(const QString &n) { v_name = n; }
+		void setCategory(const QString &c) { v_category = c; }
+		void setType(const QString &t) { v_type = t; }
+		void setFeatures(const Features &f) { v_features = f; }
+
+	private:
+		Jid v_jid;
+		QString v_name, v_category, v_type;
+		Features v_features;
+	};
+
+	typedef QValueList<AgentItem> AgentList;
+
+	class DiscoItem
+	{
+	public:
+		DiscoItem();
+		~DiscoItem();
+
+		const Jid &jid() const;
+		const QString &node() const;
+		const QString &name() const;
+
+		void setJid(const Jid &);
+		void setName(const QString &);
+		void setNode(const QString &);
+
+		enum Action {
+			None = 0,
+			Remove,
+			Update
+		};
+
+		Action action() const;
+		void setAction(Action);
+
+		const Features &features() const;
+		void setFeatures(const Features &);
+
+		struct Identity
+		{
+			QString category;
+			QString name;
+			QString type;
+		};
+
+		typedef QValueList<Identity> Identities;
+
+		const Identities &identities() const;
+		void setIdentities(const Identities &);
+
+		// some useful helper functions
+		static Action string2action(QString s);
+		static QString action2string(Action a);
+
+		DiscoItem & operator= (const DiscoItem &);
+		DiscoItem(const DiscoItem &);
+
+		operator AgentItem() const { return toAgentItem(); }
+		AgentItem toAgentItem() const;
+		void fromAgentItem(const AgentItem &);
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	typedef QValueList<DiscoItem> DiscoList;
+
+	class FormField
+	{
+	public:
+		enum { username, nick, password, name, first, last, email, address, city, state, zip, phone, url, date, misc };
+		FormField(const QString &type="", const QString &value="");
+		~FormField();
+
+		int type() const;
+		QString fieldName() const;
+		QString realName() const;
+		bool isSecret() const;
+		const QString & value() const;
+		void setType(int);
+		bool setType(const QString &);
+		void setValue(const QString &);
+
+	private:
+		int tagNameToType(const QString &) const;
+		QString typeToTagName(int) const;
+
+		int v_type;
+		QString v_value;
+
+		class Private;
+		Private *d;
+	};
+
+	class Form : public QValueList<FormField>
+	{
+	public:
+		Form(const Jid &j="");
+		~Form();
+
+		Jid jid() const;
+		QString instructions() const;
+		QString key() const;
+		void setJid(const Jid &);
+		void setInstructions(const QString &);
+		void setKey(const QString &);
+
+	private:
+		Jid v_jid;
+		QString v_instructions, v_key;
+
+		class Private;
+		Private *d;
+	};
+
+	class SearchResult
+	{
+	public:
+		SearchResult(const Jid &jid="");
+		~SearchResult();
+
+		const Jid & jid() const;
+		const QString & nick() const;
+		const QString & first() const;
+		const QString & last() const;
+		const QString & email() const;
+
+		void setJid(const Jid &);
+		void setNick(const QString &);
+		void setFirst(const QString &);
+		void setLast(const QString &);
+		void setEmail(const QString &);
+
+	private:
+		Jid v_jid;
+		QString v_nick, v_first, v_last, v_email;
+	};
+
+	class Client;
+	class LiveRosterItem;
+	class LiveRoster;
+	class S5BManager;
+	class IBBManager;
+	class JidLinkManager;
+	class FileTransferManager;
+
+	class Task : public QObject
+	{
+		Q_OBJECT
+	public:
+		enum { ErrDisc };
+		Task(Task *parent);
+		Task(Client *, bool isRoot);
+		virtual ~Task();
+
+		Task *parent() const;
+		Client *client() const;
+		QDomDocument *doc() const;
+		QString id() const;
+
+		bool success() const;
+		int statusCode() const;
+		const QString & statusString() const;
+
+		void go(bool autoDelete=false);
+		virtual bool take(const QDomElement &);
+		void safeDelete();
+
+	signals:
+		void finished();
+
+	protected:
+		virtual void onGo();
+		virtual void onDisconnect();
+		void send(const QDomElement &);
+		void setSuccess(int code=0, const QString &str="");
+		void setError(const QDomElement &);
+		void setError(int code=0, const QString &str="");
+		void debug(const char *, ...);
+		void debug(const QString &);
+		bool iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns="");
+
+	private slots:
+		void clientDisconnected();
+		void done();
+
+	private:
+		void init();
+
+		class TaskPrivate;
+		TaskPrivate *d;
+	};
+
+	class Client : public QObject
+	{
+		Q_OBJECT
+
+	public:
+		Client(QObject *parent=0);
+		~Client();
+
+		bool isActive() const;
+		void connectToServer(ClientStream *s, const Jid &j, bool auth=true);
+		void start(const QString &host, const QString &user, const QString &pass, const QString &resource);
+		void close(bool fast=false);
+
+		Stream & stream();
+		const LiveRoster & roster() const;
+		const ResourceList & resourceList() const;
+
+		void send(const QDomElement &);
+		void send(const QString &);
+
+		QString host() const;
+		QString user() const;
+		QString pass() const;
+		QString resource() const;
+		Jid jid() const;
+
+		void rosterRequest();
+		void sendMessage(const Message &);
+		void sendSubscription(const Jid &, const QString &);
+		void setPresence(const Status &);
+
+		void debug(const QString &);
+		QString genUniqueId();
+		Task *rootTask();
+		QDomDocument *doc() const;
+
+		QString OSName() const;
+		QString timeZone() const;
+		int timeZoneOffset() const;
+		QString clientName() const;
+		QString clientVersion() const;
+
+		void setOSName(const QString &);
+		void setTimeZone(const QString &, int);
+		void setClientName(const QString &);
+		void setClientVersion(const QString &);
+
+		S5BManager *s5bManager() const;
+		IBBManager *ibbManager() const;
+		JidLinkManager *jidLinkManager() const;
+
+		void setFileTransferEnabled(bool b);
+		FileTransferManager *fileTransferManager() const;
+
+		bool groupChatJoin(const QString &host, const QString &room, const QString &nick);
+		void groupChatSetStatus(const QString &host, const QString &room, const Status &);
+		void groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &);
+		void groupChatLeave(const QString &host, const QString &room);
+
+	signals:
+		void activated();
+		void disconnected();
+		//void authFinished(bool, int, const QString &);
+		void rosterRequestFinished(bool, int, const QString &);
+		void rosterItemAdded(const RosterItem &);
+		void rosterItemUpdated(const RosterItem &);
+		void rosterItemRemoved(const RosterItem &);
+		void resourceAvailable(const Jid &, const Resource &);
+		void resourceUnavailable(const Jid &, const Resource &);
+		void presenceError(const Jid &, int, const QString &);
+		void subscription(const Jid &, const QString &);
+		void messageReceived(const Message &);
+		void debugText(const QString &);
+		void xmlIncoming(const QString &);
+		void xmlOutgoing(const QString &);
+		void groupChatJoined(const Jid &);
+		void groupChatLeft(const Jid &);
+		void groupChatPresence(const Jid &, const Status &);
+		void groupChatError(const Jid &, int, const QString &);
+
+		void incomingJidLink();
+
+	private slots:
+		//void streamConnected();
+		//void streamHandshaken();
+		//void streamError(const StreamError &);
+		//void streamSSLCertificateReady(const QSSLCert &);
+		//void streamCloseFinished();
+		void streamError(int);
+		void streamReadyRead();
+		void streamIncomingXml(const QString &);
+		void streamOutgoingXml(const QString &);
+
+		void slotRosterRequestFinished();
+
+		// basic daemons
+		void ppSubscription(const Jid &, const QString &);
+		void ppPresence(const Jid &, const Status &);
+		void pmMessage(const Message &);
+		void prRoster(const Roster &);
+
+		void s5b_incomingReady();
+		void ibb_incomingReady();
+
+	public:
+		class GroupChat;
+	private:
+		void cleanup();
+		void distribute(const QDomElement &);
+		void importRoster(const Roster &);
+		void importRosterItem(const RosterItem &);
+		void updateSelfPresence(const Jid &, const Status &);
+		void updatePresence(LiveRosterItem *, const Jid &, const Status &);
+
+		class ClientPrivate;
+		ClientPrivate *d;
+	};
+
+	class LiveRosterItem : public RosterItem
+	{
+	public:
+		LiveRosterItem(const Jid &j="");
+		LiveRosterItem(const RosterItem &);
+		~LiveRosterItem();
+
+		void setRosterItem(const RosterItem &);
+
+		ResourceList & resourceList();
+		ResourceList::Iterator priority();
+
+		const ResourceList & resourceList() const;
+		ResourceList::ConstIterator priority() const;
+
+		bool isAvailable() const;
+		const Status & lastUnavailableStatus() const;
+		bool flagForDelete() const;
+
+		void setLastUnavailableStatus(const Status &);
+		void setFlagForDelete(bool);
+
+	private:
+		ResourceList v_resourceList;
+		Status v_lastUnavailableStatus;
+		bool v_flagForDelete;
+
+		class LiveRosterItemPrivate;
+		LiveRosterItemPrivate *d;
+	};
+
+	class LiveRoster : public QValueList<LiveRosterItem>
+	{
+	public:
+		LiveRoster();
+		~LiveRoster();
+
+		void flagAllForDelete();
+		LiveRoster::Iterator find(const Jid &, bool compareRes=true);
+		LiveRoster::ConstIterator find(const Jid &, bool compareRes=true) const;
+
+	private:
+		class LiveRosterPrivate;
+		LiveRosterPrivate *d;
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/include/xmpp.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/include/xmpp.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/include/xmpp.h	(revision 38)
@@ -0,0 +1,546 @@
+/*
+ * xmpp.h - XMPP "core" library API
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef XMPP_H
+#define XMPP_H
+
+#include<qobject.h>
+#include<qstring.h>
+#include<qhostaddress.h>
+#include<qstring.h>
+#include<qcstring.h>
+#include<qxml.h>
+#include<qdom.h>
+
+namespace QCA
+{
+	class TLS;
+};
+
+#ifndef CS_XMPP
+class ByteStream;
+#endif
+
+namespace XMPP
+{
+	// CS_IMPORT_BEGIN cutestuff/bytestream.h
+#ifdef CS_XMPP
+	class ByteStream;
+#endif
+	// CS_IMPORT_END
+
+	class Debug
+	{
+	public:
+		virtual ~Debug();
+
+		virtual void msg(const QString &)=0;
+		virtual void outgoingTag(const QString &)=0;
+		virtual void incomingTag(const QString &)=0;
+		virtual void outgoingXml(const QDomElement &)=0;
+		virtual void incomingXml(const QDomElement &)=0;
+	};
+
+	void setDebug(Debug *);
+
+	class Connector : public QObject
+	{
+		Q_OBJECT
+	public:
+		Connector(QObject *parent=0);
+		virtual ~Connector();
+
+		virtual void connectToServer(const QString &server)=0;
+		virtual ByteStream *stream() const=0;
+		virtual void done()=0;
+
+		bool useSSL() const;
+		bool havePeerAddress() const;
+		QHostAddress peerAddress() const;
+		Q_UINT16 peerPort() const;
+
+	signals:
+		void connected();
+		void error();
+
+	protected:
+		void setUseSSL(bool b);
+		void setPeerAddressNone();
+		void setPeerAddress(const QHostAddress &addr, Q_UINT16 port);
+
+	private:
+		bool ssl;
+		bool haveaddr;
+		QHostAddress addr;
+		Q_UINT16 port;
+	};
+
+	class AdvancedConnector : public Connector
+	{
+		Q_OBJECT
+	public:
+		enum Error { ErrConnectionRefused, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth, ErrStream };
+		AdvancedConnector(QObject *parent=0);
+		virtual ~AdvancedConnector();
+
+		class Proxy
+		{
+		public:
+			enum { None, HttpConnect, HttpPoll, Socks };
+			Proxy();
+			~Proxy();
+
+			int type() const;
+			QString host() const;
+			Q_UINT16 port() const;
+			QString url() const;
+			QString user() const;
+			QString pass() const;
+			int pollInterval() const;
+
+			void setHttpConnect(const QString &host, Q_UINT16 port);
+			void setHttpPoll(const QString &host, Q_UINT16 port, const QString &url);
+			void setSocks(const QString &host, Q_UINT16 port);
+			void setUserPass(const QString &user, const QString &pass);
+			void setPollInterval(int secs);
+
+		private:
+			int t;
+			QString v_host, v_url;
+			Q_UINT16 v_port;
+			QString v_user, v_pass;
+			int v_poll;
+		};
+
+		void setProxy(const Proxy &proxy);
+		void setOptHostPort(const QString &host, Q_UINT16 port);
+		void setOptProbe(bool);
+		void setOptSSL(bool);
+
+		void changePollInterval(int secs);
+
+		void connectToServer(const QString &server);
+		ByteStream *stream() const;
+		void done();
+
+		int errorCode() const;
+
+	signals:
+		void srvLookup(const QString &server);
+		void srvResult(bool success);
+		void httpSyncStarted();
+		void httpSyncFinished();
+
+	private slots:
+		void dns_done();
+		void srv_done();
+		void bs_connected();
+		void bs_error(int);
+		void http_syncStarted();
+		void http_syncFinished();
+
+	private:
+		class Private;
+		Private *d;
+
+		void cleanup();
+		void do_resolve();
+		void do_connect();
+		void tryNextSrv();
+	};
+
+	class TLSHandler : public QObject
+	{
+		Q_OBJECT
+	public:
+		TLSHandler(QObject *parent=0);
+		virtual ~TLSHandler();
+
+		virtual void reset()=0;
+		virtual void startClient(const QString &host)=0;
+		virtual void write(const QByteArray &a)=0;
+		virtual void writeIncoming(const QByteArray &a)=0;
+
+	signals:
+		void success();
+		void fail();
+		void closed();
+		void readyRead(const QByteArray &a);
+		void readyReadOutgoing(const QByteArray &a, int plainBytes);
+	};
+
+	class QCATLSHandler : public TLSHandler
+	{
+		Q_OBJECT
+	public:
+		QCATLSHandler(QCA::TLS *parent);
+		~QCATLSHandler();
+
+		QCA::TLS *tls() const;
+		int tlsError() const;
+
+		void reset();
+		void startClient(const QString &host);
+		void write(const QByteArray &a);
+		void writeIncoming(const QByteArray &a);
+
+	signals:
+		void tlsHandshaken();
+
+	public slots:
+		void continueAfterHandshake();
+
+	private slots:
+		void tls_handshaken();
+		void tls_readyRead();
+		void tls_readyReadOutgoing(int);
+		void tls_closed();
+		void tls_error(int);
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	class Jid
+	{
+	public:
+		Jid();
+		~Jid();
+
+		Jid(const QString &s);
+		Jid(const char *s);
+		Jid & operator=(const QString &s);
+		Jid & operator=(const char *s);
+
+		void set(const QString &s);
+		void set(const QString &domain, const QString &node, const QString &resource="");
+
+		void setDomain(const QString &s);
+		void setNode(const QString &s);
+		void setResource(const QString &s);
+
+		const QString & domain() const { return d; }
+		const QString & node() const { return n; }
+		const QString & resource() const { return r; }
+		const QString & bare() const { return b; }
+		const QString & full() const { return f; }
+
+		Jid withNode(const QString &s) const;
+		Jid withResource(const QString &s) const;
+
+		bool isValid() const;
+		bool isEmpty() const;
+		bool compare(const Jid &a, bool compareRes=true) const;
+
+		static bool validDomain(const QString &s, QString *norm=0);
+		static bool validNode(const QString &s, QString *norm=0);
+		static bool validResource(const QString &s, QString *norm=0);
+
+		// TODO: kill these later
+		const QString & host() const { return d; }
+		const QString & user() const { return n; }
+		const QString & userHost() const { return b; }
+
+	private:
+		void reset();
+		void update();
+
+		QString f, b, d, n, r;
+		bool valid;
+	};
+
+	class Stream;
+	class Stanza
+	{
+	public:
+		enum Kind { Message, Presence, IQ };
+		enum ErrorType { Cancel, Continue, Modify, Auth, Wait };
+		enum ErrorCond
+		{
+			BadRequest,
+			Conflict,
+			FeatureNotImplemented,
+			Forbidden,
+			InternalServerError,
+			ItemNotFound,
+			JidMalformed,
+			NotAllowed,
+			PaymentRequired,
+			RecipientUnavailable,
+			RegistrationRequired,
+			ServerNotFound,
+			ServerTimeout,
+			ResourceConstraint,
+			ServiceUnavailable,
+			SubscriptionRequired,
+			UndefinedCondition,
+			UnexpectedRequest
+		};
+
+		Stanza();
+		Stanza(const Stanza &from);
+		Stanza & operator=(const Stanza &from);
+		virtual ~Stanza();
+
+		class Error
+		{
+		public:
+			Error(int type=Cancel, int condition=UndefinedCondition, const QString &text="", const QDomElement &appSpec=QDomElement());
+
+			int type;
+			int condition;
+			QString text;
+			QDomElement appSpec;
+		};
+
+		bool isNull() const;
+
+		QDomElement element() const;
+		QString toString() const;
+
+		QDomDocument & doc() const;
+		QString baseNS() const;
+		QDomElement createElement(const QString &ns, const QString &tagName);
+		QDomElement createTextElement(const QString &ns, const QString &tagName, const QString &text);
+		void appendChild(const QDomElement &e);
+
+		Kind kind() const;
+		void setKind(Kind k);
+
+		Jid to() const;
+		Jid from() const;
+		QString id() const;
+		QString type() const;
+		QString lang() const;
+
+		void setTo(const Jid &j);
+		void setFrom(const Jid &j);
+		void setId(const QString &id);
+		void setType(const QString &type);
+		void setLang(const QString &lang);
+
+		Error error() const;
+		void setError(const Error &err);
+		void clearError();
+
+	private:
+		friend class Stream;
+		Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id);
+		Stanza(Stream *s, const QDomElement &e);
+
+		class Private;
+		Private *d;
+	};
+
+	class Stream : public QObject
+	{
+		Q_OBJECT
+	public:
+		enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 };
+		enum StreamCond {
+			GenericStreamError,
+			Conflict,
+			ConnectionTimeout,
+			InternalServerError,
+			InvalidFrom,
+			InvalidXml,
+			PolicyViolation,
+			ResourceConstraint,
+			SystemShutdown
+		};
+
+		Stream(QObject *parent=0);
+		virtual ~Stream();
+
+		virtual QDomDocument & doc() const=0;
+		virtual QString baseNS() const=0;
+		virtual bool old() const=0;
+
+		virtual void close()=0;
+		virtual bool stanzaAvailable() const=0;
+		virtual Stanza read()=0;
+		virtual void write(const Stanza &s)=0;
+
+		virtual int errorCondition() const=0;
+		virtual QString errorText() const=0;
+		virtual QDomElement errorAppSpec() const=0;
+
+		Stanza createStanza(Stanza::Kind k, const Jid &to="", const QString &type="", const QString &id="");
+		Stanza createStanza(const QDomElement &e);
+
+		static QString xmlToString(const QDomElement &e, bool clip=false);
+
+	signals:
+		void connectionClosed();
+		void delayedCloseFinished();
+		void readyRead();
+		void stanzaWritten();
+		void error(int);
+	};
+
+	class ClientStream : public Stream
+	{
+		Q_OBJECT
+	public:
+		enum Error {
+			ErrConnection = ErrCustom,  // Connection error, ask Connector-subclass what's up
+			ErrNeg,                     // Negotiation error, see condition
+			ErrTLS,                     // TLS error, see condition
+			ErrAuth,                    // Auth error, see condition
+			ErrSecurityLayer,           // broken SASL security layer
+			ErrBind                     // Resource binding error
+		};
+		enum Warning {
+			WarnOldVersion,             // server uses older XMPP/Jabber "0.9" protocol
+			WarnNoTLS                   // there is no chance for TLS at this point
+		};
+		enum NegCond {
+			HostGone,                   // host no longer hosted
+			HostUnknown,                // unknown host
+			RemoteConnectionFailed,     // unable to connect to a required remote resource
+			SeeOtherHost,               // a 'redirect', see errorText() for other host
+			UnsupportedVersion          // unsupported XMPP version
+		};
+		enum TLSCond {
+			TLSStart,                   // server rejected STARTTLS
+			TLSFail                     // TLS failed, ask TLSHandler-subclass what's up
+		};
+		enum SecurityLayer {
+			LayerTLS,
+			LayerSASL
+		};
+		enum AuthCond {
+			GenericAuthError,           // all-purpose "can't login" error
+			NoMech,                     // No appropriate auth mech available
+			BadProto,                   // Bad SASL auth protocol
+			BadServ,                    // Server failed mutual auth
+			EncryptionRequired,         // can't use mech without TLS
+			InvalidAuthzid,             // bad input JID
+			InvalidMech,                // bad mechanism
+			InvalidRealm,               // bad realm
+			MechTooWeak,                // can't use mech with this authzid
+			NotAuthorized,              // bad user, bad password, bad creditials
+			TemporaryAuthFailure        // please try again later!
+		};
+		enum BindCond {
+			BindNotAllowed,             // not allowed to bind a resource
+			BindConflict                // resource in-use
+		};
+
+		ClientStream(Connector *conn, TLSHandler *tlsHandler=0, QObject *parent=0);
+		ClientStream(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls=0, QObject *parent=0); // server
+		~ClientStream();
+
+		Jid jid() const;
+		void connectToServer(const Jid &jid, bool auth=true);
+		void accept(); // server
+		bool isActive() const;
+		bool isAuthenticated() const;
+
+		// login params
+		void setUsername(const QString &s);
+		void setPassword(const QString &s);
+		void setRealm(const QString &s);
+		void continueAfterParams();
+
+		// SASL information
+		QString saslMechanism() const;
+		int saslSSF() const;
+
+		// binding
+		void setResourceBinding(bool);
+
+		// security options (old protocol only uses the first !)
+		void setAllowPlain(bool);
+		void setRequireMutualAuth(bool);
+		void setSSFRange(int low, int high);
+		void setOldOnly(bool);
+		void setSASLMechanism(const QString &s);
+		void setLocalAddr(const QHostAddress &addr, Q_UINT16 port);
+
+		// reimplemented
+		QDomDocument & doc() const;
+		QString baseNS() const;
+		bool old() const;
+
+		void close();
+		bool stanzaAvailable() const;
+		Stanza read();
+		void write(const Stanza &s);
+
+		int errorCondition() const;
+		QString errorText() const;
+		QDomElement errorAppSpec() const;
+
+		// extra
+		void writeDirect(const QString &s);
+		void setNoopTime(int mills);
+
+	signals:
+		void connected();
+		void securityLayerActivated(int);
+		void needAuthParams(bool user, bool pass, bool realm);
+		void authenticated();
+		void warning(int);
+		void incomingXml(const QString &s);
+		void outgoingXml(const QString &s);
+
+	public slots:
+		void continueAfterWarning();
+
+	private slots:
+		void cr_connected();
+		void cr_error();
+
+		void bs_connectionClosed();
+		void bs_delayedCloseFinished();
+		void bs_error(int); // server only
+
+		void ss_readyRead();
+		void ss_bytesWritten(int);
+		void ss_tlsHandshaken();
+		void ss_tlsClosed();
+		void ss_error(int);
+
+		void sasl_clientFirstStep(const QString &mech, const QByteArray *clientInit);
+		void sasl_nextStep(const QByteArray &stepData);
+		void sasl_needParams(bool user, bool authzid, bool pass, bool realm);
+		void sasl_authCheck(const QString &user, const QString &authzid);
+		void sasl_authenticated();
+		void sasl_error(int);
+
+		void doNoop();
+		void doReadyRead();
+
+	private:
+		class Private;
+		Private *d;
+
+		void reset(bool all=false);
+		void processNext();
+		int convertedSASLCond() const;
+		bool handleNeed();
+		void handleError();
+		void srvProcessNext();
+	};
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/iris.pri
===================================================================
--- psi/tags/0.10-os2-b1/iris/iris.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/iris.pri	(revision 38)
@@ -0,0 +1,48 @@
+# libidn
+LIBIDN_BASE = $$IRIS_BASE/libidn
+CONFIG += libidn
+include($$IRIS_BASE/libidn.pri)
+
+DEFINES += XMPP_TEST
+
+INCLUDEPATH += $$IRIS_BASE/include $$IRIS_BASE/xmpp-core $$IRIS_BASE/xmpp-im $$IRIS_BASE/jabber
+
+HEADERS += \
+	$$IRIS_BASE/xmpp-core/hash.h \
+	$$IRIS_BASE/xmpp-core/simplesasl.h \
+	$$IRIS_BASE/xmpp-core/securestream.h \
+	$$IRIS_BASE/xmpp-core/parser.h \
+	$$IRIS_BASE/xmpp-core/xmlprotocol.h \
+	$$IRIS_BASE/xmpp-core/protocol.h \
+	$$IRIS_BASE/xmpp-core/td.h \
+	$$IRIS_BASE/xmpp-im/xmpp_tasks.h \
+	$$IRIS_BASE/xmpp-im/xmpp_xmlcommon.h \
+	$$IRIS_BASE/xmpp-im/xmpp_vcard.h \
+	$$IRIS_BASE/jabber/s5b.h \
+	$$IRIS_BASE/jabber/xmpp_ibb.h \
+	$$IRIS_BASE/jabber/xmpp_jidlink.h \
+	$$IRIS_BASE/jabber/filetransfer.h \
+	$$IRIS_BASE/include/xmpp.h \
+	$$IRIS_BASE/include/im.h
+
+SOURCES += \
+	$$IRIS_BASE/xmpp-core/connector.cpp \
+	$$IRIS_BASE/xmpp-core/tlshandler.cpp \
+	$$IRIS_BASE/xmpp-core/jid.cpp \
+	$$IRIS_BASE/xmpp-core/hash.cpp \
+	$$IRIS_BASE/xmpp-core/simplesasl.cpp \
+	$$IRIS_BASE/xmpp-core/securestream.cpp \
+	$$IRIS_BASE/xmpp-core/parser.cpp \
+	$$IRIS_BASE/xmpp-core/xmlprotocol.cpp \
+	$$IRIS_BASE/xmpp-core/protocol.cpp \
+	$$IRIS_BASE/xmpp-core/stream.cpp \
+	$$IRIS_BASE/xmpp-im/types.cpp \
+	$$IRIS_BASE/xmpp-im/client.cpp \
+	$$IRIS_BASE/xmpp-im/xmpp_tasks.cpp \
+	$$IRIS_BASE/xmpp-im/xmpp_xmlcommon.cpp \
+	$$IRIS_BASE/xmpp-im/xmpp_vcard.cpp \
+	$$IRIS_BASE/jabber/s5b.cpp \
+	$$IRIS_BASE/jabber/xmpp_ibb.cpp \
+	$$IRIS_BASE/jabber/xmpp_jidlink.cpp \
+	$$IRIS_BASE/jabber/filetransfer.cpp
+
Index: psi/tags/0.10-os2-b1/iris/jabber/filetransfer.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/jabber/filetransfer.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/jabber/filetransfer.cpp	(revision 38)
@@ -0,0 +1,770 @@
+/*
+ * filetransfer.cpp - File Transfer
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"filetransfer.h"
+
+#include<qtimer.h>
+#include<qptrlist.h>
+#include<qguardedptr.h>
+#include<qfileinfo.h>
+#include"xmpp_xmlcommon.h"
+#include"s5b.h"
+
+#define SENDBUFSIZE 65536
+
+using namespace XMPP;
+
+// firstChildElement
+//
+// Get an element's first child element
+static QDomElement firstChildElement(const QDomElement &e)
+{
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		if(n.isElement())
+			return n.toElement();
+	}
+	return QDomElement();
+}
+
+//----------------------------------------------------------------------------
+// FileTransfer
+//----------------------------------------------------------------------------
+class FileTransfer::Private
+{
+public:
+	FileTransferManager *m;
+	JT_FT *ft;
+	Jid peer;
+	QString fname;
+	Q_LLONG size;
+	Q_LLONG sent;
+	QString desc;
+	bool rangeSupported;
+	Q_LLONG rangeOffset, rangeLength, length;
+	QString streamType;
+	bool needStream;
+	QString id, iq_id;
+	S5BConnection *c;
+	Jid proxy;
+	int state;
+	bool sender;
+};
+
+FileTransfer::FileTransfer(FileTransferManager *m, QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->m = m;
+	d->ft = 0;
+	d->c = 0;
+	reset();
+}
+
+FileTransfer::~FileTransfer()
+{
+	reset();
+	delete d;
+}
+
+void FileTransfer::reset()
+{
+	d->m->unlink(this);
+
+	delete d->ft;
+	d->ft = 0;
+
+	delete d->c;
+	d->c = 0;
+
+	d->state = Idle;
+	d->needStream = false;
+	d->sent = 0;
+	d->sender = false;
+}
+
+void FileTransfer::setProxy(const Jid &proxy)
+{
+	d->proxy = proxy;
+}
+
+void FileTransfer::sendFile(const Jid &to, const QString &fname, Q_LLONG size, const QString &desc)
+{
+	d->state = Requesting;
+	d->peer = to;
+	d->fname = fname;
+	d->size = size;
+	d->desc = desc;
+	d->sender = true;
+	d->id = d->m->link(this);
+
+	d->ft = new JT_FT(d->m->client()->rootTask());
+	connect(d->ft, SIGNAL(finished()), SLOT(ft_finished()));
+	QStringList list;
+	list += "http://jabber.org/protocol/bytestreams";
+	d->ft->request(to, d->id, fname, size, desc, list);
+	d->ft->go(true);
+}
+
+int FileTransfer::dataSizeNeeded() const
+{
+	int pending = d->c->bytesToWrite();
+	if(pending >= SENDBUFSIZE)
+		return 0;
+	Q_LLONG left = d->length - (d->sent + pending);
+	int size = SENDBUFSIZE - pending;
+	if((Q_LLONG)size > left)
+		size = (int)left;
+	return size;
+}
+
+void FileTransfer::writeFileData(const QByteArray &a)
+{
+	int pending = d->c->bytesToWrite();
+	Q_LLONG left = d->length - (d->sent + pending);
+	if(left == 0)
+		return;
+
+	QByteArray block;
+	if((Q_LLONG)a.size() > left) {
+		block = a.copy();
+		block.resize((uint)left);
+	}
+	else
+		block = a;
+	d->c->write(block);
+}
+
+Jid FileTransfer::peer() const
+{
+	return d->peer;
+}
+
+QString FileTransfer::fileName() const
+{
+	return d->fname;
+}
+
+Q_LLONG FileTransfer::fileSize() const
+{
+	return d->size;
+}
+
+QString FileTransfer::description() const
+{
+	return d->desc;
+}
+
+bool FileTransfer::rangeSupported() const
+{
+	return d->rangeSupported;
+}
+
+Q_LLONG FileTransfer::offset() const
+{
+	return d->rangeOffset;
+}
+
+Q_LLONG FileTransfer::length() const
+{
+	return d->length;
+}
+
+void FileTransfer::accept(Q_LLONG offset, Q_LLONG length)
+{
+	d->state = Connecting;
+	d->rangeOffset = offset;
+	d->rangeLength = length;
+	if(length > 0)
+		d->length = length;
+	else
+		d->length = d->size;
+	d->streamType = "http://jabber.org/protocol/bytestreams";
+	d->m->con_accept(this);
+}
+
+void FileTransfer::close()
+{
+	if(d->state == Idle)
+		return;
+	if(d->state == WaitingForAccept)
+		d->m->con_reject(this);
+	else if(d->state == Active)
+		d->c->close();
+	reset();
+}
+
+S5BConnection *FileTransfer::s5bConnection() const
+{
+	return d->c;
+}
+
+void FileTransfer::ft_finished()
+{
+	JT_FT *ft = d->ft;
+	d->ft = 0;
+
+	if(ft->success()) {
+		d->state = Connecting;
+		d->rangeOffset = ft->rangeOffset();
+		d->length = ft->rangeLength();
+		if(d->length == 0)
+			d->length = d->size - d->rangeOffset;
+		d->streamType = ft->streamType();
+		d->c = d->m->client()->s5bManager()->createConnection();
+		connect(d->c, SIGNAL(connected()), SLOT(s5b_connected()));
+		connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
+		connect(d->c, SIGNAL(bytesWritten(int)), SLOT(s5b_bytesWritten(int)));
+		connect(d->c, SIGNAL(error(int)), SLOT(s5b_error(int)));
+
+		if(d->proxy.isValid())
+			d->c->setProxy(d->proxy);
+		d->c->connectToJid(d->peer, d->id);
+		accepted();
+	}
+	else {
+		reset();
+		if(ft->statusCode() == 403)
+			error(ErrReject);
+		else
+			error(ErrNeg);
+	}
+}
+
+void FileTransfer::takeConnection(S5BConnection *c)
+{
+	d->c = c;
+	connect(d->c, SIGNAL(connected()), SLOT(s5b_connected()));
+	connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
+	connect(d->c, SIGNAL(readyRead()), SLOT(s5b_readyRead()));
+	connect(d->c, SIGNAL(error(int)), SLOT(s5b_error(int)));
+	if(d->proxy.isValid())
+		d->c->setProxy(d->proxy);
+	accepted();
+	QTimer::singleShot(0, this, SLOT(doAccept()));
+}
+
+void FileTransfer::s5b_connected()
+{
+	d->state = Active;
+	connected();
+}
+
+void FileTransfer::s5b_connectionClosed()
+{
+	reset();
+	error(ErrStream);
+}
+
+void FileTransfer::s5b_readyRead()
+{
+	QByteArray a = d->c->read();
+	Q_LLONG need = d->length - d->sent;
+	if((Q_LLONG)a.size() > need)
+		a.resize((uint)need);
+	d->sent += a.size();
+	if(d->sent == d->length)
+		reset();
+	readyRead(a);
+}
+
+void FileTransfer::s5b_bytesWritten(int x)
+{
+	d->sent += x;
+	if(d->sent == d->length)
+		reset();
+	bytesWritten(x);
+}
+
+void FileTransfer::s5b_error(int x)
+{
+	reset();
+	if(x == S5BConnection::ErrRefused || x == S5BConnection::ErrConnect)
+		error(ErrConnect);
+	else if(x == S5BConnection::ErrProxy)
+		error(ErrProxy);
+	else
+		error(ErrStream);
+}
+
+void FileTransfer::man_waitForAccept(const FTRequest &req)
+{
+	d->state = WaitingForAccept;
+	d->peer = req.from;
+	d->id = req.id;
+	d->iq_id = req.iq_id;
+	d->fname = req.fname;
+	d->size = req.size;
+	d->desc = req.desc;
+	d->rangeSupported = req.rangeSupported;
+}
+
+void FileTransfer::doAccept()
+{
+	d->c->accept();
+}
+
+//----------------------------------------------------------------------------
+// FileTransferManager
+//----------------------------------------------------------------------------
+class FileTransferManager::Private
+{
+public:
+	Client *client;
+	QPtrList<FileTransfer> list, incoming;
+	JT_PushFT *pft;
+};
+
+FileTransferManager::FileTransferManager(Client *client)
+:QObject(client)
+{
+	d = new Private;
+	d->client = client;
+
+	d->pft = new JT_PushFT(d->client->rootTask());
+	connect(d->pft, SIGNAL(incoming(const FTRequest &)), SLOT(pft_incoming(const FTRequest &)));
+}
+
+FileTransferManager::~FileTransferManager()
+{
+	d->incoming.setAutoDelete(true);
+	d->incoming.clear();
+	delete d->pft;
+	delete d;
+}
+
+Client *FileTransferManager::client() const
+{
+	return d->client;
+}
+
+FileTransfer *FileTransferManager::createTransfer()
+{
+	FileTransfer *ft = new FileTransfer(this);
+	return ft;
+}
+
+FileTransfer *FileTransferManager::takeIncoming()
+{
+	if(d->incoming.isEmpty())
+		return 0;
+
+	FileTransfer *ft = d->incoming.getFirst();
+	d->incoming.removeRef(ft);
+
+	// move to active list
+	d->list.append(ft);
+	return ft;
+}
+
+void FileTransferManager::pft_incoming(const FTRequest &req)
+{
+	bool found = false;
+	for(QStringList::ConstIterator it = req.streamTypes.begin(); it != req.streamTypes.end(); ++it) {
+		if((*it) == "http://jabber.org/protocol/bytestreams") {
+			found = true;
+			break;
+		}
+	}
+	if(!found) {
+		d->pft->respondError(req.from, req.iq_id, 400, "No valid stream types");
+		return;
+	}
+	if(!d->client->s5bManager()->isAcceptableSID(req.from, req.id)) {
+		d->pft->respondError(req.from, req.iq_id, 400, "SID in use");
+		return;
+	}
+
+	FileTransfer *ft = new FileTransfer(this);
+	ft->man_waitForAccept(req);
+	d->incoming.append(ft);
+	incomingReady();
+}
+
+void FileTransferManager::s5b_incomingReady(S5BConnection *c)
+{
+	QPtrListIterator<FileTransfer> it(d->list);
+	FileTransfer *ft = 0;
+	for(FileTransfer *i; (i = it.current()); ++it) {
+		if(i->d->needStream && i->d->peer.compare(c->peer()) && i->d->id == c->sid()) {
+			ft = i;
+			break;
+		}
+	}
+	if(!ft) {
+		c->close();
+		delete c;
+		return;
+	}
+	ft->takeConnection(c);
+}
+
+QString FileTransferManager::link(FileTransfer *ft)
+{
+	d->list.append(ft);
+	return d->client->s5bManager()->genUniqueSID(ft->d->peer);
+}
+
+void FileTransferManager::con_accept(FileTransfer *ft)
+{
+	ft->d->needStream = true;
+	d->pft->respondSuccess(ft->d->peer, ft->d->iq_id, ft->d->rangeOffset, ft->d->rangeLength, ft->d->streamType);
+}
+
+void FileTransferManager::con_reject(FileTransfer *ft)
+{
+	d->pft->respondError(ft->d->peer, ft->d->iq_id, 403, "Declined");
+}
+
+void FileTransferManager::unlink(FileTransfer *ft)
+{
+	d->list.removeRef(ft);
+}
+
+//----------------------------------------------------------------------------
+// JT_FT
+//----------------------------------------------------------------------------
+class JT_FT::Private
+{
+public:
+	QDomElement iq;
+	Jid to;
+	Q_LLONG size, rangeOffset, rangeLength;
+	QString streamType;
+	QStringList streamTypes;
+};
+
+JT_FT::JT_FT(Task *parent)
+:Task(parent)
+{
+	d = new Private;
+}
+
+JT_FT::~JT_FT()
+{
+	delete d;
+}
+
+void JT_FT::request(const Jid &to, const QString &_id, const QString &fname, Q_LLONG size, const QString &desc, const QStringList &streamTypes)
+{
+	QDomElement iq;
+	d->to = to;
+	iq = createIQ(doc(), "set", to.full(), id());
+	QDomElement si = doc()->createElement("si");
+	si.setAttribute("xmlns", "http://jabber.org/protocol/si");
+	si.setAttribute("id", _id);
+	si.setAttribute("profile", "http://jabber.org/protocol/si/profile/file-transfer");
+
+	QDomElement file = doc()->createElement("file");
+	file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
+	file.setAttribute("name", fname);
+	file.setAttribute("size", QString::number(size));
+	if(!desc.isEmpty()) {
+		QDomElement de = doc()->createElement("desc");
+		de.appendChild(doc()->createTextNode(desc));
+		file.appendChild(de);
+	}
+	QDomElement range = doc()->createElement("range");
+	file.appendChild(range);
+	si.appendChild(file);
+
+	QDomElement feature = doc()->createElement("feature");
+	feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
+	QDomElement x = doc()->createElement("x");
+	x.setAttribute("xmlns", "jabber:x:data");
+	x.setAttribute("type", "form");
+
+	QDomElement field = doc()->createElement("field");
+	field.setAttribute("var", "stream-method");
+	field.setAttribute("type", "list-single");
+	for(QStringList::ConstIterator it = streamTypes.begin(); it != streamTypes.end(); ++it) {
+		QDomElement option = doc()->createElement("option");
+		QDomElement value = doc()->createElement("value");
+		value.appendChild(doc()->createTextNode(*it));
+		option.appendChild(value);
+		field.appendChild(option);
+	}
+
+	x.appendChild(field);
+	feature.appendChild(x);
+
+	si.appendChild(feature);
+	iq.appendChild(si);
+
+	d->streamTypes = streamTypes;
+	d->size = size;
+	d->iq = iq;
+}
+
+Q_LLONG JT_FT::rangeOffset() const
+{
+	return d->rangeOffset;
+}
+
+Q_LLONG JT_FT::rangeLength() const
+{
+	return d->rangeLength;
+}
+
+QString JT_FT::streamType() const
+{
+	return d->streamType;
+}
+
+void JT_FT::onGo()
+{
+	send(d->iq);
+}
+
+bool JT_FT::take(const QDomElement &x)
+{
+	if(!iqVerify(x, d->to, id()))
+		return false;
+
+	if(x.attribute("type") == "result") {
+		QDomElement si = firstChildElement(x);
+		if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") {
+			setError(900, "");
+			return true;
+		}
+
+		QString id = si.attribute("id");
+
+		Q_LLONG range_offset = 0;
+		Q_LLONG range_length = 0;
+
+		QDomElement file = si.elementsByTagName("file").item(0).toElement();
+		if(!file.isNull()) {
+			QDomElement range = file.elementsByTagName("range").item(0).toElement();
+			if(!range.isNull()) {
+				int x;
+				bool ok;
+				if(range.hasAttribute("offset")) {
+#if QT_VERSION >= 0x030200
+					x = range.attribute("offset").toLongLong(&ok);
+#else
+					x = range.attribute("offset").toLong(&ok);
+#endif
+					if(!ok || x < 0) {
+						setError(900, "");
+						return true;
+					}
+					range_offset = x;
+				}
+				if(range.hasAttribute("length")) {
+#if QT_VERSION >= 0x030200
+					x = range.attribute("length").toLongLong(&ok);
+#else
+					x = range.attribute("length").toLong(&ok);
+#endif
+					if(!ok || x < 0) {
+						setError(900, "");
+						return true;
+					}
+					range_length = x;
+				}
+			}
+		}
+
+		if(range_offset > d->size || (range_length > (d->size - range_offset))) {
+			setError(900, "");
+			return true;
+		}
+
+		QString streamtype;
+		QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
+		if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
+			QDomElement x = feature.elementsByTagName("x").item(0).toElement();
+			if(!x.isNull() && x.attribute("type") == "submit") {
+				QDomElement field = x.elementsByTagName("field").item(0).toElement();
+				if(!field.isNull() && field.attribute("var") == "stream-method") {
+					QDomElement value = field.elementsByTagName("value").item(0).toElement();
+					if(!value.isNull())
+						streamtype = value.text();
+				}
+			}
+		}
+
+		// must be one of the offered streamtypes
+		bool found = false;
+		for(QStringList::ConstIterator it = d->streamTypes.begin(); it != d->streamTypes.end(); ++it) {
+			if((*it) == streamtype) {
+				found = true;
+				break;
+			}
+		}
+		if(!found)
+			return true;
+
+		d->rangeOffset = range_offset;
+		d->rangeLength = range_length;
+		d->streamType = streamtype;
+		setSuccess();
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_PushFT
+//----------------------------------------------------------------------------
+JT_PushFT::JT_PushFT(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushFT::~JT_PushFT()
+{
+}
+
+void JT_PushFT::respondSuccess(const Jid &to, const QString &id, Q_LLONG rangeOffset, Q_LLONG rangeLength, const QString &streamType)
+{
+	QDomElement iq = createIQ(doc(), "result", to.full(), id);
+	QDomElement si = doc()->createElement("si");
+	si.setAttribute("xmlns", "http://jabber.org/protocol/si");
+
+	if(rangeOffset != 0 || rangeLength != 0) {
+		QDomElement file = doc()->createElement("file");
+		file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
+		QDomElement range = doc()->createElement("range");
+		if(rangeOffset > 0)
+			range.setAttribute("offset", QString::number(rangeOffset));
+		if(rangeLength > 0)
+			range.setAttribute("length", QString::number(rangeLength));
+		file.appendChild(range);
+		si.appendChild(file);
+	}
+
+	QDomElement feature = doc()->createElement("feature");
+	feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
+	QDomElement x = doc()->createElement("x");
+	x.setAttribute("xmlns", "jabber:x:data");
+	x.setAttribute("type", "submit");
+
+	QDomElement field = doc()->createElement("field");
+	field.setAttribute("var", "stream-method");
+	QDomElement value = doc()->createElement("value");
+	value.appendChild(doc()->createTextNode(streamType));
+	field.appendChild(value);
+
+	x.appendChild(field);
+	feature.appendChild(x);
+
+	si.appendChild(feature);
+	iq.appendChild(si);
+	send(iq);
+}
+
+void JT_PushFT::respondError(const Jid &to, const QString &id, int code, const QString &str)
+{
+	QDomElement iq = createIQ(doc(), "error", to.full(), id);
+	QDomElement err = textTag(doc(), "error", str);
+	err.setAttribute("code", QString::number(code));
+	iq.appendChild(err);
+	send(iq);
+}
+
+bool JT_PushFT::take(const QDomElement &e)
+{
+	// must be an iq-set tag
+	if(e.tagName() != "iq")
+		return false;
+	if(e.attribute("type") != "set")
+		return false;
+
+	QDomElement si = firstChildElement(e);
+	if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si")
+		return false;
+	if(si.attribute("profile") != "http://jabber.org/protocol/si/profile/file-transfer")
+		return false;
+
+	Jid from(e.attribute("from"));
+	QString id = si.attribute("id");
+
+	QDomElement file = si.elementsByTagName("file").item(0).toElement();
+	if(file.isNull())
+		return true;
+
+	QString fname = file.attribute("name");
+	if(fname.isEmpty()) {
+		respondError(from, id, 400, "Bad file name");
+		return true;
+	}
+
+	// ensure kosher
+	{
+		QFileInfo fi(fname);
+		fname = fi.fileName();
+	}
+
+	bool ok;
+#if QT_VERSION >= 0x030200
+	Q_LLONG size = file.attribute("size").toLongLong(&ok);
+#else
+	Q_LLONG size = file.attribute("size").toLong(&ok);
+#endif
+	if(!ok || size < 0) {
+		respondError(from, id, 400, "Bad file size");
+		return true;
+	}
+
+	QString desc;
+	QDomElement de = file.elementsByTagName("desc").item(0).toElement();
+	if(!de.isNull())
+		desc = de.text();
+
+	bool rangeSupported = false;
+	QDomElement range = file.elementsByTagName("range").item(0).toElement();
+	if(!range.isNull())
+		rangeSupported = true;
+
+	QStringList streamTypes;
+	QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
+	if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
+		QDomElement x = feature.elementsByTagName("x").item(0).toElement();
+		if(!x.isNull() /*&& x.attribute("type") == "form"*/) {
+			QDomElement field = x.elementsByTagName("field").item(0).toElement();
+			if(!field.isNull() && field.attribute("var") == "stream-method" && field.attribute("type") == "list-single") {
+				QDomNodeList nl = field.elementsByTagName("option");
+				for(uint n = 0; n < nl.count(); ++n) {
+					QDomElement e = nl.item(n).toElement();
+					QDomElement value = e.elementsByTagName("value").item(0).toElement();
+					if(!value.isNull())
+						streamTypes += value.text();
+				}
+			}
+		}
+	}
+
+	FTRequest r;
+	r.from = from;
+	r.iq_id = e.attribute("id");
+	r.id = id;
+	r.fname = fname;
+	r.size = size;
+	r.desc = desc;
+	r.rangeSupported = rangeSupported;
+	r.streamTypes = streamTypes;
+
+	incoming(r);
+	return true;
+}
Index: psi/tags/0.10-os2-b1/iris/jabber/filetransfer.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/jabber/filetransfer.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/jabber/filetransfer.h	(revision 38)
@@ -0,0 +1,170 @@
+/*
+ * filetransfer.h - File Transfer
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef XMPP_FILETRANSFER_H
+#define XMPP_FILETRANSFER_H
+
+#include"im.h"
+
+#if QT_VERSION < 0x030200
+typedef long int Q_LLONG;
+#endif
+
+namespace XMPP
+{
+	class S5BConnection;
+	struct FTRequest;
+
+	class FileTransfer : public QObject
+	{
+		Q_OBJECT
+	public:
+		enum { ErrReject, ErrNeg, ErrConnect, ErrProxy, ErrStream };
+		enum { Idle, Requesting, Connecting, WaitingForAccept, Active };
+		~FileTransfer();
+
+		void setProxy(const Jid &proxy);
+
+		// send
+		void sendFile(const Jid &to, const QString &fname, Q_LLONG size, const QString &desc);
+		Q_LLONG offset() const;
+		Q_LLONG length() const;
+		int dataSizeNeeded() const;
+		void writeFileData(const QByteArray &a);
+
+		// receive
+		Jid peer() const;
+		QString fileName() const;
+		Q_LLONG fileSize() const;
+		QString description() const;
+		bool rangeSupported() const;
+		void accept(Q_LLONG offset=0, Q_LLONG length=0);
+
+		// both
+		void close(); // reject, or stop sending/receiving
+		S5BConnection *s5bConnection() const; // active link
+
+	signals:
+		void accepted(); // indicates S5BConnection has started
+		void connected();
+		void readyRead(const QByteArray &a);
+		void bytesWritten(int);
+		void error(int);
+
+	private slots:
+		void ft_finished();
+		void s5b_connected();
+		void s5b_connectionClosed();
+		void s5b_readyRead();
+		void s5b_bytesWritten(int);
+		void s5b_error(int);
+		void doAccept();
+
+	private:
+		class Private;
+		Private *d;
+
+		void reset();
+
+		friend class FileTransferManager;
+		FileTransfer(FileTransferManager *, QObject *parent=0);
+		void man_waitForAccept(const FTRequest &req);
+		void takeConnection(S5BConnection *c);
+	};
+
+	class FileTransferManager : public QObject
+	{
+		Q_OBJECT
+	public:
+		FileTransferManager(Client *);
+		~FileTransferManager();
+
+		Client *client() const;
+		FileTransfer *createTransfer();
+		FileTransfer *takeIncoming();
+
+	signals:
+		void incomingReady();
+
+	private slots:
+		void pft_incoming(const FTRequest &req);
+
+	private:
+		class Private;
+		Private *d;
+
+		friend class Client;
+		void s5b_incomingReady(S5BConnection *);
+
+		friend class FileTransfer;
+		QString link(FileTransfer *);
+		void con_accept(FileTransfer *);
+		void con_reject(FileTransfer *);
+		void unlink(FileTransfer *);
+	};
+
+	class JT_FT : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_FT(Task *parent);
+		~JT_FT();
+
+		void request(const Jid &to, const QString &id, const QString &fname, Q_LLONG size, const QString &desc, const QStringList &streamTypes);
+		Q_LLONG rangeOffset() const;
+		Q_LLONG rangeLength() const;
+		QString streamType() const;
+
+		void onGo();
+		bool take(const QDomElement &);
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	struct FTRequest
+	{
+		Jid from;
+		QString iq_id, id;
+		QString fname;
+		Q_LLONG size;
+		QString desc;
+		bool rangeSupported;
+		QStringList streamTypes;
+	};
+	class JT_PushFT : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_PushFT(Task *parent);
+		~JT_PushFT();
+
+		void respondSuccess(const Jid &to, const QString &id, Q_LLONG rangeOffset, Q_LLONG rangeLength, const QString &streamType);
+		void respondError(const Jid &to, const QString &id, int code, const QString &str);
+
+		bool take(const QDomElement &);
+
+	signals:
+		void incoming(const FTRequest &req);
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/jabber/s5b.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/jabber/s5b.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/jabber/s5b.cpp	(revision 38)
@@ -0,0 +1,2533 @@
+/*
+ * s5b.cpp - direct connection protocol via tcp
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"s5b.h"
+
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<stdlib.h>
+#include<qca.h>
+#include"xmpp_xmlcommon.h"
+#include"hash.h"
+#include"socks.h"
+#include"safedelete.h"
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+#else
+# include <netinet/in.h>
+#endif
+
+#define MAXSTREAMHOSTS 5
+
+//#define S5B_DEBUG
+
+namespace XMPP {
+
+static QString makeKey(const QString &sid, const Jid &initiator, const Jid &target)
+{
+	QString str = sid + initiator.full() + target.full();
+	return QCA::SHA1::hashToString(str.utf8());
+}
+
+static bool haveHost(const StreamHostList &list, const Jid &j)
+{
+	for(StreamHostList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+		if((*it).jid().compare(j))
+			return true;
+	}
+	return false;
+}
+
+class S5BManager::Item : public QObject
+{
+	Q_OBJECT
+public:
+	enum { Idle, Initiator, Target, Active };
+	enum { ErrRefused, ErrConnect, ErrWrongHost, ErrProxy };
+	enum { Unknown, Fast, NotFast };
+	S5BManager *m;
+	int state;
+	QString sid, key, out_key, out_id, in_id;
+	Jid self, peer;
+	StreamHostList in_hosts;
+	JT_S5B *task, *proxy_task;
+	SocksClient *client, *client_out;
+	SocksUDP *client_udp, *client_out_udp;
+	S5BConnector *conn, *proxy_conn;
+	bool wantFast;
+	StreamHost proxy;
+	int targetMode; // initiator sets this once it figures it out
+	bool fast; // target sets this
+	bool activated;
+	bool lateProxy;
+	bool connSuccess;
+	bool localFailed, remoteFailed;
+	bool allowIncoming;
+	bool udp;
+	int statusCode;
+	Jid activatedStream;
+
+	Item(S5BManager *manager);
+	~Item();
+
+	void reset();
+	void startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool udp);
+	void startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool fast, bool udp);
+	void handleFast(const StreamHostList &hosts, const QString &iq_id);
+
+	void doOutgoing();
+	void doIncoming();
+	void setIncomingClient(SocksClient *sc);
+	void incomingActivate(const Jid &streamHost);
+
+signals:
+	void accepted();
+	void tryingHosts(const StreamHostList &list);
+	void proxyConnect();
+	void waitingForActivation();
+	void connected();
+	void error(int);
+
+private slots:
+	void jt_finished();
+	void conn_result(bool b);
+	void proxy_result(bool b);
+	void proxy_finished();
+	void sc_readyRead();
+	void sc_bytesWritten(int);
+	void sc_error(int);
+
+private:
+	void doConnectError();
+	void tryActivation();
+	void checkForActivation();
+	void checkFailure();
+	void finished();
+};
+
+//----------------------------------------------------------------------------
+// S5BDatagram
+//----------------------------------------------------------------------------
+S5BDatagram::S5BDatagram()
+{
+	_source = 0;
+	_dest = 0;
+}
+
+S5BDatagram::S5BDatagram(int source, int dest, const QByteArray &data)
+{
+	_source = source;
+	_dest = dest;
+	_buf = data;
+}
+
+int S5BDatagram::sourcePort() const
+{
+	return _source;
+}
+
+int S5BDatagram::destPort() const
+{
+	return _dest;
+}
+
+QByteArray S5BDatagram::data() const
+{
+	return _buf;
+}
+
+//----------------------------------------------------------------------------
+// S5BConnection
+//----------------------------------------------------------------------------
+class S5BConnection::Private
+{
+public:
+	S5BManager *m;
+	SocksClient *sc;
+	SocksUDP *su;
+	int state;
+	Jid peer;
+	QString sid;
+	bool remote;
+	bool switched;
+	bool notifyRead, notifyClose;
+	int id;
+	S5BRequest req;
+	Jid proxy;
+	Mode mode;
+	QPtrList<S5BDatagram> dglist;
+};
+
+static int id_conn = 0;
+static int num_conn = 0;
+
+S5BConnection::S5BConnection(S5BManager *m, QObject *parent)
+:ByteStream(parent)
+{
+	d = new Private;
+	d->m = m;
+	d->sc = 0;
+	d->su = 0;
+
+	++num_conn;
+	d->id = id_conn++;
+#ifdef S5B_DEBUG
+	printf("S5BConnection[%d]: constructing, count=%d, %p\n", d->id, num_conn, this);
+#endif
+
+	reset();
+}
+
+S5BConnection::~S5BConnection()
+{
+	reset(true);
+
+	--num_conn;
+#ifdef S5B_DEBUG
+	printf("S5BConnection[%d]: destructing, count=%d\n", d->id, num_conn);
+#endif
+
+	delete d;
+}
+
+void S5BConnection::reset(bool clear)
+{
+	d->m->con_unlink(this);
+	if(clear && d->sc) {
+		delete d->sc;
+		d->sc = 0;
+	}
+	delete d->su;
+	d->su = 0;
+	if(clear) {
+		d->dglist.setAutoDelete(true);
+		d->dglist.clear();
+		d->dglist.setAutoDelete(false);
+	}
+	d->state = Idle;
+	d->peer = Jid();
+	d->sid = QString();
+	d->remote = false;
+	d->switched = false;
+	d->notifyRead = false;
+	d->notifyClose = false;
+}
+
+Jid S5BConnection::proxy() const
+{
+	return d->proxy;
+}
+
+void S5BConnection::setProxy(const Jid &proxy)
+{
+	d->proxy = proxy;
+}
+
+void S5BConnection::connectToJid(const Jid &peer, const QString &sid, Mode m)
+{
+	reset(true);
+	if(!d->m->isAcceptableSID(peer, sid))
+		return;
+
+	d->peer = peer;
+	d->sid = sid;
+	d->state = Requesting;
+	d->mode = m;
+#ifdef S5B_DEBUG
+	printf("S5BConnection[%d]: connecting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+	d->m->con_connect(this);
+}
+
+void S5BConnection::accept()
+{
+	if(d->state != WaitingForAccept)
+		return;
+
+	d->state = Connecting;
+#ifdef S5B_DEBUG
+	printf("S5BConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+	d->m->con_accept(this);
+}
+
+void S5BConnection::close()
+{
+	if(d->state == Idle)
+		return;
+
+	if(d->state == WaitingForAccept)
+		d->m->con_reject(this);
+	else if(d->state == Active)
+		d->sc->close();
+#ifdef S5B_DEBUG
+	printf("S5BConnection[%d]: closing %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+	reset();
+}
+
+Jid S5BConnection::peer() const
+{
+	return d->peer;
+}
+
+QString S5BConnection::sid() const
+{
+	return d->sid;
+}
+
+bool S5BConnection::isRemote() const
+{
+	return d->remote;
+}
+
+S5BConnection::Mode S5BConnection::mode() const
+{
+	return d->mode;
+}
+
+int S5BConnection::state() const
+{
+	return d->state;
+}
+
+bool S5BConnection::isOpen() const
+{
+	if(d->state == Active)
+		return true;
+	else
+		return false;
+}
+
+void S5BConnection::write(const QByteArray &buf)
+{
+	if(d->state == Active && d->mode == Stream)
+		d->sc->write(buf);
+}
+
+QByteArray S5BConnection::read(int bytes)
+{
+	if(d->sc)
+		return d->sc->read(bytes);
+	else
+		return QByteArray();
+}
+
+int S5BConnection::bytesAvailable() const
+{
+	if(d->sc)
+		return d->sc->bytesAvailable();
+	else
+		return 0;
+}
+
+int S5BConnection::bytesToWrite() const
+{
+	if(d->state == Active)
+		return d->sc->bytesToWrite();
+	else
+		return 0;
+}
+
+void S5BConnection::writeDatagram(const S5BDatagram &i)
+{
+	QByteArray buf(i.data().size() + 4);
+	ushort ssp = htons(i.sourcePort());
+	ushort sdp = htons(i.destPort());
+	QByteArray data = i.data();
+	memcpy(buf.data(), &ssp, 2);
+	memcpy(buf.data() + 2, &sdp, 2);
+	memcpy(buf.data() + 4, data.data(), data.size());
+	sendUDP(buf);
+}
+
+S5BDatagram S5BConnection::readDatagram()
+{
+	if(d->dglist.isEmpty())
+		return S5BDatagram();
+	S5BDatagram *i = d->dglist.getFirst();
+	d->dglist.removeRef(i);
+	S5BDatagram val = *i;
+	delete i;
+	return val;
+}
+
+int S5BConnection::datagramsAvailable() const
+{
+	return d->dglist.count();
+}
+
+void S5BConnection::man_waitForAccept(const S5BRequest &r)
+{
+	d->state = WaitingForAccept;
+	d->remote = true;
+	d->req = r;
+	d->peer = r.from;
+	d->sid = r.sid;
+	d->mode = r.udp ? Datagram : Stream;
+}
+
+void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp)
+{
+	d->sc = sc;
+	connect(d->sc, SIGNAL(connectionClosed()), SLOT(sc_connectionClosed()));
+	connect(d->sc, SIGNAL(delayedCloseFinished()), SLOT(sc_delayedCloseFinished()));
+	connect(d->sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+	connect(d->sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+	connect(d->sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+	if(sc_udp) {
+		d->su = sc_udp;
+		connect(d->su, SIGNAL(packetReady(const QByteArray &)), SLOT(su_packetReady(const QByteArray &)));
+	}
+
+	d->state = Active;
+#ifdef S5B_DEBUG
+	printf("S5BConnection[%d]: %s [%s] <<< success >>>\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+
+	// bytes already in the stream?
+	if(d->sc->bytesAvailable()) {
+#ifdef S5B_DEBUG
+		printf("Stream has %d bytes in it.\n", d->sc->bytesAvailable());
+#endif
+		d->notifyRead = true;
+	}
+	// closed before it got here?
+	if(!d->sc->isOpen()) {
+#ifdef S5B_DEBUG
+		printf("Stream was closed before S5B request finished?\n");
+#endif
+		d->notifyClose = true;
+	}
+	if(d->notifyRead || d->notifyClose)
+		QTimer::singleShot(0, this, SLOT(doPending()));
+	connected();
+}
+
+void S5BConnection::doPending()
+{
+	if(d->notifyRead) {
+		if(d->notifyClose)
+			QTimer::singleShot(0, this, SLOT(doPending()));
+		sc_readyRead();
+	}
+	else if(d->notifyClose)
+		sc_connectionClosed();
+}
+
+void S5BConnection::man_udpReady(const QByteArray &buf)
+{
+	handleUDP(buf);
+}
+
+void S5BConnection::man_failed(int x)
+{
+	reset(true);
+	if(x == S5BManager::Item::ErrRefused)
+		error(ErrRefused);
+	if(x == S5BManager::Item::ErrConnect)
+		error(ErrConnect);
+	if(x == S5BManager::Item::ErrWrongHost)
+		error(ErrConnect);
+	if(x == S5BManager::Item::ErrProxy)
+		error(ErrProxy);
+}
+
+void S5BConnection::sc_connectionClosed()
+{
+	// if we have a pending read notification, postpone close
+	if(d->notifyRead) {
+#ifdef S5B_DEBUG
+		printf("closed while pending read\n");
+#endif
+		d->notifyClose = true;
+		return;
+	}
+	d->notifyClose = false;
+	reset();
+	connectionClosed();
+}
+
+void S5BConnection::sc_delayedCloseFinished()
+{
+	// echo
+	delayedCloseFinished();
+}
+
+void S5BConnection::sc_readyRead()
+{
+	if(d->mode == Datagram) {
+		// throw the data away
+		d->sc->read();
+		return;
+	}
+
+	d->notifyRead = false;
+	// echo
+	readyRead();
+}
+
+void S5BConnection::sc_bytesWritten(int x)
+{
+	// echo
+	bytesWritten(x);
+}
+
+void S5BConnection::sc_error(int)
+{
+	reset();
+	error(ErrSocket);
+}
+
+void S5BConnection::su_packetReady(const QByteArray &buf)
+{
+	handleUDP(buf);
+}
+
+void S5BConnection::handleUDP(const QByteArray &buf)
+{
+	// must be at least 4 bytes, to accomodate virtual ports
+	if(buf.size() < 4)
+		return; // drop
+
+	ushort ssp, sdp;
+	memcpy(&ssp, buf.data(), 2);
+	memcpy(&sdp, buf.data() + 2, 2);
+	int source = ntohs(ssp);
+	int dest = ntohs(sdp);
+	QByteArray data(buf.size() - 4);
+	memcpy(data.data(), buf.data() + 4, data.size());
+	d->dglist.append(new S5BDatagram(source, dest, data));
+
+	datagramReady();
+}
+
+void S5BConnection::sendUDP(const QByteArray &buf)
+{
+	if(d->su)
+		d->su->write(buf);
+	else
+		d->m->con_sendUDP(this, buf);
+}
+
+//----------------------------------------------------------------------------
+// S5BManager
+//----------------------------------------------------------------------------
+class S5BManager::Entry
+{
+public:
+	Entry()
+	{
+		i = 0;
+		query = 0;
+		udp_init = false;
+	}
+
+	~Entry()
+	{
+		delete query;
+	}
+
+	S5BConnection *c;
+	Item *i;
+	QString sid;
+	JT_S5B *query;
+	StreamHost proxyInfo;
+	QGuardedPtr<S5BServer> relatedServer;
+
+	bool udp_init;
+	QHostAddress udp_addr;
+	int udp_port;
+};
+
+class S5BManager::Private
+{
+public:
+	Client *client;
+	S5BServer *serv;
+	QPtrList<Entry> activeList;
+	S5BConnectionList incomingConns;
+	JT_PushS5B *ps;
+};
+
+S5BManager::S5BManager(Client *parent)
+:QObject(parent)
+{
+	// S5B needs SHA1
+	if(!QCA::isSupported(QCA::CAP_SHA1))
+		QCA::insertProvider(createProviderHash());
+
+	d = new Private;
+	d->client = parent;
+	d->serv = 0;
+	d->activeList.setAutoDelete(true);
+
+	d->ps = new JT_PushS5B(d->client->rootTask());
+	connect(d->ps, SIGNAL(incoming(const S5BRequest &)), SLOT(ps_incoming(const S5BRequest &)));
+	connect(d->ps, SIGNAL(incomingUDPSuccess(const Jid &, const QString &)), SLOT(ps_incomingUDPSuccess(const Jid &, const QString &)));
+	connect(d->ps, SIGNAL(incomingActivate(const Jid &, const QString &, const Jid &)), SLOT(ps_incomingActivate(const Jid &, const QString &, const Jid &)));
+}
+
+S5BManager::~S5BManager()
+{
+	setServer(0);
+	d->incomingConns.setAutoDelete(true);
+	d->incomingConns.clear();
+	delete d->ps;
+	delete d;
+}
+
+Client *S5BManager::client() const
+{
+	return d->client;
+}
+
+S5BServer *S5BManager::server() const
+{
+	return d->serv;
+}
+
+void S5BManager::setServer(S5BServer *serv)
+{
+	if(d->serv) {
+		d->serv->unlink(this);
+		d->serv = 0;
+	}
+
+	if(serv) {
+		d->serv = serv;
+		d->serv->link(this);
+	}
+}
+
+S5BConnection *S5BManager::createConnection()
+{
+	S5BConnection *c = new S5BConnection(this);
+	return c;
+}
+
+S5BConnection *S5BManager::takeIncoming()
+{
+	if(d->incomingConns.isEmpty())
+		return 0;
+
+	S5BConnection *c = d->incomingConns.getFirst();
+	d->incomingConns.removeRef(c);
+
+	// move to activeList
+	Entry *e = new Entry;
+	e->c = c;
+	e->sid = c->d->sid;
+	d->activeList.append(e);
+
+	return c;
+}
+
+void S5BManager::ps_incoming(const S5BRequest &req)
+{
+#ifdef S5B_DEBUG
+	printf("S5BManager: incoming from %s\n", req.from.full().latin1());
+#endif
+
+	bool ok = false;
+	// ensure we don't already have an incoming connection from this peer+sid
+	S5BConnection *c = findIncoming(req.from, req.sid);
+	if(!c) {
+		// do we have an active entry with this sid already?
+		Entry *e = findEntryBySID(req.from, req.sid);
+		if(e) {
+			if(e->i) {
+				// loopback
+				if(req.from.compare(d->client->jid()) && (req.id == e->i->out_id)) {
+#ifdef S5B_DEBUG
+					printf("ALLOWED: loopback\n");
+#endif
+					ok = true;
+				}
+				// allowed by 'fast mode'
+				else if(e->i->state == Item::Initiator && e->i->targetMode == Item::Unknown) {
+#ifdef S5B_DEBUG
+					printf("ALLOWED: fast-mode\n");
+#endif
+					e->i->handleFast(req.hosts, req.id);
+					return;
+				}
+			}
+		}
+		else {
+#ifdef S5B_DEBUG
+			printf("ALLOWED: we don't have it\n");
+#endif
+			ok = true;
+		}
+	}
+	if(!ok) {
+		d->ps->respondError(req.from, req.id, 406, "SID in use");
+		return;
+	}
+
+	// create an incoming connection
+	c = new S5BConnection(this);
+	c->man_waitForAccept(req);
+	d->incomingConns.append(c);
+	incomingReady();
+}
+
+void S5BManager::ps_incomingUDPSuccess(const Jid &from, const QString &key)
+{
+	Entry *e = findEntryByHash(key);
+	if(e && e->i) {
+		if(e->i->conn)
+			e->i->conn->man_udpSuccess(from);
+		else if(e->i->proxy_conn)
+			e->i->proxy_conn->man_udpSuccess(from);
+	}
+}
+
+void S5BManager::ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost)
+{
+	Entry *e = findEntryBySID(from, sid);
+	if(e && e->i)
+		e->i->incomingActivate(streamHost);
+}
+
+void S5BManager::doSuccess(const Jid &peer, const QString &id, const Jid &streamHost)
+{
+	d->ps->respondSuccess(peer, id, streamHost);
+}
+
+void S5BManager::doError(const Jid &peer, const QString &id, int code, const QString &str)
+{
+	d->ps->respondError(peer, id, code, str);
+}
+
+void S5BManager::doActivate(const Jid &peer, const QString &sid, const Jid &streamHost)
+{
+	d->ps->sendActivate(peer, sid, streamHost);
+}
+
+QString S5BManager::genUniqueSID(const Jid &peer) const
+{
+	// get unused key
+	QString sid;
+	do {
+		sid = "s5b_";
+		for(int i = 0; i < 4; ++i) {
+			int word = rand() & 0xffff;
+			for(int n = 0; n < 4; ++n) {
+				QString s;
+				s.sprintf("%x", (word >> (n * 4)) & 0xf);
+				sid.append(s);
+			}
+		}
+	} while(!isAcceptableSID(peer, sid));
+	return sid;
+}
+
+bool S5BManager::isAcceptableSID(const Jid &peer, const QString &sid) const
+{
+	QString key = makeKey(sid, d->client->jid(), peer);
+	QString key_out = makeKey(sid, peer, d->client->jid());
+
+	// if we have a server, then check through it
+	if(d->serv) {
+		if(findServerEntryByHash(key) || findServerEntryByHash(key_out))
+			return false;
+	}
+	else {
+		if(findEntryByHash(key) || findEntryByHash(key_out))
+			return false;
+	}
+	return true;
+}
+
+S5BConnection *S5BManager::findIncoming(const Jid &from, const QString &sid) const
+{
+	QPtrListIterator<S5BConnection> it(d->incomingConns);
+	for(S5BConnection *c; (c = it.current()); ++it) {
+		if(c->d->peer.compare(from) && c->d->sid == sid)
+			return c;
+	}
+	return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntry(S5BConnection *c) const
+{
+	QPtrListIterator<Entry> it(d->activeList);
+	for(Entry *e; (e = it.current()); ++it) {
+		if(e->c == c)
+			return e;
+	}
+	return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntry(Item *i) const
+{
+	QPtrListIterator<Entry> it(d->activeList);
+	for(Entry *e; (e = it.current()); ++it) {
+		if(e->i == i)
+			return e;
+	}
+	return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntryByHash(const QString &key) const
+{
+	QPtrListIterator<Entry> it(d->activeList);
+	for(Entry *e; (e = it.current()); ++it) {
+		if(e->i && e->i->key == key)
+			return e;
+	}
+	return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntryBySID(const Jid &peer, const QString &sid) const
+{
+	QPtrListIterator<Entry> it(d->activeList);
+	for(Entry *e; (e = it.current()); ++it) {
+		if(e->i && e->i->peer.compare(peer) && e->sid == sid)
+			return e;
+	}
+	return 0;
+}
+
+S5BManager::Entry *S5BManager::findServerEntryByHash(const QString &key) const
+{
+	const QPtrList<S5BManager> &manList = d->serv->managerList();
+	QPtrListIterator<S5BManager> it(manList);
+	for(S5BManager *m; (m = it.current()); ++it) {
+		Entry *e = m->findEntryByHash(key);
+		if(e)
+			return e;
+	}
+	return 0;
+}
+
+bool S5BManager::srv_ownsHash(const QString &key) const
+{
+	if(findEntryByHash(key))
+		return true;
+	return false;
+}
+
+void S5BManager::srv_incomingReady(SocksClient *sc, const QString &key)
+{
+	Entry *e = findEntryByHash(key);
+	if(!e->i->allowIncoming) {
+		sc->requestDeny();
+		SafeDelete::deleteSingle(sc);
+		return;
+	}
+	if(e->c->d->mode == S5BConnection::Datagram)
+		sc->grantUDPAssociate("", 0);
+	else
+		sc->grantConnect();
+	e->relatedServer = (S5BServer *)sender();
+	e->i->setIncomingClient(sc);
+}
+
+void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data)
+{
+	Entry *e = findEntryByHash(key);
+	if(!e->c->d->mode != S5BConnection::Datagram)
+		return; // this key isn't in udp mode?  drop!
+
+	if(init) {
+		if(e->udp_init)
+			return; // only init once
+
+		// lock on to this sender
+		e->udp_addr = addr;
+		e->udp_port = port;
+		e->udp_init = true;
+
+		// reply that initialization was successful
+		d->ps->sendUDPSuccess(e->c->d->peer, key);
+		return;
+	}
+
+	// not initialized yet?  something went wrong
+	if(!e->udp_init)
+		return;
+
+	// must come from same source as when initialized
+	if(addr.toString() != e->udp_addr.toString() || port != e->udp_port)
+		return;
+
+	e->c->man_udpReady(data);
+}
+
+void S5BManager::srv_unlink()
+{
+	d->serv = 0;
+}
+
+void S5BManager::con_connect(S5BConnection *c)
+{
+	if(findEntry(c))
+		return;
+	Entry *e = new Entry;
+	e->c = c;
+	e->sid = c->d->sid;
+	d->activeList.append(e);
+
+	if(c->d->proxy.isValid()) {
+		queryProxy(e);
+		return;
+	}
+	entryContinue(e);
+}
+
+void S5BManager::con_accept(S5BConnection *c)
+{
+	Entry *e = findEntry(c);
+	if(!e)
+		return;
+
+	if(e->c->d->req.fast) {
+		if(targetShouldOfferProxy(e)) {
+			queryProxy(e);
+			return;
+		}
+	}
+	entryContinue(e);
+}
+
+void S5BManager::con_reject(S5BConnection *c)
+{
+	d->ps->respondError(c->d->peer, c->d->req.id, 406, "Not acceptable");
+}
+
+void S5BManager::con_unlink(S5BConnection *c)
+{
+	Entry *e = findEntry(c);
+	if(!e)
+		return;
+
+	// active incoming request?  cancel it
+	if(e->i && e->i->conn)
+		d->ps->respondError(e->i->peer, e->i->out_id, 406, "Not acceptable");
+	delete e->i;
+	d->activeList.removeRef(e);
+}
+
+void S5BManager::con_sendUDP(S5BConnection *c, const QByteArray &buf)
+{
+	Entry *e = findEntry(c);
+	if(!e)
+		return;
+	if(!e->udp_init)
+		return;
+
+	if(e->relatedServer)
+		e->relatedServer->writeUDP(e->udp_addr, e->udp_port, buf);
+}
+
+void S5BManager::item_accepted()
+{
+	Item *i = (Item *)sender();
+	Entry *e = findEntry(i);
+
+	e->c->accepted(); // signal
+}
+
+void S5BManager::item_tryingHosts(const StreamHostList &list)
+{
+	Item *i = (Item *)sender();
+	Entry *e = findEntry(i);
+
+	e->c->tryingHosts(list); // signal
+}
+
+void S5BManager::item_proxyConnect()
+{
+	Item *i = (Item *)sender();
+	Entry *e = findEntry(i);
+
+	e->c->proxyConnect(); // signal
+}
+
+void S5BManager::item_waitingForActivation()
+{
+	Item *i = (Item *)sender();
+	Entry *e = findEntry(i);
+
+	e->c->waitingForActivation(); // signal
+}
+
+void S5BManager::item_connected()
+{
+	Item *i = (Item *)sender();
+	Entry *e = findEntry(i);
+
+	// grab the client
+	SocksClient *client = i->client;
+	i->client = 0;
+	SocksUDP *client_udp = i->client_udp;
+	i->client_udp = 0;
+
+	// give it to the connection
+	e->c->man_clientReady(client, client_udp);
+}
+
+void S5BManager::item_error(int x)
+{
+	Item *i = (Item *)sender();
+	Entry *e = findEntry(i);
+
+	e->c->man_failed(x);
+}
+
+void S5BManager::entryContinue(Entry *e)
+{
+	e->i = new Item(this);
+	e->i->proxy = e->proxyInfo;
+
+	connect(e->i, SIGNAL(accepted()), SLOT(item_accepted()));
+	connect(e->i, SIGNAL(tryingHosts(const StreamHostList &)), SLOT(item_tryingHosts(const StreamHostList &)));
+	connect(e->i, SIGNAL(proxyConnect()), SLOT(item_proxyConnect()));
+	connect(e->i, SIGNAL(waitingForActivation()), SLOT(item_waitingForActivation()));
+	connect(e->i, SIGNAL(connected()), SLOT(item_connected()));
+	connect(e->i, SIGNAL(error(int)), SLOT(item_error(int)));
+
+	if(e->c->isRemote()) {
+		const S5BRequest &req = e->c->d->req;
+		e->i->startTarget(e->sid, d->client->jid(), e->c->d->peer, req.hosts, req.id, req.fast, req.udp);
+	}
+	else {
+		e->i->startInitiator(e->sid, d->client->jid(), e->c->d->peer, true, e->c->d->mode == S5BConnection::Datagram ? true: false);
+		e->c->requesting(); // signal
+	}
+}
+
+void S5BManager::queryProxy(Entry *e)
+{
+	QGuardedPtr<QObject> self = this;
+	e->c->proxyQuery(); // signal
+	if(!self)
+		return;
+
+#ifdef S5B_DEBUG
+	printf("querying proxy: [%s]\n", e->c->d->proxy.full().latin1());
+#endif
+	e->query = new JT_S5B(d->client->rootTask());
+	connect(e->query, SIGNAL(finished()), SLOT(query_finished()));
+	e->query->requestProxyInfo(e->c->d->proxy);
+	e->query->go(true);
+}
+
+void S5BManager::query_finished()
+{
+	JT_S5B *query = (JT_S5B *)sender();
+	Entry *e;
+	bool found = false;
+	QPtrListIterator<Entry> it(d->activeList);
+	for(; (e = it.current()); ++it) {
+		if(e->query == query) {
+			found = true;
+			break;
+		}
+	}
+	if(!found)
+		return;
+	e->query = 0;
+
+#ifdef S5B_DEBUG
+	printf("query finished: ");
+#endif
+	if(query->success()) {
+		e->proxyInfo = query->proxyInfo();
+#ifdef S5B_DEBUG
+		printf("host/ip=[%s] port=[%d]\n", e->proxyInfo.host().latin1(), e->proxyInfo.port());
+#endif
+	}
+	else {
+#ifdef S5B_DEBUG
+		printf("fail\n");
+#endif
+	}
+
+	QGuardedPtr<QObject> self = this;
+	e->c->proxyResult(query->success()); // signal
+	if(!self)
+		return;
+
+	entryContinue(e);
+}
+
+bool S5BManager::targetShouldOfferProxy(Entry *e)
+{
+	if(!e->c->d->proxy.isValid())
+		return false;
+
+	// if target, don't offer any proxy if the initiator already did
+	const StreamHostList &hosts = e->c->d->req.hosts;
+	for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
+		if((*it).isProxy())
+			return false;
+	}
+
+	// ensure we don't offer the same proxy as the initiator
+	if(haveHost(hosts, e->c->d->proxy))
+		return false;
+
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// S5BManager::Item
+//----------------------------------------------------------------------------
+S5BManager::Item::Item(S5BManager *manager) : QObject(0)
+{
+	m = manager;
+	task = 0;
+	proxy_task = 0;
+	conn = 0;
+	proxy_conn = 0;
+	client_udp = 0;
+	client = 0;
+	client_out_udp = 0;
+	client_out = 0;
+	reset();
+}
+
+S5BManager::Item::~Item()
+{
+	reset();
+}
+
+void S5BManager::Item::reset()
+{
+	delete task;
+	task = 0;
+
+	delete proxy_task;
+	proxy_task = 0;
+
+	delete conn;
+	conn = 0;
+
+	delete proxy_conn;
+	proxy_conn = 0;
+
+	delete client_udp;
+	client_udp = 0;
+
+	delete client;
+	client = 0;
+
+	delete client_out_udp;
+	client_out_udp = 0;
+
+	delete client_out;
+	client_out = 0;
+
+	state = Idle;
+	wantFast = false;
+	targetMode = Unknown;
+	fast = false;
+	activated = false;
+	lateProxy = false;
+	connSuccess = false;
+	localFailed = false;
+	remoteFailed = false;
+	allowIncoming = false;
+	udp = false;
+}
+
+void S5BManager::Item::startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool _udp)
+{
+	sid = _sid;
+	self = _self;
+	peer = _peer;
+	key = makeKey(sid, self, peer);
+	out_key = makeKey(sid, peer, self);
+	wantFast = fast;
+	udp = _udp;
+
+#ifdef S5B_DEBUG
+	printf("S5BManager::Item initiating request %s [%s]\n", peer.full().latin1(), sid.latin1());
+#endif
+	state = Initiator;
+	doOutgoing();
+}
+
+void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool _fast, bool _udp)
+{
+	sid = _sid;
+	peer = _peer;
+	self = _self;
+	in_hosts = hosts;
+	in_id = iq_id;
+	fast = _fast;
+	key = makeKey(sid, self, peer);
+	out_key = makeKey(sid, peer, self);
+	udp = _udp;
+
+#ifdef S5B_DEBUG
+	printf("S5BManager::Item incoming request %s [%s]\n", peer.full().latin1(), sid.latin1());
+#endif
+	state = Target;
+	if(fast)
+		doOutgoing();
+	doIncoming();
+}
+
+void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq_id)
+{
+	targetMode = Fast;
+
+	QGuardedPtr<QObject> self = this;
+	accepted();
+	if(!self)
+		return;
+
+	// if we already have a stream, then bounce this request
+	if(client) {
+		m->doError(peer, iq_id, 406, "Not acceptable");
+	}
+	else {
+		in_hosts = hosts;
+		in_id = iq_id;
+		doIncoming();
+	}
+}
+
+void S5BManager::Item::doOutgoing()
+{
+	StreamHostList hosts;
+	S5BServer *serv = m->server();
+	if(serv && serv->isActive() && !haveHost(in_hosts, m->client()->jid())) {
+		QStringList hostList = serv->hostList();
+		for(QStringList::ConstIterator it = hostList.begin(); it != hostList.end(); ++it) {
+			StreamHost h;
+			h.setJid(m->client()->jid());
+			h.setHost(*it);
+			h.setPort(serv->port());
+			hosts += h;
+		}
+	}
+
+	// if the proxy is valid, then it's ok to add (the manager already ensured that it doesn't conflict)
+	if(proxy.jid().isValid())
+		hosts += proxy;
+
+	// if we're the target and we have no streamhosts of our own, then don't even bother with fast-mode
+	if(state == Target && hosts.isEmpty()) {
+		fast = false;
+		return;
+	}
+
+	allowIncoming = true;
+
+	task = new JT_S5B(m->client()->rootTask());
+	connect(task, SIGNAL(finished()), SLOT(jt_finished()));
+	task->request(peer, sid, hosts, state == Initiator ? wantFast : false, udp);
+	out_id = task->id();
+	task->go(true);
+}
+
+void S5BManager::Item::doIncoming()
+{
+	if(in_hosts.isEmpty()) {
+		doConnectError();
+		return;
+	}
+
+	StreamHostList list;
+	if(lateProxy) {
+		// take just the proxy streamhosts
+		for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
+			if((*it).isProxy())
+				list += *it;
+		}
+		lateProxy = false;
+	}
+	else {
+		// only try doing the late proxy trick if using fast mode AND we did not offer a proxy
+		if((state == Initiator || (state == Target && fast)) && !proxy.jid().isValid()) {
+			// take just the non-proxy streamhosts
+			bool hasProxies = false;
+			for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
+				if((*it).isProxy())
+					hasProxies = true;
+				else
+					list += *it;
+			}
+			if(hasProxies) {
+				lateProxy = true;
+
+				// no regular streamhosts?  wait for remote error
+				if(list.isEmpty())
+					return;
+			}
+		}
+		else
+			list = in_hosts;
+	}
+
+	conn = new S5BConnector;
+	connect(conn, SIGNAL(result(bool)), SLOT(conn_result(bool)));
+
+	QGuardedPtr<QObject> self = this;
+	tryingHosts(list);
+	if(!self)
+		return;
+
+	conn->start(m->client()->jid(), list, out_key, udp, lateProxy ? 10 : 30);
+}
+
+void S5BManager::Item::setIncomingClient(SocksClient *sc)
+{
+#ifdef S5B_DEBUG
+	printf("S5BManager::Item: %s [%s] successful incoming connection\n", peer.full().latin1(), sid.latin1());
+#endif
+
+	connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+	connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+	connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+	client = sc;
+	allowIncoming = false;
+}
+
+void S5BManager::Item::incomingActivate(const Jid &streamHost)
+{
+	if(!activated) {
+		activatedStream = streamHost;
+		checkForActivation();
+	}
+}
+
+void S5BManager::Item::jt_finished()
+{
+	JT_S5B *j = task;
+	task = 0;
+
+#ifdef S5B_DEBUG
+	printf("jt_finished: state=%s, %s\n", state == Initiator ? "initiator" : "target", j->success() ? "ok" : "fail");
+#endif
+
+	if(state == Initiator) {
+		if(targetMode == Unknown) {
+			targetMode = NotFast;
+			QGuardedPtr<QObject> self = this;
+			accepted();
+			if(!self)
+				return;
+		}
+	}
+
+	// if we've already reported successfully connecting to them, then this response doesn't matter
+	if(state == Initiator && connSuccess) {
+		tryActivation();
+		return;
+	}
+
+	if(j->success()) {
+		// stop connecting out
+		if(conn || lateProxy) {
+			delete conn;
+			conn = 0;
+			doConnectError();
+		}
+
+		Jid streamHost = j->streamHostUsed();
+
+		// they connected to us?
+		if(streamHost.compare(self)) {
+			if(client) {
+				if(state == Initiator) {
+					activatedStream = streamHost;
+					tryActivation();
+				}
+				else
+					checkForActivation();
+			}
+			else {
+#ifdef S5B_DEBUG
+				printf("S5BManager::Item %s claims to have connected to us, but we don't see this\n", peer.full().latin1());
+#endif
+				reset();
+				error(ErrWrongHost);
+			}
+		}
+		else if(streamHost.compare(proxy.jid())) {
+			// toss out any direct incoming, since it won't be used
+			delete client;
+			client = 0;
+			allowIncoming = false;
+
+#ifdef S5B_DEBUG
+			printf("attempting to connect to proxy\n");
+#endif
+			// connect to the proxy
+			proxy_conn = new S5BConnector;
+			connect(proxy_conn, SIGNAL(result(bool)), SLOT(proxy_result(bool)));
+			StreamHostList list;
+			list += proxy;
+
+			QGuardedPtr<QObject> self = this;
+			proxyConnect();
+			if(!self)
+				return;
+
+			proxy_conn->start(m->client()->jid(), list, key, udp, 30);
+		}
+		else {
+#ifdef S5B_DEBUG
+			printf("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", peer.full().latin1());
+#endif
+			reset();
+			error(ErrWrongHost);
+		}
+	}
+	else {
+#ifdef S5B_DEBUG
+		printf("S5BManager::Item %s [%s] error\n", peer.full().latin1(), sid.latin1());
+#endif
+		remoteFailed = true;
+		statusCode = j->statusCode();
+
+		if(lateProxy) {
+			if(!conn)
+				doIncoming();
+		}
+		else {
+			// if connSuccess is true at this point, then we're a Target
+			if(connSuccess)
+				checkForActivation();
+			else
+				checkFailure();
+		}
+	}
+}
+
+void S5BManager::Item::conn_result(bool b)
+{
+	if(b) {
+		SocksClient *sc = conn->takeClient();
+		SocksUDP *sc_udp = conn->takeUDP();
+		StreamHost h = conn->streamHostUsed();
+		delete conn;
+		conn = 0;
+		connSuccess = true;
+
+#ifdef S5B_DEBUG
+		printf("S5BManager::Item: %s [%s] successful outgoing connection\n", peer.full().latin1(), sid.latin1());
+#endif
+
+		connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+		connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+		connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+		m->doSuccess(peer, in_id, h.jid());
+
+		// if the first batch works, don't try proxy
+		lateProxy = false;
+
+		// if initiator, run with this one
+		if(state == Initiator) {
+			// if we had an incoming one, toss it
+			delete client_udp;
+			client_udp = sc_udp;
+			delete client;
+			client = sc;
+			allowIncoming = false;
+			activatedStream = peer;
+			tryActivation();
+		}
+		else {
+			client_out_udp = sc_udp;
+			client_out = sc;
+			checkForActivation();
+		}
+	}
+	else {
+		delete conn;
+		conn = 0;
+
+		// if we delayed the proxies for later, try now
+		if(lateProxy) {
+			if(remoteFailed)
+				doIncoming();
+		}
+		else
+			doConnectError();
+	}
+}
+
+void S5BManager::Item::proxy_result(bool b)
+{
+#ifdef S5B_DEBUG
+	printf("proxy_result: %s\n", b ? "ok" : "fail");
+#endif
+	if(b) {
+		SocksClient *sc = proxy_conn->takeClient();
+		SocksUDP *sc_udp = proxy_conn->takeUDP();
+		delete proxy_conn;
+		proxy_conn = 0;
+
+		connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+		connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+		connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+		client = sc;
+		client_udp = sc_udp;
+
+		// activate
+#ifdef S5B_DEBUG
+		printf("activating proxy stream\n");
+#endif
+		proxy_task = new JT_S5B(m->client()->rootTask());
+		connect(proxy_task, SIGNAL(finished()), SLOT(proxy_finished()));
+		proxy_task->requestActivation(proxy.jid(), sid, peer);
+		proxy_task->go(true);
+	}
+	else {
+		delete proxy_conn;
+		proxy_conn = 0;
+		reset();
+		error(ErrProxy);
+	}
+}
+
+void S5BManager::Item::proxy_finished()
+{
+	JT_S5B *j = proxy_task;
+	proxy_task = 0;
+
+	if(j->success()) {
+#ifdef S5B_DEBUG
+		printf("proxy stream activated\n");
+#endif
+		if(state == Initiator) {
+			activatedStream = proxy.jid();
+			tryActivation();
+		}
+		else
+			checkForActivation();
+	}
+	else {
+		reset();
+		error(ErrProxy);
+	}
+}
+
+void S5BManager::Item::sc_readyRead()
+{
+#ifdef S5B_DEBUG
+	printf("sc_readyRead\n");
+#endif
+	// only targets check for activation, and only should do it if there is no pending outgoing iq-set
+	if(state == Target && !task && !proxy_task)
+		checkForActivation();
+}
+
+void S5BManager::Item::sc_bytesWritten(int)
+{
+#ifdef S5B_DEBUG
+	printf("sc_bytesWritten\n");
+#endif
+	// this should only happen to the initiator, and should always be 1 byte (the '\r' sent earlier)
+	finished();
+}
+
+void S5BManager::Item::sc_error(int)
+{
+#ifdef S5B_DEBUG
+	printf("sc_error\n");
+#endif
+	reset();
+	error(ErrConnect);
+}
+
+void S5BManager::Item::doConnectError()
+{
+	localFailed = true;
+	m->doError(peer, in_id, 404, "Could not connect to given hosts");
+	checkFailure();
+}
+
+void S5BManager::Item::tryActivation()
+{
+#ifdef S5B_DEBUG
+	printf("tryActivation\n");
+#endif
+	if(activated) {
+#ifdef S5B_DEBUG
+		printf("already activated !?\n");
+#endif
+		return;
+	}
+
+	if(targetMode == NotFast) {
+#ifdef S5B_DEBUG
+		printf("tryActivation: NotFast\n");
+#endif
+		// nothing to activate, we're done
+		finished();
+	}
+	else if(targetMode == Fast) {
+		// with fast mode, we don't wait for the iq reply, so delete the task (if any)
+		delete task;
+		task = 0;
+
+		activated = true;
+
+		// if udp, activate using special stanza
+		if(udp) {
+			m->doActivate(peer, sid, activatedStream);
+		}
+		else {
+#ifdef S5B_DEBUG
+			printf("sending extra CR\n");
+#endif
+			// must send [CR] to activate target streamhost
+			QByteArray a(1);
+			a[0] = '\r';
+			client->write(a);
+		}
+	}
+}
+
+void S5BManager::Item::checkForActivation()
+{
+	QPtrList<SocksClient> clientList;
+	if(client)
+		clientList.append(client);
+	if(client_out)
+		clientList.append(client_out);
+	QPtrListIterator<SocksClient> it(clientList);
+	for(SocksClient *sc; (sc = it.current()); ++it) {
+#ifdef S5B_DEBUG
+		printf("checking for activation\n");
+#endif
+		if(fast) {
+			bool ok = false;
+			if(udp) {
+				if((sc == client_out && activatedStream.compare(self)) || (sc == client && !activatedStream.compare(self))) {
+					clientList.removeRef(sc);
+					ok = true;
+				}
+			}
+			else {
+#ifdef S5B_DEBUG
+				printf("need CR\n");
+#endif
+				if(sc->bytesAvailable() >= 1) {
+					clientList.removeRef(sc);
+					QByteArray a = sc->read(1);
+					if(a[0] != '\r') {
+						delete sc;
+						return;
+					}
+					ok = true;
+				}
+			}
+
+			if(ok) {
+				SocksUDP *sc_udp = 0;
+				if(sc == client) {
+					delete client_out_udp;
+					client_out_udp = 0;
+					sc_udp = client_udp;
+				}
+				else if(sc == client_out) {
+					delete client_udp;
+					client_udp = 0;
+					sc_udp = client_out_udp;
+				}
+
+				sc->disconnect(this);
+				clientList.setAutoDelete(true);
+				clientList.clear();
+				client = sc;
+				client_out = 0;
+				client_udp = sc_udp;
+				activated = true;
+#ifdef S5B_DEBUG
+				printf("activation success\n");
+#endif
+				break;
+			}
+		}
+		else {
+#ifdef S5B_DEBUG
+			printf("not fast mode, no need to wait for anything\n");
+#endif
+			clientList.removeRef(sc);
+			sc->disconnect(this);
+			clientList.setAutoDelete(true);
+			clientList.clear();
+			client = sc;
+			client_out = 0;
+			activated = true;
+			break;
+		}
+	}
+
+	if(activated) {
+		finished();
+	}
+	else {
+		// only emit waitingForActivation if there is nothing left to do
+		if((connSuccess || localFailed) && !proxy_task && !proxy_conn)
+			waitingForActivation();
+	}
+}
+
+void S5BManager::Item::checkFailure()
+{
+	bool failed = false;
+	if(state == Initiator) {
+		if(remoteFailed) {
+			if((localFailed && targetMode == Fast) || targetMode == NotFast)
+				failed = true;
+		}
+	}
+	else {
+		if(localFailed) {
+			if((remoteFailed && fast) || !fast)
+				failed = true;
+		}
+	}
+
+	if(failed) {
+		if(state == Initiator) {
+			reset();
+			if(statusCode == 404)
+				error(ErrConnect);
+			else
+				error(ErrRefused);
+		}
+		else {
+			reset();
+			error(ErrConnect);
+		}
+	}
+}
+
+void S5BManager::Item::finished()
+{
+	client->disconnect(this);
+	state = Active;
+#ifdef S5B_DEBUG
+	printf("S5BManager::Item %s [%s] linked successfully\n", peer.full().latin1(), sid.latin1());
+#endif
+	connected();
+}
+
+//----------------------------------------------------------------------------
+// S5BConnector
+//----------------------------------------------------------------------------
+class S5BConnector::Item : public QObject
+{
+	Q_OBJECT
+public:
+	SocksClient *client;
+	SocksUDP *client_udp;
+	StreamHost host;
+	QString key;
+	bool udp;
+	int udp_tries;
+	QTimer t;
+	Jid jid;
+
+	Item(const Jid &self, const StreamHost &_host, const QString &_key, bool _udp) : QObject(0)
+	{
+		jid = self;
+		host = _host;
+		key = _key;
+		udp = _udp;
+		client = new SocksClient;
+		client_udp = 0;
+		connect(client, SIGNAL(connected()), SLOT(sc_connected()));
+		connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
+		connect(&t, SIGNAL(timeout()), SLOT(trySendUDP()));
+	}
+
+	~Item()
+	{
+		cleanup();
+	}
+
+	void start()
+	{
+		client->connectToHost(host.host(), host.port(), key, 0, udp);
+	}
+
+	void udpSuccess()
+	{
+		t.stop();
+		client_udp->change(key, 0); // flip over to the data port
+		success();
+	}
+
+signals:
+	void result(bool);
+
+private slots:
+	void sc_connected()
+	{
+		// if udp, need to send init packet before we are good
+		if(udp) {
+			// port 1 is init
+			client_udp = client->createUDP(key, 1, client->peerAddress(), client->peerPort());
+			udp_tries = 0;
+			t.start(5000);
+			trySendUDP();
+			return;
+		}
+
+		success();
+	}
+
+	void sc_error(int)
+	{
+#ifdef S5B_DEBUG
+		printf("S5BConnector[%s]: error\n", host.host().latin1());
+#endif
+		cleanup();
+		result(false);
+	}
+
+	void trySendUDP()
+	{
+		if(udp_tries == 5) {
+			t.stop();
+			cleanup();
+			result(false);
+			return;
+		}
+
+		// send initialization with our JID
+		QCString cs = jid.full().utf8();
+		QByteArray a(cs.length());
+		memcpy(a.data(), cs.data(), a.size());
+		client_udp->write(a);
+		++udp_tries;
+	}
+
+private:
+	void cleanup()
+	{
+		delete client_udp;
+		client_udp = 0;
+		delete client;
+		client = 0;
+	}
+
+	void success()
+	{
+#ifdef S5B_DEBUG
+		printf("S5BConnector[%s]: success\n", host.host().latin1());
+#endif
+		client->disconnect(this);
+		result(true);
+	}
+};
+
+class S5BConnector::Private
+{
+public:
+	SocksClient *active;
+	SocksUDP *active_udp;
+	QPtrList<Item> itemList;
+	QString key;
+	StreamHost activeHost;
+	QTimer t;
+};
+
+S5BConnector::S5BConnector(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->active = 0;
+	d->active_udp = 0;
+	d->itemList.setAutoDelete(true);
+	connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+}
+
+S5BConnector::~S5BConnector()
+{
+	reset();
+	delete d;
+}
+
+void S5BConnector::reset()
+{
+	d->t.stop();
+	delete d->active_udp;
+	d->active_udp = 0;
+	delete d->active;
+	d->active = 0;
+	d->itemList.clear();
+}
+
+void S5BConnector::start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout)
+{
+	reset();
+
+#ifdef S5B_DEBUG
+	printf("S5BConnector: starting [%p]!\n", this);
+#endif
+	for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
+		Item *i = new Item(self, *it, key, udp);
+		connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
+		d->itemList.append(i);
+		i->start();
+	}
+	d->t.start(timeout * 1000);
+}
+
+SocksClient *S5BConnector::takeClient()
+{
+	SocksClient *c = d->active;
+	d->active = 0;
+	return c;
+}
+
+SocksUDP *S5BConnector::takeUDP()
+{
+	SocksUDP *c = d->active_udp;
+	d->active_udp = 0;
+	return c;
+}
+
+StreamHost S5BConnector::streamHostUsed() const
+{
+	return d->activeHost;
+}
+
+void S5BConnector::item_result(bool b)
+{
+	Item *i = (Item *)sender();
+	if(b) {
+		d->active = i->client;
+		i->client = 0;
+		d->active_udp = i->client_udp;
+		i->client_udp = 0;
+		d->activeHost = i->host;
+		d->itemList.clear();
+		d->t.stop();
+#ifdef S5B_DEBUG
+		printf("S5BConnector: complete! [%p]\n", this);
+#endif
+		result(true);
+	}
+	else {
+		d->itemList.removeRef(i);
+		if(d->itemList.isEmpty()) {
+			d->t.stop();
+#ifdef S5B_DEBUG
+			printf("S5BConnector: failed! [%p]\n", this);
+#endif
+			result(false);
+		}
+	}
+}
+
+void S5BConnector::t_timeout()
+{
+	reset();
+#ifdef S5B_DEBUG
+	printf("S5BConnector: failed! (timeout)\n");
+#endif
+	result(false);
+}
+
+void S5BConnector::man_udpSuccess(const Jid &streamHost)
+{
+	// was anyone sending to this streamhost?
+	QPtrListIterator<Item> it(d->itemList);
+	for(Item *i; (i = it.current()); ++it) {
+		if(i->host.jid().compare(streamHost) && i->client_udp) {
+			i->udpSuccess();
+			return;
+		}
+	}
+}
+
+//----------------------------------------------------------------------------
+// S5BServer
+//----------------------------------------------------------------------------
+class S5BServer::Item : public QObject
+{
+	Q_OBJECT
+public:
+	SocksClient *client;
+	QString host;
+	QTimer expire;
+
+	Item(SocksClient *c) : QObject(0)
+	{
+		client = c;
+		connect(client, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int)));
+		connect(client, SIGNAL(incomingConnectRequest(const QString &, int)), SLOT(sc_incomingConnectRequest(const QString &, int)));
+		connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+		connect(&expire, SIGNAL(timeout()), SLOT(doError()));
+		resetExpiration();
+	}
+
+	~Item()
+	{
+		delete client;
+	}
+
+	void resetExpiration()
+	{
+		expire.start(30000);
+	}
+
+signals:
+	void result(bool);
+
+private slots:
+	void doError()
+	{
+		expire.stop();
+		delete client;
+		client = 0;
+		result(false);
+	}
+
+	void sc_incomingMethods(int m)
+	{
+		if(m & SocksClient::AuthNone)
+			client->chooseMethod(SocksClient::AuthNone);
+		else
+			doError();
+	}
+
+	void sc_incomingConnectRequest(const QString &_host, int port)
+	{
+		if(port == 0) {
+			host = _host;
+			client->disconnect(this);
+			result(true);
+		}
+		else
+			doError();
+	}
+
+	void sc_error(int)
+	{
+		doError();
+	}
+};
+
+class S5BServer::Private
+{
+public:
+	SocksServer serv;
+	QStringList hostList;
+	QPtrList<S5BManager> manList;
+	QPtrList<Item> itemList;
+};
+
+S5BServer::S5BServer(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->itemList.setAutoDelete(true);
+	connect(&d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady()));
+	connect(&d->serv, SIGNAL(incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)), SLOT(ss_incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)));
+}
+
+S5BServer::~S5BServer()
+{
+	unlinkAll();
+	delete d;
+}
+
+bool S5BServer::isActive() const
+{
+	return d->serv.isActive();
+}
+
+bool S5BServer::start(int port)
+{
+	d->serv.stop();
+	return d->serv.listen(port, true);
+}
+
+void S5BServer::stop()
+{
+	d->serv.stop();
+}
+
+void S5BServer::setHostList(const QStringList &list)
+{
+	d->hostList = list;
+}
+
+QStringList S5BServer::hostList() const
+{
+	return d->hostList;
+}
+
+int S5BServer::port() const
+{
+	return d->serv.port();
+}
+
+void S5BServer::ss_incomingReady()
+{
+	Item *i = new Item(d->serv.takeIncoming());
+#ifdef S5B_DEBUG
+	printf("S5BServer: incoming connection from %s:%d\n", i->client->peerAddress().toString().latin1(), i->client->peerPort());
+#endif
+	connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
+	d->itemList.append(i);
+}
+
+void S5BServer::ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data)
+{
+	if(port != 0 || port != 1)
+		return;
+
+	QPtrListIterator<S5BManager> it(d->manList);
+	for(S5BManager *m; (m = it.current()); ++it) {
+		if(m->srv_ownsHash(host)) {
+			m->srv_incomingUDP(port == 1 ? true : false, addr, sourcePort, host, data);
+			return;
+		}
+	}
+}
+
+void S5BServer::item_result(bool b)
+{
+	Item *i = (Item *)sender();
+#ifdef S5B_DEBUG
+	printf("S5BServer item result: %d\n", b);
+#endif
+	if(!b) {
+		d->itemList.removeRef(i);
+		return;
+	}
+
+	SocksClient *c = i->client;
+	i->client = 0;
+	QString key = i->host;
+	d->itemList.removeRef(i);
+
+	// find the appropriate manager for this incoming connection
+	QPtrListIterator<S5BManager> it(d->manList);
+	for(S5BManager *m; (m = it.current()); ++it) {
+		if(m->srv_ownsHash(key)) {
+			m->srv_incomingReady(c, key);
+			return;
+		}
+	}
+
+	// throw it away
+	delete c;
+}
+
+void S5BServer::link(S5BManager *m)
+{
+	d->manList.append(m);
+}
+
+void S5BServer::unlink(S5BManager *m)
+{
+	d->manList.removeRef(m);
+}
+
+void S5BServer::unlinkAll()
+{
+	QPtrListIterator<S5BManager> it(d->manList);
+	for(S5BManager *m; (m = it.current()); ++it)
+		m->srv_unlink();
+	d->manList.clear();
+}
+
+const QPtrList<S5BManager> & S5BServer::managerList() const
+{
+	return d->manList;
+}
+
+void S5BServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
+{
+	d->serv.writeUDP(addr, port, data);
+}
+
+//----------------------------------------------------------------------------
+// JT_S5B
+//----------------------------------------------------------------------------
+class JT_S5B::Private
+{
+public:
+	QDomElement iq;
+	Jid to;
+	Jid streamHost;
+	StreamHost proxyInfo;
+	int mode;
+	QTimer t;
+};
+
+JT_S5B::JT_S5B(Task *parent)
+:Task(parent)
+{
+	d = new Private;
+	d->mode = -1;
+	connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+}
+
+JT_S5B::~JT_S5B()
+{
+	delete d;
+}
+
+void JT_S5B::request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast, bool udp)
+{
+	d->mode = 0;
+
+	QDomElement iq;
+	d->to = to;
+	iq = createIQ(doc(), "set", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+	query.setAttribute("sid", sid);
+	query.setAttribute("mode", udp ? "udp" : "tcp" );
+	iq.appendChild(query);
+	for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
+		QDomElement shost = doc()->createElement("streamhost");
+		shost.setAttribute("jid", (*it).jid().full());
+		shost.setAttribute("host", (*it).host());
+		shost.setAttribute("port", QString::number((*it).port()));
+		if((*it).isProxy()) {
+			QDomElement p = doc()->createElement("proxy");
+			p.setAttribute("xmlns", "http://affinix.com/jabber/stream");
+			shost.appendChild(p);
+		}
+		query.appendChild(shost);
+	}
+	if(fast) {
+		QDomElement e = doc()->createElement("fast");
+		e.setAttribute("xmlns", "http://affinix.com/jabber/stream");
+		query.appendChild(e);
+	}
+	d->iq = iq;
+}
+
+void JT_S5B::requestProxyInfo(const Jid &to)
+{
+	d->mode = 1;
+
+	QDomElement iq;
+	d->to = to;
+	iq = createIQ(doc(), "get", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+	iq.appendChild(query);
+	d->iq = iq;
+}
+
+void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &target)
+{
+	d->mode = 2;
+
+	QDomElement iq;
+	d->to = to;
+	iq = createIQ(doc(), "set", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+	query.setAttribute("sid", sid);
+	iq.appendChild(query);
+	QDomElement act = doc()->createElement("activate");
+	act.appendChild(doc()->createTextNode(target.full()));
+	query.appendChild(act);
+	d->iq = iq;
+}
+
+void JT_S5B::onGo()
+{
+	if(d->mode == 1)
+		d->t.start(15000, true);
+	send(d->iq);
+}
+
+void JT_S5B::onDisconnect()
+{
+	d->t.stop();
+}
+
+bool JT_S5B::take(const QDomElement &x)
+{
+	if(d->mode == -1)
+		return false;
+
+	if(!iqVerify(x, d->to, id()))
+		return false;
+
+	d->t.stop();
+
+	if(x.attribute("type") == "result") {
+		QDomElement q = queryTag(x);
+		if(d->mode == 0) {
+			d->streamHost = "";
+			if(!q.isNull()) {
+				QDomElement shost = q.elementsByTagName("streamhost-used").item(0).toElement();
+				if(!shost.isNull())
+					d->streamHost = shost.attribute("jid");
+			}
+
+			setSuccess();
+		}
+		else if(d->mode == 1) {
+			if(!q.isNull()) {
+				QDomElement shost = q.elementsByTagName("streamhost").item(0).toElement();
+				if(!shost.isNull()) {
+					Jid j = shost.attribute("jid");
+					if(j.isValid()) {
+						QString host = shost.attribute("host");
+						if(!host.isEmpty()) {
+							int port = shost.attribute("port").toInt();
+							StreamHost h;
+							h.setJid(j);
+							h.setHost(host);
+							h.setPort(port);
+							h.setIsProxy(true);
+							d->proxyInfo = h;
+						}
+					}
+				}
+			}
+
+			setSuccess();
+		}
+		else {
+			setSuccess();
+		}
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+void JT_S5B::t_timeout()
+{
+	d->mode = -1;
+	setError(500, "Timed out");
+}
+
+Jid JT_S5B::streamHostUsed() const
+{
+	return d->streamHost;
+}
+
+StreamHost JT_S5B::proxyInfo() const
+{
+	return d->proxyInfo;
+}
+
+//----------------------------------------------------------------------------
+// JT_PushS5B
+//----------------------------------------------------------------------------
+JT_PushS5B::JT_PushS5B(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushS5B::~JT_PushS5B()
+{
+}
+
+int JT_PushS5B::priority() const
+{
+	return 1;
+}
+
+bool JT_PushS5B::take(const QDomElement &e)
+{
+	// look for udpsuccess
+	if(e.tagName() == "message") {
+		QDomElement x = e.elementsByTagName("udpsuccess").item(0).toElement();
+		if(!x.isNull() && x.attribute("xmlns") == "http://jabber.org/protocol/bytestreams") {
+			incomingUDPSuccess(Jid(x.attribute("from")), x.attribute("dstaddr"));
+			return true;
+		}
+		x = e.elementsByTagName("activate").item(0).toElement();
+		if(!x.isNull() && x.attribute("xmlns") == "http://affinix.com/jabber/stream") {
+			incomingActivate(Jid(x.attribute("from")), x.attribute("sid"), Jid(x.attribute("jid")));
+			return true;
+		}
+		return false;
+	}
+
+	// must be an iq-set tag
+	if(e.tagName() != "iq")
+		return false;
+	if(e.attribute("type") != "set")
+		return false;
+	if(queryNS(e) != "http://jabber.org/protocol/bytestreams")
+		return false;
+
+	Jid from(e.attribute("from"));
+	QDomElement q = queryTag(e);
+	QString sid = q.attribute("sid");
+
+	StreamHostList hosts;
+	QDomNodeList nl = q.elementsByTagName("streamhost");
+	for(uint n = 0; n < nl.count(); ++n) {
+		QDomElement shost = nl.item(n).toElement();
+		if(hosts.count() < MAXSTREAMHOSTS) {
+			Jid j = shost.attribute("jid");
+			if(!j.isValid())
+				continue;
+			QString host = shost.attribute("host");
+			if(host.isEmpty())
+				continue;
+			int port = shost.attribute("port").toInt();
+			QDomElement p = shost.elementsByTagName("proxy").item(0).toElement();
+			bool isProxy = false;
+			if(!p.isNull() && p.attribute("xmlns") == "http://affinix.com/jabber/stream")
+				isProxy = true;
+
+			StreamHost h;
+			h.setJid(j);
+			h.setHost(host);
+			h.setPort(port);
+			h.setIsProxy(isProxy);
+			hosts += h;
+		}
+	}
+
+	bool fast = false;
+	QDomElement t;
+	t = q.elementsByTagName("fast").item(0).toElement();
+	if(!t.isNull() && t.attribute("xmlns") == "http://affinix.com/jabber/stream")
+		fast = true;
+
+	S5BRequest r;
+	r.from = from;
+	r.id = e.attribute("id");
+	r.sid = sid;
+	r.hosts = hosts;
+	r.fast = fast;
+	r.udp = q.attribute("mode") == "udp" ? true: false;
+
+	incoming(r);
+	return true;
+}
+
+void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &streamHost)
+{
+	QDomElement iq = createIQ(doc(), "result", to.full(), id);
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+	iq.appendChild(query);
+	QDomElement shost = doc()->createElement("streamhost-used");
+	shost.setAttribute("jid", streamHost.full());
+	query.appendChild(shost);
+	send(iq);
+}
+
+void JT_PushS5B::respondError(const Jid &to, const QString &id, int code, const QString &str)
+{
+	QDomElement iq = createIQ(doc(), "error", to.full(), id);
+	QDomElement err = textTag(doc(), "error", str);
+	err.setAttribute("code", QString::number(code));
+	iq.appendChild(err);
+	send(iq);
+}
+
+void JT_PushS5B::sendUDPSuccess(const Jid &to, const QString &dstaddr)
+{
+	QDomElement m = doc()->createElement("message");
+	m.setAttribute("to", to.full());
+	QDomElement u = doc()->createElement("udpsuccess");
+	u.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+	u.setAttribute("dstaddr", dstaddr);
+	m.appendChild(u);
+	send(m);
+}
+
+void JT_PushS5B::sendActivate(const Jid &to, const QString &sid, const Jid &streamHost)
+{
+	QDomElement m = doc()->createElement("message");
+	m.setAttribute("to", to.full());
+	QDomElement act = doc()->createElement("activate");
+	act.setAttribute("xmlns", "http://affinix.com/jabber/stream");
+	act.setAttribute("sid", sid);
+	act.setAttribute("jid", streamHost.full());
+	m.appendChild(act);
+	send(m);
+}
+
+//----------------------------------------------------------------------------
+// StreamHost
+//----------------------------------------------------------------------------
+StreamHost::StreamHost()
+{
+	v_port = -1;
+	proxy = false;
+}
+
+const Jid & StreamHost::jid() const
+{
+	return j;
+}
+
+const QString & StreamHost::host() const
+{
+	return v_host;
+}
+
+int StreamHost::port() const
+{
+	return v_port;
+}
+
+bool StreamHost::isProxy() const
+{
+	return proxy;
+}
+
+void StreamHost::setJid(const Jid &_j)
+{
+	j = _j;
+}
+
+void StreamHost::setHost(const QString &host)
+{
+	v_host = host;
+}
+
+void StreamHost::setPort(int port)
+{
+	v_port = port;
+}
+
+void StreamHost::setIsProxy(bool b)
+{
+	proxy = b;
+}
+
+}
+
+#include"s5b.moc"
Index: psi/tags/0.10-os2-b1/iris/jabber/s5b.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/jabber/s5b.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/jabber/s5b.h	(revision 38)
@@ -0,0 +1,341 @@
+/*
+ * s5b.h - direct connection protocol via tcp
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef XMPP_S5B_H
+#define XMPP_S5B_H
+
+#include<qobject.h>
+#include<qcstring.h>
+#include<qptrlist.h>
+#include<qvaluelist.h>
+#include"im.h"
+#include"bytestream.h"
+
+class SocksClient;
+class SocksUDP;
+
+namespace XMPP
+{
+	class StreamHost;
+	class S5BConnection;
+	class S5BManager;
+	class S5BServer;
+	struct S5BRequest;
+	typedef QValueList<StreamHost> StreamHostList;
+	typedef QPtrList<S5BConnection> S5BConnectionList;
+	typedef QPtrListIterator<S5BConnection> S5BConnectionListIt;
+
+	class S5BDatagram
+	{
+	public:
+		S5BDatagram();
+		S5BDatagram(int source, int dest, const QByteArray &data);
+
+		int sourcePort() const;
+		int destPort() const;
+		QByteArray data() const;
+
+	private:
+		int _source, _dest;
+		QByteArray _buf;
+	};
+
+	class S5BConnection : public ByteStream
+	{
+		Q_OBJECT
+	public:
+		enum Mode { Stream, Datagram };
+		enum Error { ErrRefused, ErrConnect, ErrProxy, ErrSocket };
+		enum State { Idle, Requesting, Connecting, WaitingForAccept, Active };
+		~S5BConnection();
+
+		Jid proxy() const;
+		void setProxy(const Jid &proxy);
+
+		void connectToJid(const Jid &peer, const QString &sid, Mode m = Stream);
+		void accept();
+		void close();
+
+		Jid peer() const;
+		QString sid() const;
+		bool isRemote() const;
+		Mode mode() const;
+		int state() const;
+
+		bool isOpen() const;
+		void write(const QByteArray &);
+		QByteArray read(int bytes=0);
+		int bytesAvailable() const;
+		int bytesToWrite() const;
+
+		void writeDatagram(const S5BDatagram &);
+		S5BDatagram readDatagram();
+		int datagramsAvailable() const;
+
+	signals:
+		void proxyQuery();                             // querying proxy for streamhost information
+		void proxyResult(bool b);                      // query success / fail
+		void requesting();                             // sent actual S5B request (initiator only)
+		void accepted();                               // target accepted (initiator only
+		void tryingHosts(const StreamHostList &hosts); // currently connecting to these hosts
+		void proxyConnect();                           // connecting to proxy
+		void waitingForActivation();                   // waiting for activation (target only)
+		void connected();                              // connection active
+		void datagramReady();
+
+	private slots:
+		void doPending();
+
+		void sc_connectionClosed();
+		void sc_delayedCloseFinished();
+		void sc_readyRead();
+		void sc_bytesWritten(int);
+		void sc_error(int);
+
+		void su_packetReady(const QByteArray &buf);
+
+	private:
+		class Private;
+		Private *d;
+
+		void reset(bool clear=false);
+		void handleUDP(const QByteArray &buf);
+		void sendUDP(const QByteArray &buf);
+
+		friend class S5BManager;
+		void man_waitForAccept(const S5BRequest &r);
+		void man_clientReady(SocksClient *, SocksUDP *);
+		void man_udpReady(const QByteArray &buf);
+		void man_failed(int);
+		S5BConnection(S5BManager *, QObject *parent=0);
+	};
+
+	class S5BManager : public QObject
+	{
+		Q_OBJECT
+	public:
+		S5BManager(Client *);
+		~S5BManager();
+
+		Client *client() const;
+		S5BServer *server() const;
+		void setServer(S5BServer *s);
+
+		bool isAcceptableSID(const Jid &peer, const QString &sid) const;
+		QString genUniqueSID(const Jid &peer) const;
+
+		S5BConnection *createConnection();
+		S5BConnection *takeIncoming();
+
+		class Item;
+		class Entry;
+
+	signals:
+		void incomingReady();
+
+	private slots:
+		void ps_incoming(const S5BRequest &req);
+		void ps_incomingUDPSuccess(const Jid &from, const QString &dstaddr);
+		void ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost);
+		void item_accepted();
+		void item_tryingHosts(const StreamHostList &list);
+		void item_proxyConnect();
+		void item_waitingForActivation();
+		void item_connected();
+		void item_error(int);
+		void query_finished();
+
+	private:
+		class Private;
+		Private *d;
+
+		S5BConnection *findIncoming(const Jid &from, const QString &sid) const;
+		Entry *findEntry(S5BConnection *) const;
+		Entry *findEntry(Item *) const;
+		Entry *findEntryByHash(const QString &key) const;
+		Entry *findEntryBySID(const Jid &peer, const QString &sid) const;
+		Entry *findServerEntryByHash(const QString &key) const;
+
+		void entryContinue(Entry *e);
+		void queryProxy(Entry *e);
+		bool targetShouldOfferProxy(Entry *e);
+
+		friend class S5BConnection;
+		void con_connect(S5BConnection *);
+		void con_accept(S5BConnection *);
+		void con_reject(S5BConnection *);
+		void con_unlink(S5BConnection *);
+		void con_sendUDP(S5BConnection *, const QByteArray &buf);
+
+		friend class S5BServer;
+		bool srv_ownsHash(const QString &key) const;
+		void srv_incomingReady(SocksClient *sc, const QString &key);
+		void srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data);
+		void srv_unlink();
+
+		friend class Item;
+		void doSuccess(const Jid &peer, const QString &id, const Jid &streamHost);
+		void doError(const Jid &peer, const QString &id, int, const QString &);
+		void doActivate(const Jid &peer, const QString &sid, const Jid &streamHost);
+	};
+
+	class S5BConnector : public QObject
+	{
+		Q_OBJECT
+	public:
+		S5BConnector(QObject *parent=0);
+		~S5BConnector();
+
+		void reset();
+		void start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout);
+		SocksClient *takeClient();
+		SocksUDP *takeUDP();
+		StreamHost streamHostUsed() const;
+
+		class Item;
+
+	signals:
+		void result(bool);
+
+	private slots:
+		void item_result(bool);
+		void t_timeout();
+
+	private:
+		class Private;
+		Private *d;
+
+		friend class S5BManager;
+		void man_udpSuccess(const Jid &streamHost);
+	};
+
+	// listens on a port for serving
+	class S5BServer : public QObject
+	{
+		Q_OBJECT
+	public:
+		S5BServer(QObject *par=0);
+		~S5BServer();
+
+		bool isActive() const;
+		bool start(int port);
+		void stop();
+		int port() const;
+		void setHostList(const QStringList &);
+		QStringList hostList() const;
+
+		class Item;
+
+	private slots:
+		void ss_incomingReady();
+		void ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data);
+		void item_result(bool);
+
+	private:
+		class Private;
+		Private *d;
+
+		friend class S5BManager;
+		void link(S5BManager *);
+		void unlink(S5BManager *);
+		void unlinkAll();
+		const QPtrList<S5BManager> & managerList() const;
+		void writeUDP(const QHostAddress &addr, int port, const QByteArray &data);
+	};
+
+	class JT_S5B : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_S5B(Task *);
+		~JT_S5B();
+
+		void request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast, bool udp=false);
+		void requestProxyInfo(const Jid &to);
+		void requestActivation(const Jid &to, const QString &sid, const Jid &target);
+
+		void onGo();
+		void onDisconnect();
+		bool take(const QDomElement &);
+
+		Jid streamHostUsed() const;
+		StreamHost proxyInfo() const;
+
+	private slots:
+		void t_timeout();
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	struct S5BRequest
+	{
+		Jid from;
+		QString id, sid;
+		StreamHostList hosts;
+		bool fast;
+		bool udp;
+	};
+	class JT_PushS5B : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_PushS5B(Task *);
+		~JT_PushS5B();
+
+		int priority() const;
+
+		void respondSuccess(const Jid &to, const QString &id, const Jid &streamHost);
+		void respondError(const Jid &to, const QString &id, int code, const QString &str);
+		void sendUDPSuccess(const Jid &to, const QString &dstaddr);
+		void sendActivate(const Jid &to, const QString &sid, const Jid &streamHost);
+
+		bool take(const QDomElement &);
+
+	signals:
+		void incoming(const S5BRequest &req);
+		void incomingUDPSuccess(const Jid &from, const QString &dstaddr);
+		void incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost);
+	};
+
+	class StreamHost
+	{
+	public:
+		StreamHost();
+
+		const Jid & jid() const;
+		const QString & host() const;
+		int port() const;
+		bool isProxy() const;
+		void setJid(const Jid &);
+		void setHost(const QString &);
+		void setPort(int);
+		void setIsProxy(bool);
+
+	private:
+		Jid j;
+		QString v_host;
+		int v_port;
+		bool proxy;
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/jabber/xmpp_ibb.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/jabber/xmpp_ibb.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/jabber/xmpp_ibb.cpp	(revision 38)
@@ -0,0 +1,638 @@
+/*
+ * ibb.cpp - Inband bytestream
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"xmpp_ibb.h"
+
+#include<qtimer.h>
+#include"xmpp_xmlcommon.h"
+#include"base64.h"
+
+#include<stdlib.h>
+
+#define IBB_PACKET_SIZE   4096
+#define IBB_PACKET_DELAY  0
+
+using namespace XMPP;
+
+static int num_conn = 0;
+static int id_conn = 0;
+
+//----------------------------------------------------------------------------
+// IBBConnection
+//----------------------------------------------------------------------------
+class IBBConnection::Private
+{
+public:
+	Private() {}
+
+	int state;
+	Jid peer;
+	QString sid;
+	IBBManager *m;
+	JT_IBB *j;
+	QDomElement comment;
+	QString iq_id;
+
+	int blockSize;
+	QByteArray recvbuf, sendbuf;
+	bool closePending, closing;
+
+	int id;
+};
+
+IBBConnection::IBBConnection(IBBManager *m)
+:ByteStream(m)
+{
+	d = new Private;
+	d->m = m;
+	d->j = 0;
+	reset();
+
+	++num_conn;
+	d->id = id_conn++;
+	QString dstr; dstr.sprintf("IBBConnection[%d]: constructing, count=%d\n", d->id, num_conn);
+	d->m->client()->debug(dstr);
+}
+
+void IBBConnection::reset(bool clear)
+{
+	d->m->unlink(this);
+	d->state = Idle;
+	d->closePending = false;
+	d->closing = false;
+
+	delete d->j;
+	d->j = 0;
+
+	d->sendbuf.resize(0);
+	if(clear)
+		d->recvbuf.resize(0);
+}
+
+IBBConnection::~IBBConnection()
+{
+	reset(true);
+
+	--num_conn;
+	QString dstr; dstr.sprintf("IBBConnection[%d]: destructing, count=%d\n", d->id, num_conn);
+	d->m->client()->debug(dstr);
+
+	delete d;
+}
+
+void IBBConnection::connectToJid(const Jid &peer, const QDomElement &comment)
+{
+	close();
+	reset(true);
+
+	d->state = Requesting;
+	d->peer = peer;
+	d->comment = comment;
+
+	QString dstr; dstr.sprintf("IBBConnection[%d]: initiating request to %s\n", d->id, peer.full().latin1());
+	d->m->client()->debug(dstr);
+
+	d->j = new JT_IBB(d->m->client()->rootTask());
+	connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
+	d->j->request(d->peer, comment);
+	d->j->go(true);
+}
+
+void IBBConnection::accept()
+{
+	if(d->state != WaitingForAccept)
+		return;
+
+	QString dstr; dstr.sprintf("IBBConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+	d->m->client()->debug(dstr);
+
+	d->m->doAccept(this, d->iq_id);
+	d->state = Active;
+	d->m->link(this);
+}
+
+void IBBConnection::close()
+{
+	if(d->state == Idle)
+		return;
+
+	if(d->state == WaitingForAccept) {
+		d->m->doReject(this, d->iq_id, 403, "Rejected");
+		reset();
+		return;
+	}
+
+	QString dstr; dstr.sprintf("IBBConnection[%d]: closing\n", d->id);
+	d->m->client()->debug(dstr);
+
+	if(d->state == Active) {
+		// if there is data pending to be written, then pend the closing
+		if(bytesToWrite() > 0) {
+			d->closePending = true;
+			trySend();
+			return;
+		}
+
+		// send a close packet
+		JT_IBB *j = new JT_IBB(d->m->client()->rootTask());
+		j->sendData(d->peer, d->sid, QByteArray(), true);
+		j->go(true);
+	}
+
+	reset();
+}
+
+int IBBConnection::state() const
+{
+	return d->state;
+}
+
+Jid IBBConnection::peer() const
+{
+	return d->peer;
+}
+
+QString IBBConnection::streamid() const
+{
+	return d->sid;
+}
+
+QDomElement IBBConnection::comment() const
+{
+	return d->comment;
+}
+
+bool IBBConnection::isOpen() const
+{
+	if(d->state == Active)
+		return true;
+	else
+		return false;
+}
+
+void IBBConnection::write(const QByteArray &a)
+{
+	if(d->state != Active || d->closePending || d->closing)
+		return;
+
+	// append to the end of our send buffer
+	int oldsize = d->sendbuf.size();
+	d->sendbuf.resize(oldsize + a.size());
+	memcpy(d->sendbuf.data() + oldsize, a.data(), a.size());
+
+	trySend();
+}
+
+QByteArray IBBConnection::read(int)
+{
+	// TODO: obey argument
+	QByteArray a = d->recvbuf.copy();
+	d->recvbuf.resize(0);
+	return a;
+}
+
+int IBBConnection::bytesAvailable() const
+{
+	return d->recvbuf.size();
+}
+
+int IBBConnection::bytesToWrite() const
+{
+	return d->sendbuf.size();
+}
+
+void IBBConnection::waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id)
+{
+	close();
+	reset(true);
+
+	d->state = WaitingForAccept;
+	d->peer = peer;
+	d->sid = sid;
+	d->comment = comment;
+	d->iq_id = iq_id;
+}
+
+void IBBConnection::takeIncomingData(const QByteArray &a, bool close)
+{
+	// append to the end of our recv buffer
+	int oldsize = d->recvbuf.size();
+	d->recvbuf.resize(oldsize + a.size());
+	memcpy(d->recvbuf.data() + oldsize, a.data(), a.size());
+
+	readyRead();
+
+	if(close) {
+		reset();
+		connectionClosed();
+	}
+}
+
+void IBBConnection::ibb_finished()
+{
+	JT_IBB *j = d->j;
+	d->j = 0;
+
+	if(j->success()) {
+		if(j->mode() == JT_IBB::ModeRequest) {
+			d->sid = j->streamid();
+
+			QString dstr; dstr.sprintf("IBBConnection[%d]: %s [%s] accepted.\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+			d->m->client()->debug(dstr);
+
+			d->state = Active;
+			d->m->link(this);
+			connected();
+		}
+		else {
+			bytesWritten(d->blockSize);
+
+			if(d->closing) {
+				reset();
+				delayedCloseFinished();
+			}
+
+			if(!d->sendbuf.isEmpty() || d->closePending)
+				QTimer::singleShot(IBB_PACKET_DELAY, this, SLOT(trySend()));
+		}
+	}
+	else {
+		if(j->mode() == JT_IBB::ModeRequest) {
+			QString dstr; dstr.sprintf("IBBConnection[%d]: %s refused.\n", d->id, d->peer.full().latin1());
+			d->m->client()->debug(dstr);
+
+			reset(true);
+			error(ErrRequest);
+		}
+		else {
+			reset(true);
+			error(ErrData);
+		}
+	}
+}
+
+void IBBConnection::trySend()
+{
+	// if we already have an active task, then don't do anything
+	if(d->j)
+		return;
+
+	QByteArray a;
+	if(!d->sendbuf.isEmpty()) {
+		// take a chunk
+		if(d->sendbuf.size() < IBB_PACKET_SIZE)
+			a.resize(d->sendbuf.size());
+		else
+			a.resize(IBB_PACKET_SIZE);
+		memcpy(a.data(), d->sendbuf.data(), a.size());
+		d->sendbuf.resize(d->sendbuf.size() - a.size());
+	}
+
+	bool doClose = false;
+	if(d->sendbuf.isEmpty() && d->closePending)
+		doClose = true;
+
+	// null operation?
+	if(a.isEmpty() && !doClose)
+		return;
+
+	printf("IBBConnection[%d]: sending [%d] bytes ", d->id, a.size());
+	if(doClose)
+		printf("and closing.\n");
+	else
+		printf("(%d bytes left)\n", d->sendbuf.size());
+
+	if(doClose) {
+		d->closePending = false;
+		d->closing = true;
+	}
+
+	d->blockSize = a.size();
+	d->j = new JT_IBB(d->m->client()->rootTask());
+	connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
+	d->j->sendData(d->peer, d->sid, a, doClose);
+	d->j->go(true);
+}
+
+
+//----------------------------------------------------------------------------
+// IBBManager
+//----------------------------------------------------------------------------
+class IBBManager::Private
+{
+public:
+	Private() {}
+
+	Client *client;
+	IBBConnectionList activeConns;
+	IBBConnectionList incomingConns;
+	JT_IBB *ibb;
+};
+
+IBBManager::IBBManager(Client *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->client = parent;
+	
+	d->ibb = new JT_IBB(d->client->rootTask(), true);
+	connect(d->ibb, SIGNAL(incomingRequest(const Jid &, const QString &, const QDomElement &)), SLOT(ibb_incomingRequest(const Jid &, const QString &, const QDomElement &)));
+	connect(d->ibb, SIGNAL(incomingData(const Jid &, const QString &, const QString &, const QByteArray &, bool)), SLOT(ibb_incomingData(const Jid &, const QString &, const QString &, const QByteArray &, bool)));
+}
+
+IBBManager::~IBBManager()
+{
+	d->incomingConns.setAutoDelete(true);
+	d->incomingConns.clear();
+	delete d->ibb;
+	delete d;
+}
+
+Client *IBBManager::client() const
+{
+	return d->client;
+}
+
+IBBConnection *IBBManager::takeIncoming()
+{
+	if(d->incomingConns.isEmpty())
+		return 0;
+
+	IBBConnection *c = d->incomingConns.getFirst();
+	d->incomingConns.removeRef(c);
+	return c;
+}
+
+void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &comment)
+{
+	QString sid = genUniqueKey();
+
+	// create a "waiting" connection
+	IBBConnection *c = new IBBConnection(this);
+	c->waitForAccept(from, sid, comment, id);
+	d->incomingConns.append(c);
+	incomingReady();
+}
+
+void IBBManager::ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close)
+{
+	IBBConnection *c = findConnection(streamid, from);
+	if(!c) {
+		d->ibb->respondError(from, id, 404, "No such stream");
+	}
+	else {
+		d->ibb->respondAck(from, id);
+		c->takeIncomingData(data, close);
+	}
+}
+
+QString IBBManager::genKey() const
+{
+	QString key = "ibb_";
+
+	for(int i = 0; i < 4; ++i) {
+		int word = rand() & 0xffff;
+		for(int n = 0; n < 4; ++n) {
+			QString s;
+			s.sprintf("%x", (word >> (n * 4)) & 0xf);
+			key.append(s);
+		}
+	}
+
+	return key;
+}
+
+QString IBBManager::genUniqueKey() const
+{
+	// get unused key
+	QString key;
+	while(1) {
+		key = genKey();
+
+		if(!findConnection(key))
+			break;
+	}
+
+	return key;
+}
+
+void IBBManager::link(IBBConnection *c)
+{
+	d->activeConns.append(c);
+}
+
+void IBBManager::unlink(IBBConnection *c)
+{
+	d->activeConns.removeRef(c);
+}
+
+IBBConnection *IBBManager::findConnection(const QString &sid, const Jid &peer) const
+{
+	IBBConnectionListIt it(d->activeConns);
+	for(IBBConnection *c; (c = it.current()); ++it) {
+		if(c->streamid() == sid && (peer.isEmpty() || c->peer().compare(peer)) )
+			return c;
+	}
+	return 0;
+}
+
+void IBBManager::doAccept(IBBConnection *c, const QString &id)
+{
+	d->ibb->respondSuccess(c->peer(), id, c->streamid());
+}
+
+void IBBManager::doReject(IBBConnection *c, const QString &id, int code, const QString &str)
+{
+	d->ibb->respondError(c->peer(), id, code, str);
+}
+
+
+//----------------------------------------------------------------------------
+// JT_IBB
+//----------------------------------------------------------------------------
+class JT_IBB::Private
+{
+public:
+	Private() {}
+
+	QDomElement iq;
+	int mode;
+	bool serve;
+	Jid to;
+	QString streamid;
+};
+
+JT_IBB::JT_IBB(Task *parent, bool serve)
+:Task(parent)
+{
+	d = new Private;
+	d->serve = serve;
+}
+
+JT_IBB::~JT_IBB()
+{
+	delete d;
+}
+
+void JT_IBB::request(const Jid &to, const QDomElement &comment)
+{
+	d->mode = ModeRequest;
+	QDomElement iq;
+	d->to = to;
+	iq = createIQ(doc(), "set", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
+	iq.appendChild(query);
+	query.appendChild(comment);
+	d->iq = iq;
+}
+
+void JT_IBB::sendData(const Jid &to, const QString &streamid, const QByteArray &a, bool close)
+{
+	d->mode = ModeSendData;
+	QDomElement iq;
+	d->to = to;
+	iq = createIQ(doc(), "set", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
+	iq.appendChild(query);
+	query.appendChild(textTag(doc(), "streamid", streamid));
+	if(!a.isEmpty())
+		query.appendChild(textTag(doc(), "data", Base64::arrayToString(a)));
+	if(close) {
+		QDomElement c = doc()->createElement("close");
+		query.appendChild(c);
+	}
+	d->iq = iq;
+}
+
+void JT_IBB::respondSuccess(const Jid &to, const QString &id, const QString &streamid)
+{
+	QDomElement iq = createIQ(doc(), "result", to.full(), id);
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
+	iq.appendChild(query);
+	query.appendChild(textTag(doc(), "streamid", streamid));
+	send(iq);
+}
+
+void JT_IBB::respondError(const Jid &to, const QString &id, int code, const QString &str)
+{
+	QDomElement iq = createIQ(doc(), "error", to.full(), id);
+	QDomElement err = textTag(doc(), "error", str);
+	err.setAttribute("code", QString::number(code));
+	iq.appendChild(err);
+	send(iq);
+}
+
+void JT_IBB::respondAck(const Jid &to, const QString &id)
+{
+	QDomElement iq = createIQ(doc(), "result", to.full(), id);
+	send(iq);
+}
+
+void JT_IBB::onGo()
+{
+	send(d->iq);
+}
+
+bool JT_IBB::take(const QDomElement &e)
+{
+	if(d->serve) {
+		// must be an iq-set tag
+		if(e.tagName() != "iq" || e.attribute("type") != "set")
+			return false;
+
+		if(queryNS(e) != "http://jabber.org/protocol/ibb")
+			return false;
+
+		Jid from(e.attribute("from"));
+		QString id = e.attribute("id");
+		QDomElement q = queryTag(e);
+
+		bool found;
+		QDomElement s = findSubTag(q, "streamid", &found);
+		if(!found) {
+			QDomElement comment = findSubTag(q, "comment", &found);
+			incomingRequest(from, id, comment);
+		}
+		else {
+			QString sid = tagContent(s);
+			QByteArray a;
+			bool close = false;
+			s = findSubTag(q, "data", &found);
+			if(found)
+				a = Base64::stringToArray(tagContent(s));
+			s = findSubTag(q, "close", &found);
+			if(found)
+				close = true;
+
+			incomingData(from, sid, id, a, close);
+		}
+
+		return true;
+	}
+	else {
+		Jid from(e.attribute("from"));
+		if(e.attribute("id") != id() || !d->to.compare(from))
+			return false;
+
+		if(e.attribute("type") == "result") {
+			QDomElement q = queryTag(e);
+
+			// request
+			if(d->mode == ModeRequest) {
+				bool found;
+				QDomElement s = findSubTag(q, "streamid", &found);
+				if(found)
+					d->streamid = tagContent(s);
+				else
+					d->streamid = "";
+				setSuccess();
+			}
+			// sendData
+			else {
+				// thank you for the ack, kind sir
+				setSuccess();
+			}
+		}
+		else {
+			setError(e);
+		}
+
+		return true;
+	}
+}
+
+QString JT_IBB::streamid() const
+{
+	return d->streamid;
+}
+
+Jid JT_IBB::jid() const
+{
+	return d->to;
+}
+
+int JT_IBB::mode() const
+{
+	return d->mode;
+}
+
Index: psi/tags/0.10-os2-b1/iris/jabber/xmpp_ibb.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/jabber/xmpp_ibb.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/jabber/xmpp_ibb.h	(revision 38)
@@ -0,0 +1,145 @@
+/*
+ * ibb.h - Inband bytestream
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef JABBER_IBB_H
+#define JABBER_IBB_H
+
+#include<qobject.h>
+#include<qdom.h>
+#include<qstring.h>
+#include<qptrlist.h>
+#include"bytestream.h"
+#include"im.h"
+
+namespace XMPP
+{
+	class Client;
+	class IBBManager;
+
+	// this is an IBB connection.  use it much like a qsocket
+	class IBBConnection : public ByteStream
+	{
+		Q_OBJECT
+	public:
+		enum { ErrRequest, ErrData };
+		enum { Idle, Requesting, WaitingForAccept, Active };
+		IBBConnection(IBBManager *);
+		~IBBConnection();
+
+		void connectToJid(const Jid &peer, const QDomElement &comment);
+		void accept();
+		void close();
+
+		int state() const;
+		Jid peer() const;
+		QString streamid() const;
+		QDomElement comment() const;
+
+		bool isOpen() const;
+		void write(const QByteArray &);
+		QByteArray read(int bytes=0);
+		int bytesAvailable() const;
+		int bytesToWrite() const;
+
+	signals:
+		void connected();
+
+	private slots:
+		void ibb_finished();
+		void trySend();
+
+	private:
+		class Private;
+		Private *d;
+
+		void reset(bool clear=false);
+
+		friend class IBBManager;
+		void waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id);
+		void takeIncomingData(const QByteArray &, bool close);
+	};
+
+	typedef QPtrList<IBBConnection> IBBConnectionList;
+	typedef QPtrListIterator<IBBConnection> IBBConnectionListIt;
+	class IBBManager : public QObject
+	{
+		Q_OBJECT
+	public:
+		IBBManager(Client *);
+		~IBBManager();
+
+		Client *client() const;
+
+		IBBConnection *takeIncoming();
+
+	signals:
+		void incomingReady();
+
+	private slots:
+		void ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &);
+		void ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close);
+
+	private:
+		class Private;
+		Private *d;
+
+		QString genKey() const;
+
+		friend class IBBConnection;
+		IBBConnection *findConnection(const QString &sid, const Jid &peer="") const;
+		QString genUniqueKey() const;
+		void link(IBBConnection *);
+		void unlink(IBBConnection *);
+		void doAccept(IBBConnection *c, const QString &id);
+		void doReject(IBBConnection *c, const QString &id, int, const QString &);
+	};
+
+	class JT_IBB : public Task
+	{
+		Q_OBJECT
+	public:
+		enum { ModeRequest, ModeSendData };
+		JT_IBB(Task *, bool serve=false);
+		~JT_IBB();
+
+		void request(const Jid &, const QDomElement &comment);
+		void sendData(const Jid &, const QString &streamid, const QByteArray &data, bool close);
+		void respondSuccess(const Jid &, const QString &id, const QString &streamid);
+		void respondError(const Jid &, const QString &id, int code, const QString &str);
+		void respondAck(const Jid &to, const QString &id);
+
+		void onGo();
+		bool take(const QDomElement &);
+
+		QString streamid() const;
+		Jid jid() const;
+		int mode() const;
+
+	signals:
+		void incomingRequest(const Jid &from, const QString &id, const QDomElement &);
+		void incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close);
+
+	private:
+		class Private;
+		Private *d;
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/jabber/xmpp_jidlink.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/jabber/xmpp_jidlink.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/jabber/xmpp_jidlink.cpp	(revision 38)
@@ -0,0 +1,318 @@
+/*
+ * jidlink.cpp - establish a link between Jabber IDs
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"xmpp_jidlink.h"
+
+#include<qdom.h>
+#include<qtimer.h>
+#include"im.h"
+#include"s5b.h"
+#include"xmpp_ibb.h"
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// JidLink
+//----------------------------------------------------------------------------
+class JidLink::Private
+{
+public:
+	Client *client;
+	ByteStream *bs;
+	int type;
+	int state;
+	Jid peer;
+};
+
+JidLink::JidLink(Client *client)
+:QObject(client->jidLinkManager())
+{
+	d = new Private;
+	d->client = client;
+	d->bs = 0;
+
+	reset();
+}
+
+JidLink::~JidLink()
+{
+	reset(true);
+
+	delete d;
+}
+
+void JidLink::reset(bool clear)
+{
+	d->type = None;
+	d->state = Idle;
+
+	if(d->bs) {
+		unlink();
+		d->bs->close();
+		if(clear) {
+			delete d->bs;
+			d->bs = 0;
+		}
+	}
+}
+
+void JidLink::connectToJid(const Jid &jid, int type, const QDomElement &comment)
+{
+	reset(true);
+	if(type == DTCP)
+		d->bs = d->client->s5bManager()->createConnection();
+	else if(type == IBB)
+		d->bs = new IBBConnection(d->client->ibbManager());
+	else
+		return;
+
+	d->type = type;
+	d->peer = jid;
+	d->state = Connecting;
+
+	link();
+
+	if(type == DTCP) {
+		S5BConnection *c = (S5BConnection *)d->bs;
+		status(StatDTCPRequesting);
+		c->connectToJid(jid, d->client->s5bManager()->genUniqueSID(jid));
+	}
+	else {
+		IBBConnection *c = (IBBConnection *)d->bs;
+		status(StatIBBRequesting);
+		c->connectToJid(jid, comment);
+	}
+}
+
+void JidLink::link()
+{
+	if(d->type == DTCP) {
+		S5BConnection *c = (S5BConnection *)d->bs;
+		connect(c, SIGNAL(connected()), SLOT(dtcp_connected()));
+		connect(c, SIGNAL(accepted()), SLOT(dtcp_accepted()));
+	}
+	else {
+		IBBConnection *c = (IBBConnection *)d->bs;
+		connect(c, SIGNAL(connected()), SLOT(ibb_connected()));
+	}
+
+	connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+	connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int)));
+	connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+	connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+}
+
+void JidLink::unlink()
+{
+	d->bs->disconnect(this);
+}
+
+void JidLink::accept()
+{
+	if(d->state != WaitingForAccept)
+		return;
+
+	QTimer::singleShot(0, this, SLOT(doRealAccept()));
+}
+
+void JidLink::doRealAccept()
+{
+	if(d->type == DTCP) {
+		((S5BConnection *)d->bs)->accept();
+		d->state = Connecting;
+		dtcp_accepted();
+	}
+	else {
+		((IBBConnection *)d->bs)->accept();
+		d->state = Active;
+		connected();
+	}
+}
+
+bool JidLink::setStream(ByteStream *bs)
+{
+	reset(true);
+	int type = None;
+	if(bs->inherits("XMPP::S5BConnection"))
+		type = DTCP;
+	else if(bs->inherits("XMPP::IBBConnection"))
+		type = IBB;
+
+	if(type == None)
+		return false;
+
+	d->type = type;
+	d->bs = bs;
+	d->state = WaitingForAccept;
+
+	link();
+
+	if(d->type == DTCP)
+		d->peer = ((S5BConnection *)d->bs)->peer();
+	else
+		d->peer = ((IBBConnection *)d->bs)->peer();
+
+	return true;
+}
+
+int JidLink::type() const
+{
+	return d->type;
+}
+
+Jid JidLink::peer() const
+{
+	return d->peer;
+}
+
+int JidLink::state() const
+{
+	return d->state;
+}
+
+bool JidLink::isOpen() const
+{
+	if(d->state == Active)
+		return true;
+	else
+		return false;
+}
+
+void JidLink::close()
+{
+	if(d->state == Idle)
+		return;
+	reset();
+}
+
+void JidLink::write(const QByteArray &a)
+{
+	if(d->state == Active)
+		d->bs->write(a);
+}
+
+QByteArray JidLink::read(int bytes)
+{
+	if(d->bs)
+		return d->bs->read(bytes);
+	else
+		return QByteArray();
+}
+
+int JidLink::bytesAvailable() const
+{
+	if(d->bs)
+		return d->bs->bytesAvailable();
+	else
+		return 0;
+}
+
+int JidLink::bytesToWrite() const
+{
+	if(d->state == Active)
+		return d->bs->bytesToWrite();
+	else
+		return 0;
+}
+
+void JidLink::dtcp_accepted()
+{
+	status(StatDTCPAccepted);
+}
+
+void JidLink::dtcp_connected()
+{
+	d->state = Active;
+	status(StatDTCPConnected);
+	connected();
+}
+
+void JidLink::ibb_connected()
+{
+	d->state = Active;
+	status(StatIBBConnected);
+	connected();
+}
+
+void JidLink::bs_connectionClosed()
+{
+	reset();
+	connectionClosed();
+}
+
+void JidLink::bs_error(int)
+{
+	reset();
+	error(ErrConnect);
+}
+
+void JidLink::bs_readyRead()
+{
+	readyRead();
+}
+
+void JidLink::bs_bytesWritten(int x)
+{
+	bytesWritten(x);
+}
+
+
+//----------------------------------------------------------------------------
+// JidLinkManager
+//----------------------------------------------------------------------------
+class JidLinkManager::Private
+{
+public:
+	Private() {}
+
+	Client *client;
+	QPtrList<JidLink> incomingList;
+};
+
+JidLinkManager::JidLinkManager(Client *par)
+:QObject(par)
+{
+	d = new Private;
+	d->client = par;
+}
+
+JidLinkManager::~JidLinkManager()
+{
+	d->incomingList.setAutoDelete(true);
+	d->incomingList.clear();
+	delete d;
+}
+
+JidLink *JidLinkManager::takeIncoming()
+{
+	if(d->incomingList.isEmpty())
+		return 0;
+
+	JidLink *j = d->incomingList.getFirst();
+	d->incomingList.removeRef(j);
+	return j;
+}
+
+void JidLinkManager::insertStream(ByteStream *bs)
+{
+	JidLink *j = new JidLink(d->client);
+	if(j->setStream(bs))
+		d->incomingList.append(j);
+}
Index: psi/tags/0.10-os2-b1/iris/jabber/xmpp_jidlink.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/jabber/xmpp_jidlink.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/jabber/xmpp_jidlink.h	(revision 38)
@@ -0,0 +1,114 @@
+/*
+ * jidlink.h - establish a link between Jabber IDs
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+   NOTE: this is not to be confused with JEP-0041
+*/
+
+#ifndef JABBER_JIDLINK_H
+#define JABBER_JIDLINK_H
+
+#include<qobject.h>
+#include<qstring.h>
+#include"xmpp.h"
+
+class ByteStream;
+
+namespace XMPP
+{
+	class Client;
+
+	class JidLink : public QObject
+	{
+		Q_OBJECT
+	public:
+		enum { None, DTCP, IBB };
+		enum { Idle, Connecting, WaitingForAccept, Active };
+		enum { ErrConnect, ErrStream };
+		enum { StatDTCPRequesting, StatDTCPAccepted, StatDTCPConnected, StatIBBRequesting, StatIBBConnected };
+		JidLink(Client *client);
+		~JidLink();
+
+		void connectToJid(const Jid &jid, int type, const QDomElement &comment);
+		void accept();
+
+		int type() const;
+		Jid peer() const;
+		int state() const;
+
+		bool isOpen() const;
+		void close();
+		void write(const QByteArray &);
+		QByteArray read(int bytes=0);
+		int bytesAvailable() const;
+		int bytesToWrite() const;
+
+	signals:
+		void connected();
+		void connectionClosed();
+		void readyRead();
+		void bytesWritten(int);
+		void error(int);
+		void status(int);
+
+	private slots:
+		void dtcp_connected();
+		void dtcp_accepted();
+		void ibb_connected();
+
+		void bs_connectionClosed();
+		void bs_error(int);
+		void bs_readyRead();
+		void bs_bytesWritten(int);
+
+		void doRealAccept();
+
+	private:
+		class Private;
+		Private *d;
+
+		void reset(bool clear=false);
+
+		void link();
+		void unlink();
+
+		friend class JidLinkManager;
+		bool setStream(ByteStream *);
+	};
+
+	// the job of JidLinkManager is to keep track of streams and properly shut them down
+	class JidLinkManager : public QObject
+	{
+		Q_OBJECT
+	public:
+		JidLinkManager(Client *);
+		~JidLinkManager();
+
+		JidLink *takeIncoming();
+
+		void insertStream(ByteStream *);
+
+	private:
+		class Private;
+		Private *d;
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/libidn.pri
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn.pri	(revision 38)
@@ -0,0 +1,18 @@
+libidn {
+	INCLUDEPATH += $$LIBIDN_BASE
+
+	unix:{
+		QMAKE_CFLAGS_WARN_ON -= -W
+	}
+	win32:{
+		QMAKE_CFLAGS += -Zm400
+	}
+
+	SOURCES += \
+		$$LIBIDN_BASE/profiles.c \
+		#$$LIBIDN_BASE/toutf8.c \
+		$$LIBIDN_BASE/rfc3454.c \
+		$$LIBIDN_BASE/nfkc.c \
+		$$LIBIDN_BASE/stringprep.c
+}
+
Index: psi/tags/0.10-os2-b1/iris/libidn/gunicomp.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/gunicomp.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/gunicomp.h	(revision 38)
@@ -0,0 +1,658 @@
+#define COMPOSE_FIRST_START 1
+#define COMPOSE_FIRST_SINGLE_START 147
+#define COMPOSE_SECOND_START 357
+#define COMPOSE_SECOND_SINGLE_START 388
+
+#define COMPOSE_TABLE_LAST 48
+
+static const guint16 compose_data[][256] = {
+  { /* page 0, index 0 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147, 148, 149, 0, 0, 1, 2, 3, 4, 5, 
+    150, 6, 7, 8, 151, 9, 10, 11, 12, 13, 14, 0, 15, 16, 17, 18, 19, 20, 21, 
+    22, 23, 0, 0, 0, 0, 0, 0, 24, 25, 26, 27, 28, 152, 29, 30, 31, 32, 33, 
+    34, 35, 36, 37, 38, 0, 39, 40, 41, 42, 43, 44, 45, 46, 47, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 153, 154, 
+    50, 155, 0, 0, 51, 0, 0, 0, 0, 156, 0, 0, 0, 0, 52, 53, 157, 0, 158, 0, 
+    0, 0, 54, 0, 0, 0, 0, 0, 55, 0, 159, 160, 56, 161, 0, 0, 57, 0, 0, 0, 0, 
+    162, 0, 0, 0, 0, 58, 59, 163, 0, 164, 0, 0, 0, 60, 0, 0, 0
+  },
+  { /* page 1, index 1 */
+    0, 0, 61, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 64, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 65, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 166, 0, 
+    0, 0, 0, 167, 168, 0, 0, 0, 0, 0, 0, 169, 170, 171, 172, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 
+    68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 70, 0, 0, 0, 0, 0, 0, 174, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 175, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0
+  },
+  { /* page 2, index 2 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 178, 179, 180, 0, 0, 0, 0, 
+    181, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 3, index 3 */
+    357, 358, 359, 360, 361, 0, 362, 363, 364, 365, 366, 367, 368, 0, 0, 369, 
+    0, 370, 0, 371, 372, 0, 0, 0, 0, 0, 0, 373, 0, 0, 0, 0, 0, 0, 0, 374, 
+    375, 376, 377, 378, 379, 0, 0, 0, 0, 380, 381, 0, 382, 383, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 384, 0, 0, 385, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 0, 0, 0, 
+    72, 0, 73, 0, 74, 0, 0, 0, 0, 0, 75, 0, 184, 0, 0, 0, 76, 0, 0, 0, 77, 0, 
+    0, 185, 0, 186, 0, 0, 78, 0, 0, 0, 79, 0, 80, 0, 81, 0, 0, 0, 0, 0, 82, 
+    0, 83, 0, 0, 0, 84, 0, 0, 0, 85, 86, 87, 0, 0, 187, 0, 0, 0, 88, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 4, index 4 */
+    0, 0, 0, 0, 0, 0, 188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, 189, 0, 90, 
+    91, 190, 92, 0, 191, 0, 0, 0, 192, 0, 0, 0, 0, 93, 0, 0, 0, 193, 0, 0, 0, 
+    194, 0, 195, 0, 0, 94, 0, 0, 196, 0, 95, 96, 197, 97, 0, 198, 0, 0, 0, 
+    199, 0, 0, 0, 0, 98, 0, 0, 0, 200, 0, 0, 0, 201, 0, 202, 0, 0, 0, 0, 0, 
+    0, 0, 0, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 204, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, 207, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 208, 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 6, index 5 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    210, 0, 211, 0, 0, 0, 0, 0, 0, 0, 0, 388, 389, 390, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 212, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213, 0, 
+    0, 214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 9, index 6 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 215, 0, 0, 0, 0, 0, 0, 0, 
+    216, 0, 0, 217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 391, 
+    0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    392, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 11, index 7 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 393, 0, 0, 0, 0, 0, 0, 0, 0, 
+    101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 394, 395, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 396, 0, 0, 0, 0, 0, 0, 0, 102, 219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 12, index 8 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 221, 
+    0, 0, 398, 0, 0, 0, 103, 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 399, 
+    400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 13, index 9 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 401, 0, 0, 0, 0, 0, 0, 0, 104, 
+    223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 402, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 403, 0, 0, 0, 0, 404, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 105, 0, 0, 224, 0, 0, 405, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 16, index 10 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 30, index 11 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 226, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 229, 0, 0, 
+    0, 0, 0, 0, 230, 231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 233, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 235, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 31, index 12 */
+    108, 109, 236, 237, 238, 239, 240, 241, 110, 111, 242, 243, 244, 245, 
+    246, 247, 112, 113, 0, 0, 0, 0, 0, 0, 114, 115, 0, 0, 0, 0, 0, 0, 116, 
+    117, 248, 249, 250, 251, 252, 253, 118, 119, 254, 255, 256, 257, 258, 
+    259, 120, 121, 0, 0, 0, 0, 0, 0, 122, 123, 0, 0, 0, 0, 0, 0, 124, 125, 0, 
+    0, 0, 0, 0, 0, 126, 127, 0, 0, 0, 0, 0, 0, 128, 129, 0, 0, 0, 0, 0, 0, 0, 
+    130, 0, 0, 0, 0, 0, 0, 131, 132, 260, 261, 262, 263, 264, 265, 133, 134, 
+    266, 267, 268, 269, 270, 271, 272, 0, 0, 0, 273, 0, 0, 0, 0, 0, 0, 0, 
+    274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, 135, 0, 0, 0, 
+    0, 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 277, 0, 0, 0, 0, 0, 0, 0, 136, 0
+  },
+  { /* page 33, index 13 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    278, 0, 279, 0, 280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 281, 0, 282, 0, 
+    283, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 34, index 14 */
+    0, 0, 0, 284, 0, 0, 0, 0, 285, 0, 0, 286, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 287, 0, 288, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 0, 0, 0, 0, 0, 0, 290, 
+    0, 291, 0, 0, 292, 0, 0, 0, 0, 293, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 294, 0, 0, 295, 296, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 297, 298, 0, 0, 299, 300, 0, 0, 301, 302, 303, 304, 0, 0, 0, 0, 
+    305, 306, 0, 0, 307, 308, 0, 0, 0, 0, 0, 0, 0, 0, 0, 309, 310, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 311, 0, 0, 0, 0, 0, 312, 313, 0, 314, 
+    0, 0, 0, 0, 0, 0, 315, 316, 317, 318, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 48, index 15 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 0, 
+    0, 0, 0, 320, 0, 321, 0, 322, 0, 323, 0, 324, 0, 325, 0, 326, 0, 327, 0, 
+    328, 0, 329, 0, 330, 0, 331, 0, 0, 332, 0, 333, 0, 334, 0, 0, 0, 0, 0, 0, 
+    137, 0, 0, 138, 0, 0, 139, 0, 0, 140, 0, 0, 141, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 386, 387, 
+    0, 0, 335, 0, 0, 0, 0, 0, 0, 0, 0, 336, 0, 0, 0, 0, 337, 0, 338, 0, 339, 
+    0, 340, 0, 341, 0, 342, 0, 343, 0, 344, 0, 345, 0, 346, 0, 347, 0, 348, 
+    0, 0, 349, 0, 350, 0, 351, 0, 0, 0, 0, 0, 0, 142, 0, 0, 143, 0, 0, 144, 
+    0, 0, 145, 0, 0, 146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 352, 353, 354, 355, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 356, 0, 0
+  }
+};
+
+static const gint16 compose_table[COMPOSE_TABLE_LAST + 1] = {
+  0 /* page 0 */,
+  1 /* page 1 */,
+  2 /* page 2 */,
+  3 /* page 3 */,
+  4 /* page 4 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  5 /* page 6 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  6 /* page 9 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  7 /* page 11 */,
+  8 /* page 12 */,
+  9 /* page 13 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  10 /* page 16 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  11 /* page 30 */,
+  12 /* page 31 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  13 /* page 33 */,
+  14 /* page 34 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  15 /* page 48 */
+};
+
+static const guint16 compose_first_single[][2] = {
+ { 0x0338, 0x226e },
+ { 0x0338, 0x2260 },
+ { 0x0338, 0x226f },
+ { 0x0307, 0x1e1e },
+ { 0x0302, 0x0134 },
+ { 0x0307, 0x1e1f },
+ { 0x0304, 0x01de },
+ { 0x0301, 0x01fa },
+ { 0x0301, 0x1e08 },
+ { 0x0301, 0x1e2e },
+ { 0x0304, 0x022a },
+ { 0x0301, 0x01fe },
+ { 0x0304, 0x01df },
+ { 0x0301, 0x01fb },
+ { 0x0301, 0x1e09 },
+ { 0x0301, 0x1e2f },
+ { 0x0304, 0x022b },
+ { 0x0301, 0x01ff },
+ { 0x0307, 0x1e64 },
+ { 0x0307, 0x1e65 },
+ { 0x0307, 0x1e66 },
+ { 0x0307, 0x1e67 },
+ { 0x0301, 0x1e78 },
+ { 0x0301, 0x1e79 },
+ { 0x0308, 0x1e7a },
+ { 0x0308, 0x1e7b },
+ { 0x0307, 0x1e9b },
+ { 0x030c, 0x01ee },
+ { 0x0304, 0x01ec },
+ { 0x0304, 0x01ed },
+ { 0x0304, 0x01e0 },
+ { 0x0304, 0x01e1 },
+ { 0x0306, 0x1e1c },
+ { 0x0306, 0x1e1d },
+ { 0x0304, 0x0230 },
+ { 0x0304, 0x0231 },
+ { 0x030c, 0x01ef },
+ { 0x0314, 0x1fec },
+ { 0x0345, 0x1fb4 },
+ { 0x0345, 0x1fc4 },
+ { 0x0345, 0x1ff4 },
+ { 0x0308, 0x0407 },
+ { 0x0301, 0x0403 },
+ { 0x0308, 0x04de },
+ { 0x0301, 0x040c },
+ { 0x0308, 0x04e6 },
+ { 0x0308, 0x04f4 },
+ { 0x0308, 0x04f8 },
+ { 0x0308, 0x04ec },
+ { 0x0301, 0x0453 },
+ { 0x0308, 0x04df },
+ { 0x0301, 0x045c },
+ { 0x0308, 0x04e7 },
+ { 0x0308, 0x04f5 },
+ { 0x0308, 0x04f9 },
+ { 0x0308, 0x04ed },
+ { 0x0308, 0x0457 },
+ { 0x030f, 0x0476 },
+ { 0x030f, 0x0477 },
+ { 0x0308, 0x04da },
+ { 0x0308, 0x04db },
+ { 0x0308, 0x04ea },
+ { 0x0308, 0x04eb },
+ { 0x0654, 0x0624 },
+ { 0x0654, 0x0626 },
+ { 0x0654, 0x06c2 },
+ { 0x0654, 0x06d3 },
+ { 0x0654, 0x06c0 },
+ { 0x093c, 0x0929 },
+ { 0x093c, 0x0931 },
+ { 0x093c, 0x0934 },
+ { 0x0bd7, 0x0b94 },
+ { 0x0bbe, 0x0bcb },
+ { 0x0c56, 0x0c48 },
+ { 0x0cd5, 0x0cc0 },
+ { 0x0cd5, 0x0ccb },
+ { 0x0d3e, 0x0d4b },
+ { 0x0dca, 0x0ddd },
+ { 0x102e, 0x1026 },
+ { 0x0304, 0x1e38 },
+ { 0x0304, 0x1e39 },
+ { 0x0304, 0x1e5c },
+ { 0x0304, 0x1e5d },
+ { 0x0307, 0x1e68 },
+ { 0x0307, 0x1e69 },
+ { 0x0302, 0x1ec6 },
+ { 0x0302, 0x1ec7 },
+ { 0x0302, 0x1ed8 },
+ { 0x0302, 0x1ed9 },
+ { 0x0345, 0x1f82 },
+ { 0x0345, 0x1f83 },
+ { 0x0345, 0x1f84 },
+ { 0x0345, 0x1f85 },
+ { 0x0345, 0x1f86 },
+ { 0x0345, 0x1f87 },
+ { 0x0345, 0x1f8a },
+ { 0x0345, 0x1f8b },
+ { 0x0345, 0x1f8c },
+ { 0x0345, 0x1f8d },
+ { 0x0345, 0x1f8e },
+ { 0x0345, 0x1f8f },
+ { 0x0345, 0x1f92 },
+ { 0x0345, 0x1f93 },
+ { 0x0345, 0x1f94 },
+ { 0x0345, 0x1f95 },
+ { 0x0345, 0x1f96 },
+ { 0x0345, 0x1f97 },
+ { 0x0345, 0x1f9a },
+ { 0x0345, 0x1f9b },
+ { 0x0345, 0x1f9c },
+ { 0x0345, 0x1f9d },
+ { 0x0345, 0x1f9e },
+ { 0x0345, 0x1f9f },
+ { 0x0345, 0x1fa2 },
+ { 0x0345, 0x1fa3 },
+ { 0x0345, 0x1fa4 },
+ { 0x0345, 0x1fa5 },
+ { 0x0345, 0x1fa6 },
+ { 0x0345, 0x1fa7 },
+ { 0x0345, 0x1faa },
+ { 0x0345, 0x1fab },
+ { 0x0345, 0x1fac },
+ { 0x0345, 0x1fad },
+ { 0x0345, 0x1fae },
+ { 0x0345, 0x1faf },
+ { 0x0345, 0x1fb2 },
+ { 0x0345, 0x1fc2 },
+ { 0x0345, 0x1ff2 },
+ { 0x0345, 0x1fb7 },
+ { 0x0345, 0x1fc7 },
+ { 0x0345, 0x1ff7 },
+ { 0x0338, 0x219a },
+ { 0x0338, 0x219b },
+ { 0x0338, 0x21ae },
+ { 0x0338, 0x21cd },
+ { 0x0338, 0x21cf },
+ { 0x0338, 0x21ce },
+ { 0x0338, 0x2204 },
+ { 0x0338, 0x2209 },
+ { 0x0338, 0x220c },
+ { 0x0338, 0x2224 },
+ { 0x0338, 0x2226 },
+ { 0x0338, 0x2241 },
+ { 0x0338, 0x2244 },
+ { 0x0338, 0x2247 },
+ { 0x0338, 0x2249 },
+ { 0x0338, 0x226d },
+ { 0x0338, 0x2262 },
+ { 0x0338, 0x2270 },
+ { 0x0338, 0x2271 },
+ { 0x0338, 0x2274 },
+ { 0x0338, 0x2275 },
+ { 0x0338, 0x2278 },
+ { 0x0338, 0x2279 },
+ { 0x0338, 0x2280 },
+ { 0x0338, 0x2281 },
+ { 0x0338, 0x22e0 },
+ { 0x0338, 0x22e1 },
+ { 0x0338, 0x2284 },
+ { 0x0338, 0x2285 },
+ { 0x0338, 0x2288 },
+ { 0x0338, 0x2289 },
+ { 0x0338, 0x22e2 },
+ { 0x0338, 0x22e3 },
+ { 0x0338, 0x22ac },
+ { 0x0338, 0x22ad },
+ { 0x0338, 0x22ae },
+ { 0x0338, 0x22af },
+ { 0x0338, 0x22ea },
+ { 0x0338, 0x22eb },
+ { 0x0338, 0x22ec },
+ { 0x0338, 0x22ed },
+ { 0x3099, 0x3094 },
+ { 0x3099, 0x304c },
+ { 0x3099, 0x304e },
+ { 0x3099, 0x3050 },
+ { 0x3099, 0x3052 },
+ { 0x3099, 0x3054 },
+ { 0x3099, 0x3056 },
+ { 0x3099, 0x3058 },
+ { 0x3099, 0x305a },
+ { 0x3099, 0x305c },
+ { 0x3099, 0x305e },
+ { 0x3099, 0x3060 },
+ { 0x3099, 0x3062 },
+ { 0x3099, 0x3065 },
+ { 0x3099, 0x3067 },
+ { 0x3099, 0x3069 },
+ { 0x3099, 0x309e },
+ { 0x3099, 0x30f4 },
+ { 0x3099, 0x30ac },
+ { 0x3099, 0x30ae },
+ { 0x3099, 0x30b0 },
+ { 0x3099, 0x30b2 },
+ { 0x3099, 0x30b4 },
+ { 0x3099, 0x30b6 },
+ { 0x3099, 0x30b8 },
+ { 0x3099, 0x30ba },
+ { 0x3099, 0x30bc },
+ { 0x3099, 0x30be },
+ { 0x3099, 0x30c0 },
+ { 0x3099, 0x30c2 },
+ { 0x3099, 0x30c5 },
+ { 0x3099, 0x30c7 },
+ { 0x3099, 0x30c9 },
+ { 0x3099, 0x30f7 },
+ { 0x3099, 0x30f8 },
+ { 0x3099, 0x30f9 },
+ { 0x3099, 0x30fa },
+ { 0x3099, 0x30fe }
+};
+static const guint16 compose_second_single[][2] = {
+ { 0x0627, 0x0622 },
+ { 0x0627, 0x0623 },
+ { 0x0627, 0x0625 },
+ { 0x09c7, 0x09cb },
+ { 0x09c7, 0x09cc },
+ { 0x0b47, 0x0b4b },
+ { 0x0b47, 0x0b48 },
+ { 0x0b47, 0x0b4c },
+ { 0x0bc6, 0x0bca },
+ { 0x0bc6, 0x0bcc },
+ { 0x0cc6, 0x0cca },
+ { 0x0cc6, 0x0cc7 },
+ { 0x0cc6, 0x0cc8 },
+ { 0x0d46, 0x0d4a },
+ { 0x0d46, 0x0d4c },
+ { 0x0dd9, 0x0dda },
+ { 0x0dd9, 0x0ddc },
+ { 0x0dd9, 0x0dde }
+};
+static const guint16 compose_array[146][31] = {
+ { 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x0100, 0x0102, 0x0226, 0x00c4, 0x1ea2, 0x00c5,      0, 0x01cd, 0x0200, 0x0202,      0,      0,      0, 0x1ea0,      0, 0x1e00,      0,      0, 0x0104,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0, 0x1e02,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e04,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e06,      0,      0,      0,      0 },
+ {      0, 0x0106, 0x0108,      0,      0,      0, 0x010a,      0,      0,      0,      0, 0x010c,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x00c7,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0, 0x1e0a,      0,      0,      0,      0, 0x010e,      0,      0,      0,      0,      0, 0x1e0c,      0,      0,      0, 0x1e10,      0, 0x1e12,      0,      0, 0x1e0e,      0,      0,      0,      0 },
+ { 0x00c8, 0x00c9, 0x00ca, 0x1ebc, 0x0112, 0x0114, 0x0116, 0x00cb, 0x1eba,      0,      0, 0x011a, 0x0204, 0x0206,      0,      0,      0, 0x1eb8,      0,      0,      0, 0x0228, 0x0118, 0x1e18,      0, 0x1e1a,      0,      0,      0,      0,      0 },
+ {      0, 0x01f4, 0x011c,      0, 0x1e20, 0x011e, 0x0120,      0,      0,      0,      0, 0x01e6,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x0122,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0, 0x0124,      0,      0,      0, 0x1e22, 0x1e26,      0,      0,      0, 0x021e,      0,      0,      0,      0,      0, 0x1e24,      0,      0,      0, 0x1e28,      0,      0, 0x1e2a,      0,      0,      0,      0,      0,      0 },
+ { 0x00cc, 0x00cd, 0x00ce, 0x0128, 0x012a, 0x012c, 0x0130, 0x00cf, 0x1ec8,      0,      0, 0x01cf, 0x0208, 0x020a,      0,      0,      0, 0x1eca,      0,      0,      0,      0, 0x012e,      0,      0, 0x1e2c,      0,      0,      0,      0,      0 },
+ {      0, 0x1e30,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x01e8,      0,      0,      0,      0,      0, 0x1e32,      0,      0,      0, 0x0136,      0,      0,      0,      0, 0x1e34,      0,      0,      0,      0 },
+ {      0, 0x0139,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x013d,      0,      0,      0,      0,      0, 0x1e36,      0,      0,      0, 0x013b,      0, 0x1e3c,      0,      0, 0x1e3a,      0,      0,      0,      0 },
+ {      0, 0x1e3e,      0,      0,      0,      0, 0x1e40,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e42,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x01f8, 0x0143,      0, 0x00d1,      0,      0, 0x1e44,      0,      0,      0,      0, 0x0147,      0,      0,      0,      0,      0, 0x1e46,      0,      0,      0, 0x0145,      0, 0x1e4a,      0,      0, 0x1e48,      0,      0,      0,      0 },
+ { 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x014c, 0x014e, 0x022e, 0x00d6, 0x1ece,      0, 0x0150, 0x01d1, 0x020c, 0x020e,      0,      0, 0x01a0, 0x1ecc,      0,      0,      0,      0, 0x01ea,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x1e54,      0,      0,      0,      0, 0x1e56,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x0154,      0,      0,      0,      0, 0x1e58,      0,      0,      0,      0, 0x0158, 0x0210, 0x0212,      0,      0,      0, 0x1e5a,      0,      0,      0, 0x0156,      0,      0,      0,      0, 0x1e5e,      0,      0,      0,      0 },
+ {      0, 0x015a, 0x015c,      0,      0,      0, 0x1e60,      0,      0,      0,      0, 0x0160,      0,      0,      0,      0,      0, 0x1e62,      0,      0, 0x0218, 0x015e,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0, 0x1e6a,      0,      0,      0,      0, 0x0164,      0,      0,      0,      0,      0, 0x1e6c,      0,      0, 0x021a, 0x0162,      0, 0x1e70,      0,      0, 0x1e6e,      0,      0,      0,      0 },
+ { 0x00d9, 0x00da, 0x00db, 0x0168, 0x016a, 0x016c,      0, 0x00dc, 0x1ee6, 0x016e, 0x0170, 0x01d3, 0x0214, 0x0216,      0,      0, 0x01af, 0x1ee4, 0x1e72,      0,      0,      0, 0x0172, 0x1e76,      0, 0x1e74,      0,      0,      0,      0,      0 },
+ {      0,      0,      0, 0x1e7c,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e7e,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1e80, 0x1e82, 0x0174,      0,      0,      0, 0x1e86, 0x1e84,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e88,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0, 0x1e8a, 0x1e8c,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1ef2, 0x00dd, 0x0176, 0x1ef8, 0x0232,      0, 0x1e8e, 0x0178, 0x1ef6,      0,      0,      0,      0,      0,      0,      0,      0, 0x1ef4,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x0179, 0x1e90,      0,      0,      0, 0x017b,      0,      0,      0,      0, 0x017d,      0,      0,      0,      0,      0, 0x1e92,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e94,      0,      0,      0,      0 },
+ { 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x0101, 0x0103, 0x0227, 0x00e4, 0x1ea3, 0x00e5,      0, 0x01ce, 0x0201, 0x0203,      0,      0,      0, 0x1ea1,      0, 0x1e01,      0,      0, 0x0105,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0, 0x1e03,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e05,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e07,      0,      0,      0,      0 },
+ {      0, 0x0107, 0x0109,      0,      0,      0, 0x010b,      0,      0,      0,      0, 0x010d,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x00e7,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0, 0x1e0b,      0,      0,      0,      0, 0x010f,      0,      0,      0,      0,      0, 0x1e0d,      0,      0,      0, 0x1e11,      0, 0x1e13,      0,      0, 0x1e0f,      0,      0,      0,      0 },
+ { 0x00e8, 0x00e9, 0x00ea, 0x1ebd, 0x0113, 0x0115, 0x0117, 0x00eb, 0x1ebb,      0,      0, 0x011b, 0x0205, 0x0207,      0,      0,      0, 0x1eb9,      0,      0,      0, 0x0229, 0x0119, 0x1e19,      0, 0x1e1b,      0,      0,      0,      0,      0 },
+ {      0, 0x01f5, 0x011d,      0, 0x1e21, 0x011f, 0x0121,      0,      0,      0,      0, 0x01e7,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x0123,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0, 0x0125,      0,      0,      0, 0x1e23, 0x1e27,      0,      0,      0, 0x021f,      0,      0,      0,      0,      0, 0x1e25,      0,      0,      0, 0x1e29,      0,      0, 0x1e2b,      0, 0x1e96,      0,      0,      0,      0 },
+ { 0x00ec, 0x00ed, 0x00ee, 0x0129, 0x012b, 0x012d,      0, 0x00ef, 0x1ec9,      0,      0, 0x01d0, 0x0209, 0x020b,      0,      0,      0, 0x1ecb,      0,      0,      0,      0, 0x012f,      0,      0, 0x1e2d,      0,      0,      0,      0,      0 },
+ {      0,      0, 0x0135,      0,      0,      0,      0,      0,      0,      0,      0, 0x01f0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x1e31,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x01e9,      0,      0,      0,      0,      0, 0x1e33,      0,      0,      0, 0x0137,      0,      0,      0,      0, 0x1e35,      0,      0,      0,      0 },
+ {      0, 0x013a,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x013e,      0,      0,      0,      0,      0, 0x1e37,      0,      0,      0, 0x013c,      0, 0x1e3d,      0,      0, 0x1e3b,      0,      0,      0,      0 },
+ {      0, 0x1e3f,      0,      0,      0,      0, 0x1e41,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e43,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x01f9, 0x0144,      0, 0x00f1,      0,      0, 0x1e45,      0,      0,      0,      0, 0x0148,      0,      0,      0,      0,      0, 0x1e47,      0,      0,      0, 0x0146,      0, 0x1e4b,      0,      0, 0x1e49,      0,      0,      0,      0 },
+ { 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x014d, 0x014f, 0x022f, 0x00f6, 0x1ecf,      0, 0x0151, 0x01d2, 0x020d, 0x020f,      0,      0, 0x01a1, 0x1ecd,      0,      0,      0,      0, 0x01eb,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x1e55,      0,      0,      0,      0, 0x1e57,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x0155,      0,      0,      0,      0, 0x1e59,      0,      0,      0,      0, 0x0159, 0x0211, 0x0213,      0,      0,      0, 0x1e5b,      0,      0,      0, 0x0157,      0,      0,      0,      0, 0x1e5f,      0,      0,      0,      0 },
+ {      0, 0x015b, 0x015d,      0,      0,      0, 0x1e61,      0,      0,      0,      0, 0x0161,      0,      0,      0,      0,      0, 0x1e63,      0,      0, 0x0219, 0x015f,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0, 0x1e6b, 0x1e97,      0,      0,      0, 0x0165,      0,      0,      0,      0,      0, 0x1e6d,      0,      0, 0x021b, 0x0163,      0, 0x1e71,      0,      0, 0x1e6f,      0,      0,      0,      0 },
+ { 0x00f9, 0x00fa, 0x00fb, 0x0169, 0x016b, 0x016d,      0, 0x00fc, 0x1ee7, 0x016f, 0x0171, 0x01d4, 0x0215, 0x0217,      0,      0, 0x01b0, 0x1ee5, 0x1e73,      0,      0,      0, 0x0173, 0x1e77,      0, 0x1e75,      0,      0,      0,      0,      0 },
+ {      0,      0,      0, 0x1e7d,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e7f,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1e81, 0x1e83, 0x0175,      0,      0,      0, 0x1e87, 0x1e85,      0, 0x1e98,      0,      0,      0,      0,      0,      0,      0, 0x1e89,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0, 0x1e8b, 0x1e8d,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1ef3, 0x00fd, 0x0177, 0x1ef9, 0x0233,      0, 0x1e8f, 0x00ff, 0x1ef7, 0x1e99,      0,      0,      0,      0,      0,      0,      0, 0x1ef5,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x017a, 0x1e91,      0,      0,      0, 0x017c,      0,      0,      0,      0, 0x017e,      0,      0,      0,      0,      0, 0x1e93,      0,      0,      0,      0,      0,      0,      0,      0, 0x1e95,      0,      0,      0,      0 },
+ { 0x1fed, 0x0385,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fc1,      0,      0,      0 },
+ { 0x1ea6, 0x1ea4,      0, 0x1eaa,      0,      0,      0,      0, 0x1ea8,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x01fc,      0,      0, 0x01e2,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1ec0, 0x1ebe,      0, 0x1ec4,      0,      0,      0,      0, 0x1ec2,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1ed2, 0x1ed0,      0, 0x1ed6,      0,      0,      0,      0, 0x1ed4,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x1e4c,      0,      0, 0x022c,      0,      0, 0x1e4e,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x01db, 0x01d7,      0,      0, 0x01d5,      0,      0,      0,      0,      0,      0, 0x01d9,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1ea7, 0x1ea5,      0, 0x1eab,      0,      0,      0,      0, 0x1ea9,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x01fd,      0,      0, 0x01e3,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1ec1, 0x1ebf,      0, 0x1ec5,      0,      0,      0,      0, 0x1ec3,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1ed3, 0x1ed1,      0, 0x1ed7,      0,      0,      0,      0, 0x1ed5,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0, 0x1e4d,      0,      0, 0x022d,      0,      0, 0x1e4f,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x01dc, 0x01d8,      0,      0, 0x01d6,      0,      0,      0,      0,      0,      0, 0x01da,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1eb0, 0x1eae,      0, 0x1eb4,      0,      0,      0,      0, 0x1eb2,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1eb1, 0x1eaf,      0, 0x1eb5,      0,      0,      0,      0, 0x1eb3,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1e14, 0x1e16,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1e15, 0x1e17,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1e50, 0x1e52,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1e51, 0x1e53,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1edc, 0x1eda,      0, 0x1ee0,      0,      0,      0,      0, 0x1ede,      0,      0,      0,      0,      0,      0,      0,      0, 0x1ee2,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1edd, 0x1edb,      0, 0x1ee1,      0,      0,      0,      0, 0x1edf,      0,      0,      0,      0,      0,      0,      0,      0, 0x1ee3,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1eea, 0x1ee8,      0, 0x1eee,      0,      0,      0,      0, 0x1eec,      0,      0,      0,      0,      0,      0,      0,      0, 0x1ef0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1eeb, 0x1ee9,      0, 0x1eef,      0,      0,      0,      0, 0x1eed,      0,      0,      0,      0,      0,      0,      0,      0, 0x1ef1,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1fba, 0x0386,      0,      0, 0x1fb9, 0x1fb8,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f08, 0x1f09,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fbc,      0,      0 },
+ { 0x1fc8, 0x0388,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f18, 0x1f19,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1fca, 0x0389,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f28, 0x1f29,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fcc,      0,      0 },
+ { 0x1fda, 0x038a,      0,      0, 0x1fd9, 0x1fd8,      0, 0x03aa,      0,      0,      0,      0,      0,      0, 0x1f38, 0x1f39,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1ff8, 0x038c,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f48, 0x1f49,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1fea, 0x038e,      0,      0, 0x1fe9, 0x1fe8,      0, 0x03ab,      0,      0,      0,      0,      0,      0,      0, 0x1f59,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1ffa, 0x038f,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f68, 0x1f69,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1ffc,      0,      0 },
+ { 0x1f70, 0x03ac,      0,      0, 0x1fb1, 0x1fb0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f00, 0x1f01,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fb6, 0x1fb3,      0,      0 },
+ { 0x1f72, 0x03ad,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f10, 0x1f11,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f74, 0x03ae,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f20, 0x1f21,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fc6, 0x1fc3,      0,      0 },
+ { 0x1f76, 0x03af,      0,      0, 0x1fd1, 0x1fd0,      0, 0x03ca,      0,      0,      0,      0,      0,      0, 0x1f30, 0x1f31,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fd6,      0,      0,      0 },
+ { 0x1f78, 0x03cc,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f40, 0x1f41,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fe4, 0x1fe5,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f7a, 0x03cd,      0,      0, 0x1fe1, 0x1fe0,      0, 0x03cb,      0,      0,      0,      0,      0,      0, 0x1f50, 0x1f51,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fe6,      0,      0,      0 },
+ { 0x1f7c, 0x03ce,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f60, 0x1f61,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1ff6, 0x1ff3,      0,      0 },
+ { 0x1fd2, 0x0390,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fd7,      0,      0,      0 },
+ { 0x1fe2, 0x03b0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fe7,      0,      0,      0 },
+ {      0, 0x03d3,      0,      0,      0,      0,      0, 0x03d4,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0, 0x04d0,      0, 0x04d2,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x0400,      0,      0,      0,      0, 0x04d6,      0, 0x0401,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0, 0x04c1,      0, 0x04dc,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x040d,      0,      0,      0, 0x04e2, 0x0419,      0, 0x04e4,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0, 0x04ee, 0x040e,      0, 0x04f0,      0,      0, 0x04f2,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0, 0x04d1,      0, 0x04d3,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x0450,      0,      0,      0,      0, 0x04d7,      0, 0x0451,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0, 0x04c2,      0, 0x04dd,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x045d,      0,      0,      0, 0x04e3, 0x0439,      0, 0x04e5,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0, 0x04ef, 0x045e,      0, 0x04f1,      0,      0, 0x04f3,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0, 0x1eac,      0,      0, 0x1eb6,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ {      0,      0, 0x1ead,      0,      0, 0x1eb7,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f02, 0x1f04,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f06, 0x1f80,      0,      0 },
+ { 0x1f03, 0x1f05,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f07, 0x1f81,      0,      0 },
+ { 0x1f0a, 0x1f0c,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f0e, 0x1f88,      0,      0 },
+ { 0x1f0b, 0x1f0d,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f0f, 0x1f89,      0,      0 },
+ { 0x1f12, 0x1f14,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f13, 0x1f15,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f1a, 0x1f1c,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f1b, 0x1f1d,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f22, 0x1f24,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f26, 0x1f90,      0,      0 },
+ { 0x1f23, 0x1f25,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f27, 0x1f91,      0,      0 },
+ { 0x1f2a, 0x1f2c,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f2e, 0x1f98,      0,      0 },
+ { 0x1f2b, 0x1f2d,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f2f, 0x1f99,      0,      0 },
+ { 0x1f32, 0x1f34,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f36,      0,      0,      0 },
+ { 0x1f33, 0x1f35,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f37,      0,      0,      0 },
+ { 0x1f3a, 0x1f3c,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f3e,      0,      0,      0 },
+ { 0x1f3b, 0x1f3d,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f3f,      0,      0,      0 },
+ { 0x1f42, 0x1f44,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f43, 0x1f45,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f4a, 0x1f4c,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f4b, 0x1f4d,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0 },
+ { 0x1f52, 0x1f54,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f56,      0,      0,      0 },
+ { 0x1f53, 0x1f55,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f57,      0,      0,      0 },
+ { 0x1f5b, 0x1f5d,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f5f,      0,      0,      0 },
+ { 0x1f62, 0x1f64,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f66, 0x1fa0,      0,      0 },
+ { 0x1f63, 0x1f65,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f67, 0x1fa1,      0,      0 },
+ { 0x1f6a, 0x1f6c,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f6e, 0x1fa8,      0,      0 },
+ { 0x1f6b, 0x1f6d,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1f6f, 0x1fa9,      0,      0 },
+ { 0x1fcd, 0x1fce,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fcf,      0,      0,      0 },
+ { 0x1fdd, 0x1fde,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x1fdf,      0,      0,      0 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x3070, 0x3071 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x3073, 0x3074 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x3076, 0x3077 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x3079, 0x307a },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x307c, 0x307d },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x30d0, 0x30d1 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x30d3, 0x30d4 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x30d6, 0x30d7 },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x30d9, 0x30da },
+ {      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0,      0, 0x30dc, 0x30dd }
+};
Index: psi/tags/0.10-os2-b1/iris/libidn/gunidecomp.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/gunidecomp.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/gunidecomp.h	(revision 38)
@@ -0,0 +1,10362 @@
+/* This file is automatically generated.  DO NOT EDIT! */
+
+#ifndef DECOMP_H
+#define DECOMP_H
+
+#define G_UNICODE_LAST_CHAR 0x10ffff
+
+#define G_UNICODE_MAX_TABLE_INDEX (0x110000 / 256)
+
+#define G_UNICODE_LAST_CHAR_PART1 0x2FAFF
+
+#define G_UNICODE_LAST_PAGE_PART1 762
+
+#define G_UNICODE_NOT_PRESENT_OFFSET 65535
+
+static const guchar cclass_data[][256] = {
+  { /* page 3, index 0 */
+    230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 
+    230, 230, 230, 230, 230, 230, 230, 232, 220, 220, 220, 220, 232, 216, 
+    220, 220, 220, 220, 220, 202, 202, 220, 220, 220, 220, 202, 202, 220, 
+    220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 1, 1, 1, 1, 1, 220, 
+    220, 220, 220, 230, 230, 230, 230, 230, 230, 230, 230, 240, 230, 220, 
+    220, 220, 230, 230, 230, 220, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 234, 234, 233, 230, 230, 230, 230, 230, 230, 230, 230, 230, 
+    230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0
+  },
+  { /* page 4, index 1 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 5, index 2 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 220, 230, 230, 230, 230, 220, 230, 230, 230, 222, 220, 230, 230, 230, 
+    230, 230, 230, 0, 220, 220, 220, 220, 220, 230, 230, 220, 230, 230, 222, 
+    228, 230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 20, 21, 22, 0, 23, 
+    0, 24, 25, 0, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 6, index 3 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 230, 230, 220, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 230, 230, 230, 230, 230, 230, 230, 0, 0, 230, 230, 230, 230, 220, 
+    230, 0, 0, 230, 230, 0, 220, 230, 230, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 7, index 4 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    230, 220, 230, 230, 220, 230, 230, 220, 220, 220, 230, 220, 220, 230, 
+    220, 230, 230, 230, 220, 230, 220, 230, 220, 230, 220, 230, 230, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 9, index 5 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 9, 0, 0, 0, 230, 220, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 10, index 6 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 11, index 7 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 12, index 8 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 84, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 13, index 9 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 14, index 10 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 103, 103, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 107, 107, 107, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 118, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, 122, 122, 122, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 15, index 11 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    220, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 220, 0, 220, 0, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 130, 0, 
+    132, 0, 0, 0, 0, 0, 130, 130, 130, 130, 0, 0, 130, 0, 230, 230, 9, 0, 
+    230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0
+  },
+  { /* page 16, index 12 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 7, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 23, index 13 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 24, index 14 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 32, index 15 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 1, 1, 230, 230, 
+    230, 230, 1, 1, 1, 230, 230, 0, 0, 0, 0, 230, 0, 0, 0, 1, 1, 230, 220, 
+    230, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 48, index 16 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 228, 232, 222, 
+    224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 251, index 17 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 254, index 18 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  },
+  { /* page 465, index 19 */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 216, 216, 1, 1, 1, 0, 0, 0, 226, 216, 216, 216, 216, 216, 
+    0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 220, 220, 220, 220, 220, 0, 0, 
+    230, 230, 230, 230, 230, 220, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0
+  }
+};
+
+static const gint16 combining_class_table_part1[763] = {
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 /* page 3 */,
+  1 /* page 4 */,
+  2 /* page 5 */,
+  3 /* page 6 */,
+  4 /* page 7 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  5 /* page 9 */,
+  6 /* page 10 */,
+  7 /* page 11 */,
+  8 /* page 12 */,
+  9 /* page 13 */,
+  10 /* page 14 */,
+  11 /* page 15 */,
+  12 /* page 16 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  13 /* page 23 */,
+  14 /* page 24 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  15 /* page 32 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  16 /* page 48 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  17 /* page 251 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  18 /* page 254 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  19 /* page 465 */,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX
+};
+
+static const gint16 combining_class_table_part2[768] = {
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX,
+  0 + G_UNICODE_MAX_TABLE_INDEX
+};
+
+typedef struct
+{
+  gunichar ch;
+  guint16 canon_offset;
+  guint16 compat_offset;
+} decomposition;
+
+static const decomposition decomp_table[] =
+{
+  { 0x00a0, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x00a8, G_UNICODE_NOT_PRESENT_OFFSET, 2 },
+  { 0x00aa, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x00af, G_UNICODE_NOT_PRESENT_OFFSET, 8 },
+  { 0x00b2, G_UNICODE_NOT_PRESENT_OFFSET, 12 },
+  { 0x00b3, G_UNICODE_NOT_PRESENT_OFFSET, 14 },
+  { 0x00b4, G_UNICODE_NOT_PRESENT_OFFSET, 16 },
+  { 0x00b5, G_UNICODE_NOT_PRESENT_OFFSET, 20 },
+  { 0x00b8, G_UNICODE_NOT_PRESENT_OFFSET, 23 },
+  { 0x00b9, G_UNICODE_NOT_PRESENT_OFFSET, 27 },
+  { 0x00ba, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x00bc, G_UNICODE_NOT_PRESENT_OFFSET, 31 },
+  { 0x00bd, G_UNICODE_NOT_PRESENT_OFFSET, 37 },
+  { 0x00be, G_UNICODE_NOT_PRESENT_OFFSET, 43 },
+  { 0x00c0, 49, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00c1, 53, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00c2, 57, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00c3, 61, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00c4, 65, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00c5, 69, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00c7, 73, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00c8, 77, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00c9, 81, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00ca, 85, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00cb, 89, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00cc, 93, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00cd, 97, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00ce, 101, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00cf, 105, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00d1, 109, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00d2, 113, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00d3, 117, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00d4, 121, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00d5, 125, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00d6, 129, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00d9, 133, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00da, 137, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00db, 141, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00dc, 145, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00dd, 149, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00e0, 153, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00e1, 157, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00e2, 161, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00e3, 165, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00e4, 169, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00e5, 173, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00e7, 177, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00e8, 181, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00e9, 185, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00ea, 189, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00eb, 193, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00ec, 197, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00ed, 201, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00ee, 205, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00ef, 209, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00f1, 213, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00f2, 217, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00f3, 221, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00f4, 225, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00f5, 229, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00f6, 233, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00f9, 237, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00fa, 241, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00fb, 245, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00fc, 249, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00fd, 253, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x00ff, 257, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0100, 261, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0101, 265, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0102, 269, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0103, 273, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0104, 277, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0105, 281, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0106, 285, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0107, 289, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0108, 293, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0109, 297, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x010a, 301, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x010b, 305, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x010c, 309, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x010d, 313, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x010e, 317, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x010f, 321, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0112, 325, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0113, 329, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0114, 333, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0115, 337, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0116, 341, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0117, 345, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0118, 349, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0119, 353, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x011a, 357, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x011b, 361, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x011c, 365, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x011d, 369, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x011e, 373, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x011f, 377, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0120, 381, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0121, 385, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0122, 389, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0123, 393, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0124, 397, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0125, 401, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0128, 405, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0129, 409, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x012a, 413, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x012b, 417, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x012c, 421, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x012d, 425, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x012e, 429, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x012f, 433, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0130, 437, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0132, G_UNICODE_NOT_PRESENT_OFFSET, 441 },
+  { 0x0133, G_UNICODE_NOT_PRESENT_OFFSET, 444 },
+  { 0x0134, 447, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0135, 451, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0136, 455, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0137, 459, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0139, 463, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x013a, 467, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x013b, 471, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x013c, 475, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x013d, 479, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x013e, 483, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x013f, G_UNICODE_NOT_PRESENT_OFFSET, 487 },
+  { 0x0140, G_UNICODE_NOT_PRESENT_OFFSET, 491 },
+  { 0x0143, 495, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0144, 499, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0145, 503, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0146, 507, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0147, 511, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0148, 515, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0149, G_UNICODE_NOT_PRESENT_OFFSET, 519 },
+  { 0x014c, 523, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x014d, 527, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x014e, 531, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x014f, 535, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0150, 539, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0151, 543, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0154, 547, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0155, 551, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0156, 555, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0157, 559, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0158, 563, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0159, 567, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x015a, 571, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x015b, 575, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x015c, 579, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x015d, 583, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x015e, 587, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x015f, 591, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0160, 595, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0161, 599, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0162, 603, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0163, 607, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0164, 611, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0165, 615, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0168, 619, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0169, 623, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x016a, 627, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x016b, 631, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x016c, 635, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x016d, 639, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x016e, 643, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x016f, 647, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0170, 651, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0171, 655, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0172, 659, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0173, 663, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0174, 667, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0175, 671, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0176, 675, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0177, 679, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0178, 683, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0179, 687, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x017a, 691, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x017b, 695, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x017c, 699, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x017d, 703, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x017e, 707, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x017f, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x01a0, 713, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01a1, 717, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01af, 721, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01b0, 725, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01c4, G_UNICODE_NOT_PRESENT_OFFSET, 729 },
+  { 0x01c5, G_UNICODE_NOT_PRESENT_OFFSET, 734 },
+  { 0x01c6, G_UNICODE_NOT_PRESENT_OFFSET, 739 },
+  { 0x01c7, G_UNICODE_NOT_PRESENT_OFFSET, 744 },
+  { 0x01c8, G_UNICODE_NOT_PRESENT_OFFSET, 747 },
+  { 0x01c9, G_UNICODE_NOT_PRESENT_OFFSET, 750 },
+  { 0x01ca, G_UNICODE_NOT_PRESENT_OFFSET, 753 },
+  { 0x01cb, G_UNICODE_NOT_PRESENT_OFFSET, 756 },
+  { 0x01cc, G_UNICODE_NOT_PRESENT_OFFSET, 759 },
+  { 0x01cd, 762, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01ce, 766, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01cf, 770, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d0, 774, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d1, 778, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d2, 782, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d3, 786, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d4, 790, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d5, 794, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d6, 800, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d7, 806, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d8, 812, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01d9, 818, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01da, 824, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01db, 830, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01dc, 836, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01de, 842, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01df, 848, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01e0, 854, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01e1, 860, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01e2, 866, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01e3, 871, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01e6, 876, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01e7, 880, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01e8, 884, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01e9, 888, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01ea, 892, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01eb, 896, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01ec, 900, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01ed, 906, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01ee, 912, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01ef, 917, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01f0, 922, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01f1, G_UNICODE_NOT_PRESENT_OFFSET, 926 },
+  { 0x01f2, G_UNICODE_NOT_PRESENT_OFFSET, 929 },
+  { 0x01f3, G_UNICODE_NOT_PRESENT_OFFSET, 932 },
+  { 0x01f4, 935, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01f5, 939, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01f8, 943, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01f9, 947, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01fa, 951, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01fb, 957, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01fc, 963, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01fd, 968, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01fe, 973, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x01ff, 978, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0200, 983, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0201, 987, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0202, 991, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0203, 995, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0204, 999, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0205, 1003, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0206, 1007, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0207, 1011, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0208, 1015, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0209, 1019, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x020a, 1023, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x020b, 1027, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x020c, 1031, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x020d, 1035, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x020e, 1039, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x020f, 1043, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0210, 1047, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0211, 1051, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0212, 1055, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0213, 1059, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0214, 1063, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0215, 1067, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0216, 1071, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0217, 1075, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0218, 1079, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0219, 1083, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x021a, 1087, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x021b, 1091, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x021e, 1095, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x021f, 1099, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0226, 1103, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0227, 1107, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0228, 1111, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0229, 1115, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x022a, 1119, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x022b, 1125, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x022c, 1131, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x022d, 1137, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x022e, 1143, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x022f, 1147, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0230, 1151, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0231, 1157, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0232, 1163, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0233, 1167, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x02b0, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x02b1, G_UNICODE_NOT_PRESENT_OFFSET, 1173 },
+  { 0x02b2, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x02b3, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x02b4, G_UNICODE_NOT_PRESENT_OFFSET, 1180 },
+  { 0x02b5, G_UNICODE_NOT_PRESENT_OFFSET, 1183 },
+  { 0x02b6, G_UNICODE_NOT_PRESENT_OFFSET, 1186 },
+  { 0x02b7, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x02b8, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x02d8, G_UNICODE_NOT_PRESENT_OFFSET, 1193 },
+  { 0x02d9, G_UNICODE_NOT_PRESENT_OFFSET, 1197 },
+  { 0x02da, G_UNICODE_NOT_PRESENT_OFFSET, 1201 },
+  { 0x02db, G_UNICODE_NOT_PRESENT_OFFSET, 1205 },
+  { 0x02dc, G_UNICODE_NOT_PRESENT_OFFSET, 1209 },
+  { 0x02dd, G_UNICODE_NOT_PRESENT_OFFSET, 1213 },
+  { 0x02e0, G_UNICODE_NOT_PRESENT_OFFSET, 1217 },
+  { 0x02e1, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x02e2, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x02e3, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x02e4, G_UNICODE_NOT_PRESENT_OFFSET, 1224 },
+  { 0x0340, 1227, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0341, 1230, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0343, 1233, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0344, 1236, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0374, 1241, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x037a, G_UNICODE_NOT_PRESENT_OFFSET, 1244 },
+  { 0x037e, 1248, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0384, G_UNICODE_NOT_PRESENT_OFFSET, 16 },
+  { 0x0385, 1250, 1255 },
+  { 0x0386, 1261, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0387, 1266, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0388, 1269, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0389, 1274, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x038a, 1279, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x038c, 1284, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x038e, 1289, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x038f, 1294, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0390, 1299, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03aa, 1306, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03ab, 1311, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03ac, 1316, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03ad, 1321, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03ae, 1326, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03af, 1331, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03b0, 1336, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03ca, 1343, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03cb, 1348, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03cc, 1353, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03cd, 1358, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03ce, 1363, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x03d0, G_UNICODE_NOT_PRESENT_OFFSET, 1368 },
+  { 0x03d1, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x03d2, G_UNICODE_NOT_PRESENT_OFFSET, 1374 },
+  { 0x03d3, 1377, 1289 },
+  { 0x03d4, 1382, 1311 },
+  { 0x03d5, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x03d6, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x03f0, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x03f1, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x03f2, G_UNICODE_NOT_PRESENT_OFFSET, 1399 },
+  { 0x03f4, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x03f5, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x0400, 1408, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0401, 1413, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0403, 1418, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0407, 1423, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x040c, 1428, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x040d, 1433, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x040e, 1438, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0419, 1443, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0439, 1448, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0450, 1453, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0451, 1458, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0453, 1463, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0457, 1468, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x045c, 1473, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x045d, 1478, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x045e, 1483, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0476, 1488, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0477, 1493, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04c1, 1498, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04c2, 1503, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04d0, 1508, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04d1, 1513, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04d2, 1518, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04d3, 1523, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04d6, 1528, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04d7, 1533, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04da, 1538, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04db, 1543, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04dc, 1548, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04dd, 1553, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04de, 1558, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04df, 1563, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04e2, 1568, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04e3, 1573, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04e4, 1578, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04e5, 1583, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04e6, 1588, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04e7, 1593, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04ea, 1598, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04eb, 1603, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04ec, 1608, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04ed, 1613, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04ee, 1618, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04ef, 1623, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04f0, 1628, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04f1, 1633, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04f2, 1638, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04f3, 1643, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04f4, 1648, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04f5, 1653, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04f8, 1658, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x04f9, 1663, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0587, G_UNICODE_NOT_PRESENT_OFFSET, 1668 },
+  { 0x0622, 1673, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0623, 1678, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0624, 1683, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0625, 1688, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0626, 1693, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0675, G_UNICODE_NOT_PRESENT_OFFSET, 1698 },
+  { 0x0676, G_UNICODE_NOT_PRESENT_OFFSET, 1703 },
+  { 0x0677, G_UNICODE_NOT_PRESENT_OFFSET, 1708 },
+  { 0x0678, G_UNICODE_NOT_PRESENT_OFFSET, 1713 },
+  { 0x06c0, 1718, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x06c2, 1723, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x06d3, 1728, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0929, 1733, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0931, 1740, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0934, 1747, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0958, 1754, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0959, 1761, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x095a, 1768, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x095b, 1775, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x095c, 1782, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x095d, 1789, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x095e, 1796, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x095f, 1803, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x09cb, 1810, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x09cc, 1817, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x09dc, 1824, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x09dd, 1831, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x09df, 1838, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0a33, 1845, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0a36, 1852, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0a59, 1859, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0a5a, 1866, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0a5b, 1873, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0a5e, 1880, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0b48, 1887, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0b4b, 1894, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0b4c, 1901, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0b5c, 1908, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0b5d, 1915, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0b94, 1922, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0bca, 1929, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0bcb, 1936, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0bcc, 1943, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0c48, 1950, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0cc0, 1957, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0cc7, 1964, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0cc8, 1971, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0cca, 1978, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0ccb, 1985, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0d4a, 1995, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0d4b, 2002, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0d4c, 2009, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0dda, 2016, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0ddc, 2023, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0ddd, 2030, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0dde, 2040, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0e33, G_UNICODE_NOT_PRESENT_OFFSET, 2047 },
+  { 0x0eb3, G_UNICODE_NOT_PRESENT_OFFSET, 2054 },
+  { 0x0edc, G_UNICODE_NOT_PRESENT_OFFSET, 2061 },
+  { 0x0edd, G_UNICODE_NOT_PRESENT_OFFSET, 2068 },
+  { 0x0f0c, G_UNICODE_NOT_PRESENT_OFFSET, 2075 },
+  { 0x0f43, 2079, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f4d, 2086, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f52, 2093, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f57, 2100, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f5c, 2107, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f69, 2114, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f73, 2121, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f75, 2128, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f76, 2135, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f77, G_UNICODE_NOT_PRESENT_OFFSET, 2142 },
+  { 0x0f78, 2152, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f79, G_UNICODE_NOT_PRESENT_OFFSET, 2159 },
+  { 0x0f81, 2169, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f93, 2176, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0f9d, 2183, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0fa2, 2190, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0fa7, 2197, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0fac, 2204, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x0fb9, 2211, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1026, 2218, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e00, 2225, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e01, 2229, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e02, 2233, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e03, 2237, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e04, 2241, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e05, 2245, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e06, 2249, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e07, 2253, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e08, 2257, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e09, 2263, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e0a, 2269, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e0b, 2273, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e0c, 2277, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e0d, 2281, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e0e, 2285, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e0f, 2289, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e10, 2293, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e11, 2297, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e12, 2301, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e13, 2305, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e14, 2309, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e15, 2315, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e16, 2321, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e17, 2327, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e18, 2333, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e19, 2337, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e1a, 2341, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e1b, 2345, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e1c, 2349, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e1d, 2355, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e1e, 2361, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e1f, 2365, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e20, 2369, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e21, 2373, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e22, 2377, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e23, 2381, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e24, 2385, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e25, 2389, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e26, 2393, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e27, 2397, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e28, 2401, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e29, 2405, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e2a, 2409, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e2b, 2413, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e2c, 2417, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e2d, 2421, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e2e, 2425, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e2f, 2431, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e30, 2437, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e31, 2441, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e32, 2445, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e33, 2449, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e34, 2453, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e35, 2457, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e36, 2461, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e37, 2465, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e38, 2469, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e39, 2475, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e3a, 2481, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e3b, 2485, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e3c, 2489, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e3d, 2493, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e3e, 2497, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e3f, 2501, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e40, 2505, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e41, 2509, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e42, 2513, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e43, 2517, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e44, 2521, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e45, 2525, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e46, 2529, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e47, 2533, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e48, 2537, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e49, 2541, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e4a, 2545, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e4b, 2549, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e4c, 2553, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e4d, 2559, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e4e, 2565, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e4f, 2571, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e50, 2577, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e51, 2583, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e52, 2589, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e53, 2595, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e54, 2601, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e55, 2605, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e56, 2609, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e57, 2613, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e58, 2617, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e59, 2621, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e5a, 2625, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e5b, 2629, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e5c, 2633, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e5d, 2639, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e5e, 2645, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e5f, 2649, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e60, 2653, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e61, 2657, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e62, 2661, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e63, 2665, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e64, 2669, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e65, 2675, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e66, 2681, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e67, 2687, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e68, 2693, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e69, 2699, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e6a, 2705, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e6b, 2709, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e6c, 2713, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e6d, 2717, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e6e, 2721, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e6f, 2725, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e70, 2729, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e71, 2733, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e72, 2737, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e73, 2741, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e74, 2745, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e75, 2749, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e76, 2753, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e77, 2757, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e78, 2761, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e79, 2767, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e7a, 2773, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e7b, 2779, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e7c, 2785, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e7d, 2789, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e7e, 2793, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e7f, 2797, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e80, 2801, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e81, 2805, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e82, 2809, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e83, 2813, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e84, 2817, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e85, 2821, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e86, 2825, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e87, 2829, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e88, 2833, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e89, 2837, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e8a, 2841, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e8b, 2845, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e8c, 2849, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e8d, 2853, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e8e, 2857, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e8f, 2861, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e90, 2865, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e91, 2869, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e92, 2873, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e93, 2877, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e94, 2881, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e95, 2885, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e96, 2889, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e97, 2893, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e98, 2897, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e99, 2901, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1e9a, G_UNICODE_NOT_PRESENT_OFFSET, 2905 },
+  { 0x1e9b, 2909, 2657 },
+  { 0x1ea0, 2914, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ea1, 2918, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ea2, 2922, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ea3, 2926, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ea4, 2930, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ea5, 2936, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ea6, 2942, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ea7, 2948, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ea8, 2954, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ea9, 2960, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eaa, 2966, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eab, 2972, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eac, 2978, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ead, 2984, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eae, 2990, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eaf, 2996, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb0, 3002, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb1, 3008, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb2, 3014, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb3, 3020, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb4, 3026, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb5, 3032, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb6, 3038, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb7, 3044, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb8, 3050, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eb9, 3054, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eba, 3058, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ebb, 3062, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ebc, 3066, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ebd, 3070, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ebe, 3074, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ebf, 3080, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec0, 3086, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec1, 3092, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec2, 3098, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec3, 3104, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec4, 3110, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec5, 3116, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec6, 3122, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec7, 3128, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec8, 3134, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ec9, 3138, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eca, 3142, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ecb, 3146, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ecc, 3150, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ecd, 3154, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ece, 3158, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ecf, 3162, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed0, 3166, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed1, 3172, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed2, 3178, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed3, 3184, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed4, 3190, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed5, 3196, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed6, 3202, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed7, 3208, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed8, 3214, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ed9, 3220, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eda, 3226, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1edb, 3232, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1edc, 3238, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1edd, 3244, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ede, 3250, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1edf, 3256, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee0, 3262, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee1, 3268, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee2, 3274, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee3, 3280, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee4, 3286, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee5, 3290, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee6, 3294, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee7, 3298, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee8, 3302, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ee9, 3308, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eea, 3314, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eeb, 3320, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eec, 3326, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eed, 3332, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eee, 3338, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1eef, 3344, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef0, 3350, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef1, 3356, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef2, 3362, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef3, 3366, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef4, 3370, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef5, 3374, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef6, 3378, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef7, 3382, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef8, 3386, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ef9, 3390, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f00, 3394, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f01, 3399, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f02, 3404, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f03, 3411, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f04, 3418, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f05, 3425, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f06, 3432, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f07, 3439, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f08, 3446, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f09, 3451, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f0a, 3456, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f0b, 3463, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f0c, 3470, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f0d, 3477, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f0e, 3484, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f0f, 3491, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f10, 3498, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f11, 3503, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f12, 3508, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f13, 3515, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f14, 3522, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f15, 3529, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f18, 3536, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f19, 3541, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f1a, 3546, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f1b, 3553, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f1c, 3560, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f1d, 3567, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f20, 3574, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f21, 3579, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f22, 3584, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f23, 3591, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f24, 3598, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f25, 3605, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f26, 3612, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f27, 3619, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f28, 3626, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f29, 3631, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f2a, 3636, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f2b, 3643, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f2c, 3650, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f2d, 3657, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f2e, 3664, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f2f, 3671, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f30, 3678, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f31, 3683, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f32, 3688, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f33, 3695, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f34, 3702, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f35, 3709, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f36, 3716, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f37, 3723, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f38, 3730, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f39, 3735, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f3a, 3740, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f3b, 3747, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f3c, 3754, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f3d, 3761, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f3e, 3768, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f3f, 3775, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f40, 3782, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f41, 3787, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f42, 3792, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f43, 3799, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f44, 3806, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f45, 3813, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f48, 3820, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f49, 3825, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f4a, 3830, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f4b, 3837, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f4c, 3844, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f4d, 3851, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f50, 3858, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f51, 3863, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f52, 3868, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f53, 3875, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f54, 3882, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f55, 3889, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f56, 3896, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f57, 3903, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f59, 3910, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f5b, 3915, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f5d, 3922, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f5f, 3929, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f60, 3936, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f61, 3941, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f62, 3946, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f63, 3953, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f64, 3960, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f65, 3967, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f66, 3974, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f67, 3981, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f68, 3988, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f69, 3993, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f6a, 3998, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f6b, 4005, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f6c, 4012, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f6d, 4019, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f6e, 4026, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f6f, 4033, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f70, 4040, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f71, 1316, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f72, 4045, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f73, 1321, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f74, 4050, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f75, 1326, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f76, 4055, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f77, 1331, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f78, 4060, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f79, 1353, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f7a, 4065, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f7b, 1358, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f7c, 4070, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f7d, 1363, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f80, 4075, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f81, 4082, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f82, 4089, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f83, 4098, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f84, 4107, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f85, 4116, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f86, 4125, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f87, 4134, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f88, 4143, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f89, 4150, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f8a, 4157, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f8b, 4166, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f8c, 4175, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f8d, 4184, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f8e, 4193, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f8f, 4202, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f90, 4211, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f91, 4218, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f92, 4225, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f93, 4234, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f94, 4243, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f95, 4252, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f96, 4261, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f97, 4270, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f98, 4279, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f99, 4286, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f9a, 4293, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f9b, 4302, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f9c, 4311, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f9d, 4320, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f9e, 4329, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1f9f, 4338, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa0, 4347, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa1, 4354, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa2, 4361, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa3, 4370, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa4, 4379, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa5, 4388, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa6, 4397, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa7, 4406, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa8, 4415, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fa9, 4422, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1faa, 4429, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fab, 4438, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fac, 4447, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fad, 4456, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fae, 4465, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1faf, 4474, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fb0, 4483, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fb1, 4488, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fb2, 4493, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fb3, 4500, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fb4, 4505, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fb6, 4512, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fb7, 4517, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fb8, 4524, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fb9, 4529, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fba, 4534, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fbb, 1261, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fbc, 4539, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fbd, G_UNICODE_NOT_PRESENT_OFFSET, 4544 },
+  { 0x1fbe, 4548, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fbf, G_UNICODE_NOT_PRESENT_OFFSET, 4544 },
+  { 0x1fc0, G_UNICODE_NOT_PRESENT_OFFSET, 4551 },
+  { 0x1fc1, 4555, 4560 },
+  { 0x1fc2, 4566, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fc3, 4573, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fc4, 4578, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fc6, 4585, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fc7, 4590, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fc8, 4597, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fc9, 1269, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fca, 4602, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fcb, 1274, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fcc, 4607, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fcd, 4612, 4618 },
+  { 0x1fce, 4624, 4630 },
+  { 0x1fcf, 4636, 4642 },
+  { 0x1fd0, 4648, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fd1, 4653, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fd2, 4658, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fd3, 1299, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fd6, 4665, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fd7, 4670, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fd8, 4677, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fd9, 4682, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fda, 4687, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fdb, 1279, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fdd, 4692, 4698 },
+  { 0x1fde, 4704, 4710 },
+  { 0x1fdf, 4716, 4722 },
+  { 0x1fe0, 4728, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fe1, 4733, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fe2, 4738, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fe3, 1336, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fe4, 4745, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fe5, 4750, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fe6, 4755, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fe7, 4760, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fe8, 4767, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fe9, 4772, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fea, 4777, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1feb, 1289, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fec, 4782, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1fed, 4787, 4792 },
+  { 0x1fee, 1250, 1255 },
+  { 0x1fef, 4798, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ff2, 4800, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ff3, 4807, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ff4, 4812, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ff6, 4819, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ff7, 4824, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ff8, 4831, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ff9, 1284, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ffa, 4836, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ffb, 1294, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ffc, 4841, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1ffd, 4846, 16 },
+  { 0x1ffe, G_UNICODE_NOT_PRESENT_OFFSET, 4849 },
+  { 0x2000, 4853, 0 },
+  { 0x2001, 4857, 0 },
+  { 0x2002, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2003, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2004, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2005, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2006, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2007, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2008, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2009, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x200a, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2011, G_UNICODE_NOT_PRESENT_OFFSET, 4861 },
+  { 0x2017, G_UNICODE_NOT_PRESENT_OFFSET, 4865 },
+  { 0x2024, G_UNICODE_NOT_PRESENT_OFFSET, 4869 },
+  { 0x2025, G_UNICODE_NOT_PRESENT_OFFSET, 4871 },
+  { 0x2026, G_UNICODE_NOT_PRESENT_OFFSET, 4874 },
+  { 0x202f, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2033, G_UNICODE_NOT_PRESENT_OFFSET, 4878 },
+  { 0x2034, G_UNICODE_NOT_PRESENT_OFFSET, 4885 },
+  { 0x2036, G_UNICODE_NOT_PRESENT_OFFSET, 4895 },
+  { 0x2037, G_UNICODE_NOT_PRESENT_OFFSET, 4902 },
+  { 0x203c, G_UNICODE_NOT_PRESENT_OFFSET, 4912 },
+  { 0x203e, G_UNICODE_NOT_PRESENT_OFFSET, 4915 },
+  { 0x2047, G_UNICODE_NOT_PRESENT_OFFSET, 4919 },
+  { 0x2048, G_UNICODE_NOT_PRESENT_OFFSET, 4922 },
+  { 0x2049, G_UNICODE_NOT_PRESENT_OFFSET, 4925 },
+  { 0x2057, G_UNICODE_NOT_PRESENT_OFFSET, 4928 },
+  { 0x205f, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x2070, G_UNICODE_NOT_PRESENT_OFFSET, 4941 },
+  { 0x2071, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x2074, G_UNICODE_NOT_PRESENT_OFFSET, 4945 },
+  { 0x2075, G_UNICODE_NOT_PRESENT_OFFSET, 4947 },
+  { 0x2076, G_UNICODE_NOT_PRESENT_OFFSET, 4949 },
+  { 0x2077, G_UNICODE_NOT_PRESENT_OFFSET, 4951 },
+  { 0x2078, G_UNICODE_NOT_PRESENT_OFFSET, 4953 },
+  { 0x2079, G_UNICODE_NOT_PRESENT_OFFSET, 4955 },
+  { 0x207a, G_UNICODE_NOT_PRESENT_OFFSET, 4957 },
+  { 0x207b, G_UNICODE_NOT_PRESENT_OFFSET, 4959 },
+  { 0x207c, G_UNICODE_NOT_PRESENT_OFFSET, 4963 },
+  { 0x207d, G_UNICODE_NOT_PRESENT_OFFSET, 4965 },
+  { 0x207e, G_UNICODE_NOT_PRESENT_OFFSET, 4967 },
+  { 0x207f, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x2080, G_UNICODE_NOT_PRESENT_OFFSET, 4941 },
+  { 0x2081, G_UNICODE_NOT_PRESENT_OFFSET, 27 },
+  { 0x2082, G_UNICODE_NOT_PRESENT_OFFSET, 12 },
+  { 0x2083, G_UNICODE_NOT_PRESENT_OFFSET, 14 },
+  { 0x2084, G_UNICODE_NOT_PRESENT_OFFSET, 4945 },
+  { 0x2085, G_UNICODE_NOT_PRESENT_OFFSET, 4947 },
+  { 0x2086, G_UNICODE_NOT_PRESENT_OFFSET, 4949 },
+  { 0x2087, G_UNICODE_NOT_PRESENT_OFFSET, 4951 },
+  { 0x2088, G_UNICODE_NOT_PRESENT_OFFSET, 4953 },
+  { 0x2089, G_UNICODE_NOT_PRESENT_OFFSET, 4955 },
+  { 0x208a, G_UNICODE_NOT_PRESENT_OFFSET, 4957 },
+  { 0x208b, G_UNICODE_NOT_PRESENT_OFFSET, 4959 },
+  { 0x208c, G_UNICODE_NOT_PRESENT_OFFSET, 4963 },
+  { 0x208d, G_UNICODE_NOT_PRESENT_OFFSET, 4965 },
+  { 0x208e, G_UNICODE_NOT_PRESENT_OFFSET, 4967 },
+  { 0x20a8, G_UNICODE_NOT_PRESENT_OFFSET, 4971 },
+  { 0x2100, G_UNICODE_NOT_PRESENT_OFFSET, 4974 },
+  { 0x2101, G_UNICODE_NOT_PRESENT_OFFSET, 4978 },
+  { 0x2102, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x2103, G_UNICODE_NOT_PRESENT_OFFSET, 4984 },
+  { 0x2105, G_UNICODE_NOT_PRESENT_OFFSET, 4988 },
+  { 0x2106, G_UNICODE_NOT_PRESENT_OFFSET, 4992 },
+  { 0x2107, G_UNICODE_NOT_PRESENT_OFFSET, 4996 },
+  { 0x2109, G_UNICODE_NOT_PRESENT_OFFSET, 4999 },
+  { 0x210a, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x210b, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x210c, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x210d, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x210e, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x210f, G_UNICODE_NOT_PRESENT_OFFSET, 5007 },
+  { 0x2110, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x2111, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x2112, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x2113, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x2115, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x2116, G_UNICODE_NOT_PRESENT_OFFSET, 5016 },
+  { 0x2119, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x211a, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x211b, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x211c, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x211d, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x2120, G_UNICODE_NOT_PRESENT_OFFSET, 5025 },
+  { 0x2121, G_UNICODE_NOT_PRESENT_OFFSET, 5028 },
+  { 0x2122, G_UNICODE_NOT_PRESENT_OFFSET, 5032 },
+  { 0x2124, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x2126, 5037, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2128, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x212a, 5040, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x212b, 69, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x212c, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x212d, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x212f, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x2130, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x2131, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x2133, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x2134, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x2135, G_UNICODE_NOT_PRESENT_OFFSET, 5052 },
+  { 0x2136, G_UNICODE_NOT_PRESENT_OFFSET, 5055 },
+  { 0x2137, G_UNICODE_NOT_PRESENT_OFFSET, 5058 },
+  { 0x2138, G_UNICODE_NOT_PRESENT_OFFSET, 5061 },
+  { 0x2139, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x213d, G_UNICODE_NOT_PRESENT_OFFSET, 5064 },
+  { 0x213e, G_UNICODE_NOT_PRESENT_OFFSET, 5067 },
+  { 0x213f, G_UNICODE_NOT_PRESENT_OFFSET, 5070 },
+  { 0x2140, G_UNICODE_NOT_PRESENT_OFFSET, 5073 },
+  { 0x2145, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x2146, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x2147, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x2148, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x2149, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x2153, G_UNICODE_NOT_PRESENT_OFFSET, 5081 },
+  { 0x2154, G_UNICODE_NOT_PRESENT_OFFSET, 5087 },
+  { 0x2155, G_UNICODE_NOT_PRESENT_OFFSET, 5093 },
+  { 0x2156, G_UNICODE_NOT_PRESENT_OFFSET, 5099 },
+  { 0x2157, G_UNICODE_NOT_PRESENT_OFFSET, 5105 },
+  { 0x2158, G_UNICODE_NOT_PRESENT_OFFSET, 5111 },
+  { 0x2159, G_UNICODE_NOT_PRESENT_OFFSET, 5117 },
+  { 0x215a, G_UNICODE_NOT_PRESENT_OFFSET, 5123 },
+  { 0x215b, G_UNICODE_NOT_PRESENT_OFFSET, 5129 },
+  { 0x215c, G_UNICODE_NOT_PRESENT_OFFSET, 5135 },
+  { 0x215d, G_UNICODE_NOT_PRESENT_OFFSET, 5141 },
+  { 0x215e, G_UNICODE_NOT_PRESENT_OFFSET, 5147 },
+  { 0x215f, G_UNICODE_NOT_PRESENT_OFFSET, 5153 },
+  { 0x2160, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x2161, G_UNICODE_NOT_PRESENT_OFFSET, 5158 },
+  { 0x2162, G_UNICODE_NOT_PRESENT_OFFSET, 5161 },
+  { 0x2163, G_UNICODE_NOT_PRESENT_OFFSET, 5165 },
+  { 0x2164, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x2165, G_UNICODE_NOT_PRESENT_OFFSET, 5170 },
+  { 0x2166, G_UNICODE_NOT_PRESENT_OFFSET, 5173 },
+  { 0x2167, G_UNICODE_NOT_PRESENT_OFFSET, 5177 },
+  { 0x2168, G_UNICODE_NOT_PRESENT_OFFSET, 5182 },
+  { 0x2169, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x216a, G_UNICODE_NOT_PRESENT_OFFSET, 5187 },
+  { 0x216b, G_UNICODE_NOT_PRESENT_OFFSET, 5190 },
+  { 0x216c, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x216d, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x216e, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x216f, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x2170, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x2171, G_UNICODE_NOT_PRESENT_OFFSET, 5194 },
+  { 0x2172, G_UNICODE_NOT_PRESENT_OFFSET, 5197 },
+  { 0x2173, G_UNICODE_NOT_PRESENT_OFFSET, 5201 },
+  { 0x2174, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x2175, G_UNICODE_NOT_PRESENT_OFFSET, 5206 },
+  { 0x2176, G_UNICODE_NOT_PRESENT_OFFSET, 5209 },
+  { 0x2177, G_UNICODE_NOT_PRESENT_OFFSET, 5213 },
+  { 0x2178, G_UNICODE_NOT_PRESENT_OFFSET, 5218 },
+  { 0x2179, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x217a, G_UNICODE_NOT_PRESENT_OFFSET, 5221 },
+  { 0x217b, G_UNICODE_NOT_PRESENT_OFFSET, 5224 },
+  { 0x217c, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x217d, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x217e, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x217f, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x219a, 5232, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x219b, 5238, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x21ae, 5244, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x21cd, 5250, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x21ce, 5256, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x21cf, 5262, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2204, 5268, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2209, 5274, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x220c, 5280, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2224, 5286, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2226, 5292, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x222c, G_UNICODE_NOT_PRESENT_OFFSET, 5298 },
+  { 0x222d, G_UNICODE_NOT_PRESENT_OFFSET, 5305 },
+  { 0x222f, G_UNICODE_NOT_PRESENT_OFFSET, 5315 },
+  { 0x2230, G_UNICODE_NOT_PRESENT_OFFSET, 5322 },
+  { 0x2241, 5332, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2244, 5338, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2247, 5344, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2249, 5350, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2260, 5356, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2262, 5360, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x226d, 5366, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x226e, 5372, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x226f, 5376, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2270, 5380, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2271, 5386, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2274, 5392, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2275, 5398, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2278, 5404, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2279, 5410, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2280, 5416, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2281, 5422, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2284, 5428, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2285, 5434, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2288, 5440, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2289, 5446, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22ac, 5452, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22ad, 5458, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22ae, 5464, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22af, 5470, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22e0, 5476, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22e1, 5482, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22e2, 5488, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22e3, 5494, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22ea, 5500, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22eb, 5506, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22ec, 5512, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x22ed, 5518, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2329, 5524, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x232a, 5528, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2460, G_UNICODE_NOT_PRESENT_OFFSET, 27 },
+  { 0x2461, G_UNICODE_NOT_PRESENT_OFFSET, 12 },
+  { 0x2462, G_UNICODE_NOT_PRESENT_OFFSET, 14 },
+  { 0x2463, G_UNICODE_NOT_PRESENT_OFFSET, 4945 },
+  { 0x2464, G_UNICODE_NOT_PRESENT_OFFSET, 4947 },
+  { 0x2465, G_UNICODE_NOT_PRESENT_OFFSET, 4949 },
+  { 0x2466, G_UNICODE_NOT_PRESENT_OFFSET, 4951 },
+  { 0x2467, G_UNICODE_NOT_PRESENT_OFFSET, 4953 },
+  { 0x2468, G_UNICODE_NOT_PRESENT_OFFSET, 4955 },
+  { 0x2469, G_UNICODE_NOT_PRESENT_OFFSET, 5532 },
+  { 0x246a, G_UNICODE_NOT_PRESENT_OFFSET, 5535 },
+  { 0x246b, G_UNICODE_NOT_PRESENT_OFFSET, 5538 },
+  { 0x246c, G_UNICODE_NOT_PRESENT_OFFSET, 5541 },
+  { 0x246d, G_UNICODE_NOT_PRESENT_OFFSET, 5544 },
+  { 0x246e, G_UNICODE_NOT_PRESENT_OFFSET, 5547 },
+  { 0x246f, G_UNICODE_NOT_PRESENT_OFFSET, 5550 },
+  { 0x2470, G_UNICODE_NOT_PRESENT_OFFSET, 5553 },
+  { 0x2471, G_UNICODE_NOT_PRESENT_OFFSET, 5556 },
+  { 0x2472, G_UNICODE_NOT_PRESENT_OFFSET, 5559 },
+  { 0x2473, G_UNICODE_NOT_PRESENT_OFFSET, 5562 },
+  { 0x2474, G_UNICODE_NOT_PRESENT_OFFSET, 5565 },
+  { 0x2475, G_UNICODE_NOT_PRESENT_OFFSET, 5569 },
+  { 0x2476, G_UNICODE_NOT_PRESENT_OFFSET, 5573 },
+  { 0x2477, G_UNICODE_NOT_PRESENT_OFFSET, 5577 },
+  { 0x2478, G_UNICODE_NOT_PRESENT_OFFSET, 5581 },
+  { 0x2479, G_UNICODE_NOT_PRESENT_OFFSET, 5585 },
+  { 0x247a, G_UNICODE_NOT_PRESENT_OFFSET, 5589 },
+  { 0x247b, G_UNICODE_NOT_PRESENT_OFFSET, 5593 },
+  { 0x247c, G_UNICODE_NOT_PRESENT_OFFSET, 5597 },
+  { 0x247d, G_UNICODE_NOT_PRESENT_OFFSET, 5601 },
+  { 0x247e, G_UNICODE_NOT_PRESENT_OFFSET, 5606 },
+  { 0x247f, G_UNICODE_NOT_PRESENT_OFFSET, 5611 },
+  { 0x2480, G_UNICODE_NOT_PRESENT_OFFSET, 5616 },
+  { 0x2481, G_UNICODE_NOT_PRESENT_OFFSET, 5621 },
+  { 0x2482, G_UNICODE_NOT_PRESENT_OFFSET, 5626 },
+  { 0x2483, G_UNICODE_NOT_PRESENT_OFFSET, 5631 },
+  { 0x2484, G_UNICODE_NOT_PRESENT_OFFSET, 5636 },
+  { 0x2485, G_UNICODE_NOT_PRESENT_OFFSET, 5641 },
+  { 0x2486, G_UNICODE_NOT_PRESENT_OFFSET, 5646 },
+  { 0x2487, G_UNICODE_NOT_PRESENT_OFFSET, 5651 },
+  { 0x2488, G_UNICODE_NOT_PRESENT_OFFSET, 5656 },
+  { 0x2489, G_UNICODE_NOT_PRESENT_OFFSET, 5659 },
+  { 0x248a, G_UNICODE_NOT_PRESENT_OFFSET, 5662 },
+  { 0x248b, G_UNICODE_NOT_PRESENT_OFFSET, 5665 },
+  { 0x248c, G_UNICODE_NOT_PRESENT_OFFSET, 5668 },
+  { 0x248d, G_UNICODE_NOT_PRESENT_OFFSET, 5671 },
+  { 0x248e, G_UNICODE_NOT_PRESENT_OFFSET, 5674 },
+  { 0x248f, G_UNICODE_NOT_PRESENT_OFFSET, 5677 },
+  { 0x2490, G_UNICODE_NOT_PRESENT_OFFSET, 5680 },
+  { 0x2491, G_UNICODE_NOT_PRESENT_OFFSET, 5683 },
+  { 0x2492, G_UNICODE_NOT_PRESENT_OFFSET, 5687 },
+  { 0x2493, G_UNICODE_NOT_PRESENT_OFFSET, 5691 },
+  { 0x2494, G_UNICODE_NOT_PRESENT_OFFSET, 5695 },
+  { 0x2495, G_UNICODE_NOT_PRESENT_OFFSET, 5699 },
+  { 0x2496, G_UNICODE_NOT_PRESENT_OFFSET, 5703 },
+  { 0x2497, G_UNICODE_NOT_PRESENT_OFFSET, 5707 },
+  { 0x2498, G_UNICODE_NOT_PRESENT_OFFSET, 5711 },
+  { 0x2499, G_UNICODE_NOT_PRESENT_OFFSET, 5715 },
+  { 0x249a, G_UNICODE_NOT_PRESENT_OFFSET, 5719 },
+  { 0x249b, G_UNICODE_NOT_PRESENT_OFFSET, 5723 },
+  { 0x249c, G_UNICODE_NOT_PRESENT_OFFSET, 5727 },
+  { 0x249d, G_UNICODE_NOT_PRESENT_OFFSET, 5731 },
+  { 0x249e, G_UNICODE_NOT_PRESENT_OFFSET, 5735 },
+  { 0x249f, G_UNICODE_NOT_PRESENT_OFFSET, 5739 },
+  { 0x24a0, G_UNICODE_NOT_PRESENT_OFFSET, 5743 },
+  { 0x24a1, G_UNICODE_NOT_PRESENT_OFFSET, 5747 },
+  { 0x24a2, G_UNICODE_NOT_PRESENT_OFFSET, 5751 },
+  { 0x24a3, G_UNICODE_NOT_PRESENT_OFFSET, 5755 },
+  { 0x24a4, G_UNICODE_NOT_PRESENT_OFFSET, 5759 },
+  { 0x24a5, G_UNICODE_NOT_PRESENT_OFFSET, 5763 },
+  { 0x24a6, G_UNICODE_NOT_PRESENT_OFFSET, 5767 },
+  { 0x24a7, G_UNICODE_NOT_PRESENT_OFFSET, 5771 },
+  { 0x24a8, G_UNICODE_NOT_PRESENT_OFFSET, 5775 },
+  { 0x24a9, G_UNICODE_NOT_PRESENT_OFFSET, 5779 },
+  { 0x24aa, G_UNICODE_NOT_PRESENT_OFFSET, 5783 },
+  { 0x24ab, G_UNICODE_NOT_PRESENT_OFFSET, 5787 },
+  { 0x24ac, G_UNICODE_NOT_PRESENT_OFFSET, 5791 },
+  { 0x24ad, G_UNICODE_NOT_PRESENT_OFFSET, 5795 },
+  { 0x24ae, G_UNICODE_NOT_PRESENT_OFFSET, 5799 },
+  { 0x24af, G_UNICODE_NOT_PRESENT_OFFSET, 5803 },
+  { 0x24b0, G_UNICODE_NOT_PRESENT_OFFSET, 5807 },
+  { 0x24b1, G_UNICODE_NOT_PRESENT_OFFSET, 5811 },
+  { 0x24b2, G_UNICODE_NOT_PRESENT_OFFSET, 5815 },
+  { 0x24b3, G_UNICODE_NOT_PRESENT_OFFSET, 5819 },
+  { 0x24b4, G_UNICODE_NOT_PRESENT_OFFSET, 5823 },
+  { 0x24b5, G_UNICODE_NOT_PRESENT_OFFSET, 5827 },
+  { 0x24b6, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x24b7, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x24b8, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x24b9, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x24ba, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x24bb, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x24bc, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x24bd, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x24be, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x24bf, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x24c0, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x24c1, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x24c2, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x24c3, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x24c4, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x24c5, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x24c6, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x24c7, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x24c8, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x24c9, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x24ca, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x24cb, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x24cc, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x24cd, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x24ce, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x24cf, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x24d0, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x24d1, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x24d2, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x24d3, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x24d4, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x24d5, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x24d6, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x24d7, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x24d8, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x24d9, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x24da, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x24db, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x24dc, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x24dd, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x24de, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x24df, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x24e0, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x24e1, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x24e2, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x24e3, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x24e4, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x24e5, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x24e6, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x24e7, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x24e8, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x24e9, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x24ea, G_UNICODE_NOT_PRESENT_OFFSET, 4941 },
+  { 0x2a0c, G_UNICODE_NOT_PRESENT_OFFSET, 5865 },
+  { 0x2a74, G_UNICODE_NOT_PRESENT_OFFSET, 5878 },
+  { 0x2a75, G_UNICODE_NOT_PRESENT_OFFSET, 5882 },
+  { 0x2a76, G_UNICODE_NOT_PRESENT_OFFSET, 5885 },
+  { 0x2adc, 5889, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2e9f, G_UNICODE_NOT_PRESENT_OFFSET, 5895 },
+  { 0x2ef3, G_UNICODE_NOT_PRESENT_OFFSET, 5899 },
+  { 0x2f00, G_UNICODE_NOT_PRESENT_OFFSET, 5903 },
+  { 0x2f01, G_UNICODE_NOT_PRESENT_OFFSET, 5907 },
+  { 0x2f02, G_UNICODE_NOT_PRESENT_OFFSET, 5911 },
+  { 0x2f03, G_UNICODE_NOT_PRESENT_OFFSET, 5915 },
+  { 0x2f04, G_UNICODE_NOT_PRESENT_OFFSET, 5919 },
+  { 0x2f05, G_UNICODE_NOT_PRESENT_OFFSET, 5923 },
+  { 0x2f06, G_UNICODE_NOT_PRESENT_OFFSET, 5927 },
+  { 0x2f07, G_UNICODE_NOT_PRESENT_OFFSET, 5931 },
+  { 0x2f08, G_UNICODE_NOT_PRESENT_OFFSET, 5935 },
+  { 0x2f09, G_UNICODE_NOT_PRESENT_OFFSET, 5939 },
+  { 0x2f0a, G_UNICODE_NOT_PRESENT_OFFSET, 5943 },
+  { 0x2f0b, G_UNICODE_NOT_PRESENT_OFFSET, 5947 },
+  { 0x2f0c, G_UNICODE_NOT_PRESENT_OFFSET, 5951 },
+  { 0x2f0d, G_UNICODE_NOT_PRESENT_OFFSET, 5955 },
+  { 0x2f0e, G_UNICODE_NOT_PRESENT_OFFSET, 5959 },
+  { 0x2f0f, G_UNICODE_NOT_PRESENT_OFFSET, 5963 },
+  { 0x2f10, G_UNICODE_NOT_PRESENT_OFFSET, 5967 },
+  { 0x2f11, G_UNICODE_NOT_PRESENT_OFFSET, 5971 },
+  { 0x2f12, G_UNICODE_NOT_PRESENT_OFFSET, 5975 },
+  { 0x2f13, G_UNICODE_NOT_PRESENT_OFFSET, 5979 },
+  { 0x2f14, G_UNICODE_NOT_PRESENT_OFFSET, 5983 },
+  { 0x2f15, G_UNICODE_NOT_PRESENT_OFFSET, 5987 },
+  { 0x2f16, G_UNICODE_NOT_PRESENT_OFFSET, 5991 },
+  { 0x2f17, G_UNICODE_NOT_PRESENT_OFFSET, 5995 },
+  { 0x2f18, G_UNICODE_NOT_PRESENT_OFFSET, 5999 },
+  { 0x2f19, G_UNICODE_NOT_PRESENT_OFFSET, 6003 },
+  { 0x2f1a, G_UNICODE_NOT_PRESENT_OFFSET, 6007 },
+  { 0x2f1b, G_UNICODE_NOT_PRESENT_OFFSET, 6011 },
+  { 0x2f1c, G_UNICODE_NOT_PRESENT_OFFSET, 6015 },
+  { 0x2f1d, G_UNICODE_NOT_PRESENT_OFFSET, 6019 },
+  { 0x2f1e, G_UNICODE_NOT_PRESENT_OFFSET, 6023 },
+  { 0x2f1f, G_UNICODE_NOT_PRESENT_OFFSET, 6027 },
+  { 0x2f20, G_UNICODE_NOT_PRESENT_OFFSET, 6031 },
+  { 0x2f21, G_UNICODE_NOT_PRESENT_OFFSET, 6035 },
+  { 0x2f22, G_UNICODE_NOT_PRESENT_OFFSET, 6039 },
+  { 0x2f23, G_UNICODE_NOT_PRESENT_OFFSET, 6043 },
+  { 0x2f24, G_UNICODE_NOT_PRESENT_OFFSET, 6047 },
+  { 0x2f25, G_UNICODE_NOT_PRESENT_OFFSET, 6051 },
+  { 0x2f26, G_UNICODE_NOT_PRESENT_OFFSET, 6055 },
+  { 0x2f27, G_UNICODE_NOT_PRESENT_OFFSET, 6059 },
+  { 0x2f28, G_UNICODE_NOT_PRESENT_OFFSET, 6063 },
+  { 0x2f29, G_UNICODE_NOT_PRESENT_OFFSET, 6067 },
+  { 0x2f2a, G_UNICODE_NOT_PRESENT_OFFSET, 6071 },
+  { 0x2f2b, G_UNICODE_NOT_PRESENT_OFFSET, 6075 },
+  { 0x2f2c, G_UNICODE_NOT_PRESENT_OFFSET, 6079 },
+  { 0x2f2d, G_UNICODE_NOT_PRESENT_OFFSET, 6083 },
+  { 0x2f2e, G_UNICODE_NOT_PRESENT_OFFSET, 6087 },
+  { 0x2f2f, G_UNICODE_NOT_PRESENT_OFFSET, 6091 },
+  { 0x2f30, G_UNICODE_NOT_PRESENT_OFFSET, 6095 },
+  { 0x2f31, G_UNICODE_NOT_PRESENT_OFFSET, 6099 },
+  { 0x2f32, G_UNICODE_NOT_PRESENT_OFFSET, 6103 },
+  { 0x2f33, G_UNICODE_NOT_PRESENT_OFFSET, 6107 },
+  { 0x2f34, G_UNICODE_NOT_PRESENT_OFFSET, 6111 },
+  { 0x2f35, G_UNICODE_NOT_PRESENT_OFFSET, 6115 },
+  { 0x2f36, G_UNICODE_NOT_PRESENT_OFFSET, 6119 },
+  { 0x2f37, G_UNICODE_NOT_PRESENT_OFFSET, 6123 },
+  { 0x2f38, G_UNICODE_NOT_PRESENT_OFFSET, 6127 },
+  { 0x2f39, G_UNICODE_NOT_PRESENT_OFFSET, 6131 },
+  { 0x2f3a, G_UNICODE_NOT_PRESENT_OFFSET, 6135 },
+  { 0x2f3b, G_UNICODE_NOT_PRESENT_OFFSET, 6139 },
+  { 0x2f3c, G_UNICODE_NOT_PRESENT_OFFSET, 6143 },
+  { 0x2f3d, G_UNICODE_NOT_PRESENT_OFFSET, 6147 },
+  { 0x2f3e, G_UNICODE_NOT_PRESENT_OFFSET, 6151 },
+  { 0x2f3f, G_UNICODE_NOT_PRESENT_OFFSET, 6155 },
+  { 0x2f40, G_UNICODE_NOT_PRESENT_OFFSET, 6159 },
+  { 0x2f41, G_UNICODE_NOT_PRESENT_OFFSET, 6163 },
+  { 0x2f42, G_UNICODE_NOT_PRESENT_OFFSET, 6167 },
+  { 0x2f43, G_UNICODE_NOT_PRESENT_OFFSET, 6171 },
+  { 0x2f44, G_UNICODE_NOT_PRESENT_OFFSET, 6175 },
+  { 0x2f45, G_UNICODE_NOT_PRESENT_OFFSET, 6179 },
+  { 0x2f46, G_UNICODE_NOT_PRESENT_OFFSET, 6183 },
+  { 0x2f47, G_UNICODE_NOT_PRESENT_OFFSET, 6187 },
+  { 0x2f48, G_UNICODE_NOT_PRESENT_OFFSET, 6191 },
+  { 0x2f49, G_UNICODE_NOT_PRESENT_OFFSET, 6195 },
+  { 0x2f4a, G_UNICODE_NOT_PRESENT_OFFSET, 6199 },
+  { 0x2f4b, G_UNICODE_NOT_PRESENT_OFFSET, 6203 },
+  { 0x2f4c, G_UNICODE_NOT_PRESENT_OFFSET, 6207 },
+  { 0x2f4d, G_UNICODE_NOT_PRESENT_OFFSET, 6211 },
+  { 0x2f4e, G_UNICODE_NOT_PRESENT_OFFSET, 6215 },
+  { 0x2f4f, G_UNICODE_NOT_PRESENT_OFFSET, 6219 },
+  { 0x2f50, G_UNICODE_NOT_PRESENT_OFFSET, 6223 },
+  { 0x2f51, G_UNICODE_NOT_PRESENT_OFFSET, 6227 },
+  { 0x2f52, G_UNICODE_NOT_PRESENT_OFFSET, 6231 },
+  { 0x2f53, G_UNICODE_NOT_PRESENT_OFFSET, 6235 },
+  { 0x2f54, G_UNICODE_NOT_PRESENT_OFFSET, 6239 },
+  { 0x2f55, G_UNICODE_NOT_PRESENT_OFFSET, 6243 },
+  { 0x2f56, G_UNICODE_NOT_PRESENT_OFFSET, 6247 },
+  { 0x2f57, G_UNICODE_NOT_PRESENT_OFFSET, 6251 },
+  { 0x2f58, G_UNICODE_NOT_PRESENT_OFFSET, 6255 },
+  { 0x2f59, G_UNICODE_NOT_PRESENT_OFFSET, 6259 },
+  { 0x2f5a, G_UNICODE_NOT_PRESENT_OFFSET, 6263 },
+  { 0x2f5b, G_UNICODE_NOT_PRESENT_OFFSET, 6267 },
+  { 0x2f5c, G_UNICODE_NOT_PRESENT_OFFSET, 6271 },
+  { 0x2f5d, G_UNICODE_NOT_PRESENT_OFFSET, 6275 },
+  { 0x2f5e, G_UNICODE_NOT_PRESENT_OFFSET, 6279 },
+  { 0x2f5f, G_UNICODE_NOT_PRESENT_OFFSET, 6283 },
+  { 0x2f60, G_UNICODE_NOT_PRESENT_OFFSET, 6287 },
+  { 0x2f61, G_UNICODE_NOT_PRESENT_OFFSET, 6291 },
+  { 0x2f62, G_UNICODE_NOT_PRESENT_OFFSET, 6295 },
+  { 0x2f63, G_UNICODE_NOT_PRESENT_OFFSET, 6299 },
+  { 0x2f64, G_UNICODE_NOT_PRESENT_OFFSET, 6303 },
+  { 0x2f65, G_UNICODE_NOT_PRESENT_OFFSET, 6307 },
+  { 0x2f66, G_UNICODE_NOT_PRESENT_OFFSET, 6311 },
+  { 0x2f67, G_UNICODE_NOT_PRESENT_OFFSET, 6315 },
+  { 0x2f68, G_UNICODE_NOT_PRESENT_OFFSET, 6319 },
+  { 0x2f69, G_UNICODE_NOT_PRESENT_OFFSET, 6323 },
+  { 0x2f6a, G_UNICODE_NOT_PRESENT_OFFSET, 6327 },
+  { 0x2f6b, G_UNICODE_NOT_PRESENT_OFFSET, 6331 },
+  { 0x2f6c, G_UNICODE_NOT_PRESENT_OFFSET, 6335 },
+  { 0x2f6d, G_UNICODE_NOT_PRESENT_OFFSET, 6339 },
+  { 0x2f6e, G_UNICODE_NOT_PRESENT_OFFSET, 6343 },
+  { 0x2f6f, G_UNICODE_NOT_PRESENT_OFFSET, 6347 },
+  { 0x2f70, G_UNICODE_NOT_PRESENT_OFFSET, 6351 },
+  { 0x2f71, G_UNICODE_NOT_PRESENT_OFFSET, 6355 },
+  { 0x2f72, G_UNICODE_NOT_PRESENT_OFFSET, 6359 },
+  { 0x2f73, G_UNICODE_NOT_PRESENT_OFFSET, 6363 },
+  { 0x2f74, G_UNICODE_NOT_PRESENT_OFFSET, 6367 },
+  { 0x2f75, G_UNICODE_NOT_PRESENT_OFFSET, 6371 },
+  { 0x2f76, G_UNICODE_NOT_PRESENT_OFFSET, 6375 },
+  { 0x2f77, G_UNICODE_NOT_PRESENT_OFFSET, 6379 },
+  { 0x2f78, G_UNICODE_NOT_PRESENT_OFFSET, 6383 },
+  { 0x2f79, G_UNICODE_NOT_PRESENT_OFFSET, 6387 },
+  { 0x2f7a, G_UNICODE_NOT_PRESENT_OFFSET, 6391 },
+  { 0x2f7b, G_UNICODE_NOT_PRESENT_OFFSET, 6395 },
+  { 0x2f7c, G_UNICODE_NOT_PRESENT_OFFSET, 6399 },
+  { 0x2f7d, G_UNICODE_NOT_PRESENT_OFFSET, 6403 },
+  { 0x2f7e, G_UNICODE_NOT_PRESENT_OFFSET, 6407 },
+  { 0x2f7f, G_UNICODE_NOT_PRESENT_OFFSET, 6411 },
+  { 0x2f80, G_UNICODE_NOT_PRESENT_OFFSET, 6415 },
+  { 0x2f81, G_UNICODE_NOT_PRESENT_OFFSET, 6419 },
+  { 0x2f82, G_UNICODE_NOT_PRESENT_OFFSET, 6423 },
+  { 0x2f83, G_UNICODE_NOT_PRESENT_OFFSET, 6427 },
+  { 0x2f84, G_UNICODE_NOT_PRESENT_OFFSET, 6431 },
+  { 0x2f85, G_UNICODE_NOT_PRESENT_OFFSET, 6435 },
+  { 0x2f86, G_UNICODE_NOT_PRESENT_OFFSET, 6439 },
+  { 0x2f87, G_UNICODE_NOT_PRESENT_OFFSET, 6443 },
+  { 0x2f88, G_UNICODE_NOT_PRESENT_OFFSET, 6447 },
+  { 0x2f89, G_UNICODE_NOT_PRESENT_OFFSET, 6451 },
+  { 0x2f8a, G_UNICODE_NOT_PRESENT_OFFSET, 6455 },
+  { 0x2f8b, G_UNICODE_NOT_PRESENT_OFFSET, 6459 },
+  { 0x2f8c, G_UNICODE_NOT_PRESENT_OFFSET, 6463 },
+  { 0x2f8d, G_UNICODE_NOT_PRESENT_OFFSET, 6467 },
+  { 0x2f8e, G_UNICODE_NOT_PRESENT_OFFSET, 6471 },
+  { 0x2f8f, G_UNICODE_NOT_PRESENT_OFFSET, 6475 },
+  { 0x2f90, G_UNICODE_NOT_PRESENT_OFFSET, 6479 },
+  { 0x2f91, G_UNICODE_NOT_PRESENT_OFFSET, 6483 },
+  { 0x2f92, G_UNICODE_NOT_PRESENT_OFFSET, 6487 },
+  { 0x2f93, G_UNICODE_NOT_PRESENT_OFFSET, 6491 },
+  { 0x2f94, G_UNICODE_NOT_PRESENT_OFFSET, 6495 },
+  { 0x2f95, G_UNICODE_NOT_PRESENT_OFFSET, 6499 },
+  { 0x2f96, G_UNICODE_NOT_PRESENT_OFFSET, 6503 },
+  { 0x2f97, G_UNICODE_NOT_PRESENT_OFFSET, 6507 },
+  { 0x2f98, G_UNICODE_NOT_PRESENT_OFFSET, 6511 },
+  { 0x2f99, G_UNICODE_NOT_PRESENT_OFFSET, 6515 },
+  { 0x2f9a, G_UNICODE_NOT_PRESENT_OFFSET, 6519 },
+  { 0x2f9b, G_UNICODE_NOT_PRESENT_OFFSET, 6523 },
+  { 0x2f9c, G_UNICODE_NOT_PRESENT_OFFSET, 6527 },
+  { 0x2f9d, G_UNICODE_NOT_PRESENT_OFFSET, 6531 },
+  { 0x2f9e, G_UNICODE_NOT_PRESENT_OFFSET, 6535 },
+  { 0x2f9f, G_UNICODE_NOT_PRESENT_OFFSET, 6539 },
+  { 0x2fa0, G_UNICODE_NOT_PRESENT_OFFSET, 6543 },
+  { 0x2fa1, G_UNICODE_NOT_PRESENT_OFFSET, 6547 },
+  { 0x2fa2, G_UNICODE_NOT_PRESENT_OFFSET, 6551 },
+  { 0x2fa3, G_UNICODE_NOT_PRESENT_OFFSET, 6555 },
+  { 0x2fa4, G_UNICODE_NOT_PRESENT_OFFSET, 6559 },
+  { 0x2fa5, G_UNICODE_NOT_PRESENT_OFFSET, 6563 },
+  { 0x2fa6, G_UNICODE_NOT_PRESENT_OFFSET, 6567 },
+  { 0x2fa7, G_UNICODE_NOT_PRESENT_OFFSET, 6571 },
+  { 0x2fa8, G_UNICODE_NOT_PRESENT_OFFSET, 6575 },
+  { 0x2fa9, G_UNICODE_NOT_PRESENT_OFFSET, 6579 },
+  { 0x2faa, G_UNICODE_NOT_PRESENT_OFFSET, 6583 },
+  { 0x2fab, G_UNICODE_NOT_PRESENT_OFFSET, 6587 },
+  { 0x2fac, G_UNICODE_NOT_PRESENT_OFFSET, 6591 },
+  { 0x2fad, G_UNICODE_NOT_PRESENT_OFFSET, 6595 },
+  { 0x2fae, G_UNICODE_NOT_PRESENT_OFFSET, 6599 },
+  { 0x2faf, G_UNICODE_NOT_PRESENT_OFFSET, 6603 },
+  { 0x2fb0, G_UNICODE_NOT_PRESENT_OFFSET, 6607 },
+  { 0x2fb1, G_UNICODE_NOT_PRESENT_OFFSET, 6611 },
+  { 0x2fb2, G_UNICODE_NOT_PRESENT_OFFSET, 6615 },
+  { 0x2fb3, G_UNICODE_NOT_PRESENT_OFFSET, 6619 },
+  { 0x2fb4, G_UNICODE_NOT_PRESENT_OFFSET, 6623 },
+  { 0x2fb5, G_UNICODE_NOT_PRESENT_OFFSET, 6627 },
+  { 0x2fb6, G_UNICODE_NOT_PRESENT_OFFSET, 6631 },
+  { 0x2fb7, G_UNICODE_NOT_PRESENT_OFFSET, 6635 },
+  { 0x2fb8, G_UNICODE_NOT_PRESENT_OFFSET, 6639 },
+  { 0x2fb9, G_UNICODE_NOT_PRESENT_OFFSET, 6643 },
+  { 0x2fba, G_UNICODE_NOT_PRESENT_OFFSET, 6647 },
+  { 0x2fbb, G_UNICODE_NOT_PRESENT_OFFSET, 6651 },
+  { 0x2fbc, G_UNICODE_NOT_PRESENT_OFFSET, 6655 },
+  { 0x2fbd, G_UNICODE_NOT_PRESENT_OFFSET, 6659 },
+  { 0x2fbe, G_UNICODE_NOT_PRESENT_OFFSET, 6663 },
+  { 0x2fbf, G_UNICODE_NOT_PRESENT_OFFSET, 6667 },
+  { 0x2fc0, G_UNICODE_NOT_PRESENT_OFFSET, 6671 },
+  { 0x2fc1, G_UNICODE_NOT_PRESENT_OFFSET, 6675 },
+  { 0x2fc2, G_UNICODE_NOT_PRESENT_OFFSET, 6679 },
+  { 0x2fc3, G_UNICODE_NOT_PRESENT_OFFSET, 6683 },
+  { 0x2fc4, G_UNICODE_NOT_PRESENT_OFFSET, 6687 },
+  { 0x2fc5, G_UNICODE_NOT_PRESENT_OFFSET, 6691 },
+  { 0x2fc6, G_UNICODE_NOT_PRESENT_OFFSET, 6695 },
+  { 0x2fc7, G_UNICODE_NOT_PRESENT_OFFSET, 6699 },
+  { 0x2fc8, G_UNICODE_NOT_PRESENT_OFFSET, 6703 },
+  { 0x2fc9, G_UNICODE_NOT_PRESENT_OFFSET, 6707 },
+  { 0x2fca, G_UNICODE_NOT_PRESENT_OFFSET, 6711 },
+  { 0x2fcb, G_UNICODE_NOT_PRESENT_OFFSET, 6715 },
+  { 0x2fcc, G_UNICODE_NOT_PRESENT_OFFSET, 6719 },
+  { 0x2fcd, G_UNICODE_NOT_PRESENT_OFFSET, 6723 },
+  { 0x2fce, G_UNICODE_NOT_PRESENT_OFFSET, 6727 },
+  { 0x2fcf, G_UNICODE_NOT_PRESENT_OFFSET, 6731 },
+  { 0x2fd0, G_UNICODE_NOT_PRESENT_OFFSET, 6735 },
+  { 0x2fd1, G_UNICODE_NOT_PRESENT_OFFSET, 6739 },
+  { 0x2fd2, G_UNICODE_NOT_PRESENT_OFFSET, 6743 },
+  { 0x2fd3, G_UNICODE_NOT_PRESENT_OFFSET, 6747 },
+  { 0x2fd4, G_UNICODE_NOT_PRESENT_OFFSET, 6751 },
+  { 0x2fd5, G_UNICODE_NOT_PRESENT_OFFSET, 6755 },
+  { 0x3000, G_UNICODE_NOT_PRESENT_OFFSET, 0 },
+  { 0x3036, G_UNICODE_NOT_PRESENT_OFFSET, 6759 },
+  { 0x3038, G_UNICODE_NOT_PRESENT_OFFSET, 5995 },
+  { 0x3039, G_UNICODE_NOT_PRESENT_OFFSET, 6763 },
+  { 0x303a, G_UNICODE_NOT_PRESENT_OFFSET, 6767 },
+  { 0x304c, 6771, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x304e, 6778, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3050, 6785, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3052, 6792, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3054, 6799, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3056, 6806, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3058, 6813, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x305a, 6820, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x305c, 6827, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x305e, 6834, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3060, 6841, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3062, 6848, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3065, 6855, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3067, 6862, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3069, 6869, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3070, 6876, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3071, 6883, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3073, 6890, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3074, 6897, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3076, 6904, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3077, 6911, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3079, 6918, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x307a, 6925, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x307c, 6932, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x307d, 6939, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x3094, 6946, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x309b, G_UNICODE_NOT_PRESENT_OFFSET, 6953 },
+  { 0x309c, G_UNICODE_NOT_PRESENT_OFFSET, 6958 },
+  { 0x309e, 6963, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x309f, G_UNICODE_NOT_PRESENT_OFFSET, 6970 },
+  { 0x30ac, 6977, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30ae, 6984, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30b0, 6991, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30b2, 6998, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30b4, 7005, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30b6, 7012, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30b8, 7019, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30ba, 7026, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30bc, 7033, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30be, 7040, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30c0, 7047, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30c2, 7054, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30c5, 7061, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30c7, 7068, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30c9, 7075, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30d0, 7082, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30d1, 7089, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30d3, 7096, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30d4, 7103, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30d6, 7110, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30d7, 7117, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30d9, 7124, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30da, 7131, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30dc, 7138, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30dd, 7145, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30f4, 7152, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30f7, 7159, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30f8, 7166, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30f9, 7173, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30fa, 7180, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30fe, 7187, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x30ff, G_UNICODE_NOT_PRESENT_OFFSET, 7194 },
+  { 0x3131, G_UNICODE_NOT_PRESENT_OFFSET, 7201 },
+  { 0x3132, G_UNICODE_NOT_PRESENT_OFFSET, 7205 },
+  { 0x3133, G_UNICODE_NOT_PRESENT_OFFSET, 7209 },
+  { 0x3134, G_UNICODE_NOT_PRESENT_OFFSET, 7213 },
+  { 0x3135, G_UNICODE_NOT_PRESENT_OFFSET, 7217 },
+  { 0x3136, G_UNICODE_NOT_PRESENT_OFFSET, 7221 },
+  { 0x3137, G_UNICODE_NOT_PRESENT_OFFSET, 7225 },
+  { 0x3138, G_UNICODE_NOT_PRESENT_OFFSET, 7229 },
+  { 0x3139, G_UNICODE_NOT_PRESENT_OFFSET, 7233 },
+  { 0x313a, G_UNICODE_NOT_PRESENT_OFFSET, 7237 },
+  { 0x313b, G_UNICODE_NOT_PRESENT_OFFSET, 7241 },
+  { 0x313c, G_UNICODE_NOT_PRESENT_OFFSET, 7245 },
+  { 0x313d, G_UNICODE_NOT_PRESENT_OFFSET, 7249 },
+  { 0x313e, G_UNICODE_NOT_PRESENT_OFFSET, 7253 },
+  { 0x313f, G_UNICODE_NOT_PRESENT_OFFSET, 7257 },
+  { 0x3140, G_UNICODE_NOT_PRESENT_OFFSET, 7261 },
+  { 0x3141, G_UNICODE_NOT_PRESENT_OFFSET, 7265 },
+  { 0x3142, G_UNICODE_NOT_PRESENT_OFFSET, 7269 },
+  { 0x3143, G_UNICODE_NOT_PRESENT_OFFSET, 7273 },
+  { 0x3144, G_UNICODE_NOT_PRESENT_OFFSET, 7277 },
+  { 0x3145, G_UNICODE_NOT_PRESENT_OFFSET, 7281 },
+  { 0x3146, G_UNICODE_NOT_PRESENT_OFFSET, 7285 },
+  { 0x3147, G_UNICODE_NOT_PRESENT_OFFSET, 7289 },
+  { 0x3148, G_UNICODE_NOT_PRESENT_OFFSET, 7293 },
+  { 0x3149, G_UNICODE_NOT_PRESENT_OFFSET, 7297 },
+  { 0x314a, G_UNICODE_NOT_PRESENT_OFFSET, 7301 },
+  { 0x314b, G_UNICODE_NOT_PRESENT_OFFSET, 7305 },
+  { 0x314c, G_UNICODE_NOT_PRESENT_OFFSET, 7309 },
+  { 0x314d, G_UNICODE_NOT_PRESENT_OFFSET, 7313 },
+  { 0x314e, G_UNICODE_NOT_PRESENT_OFFSET, 7317 },
+  { 0x314f, G_UNICODE_NOT_PRESENT_OFFSET, 7321 },
+  { 0x3150, G_UNICODE_NOT_PRESENT_OFFSET, 7325 },
+  { 0x3151, G_UNICODE_NOT_PRESENT_OFFSET, 7329 },
+  { 0x3152, G_UNICODE_NOT_PRESENT_OFFSET, 7333 },
+  { 0x3153, G_UNICODE_NOT_PRESENT_OFFSET, 7337 },
+  { 0x3154, G_UNICODE_NOT_PRESENT_OFFSET, 7341 },
+  { 0x3155, G_UNICODE_NOT_PRESENT_OFFSET, 7345 },
+  { 0x3156, G_UNICODE_NOT_PRESENT_OFFSET, 7349 },
+  { 0x3157, G_UNICODE_NOT_PRESENT_OFFSET, 7353 },
+  { 0x3158, G_UNICODE_NOT_PRESENT_OFFSET, 7357 },
+  { 0x3159, G_UNICODE_NOT_PRESENT_OFFSET, 7361 },
+  { 0x315a, G_UNICODE_NOT_PRESENT_OFFSET, 7365 },
+  { 0x315b, G_UNICODE_NOT_PRESENT_OFFSET, 7369 },
+  { 0x315c, G_UNICODE_NOT_PRESENT_OFFSET, 7373 },
+  { 0x315d, G_UNICODE_NOT_PRESENT_OFFSET, 7377 },
+  { 0x315e, G_UNICODE_NOT_PRESENT_OFFSET, 7381 },
+  { 0x315f, G_UNICODE_NOT_PRESENT_OFFSET, 7385 },
+  { 0x3160, G_UNICODE_NOT_PRESENT_OFFSET, 7389 },
+  { 0x3161, G_UNICODE_NOT_PRESENT_OFFSET, 7393 },
+  { 0x3162, G_UNICODE_NOT_PRESENT_OFFSET, 7397 },
+  { 0x3163, G_UNICODE_NOT_PRESENT_OFFSET, 7401 },
+  { 0x3164, G_UNICODE_NOT_PRESENT_OFFSET, 7405 },
+  { 0x3165, G_UNICODE_NOT_PRESENT_OFFSET, 7409 },
+  { 0x3166, G_UNICODE_NOT_PRESENT_OFFSET, 7413 },
+  { 0x3167, G_UNICODE_NOT_PRESENT_OFFSET, 7417 },
+  { 0x3168, G_UNICODE_NOT_PRESENT_OFFSET, 7421 },
+  { 0x3169, G_UNICODE_NOT_PRESENT_OFFSET, 7425 },
+  { 0x316a, G_UNICODE_NOT_PRESENT_OFFSET, 7429 },
+  { 0x316b, G_UNICODE_NOT_PRESENT_OFFSET, 7433 },
+  { 0x316c, G_UNICODE_NOT_PRESENT_OFFSET, 7437 },
+  { 0x316d, G_UNICODE_NOT_PRESENT_OFFSET, 7441 },
+  { 0x316e, G_UNICODE_NOT_PRESENT_OFFSET, 7445 },
+  { 0x316f, G_UNICODE_NOT_PRESENT_OFFSET, 7449 },
+  { 0x3170, G_UNICODE_NOT_PRESENT_OFFSET, 7453 },
+  { 0x3171, G_UNICODE_NOT_PRESENT_OFFSET, 7457 },
+  { 0x3172, G_UNICODE_NOT_PRESENT_OFFSET, 7461 },
+  { 0x3173, G_UNICODE_NOT_PRESENT_OFFSET, 7465 },
+  { 0x3174, G_UNICODE_NOT_PRESENT_OFFSET, 7469 },
+  { 0x3175, G_UNICODE_NOT_PRESENT_OFFSET, 7473 },
+  { 0x3176, G_UNICODE_NOT_PRESENT_OFFSET, 7477 },
+  { 0x3177, G_UNICODE_NOT_PRESENT_OFFSET, 7481 },
+  { 0x3178, G_UNICODE_NOT_PRESENT_OFFSET, 7485 },
+  { 0x3179, G_UNICODE_NOT_PRESENT_OFFSET, 7489 },
+  { 0x317a, G_UNICODE_NOT_PRESENT_OFFSET, 7493 },
+  { 0x317b, G_UNICODE_NOT_PRESENT_OFFSET, 7497 },
+  { 0x317c, G_UNICODE_NOT_PRESENT_OFFSET, 7501 },
+  { 0x317d, G_UNICODE_NOT_PRESENT_OFFSET, 7505 },
+  { 0x317e, G_UNICODE_NOT_PRESENT_OFFSET, 7509 },
+  { 0x317f, G_UNICODE_NOT_PRESENT_OFFSET, 7513 },
+  { 0x3180, G_UNICODE_NOT_PRESENT_OFFSET, 7517 },
+  { 0x3181, G_UNICODE_NOT_PRESENT_OFFSET, 7521 },
+  { 0x3182, G_UNICODE_NOT_PRESENT_OFFSET, 7525 },
+  { 0x3183, G_UNICODE_NOT_PRESENT_OFFSET, 7529 },
+  { 0x3184, G_UNICODE_NOT_PRESENT_OFFSET, 7533 },
+  { 0x3185, G_UNICODE_NOT_PRESENT_OFFSET, 7537 },
+  { 0x3186, G_UNICODE_NOT_PRESENT_OFFSET, 7541 },
+  { 0x3187, G_UNICODE_NOT_PRESENT_OFFSET, 7545 },
+  { 0x3188, G_UNICODE_NOT_PRESENT_OFFSET, 7549 },
+  { 0x3189, G_UNICODE_NOT_PRESENT_OFFSET, 7553 },
+  { 0x318a, G_UNICODE_NOT_PRESENT_OFFSET, 7557 },
+  { 0x318b, G_UNICODE_NOT_PRESENT_OFFSET, 7561 },
+  { 0x318c, G_UNICODE_NOT_PRESENT_OFFSET, 7565 },
+  { 0x318d, G_UNICODE_NOT_PRESENT_OFFSET, 7569 },
+  { 0x318e, G_UNICODE_NOT_PRESENT_OFFSET, 7573 },
+  { 0x3192, G_UNICODE_NOT_PRESENT_OFFSET, 5903 },
+  { 0x3193, G_UNICODE_NOT_PRESENT_OFFSET, 5927 },
+  { 0x3194, G_UNICODE_NOT_PRESENT_OFFSET, 7577 },
+  { 0x3195, G_UNICODE_NOT_PRESENT_OFFSET, 7581 },
+  { 0x3196, G_UNICODE_NOT_PRESENT_OFFSET, 7585 },
+  { 0x3197, G_UNICODE_NOT_PRESENT_OFFSET, 7589 },
+  { 0x3198, G_UNICODE_NOT_PRESENT_OFFSET, 7593 },
+  { 0x3199, G_UNICODE_NOT_PRESENT_OFFSET, 7597 },
+  { 0x319a, G_UNICODE_NOT_PRESENT_OFFSET, 5919 },
+  { 0x319b, G_UNICODE_NOT_PRESENT_OFFSET, 7601 },
+  { 0x319c, G_UNICODE_NOT_PRESENT_OFFSET, 7605 },
+  { 0x319d, G_UNICODE_NOT_PRESENT_OFFSET, 7609 },
+  { 0x319e, G_UNICODE_NOT_PRESENT_OFFSET, 7613 },
+  { 0x319f, G_UNICODE_NOT_PRESENT_OFFSET, 5935 },
+  { 0x3200, G_UNICODE_NOT_PRESENT_OFFSET, 7617 },
+  { 0x3201, G_UNICODE_NOT_PRESENT_OFFSET, 7623 },
+  { 0x3202, G_UNICODE_NOT_PRESENT_OFFSET, 7629 },
+  { 0x3203, G_UNICODE_NOT_PRESENT_OFFSET, 7635 },
+  { 0x3204, G_UNICODE_NOT_PRESENT_OFFSET, 7641 },
+  { 0x3205, G_UNICODE_NOT_PRESENT_OFFSET, 7647 },
+  { 0x3206, G_UNICODE_NOT_PRESENT_OFFSET, 7653 },
+  { 0x3207, G_UNICODE_NOT_PRESENT_OFFSET, 7659 },
+  { 0x3208, G_UNICODE_NOT_PRESENT_OFFSET, 7665 },
+  { 0x3209, G_UNICODE_NOT_PRESENT_OFFSET, 7671 },
+  { 0x320a, G_UNICODE_NOT_PRESENT_OFFSET, 7677 },
+  { 0x320b, G_UNICODE_NOT_PRESENT_OFFSET, 7683 },
+  { 0x320c, G_UNICODE_NOT_PRESENT_OFFSET, 7689 },
+  { 0x320d, G_UNICODE_NOT_PRESENT_OFFSET, 7695 },
+  { 0x320e, G_UNICODE_NOT_PRESENT_OFFSET, 7701 },
+  { 0x320f, G_UNICODE_NOT_PRESENT_OFFSET, 7710 },
+  { 0x3210, G_UNICODE_NOT_PRESENT_OFFSET, 7719 },
+  { 0x3211, G_UNICODE_NOT_PRESENT_OFFSET, 7728 },
+  { 0x3212, G_UNICODE_NOT_PRESENT_OFFSET, 7737 },
+  { 0x3213, G_UNICODE_NOT_PRESENT_OFFSET, 7746 },
+  { 0x3214, G_UNICODE_NOT_PRESENT_OFFSET, 7755 },
+  { 0x3215, G_UNICODE_NOT_PRESENT_OFFSET, 7764 },
+  { 0x3216, G_UNICODE_NOT_PRESENT_OFFSET, 7773 },
+  { 0x3217, G_UNICODE_NOT_PRESENT_OFFSET, 7782 },
+  { 0x3218, G_UNICODE_NOT_PRESENT_OFFSET, 7791 },
+  { 0x3219, G_UNICODE_NOT_PRESENT_OFFSET, 7800 },
+  { 0x321a, G_UNICODE_NOT_PRESENT_OFFSET, 7809 },
+  { 0x321b, G_UNICODE_NOT_PRESENT_OFFSET, 7818 },
+  { 0x321c, G_UNICODE_NOT_PRESENT_OFFSET, 7827 },
+  { 0x3220, G_UNICODE_NOT_PRESENT_OFFSET, 7836 },
+  { 0x3221, G_UNICODE_NOT_PRESENT_OFFSET, 7842 },
+  { 0x3222, G_UNICODE_NOT_PRESENT_OFFSET, 7848 },
+  { 0x3223, G_UNICODE_NOT_PRESENT_OFFSET, 7854 },
+  { 0x3224, G_UNICODE_NOT_PRESENT_OFFSET, 7860 },
+  { 0x3225, G_UNICODE_NOT_PRESENT_OFFSET, 7866 },
+  { 0x3226, G_UNICODE_NOT_PRESENT_OFFSET, 7872 },
+  { 0x3227, G_UNICODE_NOT_PRESENT_OFFSET, 7878 },
+  { 0x3228, G_UNICODE_NOT_PRESENT_OFFSET, 7884 },
+  { 0x3229, G_UNICODE_NOT_PRESENT_OFFSET, 7890 },
+  { 0x322a, G_UNICODE_NOT_PRESENT_OFFSET, 7896 },
+  { 0x322b, G_UNICODE_NOT_PRESENT_OFFSET, 7902 },
+  { 0x322c, G_UNICODE_NOT_PRESENT_OFFSET, 7908 },
+  { 0x322d, G_UNICODE_NOT_PRESENT_OFFSET, 7914 },
+  { 0x322e, G_UNICODE_NOT_PRESENT_OFFSET, 7920 },
+  { 0x322f, G_UNICODE_NOT_PRESENT_OFFSET, 7926 },
+  { 0x3230, G_UNICODE_NOT_PRESENT_OFFSET, 7932 },
+  { 0x3231, G_UNICODE_NOT_PRESENT_OFFSET, 7938 },
+  { 0x3232, G_UNICODE_NOT_PRESENT_OFFSET, 7944 },
+  { 0x3233, G_UNICODE_NOT_PRESENT_OFFSET, 7950 },
+  { 0x3234, G_UNICODE_NOT_PRESENT_OFFSET, 7956 },
+  { 0x3235, G_UNICODE_NOT_PRESENT_OFFSET, 7962 },
+  { 0x3236, G_UNICODE_NOT_PRESENT_OFFSET, 7968 },
+  { 0x3237, G_UNICODE_NOT_PRESENT_OFFSET, 7974 },
+  { 0x3238, G_UNICODE_NOT_PRESENT_OFFSET, 7980 },
+  { 0x3239, G_UNICODE_NOT_PRESENT_OFFSET, 7986 },
+  { 0x323a, G_UNICODE_NOT_PRESENT_OFFSET, 7992 },
+  { 0x323b, G_UNICODE_NOT_PRESENT_OFFSET, 7998 },
+  { 0x323c, G_UNICODE_NOT_PRESENT_OFFSET, 8004 },
+  { 0x323d, G_UNICODE_NOT_PRESENT_OFFSET, 8010 },
+  { 0x323e, G_UNICODE_NOT_PRESENT_OFFSET, 8016 },
+  { 0x323f, G_UNICODE_NOT_PRESENT_OFFSET, 8022 },
+  { 0x3240, G_UNICODE_NOT_PRESENT_OFFSET, 8028 },
+  { 0x3241, G_UNICODE_NOT_PRESENT_OFFSET, 8034 },
+  { 0x3242, G_UNICODE_NOT_PRESENT_OFFSET, 8040 },
+  { 0x3243, G_UNICODE_NOT_PRESENT_OFFSET, 8046 },
+  { 0x3251, G_UNICODE_NOT_PRESENT_OFFSET, 8052 },
+  { 0x3252, G_UNICODE_NOT_PRESENT_OFFSET, 8055 },
+  { 0x3253, G_UNICODE_NOT_PRESENT_OFFSET, 8058 },
+  { 0x3254, G_UNICODE_NOT_PRESENT_OFFSET, 8061 },
+  { 0x3255, G_UNICODE_NOT_PRESENT_OFFSET, 8064 },
+  { 0x3256, G_UNICODE_NOT_PRESENT_OFFSET, 8067 },
+  { 0x3257, G_UNICODE_NOT_PRESENT_OFFSET, 8070 },
+  { 0x3258, G_UNICODE_NOT_PRESENT_OFFSET, 8073 },
+  { 0x3259, G_UNICODE_NOT_PRESENT_OFFSET, 8076 },
+  { 0x325a, G_UNICODE_NOT_PRESENT_OFFSET, 8079 },
+  { 0x325b, G_UNICODE_NOT_PRESENT_OFFSET, 8082 },
+  { 0x325c, G_UNICODE_NOT_PRESENT_OFFSET, 8085 },
+  { 0x325d, G_UNICODE_NOT_PRESENT_OFFSET, 8088 },
+  { 0x325e, G_UNICODE_NOT_PRESENT_OFFSET, 8091 },
+  { 0x325f, G_UNICODE_NOT_PRESENT_OFFSET, 8094 },
+  { 0x3260, G_UNICODE_NOT_PRESENT_OFFSET, 7201 },
+  { 0x3261, G_UNICODE_NOT_PRESENT_OFFSET, 7213 },
+  { 0x3262, G_UNICODE_NOT_PRESENT_OFFSET, 7225 },
+  { 0x3263, G_UNICODE_NOT_PRESENT_OFFSET, 7233 },
+  { 0x3264, G_UNICODE_NOT_PRESENT_OFFSET, 7265 },
+  { 0x3265, G_UNICODE_NOT_PRESENT_OFFSET, 7269 },
+  { 0x3266, G_UNICODE_NOT_PRESENT_OFFSET, 7281 },
+  { 0x3267, G_UNICODE_NOT_PRESENT_OFFSET, 7289 },
+  { 0x3268, G_UNICODE_NOT_PRESENT_OFFSET, 7293 },
+  { 0x3269, G_UNICODE_NOT_PRESENT_OFFSET, 7301 },
+  { 0x326a, G_UNICODE_NOT_PRESENT_OFFSET, 7305 },
+  { 0x326b, G_UNICODE_NOT_PRESENT_OFFSET, 7309 },
+  { 0x326c, G_UNICODE_NOT_PRESENT_OFFSET, 7313 },
+  { 0x326d, G_UNICODE_NOT_PRESENT_OFFSET, 7317 },
+  { 0x326e, G_UNICODE_NOT_PRESENT_OFFSET, 8097 },
+  { 0x326f, G_UNICODE_NOT_PRESENT_OFFSET, 8104 },
+  { 0x3270, G_UNICODE_NOT_PRESENT_OFFSET, 8111 },
+  { 0x3271, G_UNICODE_NOT_PRESENT_OFFSET, 8118 },
+  { 0x3272, G_UNICODE_NOT_PRESENT_OFFSET, 8125 },
+  { 0x3273, G_UNICODE_NOT_PRESENT_OFFSET, 8132 },
+  { 0x3274, G_UNICODE_NOT_PRESENT_OFFSET, 8139 },
+  { 0x3275, G_UNICODE_NOT_PRESENT_OFFSET, 8146 },
+  { 0x3276, G_UNICODE_NOT_PRESENT_OFFSET, 8153 },
+  { 0x3277, G_UNICODE_NOT_PRESENT_OFFSET, 8160 },
+  { 0x3278, G_UNICODE_NOT_PRESENT_OFFSET, 8167 },
+  { 0x3279, G_UNICODE_NOT_PRESENT_OFFSET, 8174 },
+  { 0x327a, G_UNICODE_NOT_PRESENT_OFFSET, 8181 },
+  { 0x327b, G_UNICODE_NOT_PRESENT_OFFSET, 8188 },
+  { 0x3280, G_UNICODE_NOT_PRESENT_OFFSET, 5903 },
+  { 0x3281, G_UNICODE_NOT_PRESENT_OFFSET, 5927 },
+  { 0x3282, G_UNICODE_NOT_PRESENT_OFFSET, 7577 },
+  { 0x3283, G_UNICODE_NOT_PRESENT_OFFSET, 7581 },
+  { 0x3284, G_UNICODE_NOT_PRESENT_OFFSET, 8195 },
+  { 0x3285, G_UNICODE_NOT_PRESENT_OFFSET, 8199 },
+  { 0x3286, G_UNICODE_NOT_PRESENT_OFFSET, 8203 },
+  { 0x3287, G_UNICODE_NOT_PRESENT_OFFSET, 5947 },
+  { 0x3288, G_UNICODE_NOT_PRESENT_OFFSET, 8207 },
+  { 0x3289, G_UNICODE_NOT_PRESENT_OFFSET, 5995 },
+  { 0x328a, G_UNICODE_NOT_PRESENT_OFFSET, 6195 },
+  { 0x328b, G_UNICODE_NOT_PRESENT_OFFSET, 6243 },
+  { 0x328c, G_UNICODE_NOT_PRESENT_OFFSET, 6239 },
+  { 0x328d, G_UNICODE_NOT_PRESENT_OFFSET, 6199 },
+  { 0x328e, G_UNICODE_NOT_PRESENT_OFFSET, 6567 },
+  { 0x328f, G_UNICODE_NOT_PRESENT_OFFSET, 6027 },
+  { 0x3290, G_UNICODE_NOT_PRESENT_OFFSET, 6187 },
+  { 0x3291, G_UNICODE_NOT_PRESENT_OFFSET, 8211 },
+  { 0x3292, G_UNICODE_NOT_PRESENT_OFFSET, 8215 },
+  { 0x3293, G_UNICODE_NOT_PRESENT_OFFSET, 8219 },
+  { 0x3294, G_UNICODE_NOT_PRESENT_OFFSET, 8223 },
+  { 0x3295, G_UNICODE_NOT_PRESENT_OFFSET, 8227 },
+  { 0x3296, G_UNICODE_NOT_PRESENT_OFFSET, 8231 },
+  { 0x3297, G_UNICODE_NOT_PRESENT_OFFSET, 8235 },
+  { 0x3298, G_UNICODE_NOT_PRESENT_OFFSET, 8239 },
+  { 0x3299, G_UNICODE_NOT_PRESENT_OFFSET, 8243 },
+  { 0x329a, G_UNICODE_NOT_PRESENT_OFFSET, 8247 },
+  { 0x329b, G_UNICODE_NOT_PRESENT_OFFSET, 6051 },
+  { 0x329c, G_UNICODE_NOT_PRESENT_OFFSET, 8251 },
+  { 0x329d, G_UNICODE_NOT_PRESENT_OFFSET, 8255 },
+  { 0x329e, G_UNICODE_NOT_PRESENT_OFFSET, 8259 },
+  { 0x329f, G_UNICODE_NOT_PRESENT_OFFSET, 8263 },
+  { 0x32a0, G_UNICODE_NOT_PRESENT_OFFSET, 8267 },
+  { 0x32a1, G_UNICODE_NOT_PRESENT_OFFSET, 8271 },
+  { 0x32a2, G_UNICODE_NOT_PRESENT_OFFSET, 8275 },
+  { 0x32a3, G_UNICODE_NOT_PRESENT_OFFSET, 8279 },
+  { 0x32a4, G_UNICODE_NOT_PRESENT_OFFSET, 7585 },
+  { 0x32a5, G_UNICODE_NOT_PRESENT_OFFSET, 7589 },
+  { 0x32a6, G_UNICODE_NOT_PRESENT_OFFSET, 7593 },
+  { 0x32a7, G_UNICODE_NOT_PRESENT_OFFSET, 8283 },
+  { 0x32a8, G_UNICODE_NOT_PRESENT_OFFSET, 8287 },
+  { 0x32a9, G_UNICODE_NOT_PRESENT_OFFSET, 8291 },
+  { 0x32aa, G_UNICODE_NOT_PRESENT_OFFSET, 8295 },
+  { 0x32ab, G_UNICODE_NOT_PRESENT_OFFSET, 8299 },
+  { 0x32ac, G_UNICODE_NOT_PRESENT_OFFSET, 8303 },
+  { 0x32ad, G_UNICODE_NOT_PRESENT_OFFSET, 8307 },
+  { 0x32ae, G_UNICODE_NOT_PRESENT_OFFSET, 8311 },
+  { 0x32af, G_UNICODE_NOT_PRESENT_OFFSET, 8315 },
+  { 0x32b0, G_UNICODE_NOT_PRESENT_OFFSET, 8319 },
+  { 0x32b1, G_UNICODE_NOT_PRESENT_OFFSET, 8323 },
+  { 0x32b2, G_UNICODE_NOT_PRESENT_OFFSET, 8326 },
+  { 0x32b3, G_UNICODE_NOT_PRESENT_OFFSET, 8329 },
+  { 0x32b4, G_UNICODE_NOT_PRESENT_OFFSET, 8332 },
+  { 0x32b5, G_UNICODE_NOT_PRESENT_OFFSET, 8335 },
+  { 0x32b6, G_UNICODE_NOT_PRESENT_OFFSET, 8338 },
+  { 0x32b7, G_UNICODE_NOT_PRESENT_OFFSET, 8341 },
+  { 0x32b8, G_UNICODE_NOT_PRESENT_OFFSET, 8344 },
+  { 0x32b9, G_UNICODE_NOT_PRESENT_OFFSET, 8347 },
+  { 0x32ba, G_UNICODE_NOT_PRESENT_OFFSET, 8350 },
+  { 0x32bb, G_UNICODE_NOT_PRESENT_OFFSET, 8353 },
+  { 0x32bc, G_UNICODE_NOT_PRESENT_OFFSET, 8356 },
+  { 0x32bd, G_UNICODE_NOT_PRESENT_OFFSET, 8359 },
+  { 0x32be, G_UNICODE_NOT_PRESENT_OFFSET, 8362 },
+  { 0x32bf, G_UNICODE_NOT_PRESENT_OFFSET, 8365 },
+  { 0x32c0, G_UNICODE_NOT_PRESENT_OFFSET, 8368 },
+  { 0x32c1, G_UNICODE_NOT_PRESENT_OFFSET, 8373 },
+  { 0x32c2, G_UNICODE_NOT_PRESENT_OFFSET, 8378 },
+  { 0x32c3, G_UNICODE_NOT_PRESENT_OFFSET, 8383 },
+  { 0x32c4, G_UNICODE_NOT_PRESENT_OFFSET, 8388 },
+  { 0x32c5, G_UNICODE_NOT_PRESENT_OFFSET, 8393 },
+  { 0x32c6, G_UNICODE_NOT_PRESENT_OFFSET, 8398 },
+  { 0x32c7, G_UNICODE_NOT_PRESENT_OFFSET, 8403 },
+  { 0x32c8, G_UNICODE_NOT_PRESENT_OFFSET, 8408 },
+  { 0x32c9, G_UNICODE_NOT_PRESENT_OFFSET, 8413 },
+  { 0x32ca, G_UNICODE_NOT_PRESENT_OFFSET, 8419 },
+  { 0x32cb, G_UNICODE_NOT_PRESENT_OFFSET, 8425 },
+  { 0x32d0, G_UNICODE_NOT_PRESENT_OFFSET, 8431 },
+  { 0x32d1, G_UNICODE_NOT_PRESENT_OFFSET, 8435 },
+  { 0x32d2, G_UNICODE_NOT_PRESENT_OFFSET, 8439 },
+  { 0x32d3, G_UNICODE_NOT_PRESENT_OFFSET, 8443 },
+  { 0x32d4, G_UNICODE_NOT_PRESENT_OFFSET, 8447 },
+  { 0x32d5, G_UNICODE_NOT_PRESENT_OFFSET, 8451 },
+  { 0x32d6, G_UNICODE_NOT_PRESENT_OFFSET, 8455 },
+  { 0x32d7, G_UNICODE_NOT_PRESENT_OFFSET, 8459 },
+  { 0x32d8, G_UNICODE_NOT_PRESENT_OFFSET, 8463 },
+  { 0x32d9, G_UNICODE_NOT_PRESENT_OFFSET, 8467 },
+  { 0x32da, G_UNICODE_NOT_PRESENT_OFFSET, 8471 },
+  { 0x32db, G_UNICODE_NOT_PRESENT_OFFSET, 8475 },
+  { 0x32dc, G_UNICODE_NOT_PRESENT_OFFSET, 8479 },
+  { 0x32dd, G_UNICODE_NOT_PRESENT_OFFSET, 8483 },
+  { 0x32de, G_UNICODE_NOT_PRESENT_OFFSET, 8487 },
+  { 0x32df, G_UNICODE_NOT_PRESENT_OFFSET, 8491 },
+  { 0x32e0, G_UNICODE_NOT_PRESENT_OFFSET, 8495 },
+  { 0x32e1, G_UNICODE_NOT_PRESENT_OFFSET, 8499 },
+  { 0x32e2, G_UNICODE_NOT_PRESENT_OFFSET, 8503 },
+  { 0x32e3, G_UNICODE_NOT_PRESENT_OFFSET, 8507 },
+  { 0x32e4, G_UNICODE_NOT_PRESENT_OFFSET, 8511 },
+  { 0x32e5, G_UNICODE_NOT_PRESENT_OFFSET, 8515 },
+  { 0x32e6, G_UNICODE_NOT_PRESENT_OFFSET, 8519 },
+  { 0x32e7, G_UNICODE_NOT_PRESENT_OFFSET, 8523 },
+  { 0x32e8, G_UNICODE_NOT_PRESENT_OFFSET, 8527 },
+  { 0x32e9, G_UNICODE_NOT_PRESENT_OFFSET, 8531 },
+  { 0x32ea, G_UNICODE_NOT_PRESENT_OFFSET, 8535 },
+  { 0x32eb, G_UNICODE_NOT_PRESENT_OFFSET, 8539 },
+  { 0x32ec, G_UNICODE_NOT_PRESENT_OFFSET, 8543 },
+  { 0x32ed, G_UNICODE_NOT_PRESENT_OFFSET, 8547 },
+  { 0x32ee, G_UNICODE_NOT_PRESENT_OFFSET, 8551 },
+  { 0x32ef, G_UNICODE_NOT_PRESENT_OFFSET, 8555 },
+  { 0x32f0, G_UNICODE_NOT_PRESENT_OFFSET, 8559 },
+  { 0x32f1, G_UNICODE_NOT_PRESENT_OFFSET, 8563 },
+  { 0x32f2, G_UNICODE_NOT_PRESENT_OFFSET, 8567 },
+  { 0x32f3, G_UNICODE_NOT_PRESENT_OFFSET, 8571 },
+  { 0x32f4, G_UNICODE_NOT_PRESENT_OFFSET, 8575 },
+  { 0x32f5, G_UNICODE_NOT_PRESENT_OFFSET, 8579 },
+  { 0x32f6, G_UNICODE_NOT_PRESENT_OFFSET, 8583 },
+  { 0x32f7, G_UNICODE_NOT_PRESENT_OFFSET, 8587 },
+  { 0x32f8, G_UNICODE_NOT_PRESENT_OFFSET, 8591 },
+  { 0x32f9, G_UNICODE_NOT_PRESENT_OFFSET, 8595 },
+  { 0x32fa, G_UNICODE_NOT_PRESENT_OFFSET, 8599 },
+  { 0x32fb, G_UNICODE_NOT_PRESENT_OFFSET, 8603 },
+  { 0x32fc, G_UNICODE_NOT_PRESENT_OFFSET, 8607 },
+  { 0x32fd, G_UNICODE_NOT_PRESENT_OFFSET, 8611 },
+  { 0x32fe, G_UNICODE_NOT_PRESENT_OFFSET, 8615 },
+  { 0x3300, G_UNICODE_NOT_PRESENT_OFFSET, 8619 },
+  { 0x3301, G_UNICODE_NOT_PRESENT_OFFSET, 8635 },
+  { 0x3302, G_UNICODE_NOT_PRESENT_OFFSET, 8648 },
+  { 0x3303, G_UNICODE_NOT_PRESENT_OFFSET, 8664 },
+  { 0x3304, G_UNICODE_NOT_PRESENT_OFFSET, 8674 },
+  { 0x3305, G_UNICODE_NOT_PRESENT_OFFSET, 8690 },
+  { 0x3306, G_UNICODE_NOT_PRESENT_OFFSET, 8700 },
+  { 0x3307, G_UNICODE_NOT_PRESENT_OFFSET, 8710 },
+  { 0x3308, G_UNICODE_NOT_PRESENT_OFFSET, 8729 },
+  { 0x3309, G_UNICODE_NOT_PRESENT_OFFSET, 8742 },
+  { 0x330a, G_UNICODE_NOT_PRESENT_OFFSET, 8752 },
+  { 0x330b, G_UNICODE_NOT_PRESENT_OFFSET, 8762 },
+  { 0x330c, G_UNICODE_NOT_PRESENT_OFFSET, 8772 },
+  { 0x330d, G_UNICODE_NOT_PRESENT_OFFSET, 8785 },
+  { 0x330e, G_UNICODE_NOT_PRESENT_OFFSET, 8798 },
+  { 0x330f, G_UNICODE_NOT_PRESENT_OFFSET, 8811 },
+  { 0x3310, G_UNICODE_NOT_PRESENT_OFFSET, 8824 },
+  { 0x3311, G_UNICODE_NOT_PRESENT_OFFSET, 8837 },
+  { 0x3312, G_UNICODE_NOT_PRESENT_OFFSET, 8850 },
+  { 0x3313, G_UNICODE_NOT_PRESENT_OFFSET, 8863 },
+  { 0x3314, G_UNICODE_NOT_PRESENT_OFFSET, 8882 },
+  { 0x3315, G_UNICODE_NOT_PRESENT_OFFSET, 8889 },
+  { 0x3316, G_UNICODE_NOT_PRESENT_OFFSET, 8908 },
+  { 0x3317, G_UNICODE_NOT_PRESENT_OFFSET, 8927 },
+  { 0x3318, G_UNICODE_NOT_PRESENT_OFFSET, 8943 },
+  { 0x3319, G_UNICODE_NOT_PRESENT_OFFSET, 8956 },
+  { 0x331a, G_UNICODE_NOT_PRESENT_OFFSET, 8975 },
+  { 0x331b, G_UNICODE_NOT_PRESENT_OFFSET, 8994 },
+  { 0x331c, G_UNICODE_NOT_PRESENT_OFFSET, 9007 },
+  { 0x331d, G_UNICODE_NOT_PRESENT_OFFSET, 9017 },
+  { 0x331e, G_UNICODE_NOT_PRESENT_OFFSET, 9027 },
+  { 0x331f, G_UNICODE_NOT_PRESENT_OFFSET, 9040 },
+  { 0x3320, G_UNICODE_NOT_PRESENT_OFFSET, 9053 },
+  { 0x3321, G_UNICODE_NOT_PRESENT_OFFSET, 9069 },
+  { 0x3322, G_UNICODE_NOT_PRESENT_OFFSET, 9085 },
+  { 0x3323, G_UNICODE_NOT_PRESENT_OFFSET, 9095 },
+  { 0x3324, G_UNICODE_NOT_PRESENT_OFFSET, 9105 },
+  { 0x3325, G_UNICODE_NOT_PRESENT_OFFSET, 9118 },
+  { 0x3326, G_UNICODE_NOT_PRESENT_OFFSET, 9128 },
+  { 0x3327, G_UNICODE_NOT_PRESENT_OFFSET, 9138 },
+  { 0x3328, G_UNICODE_NOT_PRESENT_OFFSET, 9145 },
+  { 0x3329, G_UNICODE_NOT_PRESENT_OFFSET, 9152 },
+  { 0x332a, G_UNICODE_NOT_PRESENT_OFFSET, 9162 },
+  { 0x332b, G_UNICODE_NOT_PRESENT_OFFSET, 9172 },
+  { 0x332c, G_UNICODE_NOT_PRESENT_OFFSET, 9191 },
+  { 0x332d, G_UNICODE_NOT_PRESENT_OFFSET, 9204 },
+  { 0x332e, G_UNICODE_NOT_PRESENT_OFFSET, 9220 },
+  { 0x332f, G_UNICODE_NOT_PRESENT_OFFSET, 9239 },
+  { 0x3330, G_UNICODE_NOT_PRESENT_OFFSET, 9252 },
+  { 0x3331, G_UNICODE_NOT_PRESENT_OFFSET, 9262 },
+  { 0x3332, G_UNICODE_NOT_PRESENT_OFFSET, 9272 },
+  { 0x3333, G_UNICODE_NOT_PRESENT_OFFSET, 9291 },
+  { 0x3334, G_UNICODE_NOT_PRESENT_OFFSET, 9304 },
+  { 0x3335, G_UNICODE_NOT_PRESENT_OFFSET, 9323 },
+  { 0x3336, G_UNICODE_NOT_PRESENT_OFFSET, 9333 },
+  { 0x3337, G_UNICODE_NOT_PRESENT_OFFSET, 9349 },
+  { 0x3338, G_UNICODE_NOT_PRESENT_OFFSET, 9359 },
+  { 0x3339, G_UNICODE_NOT_PRESENT_OFFSET, 9372 },
+  { 0x333a, G_UNICODE_NOT_PRESENT_OFFSET, 9382 },
+  { 0x333b, G_UNICODE_NOT_PRESENT_OFFSET, 9395 },
+  { 0x333c, G_UNICODE_NOT_PRESENT_OFFSET, 9411 },
+  { 0x333d, G_UNICODE_NOT_PRESENT_OFFSET, 9424 },
+  { 0x333e, G_UNICODE_NOT_PRESENT_OFFSET, 9440 },
+  { 0x333f, G_UNICODE_NOT_PRESENT_OFFSET, 9453 },
+  { 0x3340, G_UNICODE_NOT_PRESENT_OFFSET, 9460 },
+  { 0x3341, G_UNICODE_NOT_PRESENT_OFFSET, 9476 },
+  { 0x3342, G_UNICODE_NOT_PRESENT_OFFSET, 9486 },
+  { 0x3343, G_UNICODE_NOT_PRESENT_OFFSET, 9496 },
+  { 0x3344, G_UNICODE_NOT_PRESENT_OFFSET, 9509 },
+  { 0x3345, G_UNICODE_NOT_PRESENT_OFFSET, 9519 },
+  { 0x3346, G_UNICODE_NOT_PRESENT_OFFSET, 9529 },
+  { 0x3347, G_UNICODE_NOT_PRESENT_OFFSET, 9539 },
+  { 0x3348, G_UNICODE_NOT_PRESENT_OFFSET, 9555 },
+  { 0x3349, G_UNICODE_NOT_PRESENT_OFFSET, 9568 },
+  { 0x334a, G_UNICODE_NOT_PRESENT_OFFSET, 9575 },
+  { 0x334b, G_UNICODE_NOT_PRESENT_OFFSET, 9594 },
+  { 0x334c, G_UNICODE_NOT_PRESENT_OFFSET, 9604 },
+  { 0x334d, G_UNICODE_NOT_PRESENT_OFFSET, 9620 },
+  { 0x334e, G_UNICODE_NOT_PRESENT_OFFSET, 9633 },
+  { 0x334f, G_UNICODE_NOT_PRESENT_OFFSET, 9646 },
+  { 0x3350, G_UNICODE_NOT_PRESENT_OFFSET, 9656 },
+  { 0x3351, G_UNICODE_NOT_PRESENT_OFFSET, 9666 },
+  { 0x3352, G_UNICODE_NOT_PRESENT_OFFSET, 9679 },
+  { 0x3353, G_UNICODE_NOT_PRESENT_OFFSET, 9686 },
+  { 0x3354, G_UNICODE_NOT_PRESENT_OFFSET, 9699 },
+  { 0x3355, G_UNICODE_NOT_PRESENT_OFFSET, 9715 },
+  { 0x3356, G_UNICODE_NOT_PRESENT_OFFSET, 9722 },
+  { 0x3357, G_UNICODE_NOT_PRESENT_OFFSET, 9741 },
+  { 0x3358, G_UNICODE_NOT_PRESENT_OFFSET, 9751 },
+  { 0x3359, G_UNICODE_NOT_PRESENT_OFFSET, 9756 },
+  { 0x335a, G_UNICODE_NOT_PRESENT_OFFSET, 9761 },
+  { 0x335b, G_UNICODE_NOT_PRESENT_OFFSET, 9766 },
+  { 0x335c, G_UNICODE_NOT_PRESENT_OFFSET, 9771 },
+  { 0x335d, G_UNICODE_NOT_PRESENT_OFFSET, 9776 },
+  { 0x335e, G_UNICODE_NOT_PRESENT_OFFSET, 9781 },
+  { 0x335f, G_UNICODE_NOT_PRESENT_OFFSET, 9786 },
+  { 0x3360, G_UNICODE_NOT_PRESENT_OFFSET, 9791 },
+  { 0x3361, G_UNICODE_NOT_PRESENT_OFFSET, 9796 },
+  { 0x3362, G_UNICODE_NOT_PRESENT_OFFSET, 9801 },
+  { 0x3363, G_UNICODE_NOT_PRESENT_OFFSET, 9807 },
+  { 0x3364, G_UNICODE_NOT_PRESENT_OFFSET, 9813 },
+  { 0x3365, G_UNICODE_NOT_PRESENT_OFFSET, 9819 },
+  { 0x3366, G_UNICODE_NOT_PRESENT_OFFSET, 9825 },
+  { 0x3367, G_UNICODE_NOT_PRESENT_OFFSET, 9831 },
+  { 0x3368, G_UNICODE_NOT_PRESENT_OFFSET, 9837 },
+  { 0x3369, G_UNICODE_NOT_PRESENT_OFFSET, 9843 },
+  { 0x336a, G_UNICODE_NOT_PRESENT_OFFSET, 9849 },
+  { 0x336b, G_UNICODE_NOT_PRESENT_OFFSET, 9855 },
+  { 0x336c, G_UNICODE_NOT_PRESENT_OFFSET, 9861 },
+  { 0x336d, G_UNICODE_NOT_PRESENT_OFFSET, 9867 },
+  { 0x336e, G_UNICODE_NOT_PRESENT_OFFSET, 9873 },
+  { 0x336f, G_UNICODE_NOT_PRESENT_OFFSET, 9879 },
+  { 0x3370, G_UNICODE_NOT_PRESENT_OFFSET, 9885 },
+  { 0x3371, G_UNICODE_NOT_PRESENT_OFFSET, 9891 },
+  { 0x3372, G_UNICODE_NOT_PRESENT_OFFSET, 9895 },
+  { 0x3373, G_UNICODE_NOT_PRESENT_OFFSET, 9898 },
+  { 0x3374, G_UNICODE_NOT_PRESENT_OFFSET, 9901 },
+  { 0x3375, G_UNICODE_NOT_PRESENT_OFFSET, 9905 },
+  { 0x3376, G_UNICODE_NOT_PRESENT_OFFSET, 9908 },
+  { 0x337b, G_UNICODE_NOT_PRESENT_OFFSET, 9911 },
+  { 0x337c, G_UNICODE_NOT_PRESENT_OFFSET, 9918 },
+  { 0x337d, G_UNICODE_NOT_PRESENT_OFFSET, 9925 },
+  { 0x337e, G_UNICODE_NOT_PRESENT_OFFSET, 9932 },
+  { 0x337f, G_UNICODE_NOT_PRESENT_OFFSET, 9939 },
+  { 0x3380, G_UNICODE_NOT_PRESENT_OFFSET, 9952 },
+  { 0x3381, G_UNICODE_NOT_PRESENT_OFFSET, 9955 },
+  { 0x3382, G_UNICODE_NOT_PRESENT_OFFSET, 9958 },
+  { 0x3383, G_UNICODE_NOT_PRESENT_OFFSET, 9962 },
+  { 0x3384, G_UNICODE_NOT_PRESENT_OFFSET, 9965 },
+  { 0x3385, G_UNICODE_NOT_PRESENT_OFFSET, 9968 },
+  { 0x3386, G_UNICODE_NOT_PRESENT_OFFSET, 9971 },
+  { 0x3387, G_UNICODE_NOT_PRESENT_OFFSET, 9974 },
+  { 0x3388, G_UNICODE_NOT_PRESENT_OFFSET, 9977 },
+  { 0x3389, G_UNICODE_NOT_PRESENT_OFFSET, 9981 },
+  { 0x338a, G_UNICODE_NOT_PRESENT_OFFSET, 9986 },
+  { 0x338b, G_UNICODE_NOT_PRESENT_OFFSET, 9989 },
+  { 0x338c, G_UNICODE_NOT_PRESENT_OFFSET, 9992 },
+  { 0x338d, G_UNICODE_NOT_PRESENT_OFFSET, 9996 },
+  { 0x338e, G_UNICODE_NOT_PRESENT_OFFSET, 10000 },
+  { 0x338f, G_UNICODE_NOT_PRESENT_OFFSET, 10003 },
+  { 0x3390, G_UNICODE_NOT_PRESENT_OFFSET, 10006 },
+  { 0x3391, G_UNICODE_NOT_PRESENT_OFFSET, 10009 },
+  { 0x3392, G_UNICODE_NOT_PRESENT_OFFSET, 10013 },
+  { 0x3393, G_UNICODE_NOT_PRESENT_OFFSET, 10017 },
+  { 0x3394, G_UNICODE_NOT_PRESENT_OFFSET, 10021 },
+  { 0x3395, G_UNICODE_NOT_PRESENT_OFFSET, 10025 },
+  { 0x3396, G_UNICODE_NOT_PRESENT_OFFSET, 10029 },
+  { 0x3397, G_UNICODE_NOT_PRESENT_OFFSET, 10032 },
+  { 0x3398, G_UNICODE_NOT_PRESENT_OFFSET, 10035 },
+  { 0x3399, G_UNICODE_NOT_PRESENT_OFFSET, 10038 },
+  { 0x339a, G_UNICODE_NOT_PRESENT_OFFSET, 10041 },
+  { 0x339b, G_UNICODE_NOT_PRESENT_OFFSET, 10044 },
+  { 0x339c, G_UNICODE_NOT_PRESENT_OFFSET, 10048 },
+  { 0x339d, G_UNICODE_NOT_PRESENT_OFFSET, 10051 },
+  { 0x339e, G_UNICODE_NOT_PRESENT_OFFSET, 10054 },
+  { 0x339f, G_UNICODE_NOT_PRESENT_OFFSET, 10057 },
+  { 0x33a0, G_UNICODE_NOT_PRESENT_OFFSET, 10061 },
+  { 0x33a1, G_UNICODE_NOT_PRESENT_OFFSET, 10065 },
+  { 0x33a2, G_UNICODE_NOT_PRESENT_OFFSET, 10068 },
+  { 0x33a3, G_UNICODE_NOT_PRESENT_OFFSET, 10072 },
+  { 0x33a4, G_UNICODE_NOT_PRESENT_OFFSET, 10076 },
+  { 0x33a5, G_UNICODE_NOT_PRESENT_OFFSET, 10080 },
+  { 0x33a6, G_UNICODE_NOT_PRESENT_OFFSET, 10083 },
+  { 0x33a7, G_UNICODE_NOT_PRESENT_OFFSET, 10087 },
+  { 0x33a8, G_UNICODE_NOT_PRESENT_OFFSET, 10093 },
+  { 0x33a9, G_UNICODE_NOT_PRESENT_OFFSET, 10100 },
+  { 0x33aa, G_UNICODE_NOT_PRESENT_OFFSET, 10103 },
+  { 0x33ab, G_UNICODE_NOT_PRESENT_OFFSET, 10107 },
+  { 0x33ac, G_UNICODE_NOT_PRESENT_OFFSET, 10111 },
+  { 0x33ad, G_UNICODE_NOT_PRESENT_OFFSET, 10115 },
+  { 0x33ae, G_UNICODE_NOT_PRESENT_OFFSET, 10119 },
+  { 0x33af, G_UNICODE_NOT_PRESENT_OFFSET, 10127 },
+  { 0x33b0, G_UNICODE_NOT_PRESENT_OFFSET, 10136 },
+  { 0x33b1, G_UNICODE_NOT_PRESENT_OFFSET, 10139 },
+  { 0x33b2, G_UNICODE_NOT_PRESENT_OFFSET, 10142 },
+  { 0x33b3, G_UNICODE_NOT_PRESENT_OFFSET, 10146 },
+  { 0x33b4, G_UNICODE_NOT_PRESENT_OFFSET, 10149 },
+  { 0x33b5, G_UNICODE_NOT_PRESENT_OFFSET, 10152 },
+  { 0x33b6, G_UNICODE_NOT_PRESENT_OFFSET, 10155 },
+  { 0x33b7, G_UNICODE_NOT_PRESENT_OFFSET, 10159 },
+  { 0x33b8, G_UNICODE_NOT_PRESENT_OFFSET, 10162 },
+  { 0x33b9, G_UNICODE_NOT_PRESENT_OFFSET, 10165 },
+  { 0x33ba, G_UNICODE_NOT_PRESENT_OFFSET, 10168 },
+  { 0x33bb, G_UNICODE_NOT_PRESENT_OFFSET, 10171 },
+  { 0x33bc, G_UNICODE_NOT_PRESENT_OFFSET, 10174 },
+  { 0x33bd, G_UNICODE_NOT_PRESENT_OFFSET, 10178 },
+  { 0x33be, G_UNICODE_NOT_PRESENT_OFFSET, 10181 },
+  { 0x33bf, G_UNICODE_NOT_PRESENT_OFFSET, 10184 },
+  { 0x33c0, G_UNICODE_NOT_PRESENT_OFFSET, 10187 },
+  { 0x33c1, G_UNICODE_NOT_PRESENT_OFFSET, 10191 },
+  { 0x33c2, G_UNICODE_NOT_PRESENT_OFFSET, 10195 },
+  { 0x33c3, G_UNICODE_NOT_PRESENT_OFFSET, 10200 },
+  { 0x33c4, G_UNICODE_NOT_PRESENT_OFFSET, 10203 },
+  { 0x33c5, G_UNICODE_NOT_PRESENT_OFFSET, 10206 },
+  { 0x33c6, G_UNICODE_NOT_PRESENT_OFFSET, 10209 },
+  { 0x33c7, G_UNICODE_NOT_PRESENT_OFFSET, 10216 },
+  { 0x33c8, G_UNICODE_NOT_PRESENT_OFFSET, 10220 },
+  { 0x33c9, G_UNICODE_NOT_PRESENT_OFFSET, 10223 },
+  { 0x33ca, G_UNICODE_NOT_PRESENT_OFFSET, 10226 },
+  { 0x33cb, G_UNICODE_NOT_PRESENT_OFFSET, 10229 },
+  { 0x33cc, G_UNICODE_NOT_PRESENT_OFFSET, 10232 },
+  { 0x33cd, G_UNICODE_NOT_PRESENT_OFFSET, 10235 },
+  { 0x33ce, G_UNICODE_NOT_PRESENT_OFFSET, 10238 },
+  { 0x33cf, G_UNICODE_NOT_PRESENT_OFFSET, 10241 },
+  { 0x33d0, G_UNICODE_NOT_PRESENT_OFFSET, 10244 },
+  { 0x33d1, G_UNICODE_NOT_PRESENT_OFFSET, 10247 },
+  { 0x33d2, G_UNICODE_NOT_PRESENT_OFFSET, 10250 },
+  { 0x33d3, G_UNICODE_NOT_PRESENT_OFFSET, 10254 },
+  { 0x33d4, G_UNICODE_NOT_PRESENT_OFFSET, 10257 },
+  { 0x33d5, G_UNICODE_NOT_PRESENT_OFFSET, 10260 },
+  { 0x33d6, G_UNICODE_NOT_PRESENT_OFFSET, 10264 },
+  { 0x33d7, G_UNICODE_NOT_PRESENT_OFFSET, 10268 },
+  { 0x33d8, G_UNICODE_NOT_PRESENT_OFFSET, 10271 },
+  { 0x33d9, G_UNICODE_NOT_PRESENT_OFFSET, 10276 },
+  { 0x33da, G_UNICODE_NOT_PRESENT_OFFSET, 10280 },
+  { 0x33db, G_UNICODE_NOT_PRESENT_OFFSET, 10283 },
+  { 0x33dc, G_UNICODE_NOT_PRESENT_OFFSET, 10286 },
+  { 0x33dd, G_UNICODE_NOT_PRESENT_OFFSET, 10289 },
+  { 0x33e0, G_UNICODE_NOT_PRESENT_OFFSET, 10292 },
+  { 0x33e1, G_UNICODE_NOT_PRESENT_OFFSET, 10297 },
+  { 0x33e2, G_UNICODE_NOT_PRESENT_OFFSET, 10302 },
+  { 0x33e3, G_UNICODE_NOT_PRESENT_OFFSET, 10307 },
+  { 0x33e4, G_UNICODE_NOT_PRESENT_OFFSET, 10312 },
+  { 0x33e5, G_UNICODE_NOT_PRESENT_OFFSET, 10317 },
+  { 0x33e6, G_UNICODE_NOT_PRESENT_OFFSET, 10322 },
+  { 0x33e7, G_UNICODE_NOT_PRESENT_OFFSET, 10327 },
+  { 0x33e8, G_UNICODE_NOT_PRESENT_OFFSET, 10332 },
+  { 0x33e9, G_UNICODE_NOT_PRESENT_OFFSET, 10337 },
+  { 0x33ea, G_UNICODE_NOT_PRESENT_OFFSET, 10343 },
+  { 0x33eb, G_UNICODE_NOT_PRESENT_OFFSET, 10349 },
+  { 0x33ec, G_UNICODE_NOT_PRESENT_OFFSET, 10355 },
+  { 0x33ed, G_UNICODE_NOT_PRESENT_OFFSET, 10361 },
+  { 0x33ee, G_UNICODE_NOT_PRESENT_OFFSET, 10367 },
+  { 0x33ef, G_UNICODE_NOT_PRESENT_OFFSET, 10373 },
+  { 0x33f0, G_UNICODE_NOT_PRESENT_OFFSET, 10379 },
+  { 0x33f1, G_UNICODE_NOT_PRESENT_OFFSET, 10385 },
+  { 0x33f2, G_UNICODE_NOT_PRESENT_OFFSET, 10391 },
+  { 0x33f3, G_UNICODE_NOT_PRESENT_OFFSET, 10397 },
+  { 0x33f4, G_UNICODE_NOT_PRESENT_OFFSET, 10403 },
+  { 0x33f5, G_UNICODE_NOT_PRESENT_OFFSET, 10409 },
+  { 0x33f6, G_UNICODE_NOT_PRESENT_OFFSET, 10415 },
+  { 0x33f7, G_UNICODE_NOT_PRESENT_OFFSET, 10421 },
+  { 0x33f8, G_UNICODE_NOT_PRESENT_OFFSET, 10427 },
+  { 0x33f9, G_UNICODE_NOT_PRESENT_OFFSET, 10433 },
+  { 0x33fa, G_UNICODE_NOT_PRESENT_OFFSET, 10439 },
+  { 0x33fb, G_UNICODE_NOT_PRESENT_OFFSET, 10445 },
+  { 0x33fc, G_UNICODE_NOT_PRESENT_OFFSET, 10451 },
+  { 0x33fd, G_UNICODE_NOT_PRESENT_OFFSET, 10457 },
+  { 0x33fe, G_UNICODE_NOT_PRESENT_OFFSET, 10463 },
+  { 0xf900, 10469, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf901, 10473, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf902, 6535, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf903, 10477, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf904, 10481, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf905, 10485, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf906, 10489, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf907, 6751, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf908, 6751, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf909, 10493, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf90a, 6567, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf90b, 10497, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf90c, 10501, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf90d, 10505, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf90e, 10509, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf90f, 10513, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf910, 10517, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf911, 10521, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf912, 10525, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf913, 10529, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf914, 10533, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf915, 10537, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf916, 10541, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf917, 10545, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf918, 10549, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf919, 10553, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf91a, 10557, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf91b, 10561, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf91c, 10565, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf91d, 10569, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf91e, 10573, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf91f, 10577, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf920, 10581, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf921, 10585, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf922, 10589, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf923, 10593, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf924, 10597, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf925, 10601, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf926, 10605, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf927, 10609, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf928, 10613, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf929, 10617, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf92a, 10621, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf92b, 10625, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf92c, 10629, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf92d, 10633, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf92e, 10637, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf92f, 10641, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf930, 10645, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf931, 10649, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf932, 10653, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf933, 10657, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf934, 6399, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf935, 10661, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf936, 10665, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf937, 10669, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf938, 10673, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf939, 10677, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf93a, 10681, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf93b, 10685, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf93c, 10689, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf93d, 10693, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf93e, 10697, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf93f, 10701, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf940, 6691, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf941, 10705, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf942, 10709, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf943, 10713, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf944, 10717, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf945, 10721, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf946, 10725, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf947, 10729, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf948, 10733, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf949, 10737, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf94a, 10741, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf94b, 10745, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf94c, 10749, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf94d, 10753, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf94e, 10757, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf94f, 10761, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf950, 10765, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf951, 10769, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf952, 10773, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf953, 10777, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf954, 10781, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf955, 10785, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf956, 10789, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf957, 10793, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf958, 10797, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf959, 10801, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf95a, 10805, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf95b, 10809, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf95c, 10533, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf95d, 10813, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf95e, 10817, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf95f, 10821, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf960, 10825, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf961, 10829, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf962, 10833, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf963, 10837, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf964, 10841, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf965, 10845, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf966, 10849, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf967, 10853, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf968, 10857, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf969, 10861, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf96a, 10865, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf96b, 10869, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf96c, 10873, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf96d, 10877, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf96e, 10881, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf96f, 10885, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf970, 10889, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf971, 6543, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf972, 10893, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf973, 10897, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf974, 10901, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf975, 10905, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf976, 10909, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf977, 10913, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf978, 10917, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf979, 10921, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf97a, 10925, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf97b, 10929, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf97c, 10933, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf97d, 10937, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf97e, 10941, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf97f, 10945, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf980, 10949, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf981, 6051, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf982, 10953, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf983, 10957, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf984, 10961, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf985, 10965, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf986, 10969, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf987, 10973, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf988, 10977, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf989, 10981, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf98a, 5975, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf98b, 10985, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf98c, 10989, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf98d, 10993, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf98e, 10997, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf98f, 11001, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf990, 11005, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf991, 11009, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf992, 11013, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf993, 11017, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf994, 11021, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf995, 11025, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf996, 11029, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf997, 11033, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf998, 11037, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf999, 11041, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf99a, 11045, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf99b, 11049, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf99c, 11053, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf99d, 11057, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf99e, 11061, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf99f, 11065, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a0, 11069, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a1, 10885, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a2, 11073, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a3, 11077, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a4, 11081, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a5, 11085, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a6, 11089, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a7, 11093, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a8, 11097, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9a9, 11101, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9aa, 10821, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ab, 11105, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ac, 11109, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ad, 11113, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ae, 11117, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9af, 11121, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b0, 11125, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b1, 11129, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b2, 11133, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b3, 11137, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b4, 11141, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b5, 11145, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b6, 11149, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b7, 11153, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b8, 11157, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9b9, 11161, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ba, 11165, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9bb, 11169, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9bc, 11173, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9bd, 11177, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9be, 11181, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9bf, 10533, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c0, 11185, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c1, 11189, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c2, 11193, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c3, 11197, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c4, 6747, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c5, 11201, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c6, 11205, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c7, 11209, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c8, 11213, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9c9, 11217, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ca, 11221, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9cb, 11225, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9cc, 11229, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9cd, 11233, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ce, 11237, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9cf, 11241, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d0, 11245, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d1, 8199, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d2, 11249, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d3, 11253, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d4, 11257, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d5, 11261, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d6, 11265, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d7, 11269, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d8, 11273, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9d9, 11277, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9da, 11281, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9db, 10829, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9dc, 11285, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9dd, 11289, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9de, 11293, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9df, 11297, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e0, 11301, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e1, 11305, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e2, 11309, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e3, 11313, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e4, 11317, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e5, 11321, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e6, 11325, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e7, 11329, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e8, 11333, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9e9, 6563, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ea, 11337, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9eb, 11341, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ec, 11345, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ed, 11349, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ee, 11353, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ef, 11357, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f0, 11361, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f1, 11365, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f2, 11369, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f3, 11373, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f4, 11377, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f5, 11381, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f6, 11385, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f7, 6367, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f8, 11389, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9f9, 11393, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9fa, 11397, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9fb, 11401, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9fc, 11405, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9fd, 11409, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9fe, 11413, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xf9ff, 11417, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa00, 11421, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa01, 11425, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa02, 11429, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa03, 11433, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa04, 11437, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa05, 11441, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa06, 11445, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa07, 11449, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa08, 6475, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa09, 11453, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa0a, 6487, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa0b, 11457, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa0c, 11461, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa0d, 11465, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa10, 11469, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa12, 11473, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa15, 11477, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa16, 11481, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa17, 11485, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa18, 11489, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa19, 11493, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa1a, 11497, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa1b, 11501, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa1c, 11505, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa1d, 11509, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa1e, 6395, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa20, 11513, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa22, 11517, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa25, 11521, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa26, 11525, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa2a, 11529, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa2b, 11533, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa2c, 11537, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa2d, 11541, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa30, 11545, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa31, 11549, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa32, 11553, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa33, 11557, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa34, 11561, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa35, 11565, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa36, 11569, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa37, 11573, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa38, 11577, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa39, 11581, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa3a, 11585, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa3b, 11589, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa3c, 6079, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa3d, 11593, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa3e, 11597, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa3f, 11601, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa40, 11605, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa41, 11609, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa42, 11613, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa43, 11617, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa44, 11621, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa45, 11625, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa46, 11629, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa47, 11633, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa48, 11637, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa49, 11641, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa4a, 11645, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa4b, 11649, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa4c, 8219, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa4d, 11653, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa4e, 11657, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa4f, 11661, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa50, 11665, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa51, 8235, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa52, 11669, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa53, 11673, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa54, 11677, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa55, 11681, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa56, 11685, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa57, 11029, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa58, 11689, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa59, 11693, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa5a, 11697, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa5b, 11701, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa5c, 11705, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa5d, 11709, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa5e, 11709, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa5f, 11713, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa60, 11717, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa61, 11721, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa62, 11725, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa63, 11729, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa64, 11733, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa65, 11737, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa66, 11741, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa67, 11521, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa68, 11745, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa69, 11749, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfa6a, 11753, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb00, G_UNICODE_NOT_PRESENT_OFFSET, 11757 },
+  { 0xfb01, G_UNICODE_NOT_PRESENT_OFFSET, 11760 },
+  { 0xfb02, G_UNICODE_NOT_PRESENT_OFFSET, 11763 },
+  { 0xfb03, G_UNICODE_NOT_PRESENT_OFFSET, 11766 },
+  { 0xfb04, G_UNICODE_NOT_PRESENT_OFFSET, 11770 },
+  { 0xfb05, G_UNICODE_NOT_PRESENT_OFFSET, 11774 },
+  { 0xfb06, G_UNICODE_NOT_PRESENT_OFFSET, 11774 },
+  { 0xfb13, G_UNICODE_NOT_PRESENT_OFFSET, 11777 },
+  { 0xfb14, G_UNICODE_NOT_PRESENT_OFFSET, 11782 },
+  { 0xfb15, G_UNICODE_NOT_PRESENT_OFFSET, 11787 },
+  { 0xfb16, G_UNICODE_NOT_PRESENT_OFFSET, 11792 },
+  { 0xfb17, G_UNICODE_NOT_PRESENT_OFFSET, 11797 },
+  { 0xfb1d, 11802, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb1f, 11807, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb20, G_UNICODE_NOT_PRESENT_OFFSET, 11812 },
+  { 0xfb21, G_UNICODE_NOT_PRESENT_OFFSET, 5052 },
+  { 0xfb22, G_UNICODE_NOT_PRESENT_OFFSET, 5061 },
+  { 0xfb23, G_UNICODE_NOT_PRESENT_OFFSET, 11815 },
+  { 0xfb24, G_UNICODE_NOT_PRESENT_OFFSET, 11818 },
+  { 0xfb25, G_UNICODE_NOT_PRESENT_OFFSET, 11821 },
+  { 0xfb26, G_UNICODE_NOT_PRESENT_OFFSET, 11824 },
+  { 0xfb27, G_UNICODE_NOT_PRESENT_OFFSET, 11827 },
+  { 0xfb28, G_UNICODE_NOT_PRESENT_OFFSET, 11830 },
+  { 0xfb29, G_UNICODE_NOT_PRESENT_OFFSET, 4957 },
+  { 0xfb2a, 11833, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb2b, 11838, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb2c, 11843, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb2d, 11850, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb2e, 11857, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb2f, 11862, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb30, 11867, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb31, 11872, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb32, 11877, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb33, 11882, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb34, 11887, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb35, 11892, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb36, 11897, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb38, 11902, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb39, 11907, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb3a, 11912, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb3b, 11917, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb3c, 11922, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb3e, 11927, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb40, 11932, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb41, 11937, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb43, 11942, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb44, 11947, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb46, 11952, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb47, 11957, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb48, 11962, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb49, 11967, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb4a, 11972, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb4b, 11977, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb4c, 11982, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb4d, 11987, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb4e, 11992, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0xfb4f, G_UNICODE_NOT_PRESENT_OFFSET, 11997 },
+  { 0xfb50, G_UNICODE_NOT_PRESENT_OFFSET, 12002 },
+  { 0xfb51, G_UNICODE_NOT_PRESENT_OFFSET, 12002 },
+  { 0xfb52, G_UNICODE_NOT_PRESENT_OFFSET, 12005 },
+  { 0xfb53, G_UNICODE_NOT_PRESENT_OFFSET, 12005 },
+  { 0xfb54, G_UNICODE_NOT_PRESENT_OFFSET, 12005 },
+  { 0xfb55, G_UNICODE_NOT_PRESENT_OFFSET, 12005 },
+  { 0xfb56, G_UNICODE_NOT_PRESENT_OFFSET, 12008 },
+  { 0xfb57, G_UNICODE_NOT_PRESENT_OFFSET, 12008 },
+  { 0xfb58, G_UNICODE_NOT_PRESENT_OFFSET, 12008 },
+  { 0xfb59, G_UNICODE_NOT_PRESENT_OFFSET, 12008 },
+  { 0xfb5a, G_UNICODE_NOT_PRESENT_OFFSET, 12011 },
+  { 0xfb5b, G_UNICODE_NOT_PRESENT_OFFSET, 12011 },
+  { 0xfb5c, G_UNICODE_NOT_PRESENT_OFFSET, 12011 },
+  { 0xfb5d, G_UNICODE_NOT_PRESENT_OFFSET, 12011 },
+  { 0xfb5e, G_UNICODE_NOT_PRESENT_OFFSET, 12014 },
+  { 0xfb5f, G_UNICODE_NOT_PRESENT_OFFSET, 12014 },
+  { 0xfb60, G_UNICODE_NOT_PRESENT_OFFSET, 12014 },
+  { 0xfb61, G_UNICODE_NOT_PRESENT_OFFSET, 12014 },
+  { 0xfb62, G_UNICODE_NOT_PRESENT_OFFSET, 12017 },
+  { 0xfb63, G_UNICODE_NOT_PRESENT_OFFSET, 12017 },
+  { 0xfb64, G_UNICODE_NOT_PRESENT_OFFSET, 12017 },
+  { 0xfb65, G_UNICODE_NOT_PRESENT_OFFSET, 12017 },
+  { 0xfb66, G_UNICODE_NOT_PRESENT_OFFSET, 12020 },
+  { 0xfb67, G_UNICODE_NOT_PRESENT_OFFSET, 12020 },
+  { 0xfb68, G_UNICODE_NOT_PRESENT_OFFSET, 12020 },
+  { 0xfb69, G_UNICODE_NOT_PRESENT_OFFSET, 12020 },
+  { 0xfb6a, G_UNICODE_NOT_PRESENT_OFFSET, 12023 },
+  { 0xfb6b, G_UNICODE_NOT_PRESENT_OFFSET, 12023 },
+  { 0xfb6c, G_UNICODE_NOT_PRESENT_OFFSET, 12023 },
+  { 0xfb6d, G_UNICODE_NOT_PRESENT_OFFSET, 12023 },
+  { 0xfb6e, G_UNICODE_NOT_PRESENT_OFFSET, 12026 },
+  { 0xfb6f, G_UNICODE_NOT_PRESENT_OFFSET, 12026 },
+  { 0xfb70, G_UNICODE_NOT_PRESENT_OFFSET, 12026 },
+  { 0xfb71, G_UNICODE_NOT_PRESENT_OFFSET, 12026 },
+  { 0xfb72, G_UNICODE_NOT_PRESENT_OFFSET, 12029 },
+  { 0xfb73, G_UNICODE_NOT_PRESENT_OFFSET, 12029 },
+  { 0xfb74, G_UNICODE_NOT_PRESENT_OFFSET, 12029 },
+  { 0xfb75, G_UNICODE_NOT_PRESENT_OFFSET, 12029 },
+  { 0xfb76, G_UNICODE_NOT_PRESENT_OFFSET, 12032 },
+  { 0xfb77, G_UNICODE_NOT_PRESENT_OFFSET, 12032 },
+  { 0xfb78, G_UNICODE_NOT_PRESENT_OFFSET, 12032 },
+  { 0xfb79, G_UNICODE_NOT_PRESENT_OFFSET, 12032 },
+  { 0xfb7a, G_UNICODE_NOT_PRESENT_OFFSET, 12035 },
+  { 0xfb7b, G_UNICODE_NOT_PRESENT_OFFSET, 12035 },
+  { 0xfb7c, G_UNICODE_NOT_PRESENT_OFFSET, 12035 },
+  { 0xfb7d, G_UNICODE_NOT_PRESENT_OFFSET, 12035 },
+  { 0xfb7e, G_UNICODE_NOT_PRESENT_OFFSET, 12038 },
+  { 0xfb7f, G_UNICODE_NOT_PRESENT_OFFSET, 12038 },
+  { 0xfb80, G_UNICODE_NOT_PRESENT_OFFSET, 12038 },
+  { 0xfb81, G_UNICODE_NOT_PRESENT_OFFSET, 12038 },
+  { 0xfb82, G_UNICODE_NOT_PRESENT_OFFSET, 12041 },
+  { 0xfb83, G_UNICODE_NOT_PRESENT_OFFSET, 12041 },
+  { 0xfb84, G_UNICODE_NOT_PRESENT_OFFSET, 12044 },
+  { 0xfb85, G_UNICODE_NOT_PRESENT_OFFSET, 12044 },
+  { 0xfb86, G_UNICODE_NOT_PRESENT_OFFSET, 12047 },
+  { 0xfb87, G_UNICODE_NOT_PRESENT_OFFSET, 12047 },
+  { 0xfb88, G_UNICODE_NOT_PRESENT_OFFSET, 12050 },
+  { 0xfb89, G_UNICODE_NOT_PRESENT_OFFSET, 12050 },
+  { 0xfb8a, G_UNICODE_NOT_PRESENT_OFFSET, 12053 },
+  { 0xfb8b, G_UNICODE_NOT_PRESENT_OFFSET, 12053 },
+  { 0xfb8c, G_UNICODE_NOT_PRESENT_OFFSET, 12056 },
+  { 0xfb8d, G_UNICODE_NOT_PRESENT_OFFSET, 12056 },
+  { 0xfb8e, G_UNICODE_NOT_PRESENT_OFFSET, 12059 },
+  { 0xfb8f, G_UNICODE_NOT_PRESENT_OFFSET, 12059 },
+  { 0xfb90, G_UNICODE_NOT_PRESENT_OFFSET, 12059 },
+  { 0xfb91, G_UNICODE_NOT_PRESENT_OFFSET, 12059 },
+  { 0xfb92, G_UNICODE_NOT_PRESENT_OFFSET, 12062 },
+  { 0xfb93, G_UNICODE_NOT_PRESENT_OFFSET, 12062 },
+  { 0xfb94, G_UNICODE_NOT_PRESENT_OFFSET, 12062 },
+  { 0xfb95, G_UNICODE_NOT_PRESENT_OFFSET, 12062 },
+  { 0xfb96, G_UNICODE_NOT_PRESENT_OFFSET, 12065 },
+  { 0xfb97, G_UNICODE_NOT_PRESENT_OFFSET, 12065 },
+  { 0xfb98, G_UNICODE_NOT_PRESENT_OFFSET, 12065 },
+  { 0xfb99, G_UNICODE_NOT_PRESENT_OFFSET, 12065 },
+  { 0xfb9a, G_UNICODE_NOT_PRESENT_OFFSET, 12068 },
+  { 0xfb9b, G_UNICODE_NOT_PRESENT_OFFSET, 12068 },
+  { 0xfb9c, G_UNICODE_NOT_PRESENT_OFFSET, 12068 },
+  { 0xfb9d, G_UNICODE_NOT_PRESENT_OFFSET, 12068 },
+  { 0xfb9e, G_UNICODE_NOT_PRESENT_OFFSET, 12071 },
+  { 0xfb9f, G_UNICODE_NOT_PRESENT_OFFSET, 12071 },
+  { 0xfba0, G_UNICODE_NOT_PRESENT_OFFSET, 12074 },
+  { 0xfba1, G_UNICODE_NOT_PRESENT_OFFSET, 12074 },
+  { 0xfba2, G_UNICODE_NOT_PRESENT_OFFSET, 12074 },
+  { 0xfba3, G_UNICODE_NOT_PRESENT_OFFSET, 12074 },
+  { 0xfba4, G_UNICODE_NOT_PRESENT_OFFSET, 1718 },
+  { 0xfba5, G_UNICODE_NOT_PRESENT_OFFSET, 1718 },
+  { 0xfba6, G_UNICODE_NOT_PRESENT_OFFSET, 12077 },
+  { 0xfba7, G_UNICODE_NOT_PRESENT_OFFSET, 12077 },
+  { 0xfba8, G_UNICODE_NOT_PRESENT_OFFSET, 12077 },
+  { 0xfba9, G_UNICODE_NOT_PRESENT_OFFSET, 12077 },
+  { 0xfbaa, G_UNICODE_NOT_PRESENT_OFFSET, 12080 },
+  { 0xfbab, G_UNICODE_NOT_PRESENT_OFFSET, 12080 },
+  { 0xfbac, G_UNICODE_NOT_PRESENT_OFFSET, 12080 },
+  { 0xfbad, G_UNICODE_NOT_PRESENT_OFFSET, 12080 },
+  { 0xfbae, G_UNICODE_NOT_PRESENT_OFFSET, 12083 },
+  { 0xfbaf, G_UNICODE_NOT_PRESENT_OFFSET, 12083 },
+  { 0xfbb0, G_UNICODE_NOT_PRESENT_OFFSET, 1728 },
+  { 0xfbb1, G_UNICODE_NOT_PRESENT_OFFSET, 1728 },
+  { 0xfbd3, G_UNICODE_NOT_PRESENT_OFFSET, 12086 },
+  { 0xfbd4, G_UNICODE_NOT_PRESENT_OFFSET, 12086 },
+  { 0xfbd5, G_UNICODE_NOT_PRESENT_OFFSET, 12086 },
+  { 0xfbd6, G_UNICODE_NOT_PRESENT_OFFSET, 12086 },
+  { 0xfbd7, G_UNICODE_NOT_PRESENT_OFFSET, 12089 },
+  { 0xfbd8, G_UNICODE_NOT_PRESENT_OFFSET, 12089 },
+  { 0xfbd9, G_UNICODE_NOT_PRESENT_OFFSET, 12092 },
+  { 0xfbda, G_UNICODE_NOT_PRESENT_OFFSET, 12092 },
+  { 0xfbdb, G_UNICODE_NOT_PRESENT_OFFSET, 12095 },
+  { 0xfbdc, G_UNICODE_NOT_PRESENT_OFFSET, 12095 },
+  { 0xfbdd, G_UNICODE_NOT_PRESENT_OFFSET, 1708 },
+  { 0xfbde, G_UNICODE_NOT_PRESENT_OFFSET, 12098 },
+  { 0xfbdf, G_UNICODE_NOT_PRESENT_OFFSET, 12098 },
+  { 0xfbe0, G_UNICODE_NOT_PRESENT_OFFSET, 12101 },
+  { 0xfbe1, G_UNICODE_NOT_PRESENT_OFFSET, 12101 },
+  { 0xfbe2, G_UNICODE_NOT_PRESENT_OFFSET, 12104 },
+  { 0xfbe3, G_UNICODE_NOT_PRESENT_OFFSET, 12104 },
+  { 0xfbe4, G_UNICODE_NOT_PRESENT_OFFSET, 12107 },
+  { 0xfbe5, G_UNICODE_NOT_PRESENT_OFFSET, 12107 },
+  { 0xfbe6, G_UNICODE_NOT_PRESENT_OFFSET, 12107 },
+  { 0xfbe7, G_UNICODE_NOT_PRESENT_OFFSET, 12107 },
+  { 0xfbe8, G_UNICODE_NOT_PRESENT_OFFSET, 12110 },
+  { 0xfbe9, G_UNICODE_NOT_PRESENT_OFFSET, 12110 },
+  { 0xfbea, G_UNICODE_NOT_PRESENT_OFFSET, 12113 },
+  { 0xfbeb, G_UNICODE_NOT_PRESENT_OFFSET, 12113 },
+  { 0xfbec, G_UNICODE_NOT_PRESENT_OFFSET, 12120 },
+  { 0xfbed, G_UNICODE_NOT_PRESENT_OFFSET, 12120 },
+  { 0xfbee, G_UNICODE_NOT_PRESENT_OFFSET, 12127 },
+  { 0xfbef, G_UNICODE_NOT_PRESENT_OFFSET, 12127 },
+  { 0xfbf0, G_UNICODE_NOT_PRESENT_OFFSET, 12134 },
+  { 0xfbf1, G_UNICODE_NOT_PRESENT_OFFSET, 12134 },
+  { 0xfbf2, G_UNICODE_NOT_PRESENT_OFFSET, 12141 },
+  { 0xfbf3, G_UNICODE_NOT_PRESENT_OFFSET, 12141 },
+  { 0xfbf4, G_UNICODE_NOT_PRESENT_OFFSET, 12148 },
+  { 0xfbf5, G_UNICODE_NOT_PRESENT_OFFSET, 12148 },
+  { 0xfbf6, G_UNICODE_NOT_PRESENT_OFFSET, 12155 },
+  { 0xfbf7, G_UNICODE_NOT_PRESENT_OFFSET, 12155 },
+  { 0xfbf8, G_UNICODE_NOT_PRESENT_OFFSET, 12155 },
+  { 0xfbf9, G_UNICODE_NOT_PRESENT_OFFSET, 12162 },
+  { 0xfbfa, G_UNICODE_NOT_PRESENT_OFFSET, 12162 },
+  { 0xfbfb, G_UNICODE_NOT_PRESENT_OFFSET, 12162 },
+  { 0xfbfc, G_UNICODE_NOT_PRESENT_OFFSET, 12169 },
+  { 0xfbfd, G_UNICODE_NOT_PRESENT_OFFSET, 12169 },
+  { 0xfbfe, G_UNICODE_NOT_PRESENT_OFFSET, 12169 },
+  { 0xfbff, G_UNICODE_NOT_PRESENT_OFFSET, 12169 },
+  { 0xfc00, G_UNICODE_NOT_PRESENT_OFFSET, 12172 },
+  { 0xfc01, G_UNICODE_NOT_PRESENT_OFFSET, 12179 },
+  { 0xfc02, G_UNICODE_NOT_PRESENT_OFFSET, 12186 },
+  { 0xfc03, G_UNICODE_NOT_PRESENT_OFFSET, 12162 },
+  { 0xfc04, G_UNICODE_NOT_PRESENT_OFFSET, 12193 },
+  { 0xfc05, G_UNICODE_NOT_PRESENT_OFFSET, 12200 },
+  { 0xfc06, G_UNICODE_NOT_PRESENT_OFFSET, 12205 },
+  { 0xfc07, G_UNICODE_NOT_PRESENT_OFFSET, 12210 },
+  { 0xfc08, G_UNICODE_NOT_PRESENT_OFFSET, 12215 },
+  { 0xfc09, G_UNICODE_NOT_PRESENT_OFFSET, 12220 },
+  { 0xfc0a, G_UNICODE_NOT_PRESENT_OFFSET, 12225 },
+  { 0xfc0b, G_UNICODE_NOT_PRESENT_OFFSET, 12230 },
+  { 0xfc0c, G_UNICODE_NOT_PRESENT_OFFSET, 12235 },
+  { 0xfc0d, G_UNICODE_NOT_PRESENT_OFFSET, 12240 },
+  { 0xfc0e, G_UNICODE_NOT_PRESENT_OFFSET, 12245 },
+  { 0xfc0f, G_UNICODE_NOT_PRESENT_OFFSET, 12250 },
+  { 0xfc10, G_UNICODE_NOT_PRESENT_OFFSET, 12255 },
+  { 0xfc11, G_UNICODE_NOT_PRESENT_OFFSET, 12260 },
+  { 0xfc12, G_UNICODE_NOT_PRESENT_OFFSET, 12265 },
+  { 0xfc13, G_UNICODE_NOT_PRESENT_OFFSET, 12270 },
+  { 0xfc14, G_UNICODE_NOT_PRESENT_OFFSET, 12275 },
+  { 0xfc15, G_UNICODE_NOT_PRESENT_OFFSET, 12280 },
+  { 0xfc16, G_UNICODE_NOT_PRESENT_OFFSET, 12285 },
+  { 0xfc17, G_UNICODE_NOT_PRESENT_OFFSET, 12290 },
+  { 0xfc18, G_UNICODE_NOT_PRESENT_OFFSET, 12295 },
+  { 0xfc19, G_UNICODE_NOT_PRESENT_OFFSET, 12300 },
+  { 0xfc1a, G_UNICODE_NOT_PRESENT_OFFSET, 12305 },
+  { 0xfc1b, G_UNICODE_NOT_PRESENT_OFFSET, 12310 },
+  { 0xfc1c, G_UNICODE_NOT_PRESENT_OFFSET, 12315 },
+  { 0xfc1d, G_UNICODE_NOT_PRESENT_OFFSET, 12320 },
+  { 0xfc1e, G_UNICODE_NOT_PRESENT_OFFSET, 12325 },
+  { 0xfc1f, G_UNICODE_NOT_PRESENT_OFFSET, 12330 },
+  { 0xfc20, G_UNICODE_NOT_PRESENT_OFFSET, 12335 },
+  { 0xfc21, G_UNICODE_NOT_PRESENT_OFFSET, 12340 },
+  { 0xfc22, G_UNICODE_NOT_PRESENT_OFFSET, 12345 },
+  { 0xfc23, G_UNICODE_NOT_PRESENT_OFFSET, 12350 },
+  { 0xfc24, G_UNICODE_NOT_PRESENT_OFFSET, 12355 },
+  { 0xfc25, G_UNICODE_NOT_PRESENT_OFFSET, 12360 },
+  { 0xfc26, G_UNICODE_NOT_PRESENT_OFFSET, 12365 },
+  { 0xfc27, G_UNICODE_NOT_PRESENT_OFFSET, 12370 },
+  { 0xfc28, G_UNICODE_NOT_PRESENT_OFFSET, 12375 },
+  { 0xfc29, G_UNICODE_NOT_PRESENT_OFFSET, 12380 },
+  { 0xfc2a, G_UNICODE_NOT_PRESENT_OFFSET, 12385 },
+  { 0xfc2b, G_UNICODE_NOT_PRESENT_OFFSET, 12390 },
+  { 0xfc2c, G_UNICODE_NOT_PRESENT_OFFSET, 12395 },
+  { 0xfc2d, G_UNICODE_NOT_PRESENT_OFFSET, 12400 },
+  { 0xfc2e, G_UNICODE_NOT_PRESENT_OFFSET, 12405 },
+  { 0xfc2f, G_UNICODE_NOT_PRESENT_OFFSET, 12410 },
+  { 0xfc30, G_UNICODE_NOT_PRESENT_OFFSET, 12415 },
+  { 0xfc31, G_UNICODE_NOT_PRESENT_OFFSET, 12420 },
+  { 0xfc32, G_UNICODE_NOT_PRESENT_OFFSET, 12425 },
+  { 0xfc33, G_UNICODE_NOT_PRESENT_OFFSET, 12430 },
+  { 0xfc34, G_UNICODE_NOT_PRESENT_OFFSET, 12435 },
+  { 0xfc35, G_UNICODE_NOT_PRESENT_OFFSET, 12440 },
+  { 0xfc36, G_UNICODE_NOT_PRESENT_OFFSET, 12445 },
+  { 0xfc37, G_UNICODE_NOT_PRESENT_OFFSET, 12450 },
+  { 0xfc38, G_UNICODE_NOT_PRESENT_OFFSET, 12455 },
+  { 0xfc39, G_UNICODE_NOT_PRESENT_OFFSET, 12460 },
+  { 0xfc3a, G_UNICODE_NOT_PRESENT_OFFSET, 12465 },
+  { 0xfc3b, G_UNICODE_NOT_PRESENT_OFFSET, 12470 },
+  { 0xfc3c, G_UNICODE_NOT_PRESENT_OFFSET, 12475 },
+  { 0xfc3d, G_UNICODE_NOT_PRESENT_OFFSET, 12480 },
+  { 0xfc3e, G_UNICODE_NOT_PRESENT_OFFSET, 12485 },
+  { 0xfc3f, G_UNICODE_NOT_PRESENT_OFFSET, 12490 },
+  { 0xfc40, G_UNICODE_NOT_PRESENT_OFFSET, 12495 },
+  { 0xfc41, G_UNICODE_NOT_PRESENT_OFFSET, 12500 },
+  { 0xfc42, G_UNICODE_NOT_PRESENT_OFFSET, 12505 },
+  { 0xfc43, G_UNICODE_NOT_PRESENT_OFFSET, 12510 },
+  { 0xfc44, G_UNICODE_NOT_PRESENT_OFFSET, 12515 },
+  { 0xfc45, G_UNICODE_NOT_PRESENT_OFFSET, 12520 },
+  { 0xfc46, G_UNICODE_NOT_PRESENT_OFFSET, 12525 },
+  { 0xfc47, G_UNICODE_NOT_PRESENT_OFFSET, 12530 },
+  { 0xfc48, G_UNICODE_NOT_PRESENT_OFFSET, 12535 },
+  { 0xfc49, G_UNICODE_NOT_PRESENT_OFFSET, 12540 },
+  { 0xfc4a, G_UNICODE_NOT_PRESENT_OFFSET, 12545 },
+  { 0xfc4b, G_UNICODE_NOT_PRESENT_OFFSET, 12550 },
+  { 0xfc4c, G_UNICODE_NOT_PRESENT_OFFSET, 12555 },
+  { 0xfc4d, G_UNICODE_NOT_PRESENT_OFFSET, 12560 },
+  { 0xfc4e, G_UNICODE_NOT_PRESENT_OFFSET, 12565 },
+  { 0xfc4f, G_UNICODE_NOT_PRESENT_OFFSET, 12570 },
+  { 0xfc50, G_UNICODE_NOT_PRESENT_OFFSET, 12575 },
+  { 0xfc51, G_UNICODE_NOT_PRESENT_OFFSET, 12580 },
+  { 0xfc52, G_UNICODE_NOT_PRESENT_OFFSET, 12585 },
+  { 0xfc53, G_UNICODE_NOT_PRESENT_OFFSET, 12590 },
+  { 0xfc54, G_UNICODE_NOT_PRESENT_OFFSET, 12595 },
+  { 0xfc55, G_UNICODE_NOT_PRESENT_OFFSET, 12600 },
+  { 0xfc56, G_UNICODE_NOT_PRESENT_OFFSET, 12605 },
+  { 0xfc57, G_UNICODE_NOT_PRESENT_OFFSET, 12610 },
+  { 0xfc58, G_UNICODE_NOT_PRESENT_OFFSET, 12615 },
+  { 0xfc59, G_UNICODE_NOT_PRESENT_OFFSET, 12620 },
+  { 0xfc5a, G_UNICODE_NOT_PRESENT_OFFSET, 12625 },
+  { 0xfc5b, G_UNICODE_NOT_PRESENT_OFFSET, 12630 },
+  { 0xfc5c, G_UNICODE_NOT_PRESENT_OFFSET, 12635 },
+  { 0xfc5d, G_UNICODE_NOT_PRESENT_OFFSET, 12640 },
+  { 0xfc5e, G_UNICODE_NOT_PRESENT_OFFSET, 12645 },
+  { 0xfc5f, G_UNICODE_NOT_PRESENT_OFFSET, 12651 },
+  { 0xfc60, G_UNICODE_NOT_PRESENT_OFFSET, 12657 },
+  { 0xfc61, G_UNICODE_NOT_PRESENT_OFFSET, 12663 },
+  { 0xfc62, G_UNICODE_NOT_PRESENT_OFFSET, 12669 },
+  { 0xfc63, G_UNICODE_NOT_PRESENT_OFFSET, 12675 },
+  { 0xfc64, G_UNICODE_NOT_PRESENT_OFFSET, 12681 },
+  { 0xfc65, G_UNICODE_NOT_PRESENT_OFFSET, 12688 },
+  { 0xfc66, G_UNICODE_NOT_PRESENT_OFFSET, 12186 },
+  { 0xfc67, G_UNICODE_NOT_PRESENT_OFFSET, 12695 },
+  { 0xfc68, G_UNICODE_NOT_PRESENT_OFFSET, 12162 },
+  { 0xfc69, G_UNICODE_NOT_PRESENT_OFFSET, 12193 },
+  { 0xfc6a, G_UNICODE_NOT_PRESENT_OFFSET, 12702 },
+  { 0xfc6b, G_UNICODE_NOT_PRESENT_OFFSET, 12707 },
+  { 0xfc6c, G_UNICODE_NOT_PRESENT_OFFSET, 12215 },
+  { 0xfc6d, G_UNICODE_NOT_PRESENT_OFFSET, 12712 },
+  { 0xfc6e, G_UNICODE_NOT_PRESENT_OFFSET, 12220 },
+  { 0xfc6f, G_UNICODE_NOT_PRESENT_OFFSET, 12225 },
+  { 0xfc70, G_UNICODE_NOT_PRESENT_OFFSET, 12717 },
+  { 0xfc71, G_UNICODE_NOT_PRESENT_OFFSET, 12722 },
+  { 0xfc72, G_UNICODE_NOT_PRESENT_OFFSET, 12245 },
+  { 0xfc73, G_UNICODE_NOT_PRESENT_OFFSET, 12727 },
+  { 0xfc74, G_UNICODE_NOT_PRESENT_OFFSET, 12250 },
+  { 0xfc75, G_UNICODE_NOT_PRESENT_OFFSET, 12255 },
+  { 0xfc76, G_UNICODE_NOT_PRESENT_OFFSET, 12732 },
+  { 0xfc77, G_UNICODE_NOT_PRESENT_OFFSET, 12737 },
+  { 0xfc78, G_UNICODE_NOT_PRESENT_OFFSET, 12265 },
+  { 0xfc79, G_UNICODE_NOT_PRESENT_OFFSET, 12742 },
+  { 0xfc7a, G_UNICODE_NOT_PRESENT_OFFSET, 12270 },
+  { 0xfc7b, G_UNICODE_NOT_PRESENT_OFFSET, 12275 },
+  { 0xfc7c, G_UNICODE_NOT_PRESENT_OFFSET, 12420 },
+  { 0xfc7d, G_UNICODE_NOT_PRESENT_OFFSET, 12425 },
+  { 0xfc7e, G_UNICODE_NOT_PRESENT_OFFSET, 12440 },
+  { 0xfc7f, G_UNICODE_NOT_PRESENT_OFFSET, 12445 },
+  { 0xfc80, G_UNICODE_NOT_PRESENT_OFFSET, 12450 },
+  { 0xfc81, G_UNICODE_NOT_PRESENT_OFFSET, 12470 },
+  { 0xfc82, G_UNICODE_NOT_PRESENT_OFFSET, 12475 },
+  { 0xfc83, G_UNICODE_NOT_PRESENT_OFFSET, 12480 },
+  { 0xfc84, G_UNICODE_NOT_PRESENT_OFFSET, 12485 },
+  { 0xfc85, G_UNICODE_NOT_PRESENT_OFFSET, 12505 },
+  { 0xfc86, G_UNICODE_NOT_PRESENT_OFFSET, 12510 },
+  { 0xfc87, G_UNICODE_NOT_PRESENT_OFFSET, 12515 },
+  { 0xfc88, G_UNICODE_NOT_PRESENT_OFFSET, 12747 },
+  { 0xfc89, G_UNICODE_NOT_PRESENT_OFFSET, 12535 },
+  { 0xfc8a, G_UNICODE_NOT_PRESENT_OFFSET, 12752 },
+  { 0xfc8b, G_UNICODE_NOT_PRESENT_OFFSET, 12757 },
+  { 0xfc8c, G_UNICODE_NOT_PRESENT_OFFSET, 12565 },
+  { 0xfc8d, G_UNICODE_NOT_PRESENT_OFFSET, 12762 },
+  { 0xfc8e, G_UNICODE_NOT_PRESENT_OFFSET, 12570 },
+  { 0xfc8f, G_UNICODE_NOT_PRESENT_OFFSET, 12575 },
+  { 0xfc90, G_UNICODE_NOT_PRESENT_OFFSET, 12640 },
+  { 0xfc91, G_UNICODE_NOT_PRESENT_OFFSET, 12767 },
+  { 0xfc92, G_UNICODE_NOT_PRESENT_OFFSET, 12772 },
+  { 0xfc93, G_UNICODE_NOT_PRESENT_OFFSET, 12615 },
+  { 0xfc94, G_UNICODE_NOT_PRESENT_OFFSET, 12777 },
+  { 0xfc95, G_UNICODE_NOT_PRESENT_OFFSET, 12620 },
+  { 0xfc96, G_UNICODE_NOT_PRESENT_OFFSET, 12625 },
+  { 0xfc97, G_UNICODE_NOT_PRESENT_OFFSET, 12172 },
+  { 0xfc98, G_UNICODE_NOT_PRESENT_OFFSET, 12179 },
+  { 0xfc99, G_UNICODE_NOT_PRESENT_OFFSET, 12782 },
+  { 0xfc9a, G_UNICODE_NOT_PRESENT_OFFSET, 12186 },
+  { 0xfc9b, G_UNICODE_NOT_PRESENT_OFFSET, 12789 },
+  { 0xfc9c, G_UNICODE_NOT_PRESENT_OFFSET, 12200 },
+  { 0xfc9d, G_UNICODE_NOT_PRESENT_OFFSET, 12205 },
+  { 0xfc9e, G_UNICODE_NOT_PRESENT_OFFSET, 12210 },
+  { 0xfc9f, G_UNICODE_NOT_PRESENT_OFFSET, 12215 },
+  { 0xfca0, G_UNICODE_NOT_PRESENT_OFFSET, 12796 },
+  { 0xfca1, G_UNICODE_NOT_PRESENT_OFFSET, 12230 },
+  { 0xfca2, G_UNICODE_NOT_PRESENT_OFFSET, 12235 },
+  { 0xfca3, G_UNICODE_NOT_PRESENT_OFFSET, 12240 },
+  { 0xfca4, G_UNICODE_NOT_PRESENT_OFFSET, 12245 },
+  { 0xfca5, G_UNICODE_NOT_PRESENT_OFFSET, 12801 },
+  { 0xfca6, G_UNICODE_NOT_PRESENT_OFFSET, 12265 },
+  { 0xfca7, G_UNICODE_NOT_PRESENT_OFFSET, 12280 },
+  { 0xfca8, G_UNICODE_NOT_PRESENT_OFFSET, 12285 },
+  { 0xfca9, G_UNICODE_NOT_PRESENT_OFFSET, 12290 },
+  { 0xfcaa, G_UNICODE_NOT_PRESENT_OFFSET, 12295 },
+  { 0xfcab, G_UNICODE_NOT_PRESENT_OFFSET, 12300 },
+  { 0xfcac, G_UNICODE_NOT_PRESENT_OFFSET, 12310 },
+  { 0xfcad, G_UNICODE_NOT_PRESENT_OFFSET, 12315 },
+  { 0xfcae, G_UNICODE_NOT_PRESENT_OFFSET, 12320 },
+  { 0xfcaf, G_UNICODE_NOT_PRESENT_OFFSET, 12325 },
+  { 0xfcb0, G_UNICODE_NOT_PRESENT_OFFSET, 12330 },
+  { 0xfcb1, G_UNICODE_NOT_PRESENT_OFFSET, 12335 },
+  { 0xfcb2, G_UNICODE_NOT_PRESENT_OFFSET, 12806 },
+  { 0xfcb3, G_UNICODE_NOT_PRESENT_OFFSET, 12340 },
+  { 0xfcb4, G_UNICODE_NOT_PRESENT_OFFSET, 12345 },
+  { 0xfcb5, G_UNICODE_NOT_PRESENT_OFFSET, 12350 },
+  { 0xfcb6, G_UNICODE_NOT_PRESENT_OFFSET, 12355 },
+  { 0xfcb7, G_UNICODE_NOT_PRESENT_OFFSET, 12360 },
+  { 0xfcb8, G_UNICODE_NOT_PRESENT_OFFSET, 12365 },
+  { 0xfcb9, G_UNICODE_NOT_PRESENT_OFFSET, 12375 },
+  { 0xfcba, G_UNICODE_NOT_PRESENT_OFFSET, 12380 },
+  { 0xfcbb, G_UNICODE_NOT_PRESENT_OFFSET, 12385 },
+  { 0xfcbc, G_UNICODE_NOT_PRESENT_OFFSET, 12390 },
+  { 0xfcbd, G_UNICODE_NOT_PRESENT_OFFSET, 12395 },
+  { 0xfcbe, G_UNICODE_NOT_PRESENT_OFFSET, 12400 },
+  { 0xfcbf, G_UNICODE_NOT_PRESENT_OFFSET, 12405 },
+  { 0xfcc0, G_UNICODE_NOT_PRESENT_OFFSET, 12410 },
+  { 0xfcc1, G_UNICODE_NOT_PRESENT_OFFSET, 12415 },
+  { 0xfcc2, G_UNICODE_NOT_PRESENT_OFFSET, 12430 },
+  { 0xfcc3, G_UNICODE_NOT_PRESENT_OFFSET, 12435 },
+  { 0xfcc4, G_UNICODE_NOT_PRESENT_OFFSET, 12455 },
+  { 0xfcc5, G_UNICODE_NOT_PRESENT_OFFSET, 12460 },
+  { 0xfcc6, G_UNICODE_NOT_PRESENT_OFFSET, 12465 },
+  { 0xfcc7, G_UNICODE_NOT_PRESENT_OFFSET, 12470 },
+  { 0xfcc8, G_UNICODE_NOT_PRESENT_OFFSET, 12475 },
+  { 0xfcc9, G_UNICODE_NOT_PRESENT_OFFSET, 12490 },
+  { 0xfcca, G_UNICODE_NOT_PRESENT_OFFSET, 12495 },
+  { 0xfccb, G_UNICODE_NOT_PRESENT_OFFSET, 12500 },
+  { 0xfccc, G_UNICODE_NOT_PRESENT_OFFSET, 12505 },
+  { 0xfccd, G_UNICODE_NOT_PRESENT_OFFSET, 12811 },
+  { 0xfcce, G_UNICODE_NOT_PRESENT_OFFSET, 12520 },
+  { 0xfccf, G_UNICODE_NOT_PRESENT_OFFSET, 12525 },
+  { 0xfcd0, G_UNICODE_NOT_PRESENT_OFFSET, 12530 },
+  { 0xfcd1, G_UNICODE_NOT_PRESENT_OFFSET, 12535 },
+  { 0xfcd2, G_UNICODE_NOT_PRESENT_OFFSET, 12550 },
+  { 0xfcd3, G_UNICODE_NOT_PRESENT_OFFSET, 12555 },
+  { 0xfcd4, G_UNICODE_NOT_PRESENT_OFFSET, 12560 },
+  { 0xfcd5, G_UNICODE_NOT_PRESENT_OFFSET, 12565 },
+  { 0xfcd6, G_UNICODE_NOT_PRESENT_OFFSET, 12816 },
+  { 0xfcd7, G_UNICODE_NOT_PRESENT_OFFSET, 12580 },
+  { 0xfcd8, G_UNICODE_NOT_PRESENT_OFFSET, 12585 },
+  { 0xfcd9, G_UNICODE_NOT_PRESENT_OFFSET, 12821 },
+  { 0xfcda, G_UNICODE_NOT_PRESENT_OFFSET, 12600 },
+  { 0xfcdb, G_UNICODE_NOT_PRESENT_OFFSET, 12605 },
+  { 0xfcdc, G_UNICODE_NOT_PRESENT_OFFSET, 12610 },
+  { 0xfcdd, G_UNICODE_NOT_PRESENT_OFFSET, 12615 },
+  { 0xfcde, G_UNICODE_NOT_PRESENT_OFFSET, 12826 },
+  { 0xfcdf, G_UNICODE_NOT_PRESENT_OFFSET, 12186 },
+  { 0xfce0, G_UNICODE_NOT_PRESENT_OFFSET, 12789 },
+  { 0xfce1, G_UNICODE_NOT_PRESENT_OFFSET, 12215 },
+  { 0xfce2, G_UNICODE_NOT_PRESENT_OFFSET, 12796 },
+  { 0xfce3, G_UNICODE_NOT_PRESENT_OFFSET, 12245 },
+  { 0xfce4, G_UNICODE_NOT_PRESENT_OFFSET, 12801 },
+  { 0xfce5, G_UNICODE_NOT_PRESENT_OFFSET, 12265 },
+  { 0xfce6, G_UNICODE_NOT_PRESENT_OFFSET, 12831 },
+  { 0xfce7, G_UNICODE_NOT_PRESENT_OFFSET, 12330 },
+  { 0xfce8, G_UNICODE_NOT_PRESENT_OFFSET, 12836 },
+  { 0xfce9, G_UNICODE_NOT_PRESENT_OFFSET, 12841 },
+  { 0xfcea, G_UNICODE_NOT_PRESENT_OFFSET, 12846 },
+  { 0xfceb, G_UNICODE_NOT_PRESENT_OFFSET, 12470 },
+  { 0xfcec, G_UNICODE_NOT_PRESENT_OFFSET, 12475 },
+  { 0xfced, G_UNICODE_NOT_PRESENT_OFFSET, 12505 },
+  { 0xfcee, G_UNICODE_NOT_PRESENT_OFFSET, 12565 },
+  { 0xfcef, G_UNICODE_NOT_PRESENT_OFFSET, 12816 },
+  { 0xfcf0, G_UNICODE_NOT_PRESENT_OFFSET, 12615 },
+  { 0xfcf1, G_UNICODE_NOT_PRESENT_OFFSET, 12826 },
+  { 0xfcf2, G_UNICODE_NOT_PRESENT_OFFSET, 12851 },
+  { 0xfcf3, G_UNICODE_NOT_PRESENT_OFFSET, 12858 },
+  { 0xfcf4, G_UNICODE_NOT_PRESENT_OFFSET, 12865 },
+  { 0xfcf5, G_UNICODE_NOT_PRESENT_OFFSET, 12872 },
+  { 0xfcf6, G_UNICODE_NOT_PRESENT_OFFSET, 12877 },
+  { 0xfcf7, G_UNICODE_NOT_PRESENT_OFFSET, 12882 },
+  { 0xfcf8, G_UNICODE_NOT_PRESENT_OFFSET, 12887 },
+  { 0xfcf9, G_UNICODE_NOT_PRESENT_OFFSET, 12892 },
+  { 0xfcfa, G_UNICODE_NOT_PRESENT_OFFSET, 12897 },
+  { 0xfcfb, G_UNICODE_NOT_PRESENT_OFFSET, 12902 },
+  { 0xfcfc, G_UNICODE_NOT_PRESENT_OFFSET, 12907 },
+  { 0xfcfd, G_UNICODE_NOT_PRESENT_OFFSET, 12912 },
+  { 0xfcfe, G_UNICODE_NOT_PRESENT_OFFSET, 12917 },
+  { 0xfcff, G_UNICODE_NOT_PRESENT_OFFSET, 12922 },
+  { 0xfd00, G_UNICODE_NOT_PRESENT_OFFSET, 12927 },
+  { 0xfd01, G_UNICODE_NOT_PRESENT_OFFSET, 12932 },
+  { 0xfd02, G_UNICODE_NOT_PRESENT_OFFSET, 12937 },
+  { 0xfd03, G_UNICODE_NOT_PRESENT_OFFSET, 12942 },
+  { 0xfd04, G_UNICODE_NOT_PRESENT_OFFSET, 12947 },
+  { 0xfd05, G_UNICODE_NOT_PRESENT_OFFSET, 12952 },
+  { 0xfd06, G_UNICODE_NOT_PRESENT_OFFSET, 12957 },
+  { 0xfd07, G_UNICODE_NOT_PRESENT_OFFSET, 12962 },
+  { 0xfd08, G_UNICODE_NOT_PRESENT_OFFSET, 12967 },
+  { 0xfd09, G_UNICODE_NOT_PRESENT_OFFSET, 12972 },
+  { 0xfd0a, G_UNICODE_NOT_PRESENT_OFFSET, 12977 },
+  { 0xfd0b, G_UNICODE_NOT_PRESENT_OFFSET, 12982 },
+  { 0xfd0c, G_UNICODE_NOT_PRESENT_OFFSET, 12841 },
+  { 0xfd0d, G_UNICODE_NOT_PRESENT_OFFSET, 12987 },
+  { 0xfd0e, G_UNICODE_NOT_PRESENT_OFFSET, 12992 },
+  { 0xfd0f, G_UNICODE_NOT_PRESENT_OFFSET, 12997 },
+  { 0xfd10, G_UNICODE_NOT_PRESENT_OFFSET, 13002 },
+  { 0xfd11, G_UNICODE_NOT_PRESENT_OFFSET, 12872 },
+  { 0xfd12, G_UNICODE_NOT_PRESENT_OFFSET, 12877 },
+  { 0xfd13, G_UNICODE_NOT_PRESENT_OFFSET, 12882 },
+  { 0xfd14, G_UNICODE_NOT_PRESENT_OFFSET, 12887 },
+  { 0xfd15, G_UNICODE_NOT_PRESENT_OFFSET, 12892 },
+  { 0xfd16, G_UNICODE_NOT_PRESENT_OFFSET, 12897 },
+  { 0xfd17, G_UNICODE_NOT_PRESENT_OFFSET, 12902 },
+  { 0xfd18, G_UNICODE_NOT_PRESENT_OFFSET, 12907 },
+  { 0xfd19, G_UNICODE_NOT_PRESENT_OFFSET, 12912 },
+  { 0xfd1a, G_UNICODE_NOT_PRESENT_OFFSET, 12917 },
+  { 0xfd1b, G_UNICODE_NOT_PRESENT_OFFSET, 12922 },
+  { 0xfd1c, G_UNICODE_NOT_PRESENT_OFFSET, 12927 },
+  { 0xfd1d, G_UNICODE_NOT_PRESENT_OFFSET, 12932 },
+  { 0xfd1e, G_UNICODE_NOT_PRESENT_OFFSET, 12937 },
+  { 0xfd1f, G_UNICODE_NOT_PRESENT_OFFSET, 12942 },
+  { 0xfd20, G_UNICODE_NOT_PRESENT_OFFSET, 12947 },
+  { 0xfd21, G_UNICODE_NOT_PRESENT_OFFSET, 12952 },
+  { 0xfd22, G_UNICODE_NOT_PRESENT_OFFSET, 12957 },
+  { 0xfd23, G_UNICODE_NOT_PRESENT_OFFSET, 12962 },
+  { 0xfd24, G_UNICODE_NOT_PRESENT_OFFSET, 12967 },
+  { 0xfd25, G_UNICODE_NOT_PRESENT_OFFSET, 12972 },
+  { 0xfd26, G_UNICODE_NOT_PRESENT_OFFSET, 12977 },
+  { 0xfd27, G_UNICODE_NOT_PRESENT_OFFSET, 12982 },
+  { 0xfd28, G_UNICODE_NOT_PRESENT_OFFSET, 12841 },
+  { 0xfd29, G_UNICODE_NOT_PRESENT_OFFSET, 12987 },
+  { 0xfd2a, G_UNICODE_NOT_PRESENT_OFFSET, 12992 },
+  { 0xfd2b, G_UNICODE_NOT_PRESENT_OFFSET, 12997 },
+  { 0xfd2c, G_UNICODE_NOT_PRESENT_OFFSET, 13002 },
+  { 0xfd2d, G_UNICODE_NOT_PRESENT_OFFSET, 12972 },
+  { 0xfd2e, G_UNICODE_NOT_PRESENT_OFFSET, 12977 },
+  { 0xfd2f, G_UNICODE_NOT_PRESENT_OFFSET, 12982 },
+  { 0xfd30, G_UNICODE_NOT_PRESENT_OFFSET, 12841 },
+  { 0xfd31, G_UNICODE_NOT_PRESENT_OFFSET, 12836 },
+  { 0xfd32, G_UNICODE_NOT_PRESENT_OFFSET, 12846 },
+  { 0xfd33, G_UNICODE_NOT_PRESENT_OFFSET, 12370 },
+  { 0xfd34, G_UNICODE_NOT_PRESENT_OFFSET, 12315 },
+  { 0xfd35, G_UNICODE_NOT_PRESENT_OFFSET, 12320 },
+  { 0xfd36, G_UNICODE_NOT_PRESENT_OFFSET, 12325 },
+  { 0xfd37, G_UNICODE_NOT_PRESENT_OFFSET, 12972 },
+  { 0xfd38, G_UNICODE_NOT_PRESENT_OFFSET, 12977 },
+  { 0xfd39, G_UNICODE_NOT_PRESENT_OFFSET, 12982 },
+  { 0xfd3a, G_UNICODE_NOT_PRESENT_OFFSET, 12370 },
+  { 0xfd3b, G_UNICODE_NOT_PRESENT_OFFSET, 12375 },
+  { 0xfd3c, G_UNICODE_NOT_PRESENT_OFFSET, 13007 },
+  { 0xfd3d, G_UNICODE_NOT_PRESENT_OFFSET, 13007 },
+  { 0xfd50, G_UNICODE_NOT_PRESENT_OFFSET, 13012 },
+  { 0xfd51, G_UNICODE_NOT_PRESENT_OFFSET, 13019 },
+  { 0xfd52, G_UNICODE_NOT_PRESENT_OFFSET, 13019 },
+  { 0xfd53, G_UNICODE_NOT_PRESENT_OFFSET, 13026 },
+  { 0xfd54, G_UNICODE_NOT_PRESENT_OFFSET, 13033 },
+  { 0xfd55, G_UNICODE_NOT_PRESENT_OFFSET, 13040 },
+  { 0xfd56, G_UNICODE_NOT_PRESENT_OFFSET, 13047 },
+  { 0xfd57, G_UNICODE_NOT_PRESENT_OFFSET, 13054 },
+  { 0xfd58, G_UNICODE_NOT_PRESENT_OFFSET, 13061 },
+  { 0xfd59, G_UNICODE_NOT_PRESENT_OFFSET, 13061 },
+  { 0xfd5a, G_UNICODE_NOT_PRESENT_OFFSET, 13068 },
+  { 0xfd5b, G_UNICODE_NOT_PRESENT_OFFSET, 13075 },
+  { 0xfd5c, G_UNICODE_NOT_PRESENT_OFFSET, 13082 },
+  { 0xfd5d, G_UNICODE_NOT_PRESENT_OFFSET, 13089 },
+  { 0xfd5e, G_UNICODE_NOT_PRESENT_OFFSET, 13096 },
+  { 0xfd5f, G_UNICODE_NOT_PRESENT_OFFSET, 13103 },
+  { 0xfd60, G_UNICODE_NOT_PRESENT_OFFSET, 13103 },
+  { 0xfd61, G_UNICODE_NOT_PRESENT_OFFSET, 13110 },
+  { 0xfd62, G_UNICODE_NOT_PRESENT_OFFSET, 13117 },
+  { 0xfd63, G_UNICODE_NOT_PRESENT_OFFSET, 13117 },
+  { 0xfd64, G_UNICODE_NOT_PRESENT_OFFSET, 13124 },
+  { 0xfd65, G_UNICODE_NOT_PRESENT_OFFSET, 13124 },
+  { 0xfd66, G_UNICODE_NOT_PRESENT_OFFSET, 13131 },
+  { 0xfd67, G_UNICODE_NOT_PRESENT_OFFSET, 13138 },
+  { 0xfd68, G_UNICODE_NOT_PRESENT_OFFSET, 13138 },
+  { 0xfd69, G_UNICODE_NOT_PRESENT_OFFSET, 13145 },
+  { 0xfd6a, G_UNICODE_NOT_PRESENT_OFFSET, 13152 },
+  { 0xfd6b, G_UNICODE_NOT_PRESENT_OFFSET, 13152 },
+  { 0xfd6c, G_UNICODE_NOT_PRESENT_OFFSET, 13159 },
+  { 0xfd6d, G_UNICODE_NOT_PRESENT_OFFSET, 13159 },
+  { 0xfd6e, G_UNICODE_NOT_PRESENT_OFFSET, 13166 },
+  { 0xfd6f, G_UNICODE_NOT_PRESENT_OFFSET, 13173 },
+  { 0xfd70, G_UNICODE_NOT_PRESENT_OFFSET, 13173 },
+  { 0xfd71, G_UNICODE_NOT_PRESENT_OFFSET, 13180 },
+  { 0xfd72, G_UNICODE_NOT_PRESENT_OFFSET, 13180 },
+  { 0xfd73, G_UNICODE_NOT_PRESENT_OFFSET, 13187 },
+  { 0xfd74, G_UNICODE_NOT_PRESENT_OFFSET, 13194 },
+  { 0xfd75, G_UNICODE_NOT_PRESENT_OFFSET, 13201 },
+  { 0xfd76, G_UNICODE_NOT_PRESENT_OFFSET, 13208 },
+  { 0xfd77, G_UNICODE_NOT_PRESENT_OFFSET, 13208 },
+  { 0xfd78, G_UNICODE_NOT_PRESENT_OFFSET, 13215 },
+  { 0xfd79, G_UNICODE_NOT_PRESENT_OFFSET, 13222 },
+  { 0xfd7a, G_UNICODE_NOT_PRESENT_OFFSET, 13229 },
+  { 0xfd7b, G_UNICODE_NOT_PRESENT_OFFSET, 13236 },
+  { 0xfd7c, G_UNICODE_NOT_PRESENT_OFFSET, 13243 },
+  { 0xfd7d, G_UNICODE_NOT_PRESENT_OFFSET, 13243 },
+  { 0xfd7e, G_UNICODE_NOT_PRESENT_OFFSET, 13250 },
+  { 0xfd7f, G_UNICODE_NOT_PRESENT_OFFSET, 13257 },
+  { 0xfd80, G_UNICODE_NOT_PRESENT_OFFSET, 13264 },
+  { 0xfd81, G_UNICODE_NOT_PRESENT_OFFSET, 13271 },
+  { 0xfd82, G_UNICODE_NOT_PRESENT_OFFSET, 13278 },
+  { 0xfd83, G_UNICODE_NOT_PRESENT_OFFSET, 13285 },
+  { 0xfd84, G_UNICODE_NOT_PRESENT_OFFSET, 13285 },
+  { 0xfd85, G_UNICODE_NOT_PRESENT_OFFSET, 13292 },
+  { 0xfd86, G_UNICODE_NOT_PRESENT_OFFSET, 13292 },
+  { 0xfd87, G_UNICODE_NOT_PRESENT_OFFSET, 13299 },
+  { 0xfd88, G_UNICODE_NOT_PRESENT_OFFSET, 13299 },
+  { 0xfd89, G_UNICODE_NOT_PRESENT_OFFSET, 13306 },
+  { 0xfd8a, G_UNICODE_NOT_PRESENT_OFFSET, 13313 },
+  { 0xfd8b, G_UNICODE_NOT_PRESENT_OFFSET, 13320 },
+  { 0xfd8c, G_UNICODE_NOT_PRESENT_OFFSET, 13327 },
+  { 0xfd8d, G_UNICODE_NOT_PRESENT_OFFSET, 13334 },
+  { 0xfd8e, G_UNICODE_NOT_PRESENT_OFFSET, 13341 },
+  { 0xfd8f, G_UNICODE_NOT_PRESENT_OFFSET, 13348 },
+  { 0xfd92, G_UNICODE_NOT_PRESENT_OFFSET, 13355 },
+  { 0xfd93, G_UNICODE_NOT_PRESENT_OFFSET, 13362 },
+  { 0xfd94, G_UNICODE_NOT_PRESENT_OFFSET, 13369 },
+  { 0xfd95, G_UNICODE_NOT_PRESENT_OFFSET, 13376 },
+  { 0xfd96, G_UNICODE_NOT_PRESENT_OFFSET, 13383 },
+  { 0xfd97, G_UNICODE_NOT_PRESENT_OFFSET, 13390 },
+  { 0xfd98, G_UNICODE_NOT_PRESENT_OFFSET, 13390 },
+  { 0xfd99, G_UNICODE_NOT_PRESENT_OFFSET, 13397 },
+  { 0xfd9a, G_UNICODE_NOT_PRESENT_OFFSET, 13404 },
+  { 0xfd9b, G_UNICODE_NOT_PRESENT_OFFSET, 13411 },
+  { 0xfd9c, G_UNICODE_NOT_PRESENT_OFFSET, 13418 },
+  { 0xfd9d, G_UNICODE_NOT_PRESENT_OFFSET, 13418 },
+  { 0xfd9e, G_UNICODE_NOT_PRESENT_OFFSET, 13425 },
+  { 0xfd9f, G_UNICODE_NOT_PRESENT_OFFSET, 13432 },
+  { 0xfda0, G_UNICODE_NOT_PRESENT_OFFSET, 13439 },
+  { 0xfda1, G_UNICODE_NOT_PRESENT_OFFSET, 13446 },
+  { 0xfda2, G_UNICODE_NOT_PRESENT_OFFSET, 13453 },
+  { 0xfda3, G_UNICODE_NOT_PRESENT_OFFSET, 13460 },
+  { 0xfda4, G_UNICODE_NOT_PRESENT_OFFSET, 13467 },
+  { 0xfda5, G_UNICODE_NOT_PRESENT_OFFSET, 13474 },
+  { 0xfda6, G_UNICODE_NOT_PRESENT_OFFSET, 13481 },
+  { 0xfda7, G_UNICODE_NOT_PRESENT_OFFSET, 13488 },
+  { 0xfda8, G_UNICODE_NOT_PRESENT_OFFSET, 13495 },
+  { 0xfda9, G_UNICODE_NOT_PRESENT_OFFSET, 13502 },
+  { 0xfdaa, G_UNICODE_NOT_PRESENT_OFFSET, 13509 },
+  { 0xfdab, G_UNICODE_NOT_PRESENT_OFFSET, 13516 },
+  { 0xfdac, G_UNICODE_NOT_PRESENT_OFFSET, 13523 },
+  { 0xfdad, G_UNICODE_NOT_PRESENT_OFFSET, 13530 },
+  { 0xfdae, G_UNICODE_NOT_PRESENT_OFFSET, 13537 },
+  { 0xfdaf, G_UNICODE_NOT_PRESENT_OFFSET, 13544 },
+  { 0xfdb0, G_UNICODE_NOT_PRESENT_OFFSET, 13551 },
+  { 0xfdb1, G_UNICODE_NOT_PRESENT_OFFSET, 13558 },
+  { 0xfdb2, G_UNICODE_NOT_PRESENT_OFFSET, 13565 },
+  { 0xfdb3, G_UNICODE_NOT_PRESENT_OFFSET, 13572 },
+  { 0xfdb4, G_UNICODE_NOT_PRESENT_OFFSET, 13250 },
+  { 0xfdb5, G_UNICODE_NOT_PRESENT_OFFSET, 13264 },
+  { 0xfdb6, G_UNICODE_NOT_PRESENT_OFFSET, 13579 },
+  { 0xfdb7, G_UNICODE_NOT_PRESENT_OFFSET, 13586 },
+  { 0xfdb8, G_UNICODE_NOT_PRESENT_OFFSET, 13593 },
+  { 0xfdb9, G_UNICODE_NOT_PRESENT_OFFSET, 13600 },
+  { 0xfdba, G_UNICODE_NOT_PRESENT_OFFSET, 13607 },
+  { 0xfdbb, G_UNICODE_NOT_PRESENT_OFFSET, 13614 },
+  { 0xfdbc, G_UNICODE_NOT_PRESENT_OFFSET, 13607 },
+  { 0xfdbd, G_UNICODE_NOT_PRESENT_OFFSET, 13593 },
+  { 0xfdbe, G_UNICODE_NOT_PRESENT_OFFSET, 13621 },
+  { 0xfdbf, G_UNICODE_NOT_PRESENT_OFFSET, 13628 },
+  { 0xfdc0, G_UNICODE_NOT_PRESENT_OFFSET, 13635 },
+  { 0xfdc1, G_UNICODE_NOT_PRESENT_OFFSET, 13642 },
+  { 0xfdc2, G_UNICODE_NOT_PRESENT_OFFSET, 13649 },
+  { 0xfdc3, G_UNICODE_NOT_PRESENT_OFFSET, 13614 },
+  { 0xfdc4, G_UNICODE_NOT_PRESENT_OFFSET, 13201 },
+  { 0xfdc5, G_UNICODE_NOT_PRESENT_OFFSET, 13131 },
+  { 0xfdc6, G_UNICODE_NOT_PRESENT_OFFSET, 13656 },
+  { 0xfdc7, G_UNICODE_NOT_PRESENT_OFFSET, 13663 },
+  { 0xfdf0, G_UNICODE_NOT_PRESENT_OFFSET, 13670 },
+  { 0xfdf1, G_UNICODE_NOT_PRESENT_OFFSET, 13677 },
+  { 0xfdf2, G_UNICODE_NOT_PRESENT_OFFSET, 13684 },
+  { 0xfdf3, G_UNICODE_NOT_PRESENT_OFFSET, 13693 },
+  { 0xfdf4, G_UNICODE_NOT_PRESENT_OFFSET, 13702 },
+  { 0xfdf5, G_UNICODE_NOT_PRESENT_OFFSET, 13711 },
+  { 0xfdf6, G_UNICODE_NOT_PRESENT_OFFSET, 13720 },
+  { 0xfdf7, G_UNICODE_NOT_PRESENT_OFFSET, 13729 },
+  { 0xfdf8, G_UNICODE_NOT_PRESENT_OFFSET, 13738 },
+  { 0xfdf9, G_UNICODE_NOT_PRESENT_OFFSET, 13747 },
+  { 0xfdfa, G_UNICODE_NOT_PRESENT_OFFSET, 13754 },
+  { 0xfdfb, G_UNICODE_NOT_PRESENT_OFFSET, 13788 },
+  { 0xfdfc, G_UNICODE_NOT_PRESENT_OFFSET, 13804 },
+  { 0xfe30, G_UNICODE_NOT_PRESENT_OFFSET, 4871 },
+  { 0xfe31, G_UNICODE_NOT_PRESENT_OFFSET, 13813 },
+  { 0xfe32, G_UNICODE_NOT_PRESENT_OFFSET, 13817 },
+  { 0xfe33, G_UNICODE_NOT_PRESENT_OFFSET, 13821 },
+  { 0xfe34, G_UNICODE_NOT_PRESENT_OFFSET, 13821 },
+  { 0xfe35, G_UNICODE_NOT_PRESENT_OFFSET, 4965 },
+  { 0xfe36, G_UNICODE_NOT_PRESENT_OFFSET, 4967 },
+  { 0xfe37, G_UNICODE_NOT_PRESENT_OFFSET, 13823 },
+  { 0xfe38, G_UNICODE_NOT_PRESENT_OFFSET, 13825 },
+  { 0xfe39, G_UNICODE_NOT_PRESENT_OFFSET, 13827 },
+  { 0xfe3a, G_UNICODE_NOT_PRESENT_OFFSET, 13831 },
+  { 0xfe3b, G_UNICODE_NOT_PRESENT_OFFSET, 13835 },
+  { 0xfe3c, G_UNICODE_NOT_PRESENT_OFFSET, 13839 },
+  { 0xfe3d, G_UNICODE_NOT_PRESENT_OFFSET, 13843 },
+  { 0xfe3e, G_UNICODE_NOT_PRESENT_OFFSET, 13847 },
+  { 0xfe3f, G_UNICODE_NOT_PRESENT_OFFSET, 5524 },
+  { 0xfe40, G_UNICODE_NOT_PRESENT_OFFSET, 5528 },
+  { 0xfe41, G_UNICODE_NOT_PRESENT_OFFSET, 13851 },
+  { 0xfe42, G_UNICODE_NOT_PRESENT_OFFSET, 13855 },
+  { 0xfe43, G_UNICODE_NOT_PRESENT_OFFSET, 13859 },
+  { 0xfe44, G_UNICODE_NOT_PRESENT_OFFSET, 13863 },
+  { 0xfe49, G_UNICODE_NOT_PRESENT_OFFSET, 4915 },
+  { 0xfe4a, G_UNICODE_NOT_PRESENT_OFFSET, 4915 },
+  { 0xfe4b, G_UNICODE_NOT_PRESENT_OFFSET, 4915 },
+  { 0xfe4c, G_UNICODE_NOT_PRESENT_OFFSET, 4915 },
+  { 0xfe4d, G_UNICODE_NOT_PRESENT_OFFSET, 13821 },
+  { 0xfe4e, G_UNICODE_NOT_PRESENT_OFFSET, 13821 },
+  { 0xfe4f, G_UNICODE_NOT_PRESENT_OFFSET, 13821 },
+  { 0xfe50, G_UNICODE_NOT_PRESENT_OFFSET, 13867 },
+  { 0xfe51, G_UNICODE_NOT_PRESENT_OFFSET, 13869 },
+  { 0xfe52, G_UNICODE_NOT_PRESENT_OFFSET, 4869 },
+  { 0xfe54, G_UNICODE_NOT_PRESENT_OFFSET, 1248 },
+  { 0xfe55, G_UNICODE_NOT_PRESENT_OFFSET, 13873 },
+  { 0xfe56, G_UNICODE_NOT_PRESENT_OFFSET, 13875 },
+  { 0xfe57, G_UNICODE_NOT_PRESENT_OFFSET, 13877 },
+  { 0xfe58, G_UNICODE_NOT_PRESENT_OFFSET, 13813 },
+  { 0xfe59, G_UNICODE_NOT_PRESENT_OFFSET, 4965 },
+  { 0xfe5a, G_UNICODE_NOT_PRESENT_OFFSET, 4967 },
+  { 0xfe5b, G_UNICODE_NOT_PRESENT_OFFSET, 13823 },
+  { 0xfe5c, G_UNICODE_NOT_PRESENT_OFFSET, 13825 },
+  { 0xfe5d, G_UNICODE_NOT_PRESENT_OFFSET, 13827 },
+  { 0xfe5e, G_UNICODE_NOT_PRESENT_OFFSET, 13831 },
+  { 0xfe5f, G_UNICODE_NOT_PRESENT_OFFSET, 13879 },
+  { 0xfe60, G_UNICODE_NOT_PRESENT_OFFSET, 13881 },
+  { 0xfe61, G_UNICODE_NOT_PRESENT_OFFSET, 13883 },
+  { 0xfe62, G_UNICODE_NOT_PRESENT_OFFSET, 4957 },
+  { 0xfe63, G_UNICODE_NOT_PRESENT_OFFSET, 13885 },
+  { 0xfe64, G_UNICODE_NOT_PRESENT_OFFSET, 13887 },
+  { 0xfe65, G_UNICODE_NOT_PRESENT_OFFSET, 13889 },
+  { 0xfe66, G_UNICODE_NOT_PRESENT_OFFSET, 4963 },
+  { 0xfe68, G_UNICODE_NOT_PRESENT_OFFSET, 13891 },
+  { 0xfe69, G_UNICODE_NOT_PRESENT_OFFSET, 13893 },
+  { 0xfe6a, G_UNICODE_NOT_PRESENT_OFFSET, 13895 },
+  { 0xfe6b, G_UNICODE_NOT_PRESENT_OFFSET, 13897 },
+  { 0xfe70, G_UNICODE_NOT_PRESENT_OFFSET, 13899 },
+  { 0xfe71, G_UNICODE_NOT_PRESENT_OFFSET, 13903 },
+  { 0xfe72, G_UNICODE_NOT_PRESENT_OFFSET, 13908 },
+  { 0xfe74, G_UNICODE_NOT_PRESENT_OFFSET, 13912 },
+  { 0xfe76, G_UNICODE_NOT_PRESENT_OFFSET, 13916 },
+  { 0xfe77, G_UNICODE_NOT_PRESENT_OFFSET, 13920 },
+  { 0xfe78, G_UNICODE_NOT_PRESENT_OFFSET, 13925 },
+  { 0xfe79, G_UNICODE_NOT_PRESENT_OFFSET, 13929 },
+  { 0xfe7a, G_UNICODE_NOT_PRESENT_OFFSET, 13934 },
+  { 0xfe7b, G_UNICODE_NOT_PRESENT_OFFSET, 13938 },
+  { 0xfe7c, G_UNICODE_NOT_PRESENT_OFFSET, 13943 },
+  { 0xfe7d, G_UNICODE_NOT_PRESENT_OFFSET, 13947 },
+  { 0xfe7e, G_UNICODE_NOT_PRESENT_OFFSET, 13952 },
+  { 0xfe7f, G_UNICODE_NOT_PRESENT_OFFSET, 13956 },
+  { 0xfe80, G_UNICODE_NOT_PRESENT_OFFSET, 13961 },
+  { 0xfe81, G_UNICODE_NOT_PRESENT_OFFSET, 1673 },
+  { 0xfe82, G_UNICODE_NOT_PRESENT_OFFSET, 1673 },
+  { 0xfe83, G_UNICODE_NOT_PRESENT_OFFSET, 1678 },
+  { 0xfe84, G_UNICODE_NOT_PRESENT_OFFSET, 1678 },
+  { 0xfe85, G_UNICODE_NOT_PRESENT_OFFSET, 1683 },
+  { 0xfe86, G_UNICODE_NOT_PRESENT_OFFSET, 1683 },
+  { 0xfe87, G_UNICODE_NOT_PRESENT_OFFSET, 1688 },
+  { 0xfe88, G_UNICODE_NOT_PRESENT_OFFSET, 1688 },
+  { 0xfe89, G_UNICODE_NOT_PRESENT_OFFSET, 1693 },
+  { 0xfe8a, G_UNICODE_NOT_PRESENT_OFFSET, 1693 },
+  { 0xfe8b, G_UNICODE_NOT_PRESENT_OFFSET, 1693 },
+  { 0xfe8c, G_UNICODE_NOT_PRESENT_OFFSET, 1693 },
+  { 0xfe8d, G_UNICODE_NOT_PRESENT_OFFSET, 13964 },
+  { 0xfe8e, G_UNICODE_NOT_PRESENT_OFFSET, 13964 },
+  { 0xfe8f, G_UNICODE_NOT_PRESENT_OFFSET, 13967 },
+  { 0xfe90, G_UNICODE_NOT_PRESENT_OFFSET, 13967 },
+  { 0xfe91, G_UNICODE_NOT_PRESENT_OFFSET, 13967 },
+  { 0xfe92, G_UNICODE_NOT_PRESENT_OFFSET, 13967 },
+  { 0xfe93, G_UNICODE_NOT_PRESENT_OFFSET, 13970 },
+  { 0xfe94, G_UNICODE_NOT_PRESENT_OFFSET, 13970 },
+  { 0xfe95, G_UNICODE_NOT_PRESENT_OFFSET, 13973 },
+  { 0xfe96, G_UNICODE_NOT_PRESENT_OFFSET, 13973 },
+  { 0xfe97, G_UNICODE_NOT_PRESENT_OFFSET, 13973 },
+  { 0xfe98, G_UNICODE_NOT_PRESENT_OFFSET, 13973 },
+  { 0xfe99, G_UNICODE_NOT_PRESENT_OFFSET, 13976 },
+  { 0xfe9a, G_UNICODE_NOT_PRESENT_OFFSET, 13976 },
+  { 0xfe9b, G_UNICODE_NOT_PRESENT_OFFSET, 13976 },
+  { 0xfe9c, G_UNICODE_NOT_PRESENT_OFFSET, 13976 },
+  { 0xfe9d, G_UNICODE_NOT_PRESENT_OFFSET, 13979 },
+  { 0xfe9e, G_UNICODE_NOT_PRESENT_OFFSET, 13979 },
+  { 0xfe9f, G_UNICODE_NOT_PRESENT_OFFSET, 13979 },
+  { 0xfea0, G_UNICODE_NOT_PRESENT_OFFSET, 13979 },
+  { 0xfea1, G_UNICODE_NOT_PRESENT_OFFSET, 13982 },
+  { 0xfea2, G_UNICODE_NOT_PRESENT_OFFSET, 13982 },
+  { 0xfea3, G_UNICODE_NOT_PRESENT_OFFSET, 13982 },
+  { 0xfea4, G_UNICODE_NOT_PRESENT_OFFSET, 13982 },
+  { 0xfea5, G_UNICODE_NOT_PRESENT_OFFSET, 13985 },
+  { 0xfea6, G_UNICODE_NOT_PRESENT_OFFSET, 13985 },
+  { 0xfea7, G_UNICODE_NOT_PRESENT_OFFSET, 13985 },
+  { 0xfea8, G_UNICODE_NOT_PRESENT_OFFSET, 13985 },
+  { 0xfea9, G_UNICODE_NOT_PRESENT_OFFSET, 13988 },
+  { 0xfeaa, G_UNICODE_NOT_PRESENT_OFFSET, 13988 },
+  { 0xfeab, G_UNICODE_NOT_PRESENT_OFFSET, 13991 },
+  { 0xfeac, G_UNICODE_NOT_PRESENT_OFFSET, 13991 },
+  { 0xfead, G_UNICODE_NOT_PRESENT_OFFSET, 13994 },
+  { 0xfeae, G_UNICODE_NOT_PRESENT_OFFSET, 13994 },
+  { 0xfeaf, G_UNICODE_NOT_PRESENT_OFFSET, 13997 },
+  { 0xfeb0, G_UNICODE_NOT_PRESENT_OFFSET, 13997 },
+  { 0xfeb1, G_UNICODE_NOT_PRESENT_OFFSET, 14000 },
+  { 0xfeb2, G_UNICODE_NOT_PRESENT_OFFSET, 14000 },
+  { 0xfeb3, G_UNICODE_NOT_PRESENT_OFFSET, 14000 },
+  { 0xfeb4, G_UNICODE_NOT_PRESENT_OFFSET, 14000 },
+  { 0xfeb5, G_UNICODE_NOT_PRESENT_OFFSET, 14003 },
+  { 0xfeb6, G_UNICODE_NOT_PRESENT_OFFSET, 14003 },
+  { 0xfeb7, G_UNICODE_NOT_PRESENT_OFFSET, 14003 },
+  { 0xfeb8, G_UNICODE_NOT_PRESENT_OFFSET, 14003 },
+  { 0xfeb9, G_UNICODE_NOT_PRESENT_OFFSET, 14006 },
+  { 0xfeba, G_UNICODE_NOT_PRESENT_OFFSET, 14006 },
+  { 0xfebb, G_UNICODE_NOT_PRESENT_OFFSET, 14006 },
+  { 0xfebc, G_UNICODE_NOT_PRESENT_OFFSET, 14006 },
+  { 0xfebd, G_UNICODE_NOT_PRESENT_OFFSET, 14009 },
+  { 0xfebe, G_UNICODE_NOT_PRESENT_OFFSET, 14009 },
+  { 0xfebf, G_UNICODE_NOT_PRESENT_OFFSET, 14009 },
+  { 0xfec0, G_UNICODE_NOT_PRESENT_OFFSET, 14009 },
+  { 0xfec1, G_UNICODE_NOT_PRESENT_OFFSET, 14012 },
+  { 0xfec2, G_UNICODE_NOT_PRESENT_OFFSET, 14012 },
+  { 0xfec3, G_UNICODE_NOT_PRESENT_OFFSET, 14012 },
+  { 0xfec4, G_UNICODE_NOT_PRESENT_OFFSET, 14012 },
+  { 0xfec5, G_UNICODE_NOT_PRESENT_OFFSET, 14015 },
+  { 0xfec6, G_UNICODE_NOT_PRESENT_OFFSET, 14015 },
+  { 0xfec7, G_UNICODE_NOT_PRESENT_OFFSET, 14015 },
+  { 0xfec8, G_UNICODE_NOT_PRESENT_OFFSET, 14015 },
+  { 0xfec9, G_UNICODE_NOT_PRESENT_OFFSET, 14018 },
+  { 0xfeca, G_UNICODE_NOT_PRESENT_OFFSET, 14018 },
+  { 0xfecb, G_UNICODE_NOT_PRESENT_OFFSET, 14018 },
+  { 0xfecc, G_UNICODE_NOT_PRESENT_OFFSET, 14018 },
+  { 0xfecd, G_UNICODE_NOT_PRESENT_OFFSET, 14021 },
+  { 0xfece, G_UNICODE_NOT_PRESENT_OFFSET, 14021 },
+  { 0xfecf, G_UNICODE_NOT_PRESENT_OFFSET, 14021 },
+  { 0xfed0, G_UNICODE_NOT_PRESENT_OFFSET, 14021 },
+  { 0xfed1, G_UNICODE_NOT_PRESENT_OFFSET, 14024 },
+  { 0xfed2, G_UNICODE_NOT_PRESENT_OFFSET, 14024 },
+  { 0xfed3, G_UNICODE_NOT_PRESENT_OFFSET, 14024 },
+  { 0xfed4, G_UNICODE_NOT_PRESENT_OFFSET, 14024 },
+  { 0xfed5, G_UNICODE_NOT_PRESENT_OFFSET, 14027 },
+  { 0xfed6, G_UNICODE_NOT_PRESENT_OFFSET, 14027 },
+  { 0xfed7, G_UNICODE_NOT_PRESENT_OFFSET, 14027 },
+  { 0xfed8, G_UNICODE_NOT_PRESENT_OFFSET, 14027 },
+  { 0xfed9, G_UNICODE_NOT_PRESENT_OFFSET, 14030 },
+  { 0xfeda, G_UNICODE_NOT_PRESENT_OFFSET, 14030 },
+  { 0xfedb, G_UNICODE_NOT_PRESENT_OFFSET, 14030 },
+  { 0xfedc, G_UNICODE_NOT_PRESENT_OFFSET, 14030 },
+  { 0xfedd, G_UNICODE_NOT_PRESENT_OFFSET, 14033 },
+  { 0xfede, G_UNICODE_NOT_PRESENT_OFFSET, 14033 },
+  { 0xfedf, G_UNICODE_NOT_PRESENT_OFFSET, 14033 },
+  { 0xfee0, G_UNICODE_NOT_PRESENT_OFFSET, 14033 },
+  { 0xfee1, G_UNICODE_NOT_PRESENT_OFFSET, 14036 },
+  { 0xfee2, G_UNICODE_NOT_PRESENT_OFFSET, 14036 },
+  { 0xfee3, G_UNICODE_NOT_PRESENT_OFFSET, 14036 },
+  { 0xfee4, G_UNICODE_NOT_PRESENT_OFFSET, 14036 },
+  { 0xfee5, G_UNICODE_NOT_PRESENT_OFFSET, 14039 },
+  { 0xfee6, G_UNICODE_NOT_PRESENT_OFFSET, 14039 },
+  { 0xfee7, G_UNICODE_NOT_PRESENT_OFFSET, 14039 },
+  { 0xfee8, G_UNICODE_NOT_PRESENT_OFFSET, 14039 },
+  { 0xfee9, G_UNICODE_NOT_PRESENT_OFFSET, 14042 },
+  { 0xfeea, G_UNICODE_NOT_PRESENT_OFFSET, 14042 },
+  { 0xfeeb, G_UNICODE_NOT_PRESENT_OFFSET, 14042 },
+  { 0xfeec, G_UNICODE_NOT_PRESENT_OFFSET, 14042 },
+  { 0xfeed, G_UNICODE_NOT_PRESENT_OFFSET, 14045 },
+  { 0xfeee, G_UNICODE_NOT_PRESENT_OFFSET, 14045 },
+  { 0xfeef, G_UNICODE_NOT_PRESENT_OFFSET, 12110 },
+  { 0xfef0, G_UNICODE_NOT_PRESENT_OFFSET, 12110 },
+  { 0xfef1, G_UNICODE_NOT_PRESENT_OFFSET, 14048 },
+  { 0xfef2, G_UNICODE_NOT_PRESENT_OFFSET, 14048 },
+  { 0xfef3, G_UNICODE_NOT_PRESENT_OFFSET, 14048 },
+  { 0xfef4, G_UNICODE_NOT_PRESENT_OFFSET, 14048 },
+  { 0xfef5, G_UNICODE_NOT_PRESENT_OFFSET, 14051 },
+  { 0xfef6, G_UNICODE_NOT_PRESENT_OFFSET, 14051 },
+  { 0xfef7, G_UNICODE_NOT_PRESENT_OFFSET, 14058 },
+  { 0xfef8, G_UNICODE_NOT_PRESENT_OFFSET, 14058 },
+  { 0xfef9, G_UNICODE_NOT_PRESENT_OFFSET, 14065 },
+  { 0xfefa, G_UNICODE_NOT_PRESENT_OFFSET, 14065 },
+  { 0xfefb, G_UNICODE_NOT_PRESENT_OFFSET, 14072 },
+  { 0xfefc, G_UNICODE_NOT_PRESENT_OFFSET, 14072 },
+  { 0xff01, G_UNICODE_NOT_PRESENT_OFFSET, 13877 },
+  { 0xff02, G_UNICODE_NOT_PRESENT_OFFSET, 14077 },
+  { 0xff03, G_UNICODE_NOT_PRESENT_OFFSET, 13879 },
+  { 0xff04, G_UNICODE_NOT_PRESENT_OFFSET, 13893 },
+  { 0xff05, G_UNICODE_NOT_PRESENT_OFFSET, 13895 },
+  { 0xff06, G_UNICODE_NOT_PRESENT_OFFSET, 13881 },
+  { 0xff07, G_UNICODE_NOT_PRESENT_OFFSET, 14079 },
+  { 0xff08, G_UNICODE_NOT_PRESENT_OFFSET, 4965 },
+  { 0xff09, G_UNICODE_NOT_PRESENT_OFFSET, 4967 },
+  { 0xff0a, G_UNICODE_NOT_PRESENT_OFFSET, 13883 },
+  { 0xff0b, G_UNICODE_NOT_PRESENT_OFFSET, 4957 },
+  { 0xff0c, G_UNICODE_NOT_PRESENT_OFFSET, 13867 },
+  { 0xff0d, G_UNICODE_NOT_PRESENT_OFFSET, 13885 },
+  { 0xff0e, G_UNICODE_NOT_PRESENT_OFFSET, 4869 },
+  { 0xff0f, G_UNICODE_NOT_PRESENT_OFFSET, 14081 },
+  { 0xff10, G_UNICODE_NOT_PRESENT_OFFSET, 4941 },
+  { 0xff11, G_UNICODE_NOT_PRESENT_OFFSET, 27 },
+  { 0xff12, G_UNICODE_NOT_PRESENT_OFFSET, 12 },
+  { 0xff13, G_UNICODE_NOT_PRESENT_OFFSET, 14 },
+  { 0xff14, G_UNICODE_NOT_PRESENT_OFFSET, 4945 },
+  { 0xff15, G_UNICODE_NOT_PRESENT_OFFSET, 4947 },
+  { 0xff16, G_UNICODE_NOT_PRESENT_OFFSET, 4949 },
+  { 0xff17, G_UNICODE_NOT_PRESENT_OFFSET, 4951 },
+  { 0xff18, G_UNICODE_NOT_PRESENT_OFFSET, 4953 },
+  { 0xff19, G_UNICODE_NOT_PRESENT_OFFSET, 4955 },
+  { 0xff1a, G_UNICODE_NOT_PRESENT_OFFSET, 13873 },
+  { 0xff1b, G_UNICODE_NOT_PRESENT_OFFSET, 1248 },
+  { 0xff1c, G_UNICODE_NOT_PRESENT_OFFSET, 13887 },
+  { 0xff1d, G_UNICODE_NOT_PRESENT_OFFSET, 4963 },
+  { 0xff1e, G_UNICODE_NOT_PRESENT_OFFSET, 13889 },
+  { 0xff1f, G_UNICODE_NOT_PRESENT_OFFSET, 13875 },
+  { 0xff20, G_UNICODE_NOT_PRESENT_OFFSET, 13897 },
+  { 0xff21, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0xff22, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0xff23, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0xff24, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0xff25, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0xff26, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0xff27, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0xff28, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0xff29, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0xff2a, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0xff2b, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0xff2c, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0xff2d, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0xff2e, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0xff2f, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0xff30, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0xff31, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0xff32, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0xff33, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0xff34, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0xff35, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0xff36, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0xff37, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0xff38, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0xff39, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0xff3a, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0xff3b, G_UNICODE_NOT_PRESENT_OFFSET, 14083 },
+  { 0xff3c, G_UNICODE_NOT_PRESENT_OFFSET, 13891 },
+  { 0xff3d, G_UNICODE_NOT_PRESENT_OFFSET, 14085 },
+  { 0xff3e, G_UNICODE_NOT_PRESENT_OFFSET, 14087 },
+  { 0xff3f, G_UNICODE_NOT_PRESENT_OFFSET, 13821 },
+  { 0xff40, G_UNICODE_NOT_PRESENT_OFFSET, 4798 },
+  { 0xff41, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0xff42, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0xff43, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0xff44, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0xff45, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0xff46, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0xff47, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0xff48, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0xff49, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0xff4a, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0xff4b, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0xff4c, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0xff4d, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0xff4e, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0xff4f, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0xff50, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0xff51, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0xff52, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0xff53, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0xff54, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0xff55, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0xff56, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0xff57, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0xff58, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0xff59, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0xff5a, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0xff5b, G_UNICODE_NOT_PRESENT_OFFSET, 13823 },
+  { 0xff5c, G_UNICODE_NOT_PRESENT_OFFSET, 14089 },
+  { 0xff5d, G_UNICODE_NOT_PRESENT_OFFSET, 13825 },
+  { 0xff5e, G_UNICODE_NOT_PRESENT_OFFSET, 14091 },
+  { 0xff5f, G_UNICODE_NOT_PRESENT_OFFSET, 14093 },
+  { 0xff60, G_UNICODE_NOT_PRESENT_OFFSET, 14097 },
+  { 0xff61, G_UNICODE_NOT_PRESENT_OFFSET, 14101 },
+  { 0xff62, G_UNICODE_NOT_PRESENT_OFFSET, 13851 },
+  { 0xff63, G_UNICODE_NOT_PRESENT_OFFSET, 13855 },
+  { 0xff64, G_UNICODE_NOT_PRESENT_OFFSET, 13869 },
+  { 0xff65, G_UNICODE_NOT_PRESENT_OFFSET, 14105 },
+  { 0xff66, G_UNICODE_NOT_PRESENT_OFFSET, 8615 },
+  { 0xff67, G_UNICODE_NOT_PRESENT_OFFSET, 14109 },
+  { 0xff68, G_UNICODE_NOT_PRESENT_OFFSET, 14113 },
+  { 0xff69, G_UNICODE_NOT_PRESENT_OFFSET, 14117 },
+  { 0xff6a, G_UNICODE_NOT_PRESENT_OFFSET, 14121 },
+  { 0xff6b, G_UNICODE_NOT_PRESENT_OFFSET, 14125 },
+  { 0xff6c, G_UNICODE_NOT_PRESENT_OFFSET, 14129 },
+  { 0xff6d, G_UNICODE_NOT_PRESENT_OFFSET, 14133 },
+  { 0xff6e, G_UNICODE_NOT_PRESENT_OFFSET, 14137 },
+  { 0xff6f, G_UNICODE_NOT_PRESENT_OFFSET, 14141 },
+  { 0xff70, G_UNICODE_NOT_PRESENT_OFFSET, 14145 },
+  { 0xff71, G_UNICODE_NOT_PRESENT_OFFSET, 8431 },
+  { 0xff72, G_UNICODE_NOT_PRESENT_OFFSET, 8435 },
+  { 0xff73, G_UNICODE_NOT_PRESENT_OFFSET, 8439 },
+  { 0xff74, G_UNICODE_NOT_PRESENT_OFFSET, 8443 },
+  { 0xff75, G_UNICODE_NOT_PRESENT_OFFSET, 8447 },
+  { 0xff76, G_UNICODE_NOT_PRESENT_OFFSET, 8451 },
+  { 0xff77, G_UNICODE_NOT_PRESENT_OFFSET, 8455 },
+  { 0xff78, G_UNICODE_NOT_PRESENT_OFFSET, 8459 },
+  { 0xff79, G_UNICODE_NOT_PRESENT_OFFSET, 8463 },
+  { 0xff7a, G_UNICODE_NOT_PRESENT_OFFSET, 8467 },
+  { 0xff7b, G_UNICODE_NOT_PRESENT_OFFSET, 8471 },
+  { 0xff7c, G_UNICODE_NOT_PRESENT_OFFSET, 8475 },
+  { 0xff7d, G_UNICODE_NOT_PRESENT_OFFSET, 8479 },
+  { 0xff7e, G_UNICODE_NOT_PRESENT_OFFSET, 8483 },
+  { 0xff7f, G_UNICODE_NOT_PRESENT_OFFSET, 8487 },
+  { 0xff80, G_UNICODE_NOT_PRESENT_OFFSET, 8491 },
+  { 0xff81, G_UNICODE_NOT_PRESENT_OFFSET, 8495 },
+  { 0xff82, G_UNICODE_NOT_PRESENT_OFFSET, 8499 },
+  { 0xff83, G_UNICODE_NOT_PRESENT_OFFSET, 8503 },
+  { 0xff84, G_UNICODE_NOT_PRESENT_OFFSET, 8507 },
+  { 0xff85, G_UNICODE_NOT_PRESENT_OFFSET, 8511 },
+  { 0xff86, G_UNICODE_NOT_PRESENT_OFFSET, 8515 },
+  { 0xff87, G_UNICODE_NOT_PRESENT_OFFSET, 8519 },
+  { 0xff88, G_UNICODE_NOT_PRESENT_OFFSET, 8523 },
+  { 0xff89, G_UNICODE_NOT_PRESENT_OFFSET, 8527 },
+  { 0xff8a, G_UNICODE_NOT_PRESENT_OFFSET, 8531 },
+  { 0xff8b, G_UNICODE_NOT_PRESENT_OFFSET, 8535 },
+  { 0xff8c, G_UNICODE_NOT_PRESENT_OFFSET, 8539 },
+  { 0xff8d, G_UNICODE_NOT_PRESENT_OFFSET, 8543 },
+  { 0xff8e, G_UNICODE_NOT_PRESENT_OFFSET, 8547 },
+  { 0xff8f, G_UNICODE_NOT_PRESENT_OFFSET, 8551 },
+  { 0xff90, G_UNICODE_NOT_PRESENT_OFFSET, 8555 },
+  { 0xff91, G_UNICODE_NOT_PRESENT_OFFSET, 8559 },
+  { 0xff92, G_UNICODE_NOT_PRESENT_OFFSET, 8563 },
+  { 0xff93, G_UNICODE_NOT_PRESENT_OFFSET, 8567 },
+  { 0xff94, G_UNICODE_NOT_PRESENT_OFFSET, 8571 },
+  { 0xff95, G_UNICODE_NOT_PRESENT_OFFSET, 8575 },
+  { 0xff96, G_UNICODE_NOT_PRESENT_OFFSET, 8579 },
+  { 0xff97, G_UNICODE_NOT_PRESENT_OFFSET, 8583 },
+  { 0xff98, G_UNICODE_NOT_PRESENT_OFFSET, 8587 },
+  { 0xff99, G_UNICODE_NOT_PRESENT_OFFSET, 8591 },
+  { 0xff9a, G_UNICODE_NOT_PRESENT_OFFSET, 8595 },
+  { 0xff9b, G_UNICODE_NOT_PRESENT_OFFSET, 8599 },
+  { 0xff9c, G_UNICODE_NOT_PRESENT_OFFSET, 8603 },
+  { 0xff9d, G_UNICODE_NOT_PRESENT_OFFSET, 14149 },
+  { 0xff9e, G_UNICODE_NOT_PRESENT_OFFSET, 14153 },
+  { 0xff9f, G_UNICODE_NOT_PRESENT_OFFSET, 14157 },
+  { 0xffa0, G_UNICODE_NOT_PRESENT_OFFSET, 7405 },
+  { 0xffa1, G_UNICODE_NOT_PRESENT_OFFSET, 7201 },
+  { 0xffa2, G_UNICODE_NOT_PRESENT_OFFSET, 7205 },
+  { 0xffa3, G_UNICODE_NOT_PRESENT_OFFSET, 7209 },
+  { 0xffa4, G_UNICODE_NOT_PRESENT_OFFSET, 7213 },
+  { 0xffa5, G_UNICODE_NOT_PRESENT_OFFSET, 7217 },
+  { 0xffa6, G_UNICODE_NOT_PRESENT_OFFSET, 7221 },
+  { 0xffa7, G_UNICODE_NOT_PRESENT_OFFSET, 7225 },
+  { 0xffa8, G_UNICODE_NOT_PRESENT_OFFSET, 7229 },
+  { 0xffa9, G_UNICODE_NOT_PRESENT_OFFSET, 7233 },
+  { 0xffaa, G_UNICODE_NOT_PRESENT_OFFSET, 7237 },
+  { 0xffab, G_UNICODE_NOT_PRESENT_OFFSET, 7241 },
+  { 0xffac, G_UNICODE_NOT_PRESENT_OFFSET, 7245 },
+  { 0xffad, G_UNICODE_NOT_PRESENT_OFFSET, 7249 },
+  { 0xffae, G_UNICODE_NOT_PRESENT_OFFSET, 7253 },
+  { 0xffaf, G_UNICODE_NOT_PRESENT_OFFSET, 7257 },
+  { 0xffb0, G_UNICODE_NOT_PRESENT_OFFSET, 7261 },
+  { 0xffb1, G_UNICODE_NOT_PRESENT_OFFSET, 7265 },
+  { 0xffb2, G_UNICODE_NOT_PRESENT_OFFSET, 7269 },
+  { 0xffb3, G_UNICODE_NOT_PRESENT_OFFSET, 7273 },
+  { 0xffb4, G_UNICODE_NOT_PRESENT_OFFSET, 7277 },
+  { 0xffb5, G_UNICODE_NOT_PRESENT_OFFSET, 7281 },
+  { 0xffb6, G_UNICODE_NOT_PRESENT_OFFSET, 7285 },
+  { 0xffb7, G_UNICODE_NOT_PRESENT_OFFSET, 7289 },
+  { 0xffb8, G_UNICODE_NOT_PRESENT_OFFSET, 7293 },
+  { 0xffb9, G_UNICODE_NOT_PRESENT_OFFSET, 7297 },
+  { 0xffba, G_UNICODE_NOT_PRESENT_OFFSET, 7301 },
+  { 0xffbb, G_UNICODE_NOT_PRESENT_OFFSET, 7305 },
+  { 0xffbc, G_UNICODE_NOT_PRESENT_OFFSET, 7309 },
+  { 0xffbd, G_UNICODE_NOT_PRESENT_OFFSET, 7313 },
+  { 0xffbe, G_UNICODE_NOT_PRESENT_OFFSET, 7317 },
+  { 0xffc2, G_UNICODE_NOT_PRESENT_OFFSET, 7321 },
+  { 0xffc3, G_UNICODE_NOT_PRESENT_OFFSET, 7325 },
+  { 0xffc4, G_UNICODE_NOT_PRESENT_OFFSET, 7329 },
+  { 0xffc5, G_UNICODE_NOT_PRESENT_OFFSET, 7333 },
+  { 0xffc6, G_UNICODE_NOT_PRESENT_OFFSET, 7337 },
+  { 0xffc7, G_UNICODE_NOT_PRESENT_OFFSET, 7341 },
+  { 0xffca, G_UNICODE_NOT_PRESENT_OFFSET, 7345 },
+  { 0xffcb, G_UNICODE_NOT_PRESENT_OFFSET, 7349 },
+  { 0xffcc, G_UNICODE_NOT_PRESENT_OFFSET, 7353 },
+  { 0xffcd, G_UNICODE_NOT_PRESENT_OFFSET, 7357 },
+  { 0xffce, G_UNICODE_NOT_PRESENT_OFFSET, 7361 },
+  { 0xffcf, G_UNICODE_NOT_PRESENT_OFFSET, 7365 },
+  { 0xffd2, G_UNICODE_NOT_PRESENT_OFFSET, 7369 },
+  { 0xffd3, G_UNICODE_NOT_PRESENT_OFFSET, 7373 },
+  { 0xffd4, G_UNICODE_NOT_PRESENT_OFFSET, 7377 },
+  { 0xffd5, G_UNICODE_NOT_PRESENT_OFFSET, 7381 },
+  { 0xffd6, G_UNICODE_NOT_PRESENT_OFFSET, 7385 },
+  { 0xffd7, G_UNICODE_NOT_PRESENT_OFFSET, 7389 },
+  { 0xffda, G_UNICODE_NOT_PRESENT_OFFSET, 7393 },
+  { 0xffdb, G_UNICODE_NOT_PRESENT_OFFSET, 7397 },
+  { 0xffdc, G_UNICODE_NOT_PRESENT_OFFSET, 7401 },
+  { 0xffe0, G_UNICODE_NOT_PRESENT_OFFSET, 14161 },
+  { 0xffe1, G_UNICODE_NOT_PRESENT_OFFSET, 14164 },
+  { 0xffe2, G_UNICODE_NOT_PRESENT_OFFSET, 14167 },
+  { 0xffe3, G_UNICODE_NOT_PRESENT_OFFSET, 8 },
+  { 0xffe4, G_UNICODE_NOT_PRESENT_OFFSET, 14170 },
+  { 0xffe5, G_UNICODE_NOT_PRESENT_OFFSET, 14173 },
+  { 0xffe6, G_UNICODE_NOT_PRESENT_OFFSET, 14176 },
+  { 0xffe8, G_UNICODE_NOT_PRESENT_OFFSET, 14180 },
+  { 0xffe9, G_UNICODE_NOT_PRESENT_OFFSET, 14184 },
+  { 0xffea, G_UNICODE_NOT_PRESENT_OFFSET, 14188 },
+  { 0xffeb, G_UNICODE_NOT_PRESENT_OFFSET, 14192 },
+  { 0xffec, G_UNICODE_NOT_PRESENT_OFFSET, 14196 },
+  { 0xffed, G_UNICODE_NOT_PRESENT_OFFSET, 14200 },
+  { 0xffee, G_UNICODE_NOT_PRESENT_OFFSET, 14204 },
+  { 0x1d15e, 14208, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d15f, 14217, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d160, 14226, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d161, 14239, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d162, 14252, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d163, 14265, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d164, 14278, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d1bb, 14291, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d1bc, 14300, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d1bd, 14309, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d1be, 14322, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d1bf, 14335, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d1c0, 14348, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x1d400, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d401, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d402, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d403, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d404, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d405, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d406, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d407, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d408, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d409, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d40a, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d40b, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d40c, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d40d, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d40e, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d40f, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d410, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d411, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d412, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d413, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d414, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d415, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d416, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d417, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d418, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d419, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d41a, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d41b, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d41c, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d41d, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d41e, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d41f, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d420, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d421, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d422, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d423, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d424, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d425, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d426, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d427, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d428, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d429, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d42a, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d42b, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d42c, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d42d, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d42e, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d42f, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d430, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d431, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d432, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d433, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d434, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d435, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d436, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d437, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d438, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d439, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d43a, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d43b, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d43c, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d43d, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d43e, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d43f, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d440, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d441, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d442, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d443, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d444, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d445, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d446, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d447, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d448, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d449, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d44a, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d44b, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d44c, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d44d, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d44e, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d44f, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d450, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d451, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d452, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d453, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d454, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d456, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d457, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d458, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d459, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d45a, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d45b, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d45c, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d45d, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d45e, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d45f, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d460, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d461, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d462, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d463, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d464, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d465, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d466, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d467, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d468, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d469, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d46a, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d46b, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d46c, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d46d, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d46e, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d46f, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d470, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d471, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d472, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d473, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d474, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d475, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d476, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d477, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d478, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d479, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d47a, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d47b, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d47c, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d47d, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d47e, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d47f, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d480, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d481, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d482, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d483, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d484, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d485, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d486, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d487, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d488, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d489, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d48a, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d48b, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d48c, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d48d, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d48e, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d48f, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d490, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d491, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d492, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d493, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d494, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d495, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d496, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d497, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d498, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d499, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d49a, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d49b, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d49c, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d49e, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d49f, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d4a2, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d4a5, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d4a6, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d4a9, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d4aa, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d4ab, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d4ac, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d4ae, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d4af, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d4b0, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d4b1, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d4b2, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d4b3, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d4b4, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d4b5, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d4b6, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d4b7, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d4b8, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d4b9, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d4bb, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d4bd, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d4be, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d4bf, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d4c0, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d4c2, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d4c3, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d4c5, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d4c6, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d4c7, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d4c8, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d4c9, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d4ca, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d4cb, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d4cc, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d4cd, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d4ce, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d4cf, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d4d0, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d4d1, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d4d2, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d4d3, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d4d4, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d4d5, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d4d6, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d4d7, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d4d8, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d4d9, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d4da, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d4db, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d4dc, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d4dd, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d4de, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d4df, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d4e0, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d4e1, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d4e2, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d4e3, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d4e4, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d4e5, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d4e6, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d4e7, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d4e8, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d4e9, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d4ea, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d4eb, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d4ec, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d4ed, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d4ee, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d4ef, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d4f0, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d4f1, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d4f2, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d4f3, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d4f4, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d4f5, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d4f6, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d4f7, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d4f8, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d4f9, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d4fa, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d4fb, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d4fc, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d4fd, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d4fe, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d4ff, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d500, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d501, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d502, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d503, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d504, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d505, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d507, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d508, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d509, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d50a, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d50d, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d50e, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d50f, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d510, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d511, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d512, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d513, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d514, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d516, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d517, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d518, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d519, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d51a, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d51b, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d51c, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d51e, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d51f, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d520, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d521, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d522, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d523, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d524, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d525, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d526, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d527, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d528, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d529, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d52a, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d52b, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d52c, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d52d, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d52e, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d52f, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d530, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d531, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d532, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d533, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d534, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d535, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d536, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d537, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d538, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d539, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d53b, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d53c, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d53d, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d53e, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d540, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d541, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d542, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d543, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d544, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d546, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d54a, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d54b, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d54c, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d54d, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d54e, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d54f, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d550, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d552, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d553, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d554, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d555, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d556, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d557, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d558, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d559, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d55a, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d55b, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d55c, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d55d, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d55e, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d55f, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d560, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d561, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d562, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d563, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d564, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d565, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d566, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d567, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d568, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d569, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d56a, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d56b, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d56c, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d56d, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d56e, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d56f, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d570, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d571, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d572, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d573, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d574, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d575, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d576, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d577, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d578, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d579, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d57a, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d57b, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d57c, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d57d, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d57e, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d57f, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d580, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d581, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d582, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d583, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d584, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d585, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d586, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d587, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d588, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d589, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d58a, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d58b, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d58c, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d58d, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d58e, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d58f, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d590, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d591, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d592, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d593, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d594, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d595, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d596, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d597, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d598, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d599, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d59a, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d59b, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d59c, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d59d, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d59e, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d59f, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d5a0, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d5a1, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d5a2, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d5a3, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d5a4, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d5a5, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d5a6, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d5a7, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d5a8, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d5a9, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d5aa, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d5ab, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d5ac, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d5ad, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d5ae, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d5af, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d5b0, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d5b1, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d5b2, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d5b3, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d5b4, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d5b5, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d5b6, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d5b7, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d5b8, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d5b9, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d5ba, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d5bb, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d5bc, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d5bd, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d5be, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d5bf, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d5c0, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d5c1, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d5c2, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d5c3, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d5c4, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d5c5, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d5c6, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d5c7, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d5c8, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d5c9, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d5ca, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d5cb, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d5cc, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d5cd, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d5ce, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d5cf, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d5d0, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d5d1, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d5d2, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d5d3, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d5d4, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d5d5, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d5d6, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d5d7, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d5d8, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d5d9, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d5da, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d5db, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d5dc, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d5dd, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d5de, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d5df, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d5e0, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d5e1, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d5e2, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d5e3, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d5e4, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d5e5, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d5e6, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d5e7, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d5e8, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d5e9, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d5ea, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d5eb, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d5ec, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d5ed, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d5ee, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d5ef, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d5f0, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d5f1, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d5f2, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d5f3, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d5f4, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d5f5, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d5f6, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d5f7, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d5f8, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d5f9, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d5fa, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d5fb, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d5fc, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d5fd, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d5fe, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d5ff, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d600, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d601, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d602, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d603, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d604, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d605, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d606, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d607, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d608, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d609, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d60a, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d60b, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d60c, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d60d, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d60e, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d60f, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d610, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d611, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d612, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d613, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d614, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d615, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d616, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d617, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d618, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d619, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d61a, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d61b, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d61c, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d61d, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d61e, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d61f, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d620, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d621, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d622, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d623, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d624, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d625, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d626, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d627, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d628, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d629, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d62a, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d62b, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d62c, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d62d, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d62e, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d62f, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d630, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d631, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d632, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d633, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d634, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d635, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d636, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d637, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d638, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d639, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d63a, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d63b, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d63c, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d63d, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d63e, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d63f, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d640, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d641, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d642, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d643, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d644, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d645, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d646, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d647, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d648, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d649, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d64a, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d64b, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d64c, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d64d, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d64e, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d64f, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d650, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d651, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d652, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d653, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d654, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d655, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d656, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d657, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d658, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d659, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d65a, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d65b, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d65c, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d65d, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d65e, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d65f, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d660, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d661, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d662, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d663, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d664, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d665, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d666, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d667, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d668, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d669, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d66a, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d66b, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d66c, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d66d, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d66e, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d66f, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d670, G_UNICODE_NOT_PRESENT_OFFSET, 5831 },
+  { 0x1d671, G_UNICODE_NOT_PRESENT_OFFSET, 5042 },
+  { 0x1d672, G_UNICODE_NOT_PRESENT_OFFSET, 4982 },
+  { 0x1d673, G_UNICODE_NOT_PRESENT_OFFSET, 5077 },
+  { 0x1d674, G_UNICODE_NOT_PRESENT_OFFSET, 5046 },
+  { 0x1d675, G_UNICODE_NOT_PRESENT_OFFSET, 5048 },
+  { 0x1d676, G_UNICODE_NOT_PRESENT_OFFSET, 5833 },
+  { 0x1d677, G_UNICODE_NOT_PRESENT_OFFSET, 5005 },
+  { 0x1d678, G_UNICODE_NOT_PRESENT_OFFSET, 5010 },
+  { 0x1d679, G_UNICODE_NOT_PRESENT_OFFSET, 5835 },
+  { 0x1d67a, G_UNICODE_NOT_PRESENT_OFFSET, 5040 },
+  { 0x1d67b, G_UNICODE_NOT_PRESENT_OFFSET, 5012 },
+  { 0x1d67c, G_UNICODE_NOT_PRESENT_OFFSET, 5050 },
+  { 0x1d67d, G_UNICODE_NOT_PRESENT_OFFSET, 5014 },
+  { 0x1d67e, G_UNICODE_NOT_PRESENT_OFFSET, 5837 },
+  { 0x1d67f, G_UNICODE_NOT_PRESENT_OFFSET, 5019 },
+  { 0x1d680, G_UNICODE_NOT_PRESENT_OFFSET, 5021 },
+  { 0x1d681, G_UNICODE_NOT_PRESENT_OFFSET, 5023 },
+  { 0x1d682, G_UNICODE_NOT_PRESENT_OFFSET, 5839 },
+  { 0x1d683, G_UNICODE_NOT_PRESENT_OFFSET, 5841 },
+  { 0x1d684, G_UNICODE_NOT_PRESENT_OFFSET, 5843 },
+  { 0x1d685, G_UNICODE_NOT_PRESENT_OFFSET, 5168 },
+  { 0x1d686, G_UNICODE_NOT_PRESENT_OFFSET, 5845 },
+  { 0x1d687, G_UNICODE_NOT_PRESENT_OFFSET, 5185 },
+  { 0x1d688, G_UNICODE_NOT_PRESENT_OFFSET, 5847 },
+  { 0x1d689, G_UNICODE_NOT_PRESENT_OFFSET, 5035 },
+  { 0x1d68a, G_UNICODE_NOT_PRESENT_OFFSET, 6 },
+  { 0x1d68b, G_UNICODE_NOT_PRESENT_OFFSET, 5849 },
+  { 0x1d68c, G_UNICODE_NOT_PRESENT_OFFSET, 5228 },
+  { 0x1d68d, G_UNICODE_NOT_PRESENT_OFFSET, 5079 },
+  { 0x1d68e, G_UNICODE_NOT_PRESENT_OFFSET, 5044 },
+  { 0x1d68f, G_UNICODE_NOT_PRESENT_OFFSET, 5851 },
+  { 0x1d690, G_UNICODE_NOT_PRESENT_OFFSET, 5003 },
+  { 0x1d691, G_UNICODE_NOT_PRESENT_OFFSET, 1171 },
+  { 0x1d692, G_UNICODE_NOT_PRESENT_OFFSET, 4943 },
+  { 0x1d693, G_UNICODE_NOT_PRESENT_OFFSET, 1176 },
+  { 0x1d694, G_UNICODE_NOT_PRESENT_OFFSET, 5853 },
+  { 0x1d695, G_UNICODE_NOT_PRESENT_OFFSET, 1220 },
+  { 0x1d696, G_UNICODE_NOT_PRESENT_OFFSET, 5230 },
+  { 0x1d697, G_UNICODE_NOT_PRESENT_OFFSET, 4969 },
+  { 0x1d698, G_UNICODE_NOT_PRESENT_OFFSET, 29 },
+  { 0x1d699, G_UNICODE_NOT_PRESENT_OFFSET, 5855 },
+  { 0x1d69a, G_UNICODE_NOT_PRESENT_OFFSET, 5857 },
+  { 0x1d69b, G_UNICODE_NOT_PRESENT_OFFSET, 1178 },
+  { 0x1d69c, G_UNICODE_NOT_PRESENT_OFFSET, 711 },
+  { 0x1d69d, G_UNICODE_NOT_PRESENT_OFFSET, 5859 },
+  { 0x1d69e, G_UNICODE_NOT_PRESENT_OFFSET, 5861 },
+  { 0x1d69f, G_UNICODE_NOT_PRESENT_OFFSET, 5204 },
+  { 0x1d6a0, G_UNICODE_NOT_PRESENT_OFFSET, 1189 },
+  { 0x1d6a1, G_UNICODE_NOT_PRESENT_OFFSET, 1222 },
+  { 0x1d6a2, G_UNICODE_NOT_PRESENT_OFFSET, 1191 },
+  { 0x1d6a3, G_UNICODE_NOT_PRESENT_OFFSET, 5863 },
+  { 0x1d6a8, G_UNICODE_NOT_PRESENT_OFFSET, 14361 },
+  { 0x1d6a9, G_UNICODE_NOT_PRESENT_OFFSET, 14364 },
+  { 0x1d6aa, G_UNICODE_NOT_PRESENT_OFFSET, 5067 },
+  { 0x1d6ab, G_UNICODE_NOT_PRESENT_OFFSET, 14367 },
+  { 0x1d6ac, G_UNICODE_NOT_PRESENT_OFFSET, 14370 },
+  { 0x1d6ad, G_UNICODE_NOT_PRESENT_OFFSET, 14373 },
+  { 0x1d6ae, G_UNICODE_NOT_PRESENT_OFFSET, 14376 },
+  { 0x1d6af, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d6b0, G_UNICODE_NOT_PRESENT_OFFSET, 14379 },
+  { 0x1d6b1, G_UNICODE_NOT_PRESENT_OFFSET, 14382 },
+  { 0x1d6b2, G_UNICODE_NOT_PRESENT_OFFSET, 14385 },
+  { 0x1d6b3, G_UNICODE_NOT_PRESENT_OFFSET, 14388 },
+  { 0x1d6b4, G_UNICODE_NOT_PRESENT_OFFSET, 14391 },
+  { 0x1d6b5, G_UNICODE_NOT_PRESENT_OFFSET, 14394 },
+  { 0x1d6b6, G_UNICODE_NOT_PRESENT_OFFSET, 14397 },
+  { 0x1d6b7, G_UNICODE_NOT_PRESENT_OFFSET, 5070 },
+  { 0x1d6b8, G_UNICODE_NOT_PRESENT_OFFSET, 14400 },
+  { 0x1d6b9, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d6ba, G_UNICODE_NOT_PRESENT_OFFSET, 14403 },
+  { 0x1d6bb, G_UNICODE_NOT_PRESENT_OFFSET, 14406 },
+  { 0x1d6bc, G_UNICODE_NOT_PRESENT_OFFSET, 1374 },
+  { 0x1d6bd, G_UNICODE_NOT_PRESENT_OFFSET, 14409 },
+  { 0x1d6be, G_UNICODE_NOT_PRESENT_OFFSET, 14412 },
+  { 0x1d6bf, G_UNICODE_NOT_PRESENT_OFFSET, 14415 },
+  { 0x1d6c0, G_UNICODE_NOT_PRESENT_OFFSET, 5037 },
+  { 0x1d6c1, G_UNICODE_NOT_PRESENT_OFFSET, 14418 },
+  { 0x1d6c2, G_UNICODE_NOT_PRESENT_OFFSET, 14422 },
+  { 0x1d6c3, G_UNICODE_NOT_PRESENT_OFFSET, 1368 },
+  { 0x1d6c4, G_UNICODE_NOT_PRESENT_OFFSET, 5064 },
+  { 0x1d6c5, G_UNICODE_NOT_PRESENT_OFFSET, 14425 },
+  { 0x1d6c6, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d6c7, G_UNICODE_NOT_PRESENT_OFFSET, 14428 },
+  { 0x1d6c8, G_UNICODE_NOT_PRESENT_OFFSET, 14431 },
+  { 0x1d6c9, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d6ca, G_UNICODE_NOT_PRESENT_OFFSET, 4548 },
+  { 0x1d6cb, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d6cc, G_UNICODE_NOT_PRESENT_OFFSET, 14434 },
+  { 0x1d6cd, G_UNICODE_NOT_PRESENT_OFFSET, 20 },
+  { 0x1d6ce, G_UNICODE_NOT_PRESENT_OFFSET, 14437 },
+  { 0x1d6cf, G_UNICODE_NOT_PRESENT_OFFSET, 14440 },
+  { 0x1d6d0, G_UNICODE_NOT_PRESENT_OFFSET, 14443 },
+  { 0x1d6d1, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d6d2, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d6d3, G_UNICODE_NOT_PRESENT_OFFSET, 1399 },
+  { 0x1d6d4, G_UNICODE_NOT_PRESENT_OFFSET, 14446 },
+  { 0x1d6d5, G_UNICODE_NOT_PRESENT_OFFSET, 14449 },
+  { 0x1d6d6, G_UNICODE_NOT_PRESENT_OFFSET, 14452 },
+  { 0x1d6d7, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d6d8, G_UNICODE_NOT_PRESENT_OFFSET, 14455 },
+  { 0x1d6d9, G_UNICODE_NOT_PRESENT_OFFSET, 14458 },
+  { 0x1d6da, G_UNICODE_NOT_PRESENT_OFFSET, 14461 },
+  { 0x1d6db, G_UNICODE_NOT_PRESENT_OFFSET, 14464 },
+  { 0x1d6dc, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d6dd, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d6de, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d6df, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d6e0, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d6e1, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d6e2, G_UNICODE_NOT_PRESENT_OFFSET, 14361 },
+  { 0x1d6e3, G_UNICODE_NOT_PRESENT_OFFSET, 14364 },
+  { 0x1d6e4, G_UNICODE_NOT_PRESENT_OFFSET, 5067 },
+  { 0x1d6e5, G_UNICODE_NOT_PRESENT_OFFSET, 14367 },
+  { 0x1d6e6, G_UNICODE_NOT_PRESENT_OFFSET, 14370 },
+  { 0x1d6e7, G_UNICODE_NOT_PRESENT_OFFSET, 14373 },
+  { 0x1d6e8, G_UNICODE_NOT_PRESENT_OFFSET, 14376 },
+  { 0x1d6e9, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d6ea, G_UNICODE_NOT_PRESENT_OFFSET, 14379 },
+  { 0x1d6eb, G_UNICODE_NOT_PRESENT_OFFSET, 14382 },
+  { 0x1d6ec, G_UNICODE_NOT_PRESENT_OFFSET, 14385 },
+  { 0x1d6ed, G_UNICODE_NOT_PRESENT_OFFSET, 14388 },
+  { 0x1d6ee, G_UNICODE_NOT_PRESENT_OFFSET, 14391 },
+  { 0x1d6ef, G_UNICODE_NOT_PRESENT_OFFSET, 14394 },
+  { 0x1d6f0, G_UNICODE_NOT_PRESENT_OFFSET, 14397 },
+  { 0x1d6f1, G_UNICODE_NOT_PRESENT_OFFSET, 5070 },
+  { 0x1d6f2, G_UNICODE_NOT_PRESENT_OFFSET, 14400 },
+  { 0x1d6f3, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d6f4, G_UNICODE_NOT_PRESENT_OFFSET, 14403 },
+  { 0x1d6f5, G_UNICODE_NOT_PRESENT_OFFSET, 14406 },
+  { 0x1d6f6, G_UNICODE_NOT_PRESENT_OFFSET, 1374 },
+  { 0x1d6f7, G_UNICODE_NOT_PRESENT_OFFSET, 14409 },
+  { 0x1d6f8, G_UNICODE_NOT_PRESENT_OFFSET, 14412 },
+  { 0x1d6f9, G_UNICODE_NOT_PRESENT_OFFSET, 14415 },
+  { 0x1d6fa, G_UNICODE_NOT_PRESENT_OFFSET, 5037 },
+  { 0x1d6fb, G_UNICODE_NOT_PRESENT_OFFSET, 14418 },
+  { 0x1d6fc, G_UNICODE_NOT_PRESENT_OFFSET, 14422 },
+  { 0x1d6fd, G_UNICODE_NOT_PRESENT_OFFSET, 1368 },
+  { 0x1d6fe, G_UNICODE_NOT_PRESENT_OFFSET, 5064 },
+  { 0x1d6ff, G_UNICODE_NOT_PRESENT_OFFSET, 14425 },
+  { 0x1d700, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d701, G_UNICODE_NOT_PRESENT_OFFSET, 14428 },
+  { 0x1d702, G_UNICODE_NOT_PRESENT_OFFSET, 14431 },
+  { 0x1d703, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d704, G_UNICODE_NOT_PRESENT_OFFSET, 4548 },
+  { 0x1d705, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d706, G_UNICODE_NOT_PRESENT_OFFSET, 14434 },
+  { 0x1d707, G_UNICODE_NOT_PRESENT_OFFSET, 20 },
+  { 0x1d708, G_UNICODE_NOT_PRESENT_OFFSET, 14437 },
+  { 0x1d709, G_UNICODE_NOT_PRESENT_OFFSET, 14440 },
+  { 0x1d70a, G_UNICODE_NOT_PRESENT_OFFSET, 14443 },
+  { 0x1d70b, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d70c, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d70d, G_UNICODE_NOT_PRESENT_OFFSET, 1399 },
+  { 0x1d70e, G_UNICODE_NOT_PRESENT_OFFSET, 14446 },
+  { 0x1d70f, G_UNICODE_NOT_PRESENT_OFFSET, 14449 },
+  { 0x1d710, G_UNICODE_NOT_PRESENT_OFFSET, 14452 },
+  { 0x1d711, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d712, G_UNICODE_NOT_PRESENT_OFFSET, 14455 },
+  { 0x1d713, G_UNICODE_NOT_PRESENT_OFFSET, 14458 },
+  { 0x1d714, G_UNICODE_NOT_PRESENT_OFFSET, 14461 },
+  { 0x1d715, G_UNICODE_NOT_PRESENT_OFFSET, 14464 },
+  { 0x1d716, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d717, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d718, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d719, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d71a, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d71b, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d71c, G_UNICODE_NOT_PRESENT_OFFSET, 14361 },
+  { 0x1d71d, G_UNICODE_NOT_PRESENT_OFFSET, 14364 },
+  { 0x1d71e, G_UNICODE_NOT_PRESENT_OFFSET, 5067 },
+  { 0x1d71f, G_UNICODE_NOT_PRESENT_OFFSET, 14367 },
+  { 0x1d720, G_UNICODE_NOT_PRESENT_OFFSET, 14370 },
+  { 0x1d721, G_UNICODE_NOT_PRESENT_OFFSET, 14373 },
+  { 0x1d722, G_UNICODE_NOT_PRESENT_OFFSET, 14376 },
+  { 0x1d723, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d724, G_UNICODE_NOT_PRESENT_OFFSET, 14379 },
+  { 0x1d725, G_UNICODE_NOT_PRESENT_OFFSET, 14382 },
+  { 0x1d726, G_UNICODE_NOT_PRESENT_OFFSET, 14385 },
+  { 0x1d727, G_UNICODE_NOT_PRESENT_OFFSET, 14388 },
+  { 0x1d728, G_UNICODE_NOT_PRESENT_OFFSET, 14391 },
+  { 0x1d729, G_UNICODE_NOT_PRESENT_OFFSET, 14394 },
+  { 0x1d72a, G_UNICODE_NOT_PRESENT_OFFSET, 14397 },
+  { 0x1d72b, G_UNICODE_NOT_PRESENT_OFFSET, 5070 },
+  { 0x1d72c, G_UNICODE_NOT_PRESENT_OFFSET, 14400 },
+  { 0x1d72d, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d72e, G_UNICODE_NOT_PRESENT_OFFSET, 14403 },
+  { 0x1d72f, G_UNICODE_NOT_PRESENT_OFFSET, 14406 },
+  { 0x1d730, G_UNICODE_NOT_PRESENT_OFFSET, 1374 },
+  { 0x1d731, G_UNICODE_NOT_PRESENT_OFFSET, 14409 },
+  { 0x1d732, G_UNICODE_NOT_PRESENT_OFFSET, 14412 },
+  { 0x1d733, G_UNICODE_NOT_PRESENT_OFFSET, 14415 },
+  { 0x1d734, G_UNICODE_NOT_PRESENT_OFFSET, 5037 },
+  { 0x1d735, G_UNICODE_NOT_PRESENT_OFFSET, 14418 },
+  { 0x1d736, G_UNICODE_NOT_PRESENT_OFFSET, 14422 },
+  { 0x1d737, G_UNICODE_NOT_PRESENT_OFFSET, 1368 },
+  { 0x1d738, G_UNICODE_NOT_PRESENT_OFFSET, 5064 },
+  { 0x1d739, G_UNICODE_NOT_PRESENT_OFFSET, 14425 },
+  { 0x1d73a, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d73b, G_UNICODE_NOT_PRESENT_OFFSET, 14428 },
+  { 0x1d73c, G_UNICODE_NOT_PRESENT_OFFSET, 14431 },
+  { 0x1d73d, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d73e, G_UNICODE_NOT_PRESENT_OFFSET, 4548 },
+  { 0x1d73f, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d740, G_UNICODE_NOT_PRESENT_OFFSET, 14434 },
+  { 0x1d741, G_UNICODE_NOT_PRESENT_OFFSET, 20 },
+  { 0x1d742, G_UNICODE_NOT_PRESENT_OFFSET, 14437 },
+  { 0x1d743, G_UNICODE_NOT_PRESENT_OFFSET, 14440 },
+  { 0x1d744, G_UNICODE_NOT_PRESENT_OFFSET, 14443 },
+  { 0x1d745, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d746, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d747, G_UNICODE_NOT_PRESENT_OFFSET, 1399 },
+  { 0x1d748, G_UNICODE_NOT_PRESENT_OFFSET, 14446 },
+  { 0x1d749, G_UNICODE_NOT_PRESENT_OFFSET, 14449 },
+  { 0x1d74a, G_UNICODE_NOT_PRESENT_OFFSET, 14452 },
+  { 0x1d74b, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d74c, G_UNICODE_NOT_PRESENT_OFFSET, 14455 },
+  { 0x1d74d, G_UNICODE_NOT_PRESENT_OFFSET, 14458 },
+  { 0x1d74e, G_UNICODE_NOT_PRESENT_OFFSET, 14461 },
+  { 0x1d74f, G_UNICODE_NOT_PRESENT_OFFSET, 14464 },
+  { 0x1d750, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d751, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d752, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d753, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d754, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d755, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d756, G_UNICODE_NOT_PRESENT_OFFSET, 14361 },
+  { 0x1d757, G_UNICODE_NOT_PRESENT_OFFSET, 14364 },
+  { 0x1d758, G_UNICODE_NOT_PRESENT_OFFSET, 5067 },
+  { 0x1d759, G_UNICODE_NOT_PRESENT_OFFSET, 14367 },
+  { 0x1d75a, G_UNICODE_NOT_PRESENT_OFFSET, 14370 },
+  { 0x1d75b, G_UNICODE_NOT_PRESENT_OFFSET, 14373 },
+  { 0x1d75c, G_UNICODE_NOT_PRESENT_OFFSET, 14376 },
+  { 0x1d75d, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d75e, G_UNICODE_NOT_PRESENT_OFFSET, 14379 },
+  { 0x1d75f, G_UNICODE_NOT_PRESENT_OFFSET, 14382 },
+  { 0x1d760, G_UNICODE_NOT_PRESENT_OFFSET, 14385 },
+  { 0x1d761, G_UNICODE_NOT_PRESENT_OFFSET, 14388 },
+  { 0x1d762, G_UNICODE_NOT_PRESENT_OFFSET, 14391 },
+  { 0x1d763, G_UNICODE_NOT_PRESENT_OFFSET, 14394 },
+  { 0x1d764, G_UNICODE_NOT_PRESENT_OFFSET, 14397 },
+  { 0x1d765, G_UNICODE_NOT_PRESENT_OFFSET, 5070 },
+  { 0x1d766, G_UNICODE_NOT_PRESENT_OFFSET, 14400 },
+  { 0x1d767, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d768, G_UNICODE_NOT_PRESENT_OFFSET, 14403 },
+  { 0x1d769, G_UNICODE_NOT_PRESENT_OFFSET, 14406 },
+  { 0x1d76a, G_UNICODE_NOT_PRESENT_OFFSET, 1374 },
+  { 0x1d76b, G_UNICODE_NOT_PRESENT_OFFSET, 14409 },
+  { 0x1d76c, G_UNICODE_NOT_PRESENT_OFFSET, 14412 },
+  { 0x1d76d, G_UNICODE_NOT_PRESENT_OFFSET, 14415 },
+  { 0x1d76e, G_UNICODE_NOT_PRESENT_OFFSET, 5037 },
+  { 0x1d76f, G_UNICODE_NOT_PRESENT_OFFSET, 14418 },
+  { 0x1d770, G_UNICODE_NOT_PRESENT_OFFSET, 14422 },
+  { 0x1d771, G_UNICODE_NOT_PRESENT_OFFSET, 1368 },
+  { 0x1d772, G_UNICODE_NOT_PRESENT_OFFSET, 5064 },
+  { 0x1d773, G_UNICODE_NOT_PRESENT_OFFSET, 14425 },
+  { 0x1d774, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d775, G_UNICODE_NOT_PRESENT_OFFSET, 14428 },
+  { 0x1d776, G_UNICODE_NOT_PRESENT_OFFSET, 14431 },
+  { 0x1d777, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d778, G_UNICODE_NOT_PRESENT_OFFSET, 4548 },
+  { 0x1d779, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d77a, G_UNICODE_NOT_PRESENT_OFFSET, 14434 },
+  { 0x1d77b, G_UNICODE_NOT_PRESENT_OFFSET, 20 },
+  { 0x1d77c, G_UNICODE_NOT_PRESENT_OFFSET, 14437 },
+  { 0x1d77d, G_UNICODE_NOT_PRESENT_OFFSET, 14440 },
+  { 0x1d77e, G_UNICODE_NOT_PRESENT_OFFSET, 14443 },
+  { 0x1d77f, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d780, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d781, G_UNICODE_NOT_PRESENT_OFFSET, 1399 },
+  { 0x1d782, G_UNICODE_NOT_PRESENT_OFFSET, 14446 },
+  { 0x1d783, G_UNICODE_NOT_PRESENT_OFFSET, 14449 },
+  { 0x1d784, G_UNICODE_NOT_PRESENT_OFFSET, 14452 },
+  { 0x1d785, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d786, G_UNICODE_NOT_PRESENT_OFFSET, 14455 },
+  { 0x1d787, G_UNICODE_NOT_PRESENT_OFFSET, 14458 },
+  { 0x1d788, G_UNICODE_NOT_PRESENT_OFFSET, 14461 },
+  { 0x1d789, G_UNICODE_NOT_PRESENT_OFFSET, 14464 },
+  { 0x1d78a, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d78b, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d78c, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d78d, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d78e, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d78f, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d790, G_UNICODE_NOT_PRESENT_OFFSET, 14361 },
+  { 0x1d791, G_UNICODE_NOT_PRESENT_OFFSET, 14364 },
+  { 0x1d792, G_UNICODE_NOT_PRESENT_OFFSET, 5067 },
+  { 0x1d793, G_UNICODE_NOT_PRESENT_OFFSET, 14367 },
+  { 0x1d794, G_UNICODE_NOT_PRESENT_OFFSET, 14370 },
+  { 0x1d795, G_UNICODE_NOT_PRESENT_OFFSET, 14373 },
+  { 0x1d796, G_UNICODE_NOT_PRESENT_OFFSET, 14376 },
+  { 0x1d797, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d798, G_UNICODE_NOT_PRESENT_OFFSET, 14379 },
+  { 0x1d799, G_UNICODE_NOT_PRESENT_OFFSET, 14382 },
+  { 0x1d79a, G_UNICODE_NOT_PRESENT_OFFSET, 14385 },
+  { 0x1d79b, G_UNICODE_NOT_PRESENT_OFFSET, 14388 },
+  { 0x1d79c, G_UNICODE_NOT_PRESENT_OFFSET, 14391 },
+  { 0x1d79d, G_UNICODE_NOT_PRESENT_OFFSET, 14394 },
+  { 0x1d79e, G_UNICODE_NOT_PRESENT_OFFSET, 14397 },
+  { 0x1d79f, G_UNICODE_NOT_PRESENT_OFFSET, 5070 },
+  { 0x1d7a0, G_UNICODE_NOT_PRESENT_OFFSET, 14400 },
+  { 0x1d7a1, G_UNICODE_NOT_PRESENT_OFFSET, 1402 },
+  { 0x1d7a2, G_UNICODE_NOT_PRESENT_OFFSET, 14403 },
+  { 0x1d7a3, G_UNICODE_NOT_PRESENT_OFFSET, 14406 },
+  { 0x1d7a4, G_UNICODE_NOT_PRESENT_OFFSET, 1374 },
+  { 0x1d7a5, G_UNICODE_NOT_PRESENT_OFFSET, 14409 },
+  { 0x1d7a6, G_UNICODE_NOT_PRESENT_OFFSET, 14412 },
+  { 0x1d7a7, G_UNICODE_NOT_PRESENT_OFFSET, 14415 },
+  { 0x1d7a8, G_UNICODE_NOT_PRESENT_OFFSET, 5037 },
+  { 0x1d7a9, G_UNICODE_NOT_PRESENT_OFFSET, 14418 },
+  { 0x1d7aa, G_UNICODE_NOT_PRESENT_OFFSET, 14422 },
+  { 0x1d7ab, G_UNICODE_NOT_PRESENT_OFFSET, 1368 },
+  { 0x1d7ac, G_UNICODE_NOT_PRESENT_OFFSET, 5064 },
+  { 0x1d7ad, G_UNICODE_NOT_PRESENT_OFFSET, 14425 },
+  { 0x1d7ae, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d7af, G_UNICODE_NOT_PRESENT_OFFSET, 14428 },
+  { 0x1d7b0, G_UNICODE_NOT_PRESENT_OFFSET, 14431 },
+  { 0x1d7b1, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d7b2, G_UNICODE_NOT_PRESENT_OFFSET, 4548 },
+  { 0x1d7b3, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d7b4, G_UNICODE_NOT_PRESENT_OFFSET, 14434 },
+  { 0x1d7b5, G_UNICODE_NOT_PRESENT_OFFSET, 20 },
+  { 0x1d7b6, G_UNICODE_NOT_PRESENT_OFFSET, 14437 },
+  { 0x1d7b7, G_UNICODE_NOT_PRESENT_OFFSET, 14440 },
+  { 0x1d7b8, G_UNICODE_NOT_PRESENT_OFFSET, 14443 },
+  { 0x1d7b9, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d7ba, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d7bb, G_UNICODE_NOT_PRESENT_OFFSET, 1399 },
+  { 0x1d7bc, G_UNICODE_NOT_PRESENT_OFFSET, 14446 },
+  { 0x1d7bd, G_UNICODE_NOT_PRESENT_OFFSET, 14449 },
+  { 0x1d7be, G_UNICODE_NOT_PRESENT_OFFSET, 14452 },
+  { 0x1d7bf, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d7c0, G_UNICODE_NOT_PRESENT_OFFSET, 14455 },
+  { 0x1d7c1, G_UNICODE_NOT_PRESENT_OFFSET, 14458 },
+  { 0x1d7c2, G_UNICODE_NOT_PRESENT_OFFSET, 14461 },
+  { 0x1d7c3, G_UNICODE_NOT_PRESENT_OFFSET, 14464 },
+  { 0x1d7c4, G_UNICODE_NOT_PRESENT_OFFSET, 1405 },
+  { 0x1d7c5, G_UNICODE_NOT_PRESENT_OFFSET, 1371 },
+  { 0x1d7c6, G_UNICODE_NOT_PRESENT_OFFSET, 1393 },
+  { 0x1d7c7, G_UNICODE_NOT_PRESENT_OFFSET, 1387 },
+  { 0x1d7c8, G_UNICODE_NOT_PRESENT_OFFSET, 1396 },
+  { 0x1d7c9, G_UNICODE_NOT_PRESENT_OFFSET, 1390 },
+  { 0x1d7ce, G_UNICODE_NOT_PRESENT_OFFSET, 4941 },
+  { 0x1d7cf, G_UNICODE_NOT_PRESENT_OFFSET, 27 },
+  { 0x1d7d0, G_UNICODE_NOT_PRESENT_OFFSET, 12 },
+  { 0x1d7d1, G_UNICODE_NOT_PRESENT_OFFSET, 14 },
+  { 0x1d7d2, G_UNICODE_NOT_PRESENT_OFFSET, 4945 },
+  { 0x1d7d3, G_UNICODE_NOT_PRESENT_OFFSET, 4947 },
+  { 0x1d7d4, G_UNICODE_NOT_PRESENT_OFFSET, 4949 },
+  { 0x1d7d5, G_UNICODE_NOT_PRESENT_OFFSET, 4951 },
+  { 0x1d7d6, G_UNICODE_NOT_PRESENT_OFFSET, 4953 },
+  { 0x1d7d7, G_UNICODE_NOT_PRESENT_OFFSET, 4955 },
+  { 0x1d7d8, G_UNICODE_NOT_PRESENT_OFFSET, 4941 },
+  { 0x1d7d9, G_UNICODE_NOT_PRESENT_OFFSET, 27 },
+  { 0x1d7da, G_UNICODE_NOT_PRESENT_OFFSET, 12 },
+  { 0x1d7db, G_UNICODE_NOT_PRESENT_OFFSET, 14 },
+  { 0x1d7dc, G_UNICODE_NOT_PRESENT_OFFSET, 4945 },
+  { 0x1d7dd, G_UNICODE_NOT_PRESENT_OFFSET, 4947 },
+  { 0x1d7de, G_UNICODE_NOT_PRESENT_OFFSET, 4949 },
+  { 0x1d7df, G_UNICODE_NOT_PRESENT_OFFSET, 4951 },
+  { 0x1d7e0, G_UNICODE_NOT_PRESENT_OFFSET, 4953 },
+  { 0x1d7e1, G_UNICODE_NOT_PRESENT_OFFSET, 4955 },
+  { 0x1d7e2, G_UNICODE_NOT_PRESENT_OFFSET, 4941 },
+  { 0x1d7e3, G_UNICODE_NOT_PRESENT_OFFSET, 27 },
+  { 0x1d7e4, G_UNICODE_NOT_PRESENT_OFFSET, 12 },
+  { 0x1d7e5, G_UNICODE_NOT_PRESENT_OFFSET, 14 },
+  { 0x1d7e6, G_UNICODE_NOT_PRESENT_OFFSET, 4945 },
+  { 0x1d7e7, G_UNICODE_NOT_PRESENT_OFFSET, 4947 },
+  { 0x1d7e8, G_UNICODE_NOT_PRESENT_OFFSET, 4949 },
+  { 0x1d7e9, G_UNICODE_NOT_PRESENT_OFFSET, 4951 },
+  { 0x1d7ea, G_UNICODE_NOT_PRESENT_OFFSET, 4953 },
+  { 0x1d7eb, G_UNICODE_NOT_PRESENT_OFFSET, 4955 },
+  { 0x1d7ec, G_UNICODE_NOT_PRESENT_OFFSET, 4941 },
+  { 0x1d7ed, G_UNICODE_NOT_PRESENT_OFFSET, 27 },
+  { 0x1d7ee, G_UNICODE_NOT_PRESENT_OFFSET, 12 },
+  { 0x1d7ef, G_UNICODE_NOT_PRESENT_OFFSET, 14 },
+  { 0x1d7f0, G_UNICODE_NOT_PRESENT_OFFSET, 4945 },
+  { 0x1d7f1, G_UNICODE_NOT_PRESENT_OFFSET, 4947 },
+  { 0x1d7f2, G_UNICODE_NOT_PRESENT_OFFSET, 4949 },
+  { 0x1d7f3, G_UNICODE_NOT_PRESENT_OFFSET, 4951 },
+  { 0x1d7f4, G_UNICODE_NOT_PRESENT_OFFSET, 4953 },
+  { 0x1d7f5, G_UNICODE_NOT_PRESENT_OFFSET, 4955 },
+  { 0x1d7f6, G_UNICODE_NOT_PRESENT_OFFSET, 4941 },
+  { 0x1d7f7, G_UNICODE_NOT_PRESENT_OFFSET, 27 },
+  { 0x1d7f8, G_UNICODE_NOT_PRESENT_OFFSET, 12 },
+  { 0x1d7f9, G_UNICODE_NOT_PRESENT_OFFSET, 14 },
+  { 0x1d7fa, G_UNICODE_NOT_PRESENT_OFFSET, 4945 },
+  { 0x1d7fb, G_UNICODE_NOT_PRESENT_OFFSET, 4947 },
+  { 0x1d7fc, G_UNICODE_NOT_PRESENT_OFFSET, 4949 },
+  { 0x1d7fd, G_UNICODE_NOT_PRESENT_OFFSET, 4951 },
+  { 0x1d7fe, G_UNICODE_NOT_PRESENT_OFFSET, 4953 },
+  { 0x1d7ff, G_UNICODE_NOT_PRESENT_OFFSET, 4955 },
+  { 0x2f800, 14468, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f801, 14472, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f802, 14476, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f803, 14480, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f804, 14485, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f805, 11545, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f806, 14489, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f807, 14493, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f808, 14497, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f809, 14501, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f80a, 11549, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f80b, 14505, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f80c, 14509, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f80d, 14513, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f80e, 11553, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f80f, 14518, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f810, 14522, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f811, 14526, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f812, 14530, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f813, 14535, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f814, 14539, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f815, 14543, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f816, 14547, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f817, 14552, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f818, 14556, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f819, 14560, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f81a, 14564, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f81b, 14568, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f81c, 14572, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f81d, 5967, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f81e, 14577, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f81f, 14581, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f820, 14585, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f821, 14589, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f822, 14593, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f823, 14597, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f824, 14601, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f825, 14605, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f826, 11557, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f827, 11561, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f828, 14609, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f829, 14613, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f82a, 14617, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f82b, 10837, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f82c, 14621, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f82d, 11565, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f82e, 14625, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f82f, 14629, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f830, 14633, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f831, 14637, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f832, 14637, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f833, 14637, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f834, 14641, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f835, 14646, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f836, 14650, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f837, 14654, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f838, 14658, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f839, 14663, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f83a, 14667, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f83b, 14671, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f83c, 14675, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f83d, 14679, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f83e, 14683, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f83f, 14687, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f840, 14691, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f841, 14695, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f842, 14699, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f843, 14703, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f844, 14707, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f845, 14711, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f846, 14711, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f847, 14715, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f848, 14719, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f849, 14723, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f84a, 14727, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f84b, 14731, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f84c, 11573, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f84d, 14735, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f84e, 14739, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f84f, 14743, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f850, 11421, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f851, 14747, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f852, 14751, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f853, 14755, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f854, 14759, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f855, 14763, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f856, 14767, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f857, 14771, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f858, 14775, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f859, 14779, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f85a, 14784, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f85b, 14788, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f85c, 14792, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f85d, 14796, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f85e, 14800, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f85f, 14804, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f860, 14808, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f861, 14813, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f862, 14818, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f863, 14822, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f864, 14826, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f865, 14830, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f866, 14834, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f867, 14838, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f868, 14842, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f869, 14847, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f86a, 14851, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f86b, 14851, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f86c, 14855, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f86d, 14860, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f86e, 14864, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f86f, 10821, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f870, 14868, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f871, 14872, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f872, 14877, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f873, 14881, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f874, 14885, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f875, 6071, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f876, 14889, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f877, 14893, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f878, 6079, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f879, 14897, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f87a, 14901, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f87b, 14905, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f87c, 14910, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f87d, 14914, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f87e, 14919, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f87f, 14923, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f880, 14927, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f881, 14931, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f882, 14935, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f883, 14939, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f884, 14943, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f885, 14947, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f886, 14951, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f887, 14955, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f888, 14959, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f889, 14963, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f88a, 14968, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f88b, 14972, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f88c, 14976, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f88d, 14980, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f88e, 10613, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f88f, 14984, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f890, 6119, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f891, 14989, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f892, 14989, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f893, 14994, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f894, 14998, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f895, 14998, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f896, 15002, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f897, 15006, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f898, 15011, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f899, 15016, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f89a, 15020, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f89b, 15024, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f89c, 15028, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f89d, 15032, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f89e, 15036, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f89f, 15040, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a0, 15044, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a1, 15048, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a2, 15052, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a3, 11593, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a4, 15056, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a5, 15061, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a6, 15065, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a7, 15069, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a8, 15073, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8a9, 15069, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8aa, 15077, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ab, 11601, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ac, 15081, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ad, 15085, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ae, 15089, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8af, 15093, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b0, 11605, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b1, 10505, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b2, 15097, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b3, 15101, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b4, 15105, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b5, 15109, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b6, 15113, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b7, 15117, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b8, 15121, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8b9, 15126, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ba, 15130, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8bb, 15134, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8bc, 15138, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8bd, 15142, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8be, 15146, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8bf, 15151, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c0, 15155, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c1, 15159, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c2, 15163, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c3, 15167, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c4, 15171, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c5, 15175, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c6, 15179, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c7, 15183, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c8, 11609, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8c9, 15187, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ca, 15191, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8cb, 15196, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8cc, 15200, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8cd, 15204, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ce, 15208, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8cf, 11617, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d0, 15212, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d1, 15216, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d2, 15220, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d3, 15224, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d4, 15228, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d5, 15232, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d6, 15236, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d7, 15240, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d8, 10617, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8d9, 15244, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8da, 15248, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8db, 15252, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8dc, 15256, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8dd, 15260, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8de, 15265, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8df, 15269, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e0, 15273, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e1, 15277, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e2, 11621, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e3, 15281, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e4, 15286, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e5, 15290, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e6, 15294, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e7, 15298, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e8, 15302, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8e9, 15306, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ea, 15310, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8eb, 15314, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ec, 15318, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ed, 15323, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ee, 15327, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ef, 15331, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f0, 15335, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f1, 15340, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f2, 15344, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f3, 15348, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f4, 15352, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f5, 10889, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f6, 15356, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f7, 15360, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f8, 15365, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8f9, 15370, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8fa, 15375, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8fb, 15379, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8fc, 15384, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8fd, 15388, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8fe, 15392, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f8ff, 15396, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f900, 15400, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f901, 11625, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f902, 11221, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f903, 15404, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f904, 15408, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f905, 15412, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f906, 15416, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f907, 15421, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f908, 15425, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f909, 15429, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f90a, 15433, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f90b, 15437, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f90c, 15441, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f90d, 15445, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f90e, 15450, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f90f, 15454, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f910, 15458, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f911, 15463, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f912, 15468, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f913, 15472, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f914, 15476, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f915, 15480, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f916, 15484, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f917, 15488, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f918, 15492, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f919, 15496, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f91a, 15500, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f91b, 15504, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f91c, 15509, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f91d, 15513, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f91e, 15518, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f91f, 15522, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f920, 15526, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f921, 15530, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f922, 15534, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f923, 15538, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f924, 15543, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f925, 15547, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f926, 15551, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f927, 15556, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f928, 15561, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f929, 15565, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f92a, 15569, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f92b, 15573, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f92c, 15577, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f92d, 15577, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f92e, 15581, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f92f, 15585, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f930, 15589, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f931, 15593, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f932, 15597, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f933, 15601, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f934, 15605, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f935, 15609, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f936, 15614, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f937, 15618, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f938, 10833, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f939, 15623, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f93a, 15628, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f93b, 15632, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f93c, 15637, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f93d, 15642, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f93e, 15647, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f93f, 15651, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f940, 15655, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f941, 15659, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f942, 15664, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f943, 15669, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f944, 15674, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f945, 15679, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f946, 15683, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f947, 15683, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f948, 15687, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f949, 15691, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f94a, 15695, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f94b, 15699, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f94c, 15703, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f94d, 15707, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f94e, 15712, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f94f, 10685, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f950, 15716, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f951, 15720, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f952, 15724, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f953, 11665, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f954, 15729, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f955, 15734, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f956, 11501, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f957, 15739, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f958, 15743, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f959, 11677, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f95a, 15747, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f95b, 15751, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f95c, 15755, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f95d, 15760, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f95e, 15760, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f95f, 15765, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f960, 15769, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f961, 15773, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f962, 15778, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f963, 15782, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f964, 15786, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f965, 15790, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f966, 15795, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f967, 15799, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f968, 15803, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f969, 15807, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f96a, 15811, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f96b, 15815, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f96c, 15820, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f96d, 15824, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f96e, 15828, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f96f, 15832, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f970, 15836, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f971, 15840, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f972, 15844, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f973, 15849, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f974, 15854, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f975, 15858, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f976, 15863, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f977, 15867, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f978, 15872, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f979, 15876, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f97a, 11701, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f97b, 15880, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f97c, 15885, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f97d, 15890, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f97e, 15894, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f97f, 15899, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f980, 15903, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f981, 15908, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f982, 15912, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f983, 15916, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f984, 15920, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f985, 15924, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f986, 15928, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f987, 15932, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f988, 15937, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f989, 15942, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f98a, 15947, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f98b, 14994, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f98c, 15952, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f98d, 15956, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f98e, 15960, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f98f, 15964, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f990, 15968, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f991, 15972, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f992, 15976, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f993, 15980, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f994, 15984, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f995, 15988, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f996, 15992, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f997, 15996, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f998, 10901, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f999, 16001, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f99a, 16005, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f99b, 16009, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f99c, 16013, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f99d, 16017, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f99e, 16021, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f99f, 11713, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a0, 16025, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a1, 16029, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a2, 16033, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a3, 16037, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a4, 16041, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a5, 16046, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a6, 16051, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a7, 16056, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a8, 16060, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9a9, 16064, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9aa, 16068, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ab, 16072, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ac, 16077, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ad, 16081, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ae, 16086, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9af, 16090, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b0, 16094, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b1, 16099, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b2, 16104, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b3, 16108, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b4, 10665, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b5, 16112, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b6, 16116, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b7, 16120, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b8, 16124, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9b9, 16128, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ba, 16132, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9bb, 16136, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9bc, 16140, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9bd, 16144, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9be, 16148, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9bf, 16152, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c0, 16156, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c1, 16160, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c2, 16164, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c3, 16168, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c4, 6479, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c5, 16172, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c6, 16177, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c7, 16181, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c8, 16185, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9c9, 16189, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ca, 16193, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9cb, 16197, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9cc, 16202, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9cd, 16207, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ce, 16211, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9cf, 16215, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d0, 16219, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d1, 16223, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d2, 6507, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d3, 16227, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d4, 16232, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d5, 16236, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d6, 16240, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d7, 16244, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d8, 16248, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9d9, 16253, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9da, 16258, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9db, 16262, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9dc, 16266, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9dd, 16270, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9de, 16275, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9df, 16279, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e0, 16283, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e1, 16288, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e2, 16293, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e3, 16297, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e4, 16301, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e5, 16305, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e6, 16310, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e7, 16314, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e8, 16318, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9e9, 16322, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ea, 16326, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9eb, 16330, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ec, 16334, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ed, 16338, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ee, 16343, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ef, 16347, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f0, 16351, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f1, 16355, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f2, 16360, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f3, 16364, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f4, 16368, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f5, 16372, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f6, 16376, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f7, 16381, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f8, 16386, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9f9, 16390, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9fa, 16394, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9fb, 16398, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9fc, 16403, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9fd, 16407, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9fe, 16412, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2f9ff, 16412, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa00, 16416, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa01, 16420, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa02, 16425, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa03, 16429, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa04, 16433, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa05, 16437, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa06, 16441, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa07, 16445, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa08, 16449, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa09, 16453, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa0a, 16458, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa0b, 16462, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa0c, 16466, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa0d, 16470, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa0e, 16474, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa0f, 16478, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa10, 16482, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa11, 16487, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa12, 16491, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa13, 16496, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa14, 16501, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa15, 6699, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa16, 16506, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa17, 6715, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa18, 16510, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa19, 16514, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa1a, 16518, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa1b, 16522, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa1c, 6735, G_UNICODE_NOT_PRESENT_OFFSET },
+  { 0x2fa1d, 16526, G_UNICODE_NOT_PRESENT_OFFSET }
+};
+
+static const gchar decomp_expansion_string[] = 
+  "\x20\0" /* offset 0 */
+  "\x20\xcc\x88\0" /* offset 2 */
+  "\x61\0" /* offset 6 */
+  "\x20\xcc\x84\0" /* offset 8 */
+  "\x32\0" /* offset 12 */
+  "\x33\0" /* offset 14 */
+  "\x20\xcc\x81\0" /* offset 16 */
+  "\xce\xbc\0" /* offset 20 */
+  "\x20\xcc\xa7\0" /* offset 23 */
+  "\x31\0" /* offset 27 */
+  "\x6f\0" /* offset 29 */
+  "\x31\xe2\x81\x84\x34\0" /* offset 31 */
+  "\x31\xe2\x81\x84\x32\0" /* offset 37 */
+  "\x33\xe2\x81\x84\x34\0" /* offset 43 */
+  "\x41\xcc\x80\0" /* offset 49 */
+  "\x41\xcc\x81\0" /* offset 53 */
+  "\x41\xcc\x82\0" /* offset 57 */
+  "\x41\xcc\x83\0" /* offset 61 */
+  "\x41\xcc\x88\0" /* offset 65 */
+  "\x41\xcc\x8a\0" /* offset 69 */
+  "\x43\xcc\xa7\0" /* offset 73 */
+  "\x45\xcc\x80\0" /* offset 77 */
+  "\x45\xcc\x81\0" /* offset 81 */
+  "\x45\xcc\x82\0" /* offset 85 */
+  "\x45\xcc\x88\0" /* offset 89 */
+  "\x49\xcc\x80\0" /* offset 93 */
+  "\x49\xcc\x81\0" /* offset 97 */
+  "\x49\xcc\x82\0" /* offset 101 */
+  "\x49\xcc\x88\0" /* offset 105 */
+  "\x4e\xcc\x83\0" /* offset 109 */
+  "\x4f\xcc\x80\0" /* offset 113 */
+  "\x4f\xcc\x81\0" /* offset 117 */
+  "\x4f\xcc\x82\0" /* offset 121 */
+  "\x4f\xcc\x83\0" /* offset 125 */
+  "\x4f\xcc\x88\0" /* offset 129 */
+  "\x55\xcc\x80\0" /* offset 133 */
+  "\x55\xcc\x81\0" /* offset 137 */
+  "\x55\xcc\x82\0" /* offset 141 */
+  "\x55\xcc\x88\0" /* offset 145 */
+  "\x59\xcc\x81\0" /* offset 149 */
+  "\x61\xcc\x80\0" /* offset 153 */
+  "\x61\xcc\x81\0" /* offset 157 */
+  "\x61\xcc\x82\0" /* offset 161 */
+  "\x61\xcc\x83\0" /* offset 165 */
+  "\x61\xcc\x88\0" /* offset 169 */
+  "\x61\xcc\x8a\0" /* offset 173 */
+  "\x63\xcc\xa7\0" /* offset 177 */
+  "\x65\xcc\x80\0" /* offset 181 */
+  "\x65\xcc\x81\0" /* offset 185 */
+  "\x65\xcc\x82\0" /* offset 189 */
+  "\x65\xcc\x88\0" /* offset 193 */
+  "\x69\xcc\x80\0" /* offset 197 */
+  "\x69\xcc\x81\0" /* offset 201 */
+  "\x69\xcc\x82\0" /* offset 205 */
+  "\x69\xcc\x88\0" /* offset 209 */
+  "\x6e\xcc\x83\0" /* offset 213 */
+  "\x6f\xcc\x80\0" /* offset 217 */
+  "\x6f\xcc\x81\0" /* offset 221 */
+  "\x6f\xcc\x82\0" /* offset 225 */
+  "\x6f\xcc\x83\0" /* offset 229 */
+  "\x6f\xcc\x88\0" /* offset 233 */
+  "\x75\xcc\x80\0" /* offset 237 */
+  "\x75\xcc\x81\0" /* offset 241 */
+  "\x75\xcc\x82\0" /* offset 245 */
+  "\x75\xcc\x88\0" /* offset 249 */
+  "\x79\xcc\x81\0" /* offset 253 */
+  "\x79\xcc\x88\0" /* offset 257 */
+  "\x41\xcc\x84\0" /* offset 261 */
+  "\x61\xcc\x84\0" /* offset 265 */
+  "\x41\xcc\x86\0" /* offset 269 */
+  "\x61\xcc\x86\0" /* offset 273 */
+  "\x41\xcc\xa8\0" /* offset 277 */
+  "\x61\xcc\xa8\0" /* offset 281 */
+  "\x43\xcc\x81\0" /* offset 285 */
+  "\x63\xcc\x81\0" /* offset 289 */
+  "\x43\xcc\x82\0" /* offset 293 */
+  "\x63\xcc\x82\0" /* offset 297 */
+  "\x43\xcc\x87\0" /* offset 301 */
+  "\x63\xcc\x87\0" /* offset 305 */
+  "\x43\xcc\x8c\0" /* offset 309 */
+  "\x63\xcc\x8c\0" /* offset 313 */
+  "\x44\xcc\x8c\0" /* offset 317 */
+  "\x64\xcc\x8c\0" /* offset 321 */
+  "\x45\xcc\x84\0" /* offset 325 */
+  "\x65\xcc\x84\0" /* offset 329 */
+  "\x45\xcc\x86\0" /* offset 333 */
+  "\x65\xcc\x86\0" /* offset 337 */
+  "\x45\xcc\x87\0" /* offset 341 */
+  "\x65\xcc\x87\0" /* offset 345 */
+  "\x45\xcc\xa8\0" /* offset 349 */
+  "\x65\xcc\xa8\0" /* offset 353 */
+  "\x45\xcc\x8c\0" /* offset 357 */
+  "\x65\xcc\x8c\0" /* offset 361 */
+  "\x47\xcc\x82\0" /* offset 365 */
+  "\x67\xcc\x82\0" /* offset 369 */
+  "\x47\xcc\x86\0" /* offset 373 */
+  "\x67\xcc\x86\0" /* offset 377 */
+  "\x47\xcc\x87\0" /* offset 381 */
+  "\x67\xcc\x87\0" /* offset 385 */
+  "\x47\xcc\xa7\0" /* offset 389 */
+  "\x67\xcc\xa7\0" /* offset 393 */
+  "\x48\xcc\x82\0" /* offset 397 */
+  "\x68\xcc\x82\0" /* offset 401 */
+  "\x49\xcc\x83\0" /* offset 405 */
+  "\x69\xcc\x83\0" /* offset 409 */
+  "\x49\xcc\x84\0" /* offset 413 */
+  "\x69\xcc\x84\0" /* offset 417 */
+  "\x49\xcc\x86\0" /* offset 421 */
+  "\x69\xcc\x86\0" /* offset 425 */
+  "\x49\xcc\xa8\0" /* offset 429 */
+  "\x69\xcc\xa8\0" /* offset 433 */
+  "\x49\xcc\x87\0" /* offset 437 */
+  "\x49\x4a\0" /* offset 441 */
+  "\x69\x6a\0" /* offset 444 */
+  "\x4a\xcc\x82\0" /* offset 447 */
+  "\x6a\xcc\x82\0" /* offset 451 */
+  "\x4b\xcc\xa7\0" /* offset 455 */
+  "\x6b\xcc\xa7\0" /* offset 459 */
+  "\x4c\xcc\x81\0" /* offset 463 */
+  "\x6c\xcc\x81\0" /* offset 467 */
+  "\x4c\xcc\xa7\0" /* offset 471 */
+  "\x6c\xcc\xa7\0" /* offset 475 */
+  "\x4c\xcc\x8c\0" /* offset 479 */
+  "\x6c\xcc\x8c\0" /* offset 483 */
+  "\x4c\xc2\xb7\0" /* offset 487 */
+  "\x6c\xc2\xb7\0" /* offset 491 */
+  "\x4e\xcc\x81\0" /* offset 495 */
+  "\x6e\xcc\x81\0" /* offset 499 */
+  "\x4e\xcc\xa7\0" /* offset 503 */
+  "\x6e\xcc\xa7\0" /* offset 507 */
+  "\x4e\xcc\x8c\0" /* offset 511 */
+  "\x6e\xcc\x8c\0" /* offset 515 */
+  "\xca\xbc\x6e\0" /* offset 519 */
+  "\x4f\xcc\x84\0" /* offset 523 */
+  "\x6f\xcc\x84\0" /* offset 527 */
+  "\x4f\xcc\x86\0" /* offset 531 */
+  "\x6f\xcc\x86\0" /* offset 535 */
+  "\x4f\xcc\x8b\0" /* offset 539 */
+  "\x6f\xcc\x8b\0" /* offset 543 */
+  "\x52\xcc\x81\0" /* offset 547 */
+  "\x72\xcc\x81\0" /* offset 551 */
+  "\x52\xcc\xa7\0" /* offset 555 */
+  "\x72\xcc\xa7\0" /* offset 559 */
+  "\x52\xcc\x8c\0" /* offset 563 */
+  "\x72\xcc\x8c\0" /* offset 567 */
+  "\x53\xcc\x81\0" /* offset 571 */
+  "\x73\xcc\x81\0" /* offset 575 */
+  "\x53\xcc\x82\0" /* offset 579 */
+  "\x73\xcc\x82\0" /* offset 583 */
+  "\x53\xcc\xa7\0" /* offset 587 */
+  "\x73\xcc\xa7\0" /* offset 591 */
+  "\x53\xcc\x8c\0" /* offset 595 */
+  "\x73\xcc\x8c\0" /* offset 599 */
+  "\x54\xcc\xa7\0" /* offset 603 */
+  "\x74\xcc\xa7\0" /* offset 607 */
+  "\x54\xcc\x8c\0" /* offset 611 */
+  "\x74\xcc\x8c\0" /* offset 615 */
+  "\x55\xcc\x83\0" /* offset 619 */
+  "\x75\xcc\x83\0" /* offset 623 */
+  "\x55\xcc\x84\0" /* offset 627 */
+  "\x75\xcc\x84\0" /* offset 631 */
+  "\x55\xcc\x86\0" /* offset 635 */
+  "\x75\xcc\x86\0" /* offset 639 */
+  "\x55\xcc\x8a\0" /* offset 643 */
+  "\x75\xcc\x8a\0" /* offset 647 */
+  "\x55\xcc\x8b\0" /* offset 651 */
+  "\x75\xcc\x8b\0" /* offset 655 */
+  "\x55\xcc\xa8\0" /* offset 659 */
+  "\x75\xcc\xa8\0" /* offset 663 */
+  "\x57\xcc\x82\0" /* offset 667 */
+  "\x77\xcc\x82\0" /* offset 671 */
+  "\x59\xcc\x82\0" /* offset 675 */
+  "\x79\xcc\x82\0" /* offset 679 */
+  "\x59\xcc\x88\0" /* offset 683 */
+  "\x5a\xcc\x81\0" /* offset 687 */
+  "\x7a\xcc\x81\0" /* offset 691 */
+  "\x5a\xcc\x87\0" /* offset 695 */
+  "\x7a\xcc\x87\0" /* offset 699 */
+  "\x5a\xcc\x8c\0" /* offset 703 */
+  "\x7a\xcc\x8c\0" /* offset 707 */
+  "\x73\0" /* offset 711 */
+  "\x4f\xcc\x9b\0" /* offset 713 */
+  "\x6f\xcc\x9b\0" /* offset 717 */
+  "\x55\xcc\x9b\0" /* offset 721 */
+  "\x75\xcc\x9b\0" /* offset 725 */
+  "\x44\x5a\xcc\x8c\0" /* offset 729 */
+  "\x44\x7a\xcc\x8c\0" /* offset 734 */
+  "\x64\x7a\xcc\x8c\0" /* offset 739 */
+  "\x4c\x4a\0" /* offset 744 */
+  "\x4c\x6a\0" /* offset 747 */
+  "\x6c\x6a\0" /* offset 750 */
+  "\x4e\x4a\0" /* offset 753 */
+  "\x4e\x6a\0" /* offset 756 */
+  "\x6e\x6a\0" /* offset 759 */
+  "\x41\xcc\x8c\0" /* offset 762 */
+  "\x61\xcc\x8c\0" /* offset 766 */
+  "\x49\xcc\x8c\0" /* offset 770 */
+  "\x69\xcc\x8c\0" /* offset 774 */
+  "\x4f\xcc\x8c\0" /* offset 778 */
+  "\x6f\xcc\x8c\0" /* offset 782 */
+  "\x55\xcc\x8c\0" /* offset 786 */
+  "\x75\xcc\x8c\0" /* offset 790 */
+  "\x55\xcc\x88\xcc\x84\0" /* offset 794 */
+  "\x75\xcc\x88\xcc\x84\0" /* offset 800 */
+  "\x55\xcc\x88\xcc\x81\0" /* offset 806 */
+  "\x75\xcc\x88\xcc\x81\0" /* offset 812 */
+  "\x55\xcc\x88\xcc\x8c\0" /* offset 818 */
+  "\x75\xcc\x88\xcc\x8c\0" /* offset 824 */
+  "\x55\xcc\x88\xcc\x80\0" /* offset 830 */
+  "\x75\xcc\x88\xcc\x80\0" /* offset 836 */
+  "\x41\xcc\x88\xcc\x84\0" /* offset 842 */
+  "\x61\xcc\x88\xcc\x84\0" /* offset 848 */
+  "\x41\xcc\x87\xcc\x84\0" /* offset 854 */
+  "\x61\xcc\x87\xcc\x84\0" /* offset 860 */
+  "\xc3\x86\xcc\x84\0" /* offset 866 */
+  "\xc3\xa6\xcc\x84\0" /* offset 871 */
+  "\x47\xcc\x8c\0" /* offset 876 */
+  "\x67\xcc\x8c\0" /* offset 880 */
+  "\x4b\xcc\x8c\0" /* offset 884 */
+  "\x6b\xcc\x8c\0" /* offset 888 */
+  "\x4f\xcc\xa8\0" /* offset 892 */
+  "\x6f\xcc\xa8\0" /* offset 896 */
+  "\x4f\xcc\xa8\xcc\x84\0" /* offset 900 */
+  "\x6f\xcc\xa8\xcc\x84\0" /* offset 906 */
+  "\xc6\xb7\xcc\x8c\0" /* offset 912 */
+  "\xca\x92\xcc\x8c\0" /* offset 917 */
+  "\x6a\xcc\x8c\0" /* offset 922 */
+  "\x44\x5a\0" /* offset 926 */
+  "\x44\x7a\0" /* offset 929 */
+  "\x64\x7a\0" /* offset 932 */
+  "\x47\xcc\x81\0" /* offset 935 */
+  "\x67\xcc\x81\0" /* offset 939 */
+  "\x4e\xcc\x80\0" /* offset 943 */
+  "\x6e\xcc\x80\0" /* offset 947 */
+  "\x41\xcc\x8a\xcc\x81\0" /* offset 951 */
+  "\x61\xcc\x8a\xcc\x81\0" /* offset 957 */
+  "\xc3\x86\xcc\x81\0" /* offset 963 */
+  "\xc3\xa6\xcc\x81\0" /* offset 968 */
+  "\xc3\x98\xcc\x81\0" /* offset 973 */
+  "\xc3\xb8\xcc\x81\0" /* offset 978 */
+  "\x41\xcc\x8f\0" /* offset 983 */
+  "\x61\xcc\x8f\0" /* offset 987 */
+  "\x41\xcc\x91\0" /* offset 991 */
+  "\x61\xcc\x91\0" /* offset 995 */
+  "\x45\xcc\x8f\0" /* offset 999 */
+  "\x65\xcc\x8f\0" /* offset 1003 */
+  "\x45\xcc\x91\0" /* offset 1007 */
+  "\x65\xcc\x91\0" /* offset 1011 */
+  "\x49\xcc\x8f\0" /* offset 1015 */
+  "\x69\xcc\x8f\0" /* offset 1019 */
+  "\x49\xcc\x91\0" /* offset 1023 */
+  "\x69\xcc\x91\0" /* offset 1027 */
+  "\x4f\xcc\x8f\0" /* offset 1031 */
+  "\x6f\xcc\x8f\0" /* offset 1035 */
+  "\x4f\xcc\x91\0" /* offset 1039 */
+  "\x6f\xcc\x91\0" /* offset 1043 */
+  "\x52\xcc\x8f\0" /* offset 1047 */
+  "\x72\xcc\x8f\0" /* offset 1051 */
+  "\x52\xcc\x91\0" /* offset 1055 */
+  "\x72\xcc\x91\0" /* offset 1059 */
+  "\x55\xcc\x8f\0" /* offset 1063 */
+  "\x75\xcc\x8f\0" /* offset 1067 */
+  "\x55\xcc\x91\0" /* offset 1071 */
+  "\x75\xcc\x91\0" /* offset 1075 */
+  "\x53\xcc\xa6\0" /* offset 1079 */
+  "\x73\xcc\xa6\0" /* offset 1083 */
+  "\x54\xcc\xa6\0" /* offset 1087 */
+  "\x74\xcc\xa6\0" /* offset 1091 */
+  "\x48\xcc\x8c\0" /* offset 1095 */
+  "\x68\xcc\x8c\0" /* offset 1099 */
+  "\x41\xcc\x87\0" /* offset 1103 */
+  "\x61\xcc\x87\0" /* offset 1107 */
+  "\x45\xcc\xa7\0" /* offset 1111 */
+  "\x65\xcc\xa7\0" /* offset 1115 */
+  "\x4f\xcc\x88\xcc\x84\0" /* offset 1119 */
+  "\x6f\xcc\x88\xcc\x84\0" /* offset 1125 */
+  "\x4f\xcc\x83\xcc\x84\0" /* offset 1131 */
+  "\x6f\xcc\x83\xcc\x84\0" /* offset 1137 */
+  "\x4f\xcc\x87\0" /* offset 1143 */
+  "\x6f\xcc\x87\0" /* offset 1147 */
+  "\x4f\xcc\x87\xcc\x84\0" /* offset 1151 */
+  "\x6f\xcc\x87\xcc\x84\0" /* offset 1157 */
+  "\x59\xcc\x84\0" /* offset 1163 */
+  "\x79\xcc\x84\0" /* offset 1167 */
+  "\x68\0" /* offset 1171 */
+  "\xc9\xa6\0" /* offset 1173 */
+  "\x6a\0" /* offset 1176 */
+  "\x72\0" /* offset 1178 */
+  "\xc9\xb9\0" /* offset 1180 */
+  "\xc9\xbb\0" /* offset 1183 */
+  "\xca\x81\0" /* offset 1186 */
+  "\x77\0" /* offset 1189 */
+  "\x79\0" /* offset 1191 */
+  "\x20\xcc\x86\0" /* offset 1193 */
+  "\x20\xcc\x87\0" /* offset 1197 */
+  "\x20\xcc\x8a\0" /* offset 1201 */
+  "\x20\xcc\xa8\0" /* offset 1205 */
+  "\x20\xcc\x83\0" /* offset 1209 */
+  "\x20\xcc\x8b\0" /* offset 1213 */
+  "\xc9\xa3\0" /* offset 1217 */
+  "\x6c\0" /* offset 1220 */
+  "\x78\0" /* offset 1222 */
+  "\xca\x95\0" /* offset 1224 */
+  "\xcc\x80\0" /* offset 1227 */
+  "\xcc\x81\0" /* offset 1230 */
+  "\xcc\x93\0" /* offset 1233 */
+  "\xcc\x88\xcc\x81\0" /* offset 1236 */
+  "\xca\xb9\0" /* offset 1241 */
+  "\x20\xcd\x85\0" /* offset 1244 */
+  "\x3b\0" /* offset 1248 */
+  "\xc2\xa8\xcc\x81\0" /* offset 1250 */
+  "\x20\xcc\x88\xcc\x81\0" /* offset 1255 */
+  "\xce\x91\xcc\x81\0" /* offset 1261 */
+  "\xc2\xb7\0" /* offset 1266 */
+  "\xce\x95\xcc\x81\0" /* offset 1269 */
+  "\xce\x97\xcc\x81\0" /* offset 1274 */
+  "\xce\x99\xcc\x81\0" /* offset 1279 */
+  "\xce\x9f\xcc\x81\0" /* offset 1284 */
+  "\xce\xa5\xcc\x81\0" /* offset 1289 */
+  "\xce\xa9\xcc\x81\0" /* offset 1294 */
+  "\xce\xb9\xcc\x88\xcc\x81\0" /* offset 1299 */
+  "\xce\x99\xcc\x88\0" /* offset 1306 */
+  "\xce\xa5\xcc\x88\0" /* offset 1311 */
+  "\xce\xb1\xcc\x81\0" /* offset 1316 */
+  "\xce\xb5\xcc\x81\0" /* offset 1321 */
+  "\xce\xb7\xcc\x81\0" /* offset 1326 */
+  "\xce\xb9\xcc\x81\0" /* offset 1331 */
+  "\xcf\x85\xcc\x88\xcc\x81\0" /* offset 1336 */
+  "\xce\xb9\xcc\x88\0" /* offset 1343 */
+  "\xcf\x85\xcc\x88\0" /* offset 1348 */
+  "\xce\xbf\xcc\x81\0" /* offset 1353 */
+  "\xcf\x85\xcc\x81\0" /* offset 1358 */
+  "\xcf\x89\xcc\x81\0" /* offset 1363 */
+  "\xce\xb2\0" /* offset 1368 */
+  "\xce\xb8\0" /* offset 1371 */
+  "\xce\xa5\0" /* offset 1374 */
+  "\xcf\x92\xcc\x81\0" /* offset 1377 */
+  "\xcf\x92\xcc\x88\0" /* offset 1382 */
+  "\xcf\x86\0" /* offset 1387 */
+  "\xcf\x80\0" /* offset 1390 */
+  "\xce\xba\0" /* offset 1393 */
+  "\xcf\x81\0" /* offset 1396 */
+  "\xcf\x82\0" /* offset 1399 */
+  "\xce\x98\0" /* offset 1402 */
+  "\xce\xb5\0" /* offset 1405 */
+  "\xd0\x95\xcc\x80\0" /* offset 1408 */
+  "\xd0\x95\xcc\x88\0" /* offset 1413 */
+  "\xd0\x93\xcc\x81\0" /* offset 1418 */
+  "\xd0\x86\xcc\x88\0" /* offset 1423 */
+  "\xd0\x9a\xcc\x81\0" /* offset 1428 */
+  "\xd0\x98\xcc\x80\0" /* offset 1433 */
+  "\xd0\xa3\xcc\x86\0" /* offset 1438 */
+  "\xd0\x98\xcc\x86\0" /* offset 1443 */
+  "\xd0\xb8\xcc\x86\0" /* offset 1448 */
+  "\xd0\xb5\xcc\x80\0" /* offset 1453 */
+  "\xd0\xb5\xcc\x88\0" /* offset 1458 */
+  "\xd0\xb3\xcc\x81\0" /* offset 1463 */
+  "\xd1\x96\xcc\x88\0" /* offset 1468 */
+  "\xd0\xba\xcc\x81\0" /* offset 1473 */
+  "\xd0\xb8\xcc\x80\0" /* offset 1478 */
+  "\xd1\x83\xcc\x86\0" /* offset 1483 */
+  "\xd1\xb4\xcc\x8f\0" /* offset 1488 */
+  "\xd1\xb5\xcc\x8f\0" /* offset 1493 */
+  "\xd0\x96\xcc\x86\0" /* offset 1498 */
+  "\xd0\xb6\xcc\x86\0" /* offset 1503 */
+  "\xd0\x90\xcc\x86\0" /* offset 1508 */
+  "\xd0\xb0\xcc\x86\0" /* offset 1513 */
+  "\xd0\x90\xcc\x88\0" /* offset 1518 */
+  "\xd0\xb0\xcc\x88\0" /* offset 1523 */
+  "\xd0\x95\xcc\x86\0" /* offset 1528 */
+  "\xd0\xb5\xcc\x86\0" /* offset 1533 */
+  "\xd3\x98\xcc\x88\0" /* offset 1538 */
+  "\xd3\x99\xcc\x88\0" /* offset 1543 */
+  "\xd0\x96\xcc\x88\0" /* offset 1548 */
+  "\xd0\xb6\xcc\x88\0" /* offset 1553 */
+  "\xd0\x97\xcc\x88\0" /* offset 1558 */
+  "\xd0\xb7\xcc\x88\0" /* offset 1563 */
+  "\xd0\x98\xcc\x84\0" /* offset 1568 */
+  "\xd0\xb8\xcc\x84\0" /* offset 1573 */
+  "\xd0\x98\xcc\x88\0" /* offset 1578 */
+  "\xd0\xb8\xcc\x88\0" /* offset 1583 */
+  "\xd0\x9e\xcc\x88\0" /* offset 1588 */
+  "\xd0\xbe\xcc\x88\0" /* offset 1593 */
+  "\xd3\xa8\xcc\x88\0" /* offset 1598 */
+  "\xd3\xa9\xcc\x88\0" /* offset 1603 */
+  "\xd0\xad\xcc\x88\0" /* offset 1608 */
+  "\xd1\x8d\xcc\x88\0" /* offset 1613 */
+  "\xd0\xa3\xcc\x84\0" /* offset 1618 */
+  "\xd1\x83\xcc\x84\0" /* offset 1623 */
+  "\xd0\xa3\xcc\x88\0" /* offset 1628 */
+  "\xd1\x83\xcc\x88\0" /* offset 1633 */
+  "\xd0\xa3\xcc\x8b\0" /* offset 1638 */
+  "\xd1\x83\xcc\x8b\0" /* offset 1643 */
+  "\xd0\xa7\xcc\x88\0" /* offset 1648 */
+  "\xd1\x87\xcc\x88\0" /* offset 1653 */
+  "\xd0\xab\xcc\x88\0" /* offset 1658 */
+  "\xd1\x8b\xcc\x88\0" /* offset 1663 */
+  "\xd5\xa5\xd6\x82\0" /* offset 1668 */
+  "\xd8\xa7\xd9\x93\0" /* offset 1673 */
+  "\xd8\xa7\xd9\x94\0" /* offset 1678 */
+  "\xd9\x88\xd9\x94\0" /* offset 1683 */
+  "\xd8\xa7\xd9\x95\0" /* offset 1688 */
+  "\xd9\x8a\xd9\x94\0" /* offset 1693 */
+  "\xd8\xa7\xd9\xb4\0" /* offset 1698 */
+  "\xd9\x88\xd9\xb4\0" /* offset 1703 */
+  "\xdb\x87\xd9\xb4\0" /* offset 1708 */
+  "\xd9\x8a\xd9\xb4\0" /* offset 1713 */
+  "\xdb\x95\xd9\x94\0" /* offset 1718 */
+  "\xdb\x81\xd9\x94\0" /* offset 1723 */
+  "\xdb\x92\xd9\x94\0" /* offset 1728 */
+  "\xe0\xa4\xa8\xe0\xa4\xbc\0" /* offset 1733 */
+  "\xe0\xa4\xb0\xe0\xa4\xbc\0" /* offset 1740 */
+  "\xe0\xa4\xb3\xe0\xa4\xbc\0" /* offset 1747 */
+  "\xe0\xa4\x95\xe0\xa4\xbc\0" /* offset 1754 */
+  "\xe0\xa4\x96\xe0\xa4\xbc\0" /* offset 1761 */
+  "\xe0\xa4\x97\xe0\xa4\xbc\0" /* offset 1768 */
+  "\xe0\xa4\x9c\xe0\xa4\xbc\0" /* offset 1775 */
+  "\xe0\xa4\xa1\xe0\xa4\xbc\0" /* offset 1782 */
+  "\xe0\xa4\xa2\xe0\xa4\xbc\0" /* offset 1789 */
+  "\xe0\xa4\xab\xe0\xa4\xbc\0" /* offset 1796 */
+  "\xe0\xa4\xaf\xe0\xa4\xbc\0" /* offset 1803 */
+  "\xe0\xa7\x87\xe0\xa6\xbe\0" /* offset 1810 */
+  "\xe0\xa7\x87\xe0\xa7\x97\0" /* offset 1817 */
+  "\xe0\xa6\xa1\xe0\xa6\xbc\0" /* offset 1824 */
+  "\xe0\xa6\xa2\xe0\xa6\xbc\0" /* offset 1831 */
+  "\xe0\xa6\xaf\xe0\xa6\xbc\0" /* offset 1838 */
+  "\xe0\xa8\xb2\xe0\xa8\xbc\0" /* offset 1845 */
+  "\xe0\xa8\xb8\xe0\xa8\xbc\0" /* offset 1852 */
+  "\xe0\xa8\x96\xe0\xa8\xbc\0" /* offset 1859 */
+  "\xe0\xa8\x97\xe0\xa8\xbc\0" /* offset 1866 */
+  "\xe0\xa8\x9c\xe0\xa8\xbc\0" /* offset 1873 */
+  "\xe0\xa8\xab\xe0\xa8\xbc\0" /* offset 1880 */
+  "\xe0\xad\x87\xe0\xad\x96\0" /* offset 1887 */
+  "\xe0\xad\x87\xe0\xac\xbe\0" /* offset 1894 */
+  "\xe0\xad\x87\xe0\xad\x97\0" /* offset 1901 */
+  "\xe0\xac\xa1\xe0\xac\xbc\0" /* offset 1908 */
+  "\xe0\xac\xa2\xe0\xac\xbc\0" /* offset 1915 */
+  "\xe0\xae\x92\xe0\xaf\x97\0" /* offset 1922 */
+  "\xe0\xaf\x86\xe0\xae\xbe\0" /* offset 1929 */
+  "\xe0\xaf\x87\xe0\xae\xbe\0" /* offset 1936 */
+  "\xe0\xaf\x86\xe0\xaf\x97\0" /* offset 1943 */
+  "\xe0\xb1\x86\xe0\xb1\x96\0" /* offset 1950 */
+  "\xe0\xb2\xbf\xe0\xb3\x95\0" /* offset 1957 */
+  "\xe0\xb3\x86\xe0\xb3\x95\0" /* offset 1964 */
+  "\xe0\xb3\x86\xe0\xb3\x96\0" /* offset 1971 */
+  "\xe0\xb3\x86\xe0\xb3\x82\0" /* offset 1978 */
+  "\xe0\xb3\x86\xe0\xb3\x82\xe0\xb3\x95\0" /* offset 1985 */
+  "\xe0\xb5\x86\xe0\xb4\xbe\0" /* offset 1995 */
+  "\xe0\xb5\x87\xe0\xb4\xbe\0" /* offset 2002 */
+  "\xe0\xb5\x86\xe0\xb5\x97\0" /* offset 2009 */
+  "\xe0\xb7\x99\xe0\xb7\x8a\0" /* offset 2016 */
+  "\xe0\xb7\x99\xe0\xb7\x8f\0" /* offset 2023 */
+  "\xe0\xb7\x99\xe0\xb7\x8f\xe0\xb7\x8a\0" /* offset 2030 */
+  "\xe0\xb7\x99\xe0\xb7\x9f\0" /* offset 2040 */
+  "\xe0\xb9\x8d\xe0\xb8\xb2\0" /* offset 2047 */
+  "\xe0\xbb\x8d\xe0\xba\xb2\0" /* offset 2054 */
+  "\xe0\xba\xab\xe0\xba\x99\0" /* offset 2061 */
+  "\xe0\xba\xab\xe0\xba\xa1\0" /* offset 2068 */
+  "\xe0\xbc\x8b\0" /* offset 2075 */
+  "\xe0\xbd\x82\xe0\xbe\xb7\0" /* offset 2079 */
+  "\xe0\xbd\x8c\xe0\xbe\xb7\0" /* offset 2086 */
+  "\xe0\xbd\x91\xe0\xbe\xb7\0" /* offset 2093 */
+  "\xe0\xbd\x96\xe0\xbe\xb7\0" /* offset 2100 */
+  "\xe0\xbd\x9b\xe0\xbe\xb7\0" /* offset 2107 */
+  "\xe0\xbd\x80\xe0\xbe\xb5\0" /* offset 2114 */
+  "\xe0\xbd\xb1\xe0\xbd\xb2\0" /* offset 2121 */
+  "\xe0\xbd\xb1\xe0\xbd\xb4\0" /* offset 2128 */
+  "\xe0\xbe\xb2\xe0\xbe\x80\0" /* offset 2135 */
+  "\xe0\xbe\xb2\xe0\xbd\xb1\xe0\xbe\x80\0" /* offset 2142 */
+  "\xe0\xbe\xb3\xe0\xbe\x80\0" /* offset 2152 */
+  "\xe0\xbe\xb3\xe0\xbd\xb1\xe0\xbe\x80\0" /* offset 2159 */
+  "\xe0\xbd\xb1\xe0\xbe\x80\0" /* offset 2169 */
+  "\xe0\xbe\x92\xe0\xbe\xb7\0" /* offset 2176 */
+  "\xe0\xbe\x9c\xe0\xbe\xb7\0" /* offset 2183 */
+  "\xe0\xbe\xa1\xe0\xbe\xb7\0" /* offset 2190 */
+  "\xe0\xbe\xa6\xe0\xbe\xb7\0" /* offset 2197 */
+  "\xe0\xbe\xab\xe0\xbe\xb7\0" /* offset 2204 */
+  "\xe0\xbe\x90\xe0\xbe\xb5\0" /* offset 2211 */
+  "\xe1\x80\xa5\xe1\x80\xae\0" /* offset 2218 */
+  "\x41\xcc\xa5\0" /* offset 2225 */
+  "\x61\xcc\xa5\0" /* offset 2229 */
+  "\x42\xcc\x87\0" /* offset 2233 */
+  "\x62\xcc\x87\0" /* offset 2237 */
+  "\x42\xcc\xa3\0" /* offset 2241 */
+  "\x62\xcc\xa3\0" /* offset 2245 */
+  "\x42\xcc\xb1\0" /* offset 2249 */
+  "\x62\xcc\xb1\0" /* offset 2253 */
+  "\x43\xcc\xa7\xcc\x81\0" /* offset 2257 */
+  "\x63\xcc\xa7\xcc\x81\0" /* offset 2263 */
+  "\x44\xcc\x87\0" /* offset 2269 */
+  "\x64\xcc\x87\0" /* offset 2273 */
+  "\x44\xcc\xa3\0" /* offset 2277 */
+  "\x64\xcc\xa3\0" /* offset 2281 */
+  "\x44\xcc\xb1\0" /* offset 2285 */
+  "\x64\xcc\xb1\0" /* offset 2289 */
+  "\x44\xcc\xa7\0" /* offset 2293 */
+  "\x64\xcc\xa7\0" /* offset 2297 */
+  "\x44\xcc\xad\0" /* offset 2301 */
+  "\x64\xcc\xad\0" /* offset 2305 */
+  "\x45\xcc\x84\xcc\x80\0" /* offset 2309 */
+  "\x65\xcc\x84\xcc\x80\0" /* offset 2315 */
+  "\x45\xcc\x84\xcc\x81\0" /* offset 2321 */
+  "\x65\xcc\x84\xcc\x81\0" /* offset 2327 */
+  "\x45\xcc\xad\0" /* offset 2333 */
+  "\x65\xcc\xad\0" /* offset 2337 */
+  "\x45\xcc\xb0\0" /* offset 2341 */
+  "\x65\xcc\xb0\0" /* offset 2345 */
+  "\x45\xcc\xa7\xcc\x86\0" /* offset 2349 */
+  "\x65\xcc\xa7\xcc\x86\0" /* offset 2355 */
+  "\x46\xcc\x87\0" /* offset 2361 */
+  "\x66\xcc\x87\0" /* offset 2365 */
+  "\x47\xcc\x84\0" /* offset 2369 */
+  "\x67\xcc\x84\0" /* offset 2373 */
+  "\x48\xcc\x87\0" /* offset 2377 */
+  "\x68\xcc\x87\0" /* offset 2381 */
+  "\x48\xcc\xa3\0" /* offset 2385 */
+  "\x68\xcc\xa3\0" /* offset 2389 */
+  "\x48\xcc\x88\0" /* offset 2393 */
+  "\x68\xcc\x88\0" /* offset 2397 */
+  "\x48\xcc\xa7\0" /* offset 2401 */
+  "\x68\xcc\xa7\0" /* offset 2405 */
+  "\x48\xcc\xae\0" /* offset 2409 */
+  "\x68\xcc\xae\0" /* offset 2413 */
+  "\x49\xcc\xb0\0" /* offset 2417 */
+  "\x69\xcc\xb0\0" /* offset 2421 */
+  "\x49\xcc\x88\xcc\x81\0" /* offset 2425 */
+  "\x69\xcc\x88\xcc\x81\0" /* offset 2431 */
+  "\x4b\xcc\x81\0" /* offset 2437 */
+  "\x6b\xcc\x81\0" /* offset 2441 */
+  "\x4b\xcc\xa3\0" /* offset 2445 */
+  "\x6b\xcc\xa3\0" /* offset 2449 */
+  "\x4b\xcc\xb1\0" /* offset 2453 */
+  "\x6b\xcc\xb1\0" /* offset 2457 */
+  "\x4c\xcc\xa3\0" /* offset 2461 */
+  "\x6c\xcc\xa3\0" /* offset 2465 */
+  "\x4c\xcc\xa3\xcc\x84\0" /* offset 2469 */
+  "\x6c\xcc\xa3\xcc\x84\0" /* offset 2475 */
+  "\x4c\xcc\xb1\0" /* offset 2481 */
+  "\x6c\xcc\xb1\0" /* offset 2485 */
+  "\x4c\xcc\xad\0" /* offset 2489 */
+  "\x6c\xcc\xad\0" /* offset 2493 */
+  "\x4d\xcc\x81\0" /* offset 2497 */
+  "\x6d\xcc\x81\0" /* offset 2501 */
+  "\x4d\xcc\x87\0" /* offset 2505 */
+  "\x6d\xcc\x87\0" /* offset 2509 */
+  "\x4d\xcc\xa3\0" /* offset 2513 */
+  "\x6d\xcc\xa3\0" /* offset 2517 */
+  "\x4e\xcc\x87\0" /* offset 2521 */
+  "\x6e\xcc\x87\0" /* offset 2525 */
+  "\x4e\xcc\xa3\0" /* offset 2529 */
+  "\x6e\xcc\xa3\0" /* offset 2533 */
+  "\x4e\xcc\xb1\0" /* offset 2537 */
+  "\x6e\xcc\xb1\0" /* offset 2541 */
+  "\x4e\xcc\xad\0" /* offset 2545 */
+  "\x6e\xcc\xad\0" /* offset 2549 */
+  "\x4f\xcc\x83\xcc\x81\0" /* offset 2553 */
+  "\x6f\xcc\x83\xcc\x81\0" /* offset 2559 */
+  "\x4f\xcc\x83\xcc\x88\0" /* offset 2565 */
+  "\x6f\xcc\x83\xcc\x88\0" /* offset 2571 */
+  "\x4f\xcc\x84\xcc\x80\0" /* offset 2577 */
+  "\x6f\xcc\x84\xcc\x80\0" /* offset 2583 */
+  "\x4f\xcc\x84\xcc\x81\0" /* offset 2589 */
+  "\x6f\xcc\x84\xcc\x81\0" /* offset 2595 */
+  "\x50\xcc\x81\0" /* offset 2601 */
+  "\x70\xcc\x81\0" /* offset 2605 */
+  "\x50\xcc\x87\0" /* offset 2609 */
+  "\x70\xcc\x87\0" /* offset 2613 */
+  "\x52\xcc\x87\0" /* offset 2617 */
+  "\x72\xcc\x87\0" /* offset 2621 */
+  "\x52\xcc\xa3\0" /* offset 2625 */
+  "\x72\xcc\xa3\0" /* offset 2629 */
+  "\x52\xcc\xa3\xcc\x84\0" /* offset 2633 */
+  "\x72\xcc\xa3\xcc\x84\0" /* offset 2639 */
+  "\x52\xcc\xb1\0" /* offset 2645 */
+  "\x72\xcc\xb1\0" /* offset 2649 */
+  "\x53\xcc\x87\0" /* offset 2653 */
+  "\x73\xcc\x87\0" /* offset 2657 */
+  "\x53\xcc\xa3\0" /* offset 2661 */
+  "\x73\xcc\xa3\0" /* offset 2665 */
+  "\x53\xcc\x81\xcc\x87\0" /* offset 2669 */
+  "\x73\xcc\x81\xcc\x87\0" /* offset 2675 */
+  "\x53\xcc\x8c\xcc\x87\0" /* offset 2681 */
+  "\x73\xcc\x8c\xcc\x87\0" /* offset 2687 */
+  "\x53\xcc\xa3\xcc\x87\0" /* offset 2693 */
+  "\x73\xcc\xa3\xcc\x87\0" /* offset 2699 */
+  "\x54\xcc\x87\0" /* offset 2705 */
+  "\x74\xcc\x87\0" /* offset 2709 */
+  "\x54\xcc\xa3\0" /* offset 2713 */
+  "\x74\xcc\xa3\0" /* offset 2717 */
+  "\x54\xcc\xb1\0" /* offset 2721 */
+  "\x74\xcc\xb1\0" /* offset 2725 */
+  "\x54\xcc\xad\0" /* offset 2729 */
+  "\x74\xcc\xad\0" /* offset 2733 */
+  "\x55\xcc\xa4\0" /* offset 2737 */
+  "\x75\xcc\xa4\0" /* offset 2741 */
+  "\x55\xcc\xb0\0" /* offset 2745 */
+  "\x75\xcc\xb0\0" /* offset 2749 */
+  "\x55\xcc\xad\0" /* offset 2753 */
+  "\x75\xcc\xad\0" /* offset 2757 */
+  "\x55\xcc\x83\xcc\x81\0" /* offset 2761 */
+  "\x75\xcc\x83\xcc\x81\0" /* offset 2767 */
+  "\x55\xcc\x84\xcc\x88\0" /* offset 2773 */
+  "\x75\xcc\x84\xcc\x88\0" /* offset 2779 */
+  "\x56\xcc\x83\0" /* offset 2785 */
+  "\x76\xcc\x83\0" /* offset 2789 */
+  "\x56\xcc\xa3\0" /* offset 2793 */
+  "\x76\xcc\xa3\0" /* offset 2797 */
+  "\x57\xcc\x80\0" /* offset 2801 */
+  "\x77\xcc\x80\0" /* offset 2805 */
+  "\x57\xcc\x81\0" /* offset 2809 */
+  "\x77\xcc\x81\0" /* offset 2813 */
+  "\x57\xcc\x88\0" /* offset 2817 */
+  "\x77\xcc\x88\0" /* offset 2821 */
+  "\x57\xcc\x87\0" /* offset 2825 */
+  "\x77\xcc\x87\0" /* offset 2829 */
+  "\x57\xcc\xa3\0" /* offset 2833 */
+  "\x77\xcc\xa3\0" /* offset 2837 */
+  "\x58\xcc\x87\0" /* offset 2841 */
+  "\x78\xcc\x87\0" /* offset 2845 */
+  "\x58\xcc\x88\0" /* offset 2849 */
+  "\x78\xcc\x88\0" /* offset 2853 */
+  "\x59\xcc\x87\0" /* offset 2857 */
+  "\x79\xcc\x87\0" /* offset 2861 */
+  "\x5a\xcc\x82\0" /* offset 2865 */
+  "\x7a\xcc\x82\0" /* offset 2869 */
+  "\x5a\xcc\xa3\0" /* offset 2873 */
+  "\x7a\xcc\xa3\0" /* offset 2877 */
+  "\x5a\xcc\xb1\0" /* offset 2881 */
+  "\x7a\xcc\xb1\0" /* offset 2885 */
+  "\x68\xcc\xb1\0" /* offset 2889 */
+  "\x74\xcc\x88\0" /* offset 2893 */
+  "\x77\xcc\x8a\0" /* offset 2897 */
+  "\x79\xcc\x8a\0" /* offset 2901 */
+  "\x61\xca\xbe\0" /* offset 2905 */
+  "\xc5\xbf\xcc\x87\0" /* offset 2909 */
+  "\x41\xcc\xa3\0" /* offset 2914 */
+  "\x61\xcc\xa3\0" /* offset 2918 */
+  "\x41\xcc\x89\0" /* offset 2922 */
+  "\x61\xcc\x89\0" /* offset 2926 */
+  "\x41\xcc\x82\xcc\x81\0" /* offset 2930 */
+  "\x61\xcc\x82\xcc\x81\0" /* offset 2936 */
+  "\x41\xcc\x82\xcc\x80\0" /* offset 2942 */
+  "\x61\xcc\x82\xcc\x80\0" /* offset 2948 */
+  "\x41\xcc\x82\xcc\x89\0" /* offset 2954 */
+  "\x61\xcc\x82\xcc\x89\0" /* offset 2960 */
+  "\x41\xcc\x82\xcc\x83\0" /* offset 2966 */
+  "\x61\xcc\x82\xcc\x83\0" /* offset 2972 */
+  "\x41\xcc\xa3\xcc\x82\0" /* offset 2978 */
+  "\x61\xcc\xa3\xcc\x82\0" /* offset 2984 */
+  "\x41\xcc\x86\xcc\x81\0" /* offset 2990 */
+  "\x61\xcc\x86\xcc\x81\0" /* offset 2996 */
+  "\x41\xcc\x86\xcc\x80\0" /* offset 3002 */
+  "\x61\xcc\x86\xcc\x80\0" /* offset 3008 */
+  "\x41\xcc\x86\xcc\x89\0" /* offset 3014 */
+  "\x61\xcc\x86\xcc\x89\0" /* offset 3020 */
+  "\x41\xcc\x86\xcc\x83\0" /* offset 3026 */
+  "\x61\xcc\x86\xcc\x83\0" /* offset 3032 */
+  "\x41\xcc\xa3\xcc\x86\0" /* offset 3038 */
+  "\x61\xcc\xa3\xcc\x86\0" /* offset 3044 */
+  "\x45\xcc\xa3\0" /* offset 3050 */
+  "\x65\xcc\xa3\0" /* offset 3054 */
+  "\x45\xcc\x89\0" /* offset 3058 */
+  "\x65\xcc\x89\0" /* offset 3062 */
+  "\x45\xcc\x83\0" /* offset 3066 */
+  "\x65\xcc\x83\0" /* offset 3070 */
+  "\x45\xcc\x82\xcc\x81\0" /* offset 3074 */
+  "\x65\xcc\x82\xcc\x81\0" /* offset 3080 */
+  "\x45\xcc\x82\xcc\x80\0" /* offset 3086 */
+  "\x65\xcc\x82\xcc\x80\0" /* offset 3092 */
+  "\x45\xcc\x82\xcc\x89\0" /* offset 3098 */
+  "\x65\xcc\x82\xcc\x89\0" /* offset 3104 */
+  "\x45\xcc\x82\xcc\x83\0" /* offset 3110 */
+  "\x65\xcc\x82\xcc\x83\0" /* offset 3116 */
+  "\x45\xcc\xa3\xcc\x82\0" /* offset 3122 */
+  "\x65\xcc\xa3\xcc\x82\0" /* offset 3128 */
+  "\x49\xcc\x89\0" /* offset 3134 */
+  "\x69\xcc\x89\0" /* offset 3138 */
+  "\x49\xcc\xa3\0" /* offset 3142 */
+  "\x69\xcc\xa3\0" /* offset 3146 */
+  "\x4f\xcc\xa3\0" /* offset 3150 */
+  "\x6f\xcc\xa3\0" /* offset 3154 */
+  "\x4f\xcc\x89\0" /* offset 3158 */
+  "\x6f\xcc\x89\0" /* offset 3162 */
+  "\x4f\xcc\x82\xcc\x81\0" /* offset 3166 */
+  "\x6f\xcc\x82\xcc\x81\0" /* offset 3172 */
+  "\x4f\xcc\x82\xcc\x80\0" /* offset 3178 */
+  "\x6f\xcc\x82\xcc\x80\0" /* offset 3184 */
+  "\x4f\xcc\x82\xcc\x89\0" /* offset 3190 */
+  "\x6f\xcc\x82\xcc\x89\0" /* offset 3196 */
+  "\x4f\xcc\x82\xcc\x83\0" /* offset 3202 */
+  "\x6f\xcc\x82\xcc\x83\0" /* offset 3208 */
+  "\x4f\xcc\xa3\xcc\x82\0" /* offset 3214 */
+  "\x6f\xcc\xa3\xcc\x82\0" /* offset 3220 */
+  "\x4f\xcc\x9b\xcc\x81\0" /* offset 3226 */
+  "\x6f\xcc\x9b\xcc\x81\0" /* offset 3232 */
+  "\x4f\xcc\x9b\xcc\x80\0" /* offset 3238 */
+  "\x6f\xcc\x9b\xcc\x80\0" /* offset 3244 */
+  "\x4f\xcc\x9b\xcc\x89\0" /* offset 3250 */
+  "\x6f\xcc\x9b\xcc\x89\0" /* offset 3256 */
+  "\x4f\xcc\x9b\xcc\x83\0" /* offset 3262 */
+  "\x6f\xcc\x9b\xcc\x83\0" /* offset 3268 */
+  "\x4f\xcc\x9b\xcc\xa3\0" /* offset 3274 */
+  "\x6f\xcc\x9b\xcc\xa3\0" /* offset 3280 */
+  "\x55\xcc\xa3\0" /* offset 3286 */
+  "\x75\xcc\xa3\0" /* offset 3290 */
+  "\x55\xcc\x89\0" /* offset 3294 */
+  "\x75\xcc\x89\0" /* offset 3298 */
+  "\x55\xcc\x9b\xcc\x81\0" /* offset 3302 */
+  "\x75\xcc\x9b\xcc\x81\0" /* offset 3308 */
+  "\x55\xcc\x9b\xcc\x80\0" /* offset 3314 */
+  "\x75\xcc\x9b\xcc\x80\0" /* offset 3320 */
+  "\x55\xcc\x9b\xcc\x89\0" /* offset 3326 */
+  "\x75\xcc\x9b\xcc\x89\0" /* offset 3332 */
+  "\x55\xcc\x9b\xcc\x83\0" /* offset 3338 */
+  "\x75\xcc\x9b\xcc\x83\0" /* offset 3344 */
+  "\x55\xcc\x9b\xcc\xa3\0" /* offset 3350 */
+  "\x75\xcc\x9b\xcc\xa3\0" /* offset 3356 */
+  "\x59\xcc\x80\0" /* offset 3362 */
+  "\x79\xcc\x80\0" /* offset 3366 */
+  "\x59\xcc\xa3\0" /* offset 3370 */
+  "\x79\xcc\xa3\0" /* offset 3374 */
+  "\x59\xcc\x89\0" /* offset 3378 */
+  "\x79\xcc\x89\0" /* offset 3382 */
+  "\x59\xcc\x83\0" /* offset 3386 */
+  "\x79\xcc\x83\0" /* offset 3390 */
+  "\xce\xb1\xcc\x93\0" /* offset 3394 */
+  "\xce\xb1\xcc\x94\0" /* offset 3399 */
+  "\xce\xb1\xcc\x93\xcc\x80\0" /* offset 3404 */
+  "\xce\xb1\xcc\x94\xcc\x80\0" /* offset 3411 */
+  "\xce\xb1\xcc\x93\xcc\x81\0" /* offset 3418 */
+  "\xce\xb1\xcc\x94\xcc\x81\0" /* offset 3425 */
+  "\xce\xb1\xcc\x93\xcd\x82\0" /* offset 3432 */
+  "\xce\xb1\xcc\x94\xcd\x82\0" /* offset 3439 */
+  "\xce\x91\xcc\x93\0" /* offset 3446 */
+  "\xce\x91\xcc\x94\0" /* offset 3451 */
+  "\xce\x91\xcc\x93\xcc\x80\0" /* offset 3456 */
+  "\xce\x91\xcc\x94\xcc\x80\0" /* offset 3463 */
+  "\xce\x91\xcc\x93\xcc\x81\0" /* offset 3470 */
+  "\xce\x91\xcc\x94\xcc\x81\0" /* offset 3477 */
+  "\xce\x91\xcc\x93\xcd\x82\0" /* offset 3484 */
+  "\xce\x91\xcc\x94\xcd\x82\0" /* offset 3491 */
+  "\xce\xb5\xcc\x93\0" /* offset 3498 */
+  "\xce\xb5\xcc\x94\0" /* offset 3503 */
+  "\xce\xb5\xcc\x93\xcc\x80\0" /* offset 3508 */
+  "\xce\xb5\xcc\x94\xcc\x80\0" /* offset 3515 */
+  "\xce\xb5\xcc\x93\xcc\x81\0" /* offset 3522 */
+  "\xce\xb5\xcc\x94\xcc\x81\0" /* offset 3529 */
+  "\xce\x95\xcc\x93\0" /* offset 3536 */
+  "\xce\x95\xcc\x94\0" /* offset 3541 */
+  "\xce\x95\xcc\x93\xcc\x80\0" /* offset 3546 */
+  "\xce\x95\xcc\x94\xcc\x80\0" /* offset 3553 */
+  "\xce\x95\xcc\x93\xcc\x81\0" /* offset 3560 */
+  "\xce\x95\xcc\x94\xcc\x81\0" /* offset 3567 */
+  "\xce\xb7\xcc\x93\0" /* offset 3574 */
+  "\xce\xb7\xcc\x94\0" /* offset 3579 */
+  "\xce\xb7\xcc\x93\xcc\x80\0" /* offset 3584 */
+  "\xce\xb7\xcc\x94\xcc\x80\0" /* offset 3591 */
+  "\xce\xb7\xcc\x93\xcc\x81\0" /* offset 3598 */
+  "\xce\xb7\xcc\x94\xcc\x81\0" /* offset 3605 */
+  "\xce\xb7\xcc\x93\xcd\x82\0" /* offset 3612 */
+  "\xce\xb7\xcc\x94\xcd\x82\0" /* offset 3619 */
+  "\xce\x97\xcc\x93\0" /* offset 3626 */
+  "\xce\x97\xcc\x94\0" /* offset 3631 */
+  "\xce\x97\xcc\x93\xcc\x80\0" /* offset 3636 */
+  "\xce\x97\xcc\x94\xcc\x80\0" /* offset 3643 */
+  "\xce\x97\xcc\x93\xcc\x81\0" /* offset 3650 */
+  "\xce\x97\xcc\x94\xcc\x81\0" /* offset 3657 */
+  "\xce\x97\xcc\x93\xcd\x82\0" /* offset 3664 */
+  "\xce\x97\xcc\x94\xcd\x82\0" /* offset 3671 */
+  "\xce\xb9\xcc\x93\0" /* offset 3678 */
+  "\xce\xb9\xcc\x94\0" /* offset 3683 */
+  "\xce\xb9\xcc\x93\xcc\x80\0" /* offset 3688 */
+  "\xce\xb9\xcc\x94\xcc\x80\0" /* offset 3695 */
+  "\xce\xb9\xcc\x93\xcc\x81\0" /* offset 3702 */
+  "\xce\xb9\xcc\x94\xcc\x81\0" /* offset 3709 */
+  "\xce\xb9\xcc\x93\xcd\x82\0" /* offset 3716 */
+  "\xce\xb9\xcc\x94\xcd\x82\0" /* offset 3723 */
+  "\xce\x99\xcc\x93\0" /* offset 3730 */
+  "\xce\x99\xcc\x94\0" /* offset 3735 */
+  "\xce\x99\xcc\x93\xcc\x80\0" /* offset 3740 */
+  "\xce\x99\xcc\x94\xcc\x80\0" /* offset 3747 */
+  "\xce\x99\xcc\x93\xcc\x81\0" /* offset 3754 */
+  "\xce\x99\xcc\x94\xcc\x81\0" /* offset 3761 */
+  "\xce\x99\xcc\x93\xcd\x82\0" /* offset 3768 */
+  "\xce\x99\xcc\x94\xcd\x82\0" /* offset 3775 */
+  "\xce\xbf\xcc\x93\0" /* offset 3782 */
+  "\xce\xbf\xcc\x94\0" /* offset 3787 */
+  "\xce\xbf\xcc\x93\xcc\x80\0" /* offset 3792 */
+  "\xce\xbf\xcc\x94\xcc\x80\0" /* offset 3799 */
+  "\xce\xbf\xcc\x93\xcc\x81\0" /* offset 3806 */
+  "\xce\xbf\xcc\x94\xcc\x81\0" /* offset 3813 */
+  "\xce\x9f\xcc\x93\0" /* offset 3820 */
+  "\xce\x9f\xcc\x94\0" /* offset 3825 */
+  "\xce\x9f\xcc\x93\xcc\x80\0" /* offset 3830 */
+  "\xce\x9f\xcc\x94\xcc\x80\0" /* offset 3837 */
+  "\xce\x9f\xcc\x93\xcc\x81\0" /* offset 3844 */
+  "\xce\x9f\xcc\x94\xcc\x81\0" /* offset 3851 */
+  "\xcf\x85\xcc\x93\0" /* offset 3858 */
+  "\xcf\x85\xcc\x94\0" /* offset 3863 */
+  "\xcf\x85\xcc\x93\xcc\x80\0" /* offset 3868 */
+  "\xcf\x85\xcc\x94\xcc\x80\0" /* offset 3875 */
+  "\xcf\x85\xcc\x93\xcc\x81\0" /* offset 3882 */
+  "\xcf\x85\xcc\x94\xcc\x81\0" /* offset 3889 */
+  "\xcf\x85\xcc\x93\xcd\x82\0" /* offset 3896 */
+  "\xcf\x85\xcc\x94\xcd\x82\0" /* offset 3903 */
+  "\xce\xa5\xcc\x94\0" /* offset 3910 */
+  "\xce\xa5\xcc\x94\xcc\x80\0" /* offset 3915 */
+  "\xce\xa5\xcc\x94\xcc\x81\0" /* offset 3922 */
+  "\xce\xa5\xcc\x94\xcd\x82\0" /* offset 3929 */
+  "\xcf\x89\xcc\x93\0" /* offset 3936 */
+  "\xcf\x89\xcc\x94\0" /* offset 3941 */
+  "\xcf\x89\xcc\x93\xcc\x80\0" /* offset 3946 */
+  "\xcf\x89\xcc\x94\xcc\x80\0" /* offset 3953 */
+  "\xcf\x89\xcc\x93\xcc\x81\0" /* offset 3960 */
+  "\xcf\x89\xcc\x94\xcc\x81\0" /* offset 3967 */
+  "\xcf\x89\xcc\x93\xcd\x82\0" /* offset 3974 */
+  "\xcf\x89\xcc\x94\xcd\x82\0" /* offset 3981 */
+  "\xce\xa9\xcc\x93\0" /* offset 3988 */
+  "\xce\xa9\xcc\x94\0" /* offset 3993 */
+  "\xce\xa9\xcc\x93\xcc\x80\0" /* offset 3998 */
+  "\xce\xa9\xcc\x94\xcc\x80\0" /* offset 4005 */
+  "\xce\xa9\xcc\x93\xcc\x81\0" /* offset 4012 */
+  "\xce\xa9\xcc\x94\xcc\x81\0" /* offset 4019 */
+  "\xce\xa9\xcc\x93\xcd\x82\0" /* offset 4026 */
+  "\xce\xa9\xcc\x94\xcd\x82\0" /* offset 4033 */
+  "\xce\xb1\xcc\x80\0" /* offset 4040 */
+  "\xce\xb5\xcc\x80\0" /* offset 4045 */
+  "\xce\xb7\xcc\x80\0" /* offset 4050 */
+  "\xce\xb9\xcc\x80\0" /* offset 4055 */
+  "\xce\xbf\xcc\x80\0" /* offset 4060 */
+  "\xcf\x85\xcc\x80\0" /* offset 4065 */
+  "\xcf\x89\xcc\x80\0" /* offset 4070 */
+  "\xce\xb1\xcc\x93\xcd\x85\0" /* offset 4075 */
+  "\xce\xb1\xcc\x94\xcd\x85\0" /* offset 4082 */
+  "\xce\xb1\xcc\x93\xcc\x80\xcd\x85\0" /* offset 4089 */
+  "\xce\xb1\xcc\x94\xcc\x80\xcd\x85\0" /* offset 4098 */
+  "\xce\xb1\xcc\x93\xcc\x81\xcd\x85\0" /* offset 4107 */
+  "\xce\xb1\xcc\x94\xcc\x81\xcd\x85\0" /* offset 4116 */
+  "\xce\xb1\xcc\x93\xcd\x82\xcd\x85\0" /* offset 4125 */
+  "\xce\xb1\xcc\x94\xcd\x82\xcd\x85\0" /* offset 4134 */
+  "\xce\x91\xcc\x93\xcd\x85\0" /* offset 4143 */
+  "\xce\x91\xcc\x94\xcd\x85\0" /* offset 4150 */
+  "\xce\x91\xcc\x93\xcc\x80\xcd\x85\0" /* offset 4157 */
+  "\xce\x91\xcc\x94\xcc\x80\xcd\x85\0" /* offset 4166 */
+  "\xce\x91\xcc\x93\xcc\x81\xcd\x85\0" /* offset 4175 */
+  "\xce\x91\xcc\x94\xcc\x81\xcd\x85\0" /* offset 4184 */
+  "\xce\x91\xcc\x93\xcd\x82\xcd\x85\0" /* offset 4193 */
+  "\xce\x91\xcc\x94\xcd\x82\xcd\x85\0" /* offset 4202 */
+  "\xce\xb7\xcc\x93\xcd\x85\0" /* offset 4211 */
+  "\xce\xb7\xcc\x94\xcd\x85\0" /* offset 4218 */
+  "\xce\xb7\xcc\x93\xcc\x80\xcd\x85\0" /* offset 4225 */
+  "\xce\xb7\xcc\x94\xcc\x80\xcd\x85\0" /* offset 4234 */
+  "\xce\xb7\xcc\x93\xcc\x81\xcd\x85\0" /* offset 4243 */
+  "\xce\xb7\xcc\x94\xcc\x81\xcd\x85\0" /* offset 4252 */
+  "\xce\xb7\xcc\x93\xcd\x82\xcd\x85\0" /* offset 4261 */
+  "\xce\xb7\xcc\x94\xcd\x82\xcd\x85\0" /* offset 4270 */
+  "\xce\x97\xcc\x93\xcd\x85\0" /* offset 4279 */
+  "\xce\x97\xcc\x94\xcd\x85\0" /* offset 4286 */
+  "\xce\x97\xcc\x93\xcc\x80\xcd\x85\0" /* offset 4293 */
+  "\xce\x97\xcc\x94\xcc\x80\xcd\x85\0" /* offset 4302 */
+  "\xce\x97\xcc\x93\xcc\x81\xcd\x85\0" /* offset 4311 */
+  "\xce\x97\xcc\x94\xcc\x81\xcd\x85\0" /* offset 4320 */
+  "\xce\x97\xcc\x93\xcd\x82\xcd\x85\0" /* offset 4329 */
+  "\xce\x97\xcc\x94\xcd\x82\xcd\x85\0" /* offset 4338 */
+  "\xcf\x89\xcc\x93\xcd\x85\0" /* offset 4347 */
+  "\xcf\x89\xcc\x94\xcd\x85\0" /* offset 4354 */
+  "\xcf\x89\xcc\x93\xcc\x80\xcd\x85\0" /* offset 4361 */
+  "\xcf\x89\xcc\x94\xcc\x80\xcd\x85\0" /* offset 4370 */
+  "\xcf\x89\xcc\x93\xcc\x81\xcd\x85\0" /* offset 4379 */
+  "\xcf\x89\xcc\x94\xcc\x81\xcd\x85\0" /* offset 4388 */
+  "\xcf\x89\xcc\x93\xcd\x82\xcd\x85\0" /* offset 4397 */
+  "\xcf\x89\xcc\x94\xcd\x82\xcd\x85\0" /* offset 4406 */
+  "\xce\xa9\xcc\x93\xcd\x85\0" /* offset 4415 */
+  "\xce\xa9\xcc\x94\xcd\x85\0" /* offset 4422 */
+  "\xce\xa9\xcc\x93\xcc\x80\xcd\x85\0" /* offset 4429 */
+  "\xce\xa9\xcc\x94\xcc\x80\xcd\x85\0" /* offset 4438 */
+  "\xce\xa9\xcc\x93\xcc\x81\xcd\x85\0" /* offset 4447 */
+  "\xce\xa9\xcc\x94\xcc\x81\xcd\x85\0" /* offset 4456 */
+  "\xce\xa9\xcc\x93\xcd\x82\xcd\x85\0" /* offset 4465 */
+  "\xce\xa9\xcc\x94\xcd\x82\xcd\x85\0" /* offset 4474 */
+  "\xce\xb1\xcc\x86\0" /* offset 4483 */
+  "\xce\xb1\xcc\x84\0" /* offset 4488 */
+  "\xce\xb1\xcc\x80\xcd\x85\0" /* offset 4493 */
+  "\xce\xb1\xcd\x85\0" /* offset 4500 */
+  "\xce\xb1\xcc\x81\xcd\x85\0" /* offset 4505 */
+  "\xce\xb1\xcd\x82\0" /* offset 4512 */
+  "\xce\xb1\xcd\x82\xcd\x85\0" /* offset 4517 */
+  "\xce\x91\xcc\x86\0" /* offset 4524 */
+  "\xce\x91\xcc\x84\0" /* offset 4529 */
+  "\xce\x91\xcc\x80\0" /* offset 4534 */
+  "\xce\x91\xcd\x85\0" /* offset 4539 */
+  "\x20\xcc\x93\0" /* offset 4544 */
+  "\xce\xb9\0" /* offset 4548 */
+  "\x20\xcd\x82\0" /* offset 4551 */
+  "\xc2\xa8\xcd\x82\0" /* offset 4555 */
+  "\x20\xcc\x88\xcd\x82\0" /* offset 4560 */
+  "\xce\xb7\xcc\x80\xcd\x85\0" /* offset 4566 */
+  "\xce\xb7\xcd\x85\0" /* offset 4573 */
+  "\xce\xb7\xcc\x81\xcd\x85\0" /* offset 4578 */
+  "\xce\xb7\xcd\x82\0" /* offset 4585 */
+  "\xce\xb7\xcd\x82\xcd\x85\0" /* offset 4590 */
+  "\xce\x95\xcc\x80\0" /* offset 4597 */
+  "\xce\x97\xcc\x80\0" /* offset 4602 */
+  "\xce\x97\xcd\x85\0" /* offset 4607 */
+  "\xe1\xbe\xbf\xcc\x80\0" /* offset 4612 */
+  "\x20\xcc\x93\xcc\x80\0" /* offset 4618 */
+  "\xe1\xbe\xbf\xcc\x81\0" /* offset 4624 */
+  "\x20\xcc\x93\xcc\x81\0" /* offset 4630 */
+  "\xe1\xbe\xbf\xcd\x82\0" /* offset 4636 */
+  "\x20\xcc\x93\xcd\x82\0" /* offset 4642 */
+  "\xce\xb9\xcc\x86\0" /* offset 4648 */
+  "\xce\xb9\xcc\x84\0" /* offset 4653 */
+  "\xce\xb9\xcc\x88\xcc\x80\0" /* offset 4658 */
+  "\xce\xb9\xcd\x82\0" /* offset 4665 */
+  "\xce\xb9\xcc\x88\xcd\x82\0" /* offset 4670 */
+  "\xce\x99\xcc\x86\0" /* offset 4677 */
+  "\xce\x99\xcc\x84\0" /* offset 4682 */
+  "\xce\x99\xcc\x80\0" /* offset 4687 */
+  "\xe1\xbf\xbe\xcc\x80\0" /* offset 4692 */
+  "\x20\xcc\x94\xcc\x80\0" /* offset 4698 */
+  "\xe1\xbf\xbe\xcc\x81\0" /* offset 4704 */
+  "\x20\xcc\x94\xcc\x81\0" /* offset 4710 */
+  "\xe1\xbf\xbe\xcd\x82\0" /* offset 4716 */
+  "\x20\xcc\x94\xcd\x82\0" /* offset 4722 */
+  "\xcf\x85\xcc\x86\0" /* offset 4728 */
+  "\xcf\x85\xcc\x84\0" /* offset 4733 */
+  "\xcf\x85\xcc\x88\xcc\x80\0" /* offset 4738 */
+  "\xcf\x81\xcc\x93\0" /* offset 4745 */
+  "\xcf\x81\xcc\x94\0" /* offset 4750 */
+  "\xcf\x85\xcd\x82\0" /* offset 4755 */
+  "\xcf\x85\xcc\x88\xcd\x82\0" /* offset 4760 */
+  "\xce\xa5\xcc\x86\0" /* offset 4767 */
+  "\xce\xa5\xcc\x84\0" /* offset 4772 */
+  "\xce\xa5\xcc\x80\0" /* offset 4777 */
+  "\xce\xa1\xcc\x94\0" /* offset 4782 */
+  "\xc2\xa8\xcc\x80\0" /* offset 4787 */
+  "\x20\xcc\x88\xcc\x80\0" /* offset 4792 */
+  "\x60\0" /* offset 4798 */
+  "\xcf\x89\xcc\x80\xcd\x85\0" /* offset 4800 */
+  "\xcf\x89\xcd\x85\0" /* offset 4807 */
+  "\xcf\x89\xcc\x81\xcd\x85\0" /* offset 4812 */
+  "\xcf\x89\xcd\x82\0" /* offset 4819 */
+  "\xcf\x89\xcd\x82\xcd\x85\0" /* offset 4824 */
+  "\xce\x9f\xcc\x80\0" /* offset 4831 */
+  "\xce\xa9\xcc\x80\0" /* offset 4836 */
+  "\xce\xa9\xcd\x85\0" /* offset 4841 */
+  "\xc2\xb4\0" /* offset 4846 */
+  "\x20\xcc\x94\0" /* offset 4849 */
+  "\xe2\x80\x82\0" /* offset 4853 */
+  "\xe2\x80\x83\0" /* offset 4857 */
+  "\xe2\x80\x90\0" /* offset 4861 */
+  "\x20\xcc\xb3\0" /* offset 4865 */
+  "\x2e\0" /* offset 4869 */
+  "\x2e\x2e\0" /* offset 4871 */
+  "\x2e\x2e\x2e\0" /* offset 4874 */
+  "\xe2\x80\xb2\xe2\x80\xb2\0" /* offset 4878 */
+  "\xe2\x80\xb2\xe2\x80\xb2\xe2\x80\xb2\0" /* offset 4885 */
+  "\xe2\x80\xb5\xe2\x80\xb5\0" /* offset 4895 */
+  "\xe2\x80\xb5\xe2\x80\xb5\xe2\x80\xb5\0" /* offset 4902 */
+  "\x21\x21\0" /* offset 4912 */
+  "\x20\xcc\x85\0" /* offset 4915 */
+  "\x3f\x3f\0" /* offset 4919 */
+  "\x3f\x21\0" /* offset 4922 */
+  "\x21\x3f\0" /* offset 4925 */
+  "\xe2\x80\xb2\xe2\x80\xb2\xe2\x80\xb2\xe2\x80\xb2\0" /* offset 4928 */
+  "\x30\0" /* offset 4941 */
+  "\x69\0" /* offset 4943 */
+  "\x34\0" /* offset 4945 */
+  "\x35\0" /* offset 4947 */
+  "\x36\0" /* offset 4949 */
+  "\x37\0" /* offset 4951 */
+  "\x38\0" /* offset 4953 */
+  "\x39\0" /* offset 4955 */
+  "\x2b\0" /* offset 4957 */
+  "\xe2\x88\x92\0" /* offset 4959 */
+  "\x3d\0" /* offset 4963 */
+  "\x28\0" /* offset 4965 */
+  "\x29\0" /* offset 4967 */
+  "\x6e\0" /* offset 4969 */
+  "\x52\x73\0" /* offset 4971 */
+  "\x61\x2f\x63\0" /* offset 4974 */
+  "\x61\x2f\x73\0" /* offset 4978 */
+  "\x43\0" /* offset 4982 */
+  "\xc2\xb0\x43\0" /* offset 4984 */
+  "\x63\x2f\x6f\0" /* offset 4988 */
+  "\x63\x2f\x75\0" /* offset 4992 */
+  "\xc6\x90\0" /* offset 4996 */
+  "\xc2\xb0\x46\0" /* offset 4999 */
+  "\x67\0" /* offset 5003 */
+  "\x48\0" /* offset 5005 */
+  "\xc4\xa7\0" /* offset 5007 */
+  "\x49\0" /* offset 5010 */
+  "\x4c\0" /* offset 5012 */
+  "\x4e\0" /* offset 5014 */
+  "\x4e\x6f\0" /* offset 5016 */
+  "\x50\0" /* offset 5019 */
+  "\x51\0" /* offset 5021 */
+  "\x52\0" /* offset 5023 */
+  "\x53\x4d\0" /* offset 5025 */
+  "\x54\x45\x4c\0" /* offset 5028 */
+  "\x54\x4d\0" /* offset 5032 */
+  "\x5a\0" /* offset 5035 */
+  "\xce\xa9\0" /* offset 5037 */
+  "\x4b\0" /* offset 5040 */
+  "\x42\0" /* offset 5042 */
+  "\x65\0" /* offset 5044 */
+  "\x45\0" /* offset 5046 */
+  "\x46\0" /* offset 5048 */
+  "\x4d\0" /* offset 5050 */
+  "\xd7\x90\0" /* offset 5052 */
+  "\xd7\x91\0" /* offset 5055 */
+  "\xd7\x92\0" /* offset 5058 */
+  "\xd7\x93\0" /* offset 5061 */
+  "\xce\xb3\0" /* offset 5064 */
+  "\xce\x93\0" /* offset 5067 */
+  "\xce\xa0\0" /* offset 5070 */
+  "\xe2\x88\x91\0" /* offset 5073 */
+  "\x44\0" /* offset 5077 */
+  "\x64\0" /* offset 5079 */
+  "\x31\xe2\x81\x84\x33\0" /* offset 5081 */
+  "\x32\xe2\x81\x84\x33\0" /* offset 5087 */
+  "\x31\xe2\x81\x84\x35\0" /* offset 5093 */
+  "\x32\xe2\x81\x84\x35\0" /* offset 5099 */
+  "\x33\xe2\x81\x84\x35\0" /* offset 5105 */
+  "\x34\xe2\x81\x84\x35\0" /* offset 5111 */
+  "\x31\xe2\x81\x84\x36\0" /* offset 5117 */
+  "\x35\xe2\x81\x84\x36\0" /* offset 5123 */
+  "\x31\xe2\x81\x84\x38\0" /* offset 5129 */
+  "\x33\xe2\x81\x84\x38\0" /* offset 5135 */
+  "\x35\xe2\x81\x84\x38\0" /* offset 5141 */
+  "\x37\xe2\x81\x84\x38\0" /* offset 5147 */
+  "\x31\xe2\x81\x84\0" /* offset 5153 */
+  "\x49\x49\0" /* offset 5158 */
+  "\x49\x49\x49\0" /* offset 5161 */
+  "\x49\x56\0" /* offset 5165 */
+  "\x56\0" /* offset 5168 */
+  "\x56\x49\0" /* offset 5170 */
+  "\x56\x49\x49\0" /* offset 5173 */
+  "\x56\x49\x49\x49\0" /* offset 5177 */
+  "\x49\x58\0" /* offset 5182 */
+  "\x58\0" /* offset 5185 */
+  "\x58\x49\0" /* offset 5187 */
+  "\x58\x49\x49\0" /* offset 5190 */
+  "\x69\x69\0" /* offset 5194 */
+  "\x69\x69\x69\0" /* offset 5197 */
+  "\x69\x76\0" /* offset 5201 */
+  "\x76\0" /* offset 5204 */
+  "\x76\x69\0" /* offset 5206 */
+  "\x76\x69\x69\0" /* offset 5209 */
+  "\x76\x69\x69\x69\0" /* offset 5213 */
+  "\x69\x78\0" /* offset 5218 */
+  "\x78\x69\0" /* offset 5221 */
+  "\x78\x69\x69\0" /* offset 5224 */
+  "\x63\0" /* offset 5228 */
+  "\x6d\0" /* offset 5230 */
+  "\xe2\x86\x90\xcc\xb8\0" /* offset 5232 */
+  "\xe2\x86\x92\xcc\xb8\0" /* offset 5238 */
+  "\xe2\x86\x94\xcc\xb8\0" /* offset 5244 */
+  "\xe2\x87\x90\xcc\xb8\0" /* offset 5250 */
+  "\xe2\x87\x94\xcc\xb8\0" /* offset 5256 */
+  "\xe2\x87\x92\xcc\xb8\0" /* offset 5262 */
+  "\xe2\x88\x83\xcc\xb8\0" /* offset 5268 */
+  "\xe2\x88\x88\xcc\xb8\0" /* offset 5274 */
+  "\xe2\x88\x8b\xcc\xb8\0" /* offset 5280 */
+  "\xe2\x88\xa3\xcc\xb8\0" /* offset 5286 */
+  "\xe2\x88\xa5\xcc\xb8\0" /* offset 5292 */
+  "\xe2\x88\xab\xe2\x88\xab\0" /* offset 5298 */
+  "\xe2\x88\xab\xe2\x88\xab\xe2\x88\xab\0" /* offset 5305 */
+  "\xe2\x88\xae\xe2\x88\xae\0" /* offset 5315 */
+  "\xe2\x88\xae\xe2\x88\xae\xe2\x88\xae\0" /* offset 5322 */
+  "\xe2\x88\xbc\xcc\xb8\0" /* offset 5332 */
+  "\xe2\x89\x83\xcc\xb8\0" /* offset 5338 */
+  "\xe2\x89\x85\xcc\xb8\0" /* offset 5344 */
+  "\xe2\x89\x88\xcc\xb8\0" /* offset 5350 */
+  "\x3d\xcc\xb8\0" /* offset 5356 */
+  "\xe2\x89\xa1\xcc\xb8\0" /* offset 5360 */
+  "\xe2\x89\x8d\xcc\xb8\0" /* offset 5366 */
+  "\x3c\xcc\xb8\0" /* offset 5372 */
+  "\x3e\xcc\xb8\0" /* offset 5376 */
+  "\xe2\x89\xa4\xcc\xb8\0" /* offset 5380 */
+  "\xe2\x89\xa5\xcc\xb8\0" /* offset 5386 */
+  "\xe2\x89\xb2\xcc\xb8\0" /* offset 5392 */
+  "\xe2\x89\xb3\xcc\xb8\0" /* offset 5398 */
+  "\xe2\x89\xb6\xcc\xb8\0" /* offset 5404 */
+  "\xe2\x89\xb7\xcc\xb8\0" /* offset 5410 */
+  "\xe2\x89\xba\xcc\xb8\0" /* offset 5416 */
+  "\xe2\x89\xbb\xcc\xb8\0" /* offset 5422 */
+  "\xe2\x8a\x82\xcc\xb8\0" /* offset 5428 */
+  "\xe2\x8a\x83\xcc\xb8\0" /* offset 5434 */
+  "\xe2\x8a\x86\xcc\xb8\0" /* offset 5440 */
+  "\xe2\x8a\x87\xcc\xb8\0" /* offset 5446 */
+  "\xe2\x8a\xa2\xcc\xb8\0" /* offset 5452 */
+  "\xe2\x8a\xa8\xcc\xb8\0" /* offset 5458 */
+  "\xe2\x8a\xa9\xcc\xb8\0" /* offset 5464 */
+  "\xe2\x8a\xab\xcc\xb8\0" /* offset 5470 */
+  "\xe2\x89\xbc\xcc\xb8\0" /* offset 5476 */
+  "\xe2\x89\xbd\xcc\xb8\0" /* offset 5482 */
+  "\xe2\x8a\x91\xcc\xb8\0" /* offset 5488 */
+  "\xe2\x8a\x92\xcc\xb8\0" /* offset 5494 */
+  "\xe2\x8a\xb2\xcc\xb8\0" /* offset 5500 */
+  "\xe2\x8a\xb3\xcc\xb8\0" /* offset 5506 */
+  "\xe2\x8a\xb4\xcc\xb8\0" /* offset 5512 */
+  "\xe2\x8a\xb5\xcc\xb8\0" /* offset 5518 */
+  "\xe3\x80\x88\0" /* offset 5524 */
+  "\xe3\x80\x89\0" /* offset 5528 */
+  "\x31\x30\0" /* offset 5532 */
+  "\x31\x31\0" /* offset 5535 */
+  "\x31\x32\0" /* offset 5538 */
+  "\x31\x33\0" /* offset 5541 */
+  "\x31\x34\0" /* offset 5544 */
+  "\x31\x35\0" /* offset 5547 */
+  "\x31\x36\0" /* offset 5550 */
+  "\x31\x37\0" /* offset 5553 */
+  "\x31\x38\0" /* offset 5556 */
+  "\x31\x39\0" /* offset 5559 */
+  "\x32\x30\0" /* offset 5562 */
+  "\x28\x31\x29\0" /* offset 5565 */
+  "\x28\x32\x29\0" /* offset 5569 */
+  "\x28\x33\x29\0" /* offset 5573 */
+  "\x28\x34\x29\0" /* offset 5577 */
+  "\x28\x35\x29\0" /* offset 5581 */
+  "\x28\x36\x29\0" /* offset 5585 */
+  "\x28\x37\x29\0" /* offset 5589 */
+  "\x28\x38\x29\0" /* offset 5593 */
+  "\x28\x39\x29\0" /* offset 5597 */
+  "\x28\x31\x30\x29\0" /* offset 5601 */
+  "\x28\x31\x31\x29\0" /* offset 5606 */
+  "\x28\x31\x32\x29\0" /* offset 5611 */
+  "\x28\x31\x33\x29\0" /* offset 5616 */
+  "\x28\x31\x34\x29\0" /* offset 5621 */
+  "\x28\x31\x35\x29\0" /* offset 5626 */
+  "\x28\x31\x36\x29\0" /* offset 5631 */
+  "\x28\x31\x37\x29\0" /* offset 5636 */
+  "\x28\x31\x38\x29\0" /* offset 5641 */
+  "\x28\x31\x39\x29\0" /* offset 5646 */
+  "\x28\x32\x30\x29\0" /* offset 5651 */
+  "\x31\x2e\0" /* offset 5656 */
+  "\x32\x2e\0" /* offset 5659 */
+  "\x33\x2e\0" /* offset 5662 */
+  "\x34\x2e\0" /* offset 5665 */
+  "\x35\x2e\0" /* offset 5668 */
+  "\x36\x2e\0" /* offset 5671 */
+  "\x37\x2e\0" /* offset 5674 */
+  "\x38\x2e\0" /* offset 5677 */
+  "\x39\x2e\0" /* offset 5680 */
+  "\x31\x30\x2e\0" /* offset 5683 */
+  "\x31\x31\x2e\0" /* offset 5687 */
+  "\x31\x32\x2e\0" /* offset 5691 */
+  "\x31\x33\x2e\0" /* offset 5695 */
+  "\x31\x34\x2e\0" /* offset 5699 */
+  "\x31\x35\x2e\0" /* offset 5703 */
+  "\x31\x36\x2e\0" /* offset 5707 */
+  "\x31\x37\x2e\0" /* offset 5711 */
+  "\x31\x38\x2e\0" /* offset 5715 */
+  "\x31\x39\x2e\0" /* offset 5719 */
+  "\x32\x30\x2e\0" /* offset 5723 */
+  "\x28\x61\x29\0" /* offset 5727 */
+  "\x28\x62\x29\0" /* offset 5731 */
+  "\x28\x63\x29\0" /* offset 5735 */
+  "\x28\x64\x29\0" /* offset 5739 */
+  "\x28\x65\x29\0" /* offset 5743 */
+  "\x28\x66\x29\0" /* offset 5747 */
+  "\x28\x67\x29\0" /* offset 5751 */
+  "\x28\x68\x29\0" /* offset 5755 */
+  "\x28\x69\x29\0" /* offset 5759 */
+  "\x28\x6a\x29\0" /* offset 5763 */
+  "\x28\x6b\x29\0" /* offset 5767 */
+  "\x28\x6c\x29\0" /* offset 5771 */
+  "\x28\x6d\x29\0" /* offset 5775 */
+  "\x28\x6e\x29\0" /* offset 5779 */
+  "\x28\x6f\x29\0" /* offset 5783 */
+  "\x28\x70\x29\0" /* offset 5787 */
+  "\x28\x71\x29\0" /* offset 5791 */
+  "\x28\x72\x29\0" /* offset 5795 */
+  "\x28\x73\x29\0" /* offset 5799 */
+  "\x28\x74\x29\0" /* offset 5803 */
+  "\x28\x75\x29\0" /* offset 5807 */
+  "\x28\x76\x29\0" /* offset 5811 */
+  "\x28\x77\x29\0" /* offset 5815 */
+  "\x28\x78\x29\0" /* offset 5819 */
+  "\x28\x79\x29\0" /* offset 5823 */
+  "\x28\x7a\x29\0" /* offset 5827 */
+  "\x41\0" /* offset 5831 */
+  "\x47\0" /* offset 5833 */
+  "\x4a\0" /* offset 5835 */
+  "\x4f\0" /* offset 5837 */
+  "\x53\0" /* offset 5839 */
+  "\x54\0" /* offset 5841 */
+  "\x55\0" /* offset 5843 */
+  "\x57\0" /* offset 5845 */
+  "\x59\0" /* offset 5847 */
+  "\x62\0" /* offset 5849 */
+  "\x66\0" /* offset 5851 */
+  "\x6b\0" /* offset 5853 */
+  "\x70\0" /* offset 5855 */
+  "\x71\0" /* offset 5857 */
+  "\x74\0" /* offset 5859 */
+  "\x75\0" /* offset 5861 */
+  "\x7a\0" /* offset 5863 */
+  "\xe2\x88\xab\xe2\x88\xab\xe2\x88\xab\xe2\x88\xab\0" /* offset 5865 */
+  "\x3a\x3a\x3d\0" /* offset 5878 */
+  "\x3d\x3d\0" /* offset 5882 */
+  "\x3d\x3d\x3d\0" /* offset 5885 */
+  "\xe2\xab\x9d\xcc\xb8\0" /* offset 5889 */
+  "\xe6\xaf\x8d\0" /* offset 5895 */
+  "\xe9\xbe\x9f\0" /* offset 5899 */
+  "\xe4\xb8\x80\0" /* offset 5903 */
+  "\xe4\xb8\xa8\0" /* offset 5907 */
+  "\xe4\xb8\xb6\0" /* offset 5911 */
+  "\xe4\xb8\xbf\0" /* offset 5915 */
+  "\xe4\xb9\x99\0" /* offset 5919 */
+  "\xe4\xba\x85\0" /* offset 5923 */
+  "\xe4\xba\x8c\0" /* offset 5927 */
+  "\xe4\xba\xa0\0" /* offset 5931 */
+  "\xe4\xba\xba\0" /* offset 5935 */
+  "\xe5\x84\xbf\0" /* offset 5939 */
+  "\xe5\x85\xa5\0" /* offset 5943 */
+  "\xe5\x85\xab\0" /* offset 5947 */
+  "\xe5\x86\x82\0" /* offset 5951 */
+  "\xe5\x86\x96\0" /* offset 5955 */
+  "\xe5\x86\xab\0" /* offset 5959 */
+  "\xe5\x87\xa0\0" /* offset 5963 */
+  "\xe5\x87\xb5\0" /* offset 5967 */
+  "\xe5\x88\x80\0" /* offset 5971 */
+  "\xe5\x8a\x9b\0" /* offset 5975 */
+  "\xe5\x8b\xb9\0" /* offset 5979 */
+  "\xe5\x8c\x95\0" /* offset 5983 */
+  "\xe5\x8c\x9a\0" /* offset 5987 */
+  "\xe5\x8c\xb8\0" /* offset 5991 */
+  "\xe5\x8d\x81\0" /* offset 5995 */
+  "\xe5\x8d\x9c\0" /* offset 5999 */
+  "\xe5\x8d\xa9\0" /* offset 6003 */
+  "\xe5\x8e\x82\0" /* offset 6007 */
+  "\xe5\x8e\xb6\0" /* offset 6011 */
+  "\xe5\x8f\x88\0" /* offset 6015 */
+  "\xe5\x8f\xa3\0" /* offset 6019 */
+  "\xe5\x9b\x97\0" /* offset 6023 */
+  "\xe5\x9c\x9f\0" /* offset 6027 */
+  "\xe5\xa3\xab\0" /* offset 6031 */
+  "\xe5\xa4\x82\0" /* offset 6035 */
+  "\xe5\xa4\x8a\0" /* offset 6039 */
+  "\xe5\xa4\x95\0" /* offset 6043 */
+  "\xe5\xa4\xa7\0" /* offset 6047 */
+  "\xe5\xa5\xb3\0" /* offset 6051 */
+  "\xe5\xad\x90\0" /* offset 6055 */
+  "\xe5\xae\x80\0" /* offset 6059 */
+  "\xe5\xaf\xb8\0" /* offset 6063 */
+  "\xe5\xb0\x8f\0" /* offset 6067 */
+  "\xe5\xb0\xa2\0" /* offset 6071 */
+  "\xe5\xb0\xb8\0" /* offset 6075 */
+  "\xe5\xb1\xae\0" /* offset 6079 */
+  "\xe5\xb1\xb1\0" /* offset 6083 */
+  "\xe5\xb7\x9b\0" /* offset 6087 */
+  "\xe5\xb7\xa5\0" /* offset 6091 */
+  "\xe5\xb7\xb1\0" /* offset 6095 */
+  "\xe5\xb7\xbe\0" /* offset 6099 */
+  "\xe5\xb9\xb2\0" /* offset 6103 */
+  "\xe5\xb9\xba\0" /* offset 6107 */
+  "\xe5\xb9\xbf\0" /* offset 6111 */
+  "\xe5\xbb\xb4\0" /* offset 6115 */
+  "\xe5\xbb\xbe\0" /* offset 6119 */
+  "\xe5\xbc\x8b\0" /* offset 6123 */
+  "\xe5\xbc\x93\0" /* offset 6127 */
+  "\xe5\xbd\x90\0" /* offset 6131 */
+  "\xe5\xbd\xa1\0" /* offset 6135 */
+  "\xe5\xbd\xb3\0" /* offset 6139 */
+  "\xe5\xbf\x83\0" /* offset 6143 */
+  "\xe6\x88\x88\0" /* offset 6147 */
+  "\xe6\x88\xb6\0" /* offset 6151 */
+  "\xe6\x89\x8b\0" /* offset 6155 */
+  "\xe6\x94\xaf\0" /* offset 6159 */
+  "\xe6\x94\xb4\0" /* offset 6163 */
+  "\xe6\x96\x87\0" /* offset 6167 */
+  "\xe6\x96\x97\0" /* offset 6171 */
+  "\xe6\x96\xa4\0" /* offset 6175 */
+  "\xe6\x96\xb9\0" /* offset 6179 */
+  "\xe6\x97\xa0\0" /* offset 6183 */
+  "\xe6\x97\xa5\0" /* offset 6187 */
+  "\xe6\x9b\xb0\0" /* offset 6191 */
+  "\xe6\x9c\x88\0" /* offset 6195 */
+  "\xe6\x9c\xa8\0" /* offset 6199 */
+  "\xe6\xac\xa0\0" /* offset 6203 */
+  "\xe6\xad\xa2\0" /* offset 6207 */
+  "\xe6\xad\xb9\0" /* offset 6211 */
+  "\xe6\xae\xb3\0" /* offset 6215 */
+  "\xe6\xaf\x8b\0" /* offset 6219 */
+  "\xe6\xaf\x94\0" /* offset 6223 */
+  "\xe6\xaf\x9b\0" /* offset 6227 */
+  "\xe6\xb0\x8f\0" /* offset 6231 */
+  "\xe6\xb0\x94\0" /* offset 6235 */
+  "\xe6\xb0\xb4\0" /* offset 6239 */
+  "\xe7\x81\xab\0" /* offset 6243 */
+  "\xe7\x88\xaa\0" /* offset 6247 */
+  "\xe7\x88\xb6\0" /* offset 6251 */
+  "\xe7\x88\xbb\0" /* offset 6255 */
+  "\xe7\x88\xbf\0" /* offset 6259 */
+  "\xe7\x89\x87\0" /* offset 6263 */
+  "\xe7\x89\x99\0" /* offset 6267 */
+  "\xe7\x89\x9b\0" /* offset 6271 */
+  "\xe7\x8a\xac\0" /* offset 6275 */
+  "\xe7\x8e\x84\0" /* offset 6279 */
+  "\xe7\x8e\x89\0" /* offset 6283 */
+  "\xe7\x93\x9c\0" /* offset 6287 */
+  "\xe7\x93\xa6\0" /* offset 6291 */
+  "\xe7\x94\x98\0" /* offset 6295 */
+  "\xe7\x94\x9f\0" /* offset 6299 */
+  "\xe7\x94\xa8\0" /* offset 6303 */
+  "\xe7\x94\xb0\0" /* offset 6307 */
+  "\xe7\x96\x8b\0" /* offset 6311 */
+  "\xe7\x96\x92\0" /* offset 6315 */
+  "\xe7\x99\xb6\0" /* offset 6319 */
+  "\xe7\x99\xbd\0" /* offset 6323 */
+  "\xe7\x9a\xae\0" /* offset 6327 */
+  "\xe7\x9a\xbf\0" /* offset 6331 */
+  "\xe7\x9b\xae\0" /* offset 6335 */
+  "\xe7\x9f\x9b\0" /* offset 6339 */
+  "\xe7\x9f\xa2\0" /* offset 6343 */
+  "\xe7\x9f\xb3\0" /* offset 6347 */
+  "\xe7\xa4\xba\0" /* offset 6351 */
+  "\xe7\xa6\xb8\0" /* offset 6355 */
+  "\xe7\xa6\xbe\0" /* offset 6359 */
+  "\xe7\xa9\xb4\0" /* offset 6363 */
+  "\xe7\xab\x8b\0" /* offset 6367 */
+  "\xe7\xab\xb9\0" /* offset 6371 */
+  "\xe7\xb1\xb3\0" /* offset 6375 */
+  "\xe7\xb3\xb8\0" /* offset 6379 */
+  "\xe7\xbc\xb6\0" /* offset 6383 */
+  "\xe7\xbd\x91\0" /* offset 6387 */
+  "\xe7\xbe\x8a\0" /* offset 6391 */
+  "\xe7\xbe\xbd\0" /* offset 6395 */
+  "\xe8\x80\x81\0" /* offset 6399 */
+  "\xe8\x80\x8c\0" /* offset 6403 */
+  "\xe8\x80\x92\0" /* offset 6407 */
+  "\xe8\x80\xb3\0" /* offset 6411 */
+  "\xe8\x81\xbf\0" /* offset 6415 */
+  "\xe8\x82\x89\0" /* offset 6419 */
+  "\xe8\x87\xa3\0" /* offset 6423 */
+  "\xe8\x87\xaa\0" /* offset 6427 */
+  "\xe8\x87\xb3\0" /* offset 6431 */
+  "\xe8\x87\xbc\0" /* offset 6435 */
+  "\xe8\x88\x8c\0" /* offset 6439 */
+  "\xe8\x88\x9b\0" /* offset 6443 */
+  "\xe8\x88\x9f\0" /* offset 6447 */
+  "\xe8\x89\xae\0" /* offset 6451 */
+  "\xe8\x89\xb2\0" /* offset 6455 */
+  "\xe8\x89\xb8\0" /* offset 6459 */
+  "\xe8\x99\x8d\0" /* offset 6463 */
+  "\xe8\x99\xab\0" /* offset 6467 */
+  "\xe8\xa1\x80\0" /* offset 6471 */
+  "\xe8\xa1\x8c\0" /* offset 6475 */
+  "\xe8\xa1\xa3\0" /* offset 6479 */
+  "\xe8\xa5\xbe\0" /* offset 6483 */
+  "\xe8\xa6\x8b\0" /* offset 6487 */
+  "\xe8\xa7\x92\0" /* offset 6491 */
+  "\xe8\xa8\x80\0" /* offset 6495 */
+  "\xe8\xb0\xb7\0" /* offset 6499 */
+  "\xe8\xb1\x86\0" /* offset 6503 */
+  "\xe8\xb1\x95\0" /* offset 6507 */
+  "\xe8\xb1\xb8\0" /* offset 6511 */
+  "\xe8\xb2\x9d\0" /* offset 6515 */
+  "\xe8\xb5\xa4\0" /* offset 6519 */
+  "\xe8\xb5\xb0\0" /* offset 6523 */
+  "\xe8\xb6\xb3\0" /* offset 6527 */
+  "\xe8\xba\xab\0" /* offset 6531 */
+  "\xe8\xbb\x8a\0" /* offset 6535 */
+  "\xe8\xbe\x9b\0" /* offset 6539 */
+  "\xe8\xbe\xb0\0" /* offset 6543 */
+  "\xe8\xbe\xb5\0" /* offset 6547 */
+  "\xe9\x82\x91\0" /* offset 6551 */
+  "\xe9\x85\x89\0" /* offset 6555 */
+  "\xe9\x87\x86\0" /* offset 6559 */
+  "\xe9\x87\x8c\0" /* offset 6563 */
+  "\xe9\x87\x91\0" /* offset 6567 */
+  "\xe9\x95\xb7\0" /* offset 6571 */
+  "\xe9\x96\x80\0" /* offset 6575 */
+  "\xe9\x98\x9c\0" /* offset 6579 */
+  "\xe9\x9a\xb6\0" /* offset 6583 */
+  "\xe9\x9a\xb9\0" /* offset 6587 */
+  "\xe9\x9b\xa8\0" /* offset 6591 */
+  "\xe9\x9d\x91\0" /* offset 6595 */
+  "\xe9\x9d\x9e\0" /* offset 6599 */
+  "\xe9\x9d\xa2\0" /* offset 6603 */
+  "\xe9\x9d\xa9\0" /* offset 6607 */
+  "\xe9\x9f\x8b\0" /* offset 6611 */
+  "\xe9\x9f\xad\0" /* offset 6615 */
+  "\xe9\x9f\xb3\0" /* offset 6619 */
+  "\xe9\xa0\x81\0" /* offset 6623 */
+  "\xe9\xa2\xa8\0" /* offset 6627 */
+  "\xe9\xa3\x9b\0" /* offset 6631 */
+  "\xe9\xa3\x9f\0" /* offset 6635 */
+  "\xe9\xa6\x96\0" /* offset 6639 */
+  "\xe9\xa6\x99\0" /* offset 6643 */
+  "\xe9\xa6\xac\0" /* offset 6647 */
+  "\xe9\xaa\xa8\0" /* offset 6651 */
+  "\xe9\xab\x98\0" /* offset 6655 */
+  "\xe9\xab\x9f\0" /* offset 6659 */
+  "\xe9\xac\xa5\0" /* offset 6663 */
+  "\xe9\xac\xaf\0" /* offset 6667 */
+  "\xe9\xac\xb2\0" /* offset 6671 */
+  "\xe9\xac\xbc\0" /* offset 6675 */
+  "\xe9\xad\x9a\0" /* offset 6679 */
+  "\xe9\xb3\xa5\0" /* offset 6683 */
+  "\xe9\xb9\xb5\0" /* offset 6687 */
+  "\xe9\xb9\xbf\0" /* offset 6691 */
+  "\xe9\xba\xa5\0" /* offset 6695 */
+  "\xe9\xba\xbb\0" /* offset 6699 */
+  "\xe9\xbb\x83\0" /* offset 6703 */
+  "\xe9\xbb\x8d\0" /* offset 6707 */
+  "\xe9\xbb\x91\0" /* offset 6711 */
+  "\xe9\xbb\xb9\0" /* offset 6715 */
+  "\xe9\xbb\xbd\0" /* offset 6719 */
+  "\xe9\xbc\x8e\0" /* offset 6723 */
+  "\xe9\xbc\x93\0" /* offset 6727 */
+  "\xe9\xbc\xa0\0" /* offset 6731 */
+  "\xe9\xbc\xbb\0" /* offset 6735 */
+  "\xe9\xbd\x8a\0" /* offset 6739 */
+  "\xe9\xbd\x92\0" /* offset 6743 */
+  "\xe9\xbe\x8d\0" /* offset 6747 */
+  "\xe9\xbe\x9c\0" /* offset 6751 */
+  "\xe9\xbe\xa0\0" /* offset 6755 */
+  "\xe3\x80\x92\0" /* offset 6759 */
+  "\xe5\x8d\x84\0" /* offset 6763 */
+  "\xe5\x8d\x85\0" /* offset 6767 */
+  "\xe3\x81\x8b\xe3\x82\x99\0" /* offset 6771 */
+  "\xe3\x81\x8d\xe3\x82\x99\0" /* offset 6778 */
+  "\xe3\x81\x8f\xe3\x82\x99\0" /* offset 6785 */
+  "\xe3\x81\x91\xe3\x82\x99\0" /* offset 6792 */
+  "\xe3\x81\x93\xe3\x82\x99\0" /* offset 6799 */
+  "\xe3\x81\x95\xe3\x82\x99\0" /* offset 6806 */
+  "\xe3\x81\x97\xe3\x82\x99\0" /* offset 6813 */
+  "\xe3\x81\x99\xe3\x82\x99\0" /* offset 6820 */
+  "\xe3\x81\x9b\xe3\x82\x99\0" /* offset 6827 */
+  "\xe3\x81\x9d\xe3\x82\x99\0" /* offset 6834 */
+  "\xe3\x81\x9f\xe3\x82\x99\0" /* offset 6841 */
+  "\xe3\x81\xa1\xe3\x82\x99\0" /* offset 6848 */
+  "\xe3\x81\xa4\xe3\x82\x99\0" /* offset 6855 */
+  "\xe3\x81\xa6\xe3\x82\x99\0" /* offset 6862 */
+  "\xe3\x81\xa8\xe3\x82\x99\0" /* offset 6869 */
+  "\xe3\x81\xaf\xe3\x82\x99\0" /* offset 6876 */
+  "\xe3\x81\xaf\xe3\x82\x9a\0" /* offset 6883 */
+  "\xe3\x81\xb2\xe3\x82\x99\0" /* offset 6890 */
+  "\xe3\x81\xb2\xe3\x82\x9a\0" /* offset 6897 */
+  "\xe3\x81\xb5\xe3\x82\x99\0" /* offset 6904 */
+  "\xe3\x81\xb5\xe3\x82\x9a\0" /* offset 6911 */
+  "\xe3\x81\xb8\xe3\x82\x99\0" /* offset 6918 */
+  "\xe3\x81\xb8\xe3\x82\x9a\0" /* offset 6925 */
+  "\xe3\x81\xbb\xe3\x82\x99\0" /* offset 6932 */
+  "\xe3\x81\xbb\xe3\x82\x9a\0" /* offset 6939 */
+  "\xe3\x81\x86\xe3\x82\x99\0" /* offset 6946 */
+  "\x20\xe3\x82\x99\0" /* offset 6953 */
+  "\x20\xe3\x82\x9a\0" /* offset 6958 */
+  "\xe3\x82\x9d\xe3\x82\x99\0" /* offset 6963 */
+  "\xe3\x82\x88\xe3\x82\x8a\0" /* offset 6970 */
+  "\xe3\x82\xab\xe3\x82\x99\0" /* offset 6977 */
+  "\xe3\x82\xad\xe3\x82\x99\0" /* offset 6984 */
+  "\xe3\x82\xaf\xe3\x82\x99\0" /* offset 6991 */
+  "\xe3\x82\xb1\xe3\x82\x99\0" /* offset 6998 */
+  "\xe3\x82\xb3\xe3\x82\x99\0" /* offset 7005 */
+  "\xe3\x82\xb5\xe3\x82\x99\0" /* offset 7012 */
+  "\xe3\x82\xb7\xe3\x82\x99\0" /* offset 7019 */
+  "\xe3\x82\xb9\xe3\x82\x99\0" /* offset 7026 */
+  "\xe3\x82\xbb\xe3\x82\x99\0" /* offset 7033 */
+  "\xe3\x82\xbd\xe3\x82\x99\0" /* offset 7040 */
+  "\xe3\x82\xbf\xe3\x82\x99\0" /* offset 7047 */
+  "\xe3\x83\x81\xe3\x82\x99\0" /* offset 7054 */
+  "\xe3\x83\x84\xe3\x82\x99\0" /* offset 7061 */
+  "\xe3\x83\x86\xe3\x82\x99\0" /* offset 7068 */
+  "\xe3\x83\x88\xe3\x82\x99\0" /* offset 7075 */
+  "\xe3\x83\x8f\xe3\x82\x99\0" /* offset 7082 */
+  "\xe3\x83\x8f\xe3\x82\x9a\0" /* offset 7089 */
+  "\xe3\x83\x92\xe3\x82\x99\0" /* offset 7096 */
+  "\xe3\x83\x92\xe3\x82\x9a\0" /* offset 7103 */
+  "\xe3\x83\x95\xe3\x82\x99\0" /* offset 7110 */
+  "\xe3\x83\x95\xe3\x82\x9a\0" /* offset 7117 */
+  "\xe3\x83\x98\xe3\x82\x99\0" /* offset 7124 */
+  "\xe3\x83\x98\xe3\x82\x9a\0" /* offset 7131 */
+  "\xe3\x83\x9b\xe3\x82\x99\0" /* offset 7138 */
+  "\xe3\x83\x9b\xe3\x82\x9a\0" /* offset 7145 */
+  "\xe3\x82\xa6\xe3\x82\x99\0" /* offset 7152 */
+  "\xe3\x83\xaf\xe3\x82\x99\0" /* offset 7159 */
+  "\xe3\x83\xb0\xe3\x82\x99\0" /* offset 7166 */
+  "\xe3\x83\xb1\xe3\x82\x99\0" /* offset 7173 */
+  "\xe3\x83\xb2\xe3\x82\x99\0" /* offset 7180 */
+  "\xe3\x83\xbd\xe3\x82\x99\0" /* offset 7187 */
+  "\xe3\x82\xb3\xe3\x83\x88\0" /* offset 7194 */
+  "\xe1\x84\x80\0" /* offset 7201 */
+  "\xe1\x84\x81\0" /* offset 7205 */
+  "\xe1\x86\xaa\0" /* offset 7209 */
+  "\xe1\x84\x82\0" /* offset 7213 */
+  "\xe1\x86\xac\0" /* offset 7217 */
+  "\xe1\x86\xad\0" /* offset 7221 */
+  "\xe1\x84\x83\0" /* offset 7225 */
+  "\xe1\x84\x84\0" /* offset 7229 */
+  "\xe1\x84\x85\0" /* offset 7233 */
+  "\xe1\x86\xb0\0" /* offset 7237 */
+  "\xe1\x86\xb1\0" /* offset 7241 */
+  "\xe1\x86\xb2\0" /* offset 7245 */
+  "\xe1\x86\xb3\0" /* offset 7249 */
+  "\xe1\x86\xb4\0" /* offset 7253 */
+  "\xe1\x86\xb5\0" /* offset 7257 */
+  "\xe1\x84\x9a\0" /* offset 7261 */
+  "\xe1\x84\x86\0" /* offset 7265 */
+  "\xe1\x84\x87\0" /* offset 7269 */
+  "\xe1\x84\x88\0" /* offset 7273 */
+  "\xe1\x84\xa1\0" /* offset 7277 */
+  "\xe1\x84\x89\0" /* offset 7281 */
+  "\xe1\x84\x8a\0" /* offset 7285 */
+  "\xe1\x84\x8b\0" /* offset 7289 */
+  "\xe1\x84\x8c\0" /* offset 7293 */
+  "\xe1\x84\x8d\0" /* offset 7297 */
+  "\xe1\x84\x8e\0" /* offset 7301 */
+  "\xe1\x84\x8f\0" /* offset 7305 */
+  "\xe1\x84\x90\0" /* offset 7309 */
+  "\xe1\x84\x91\0" /* offset 7313 */
+  "\xe1\x84\x92\0" /* offset 7317 */
+  "\xe1\x85\xa1\0" /* offset 7321 */
+  "\xe1\x85\xa2\0" /* offset 7325 */
+  "\xe1\x85\xa3\0" /* offset 7329 */
+  "\xe1\x85\xa4\0" /* offset 7333 */
+  "\xe1\x85\xa5\0" /* offset 7337 */
+  "\xe1\x85\xa6\0" /* offset 7341 */
+  "\xe1\x85\xa7\0" /* offset 7345 */
+  "\xe1\x85\xa8\0" /* offset 7349 */
+  "\xe1\x85\xa9\0" /* offset 7353 */
+  "\xe1\x85\xaa\0" /* offset 7357 */
+  "\xe1\x85\xab\0" /* offset 7361 */
+  "\xe1\x85\xac\0" /* offset 7365 */
+  "\xe1\x85\xad\0" /* offset 7369 */
+  "\xe1\x85\xae\0" /* offset 7373 */
+  "\xe1\x85\xaf\0" /* offset 7377 */
+  "\xe1\x85\xb0\0" /* offset 7381 */
+  "\xe1\x85\xb1\0" /* offset 7385 */
+  "\xe1\x85\xb2\0" /* offset 7389 */
+  "\xe1\x85\xb3\0" /* offset 7393 */
+  "\xe1\x85\xb4\0" /* offset 7397 */
+  "\xe1\x85\xb5\0" /* offset 7401 */
+  "\xe1\x85\xa0\0" /* offset 7405 */
+  "\xe1\x84\x94\0" /* offset 7409 */
+  "\xe1\x84\x95\0" /* offset 7413 */
+  "\xe1\x87\x87\0" /* offset 7417 */
+  "\xe1\x87\x88\0" /* offset 7421 */
+  "\xe1\x87\x8c\0" /* offset 7425 */
+  "\xe1\x87\x8e\0" /* offset 7429 */
+  "\xe1\x87\x93\0" /* offset 7433 */
+  "\xe1\x87\x97\0" /* offset 7437 */
+  "\xe1\x87\x99\0" /* offset 7441 */
+  "\xe1\x84\x9c\0" /* offset 7445 */
+  "\xe1\x87\x9d\0" /* offset 7449 */
+  "\xe1\x87\x9f\0" /* offset 7453 */
+  "\xe1\x84\x9d\0" /* offset 7457 */
+  "\xe1\x84\x9e\0" /* offset 7461 */
+  "\xe1\x84\xa0\0" /* offset 7465 */
+  "\xe1\x84\xa2\0" /* offset 7469 */
+  "\xe1\x84\xa3\0" /* offset 7473 */
+  "\xe1\x84\xa7\0" /* offset 7477 */
+  "\xe1\x84\xa9\0" /* offset 7481 */
+  "\xe1\x84\xab\0" /* offset 7485 */
+  "\xe1\x84\xac\0" /* offset 7489 */
+  "\xe1\x84\xad\0" /* offset 7493 */
+  "\xe1\x84\xae\0" /* offset 7497 */
+  "\xe1\x84\xaf\0" /* offset 7501 */
+  "\xe1\x84\xb2\0" /* offset 7505 */
+  "\xe1\x84\xb6\0" /* offset 7509 */
+  "\xe1\x85\x80\0" /* offset 7513 */
+  "\xe1\x85\x87\0" /* offset 7517 */
+  "\xe1\x85\x8c\0" /* offset 7521 */
+  "\xe1\x87\xb1\0" /* offset 7525 */
+  "\xe1\x87\xb2\0" /* offset 7529 */
+  "\xe1\x85\x97\0" /* offset 7533 */
+  "\xe1\x85\x98\0" /* offset 7537 */
+  "\xe1\x85\x99\0" /* offset 7541 */
+  "\xe1\x86\x84\0" /* offset 7545 */
+  "\xe1\x86\x85\0" /* offset 7549 */
+  "\xe1\x86\x88\0" /* offset 7553 */
+  "\xe1\x86\x91\0" /* offset 7557 */
+  "\xe1\x86\x92\0" /* offset 7561 */
+  "\xe1\x86\x94\0" /* offset 7565 */
+  "\xe1\x86\x9e\0" /* offset 7569 */
+  "\xe1\x86\xa1\0" /* offset 7573 */
+  "\xe4\xb8\x89\0" /* offset 7577 */
+  "\xe5\x9b\x9b\0" /* offset 7581 */
+  "\xe4\xb8\x8a\0" /* offset 7585 */
+  "\xe4\xb8\xad\0" /* offset 7589 */
+  "\xe4\xb8\x8b\0" /* offset 7593 */
+  "\xe7\x94\xb2\0" /* offset 7597 */
+  "\xe4\xb8\x99\0" /* offset 7601 */
+  "\xe4\xb8\x81\0" /* offset 7605 */
+  "\xe5\xa4\xa9\0" /* offset 7609 */
+  "\xe5\x9c\xb0\0" /* offset 7613 */
+  "\x28\xe1\x84\x80\x29\0" /* offset 7617 */
+  "\x28\xe1\x84\x82\x29\0" /* offset 7623 */
+  "\x28\xe1\x84\x83\x29\0" /* offset 7629 */
+  "\x28\xe1\x84\x85\x29\0" /* offset 7635 */
+  "\x28\xe1\x84\x86\x29\0" /* offset 7641 */
+  "\x28\xe1\x84\x87\x29\0" /* offset 7647 */
+  "\x28\xe1\x84\x89\x29\0" /* offset 7653 */
+  "\x28\xe1\x84\x8b\x29\0" /* offset 7659 */
+  "\x28\xe1\x84\x8c\x29\0" /* offset 7665 */
+  "\x28\xe1\x84\x8e\x29\0" /* offset 7671 */
+  "\x28\xe1\x84\x8f\x29\0" /* offset 7677 */
+  "\x28\xe1\x84\x90\x29\0" /* offset 7683 */
+  "\x28\xe1\x84\x91\x29\0" /* offset 7689 */
+  "\x28\xe1\x84\x92\x29\0" /* offset 7695 */
+  "\x28\xe1\x84\x80\xe1\x85\xa1\x29\0" /* offset 7701 */
+  "\x28\xe1\x84\x82\xe1\x85\xa1\x29\0" /* offset 7710 */
+  "\x28\xe1\x84\x83\xe1\x85\xa1\x29\0" /* offset 7719 */
+  "\x28\xe1\x84\x85\xe1\x85\xa1\x29\0" /* offset 7728 */
+  "\x28\xe1\x84\x86\xe1\x85\xa1\x29\0" /* offset 7737 */
+  "\x28\xe1\x84\x87\xe1\x85\xa1\x29\0" /* offset 7746 */
+  "\x28\xe1\x84\x89\xe1\x85\xa1\x29\0" /* offset 7755 */
+  "\x28\xe1\x84\x8b\xe1\x85\xa1\x29\0" /* offset 7764 */
+  "\x28\xe1\x84\x8c\xe1\x85\xa1\x29\0" /* offset 7773 */
+  "\x28\xe1\x84\x8e\xe1\x85\xa1\x29\0" /* offset 7782 */
+  "\x28\xe1\x84\x8f\xe1\x85\xa1\x29\0" /* offset 7791 */
+  "\x28\xe1\x84\x90\xe1\x85\xa1\x29\0" /* offset 7800 */
+  "\x28\xe1\x84\x91\xe1\x85\xa1\x29\0" /* offset 7809 */
+  "\x28\xe1\x84\x92\xe1\x85\xa1\x29\0" /* offset 7818 */
+  "\x28\xe1\x84\x8c\xe1\x85\xae\x29\0" /* offset 7827 */
+  "\x28\xe4\xb8\x80\x29\0" /* offset 7836 */
+  "\x28\xe4\xba\x8c\x29\0" /* offset 7842 */
+  "\x28\xe4\xb8\x89\x29\0" /* offset 7848 */
+  "\x28\xe5\x9b\x9b\x29\0" /* offset 7854 */
+  "\x28\xe4\xba\x94\x29\0" /* offset 7860 */
+  "\x28\xe5\x85\xad\x29\0" /* offset 7866 */
+  "\x28\xe4\xb8\x83\x29\0" /* offset 7872 */
+  "\x28\xe5\x85\xab\x29\0" /* offset 7878 */
+  "\x28\xe4\xb9\x9d\x29\0" /* offset 7884 */
+  "\x28\xe5\x8d\x81\x29\0" /* offset 7890 */
+  "\x28\xe6\x9c\x88\x29\0" /* offset 7896 */
+  "\x28\xe7\x81\xab\x29\0" /* offset 7902 */
+  "\x28\xe6\xb0\xb4\x29\0" /* offset 7908 */
+  "\x28\xe6\x9c\xa8\x29\0" /* offset 7914 */
+  "\x28\xe9\x87\x91\x29\0" /* offset 7920 */
+  "\x28\xe5\x9c\x9f\x29\0" /* offset 7926 */
+  "\x28\xe6\x97\xa5\x29\0" /* offset 7932 */
+  "\x28\xe6\xa0\xaa\x29\0" /* offset 7938 */
+  "\x28\xe6\x9c\x89\x29\0" /* offset 7944 */
+  "\x28\xe7\xa4\xbe\x29\0" /* offset 7950 */
+  "\x28\xe5\x90\x8d\x29\0" /* offset 7956 */
+  "\x28\xe7\x89\xb9\x29\0" /* offset 7962 */
+  "\x28\xe8\xb2\xa1\x29\0" /* offset 7968 */
+  "\x28\xe7\xa5\x9d\x29\0" /* offset 7974 */
+  "\x28\xe5\x8a\xb4\x29\0" /* offset 7980 */
+  "\x28\xe4\xbb\xa3\x29\0" /* offset 7986 */
+  "\x28\xe5\x91\xbc\x29\0" /* offset 7992 */
+  "\x28\xe5\xad\xa6\x29\0" /* offset 7998 */
+  "\x28\xe7\x9b\xa3\x29\0" /* offset 8004 */
+  "\x28\xe4\xbc\x81\x29\0" /* offset 8010 */
+  "\x28\xe8\xb3\x87\x29\0" /* offset 8016 */
+  "\x28\xe5\x8d\x94\x29\0" /* offset 8022 */
+  "\x28\xe7\xa5\xad\x29\0" /* offset 8028 */
+  "\x28\xe4\xbc\x91\x29\0" /* offset 8034 */
+  "\x28\xe8\x87\xaa\x29\0" /* offset 8040 */
+  "\x28\xe8\x87\xb3\x29\0" /* offset 8046 */
+  "\x32\x31\0" /* offset 8052 */
+  "\x32\x32\0" /* offset 8055 */
+  "\x32\x33\0" /* offset 8058 */
+  "\x32\x34\0" /* offset 8061 */
+  "\x32\x35\0" /* offset 8064 */
+  "\x32\x36\0" /* offset 8067 */
+  "\x32\x37\0" /* offset 8070 */
+  "\x32\x38\0" /* offset 8073 */
+  "\x32\x39\0" /* offset 8076 */
+  "\x33\x30\0" /* offset 8079 */
+  "\x33\x31\0" /* offset 8082 */
+  "\x33\x32\0" /* offset 8085 */
+  "\x33\x33\0" /* offset 8088 */
+  "\x33\x34\0" /* offset 8091 */
+  "\x33\x35\0" /* offset 8094 */
+  "\xe1\x84\x80\xe1\x85\xa1\0" /* offset 8097 */
+  "\xe1\x84\x82\xe1\x85\xa1\0" /* offset 8104 */
+  "\xe1\x84\x83\xe1\x85\xa1\0" /* offset 8111 */
+  "\xe1\x84\x85\xe1\x85\xa1\0" /* offset 8118 */
+  "\xe1\x84\x86\xe1\x85\xa1\0" /* offset 8125 */
+  "\xe1\x84\x87\xe1\x85\xa1\0" /* offset 8132 */
+  "\xe1\x84\x89\xe1\x85\xa1\0" /* offset 8139 */
+  "\xe1\x84\x8b\xe1\x85\xa1\0" /* offset 8146 */
+  "\xe1\x84\x8c\xe1\x85\xa1\0" /* offset 8153 */
+  "\xe1\x84\x8e\xe1\x85\xa1\0" /* offset 8160 */
+  "\xe1\x84\x8f\xe1\x85\xa1\0" /* offset 8167 */
+  "\xe1\x84\x90\xe1\x85\xa1\0" /* offset 8174 */
+  "\xe1\x84\x91\xe1\x85\xa1\0" /* offset 8181 */
+  "\xe1\x84\x92\xe1\x85\xa1\0" /* offset 8188 */
+  "\xe4\xba\x94\0" /* offset 8195 */
+  "\xe5\x85\xad\0" /* offset 8199 */
+  "\xe4\xb8\x83\0" /* offset 8203 */
+  "\xe4\xb9\x9d\0" /* offset 8207 */
+  "\xe6\xa0\xaa\0" /* offset 8211 */
+  "\xe6\x9c\x89\0" /* offset 8215 */
+  "\xe7\xa4\xbe\0" /* offset 8219 */
+  "\xe5\x90\x8d\0" /* offset 8223 */
+  "\xe7\x89\xb9\0" /* offset 8227 */
+  "\xe8\xb2\xa1\0" /* offset 8231 */
+  "\xe7\xa5\x9d\0" /* offset 8235 */
+  "\xe5\x8a\xb4\0" /* offset 8239 */
+  "\xe7\xa7\x98\0" /* offset 8243 */
+  "\xe7\x94\xb7\0" /* offset 8247 */
+  "\xe9\x81\xa9\0" /* offset 8251 */
+  "\xe5\x84\xaa\0" /* offset 8255 */
+  "\xe5\x8d\xb0\0" /* offset 8259 */
+  "\xe6\xb3\xa8\0" /* offset 8263 */
+  "\xe9\xa0\x85\0" /* offset 8267 */
+  "\xe4\xbc\x91\0" /* offset 8271 */
+  "\xe5\x86\x99\0" /* offset 8275 */
+  "\xe6\xad\xa3\0" /* offset 8279 */
+  "\xe5\xb7\xa6\0" /* offset 8283 */
+  "\xe5\x8f\xb3\0" /* offset 8287 */
+  "\xe5\x8c\xbb\0" /* offset 8291 */
+  "\xe5\xae\x97\0" /* offset 8295 */
+  "\xe5\xad\xa6\0" /* offset 8299 */
+  "\xe7\x9b\xa3\0" /* offset 8303 */
+  "\xe4\xbc\x81\0" /* offset 8307 */
+  "\xe8\xb3\x87\0" /* offset 8311 */
+  "\xe5\x8d\x94\0" /* offset 8315 */
+  "\xe5\xa4\x9c\0" /* offset 8319 */
+  "\x33\x36\0" /* offset 8323 */
+  "\x33\x37\0" /* offset 8326 */
+  "\x33\x38\0" /* offset 8329 */
+  "\x33\x39\0" /* offset 8332 */
+  "\x34\x30\0" /* offset 8335 */
+  "\x34\x31\0" /* offset 8338 */
+  "\x34\x32\0" /* offset 8341 */
+  "\x34\x33\0" /* offset 8344 */
+  "\x34\x34\0" /* offset 8347 */
+  "\x34\x35\0" /* offset 8350 */
+  "\x34\x36\0" /* offset 8353 */
+  "\x34\x37\0" /* offset 8356 */
+  "\x34\x38\0" /* offset 8359 */
+  "\x34\x39\0" /* offset 8362 */
+  "\x35\x30\0" /* offset 8365 */
+  "\x31\xe6\x9c\x88\0" /* offset 8368 */
+  "\x32\xe6\x9c\x88\0" /* offset 8373 */
+  "\x33\xe6\x9c\x88\0" /* offset 8378 */
+  "\x34\xe6\x9c\x88\0" /* offset 8383 */
+  "\x35\xe6\x9c\x88\0" /* offset 8388 */
+  "\x36\xe6\x9c\x88\0" /* offset 8393 */
+  "\x37\xe6\x9c\x88\0" /* offset 8398 */
+  "\x38\xe6\x9c\x88\0" /* offset 8403 */
+  "\x39\xe6\x9c\x88\0" /* offset 8408 */
+  "\x31\x30\xe6\x9c\x88\0" /* offset 8413 */
+  "\x31\x31\xe6\x9c\x88\0" /* offset 8419 */
+  "\x31\x32\xe6\x9c\x88\0" /* offset 8425 */
+  "\xe3\x82\xa2\0" /* offset 8431 */
+  "\xe3\x82\xa4\0" /* offset 8435 */
+  "\xe3\x82\xa6\0" /* offset 8439 */
+  "\xe3\x82\xa8\0" /* offset 8443 */
+  "\xe3\x82\xaa\0" /* offset 8447 */
+  "\xe3\x82\xab\0" /* offset 8451 */
+  "\xe3\x82\xad\0" /* offset 8455 */
+  "\xe3\x82\xaf\0" /* offset 8459 */
+  "\xe3\x82\xb1\0" /* offset 8463 */
+  "\xe3\x82\xb3\0" /* offset 8467 */
+  "\xe3\x82\xb5\0" /* offset 8471 */
+  "\xe3\x82\xb7\0" /* offset 8475 */
+  "\xe3\x82\xb9\0" /* offset 8479 */
+  "\xe3\x82\xbb\0" /* offset 8483 */
+  "\xe3\x82\xbd\0" /* offset 8487 */
+  "\xe3\x82\xbf\0" /* offset 8491 */
+  "\xe3\x83\x81\0" /* offset 8495 */
+  "\xe3\x83\x84\0" /* offset 8499 */
+  "\xe3\x83\x86\0" /* offset 8503 */
+  "\xe3\x83\x88\0" /* offset 8507 */
+  "\xe3\x83\x8a\0" /* offset 8511 */
+  "\xe3\x83\x8b\0" /* offset 8515 */
+  "\xe3\x83\x8c\0" /* offset 8519 */
+  "\xe3\x83\x8d\0" /* offset 8523 */
+  "\xe3\x83\x8e\0" /* offset 8527 */
+  "\xe3\x83\x8f\0" /* offset 8531 */
+  "\xe3\x83\x92\0" /* offset 8535 */
+  "\xe3\x83\x95\0" /* offset 8539 */
+  "\xe3\x83\x98\0" /* offset 8543 */
+  "\xe3\x83\x9b\0" /* offset 8547 */
+  "\xe3\x83\x9e\0" /* offset 8551 */
+  "\xe3\x83\x9f\0" /* offset 8555 */
+  "\xe3\x83\xa0\0" /* offset 8559 */
+  "\xe3\x83\xa1\0" /* offset 8563 */
+  "\xe3\x83\xa2\0" /* offset 8567 */
+  "\xe3\x83\xa4\0" /* offset 8571 */
+  "\xe3\x83\xa6\0" /* offset 8575 */
+  "\xe3\x83\xa8\0" /* offset 8579 */
+  "\xe3\x83\xa9\0" /* offset 8583 */
+  "\xe3\x83\xaa\0" /* offset 8587 */
+  "\xe3\x83\xab\0" /* offset 8591 */
+  "\xe3\x83\xac\0" /* offset 8595 */
+  "\xe3\x83\xad\0" /* offset 8599 */
+  "\xe3\x83\xaf\0" /* offset 8603 */
+  "\xe3\x83\xb0\0" /* offset 8607 */
+  "\xe3\x83\xb1\0" /* offset 8611 */
+  "\xe3\x83\xb2\0" /* offset 8615 */
+  "\xe3\x82\xa2\xe3\x83\x8f\xe3\x82\x9a\xe3\x83\xbc\xe3\x83\x88\0" /* offset 8619 */
+  "\xe3\x82\xa2\xe3\x83\xab\xe3\x83\x95\xe3\x82\xa1\0" /* offset 8635 */
+  "\xe3\x82\xa2\xe3\x83\xb3\xe3\x83\x98\xe3\x82\x9a\xe3\x82\xa2\0" /* offset 8648 */
+  "\xe3\x82\xa2\xe3\x83\xbc\xe3\x83\xab\0" /* offset 8664 */
+  "\xe3\x82\xa4\xe3\x83\x8b\xe3\x83\xb3\xe3\x82\xaf\xe3\x82\x99\0" /* offset 8674 */
+  "\xe3\x82\xa4\xe3\x83\xb3\xe3\x83\x81\0" /* offset 8690 */
+  "\xe3\x82\xa6\xe3\x82\xa9\xe3\x83\xb3\0" /* offset 8700 */
+  "\xe3\x82\xa8\xe3\x82\xb9\xe3\x82\xaf\xe3\x83\xbc\xe3\x83\x88\xe3\x82\x99\0" /* offset 8710 */
+  "\xe3\x82\xa8\xe3\x83\xbc\xe3\x82\xab\xe3\x83\xbc\0" /* offset 8729 */
+  "\xe3\x82\xaa\xe3\x83\xb3\xe3\x82\xb9\0" /* offset 8742 */
+  "\xe3\x82\xaa\xe3\x83\xbc\xe3\x83\xa0\0" /* offset 8752 */
+  "\xe3\x82\xab\xe3\x82\xa4\xe3\x83\xaa\0" /* offset 8762 */
+  "\xe3\x82\xab\xe3\x83\xa9\xe3\x83\x83\xe3\x83\x88\0" /* offset 8772 */
+  "\xe3\x82\xab\xe3\x83\xad\xe3\x83\xaa\xe3\x83\xbc\0" /* offset 8785 */
+  "\xe3\x82\xab\xe3\x82\x99\xe3\x83\xad\xe3\x83\xb3\0" /* offset 8798 */
+  "\xe3\x82\xab\xe3\x82\x99\xe3\x83\xb3\xe3\x83\x9e\0" /* offset 8811 */
+  "\xe3\x82\xad\xe3\x82\x99\xe3\x82\xab\xe3\x82\x99\0" /* offset 8824 */
+  "\xe3\x82\xad\xe3\x82\x99\xe3\x83\x8b\xe3\x83\xbc\0" /* offset 8837 */
+  "\xe3\x82\xad\xe3\x83\xa5\xe3\x83\xaa\xe3\x83\xbc\0" /* offset 8850 */
+  "\xe3\x82\xad\xe3\x82\x99\xe3\x83\xab\xe3\x82\xbf\xe3\x82\x99\xe3\x83\xbc\0" /* offset 8863 */
+  "\xe3\x82\xad\xe3\x83\xad\0" /* offset 8882 */
+  "\xe3\x82\xad\xe3\x83\xad\xe3\x82\xaf\xe3\x82\x99\xe3\x83\xa9\xe3\x83\xa0\0" /* offset 8889 */
+  "\xe3\x82\xad\xe3\x83\xad\xe3\x83\xa1\xe3\x83\xbc\xe3\x83\x88\xe3\x83\xab\0" /* offset 8908 */
+  "\xe3\x82\xad\xe3\x83\xad\xe3\x83\xaf\xe3\x83\x83\xe3\x83\x88\0" /* offset 8927 */
+  "\xe3\x82\xaf\xe3\x82\x99\xe3\x83\xa9\xe3\x83\xa0\0" /* offset 8943 */
+  "\xe3\x82\xaf\xe3\x82\x99\xe3\x83\xa9\xe3\x83\xa0\xe3\x83\x88\xe3\x83\xb3\0" /* offset 8956 */
+  "\xe3\x82\xaf\xe3\x83\xab\xe3\x82\xbb\xe3\x82\x99\xe3\x82\xa4\xe3\x83\xad\0" /* offset 8975 */
+  "\xe3\x82\xaf\xe3\x83\xad\xe3\x83\xbc\xe3\x83\x8d\0" /* offset 8994 */
+  "\xe3\x82\xb1\xe3\x83\xbc\xe3\x82\xb9\0" /* offset 9007 */
+  "\xe3\x82\xb3\xe3\x83\xab\xe3\x83\x8a\0" /* offset 9017 */
+  "\xe3\x82\xb3\xe3\x83\xbc\xe3\x83\x9b\xe3\x82\x9a\0" /* offset 9027 */
+  "\xe3\x82\xb5\xe3\x82\xa4\xe3\x82\xaf\xe3\x83\xab\0" /* offset 9040 */
+  "\xe3\x82\xb5\xe3\x83\xb3\xe3\x83\x81\xe3\x83\xbc\xe3\x83\xa0\0" /* offset 9053 */
+  "\xe3\x82\xb7\xe3\x83\xaa\xe3\x83\xb3\xe3\x82\xaf\xe3\x82\x99\0" /* offset 9069 */
+  "\xe3\x82\xbb\xe3\x83\xb3\xe3\x83\x81\0" /* offset 9085 */
+  "\xe3\x82\xbb\xe3\x83\xb3\xe3\x83\x88\0" /* offset 9095 */
+  "\xe3\x82\xbf\xe3\x82\x99\xe3\x83\xbc\xe3\x82\xb9\0" /* offset 9105 */
+  "\xe3\x83\x86\xe3\x82\x99\xe3\x82\xb7\0" /* offset 9118 */
+  "\xe3\x83\x88\xe3\x82\x99\xe3\x83\xab\0" /* offset 9128 */
+  "\xe3\x83\x88\xe3\x83\xb3\0" /* offset 9138 */
+  "\xe3\x83\x8a\xe3\x83\x8e\0" /* offset 9145 */
+  "\xe3\x83\x8e\xe3\x83\x83\xe3\x83\x88\0" /* offset 9152 */
+  "\xe3\x83\x8f\xe3\x82\xa4\xe3\x83\x84\0" /* offset 9162 */
+  "\xe3\x83\x8f\xe3\x82\x9a\xe3\x83\xbc\xe3\x82\xbb\xe3\x83\xb3\xe3\x83\x88\0" /* offset 9172 */
+  "\xe3\x83\x8f\xe3\x82\x9a\xe3\x83\xbc\xe3\x83\x84\0" /* offset 9191 */
+  "\xe3\x83\x8f\xe3\x82\x99\xe3\x83\xbc\xe3\x83\xac\xe3\x83\xab\0" /* offset 9204 */
+  "\xe3\x83\x92\xe3\x82\x9a\xe3\x82\xa2\xe3\x82\xb9\xe3\x83\x88\xe3\x83\xab\0" /* offset 9220 */
+  "\xe3\x83\x92\xe3\x82\x9a\xe3\x82\xaf\xe3\x83\xab\0" /* offset 9239 */
+  "\xe3\x83\x92\xe3\x82\x9a\xe3\x82\xb3\0" /* offset 9252 */
+  "\xe3\x83\x92\xe3\x82\x99\xe3\x83\xab\0" /* offset 9262 */
+  "\xe3\x83\x95\xe3\x82\xa1\xe3\x83\xa9\xe3\x83\x83\xe3\x83\x88\xe3\x82\x99\0" /* offset 9272 */
+  "\xe3\x83\x95\xe3\x82\xa3\xe3\x83\xbc\xe3\x83\x88\0" /* offset 9291 */
+  "\xe3\x83\x95\xe3\x82\x99\xe3\x83\x83\xe3\x82\xb7\xe3\x82\xa7\xe3\x83\xab\0" /* offset 9304 */
+  "\xe3\x83\x95\xe3\x83\xa9\xe3\x83\xb3\0" /* offset 9323 */
+  "\xe3\x83\x98\xe3\x82\xaf\xe3\x82\xbf\xe3\x83\xbc\xe3\x83\xab\0" /* offset 9333 */
+  "\xe3\x83\x98\xe3\x82\x9a\xe3\x82\xbd\0" /* offset 9349 */
+  "\xe3\x83\x98\xe3\x82\x9a\xe3\x83\x8b\xe3\x83\x92\0" /* offset 9359 */
+  "\xe3\x83\x98\xe3\x83\xab\xe3\x83\x84\0" /* offset 9372 */
+  "\xe3\x83\x98\xe3\x82\x9a\xe3\x83\xb3\xe3\x82\xb9\0" /* offset 9382 */
+  "\xe3\x83\x98\xe3\x82\x9a\xe3\x83\xbc\xe3\x82\xb7\xe3\x82\x99\0" /* offset 9395 */
+  "\xe3\x83\x98\xe3\x82\x99\xe3\x83\xbc\xe3\x82\xbf\0" /* offset 9411 */
+  "\xe3\x83\x9b\xe3\x82\x9a\xe3\x82\xa4\xe3\x83\xb3\xe3\x83\x88\0" /* offset 9424 */
+  "\xe3\x83\x9b\xe3\x82\x99\xe3\x83\xab\xe3\x83\x88\0" /* offset 9440 */
+  "\xe3\x83\x9b\xe3\x83\xb3\0" /* offset 9453 */
+  "\xe3\x83\x9b\xe3\x82\x9a\xe3\x83\xb3\xe3\x83\x88\xe3\x82\x99\0" /* offset 9460 */
+  "\xe3\x83\x9b\xe3\x83\xbc\xe3\x83\xab\0" /* offset 9476 */
+  "\xe3\x83\x9b\xe3\x83\xbc\xe3\x83\xb3\0" /* offset 9486 */
+  "\xe3\x83\x9e\xe3\x82\xa4\xe3\x82\xaf\xe3\x83\xad\0" /* offset 9496 */
+  "\xe3\x83\x9e\xe3\x82\xa4\xe3\x83\xab\0" /* offset 9509 */
+  "\xe3\x83\x9e\xe3\x83\x83\xe3\x83\x8f\0" /* offset 9519 */
+  "\xe3\x83\x9e\xe3\x83\xab\xe3\x82\xaf\0" /* offset 9529 */
+  "\xe3\x83\x9e\xe3\x83\xb3\xe3\x82\xb7\xe3\x83\xa7\xe3\x83\xb3\0" /* offset 9539 */
+  "\xe3\x83\x9f\xe3\x82\xaf\xe3\x83\xad\xe3\x83\xb3\0" /* offset 9555 */
+  "\xe3\x83\x9f\xe3\x83\xaa\0" /* offset 9568 */
+  "\xe3\x83\x9f\xe3\x83\xaa\xe3\x83\x8f\xe3\x82\x99\xe3\x83\xbc\xe3\x83\xab\0" /* offset 9575 */
+  "\xe3\x83\xa1\xe3\x82\xab\xe3\x82\x99\0" /* offset 9594 */
+  "\xe3\x83\xa1\xe3\x82\xab\xe3\x82\x99\xe3\x83\x88\xe3\x83\xb3\0" /* offset 9604 */
+  "\xe3\x83\xa1\xe3\x83\xbc\xe3\x83\x88\xe3\x83\xab\0" /* offset 9620 */
+  "\xe3\x83\xa4\xe3\x83\xbc\xe3\x83\x88\xe3\x82\x99\0" /* offset 9633 */
+  "\xe3\x83\xa4\xe3\x83\xbc\xe3\x83\xab\0" /* offset 9646 */
+  "\xe3\x83\xa6\xe3\x82\xa2\xe3\x83\xb3\0" /* offset 9656 */
+  "\xe3\x83\xaa\xe3\x83\x83\xe3\x83\x88\xe3\x83\xab\0" /* offset 9666 */
+  "\xe3\x83\xaa\xe3\x83\xa9\0" /* offset 9679 */
+  "\xe3\x83\xab\xe3\x83\x92\xe3\x82\x9a\xe3\x83\xbc\0" /* offset 9686 */
+  "\xe3\x83\xab\xe3\x83\xbc\xe3\x83\x95\xe3\x82\x99\xe3\x83\xab\0" /* offset 9699 */
+  "\xe3\x83\xac\xe3\x83\xa0\0" /* offset 9715 */
+  "\xe3\x83\xac\xe3\x83\xb3\xe3\x83\x88\xe3\x82\xb1\xe3\x82\x99\xe3\x83\xb3\0" /* offset 9722 */
+  "\xe3\x83\xaf\xe3\x83\x83\xe3\x83\x88\0" /* offset 9741 */
+  "\x30\xe7\x82\xb9\0" /* offset 9751 */
+  "\x31\xe7\x82\xb9\0" /* offset 9756 */
+  "\x32\xe7\x82\xb9\0" /* offset 9761 */
+  "\x33\xe7\x82\xb9\0" /* offset 9766 */
+  "\x34\xe7\x82\xb9\0" /* offset 9771 */
+  "\x35\xe7\x82\xb9\0" /* offset 9776 */
+  "\x36\xe7\x82\xb9\0" /* offset 9781 */
+  "\x37\xe7\x82\xb9\0" /* offset 9786 */
+  "\x38\xe7\x82\xb9\0" /* offset 9791 */
+  "\x39\xe7\x82\xb9\0" /* offset 9796 */
+  "\x31\x30\xe7\x82\xb9\0" /* offset 9801 */
+  "\x31\x31\xe7\x82\xb9\0" /* offset 9807 */
+  "\x31\x32\xe7\x82\xb9\0" /* offset 9813 */
+  "\x31\x33\xe7\x82\xb9\0" /* offset 9819 */
+  "\x31\x34\xe7\x82\xb9\0" /* offset 9825 */
+  "\x31\x35\xe7\x82\xb9\0" /* offset 9831 */
+  "\x31\x36\xe7\x82\xb9\0" /* offset 9837 */
+  "\x31\x37\xe7\x82\xb9\0" /* offset 9843 */
+  "\x31\x38\xe7\x82\xb9\0" /* offset 9849 */
+  "\x31\x39\xe7\x82\xb9\0" /* offset 9855 */
+  "\x32\x30\xe7\x82\xb9\0" /* offset 9861 */
+  "\x32\x31\xe7\x82\xb9\0" /* offset 9867 */
+  "\x32\x32\xe7\x82\xb9\0" /* offset 9873 */
+  "\x32\x33\xe7\x82\xb9\0" /* offset 9879 */
+  "\x32\x34\xe7\x82\xb9\0" /* offset 9885 */
+  "\x68\x50\x61\0" /* offset 9891 */
+  "\x64\x61\0" /* offset 9895 */
+  "\x41\x55\0" /* offset 9898 */
+  "\x62\x61\x72\0" /* offset 9901 */
+  "\x6f\x56\0" /* offset 9905 */
+  "\x70\x63\0" /* offset 9908 */
+  "\xe5\xb9\xb3\xe6\x88\x90\0" /* offset 9911 */
+  "\xe6\x98\xad\xe5\x92\x8c\0" /* offset 9918 */
+  "\xe5\xa4\xa7\xe6\xad\xa3\0" /* offset 9925 */
+  "\xe6\x98\x8e\xe6\xb2\xbb\0" /* offset 9932 */
+  "\xe6\xa0\xaa\xe5\xbc\x8f\xe4\xbc\x9a\xe7\xa4\xbe\0" /* offset 9939 */
+  "\x70\x41\0" /* offset 9952 */
+  "\x6e\x41\0" /* offset 9955 */
+  "\xce\xbc\x41\0" /* offset 9958 */
+  "\x6d\x41\0" /* offset 9962 */
+  "\x6b\x41\0" /* offset 9965 */
+  "\x4b\x42\0" /* offset 9968 */
+  "\x4d\x42\0" /* offset 9971 */
+  "\x47\x42\0" /* offset 9974 */
+  "\x63\x61\x6c\0" /* offset 9977 */
+  "\x6b\x63\x61\x6c\0" /* offset 9981 */
+  "\x70\x46\0" /* offset 9986 */
+  "\x6e\x46\0" /* offset 9989 */
+  "\xce\xbc\x46\0" /* offset 9992 */
+  "\xce\xbc\x67\0" /* offset 9996 */
+  "\x6d\x67\0" /* offset 10000 */
+  "\x6b\x67\0" /* offset 10003 */
+  "\x48\x7a\0" /* offset 10006 */
+  "\x6b\x48\x7a\0" /* offset 10009 */
+  "\x4d\x48\x7a\0" /* offset 10013 */
+  "\x47\x48\x7a\0" /* offset 10017 */
+  "\x54\x48\x7a\0" /* offset 10021 */
+  "\xce\xbc\x6c\0" /* offset 10025 */
+  "\x6d\x6c\0" /* offset 10029 */
+  "\x64\x6c\0" /* offset 10032 */
+  "\x6b\x6c\0" /* offset 10035 */
+  "\x66\x6d\0" /* offset 10038 */
+  "\x6e\x6d\0" /* offset 10041 */
+  "\xce\xbc\x6d\0" /* offset 10044 */
+  "\x6d\x6d\0" /* offset 10048 */
+  "\x63\x6d\0" /* offset 10051 */
+  "\x6b\x6d\0" /* offset 10054 */
+  "\x6d\x6d\x32\0" /* offset 10057 */
+  "\x63\x6d\x32\0" /* offset 10061 */
+  "\x6d\x32\0" /* offset 10065 */
+  "\x6b\x6d\x32\0" /* offset 10068 */
+  "\x6d\x6d\x33\0" /* offset 10072 */
+  "\x63\x6d\x33\0" /* offset 10076 */
+  "\x6d\x33\0" /* offset 10080 */
+  "\x6b\x6d\x33\0" /* offset 10083 */
+  "\x6d\xe2\x88\x95\x73\0" /* offset 10087 */
+  "\x6d\xe2\x88\x95\x73\x32\0" /* offset 10093 */
+  "\x50\x61\0" /* offset 10100 */
+  "\x6b\x50\x61\0" /* offset 10103 */
+  "\x4d\x50\x61\0" /* offset 10107 */
+  "\x47\x50\x61\0" /* offset 10111 */
+  "\x72\x61\x64\0" /* offset 10115 */
+  "\x72\x61\x64\xe2\x88\x95\x73\0" /* offset 10119 */
+  "\x72\x61\x64\xe2\x88\x95\x73\x32\0" /* offset 10127 */
+  "\x70\x73\0" /* offset 10136 */
+  "\x6e\x73\0" /* offset 10139 */
+  "\xce\xbc\x73\0" /* offset 10142 */
+  "\x6d\x73\0" /* offset 10146 */
+  "\x70\x56\0" /* offset 10149 */
+  "\x6e\x56\0" /* offset 10152 */
+  "\xce\xbc\x56\0" /* offset 10155 */
+  "\x6d\x56\0" /* offset 10159 */
+  "\x6b\x56\0" /* offset 10162 */
+  "\x4d\x56\0" /* offset 10165 */
+  "\x70\x57\0" /* offset 10168 */
+  "\x6e\x57\0" /* offset 10171 */
+  "\xce\xbc\x57\0" /* offset 10174 */
+  "\x6d\x57\0" /* offset 10178 */
+  "\x6b\x57\0" /* offset 10181 */
+  "\x4d\x57\0" /* offset 10184 */
+  "\x6b\xce\xa9\0" /* offset 10187 */
+  "\x4d\xce\xa9\0" /* offset 10191 */
+  "\x61\x2e\x6d\x2e\0" /* offset 10195 */
+  "\x42\x71\0" /* offset 10200 */
+  "\x63\x63\0" /* offset 10203 */
+  "\x63\x64\0" /* offset 10206 */
+  "\x43\xe2\x88\x95\x6b\x67\0" /* offset 10209 */
+  "\x43\x6f\x2e\0" /* offset 10216 */
+  "\x64\x42\0" /* offset 10220 */
+  "\x47\x79\0" /* offset 10223 */
+  "\x68\x61\0" /* offset 10226 */
+  "\x48\x50\0" /* offset 10229 */
+  "\x69\x6e\0" /* offset 10232 */
+  "\x4b\x4b\0" /* offset 10235 */
+  "\x4b\x4d\0" /* offset 10238 */
+  "\x6b\x74\0" /* offset 10241 */
+  "\x6c\x6d\0" /* offset 10244 */
+  "\x6c\x6e\0" /* offset 10247 */
+  "\x6c\x6f\x67\0" /* offset 10250 */
+  "\x6c\x78\0" /* offset 10254 */
+  "\x6d\x62\0" /* offset 10257 */
+  "\x6d\x69\x6c\0" /* offset 10260 */
+  "\x6d\x6f\x6c\0" /* offset 10264 */
+  "\x50\x48\0" /* offset 10268 */
+  "\x70\x2e\x6d\x2e\0" /* offset 10271 */
+  "\x50\x50\x4d\0" /* offset 10276 */
+  "\x50\x52\0" /* offset 10280 */
+  "\x73\x72\0" /* offset 10283 */
+  "\x53\x76\0" /* offset 10286 */
+  "\x57\x62\0" /* offset 10289 */
+  "\x31\xe6\x97\xa5\0" /* offset 10292 */
+  "\x32\xe6\x97\xa5\0" /* offset 10297 */
+  "\x33\xe6\x97\xa5\0" /* offset 10302 */
+  "\x34\xe6\x97\xa5\0" /* offset 10307 */
+  "\x35\xe6\x97\xa5\0" /* offset 10312 */
+  "\x36\xe6\x97\xa5\0" /* offset 10317 */
+  "\x37\xe6\x97\xa5\0" /* offset 10322 */
+  "\x38\xe6\x97\xa5\0" /* offset 10327 */
+  "\x39\xe6\x97\xa5\0" /* offset 10332 */
+  "\x31\x30\xe6\x97\xa5\0" /* offset 10337 */
+  "\x31\x31\xe6\x97\xa5\0" /* offset 10343 */
+  "\x31\x32\xe6\x97\xa5\0" /* offset 10349 */
+  "\x31\x33\xe6\x97\xa5\0" /* offset 10355 */
+  "\x31\x34\xe6\x97\xa5\0" /* offset 10361 */
+  "\x31\x35\xe6\x97\xa5\0" /* offset 10367 */
+  "\x31\x36\xe6\x97\xa5\0" /* offset 10373 */
+  "\x31\x37\xe6\x97\xa5\0" /* offset 10379 */
+  "\x31\x38\xe6\x97\xa5\0" /* offset 10385 */
+  "\x31\x39\xe6\x97\xa5\0" /* offset 10391 */
+  "\x32\x30\xe6\x97\xa5\0" /* offset 10397 */
+  "\x32\x31\xe6\x97\xa5\0" /* offset 10403 */
+  "\x32\x32\xe6\x97\xa5\0" /* offset 10409 */
+  "\x32\x33\xe6\x97\xa5\0" /* offset 10415 */
+  "\x32\x34\xe6\x97\xa5\0" /* offset 10421 */
+  "\x32\x35\xe6\x97\xa5\0" /* offset 10427 */
+  "\x32\x36\xe6\x97\xa5\0" /* offset 10433 */
+  "\x32\x37\xe6\x97\xa5\0" /* offset 10439 */
+  "\x32\x38\xe6\x97\xa5\0" /* offset 10445 */
+  "\x32\x39\xe6\x97\xa5\0" /* offset 10451 */
+  "\x33\x30\xe6\x97\xa5\0" /* offset 10457 */
+  "\x33\x31\xe6\x97\xa5\0" /* offset 10463 */
+  "\xe8\xb1\x88\0" /* offset 10469 */
+  "\xe6\x9b\xb4\0" /* offset 10473 */
+  "\xe8\xb3\x88\0" /* offset 10477 */
+  "\xe6\xbb\x91\0" /* offset 10481 */
+  "\xe4\xb8\xb2\0" /* offset 10485 */
+  "\xe5\x8f\xa5\0" /* offset 10489 */
+  "\xe5\xa5\x91\0" /* offset 10493 */
+  "\xe5\x96\x87\0" /* offset 10497 */
+  "\xe5\xa5\x88\0" /* offset 10501 */
+  "\xe6\x87\xb6\0" /* offset 10505 */
+  "\xe7\x99\xa9\0" /* offset 10509 */
+  "\xe7\xbe\x85\0" /* offset 10513 */
+  "\xe8\x98\xbf\0" /* offset 10517 */
+  "\xe8\x9e\xba\0" /* offset 10521 */
+  "\xe8\xa3\xb8\0" /* offset 10525 */
+  "\xe9\x82\x8f\0" /* offset 10529 */
+  "\xe6\xa8\x82\0" /* offset 10533 */
+  "\xe6\xb4\x9b\0" /* offset 10537 */
+  "\xe7\x83\x99\0" /* offset 10541 */
+  "\xe7\x8f\x9e\0" /* offset 10545 */
+  "\xe8\x90\xbd\0" /* offset 10549 */
+  "\xe9\x85\xaa\0" /* offset 10553 */
+  "\xe9\xa7\xb1\0" /* offset 10557 */
+  "\xe4\xba\x82\0" /* offset 10561 */
+  "\xe5\x8d\xb5\0" /* offset 10565 */
+  "\xe6\xac\x84\0" /* offset 10569 */
+  "\xe7\x88\x9b\0" /* offset 10573 */
+  "\xe8\x98\xad\0" /* offset 10577 */
+  "\xe9\xb8\x9e\0" /* offset 10581 */
+  "\xe5\xb5\x90\0" /* offset 10585 */
+  "\xe6\xbf\xab\0" /* offset 10589 */
+  "\xe8\x97\x8d\0" /* offset 10593 */
+  "\xe8\xa5\xa4\0" /* offset 10597 */
+  "\xe6\x8b\x89\0" /* offset 10601 */
+  "\xe8\x87\x98\0" /* offset 10605 */
+  "\xe8\xa0\x9f\0" /* offset 10609 */
+  "\xe5\xbb\x8a\0" /* offset 10613 */
+  "\xe6\x9c\x97\0" /* offset 10617 */
+  "\xe6\xb5\xaa\0" /* offset 10621 */
+  "\xe7\x8b\xbc\0" /* offset 10625 */
+  "\xe9\x83\x8e\0" /* offset 10629 */
+  "\xe4\xbe\x86\0" /* offset 10633 */
+  "\xe5\x86\xb7\0" /* offset 10637 */
+  "\xe5\x8b\x9e\0" /* offset 10641 */
+  "\xe6\x93\x84\0" /* offset 10645 */
+  "\xe6\xab\x93\0" /* offset 10649 */
+  "\xe7\x88\x90\0" /* offset 10653 */
+  "\xe7\x9b\xa7\0" /* offset 10657 */
+  "\xe8\x98\x86\0" /* offset 10661 */
+  "\xe8\x99\x9c\0" /* offset 10665 */
+  "\xe8\xb7\xaf\0" /* offset 10669 */
+  "\xe9\x9c\xb2\0" /* offset 10673 */
+  "\xe9\xad\xaf\0" /* offset 10677 */
+  "\xe9\xb7\xba\0" /* offset 10681 */
+  "\xe7\xa2\x8c\0" /* offset 10685 */
+  "\xe7\xa5\xbf\0" /* offset 10689 */
+  "\xe7\xb6\xa0\0" /* offset 10693 */
+  "\xe8\x8f\x89\0" /* offset 10697 */
+  "\xe9\x8c\x84\0" /* offset 10701 */
+  "\xe8\xab\x96\0" /* offset 10705 */
+  "\xe5\xa3\x9f\0" /* offset 10709 */
+  "\xe5\xbc\x84\0" /* offset 10713 */
+  "\xe7\xb1\xa0\0" /* offset 10717 */
+  "\xe8\x81\xbe\0" /* offset 10721 */
+  "\xe7\x89\xa2\0" /* offset 10725 */
+  "\xe7\xa3\x8a\0" /* offset 10729 */
+  "\xe8\xb3\x82\0" /* offset 10733 */
+  "\xe9\x9b\xb7\0" /* offset 10737 */
+  "\xe5\xa3\x98\0" /* offset 10741 */
+  "\xe5\xb1\xa2\0" /* offset 10745 */
+  "\xe6\xa8\x93\0" /* offset 10749 */
+  "\xe6\xb7\x9a\0" /* offset 10753 */
+  "\xe6\xbc\x8f\0" /* offset 10757 */
+  "\xe7\xb4\xaf\0" /* offset 10761 */
+  "\xe7\xb8\xb7\0" /* offset 10765 */
+  "\xe9\x99\x8b\0" /* offset 10769 */
+  "\xe5\x8b\x92\0" /* offset 10773 */
+  "\xe8\x82\x8b\0" /* offset 10777 */
+  "\xe5\x87\x9c\0" /* offset 10781 */
+  "\xe5\x87\x8c\0" /* offset 10785 */
+  "\xe7\xa8\x9c\0" /* offset 10789 */
+  "\xe7\xb6\xbe\0" /* offset 10793 */
+  "\xe8\x8f\xb1\0" /* offset 10797 */
+  "\xe9\x99\xb5\0" /* offset 10801 */
+  "\xe8\xae\x80\0" /* offset 10805 */
+  "\xe6\x8b\x8f\0" /* offset 10809 */
+  "\xe8\xab\xbe\0" /* offset 10813 */
+  "\xe4\xb8\xb9\0" /* offset 10817 */
+  "\xe5\xaf\xa7\0" /* offset 10821 */
+  "\xe6\x80\x92\0" /* offset 10825 */
+  "\xe7\x8e\x87\0" /* offset 10829 */
+  "\xe7\x95\xb0\0" /* offset 10833 */
+  "\xe5\x8c\x97\0" /* offset 10837 */
+  "\xe7\xa3\xbb\0" /* offset 10841 */
+  "\xe4\xbe\xbf\0" /* offset 10845 */
+  "\xe5\xbe\xa9\0" /* offset 10849 */
+  "\xe4\xb8\x8d\0" /* offset 10853 */
+  "\xe6\xb3\x8c\0" /* offset 10857 */
+  "\xe6\x95\xb8\0" /* offset 10861 */
+  "\xe7\xb4\xa2\0" /* offset 10865 */
+  "\xe5\x8f\x83\0" /* offset 10869 */
+  "\xe5\xa1\x9e\0" /* offset 10873 */
+  "\xe7\x9c\x81\0" /* offset 10877 */
+  "\xe8\x91\x89\0" /* offset 10881 */
+  "\xe8\xaa\xaa\0" /* offset 10885 */
+  "\xe6\xae\xba\0" /* offset 10889 */
+  "\xe6\xb2\x88\0" /* offset 10893 */
+  "\xe6\x8b\xbe\0" /* offset 10897 */
+  "\xe8\x8b\xa5\0" /* offset 10901 */
+  "\xe6\x8e\xa0\0" /* offset 10905 */
+  "\xe7\x95\xa5\0" /* offset 10909 */
+  "\xe4\xba\xae\0" /* offset 10913 */
+  "\xe5\x85\xa9\0" /* offset 10917 */
+  "\xe5\x87\x89\0" /* offset 10921 */
+  "\xe6\xa2\x81\0" /* offset 10925 */
+  "\xe7\xb3\xa7\0" /* offset 10929 */
+  "\xe8\x89\xaf\0" /* offset 10933 */
+  "\xe8\xab\x92\0" /* offset 10937 */
+  "\xe9\x87\x8f\0" /* offset 10941 */
+  "\xe5\x8b\xb5\0" /* offset 10945 */
+  "\xe5\x91\x82\0" /* offset 10949 */
+  "\xe5\xbb\xac\0" /* offset 10953 */
+  "\xe6\x97\x85\0" /* offset 10957 */
+  "\xe6\xbf\xbe\0" /* offset 10961 */
+  "\xe7\xa4\xaa\0" /* offset 10965 */
+  "\xe9\x96\xad\0" /* offset 10969 */
+  "\xe9\xa9\xaa\0" /* offset 10973 */
+  "\xe9\xba\x97\0" /* offset 10977 */
+  "\xe9\xbb\x8e\0" /* offset 10981 */
+  "\xe6\x9b\x86\0" /* offset 10985 */
+  "\xe6\xad\xb7\0" /* offset 10989 */
+  "\xe8\xbd\xa2\0" /* offset 10993 */
+  "\xe5\xb9\xb4\0" /* offset 10997 */
+  "\xe6\x86\x90\0" /* offset 11001 */
+  "\xe6\x88\x80\0" /* offset 11005 */
+  "\xe6\x92\x9a\0" /* offset 11009 */
+  "\xe6\xbc\xa3\0" /* offset 11013 */
+  "\xe7\x85\x89\0" /* offset 11017 */
+  "\xe7\x92\x89\0" /* offset 11021 */
+  "\xe7\xa7\x8a\0" /* offset 11025 */
+  "\xe7\xb7\xb4\0" /* offset 11029 */
+  "\xe8\x81\xaf\0" /* offset 11033 */
+  "\xe8\xbc\xa6\0" /* offset 11037 */
+  "\xe8\x93\xae\0" /* offset 11041 */
+  "\xe9\x80\xa3\0" /* offset 11045 */
+  "\xe9\x8d\x8a\0" /* offset 11049 */
+  "\xe5\x88\x97\0" /* offset 11053 */
+  "\xe5\x8a\xa3\0" /* offset 11057 */
+  "\xe5\x92\xbd\0" /* offset 11061 */
+  "\xe7\x83\x88\0" /* offset 11065 */
+  "\xe8\xa3\x82\0" /* offset 11069 */
+  "\xe5\xbb\x89\0" /* offset 11073 */
+  "\xe5\xbf\xb5\0" /* offset 11077 */
+  "\xe6\x8d\xbb\0" /* offset 11081 */
+  "\xe6\xae\xae\0" /* offset 11085 */
+  "\xe7\xb0\xbe\0" /* offset 11089 */
+  "\xe7\x8d\xb5\0" /* offset 11093 */
+  "\xe4\xbb\xa4\0" /* offset 11097 */
+  "\xe5\x9b\xb9\0" /* offset 11101 */
+  "\xe5\xb6\xba\0" /* offset 11105 */
+  "\xe6\x80\x9c\0" /* offset 11109 */
+  "\xe7\x8e\xb2\0" /* offset 11113 */
+  "\xe7\x91\xa9\0" /* offset 11117 */
+  "\xe7\xbe\x9a\0" /* offset 11121 */
+  "\xe8\x81\x86\0" /* offset 11125 */
+  "\xe9\x88\xb4\0" /* offset 11129 */
+  "\xe9\x9b\xb6\0" /* offset 11133 */
+  "\xe9\x9d\x88\0" /* offset 11137 */
+  "\xe9\xa0\x98\0" /* offset 11141 */
+  "\xe4\xbe\x8b\0" /* offset 11145 */
+  "\xe7\xa6\xae\0" /* offset 11149 */
+  "\xe9\x86\xb4\0" /* offset 11153 */
+  "\xe9\x9a\xb8\0" /* offset 11157 */
+  "\xe6\x83\xa1\0" /* offset 11161 */
+  "\xe4\xba\x86\0" /* offset 11165 */
+  "\xe5\x83\x9a\0" /* offset 11169 */
+  "\xe5\xaf\xae\0" /* offset 11173 */
+  "\xe5\xb0\xbf\0" /* offset 11177 */
+  "\xe6\x96\x99\0" /* offset 11181 */
+  "\xe7\x87\x8e\0" /* offset 11185 */
+  "\xe7\x99\x82\0" /* offset 11189 */
+  "\xe8\x93\xbc\0" /* offset 11193 */
+  "\xe9\x81\xbc\0" /* offset 11197 */
+  "\xe6\x9a\x88\0" /* offset 11201 */
+  "\xe9\x98\xae\0" /* offset 11205 */
+  "\xe5\x8a\x89\0" /* offset 11209 */
+  "\xe6\x9d\xbb\0" /* offset 11213 */
+  "\xe6\x9f\xb3\0" /* offset 11217 */
+  "\xe6\xb5\x81\0" /* offset 11221 */
+  "\xe6\xba\x9c\0" /* offset 11225 */
+  "\xe7\x90\x89\0" /* offset 11229 */
+  "\xe7\x95\x99\0" /* offset 11233 */
+  "\xe7\xa1\xab\0" /* offset 11237 */
+  "\xe7\xb4\x90\0" /* offset 11241 */
+  "\xe9\xa1\x9e\0" /* offset 11245 */
+  "\xe6\x88\xae\0" /* offset 11249 */
+  "\xe9\x99\xb8\0" /* offset 11253 */
+  "\xe5\x80\xab\0" /* offset 11257 */
+  "\xe5\xb4\x99\0" /* offset 11261 */
+  "\xe6\xb7\xaa\0" /* offset 11265 */
+  "\xe8\xbc\xaa\0" /* offset 11269 */
+  "\xe5\xbe\x8b\0" /* offset 11273 */
+  "\xe6\x85\x84\0" /* offset 11277 */
+  "\xe6\xa0\x97\0" /* offset 11281 */
+  "\xe9\x9a\x86\0" /* offset 11285 */
+  "\xe5\x88\xa9\0" /* offset 11289 */
+  "\xe5\x90\x8f\0" /* offset 11293 */
+  "\xe5\xb1\xa5\0" /* offset 11297 */
+  "\xe6\x98\x93\0" /* offset 11301 */
+  "\xe6\x9d\x8e\0" /* offset 11305 */
+  "\xe6\xa2\xa8\0" /* offset 11309 */
+  "\xe6\xb3\xa5\0" /* offset 11313 */
+  "\xe7\x90\x86\0" /* offset 11317 */
+  "\xe7\x97\xa2\0" /* offset 11321 */
+  "\xe7\xbd\xb9\0" /* offset 11325 */
+  "\xe8\xa3\x8f\0" /* offset 11329 */
+  "\xe8\xa3\xa1\0" /* offset 11333 */
+  "\xe9\x9b\xa2\0" /* offset 11337 */
+  "\xe5\x8c\xbf\0" /* offset 11341 */
+  "\xe6\xba\xba\0" /* offset 11345 */
+  "\xe5\x90\x9d\0" /* offset 11349 */
+  "\xe7\x87\x90\0" /* offset 11353 */
+  "\xe7\x92\x98\0" /* offset 11357 */
+  "\xe8\x97\xba\0" /* offset 11361 */
+  "\xe9\x9a\xa3\0" /* offset 11365 */
+  "\xe9\xb1\x97\0" /* offset 11369 */
+  "\xe9\xba\x9f\0" /* offset 11373 */
+  "\xe6\x9e\x97\0" /* offset 11377 */
+  "\xe6\xb7\x8b\0" /* offset 11381 */
+  "\xe8\x87\xa8\0" /* offset 11385 */
+  "\xe7\xac\xa0\0" /* offset 11389 */
+  "\xe7\xb2\x92\0" /* offset 11393 */
+  "\xe7\x8b\x80\0" /* offset 11397 */
+  "\xe7\x82\x99\0" /* offset 11401 */
+  "\xe8\xad\x98\0" /* offset 11405 */
+  "\xe4\xbb\x80\0" /* offset 11409 */
+  "\xe8\x8c\xb6\0" /* offset 11413 */
+  "\xe5\x88\xba\0" /* offset 11417 */
+  "\xe5\x88\x87\0" /* offset 11421 */
+  "\xe5\xba\xa6\0" /* offset 11425 */
+  "\xe6\x8b\x93\0" /* offset 11429 */
+  "\xe7\xb3\x96\0" /* offset 11433 */
+  "\xe5\xae\x85\0" /* offset 11437 */
+  "\xe6\xb4\x9e\0" /* offset 11441 */
+  "\xe6\x9a\xb4\0" /* offset 11445 */
+  "\xe8\xbc\xbb\0" /* offset 11449 */
+  "\xe9\x99\x8d\0" /* offset 11453 */
+  "\xe5\xbb\x93\0" /* offset 11457 */
+  "\xe5\x85\x80\0" /* offset 11461 */
+  "\xe5\x97\x80\0" /* offset 11465 */
+  "\xe5\xa1\x9a\0" /* offset 11469 */
+  "\xe6\x99\xb4\0" /* offset 11473 */
+  "\xe5\x87\x9e\0" /* offset 11477 */
+  "\xe7\x8c\xaa\0" /* offset 11481 */
+  "\xe7\x9b\x8a\0" /* offset 11485 */
+  "\xe7\xa4\xbc\0" /* offset 11489 */
+  "\xe7\xa5\x9e\0" /* offset 11493 */
+  "\xe7\xa5\xa5\0" /* offset 11497 */
+  "\xe7\xa6\x8f\0" /* offset 11501 */
+  "\xe9\x9d\x96\0" /* offset 11505 */
+  "\xe7\xb2\xbe\0" /* offset 11509 */
+  "\xe8\x98\x92\0" /* offset 11513 */
+  "\xe8\xab\xb8\0" /* offset 11517 */
+  "\xe9\x80\xb8\0" /* offset 11521 */
+  "\xe9\x83\xbd\0" /* offset 11525 */
+  "\xe9\xa3\xaf\0" /* offset 11529 */
+  "\xe9\xa3\xbc\0" /* offset 11533 */
+  "\xe9\xa4\xa8\0" /* offset 11537 */
+  "\xe9\xb6\xb4\0" /* offset 11541 */
+  "\xe4\xbe\xae\0" /* offset 11545 */
+  "\xe5\x83\xa7\0" /* offset 11549 */
+  "\xe5\x85\x8d\0" /* offset 11553 */
+  "\xe5\x8b\x89\0" /* offset 11557 */
+  "\xe5\x8b\xa4\0" /* offset 11561 */
+  "\xe5\x8d\x91\0" /* offset 11565 */
+  "\xe5\x96\x9d\0" /* offset 11569 */
+  "\xe5\x98\x86\0" /* offset 11573 */
+  "\xe5\x99\xa8\0" /* offset 11577 */
+  "\xe5\xa1\x80\0" /* offset 11581 */
+  "\xe5\xa2\xa8\0" /* offset 11585 */
+  "\xe5\xb1\xa4\0" /* offset 11589 */
+  "\xe6\x82\x94\0" /* offset 11593 */
+  "\xe6\x85\xa8\0" /* offset 11597 */
+  "\xe6\x86\x8e\0" /* offset 11601 */
+  "\xe6\x87\xb2\0" /* offset 11605 */
+  "\xe6\x95\x8f\0" /* offset 11609 */
+  "\xe6\x97\xa2\0" /* offset 11613 */
+  "\xe6\x9a\x91\0" /* offset 11617 */
+  "\xe6\xa2\x85\0" /* offset 11621 */
+  "\xe6\xb5\xb7\0" /* offset 11625 */
+  "\xe6\xb8\x9a\0" /* offset 11629 */
+  "\xe6\xbc\xa2\0" /* offset 11633 */
+  "\xe7\x85\xae\0" /* offset 11637 */
+  "\xe7\x88\xab\0" /* offset 11641 */
+  "\xe7\x90\xa2\0" /* offset 11645 */
+  "\xe7\xa2\x91\0" /* offset 11649 */
+  "\xe7\xa5\x89\0" /* offset 11653 */
+  "\xe7\xa5\x88\0" /* offset 11657 */
+  "\xe7\xa5\x90\0" /* offset 11661 */
+  "\xe7\xa5\x96\0" /* offset 11665 */
+  "\xe7\xa6\x8d\0" /* offset 11669 */
+  "\xe7\xa6\x8e\0" /* offset 11673 */
+  "\xe7\xa9\x80\0" /* offset 11677 */
+  "\xe7\xaa\x81\0" /* offset 11681 */
+  "\xe7\xaf\x80\0" /* offset 11685 */
+  "\xe7\xb8\x89\0" /* offset 11689 */
+  "\xe7\xb9\x81\0" /* offset 11693 */
+  "\xe7\xbd\xb2\0" /* offset 11697 */
+  "\xe8\x80\x85\0" /* offset 11701 */
+  "\xe8\x87\xad\0" /* offset 11705 */
+  "\xe8\x89\xb9\0" /* offset 11709 */
+  "\xe8\x91\x97\0" /* offset 11713 */
+  "\xe8\xa4\x90\0" /* offset 11717 */
+  "\xe8\xa6\x96\0" /* offset 11721 */
+  "\xe8\xac\x81\0" /* offset 11725 */
+  "\xe8\xac\xb9\0" /* offset 11729 */
+  "\xe8\xb3\x93\0" /* offset 11733 */
+  "\xe8\xb4\x88\0" /* offset 11737 */
+  "\xe8\xbe\xb6\0" /* offset 11741 */
+  "\xe9\x9b\xa3\0" /* offset 11745 */
+  "\xe9\x9f\xbf\0" /* offset 11749 */
+  "\xe9\xa0\xbb\0" /* offset 11753 */
+  "\x66\x66\0" /* offset 11757 */
+  "\x66\x69\0" /* offset 11760 */
+  "\x66\x6c\0" /* offset 11763 */
+  "\x66\x66\x69\0" /* offset 11766 */
+  "\x66\x66\x6c\0" /* offset 11770 */
+  "\x73\x74\0" /* offset 11774 */
+  "\xd5\xb4\xd5\xb6\0" /* offset 11777 */
+  "\xd5\xb4\xd5\xa5\0" /* offset 11782 */
+  "\xd5\xb4\xd5\xab\0" /* offset 11787 */
+  "\xd5\xbe\xd5\xb6\0" /* offset 11792 */
+  "\xd5\xb4\xd5\xad\0" /* offset 11797 */
+  "\xd7\x99\xd6\xb4\0" /* offset 11802 */
+  "\xd7\xb2\xd6\xb7\0" /* offset 11807 */
+  "\xd7\xa2\0" /* offset 11812 */
+  "\xd7\x94\0" /* offset 11815 */
+  "\xd7\x9b\0" /* offset 11818 */
+  "\xd7\x9c\0" /* offset 11821 */
+  "\xd7\x9d\0" /* offset 11824 */
+  "\xd7\xa8\0" /* offset 11827 */
+  "\xd7\xaa\0" /* offset 11830 */
+  "\xd7\xa9\xd7\x81\0" /* offset 11833 */
+  "\xd7\xa9\xd7\x82\0" /* offset 11838 */
+  "\xd7\xa9\xd6\xbc\xd7\x81\0" /* offset 11843 */
+  "\xd7\xa9\xd6\xbc\xd7\x82\0" /* offset 11850 */
+  "\xd7\x90\xd6\xb7\0" /* offset 11857 */
+  "\xd7\x90\xd6\xb8\0" /* offset 11862 */
+  "\xd7\x90\xd6\xbc\0" /* offset 11867 */
+  "\xd7\x91\xd6\xbc\0" /* offset 11872 */
+  "\xd7\x92\xd6\xbc\0" /* offset 11877 */
+  "\xd7\x93\xd6\xbc\0" /* offset 11882 */
+  "\xd7\x94\xd6\xbc\0" /* offset 11887 */
+  "\xd7\x95\xd6\xbc\0" /* offset 11892 */
+  "\xd7\x96\xd6\xbc\0" /* offset 11897 */
+  "\xd7\x98\xd6\xbc\0" /* offset 11902 */
+  "\xd7\x99\xd6\xbc\0" /* offset 11907 */
+  "\xd7\x9a\xd6\xbc\0" /* offset 11912 */
+  "\xd7\x9b\xd6\xbc\0" /* offset 11917 */
+  "\xd7\x9c\xd6\xbc\0" /* offset 11922 */
+  "\xd7\x9e\xd6\xbc\0" /* offset 11927 */
+  "\xd7\xa0\xd6\xbc\0" /* offset 11932 */
+  "\xd7\xa1\xd6\xbc\0" /* offset 11937 */
+  "\xd7\xa3\xd6\xbc\0" /* offset 11942 */
+  "\xd7\xa4\xd6\xbc\0" /* offset 11947 */
+  "\xd7\xa6\xd6\xbc\0" /* offset 11952 */
+  "\xd7\xa7\xd6\xbc\0" /* offset 11957 */
+  "\xd7\xa8\xd6\xbc\0" /* offset 11962 */
+  "\xd7\xa9\xd6\xbc\0" /* offset 11967 */
+  "\xd7\xaa\xd6\xbc\0" /* offset 11972 */
+  "\xd7\x95\xd6\xb9\0" /* offset 11977 */
+  "\xd7\x91\xd6\xbf\0" /* offset 11982 */
+  "\xd7\x9b\xd6\xbf\0" /* offset 11987 */
+  "\xd7\xa4\xd6\xbf\0" /* offset 11992 */
+  "\xd7\x90\xd7\x9c\0" /* offset 11997 */
+  "\xd9\xb1\0" /* offset 12002 */
+  "\xd9\xbb\0" /* offset 12005 */
+  "\xd9\xbe\0" /* offset 12008 */
+  "\xda\x80\0" /* offset 12011 */
+  "\xd9\xba\0" /* offset 12014 */
+  "\xd9\xbf\0" /* offset 12017 */
+  "\xd9\xb9\0" /* offset 12020 */
+  "\xda\xa4\0" /* offset 12023 */
+  "\xda\xa6\0" /* offset 12026 */
+  "\xda\x84\0" /* offset 12029 */
+  "\xda\x83\0" /* offset 12032 */
+  "\xda\x86\0" /* offset 12035 */
+  "\xda\x87\0" /* offset 12038 */
+  "\xda\x8d\0" /* offset 12041 */
+  "\xda\x8c\0" /* offset 12044 */
+  "\xda\x8e\0" /* offset 12047 */
+  "\xda\x88\0" /* offset 12050 */
+  "\xda\x98\0" /* offset 12053 */
+  "\xda\x91\0" /* offset 12056 */
+  "\xda\xa9\0" /* offset 12059 */
+  "\xda\xaf\0" /* offset 12062 */
+  "\xda\xb3\0" /* offset 12065 */
+  "\xda\xb1\0" /* offset 12068 */
+  "\xda\xba\0" /* offset 12071 */
+  "\xda\xbb\0" /* offset 12074 */
+  "\xdb\x81\0" /* offset 12077 */
+  "\xda\xbe\0" /* offset 12080 */
+  "\xdb\x92\0" /* offset 12083 */
+  "\xda\xad\0" /* offset 12086 */
+  "\xdb\x87\0" /* offset 12089 */
+  "\xdb\x86\0" /* offset 12092 */
+  "\xdb\x88\0" /* offset 12095 */
+  "\xdb\x8b\0" /* offset 12098 */
+  "\xdb\x85\0" /* offset 12101 */
+  "\xdb\x89\0" /* offset 12104 */
+  "\xdb\x90\0" /* offset 12107 */
+  "\xd9\x89\0" /* offset 12110 */
+  "\xd9\x8a\xd9\x94\xd8\xa7\0" /* offset 12113 */
+  "\xd9\x8a\xd9\x94\xdb\x95\0" /* offset 12120 */
+  "\xd9\x8a\xd9\x94\xd9\x88\0" /* offset 12127 */
+  "\xd9\x8a\xd9\x94\xdb\x87\0" /* offset 12134 */
+  "\xd9\x8a\xd9\x94\xdb\x86\0" /* offset 12141 */
+  "\xd9\x8a\xd9\x94\xdb\x88\0" /* offset 12148 */
+  "\xd9\x8a\xd9\x94\xdb\x90\0" /* offset 12155 */
+  "\xd9\x8a\xd9\x94\xd9\x89\0" /* offset 12162 */
+  "\xdb\x8c\0" /* offset 12169 */
+  "\xd9\x8a\xd9\x94\xd8\xac\0" /* offset 12172 */
+  "\xd9\x8a\xd9\x94\xd8\xad\0" /* offset 12179 */
+  "\xd9\x8a\xd9\x94\xd9\x85\0" /* offset 12186 */
+  "\xd9\x8a\xd9\x94\xd9\x8a\0" /* offset 12193 */
+  "\xd8\xa8\xd8\xac\0" /* offset 12200 */
+  "\xd8\xa8\xd8\xad\0" /* offset 12205 */
+  "\xd8\xa8\xd8\xae\0" /* offset 12210 */
+  "\xd8\xa8\xd9\x85\0" /* offset 12215 */
+  "\xd8\xa8\xd9\x89\0" /* offset 12220 */
+  "\xd8\xa8\xd9\x8a\0" /* offset 12225 */
+  "\xd8\xaa\xd8\xac\0" /* offset 12230 */
+  "\xd8\xaa\xd8\xad\0" /* offset 12235 */
+  "\xd8\xaa\xd8\xae\0" /* offset 12240 */
+  "\xd8\xaa\xd9\x85\0" /* offset 12245 */
+  "\xd8\xaa\xd9\x89\0" /* offset 12250 */
+  "\xd8\xaa\xd9\x8a\0" /* offset 12255 */
+  "\xd8\xab\xd8\xac\0" /* offset 12260 */
+  "\xd8\xab\xd9\x85\0" /* offset 12265 */
+  "\xd8\xab\xd9\x89\0" /* offset 12270 */
+  "\xd8\xab\xd9\x8a\0" /* offset 12275 */
+  "\xd8\xac\xd8\xad\0" /* offset 12280 */
+  "\xd8\xac\xd9\x85\0" /* offset 12285 */
+  "\xd8\xad\xd8\xac\0" /* offset 12290 */
+  "\xd8\xad\xd9\x85\0" /* offset 12295 */
+  "\xd8\xae\xd8\xac\0" /* offset 12300 */
+  "\xd8\xae\xd8\xad\0" /* offset 12305 */
+  "\xd8\xae\xd9\x85\0" /* offset 12310 */
+  "\xd8\xb3\xd8\xac\0" /* offset 12315 */
+  "\xd8\xb3\xd8\xad\0" /* offset 12320 */
+  "\xd8\xb3\xd8\xae\0" /* offset 12325 */
+  "\xd8\xb3\xd9\x85\0" /* offset 12330 */
+  "\xd8\xb5\xd8\xad\0" /* offset 12335 */
+  "\xd8\xb5\xd9\x85\0" /* offset 12340 */
+  "\xd8\xb6\xd8\xac\0" /* offset 12345 */
+  "\xd8\xb6\xd8\xad\0" /* offset 12350 */
+  "\xd8\xb6\xd8\xae\0" /* offset 12355 */
+  "\xd8\xb6\xd9\x85\0" /* offset 12360 */
+  "\xd8\xb7\xd8\xad\0" /* offset 12365 */
+  "\xd8\xb7\xd9\x85\0" /* offset 12370 */
+  "\xd8\xb8\xd9\x85\0" /* offset 12375 */
+  "\xd8\xb9\xd8\xac\0" /* offset 12380 */
+  "\xd8\xb9\xd9\x85\0" /* offset 12385 */
+  "\xd8\xba\xd8\xac\0" /* offset 12390 */
+  "\xd8\xba\xd9\x85\0" /* offset 12395 */
+  "\xd9\x81\xd8\xac\0" /* offset 12400 */
+  "\xd9\x81\xd8\xad\0" /* offset 12405 */
+  "\xd9\x81\xd8\xae\0" /* offset 12410 */
+  "\xd9\x81\xd9\x85\0" /* offset 12415 */
+  "\xd9\x81\xd9\x89\0" /* offset 12420 */
+  "\xd9\x81\xd9\x8a\0" /* offset 12425 */
+  "\xd9\x82\xd8\xad\0" /* offset 12430 */
+  "\xd9\x82\xd9\x85\0" /* offset 12435 */
+  "\xd9\x82\xd9\x89\0" /* offset 12440 */
+  "\xd9\x82\xd9\x8a\0" /* offset 12445 */
+  "\xd9\x83\xd8\xa7\0" /* offset 12450 */
+  "\xd9\x83\xd8\xac\0" /* offset 12455 */
+  "\xd9\x83\xd8\xad\0" /* offset 12460 */
+  "\xd9\x83\xd8\xae\0" /* offset 12465 */
+  "\xd9\x83\xd9\x84\0" /* offset 12470 */
+  "\xd9\x83\xd9\x85\0" /* offset 12475 */
+  "\xd9\x83\xd9\x89\0" /* offset 12480 */
+  "\xd9\x83\xd9\x8a\0" /* offset 12485 */
+  "\xd9\x84\xd8\xac\0" /* offset 12490 */
+  "\xd9\x84\xd8\xad\0" /* offset 12495 */
+  "\xd9\x84\xd8\xae\0" /* offset 12500 */
+  "\xd9\x84\xd9\x85\0" /* offset 12505 */
+  "\xd9\x84\xd9\x89\0" /* offset 12510 */
+  "\xd9\x84\xd9\x8a\0" /* offset 12515 */
+  "\xd9\x85\xd8\xac\0" /* offset 12520 */
+  "\xd9\x85\xd8\xad\0" /* offset 12525 */
+  "\xd9\x85\xd8\xae\0" /* offset 12530 */
+  "\xd9\x85\xd9\x85\0" /* offset 12535 */
+  "\xd9\x85\xd9\x89\0" /* offset 12540 */
+  "\xd9\x85\xd9\x8a\0" /* offset 12545 */
+  "\xd9\x86\xd8\xac\0" /* offset 12550 */
+  "\xd9\x86\xd8\xad\0" /* offset 12555 */
+  "\xd9\x86\xd8\xae\0" /* offset 12560 */
+  "\xd9\x86\xd9\x85\0" /* offset 12565 */
+  "\xd9\x86\xd9\x89\0" /* offset 12570 */
+  "\xd9\x86\xd9\x8a\0" /* offset 12575 */
+  "\xd9\x87\xd8\xac\0" /* offset 12580 */
+  "\xd9\x87\xd9\x85\0" /* offset 12585 */
+  "\xd9\x87\xd9\x89\0" /* offset 12590 */
+  "\xd9\x87\xd9\x8a\0" /* offset 12595 */
+  "\xd9\x8a\xd8\xac\0" /* offset 12600 */
+  "\xd9\x8a\xd8\xad\0" /* offset 12605 */
+  "\xd9\x8a\xd8\xae\0" /* offset 12610 */
+  "\xd9\x8a\xd9\x85\0" /* offset 12615 */
+  "\xd9\x8a\xd9\x89\0" /* offset 12620 */
+  "\xd9\x8a\xd9\x8a\0" /* offset 12625 */
+  "\xd8\xb0\xd9\xb0\0" /* offset 12630 */
+  "\xd8\xb1\xd9\xb0\0" /* offset 12635 */
+  "\xd9\x89\xd9\xb0\0" /* offset 12640 */
+  "\x20\xd9\x8c\xd9\x91\0" /* offset 12645 */
+  "\x20\xd9\x8d\xd9\x91\0" /* offset 12651 */
+  "\x20\xd9\x8e\xd9\x91\0" /* offset 12657 */
+  "\x20\xd9\x8f\xd9\x91\0" /* offset 12663 */
+  "\x20\xd9\x90\xd9\x91\0" /* offset 12669 */
+  "\x20\xd9\x91\xd9\xb0\0" /* offset 12675 */
+  "\xd9\x8a\xd9\x94\xd8\xb1\0" /* offset 12681 */
+  "\xd9\x8a\xd9\x94\xd8\xb2\0" /* offset 12688 */
+  "\xd9\x8a\xd9\x94\xd9\x86\0" /* offset 12695 */
+  "\xd8\xa8\xd8\xb1\0" /* offset 12702 */
+  "\xd8\xa8\xd8\xb2\0" /* offset 12707 */
+  "\xd8\xa8\xd9\x86\0" /* offset 12712 */
+  "\xd8\xaa\xd8\xb1\0" /* offset 12717 */
+  "\xd8\xaa\xd8\xb2\0" /* offset 12722 */
+  "\xd8\xaa\xd9\x86\0" /* offset 12727 */
+  "\xd8\xab\xd8\xb1\0" /* offset 12732 */
+  "\xd8\xab\xd8\xb2\0" /* offset 12737 */
+  "\xd8\xab\xd9\x86\0" /* offset 12742 */
+  "\xd9\x85\xd8\xa7\0" /* offset 12747 */
+  "\xd9\x86\xd8\xb1\0" /* offset 12752 */
+  "\xd9\x86\xd8\xb2\0" /* offset 12757 */
+  "\xd9\x86\xd9\x86\0" /* offset 12762 */
+  "\xd9\x8a\xd8\xb1\0" /* offset 12767 */
+  "\xd9\x8a\xd8\xb2\0" /* offset 12772 */
+  "\xd9\x8a\xd9\x86\0" /* offset 12777 */
+  "\xd9\x8a\xd9\x94\xd8\xae\0" /* offset 12782 */
+  "\xd9\x8a\xd9\x94\xd9\x87\0" /* offset 12789 */
+  "\xd8\xa8\xd9\x87\0" /* offset 12796 */
+  "\xd8\xaa\xd9\x87\0" /* offset 12801 */
+  "\xd8\xb5\xd8\xae\0" /* offset 12806 */
+  "\xd9\x84\xd9\x87\0" /* offset 12811 */
+  "\xd9\x86\xd9\x87\0" /* offset 12816 */
+  "\xd9\x87\xd9\xb0\0" /* offset 12821 */
+  "\xd9\x8a\xd9\x87\0" /* offset 12826 */
+  "\xd8\xab\xd9\x87\0" /* offset 12831 */
+  "\xd8\xb3\xd9\x87\0" /* offset 12836 */
+  "\xd8\xb4\xd9\x85\0" /* offset 12841 */
+  "\xd8\xb4\xd9\x87\0" /* offset 12846 */
+  "\xd9\x80\xd9\x8e\xd9\x91\0" /* offset 12851 */
+  "\xd9\x80\xd9\x8f\xd9\x91\0" /* offset 12858 */
+  "\xd9\x80\xd9\x90\xd9\x91\0" /* offset 12865 */
+  "\xd8\xb7\xd9\x89\0" /* offset 12872 */
+  "\xd8\xb7\xd9\x8a\0" /* offset 12877 */
+  "\xd8\xb9\xd9\x89\0" /* offset 12882 */
+  "\xd8\xb9\xd9\x8a\0" /* offset 12887 */
+  "\xd8\xba\xd9\x89\0" /* offset 12892 */
+  "\xd8\xba\xd9\x8a\0" /* offset 12897 */
+  "\xd8\xb3\xd9\x89\0" /* offset 12902 */
+  "\xd8\xb3\xd9\x8a\0" /* offset 12907 */
+  "\xd8\xb4\xd9\x89\0" /* offset 12912 */
+  "\xd8\xb4\xd9\x8a\0" /* offset 12917 */
+  "\xd8\xad\xd9\x89\0" /* offset 12922 */
+  "\xd8\xad\xd9\x8a\0" /* offset 12927 */
+  "\xd8\xac\xd9\x89\0" /* offset 12932 */
+  "\xd8\xac\xd9\x8a\0" /* offset 12937 */
+  "\xd8\xae\xd9\x89\0" /* offset 12942 */
+  "\xd8\xae\xd9\x8a\0" /* offset 12947 */
+  "\xd8\xb5\xd9\x89\0" /* offset 12952 */
+  "\xd8\xb5\xd9\x8a\0" /* offset 12957 */
+  "\xd8\xb6\xd9\x89\0" /* offset 12962 */
+  "\xd8\xb6\xd9\x8a\0" /* offset 12967 */
+  "\xd8\xb4\xd8\xac\0" /* offset 12972 */
+  "\xd8\xb4\xd8\xad\0" /* offset 12977 */
+  "\xd8\xb4\xd8\xae\0" /* offset 12982 */
+  "\xd8\xb4\xd8\xb1\0" /* offset 12987 */
+  "\xd8\xb3\xd8\xb1\0" /* offset 12992 */
+  "\xd8\xb5\xd8\xb1\0" /* offset 12997 */
+  "\xd8\xb6\xd8\xb1\0" /* offset 13002 */
+  "\xd8\xa7\xd9\x8b\0" /* offset 13007 */
+  "\xd8\xaa\xd8\xac\xd9\x85\0" /* offset 13012 */
+  "\xd8\xaa\xd8\xad\xd8\xac\0" /* offset 13019 */
+  "\xd8\xaa\xd8\xad\xd9\x85\0" /* offset 13026 */
+  "\xd8\xaa\xd8\xae\xd9\x85\0" /* offset 13033 */
+  "\xd8\xaa\xd9\x85\xd8\xac\0" /* offset 13040 */
+  "\xd8\xaa\xd9\x85\xd8\xad\0" /* offset 13047 */
+  "\xd8\xaa\xd9\x85\xd8\xae\0" /* offset 13054 */
+  "\xd8\xac\xd9\x85\xd8\xad\0" /* offset 13061 */
+  "\xd8\xad\xd9\x85\xd9\x8a\0" /* offset 13068 */
+  "\xd8\xad\xd9\x85\xd9\x89\0" /* offset 13075 */
+  "\xd8\xb3\xd8\xad\xd8\xac\0" /* offset 13082 */
+  "\xd8\xb3\xd8\xac\xd8\xad\0" /* offset 13089 */
+  "\xd8\xb3\xd8\xac\xd9\x89\0" /* offset 13096 */
+  "\xd8\xb3\xd9\x85\xd8\xad\0" /* offset 13103 */
+  "\xd8\xb3\xd9\x85\xd8\xac\0" /* offset 13110 */
+  "\xd8\xb3\xd9\x85\xd9\x85\0" /* offset 13117 */
+  "\xd8\xb5\xd8\xad\xd8\xad\0" /* offset 13124 */
+  "\xd8\xb5\xd9\x85\xd9\x85\0" /* offset 13131 */
+  "\xd8\xb4\xd8\xad\xd9\x85\0" /* offset 13138 */
+  "\xd8\xb4\xd8\xac\xd9\x8a\0" /* offset 13145 */
+  "\xd8\xb4\xd9\x85\xd8\xae\0" /* offset 13152 */
+  "\xd8\xb4\xd9\x85\xd9\x85\0" /* offset 13159 */
+  "\xd8\xb6\xd8\xad\xd9\x89\0" /* offset 13166 */
+  "\xd8\xb6\xd8\xae\xd9\x85\0" /* offset 13173 */
+  "\xd8\xb7\xd9\x85\xd8\xad\0" /* offset 13180 */
+  "\xd8\xb7\xd9\x85\xd9\x85\0" /* offset 13187 */
+  "\xd8\xb7\xd9\x85\xd9\x8a\0" /* offset 13194 */
+  "\xd8\xb9\xd8\xac\xd9\x85\0" /* offset 13201 */
+  "\xd8\xb9\xd9\x85\xd9\x85\0" /* offset 13208 */
+  "\xd8\xb9\xd9\x85\xd9\x89\0" /* offset 13215 */
+  "\xd8\xba\xd9\x85\xd9\x85\0" /* offset 13222 */
+  "\xd8\xba\xd9\x85\xd9\x8a\0" /* offset 13229 */
+  "\xd8\xba\xd9\x85\xd9\x89\0" /* offset 13236 */
+  "\xd9\x81\xd8\xae\xd9\x85\0" /* offset 13243 */
+  "\xd9\x82\xd9\x85\xd8\xad\0" /* offset 13250 */
+  "\xd9\x82\xd9\x85\xd9\x85\0" /* offset 13257 */
+  "\xd9\x84\xd8\xad\xd9\x85\0" /* offset 13264 */
+  "\xd9\x84\xd8\xad\xd9\x8a\0" /* offset 13271 */
+  "\xd9\x84\xd8\xad\xd9\x89\0" /* offset 13278 */
+  "\xd9\x84\xd8\xac\xd8\xac\0" /* offset 13285 */
+  "\xd9\x84\xd8\xae\xd9\x85\0" /* offset 13292 */
+  "\xd9\x84\xd9\x85\xd8\xad\0" /* offset 13299 */
+  "\xd9\x85\xd8\xad\xd8\xac\0" /* offset 13306 */
+  "\xd9\x85\xd8\xad\xd9\x85\0" /* offset 13313 */
+  "\xd9\x85\xd8\xad\xd9\x8a\0" /* offset 13320 */
+  "\xd9\x85\xd8\xac\xd8\xad\0" /* offset 13327 */
+  "\xd9\x85\xd8\xac\xd9\x85\0" /* offset 13334 */
+  "\xd9\x85\xd8\xae\xd8\xac\0" /* offset 13341 */
+  "\xd9\x85\xd8\xae\xd9\x85\0" /* offset 13348 */
+  "\xd9\x85\xd8\xac\xd8\xae\0" /* offset 13355 */
+  "\xd9\x87\xd9\x85\xd8\xac\0" /* offset 13362 */
+  "\xd9\x87\xd9\x85\xd9\x85\0" /* offset 13369 */
+  "\xd9\x86\xd8\xad\xd9\x85\0" /* offset 13376 */
+  "\xd9\x86\xd8\xad\xd9\x89\0" /* offset 13383 */
+  "\xd9\x86\xd8\xac\xd9\x85\0" /* offset 13390 */
+  "\xd9\x86\xd8\xac\xd9\x89\0" /* offset 13397 */
+  "\xd9\x86\xd9\x85\xd9\x8a\0" /* offset 13404 */
+  "\xd9\x86\xd9\x85\xd9\x89\0" /* offset 13411 */
+  "\xd9\x8a\xd9\x85\xd9\x85\0" /* offset 13418 */
+  "\xd8\xa8\xd8\xae\xd9\x8a\0" /* offset 13425 */
+  "\xd8\xaa\xd8\xac\xd9\x8a\0" /* offset 13432 */
+  "\xd8\xaa\xd8\xac\xd9\x89\0" /* offset 13439 */
+  "\xd8\xaa\xd8\xae\xd9\x8a\0" /* offset 13446 */
+  "\xd8\xaa\xd8\xae\xd9\x89\0" /* offset 13453 */
+  "\xd8\xaa\xd9\x85\xd9\x8a\0" /* offset 13460 */
+  "\xd8\xaa\xd9\x85\xd9\x89\0" /* offset 13467 */
+  "\xd8\xac\xd9\x85\xd9\x8a\0" /* offset 13474 */
+  "\xd8\xac\xd8\xad\xd9\x89\0" /* offset 13481 */
+  "\xd8\xac\xd9\x85\xd9\x89\0" /* offset 13488 */
+  "\xd8\xb3\xd8\xae\xd9\x89\0" /* offset 13495 */
+  "\xd8\xb5\xd8\xad\xd9\x8a\0" /* offset 13502 */
+  "\xd8\xb4\xd8\xad\xd9\x8a\0" /* offset 13509 */
+  "\xd8\xb6\xd8\xad\xd9\x8a\0" /* offset 13516 */
+  "\xd9\x84\xd8\xac\xd9\x8a\0" /* offset 13523 */
+  "\xd9\x84\xd9\x85\xd9\x8a\0" /* offset 13530 */
+  "\xd9\x8a\xd8\xad\xd9\x8a\0" /* offset 13537 */
+  "\xd9\x8a\xd8\xac\xd9\x8a\0" /* offset 13544 */
+  "\xd9\x8a\xd9\x85\xd9\x8a\0" /* offset 13551 */
+  "\xd9\x85\xd9\x85\xd9\x8a\0" /* offset 13558 */
+  "\xd9\x82\xd9\x85\xd9\x8a\0" /* offset 13565 */
+  "\xd9\x86\xd8\xad\xd9\x8a\0" /* offset 13572 */
+  "\xd8\xb9\xd9\x85\xd9\x8a\0" /* offset 13579 */
+  "\xd9\x83\xd9\x85\xd9\x8a\0" /* offset 13586 */
+  "\xd9\x86\xd8\xac\xd8\xad\0" /* offset 13593 */
+  "\xd9\x85\xd8\xae\xd9\x8a\0" /* offset 13600 */
+  "\xd9\x84\xd8\xac\xd9\x85\0" /* offset 13607 */
+  "\xd9\x83\xd9\x85\xd9\x85\0" /* offset 13614 */
+  "\xd8\xac\xd8\xad\xd9\x8a\0" /* offset 13621 */
+  "\xd8\xad\xd8\xac\xd9\x8a\0" /* offset 13628 */
+  "\xd9\x85\xd8\xac\xd9\x8a\0" /* offset 13635 */
+  "\xd9\x81\xd9\x85\xd9\x8a\0" /* offset 13642 */
+  "\xd8\xa8\xd8\xad\xd9\x8a\0" /* offset 13649 */
+  "\xd8\xb3\xd8\xae\xd9\x8a\0" /* offset 13656 */
+  "\xd9\x86\xd8\xac\xd9\x8a\0" /* offset 13663 */
+  "\xd8\xb5\xd9\x84\xdb\x92\0" /* offset 13670 */
+  "\xd9\x82\xd9\x84\xdb\x92\0" /* offset 13677 */
+  "\xd8\xa7\xd9\x84\xd9\x84\xd9\x87\0" /* offset 13684 */
+  "\xd8\xa7\xd9\x83\xd8\xa8\xd8\xb1\0" /* offset 13693 */
+  "\xd9\x85\xd8\xad\xd9\x85\xd8\xaf\0" /* offset 13702 */
+  "\xd8\xb5\xd9\x84\xd8\xb9\xd9\x85\0" /* offset 13711 */
+  "\xd8\xb1\xd8\xb3\xd9\x88\xd9\x84\0" /* offset 13720 */
+  "\xd8\xb9\xd9\x84\xd9\x8a\xd9\x87\0" /* offset 13729 */
+  "\xd9\x88\xd8\xb3\xd9\x84\xd9\x85\0" /* offset 13738 */
+  "\xd8\xb5\xd9\x84\xd9\x89\0" /* offset 13747 */
+  "\xd8\xb5\xd9\x84\xd9\x89\x20\xd8\xa7\xd9\x84\xd9\x84\xd9\x87\x20\xd8\xb9\xd9\x84\xd9\x8a\xd9\x87\x20\xd9\x88\xd8\xb3\xd9\x84\xd9\x85\0" /* offset 13754 */
+  "\xd8\xac\xd9\x84\x20\xd8\xac\xd9\x84\xd8\xa7\xd9\x84\xd9\x87\0" /* offset 13788 */
+  "\xd8\xb1\xdb\x8c\xd8\xa7\xd9\x84\0" /* offset 13804 */
+  "\xe2\x80\x94\0" /* offset 13813 */
+  "\xe2\x80\x93\0" /* offset 13817 */
+  "\x5f\0" /* offset 13821 */
+  "\x7b\0" /* offset 13823 */
+  "\x7d\0" /* offset 13825 */
+  "\xe3\x80\x94\0" /* offset 13827 */
+  "\xe3\x80\x95\0" /* offset 13831 */
+  "\xe3\x80\x90\0" /* offset 13835 */
+  "\xe3\x80\x91\0" /* offset 13839 */
+  "\xe3\x80\x8a\0" /* offset 13843 */
+  "\xe3\x80\x8b\0" /* offset 13847 */
+  "\xe3\x80\x8c\0" /* offset 13851 */
+  "\xe3\x80\x8d\0" /* offset 13855 */
+  "\xe3\x80\x8e\0" /* offset 13859 */
+  "\xe3\x80\x8f\0" /* offset 13863 */
+  "\x2c\0" /* offset 13867 */
+  "\xe3\x80\x81\0" /* offset 13869 */
+  "\x3a\0" /* offset 13873 */
+  "\x3f\0" /* offset 13875 */
+  "\x21\0" /* offset 13877 */
+  "\x23\0" /* offset 13879 */
+  "\x26\0" /* offset 13881 */
+  "\x2a\0" /* offset 13883 */
+  "\x2d\0" /* offset 13885 */
+  "\x3c\0" /* offset 13887 */
+  "\x3e\0" /* offset 13889 */
+  "\x5c\0" /* offset 13891 */
+  "\x24\0" /* offset 13893 */
+  "\x25\0" /* offset 13895 */
+  "\x40\0" /* offset 13897 */
+  "\x20\xd9\x8b\0" /* offset 13899 */
+  "\xd9\x80\xd9\x8b\0" /* offset 13903 */
+  "\x20\xd9\x8c\0" /* offset 13908 */
+  "\x20\xd9\x8d\0" /* offset 13912 */
+  "\x20\xd9\x8e\0" /* offset 13916 */
+  "\xd9\x80\xd9\x8e\0" /* offset 13920 */
+  "\x20\xd9\x8f\0" /* offset 13925 */
+  "\xd9\x80\xd9\x8f\0" /* offset 13929 */
+  "\x20\xd9\x90\0" /* offset 13934 */
+  "\xd9\x80\xd9\x90\0" /* offset 13938 */
+  "\x20\xd9\x91\0" /* offset 13943 */
+  "\xd9\x80\xd9\x91\0" /* offset 13947 */
+  "\x20\xd9\x92\0" /* offset 13952 */
+  "\xd9\x80\xd9\x92\0" /* offset 13956 */
+  "\xd8\xa1\0" /* offset 13961 */
+  "\xd8\xa7\0" /* offset 13964 */
+  "\xd8\xa8\0" /* offset 13967 */
+  "\xd8\xa9\0" /* offset 13970 */
+  "\xd8\xaa\0" /* offset 13973 */
+  "\xd8\xab\0" /* offset 13976 */
+  "\xd8\xac\0" /* offset 13979 */
+  "\xd8\xad\0" /* offset 13982 */
+  "\xd8\xae\0" /* offset 13985 */
+  "\xd8\xaf\0" /* offset 13988 */
+  "\xd8\xb0\0" /* offset 13991 */
+  "\xd8\xb1\0" /* offset 13994 */
+  "\xd8\xb2\0" /* offset 13997 */
+  "\xd8\xb3\0" /* offset 14000 */
+  "\xd8\xb4\0" /* offset 14003 */
+  "\xd8\xb5\0" /* offset 14006 */
+  "\xd8\xb6\0" /* offset 14009 */
+  "\xd8\xb7\0" /* offset 14012 */
+  "\xd8\xb8\0" /* offset 14015 */
+  "\xd8\xb9\0" /* offset 14018 */
+  "\xd8\xba\0" /* offset 14021 */
+  "\xd9\x81\0" /* offset 14024 */
+  "\xd9\x82\0" /* offset 14027 */
+  "\xd9\x83\0" /* offset 14030 */
+  "\xd9\x84\0" /* offset 14033 */
+  "\xd9\x85\0" /* offset 14036 */
+  "\xd9\x86\0" /* offset 14039 */
+  "\xd9\x87\0" /* offset 14042 */
+  "\xd9\x88\0" /* offset 14045 */
+  "\xd9\x8a\0" /* offset 14048 */
+  "\xd9\x84\xd8\xa7\xd9\x93\0" /* offset 14051 */
+  "\xd9\x84\xd8\xa7\xd9\x94\0" /* offset 14058 */
+  "\xd9\x84\xd8\xa7\xd9\x95\0" /* offset 14065 */
+  "\xd9\x84\xd8\xa7\0" /* offset 14072 */
+  "\x22\0" /* offset 14077 */
+  "\x27\0" /* offset 14079 */
+  "\x2f\0" /* offset 14081 */
+  "\x5b\0" /* offset 14083 */
+  "\x5d\0" /* offset 14085 */
+  "\x5e\0" /* offset 14087 */
+  "\x7c\0" /* offset 14089 */
+  "\x7e\0" /* offset 14091 */
+  "\xe2\xa6\x85\0" /* offset 14093 */
+  "\xe2\xa6\x86\0" /* offset 14097 */
+  "\xe3\x80\x82\0" /* offset 14101 */
+  "\xe3\x83\xbb\0" /* offset 14105 */
+  "\xe3\x82\xa1\0" /* offset 14109 */
+  "\xe3\x82\xa3\0" /* offset 14113 */
+  "\xe3\x82\xa5\0" /* offset 14117 */
+  "\xe3\x82\xa7\0" /* offset 14121 */
+  "\xe3\x82\xa9\0" /* offset 14125 */
+  "\xe3\x83\xa3\0" /* offset 14129 */
+  "\xe3\x83\xa5\0" /* offset 14133 */
+  "\xe3\x83\xa7\0" /* offset 14137 */
+  "\xe3\x83\x83\0" /* offset 14141 */
+  "\xe3\x83\xbc\0" /* offset 14145 */
+  "\xe3\x83\xb3\0" /* offset 14149 */
+  "\xe3\x82\x99\0" /* offset 14153 */
+  "\xe3\x82\x9a\0" /* offset 14157 */
+  "\xc2\xa2\0" /* offset 14161 */
+  "\xc2\xa3\0" /* offset 14164 */
+  "\xc2\xac\0" /* offset 14167 */
+  "\xc2\xa6\0" /* offset 14170 */
+  "\xc2\xa5\0" /* offset 14173 */
+  "\xe2\x82\xa9\0" /* offset 14176 */
+  "\xe2\x94\x82\0" /* offset 14180 */
+  "\xe2\x86\x90\0" /* offset 14184 */
+  "\xe2\x86\x91\0" /* offset 14188 */
+  "\xe2\x86\x92\0" /* offset 14192 */
+  "\xe2\x86\x93\0" /* offset 14196 */
+  "\xe2\x96\xa0\0" /* offset 14200 */
+  "\xe2\x97\x8b\0" /* offset 14204 */
+  "\xf0\x9d\x85\x97\xf0\x9d\x85\xa5\0" /* offset 14208 */
+  "\xf0\x9d\x85\x98\xf0\x9d\x85\xa5\0" /* offset 14217 */
+  "\xf0\x9d\x85\x98\xf0\x9d\x85\xa5\xf0\x9d\x85\xae\0" /* offset 14226 */
+  "\xf0\x9d\x85\x98\xf0\x9d\x85\xa5\xf0\x9d\x85\xaf\0" /* offset 14239 */
+  "\xf0\x9d\x85\x98\xf0\x9d\x85\xa5\xf0\x9d\x85\xb0\0" /* offset 14252 */
+  "\xf0\x9d\x85\x98\xf0\x9d\x85\xa5\xf0\x9d\x85\xb1\0" /* offset 14265 */
+  "\xf0\x9d\x85\x98\xf0\x9d\x85\xa5\xf0\x9d\x85\xb2\0" /* offset 14278 */
+  "\xf0\x9d\x86\xb9\xf0\x9d\x85\xa5\0" /* offset 14291 */
+  "\xf0\x9d\x86\xba\xf0\x9d\x85\xa5\0" /* offset 14300 */
+  "\xf0\x9d\x86\xb9\xf0\x9d\x85\xa5\xf0\x9d\x85\xae\0" /* offset 14309 */
+  "\xf0\x9d\x86\xba\xf0\x9d\x85\xa5\xf0\x9d\x85\xae\0" /* offset 14322 */
+  "\xf0\x9d\x86\xb9\xf0\x9d\x85\xa5\xf0\x9d\x85\xaf\0" /* offset 14335 */
+  "\xf0\x9d\x86\xba\xf0\x9d\x85\xa5\xf0\x9d\x85\xaf\0" /* offset 14348 */
+  "\xce\x91\0" /* offset 14361 */
+  "\xce\x92\0" /* offset 14364 */
+  "\xce\x94\0" /* offset 14367 */
+  "\xce\x95\0" /* offset 14370 */
+  "\xce\x96\0" /* offset 14373 */
+  "\xce\x97\0" /* offset 14376 */
+  "\xce\x99\0" /* offset 14379 */
+  "\xce\x9a\0" /* offset 14382 */
+  "\xce\x9b\0" /* offset 14385 */
+  "\xce\x9c\0" /* offset 14388 */
+  "\xce\x9d\0" /* offset 14391 */
+  "\xce\x9e\0" /* offset 14394 */
+  "\xce\x9f\0" /* offset 14397 */
+  "\xce\xa1\0" /* offset 14400 */
+  "\xce\xa3\0" /* offset 14403 */
+  "\xce\xa4\0" /* offset 14406 */
+  "\xce\xa6\0" /* offset 14409 */
+  "\xce\xa7\0" /* offset 14412 */
+  "\xce\xa8\0" /* offset 14415 */
+  "\xe2\x88\x87\0" /* offset 14418 */
+  "\xce\xb1\0" /* offset 14422 */
+  "\xce\xb4\0" /* offset 14425 */
+  "\xce\xb6\0" /* offset 14428 */
+  "\xce\xb7\0" /* offset 14431 */
+  "\xce\xbb\0" /* offset 14434 */
+  "\xce\xbd\0" /* offset 14437 */
+  "\xce\xbe\0" /* offset 14440 */
+  "\xce\xbf\0" /* offset 14443 */
+  "\xcf\x83\0" /* offset 14446 */
+  "\xcf\x84\0" /* offset 14449 */
+  "\xcf\x85\0" /* offset 14452 */
+  "\xcf\x87\0" /* offset 14455 */
+  "\xcf\x88\0" /* offset 14458 */
+  "\xcf\x89\0" /* offset 14461 */
+  "\xe2\x88\x82\0" /* offset 14464 */
+  "\xe4\xb8\xbd\0" /* offset 14468 */
+  "\xe4\xb8\xb8\0" /* offset 14472 */
+  "\xe4\xb9\x81\0" /* offset 14476 */
+  "\xf0\xa0\x84\xa2\0" /* offset 14480 */
+  "\xe4\xbd\xa0\0" /* offset 14485 */
+  "\xe4\xbe\xbb\0" /* offset 14489 */
+  "\xe5\x80\x82\0" /* offset 14493 */
+  "\xe5\x81\xba\0" /* offset 14497 */
+  "\xe5\x82\x99\0" /* offset 14501 */
+  "\xe5\x83\x8f\0" /* offset 14505 */
+  "\xe3\x92\x9e\0" /* offset 14509 */
+  "\xf0\xa0\x98\xba\0" /* offset 14513 */
+  "\xe5\x85\x94\0" /* offset 14518 */
+  "\xe5\x85\xa4\0" /* offset 14522 */
+  "\xe5\x85\xb7\0" /* offset 14526 */
+  "\xf0\xa0\x94\x9c\0" /* offset 14530 */
+  "\xe3\x92\xb9\0" /* offset 14535 */
+  "\xe5\x85\xa7\0" /* offset 14539 */
+  "\xe5\x86\x8d\0" /* offset 14543 */
+  "\xf0\xa0\x95\x8b\0" /* offset 14547 */
+  "\xe5\x86\x97\0" /* offset 14552 */
+  "\xe5\x86\xa4\0" /* offset 14556 */
+  "\xe4\xbb\x8c\0" /* offset 14560 */
+  "\xe5\x86\xac\0" /* offset 14564 */
+  "\xe5\x86\xb5\0" /* offset 14568 */
+  "\xf0\xa9\x87\x9f\0" /* offset 14572 */
+  "\xe5\x88\x83\0" /* offset 14577 */
+  "\xe3\x93\x9f\0" /* offset 14581 */
+  "\xe5\x88\xbb\0" /* offset 14585 */
+  "\xe5\x89\x86\0" /* offset 14589 */
+  "\xe5\x89\xb2\0" /* offset 14593 */
+  "\xe5\x89\xb7\0" /* offset 14597 */
+  "\xe3\x94\x95\0" /* offset 14601 */
+  "\xe5\x8b\x87\0" /* offset 14605 */
+  "\xe5\x8b\xba\0" /* offset 14609 */
+  "\xe5\x8c\x85\0" /* offset 14613 */
+  "\xe5\x8c\x86\0" /* offset 14617 */
+  "\xe5\x8d\x89\0" /* offset 14621 */
+  "\xe5\x8d\x9a\0" /* offset 14625 */
+  "\xe5\x8d\xb3\0" /* offset 14629 */
+  "\xe5\x8d\xbd\0" /* offset 14633 */
+  "\xe5\x8d\xbf\0" /* offset 14637 */
+  "\xf0\xa0\xa8\xac\0" /* offset 14641 */
+  "\xe7\x81\xb0\0" /* offset 14646 */
+  "\xe5\x8f\x8a\0" /* offset 14650 */
+  "\xe5\x8f\x9f\0" /* offset 14654 */
+  "\xf0\xa0\xad\xa3\0" /* offset 14658 */
+  "\xe5\x8f\xab\0" /* offset 14663 */
+  "\xe5\x8f\xb1\0" /* offset 14667 */
+  "\xe5\x90\x86\0" /* offset 14671 */
+  "\xe5\x92\x9e\0" /* offset 14675 */
+  "\xe5\x90\xb8\0" /* offset 14679 */
+  "\xe5\x91\x88\0" /* offset 14683 */
+  "\xe5\x91\xa8\0" /* offset 14687 */
+  "\xe5\x92\xa2\0" /* offset 14691 */
+  "\xe5\x93\xb6\0" /* offset 14695 */
+  "\xe5\x94\x90\0" /* offset 14699 */
+  "\xe5\x95\x93\0" /* offset 14703 */
+  "\xe5\x95\xa3\0" /* offset 14707 */
+  "\xe5\x96\x84\0" /* offset 14711 */
+  "\xe5\x96\x99\0" /* offset 14715 */
+  "\xe5\x96\xab\0" /* offset 14719 */
+  "\xe5\x96\xb3\0" /* offset 14723 */
+  "\xe5\x97\x82\0" /* offset 14727 */
+  "\xe5\x9c\x96\0" /* offset 14731 */
+  "\xe5\x9c\x97\0" /* offset 14735 */
+  "\xe5\x99\x91\0" /* offset 14739 */
+  "\xe5\x99\xb4\0" /* offset 14743 */
+  "\xe5\xa3\xae\0" /* offset 14747 */
+  "\xe5\x9f\x8e\0" /* offset 14751 */
+  "\xe5\x9f\xb4\0" /* offset 14755 */
+  "\xe5\xa0\x8d\0" /* offset 14759 */
+  "\xe5\x9e\x8b\0" /* offset 14763 */
+  "\xe5\xa0\xb2\0" /* offset 14767 */
+  "\xe5\xa0\xb1\0" /* offset 14771 */
+  "\xe5\xa2\xac\0" /* offset 14775 */
+  "\xf0\xa1\x93\xa4\0" /* offset 14779 */
+  "\xe5\xa3\xb2\0" /* offset 14784 */
+  "\xe5\xa3\xb7\0" /* offset 14788 */
+  "\xe5\xa4\x86\0" /* offset 14792 */
+  "\xe5\xa4\x9a\0" /* offset 14796 */
+  "\xe5\xa4\xa2\0" /* offset 14800 */
+  "\xe5\xa5\xa2\0" /* offset 14804 */
+  "\xf0\xa1\x9a\xa8\0" /* offset 14808 */
+  "\xf0\xa1\x9b\xaa\0" /* offset 14813 */
+  "\xe5\xa7\xac\0" /* offset 14818 */
+  "\xe5\xa8\x9b\0" /* offset 14822 */
+  "\xe5\xa8\xa7\0" /* offset 14826 */
+  "\xe5\xa7\x98\0" /* offset 14830 */
+  "\xe5\xa9\xa6\0" /* offset 14834 */
+  "\xe3\x9b\xae\0" /* offset 14838 */
+  "\xf0\xa1\x8d\xaa\0" /* offset 14842 */
+  "\xe5\xac\x88\0" /* offset 14847 */
+  "\xe5\xac\xbe\0" /* offset 14851 */
+  "\xf0\xa1\xa7\x88\0" /* offset 14855 */
+  "\xe5\xaf\x83\0" /* offset 14860 */
+  "\xe5\xaf\x98\0" /* offset 14864 */
+  "\xe5\xaf\xb3\0" /* offset 14868 */
+  "\xf0\xa1\xac\x98\0" /* offset 14872 */
+  "\xe5\xaf\xbf\0" /* offset 14877 */
+  "\xe5\xb0\x86\0" /* offset 14881 */
+  "\xe5\xbc\xb3\0" /* offset 14885 */
+  "\xe3\x9e\x81\0" /* offset 14889 */
+  "\xe5\xb1\xa0\0" /* offset 14893 */
+  "\xe5\xb3\x80\0" /* offset 14897 */
+  "\xe5\xb2\x8d\0" /* offset 14901 */
+  "\xf0\xa1\xb7\xa4\0" /* offset 14905 */
+  "\xe5\xb5\x83\0" /* offset 14910 */
+  "\xf0\xa1\xb7\xa6\0" /* offset 14914 */
+  "\xe5\xb5\xae\0" /* offset 14919 */
+  "\xe5\xb5\xab\0" /* offset 14923 */
+  "\xe5\xb5\xbc\0" /* offset 14927 */
+  "\xe5\xb7\xa1\0" /* offset 14931 */
+  "\xe5\xb7\xa2\0" /* offset 14935 */
+  "\xe3\xa0\xaf\0" /* offset 14939 */
+  "\xe5\xb7\xbd\0" /* offset 14943 */
+  "\xe5\xb8\xa8\0" /* offset 14947 */
+  "\xe5\xb8\xbd\0" /* offset 14951 */
+  "\xe5\xb9\xa9\0" /* offset 14955 */
+  "\xe3\xa1\xa2\0" /* offset 14959 */
+  "\xf0\xa2\x86\x83\0" /* offset 14963 */
+  "\xe3\xa1\xbc\0" /* offset 14968 */
+  "\xe5\xba\xb0\0" /* offset 14972 */
+  "\xe5\xba\xb3\0" /* offset 14976 */
+  "\xe5\xba\xb6\0" /* offset 14980 */
+  "\xf0\xaa\x8e\x92\0" /* offset 14984 */
+  "\xf0\xa2\x8c\xb1\0" /* offset 14989 */
+  "\xe8\x88\x81\0" /* offset 14994 */
+  "\xe5\xbc\xa2\0" /* offset 14998 */
+  "\xe3\xa3\x87\0" /* offset 15002 */
+  "\xf0\xa3\x8a\xb8\0" /* offset 15006 */
+  "\xf0\xa6\x87\x9a\0" /* offset 15011 */
+  "\xe5\xbd\xa2\0" /* offset 15016 */
+  "\xe5\xbd\xab\0" /* offset 15020 */
+  "\xe3\xa3\xa3\0" /* offset 15024 */
+  "\xe5\xbe\x9a\0" /* offset 15028 */
+  "\xe5\xbf\x8d\0" /* offset 15032 */
+  "\xe5\xbf\x97\0" /* offset 15036 */
+  "\xe5\xbf\xb9\0" /* offset 15040 */
+  "\xe6\x82\x81\0" /* offset 15044 */
+  "\xe3\xa4\xba\0" /* offset 15048 */
+  "\xe3\xa4\x9c\0" /* offset 15052 */
+  "\xf0\xa2\x9b\x94\0" /* offset 15056 */
+  "\xe6\x83\x87\0" /* offset 15061 */
+  "\xe6\x85\x88\0" /* offset 15065 */
+  "\xe6\x85\x8c\0" /* offset 15069 */
+  "\xe6\x85\x8e\0" /* offset 15073 */
+  "\xe6\x85\xba\0" /* offset 15077 */
+  "\xe6\x86\xb2\0" /* offset 15081 */
+  "\xe6\x86\xa4\0" /* offset 15085 */
+  "\xe6\x86\xaf\0" /* offset 15089 */
+  "\xe6\x87\x9e\0" /* offset 15093 */
+  "\xe6\x88\x90\0" /* offset 15097 */
+  "\xe6\x88\x9b\0" /* offset 15101 */
+  "\xe6\x89\x9d\0" /* offset 15105 */
+  "\xe6\x8a\xb1\0" /* offset 15109 */
+  "\xe6\x8b\x94\0" /* offset 15113 */
+  "\xe6\x8d\x90\0" /* offset 15117 */
+  "\xf0\xa2\xac\x8c\0" /* offset 15121 */
+  "\xe6\x8c\xbd\0" /* offset 15126 */
+  "\xe6\x8b\xbc\0" /* offset 15130 */
+  "\xe6\x8d\xa8\0" /* offset 15134 */
+  "\xe6\x8e\x83\0" /* offset 15138 */
+  "\xe6\x8f\xa4\0" /* offset 15142 */
+  "\xf0\xa2\xaf\xb1\0" /* offset 15146 */
+  "\xe6\x90\xa2\0" /* offset 15151 */
+  "\xe6\x8f\x85\0" /* offset 15155 */
+  "\xe6\x8e\xa9\0" /* offset 15159 */
+  "\xe3\xa8\xae\0" /* offset 15163 */
+  "\xe6\x91\xa9\0" /* offset 15167 */
+  "\xe6\x91\xbe\0" /* offset 15171 */
+  "\xe6\x92\x9d\0" /* offset 15175 */
+  "\xe6\x91\xb7\0" /* offset 15179 */
+  "\xe3\xa9\xac\0" /* offset 15183 */
+  "\xe6\x95\xac\0" /* offset 15187 */
+  "\xf0\xa3\x80\x8a\0" /* offset 15191 */
+  "\xe6\x97\xa3\0" /* offset 15196 */
+  "\xe6\x9b\xb8\0" /* offset 15200 */
+  "\xe6\x99\x89\0" /* offset 15204 */
+  "\xe3\xac\x99\0" /* offset 15208 */
+  "\xe3\xac\x88\0" /* offset 15212 */
+  "\xe3\xab\xa4\0" /* offset 15216 */
+  "\xe5\x86\x92\0" /* offset 15220 */
+  "\xe5\x86\x95\0" /* offset 15224 */
+  "\xe6\x9c\x80\0" /* offset 15228 */
+  "\xe6\x9a\x9c\0" /* offset 15232 */
+  "\xe8\x82\xad\0" /* offset 15236 */
+  "\xe4\x8f\x99\0" /* offset 15240 */
+  "\xe6\x9c\x9b\0" /* offset 15244 */
+  "\xe6\x9c\xa1\0" /* offset 15248 */
+  "\xe6\x9d\x9e\0" /* offset 15252 */
+  "\xe6\x9d\x93\0" /* offset 15256 */
+  "\xf0\xa3\x8f\x83\0" /* offset 15260 */
+  "\xe3\xad\x89\0" /* offset 15265 */
+  "\xe6\x9f\xba\0" /* offset 15269 */
+  "\xe6\x9e\x85\0" /* offset 15273 */
+  "\xe6\xa1\x92\0" /* offset 15277 */
+  "\xf0\xa3\x91\xad\0" /* offset 15281 */
+  "\xe6\xa2\x8e\0" /* offset 15286 */
+  "\xe6\xa0\x9f\0" /* offset 15290 */
+  "\xe6\xa4\x94\0" /* offset 15294 */
+  "\xe3\xae\x9d\0" /* offset 15298 */
+  "\xe6\xa5\x82\0" /* offset 15302 */
+  "\xe6\xa6\xa3\0" /* offset 15306 */
+  "\xe6\xa7\xaa\0" /* offset 15310 */
+  "\xe6\xaa\xa8\0" /* offset 15314 */
+  "\xf0\xa3\x9a\xa3\0" /* offset 15318 */
+  "\xe6\xab\x9b\0" /* offset 15323 */
+  "\xe3\xb0\x98\0" /* offset 15327 */
+  "\xe6\xac\xa1\0" /* offset 15331 */
+  "\xf0\xa3\xa2\xa7\0" /* offset 15335 */
+  "\xe6\xad\x94\0" /* offset 15340 */
+  "\xe3\xb1\x8e\0" /* offset 15344 */
+  "\xe6\xad\xb2\0" /* offset 15348 */
+  "\xe6\xae\x9f\0" /* offset 15352 */
+  "\xe6\xae\xbb\0" /* offset 15356 */
+  "\xf0\xa3\xaa\x8d\0" /* offset 15360 */
+  "\xf0\xa1\xb4\x8b\0" /* offset 15365 */
+  "\xf0\xa3\xab\xba\0" /* offset 15370 */
+  "\xe6\xb1\x8e\0" /* offset 15375 */
+  "\xf0\xa3\xb2\xbc\0" /* offset 15379 */
+  "\xe6\xb2\xbf\0" /* offset 15384 */
+  "\xe6\xb3\x8d\0" /* offset 15388 */
+  "\xe6\xb1\xa7\0" /* offset 15392 */
+  "\xe6\xb4\x96\0" /* offset 15396 */
+  "\xe6\xb4\xbe\0" /* offset 15400 */
+  "\xe6\xb5\xa9\0" /* offset 15404 */
+  "\xe6\xb5\xb8\0" /* offset 15408 */
+  "\xe6\xb6\x85\0" /* offset 15412 */
+  "\xf0\xa3\xb4\x9e\0" /* offset 15416 */
+  "\xe6\xb4\xb4\0" /* offset 15421 */
+  "\xe6\xb8\xaf\0" /* offset 15425 */
+  "\xe6\xb9\xae\0" /* offset 15429 */
+  "\xe3\xb4\xb3\0" /* offset 15433 */
+  "\xe6\xbb\x8b\0" /* offset 15437 */
+  "\xe6\xbb\x87\0" /* offset 15441 */
+  "\xf0\xa3\xbb\x91\0" /* offset 15445 */
+  "\xe6\xb7\xb9\0" /* offset 15450 */
+  "\xe6\xbd\xae\0" /* offset 15454 */
+  "\xf0\xa3\xbd\x9e\0" /* offset 15458 */
+  "\xf0\xa3\xbe\x8e\0" /* offset 15463 */
+  "\xe6\xbf\x86\0" /* offset 15468 */
+  "\xe7\x80\xb9\0" /* offset 15472 */
+  "\xe7\x80\x9e\0" /* offset 15476 */
+  "\xe7\x80\x9b\0" /* offset 15480 */
+  "\xe3\xb6\x96\0" /* offset 15484 */
+  "\xe7\x81\x8a\0" /* offset 15488 */
+  "\xe7\x81\xbd\0" /* offset 15492 */
+  "\xe7\x81\xb7\0" /* offset 15496 */
+  "\xe7\x82\xad\0" /* offset 15500 */
+  "\xf0\xa0\x94\xa5\0" /* offset 15504 */
+  "\xe7\x85\x85\0" /* offset 15509 */
+  "\xf0\xa4\x89\xa3\0" /* offset 15513 */
+  "\xe7\x86\x9c\0" /* offset 15518 */
+  "\xe4\x8e\xab\0" /* offset 15522 */
+  "\xe7\x88\xa8\0" /* offset 15526 */
+  "\xe7\x88\xb5\0" /* offset 15530 */
+  "\xe7\x89\x90\0" /* offset 15534 */
+  "\xf0\xa4\x98\x88\0" /* offset 15538 */
+  "\xe7\x8a\x80\0" /* offset 15543 */
+  "\xe7\x8a\x95\0" /* offset 15547 */
+  "\xf0\xa4\x9c\xb5\0" /* offset 15551 */
+  "\xf0\xa4\xa0\x94\0" /* offset 15556 */
+  "\xe7\x8d\xba\0" /* offset 15561 */
+  "\xe7\x8e\x8b\0" /* offset 15565 */
+  "\xe3\xba\xac\0" /* offset 15569 */
+  "\xe7\x8e\xa5\0" /* offset 15573 */
+  "\xe3\xba\xb8\0" /* offset 15577 */
+  "\xe7\x91\x87\0" /* offset 15581 */
+  "\xe7\x91\x9c\0" /* offset 15585 */
+  "\xe7\x91\xb1\0" /* offset 15589 */
+  "\xe7\x92\x85\0" /* offset 15593 */
+  "\xe7\x93\x8a\0" /* offset 15597 */
+  "\xe3\xbc\x9b\0" /* offset 15601 */
+  "\xe7\x94\xa4\0" /* offset 15605 */
+  "\xf0\xa4\xb0\xb6\0" /* offset 15609 */
+  "\xe7\x94\xbe\0" /* offset 15614 */
+  "\xf0\xa4\xb2\x92\0" /* offset 15618 */
+  "\xf0\xa2\x86\x9f\0" /* offset 15623 */
+  "\xe7\x98\x90\0" /* offset 15628 */
+  "\xf0\xa4\xbe\xa1\0" /* offset 15632 */
+  "\xf0\xa4\xbe\xb8\0" /* offset 15637 */
+  "\xf0\xa5\x81\x84\0" /* offset 15642 */
+  "\xe3\xbf\xbc\0" /* offset 15647 */
+  "\xe4\x80\x88\0" /* offset 15651 */
+  "\xe7\x9b\xb4\0" /* offset 15655 */
+  "\xf0\xa5\x83\xb3\0" /* offset 15659 */
+  "\xf0\xa5\x83\xb2\0" /* offset 15664 */
+  "\xf0\xa5\x84\x99\0" /* offset 15669 */
+  "\xf0\xa5\x84\xb3\0" /* offset 15674 */
+  "\xe7\x9c\x9e\0" /* offset 15679 */
+  "\xe7\x9c\x9f\0" /* offset 15683 */
+  "\xe7\x9d\x8a\0" /* offset 15687 */
+  "\xe4\x80\xb9\0" /* offset 15691 */
+  "\xe7\x9e\x8b\0" /* offset 15695 */
+  "\xe4\x81\x86\0" /* offset 15699 */
+  "\xe4\x82\x96\0" /* offset 15703 */
+  "\xf0\xa5\x90\x9d\0" /* offset 15707 */
+  "\xe7\xa1\x8e\0" /* offset 15712 */
+  "\xe7\xa3\x8c\0" /* offset 15716 */
+  "\xe4\x83\xa3\0" /* offset 15720 */
+  "\xf0\xa5\x98\xa6\0" /* offset 15724 */
+  "\xf0\xa5\x9a\x9a\0" /* offset 15729 */
+  "\xf0\xa5\x9b\x85\0" /* offset 15734 */
+  "\xe7\xa7\xab\0" /* offset 15739 */
+  "\xe4\x84\xaf\0" /* offset 15743 */
+  "\xe7\xa9\x8a\0" /* offset 15747 */
+  "\xe7\xa9\x8f\0" /* offset 15751 */
+  "\xf0\xa5\xa5\xbc\0" /* offset 15755 */
+  "\xf0\xa5\xaa\xa7\0" /* offset 15760 */
+  "\xe7\xaa\xae\0" /* offset 15765 */
+  "\xe4\x88\x82\0" /* offset 15769 */
+  "\xf0\xa5\xae\xab\0" /* offset 15773 */
+  "\xe7\xaf\x86\0" /* offset 15778 */
+  "\xe7\xaf\x89\0" /* offset 15782 */
+  "\xe4\x88\xa7\0" /* offset 15786 */
+  "\xf0\xa5\xb2\x80\0" /* offset 15790 */
+  "\xe7\xb3\x92\0" /* offset 15795 */
+  "\xe4\x8a\xa0\0" /* offset 15799 */
+  "\xe7\xb3\xa8\0" /* offset 15803 */
+  "\xe7\xb3\xa3\0" /* offset 15807 */
+  "\xe7\xb4\x80\0" /* offset 15811 */
+  "\xf0\xa5\xbe\x86\0" /* offset 15815 */
+  "\xe7\xb5\xa3\0" /* offset 15820 */
+  "\xe4\x8c\x81\0" /* offset 15824 */
+  "\xe7\xb7\x87\0" /* offset 15828 */
+  "\xe7\xb8\x82\0" /* offset 15832 */
+  "\xe7\xb9\x85\0" /* offset 15836 */
+  "\xe4\x8c\xb4\0" /* offset 15840 */
+  "\xf0\xa6\x88\xa8\0" /* offset 15844 */
+  "\xf0\xa6\x89\x87\0" /* offset 15849 */
+  "\xe4\x8d\x99\0" /* offset 15854 */
+  "\xf0\xa6\x8b\x99\0" /* offset 15858 */
+  "\xe7\xbd\xba\0" /* offset 15863 */
+  "\xf0\xa6\x8c\xbe\0" /* offset 15867 */
+  "\xe7\xbe\x95\0" /* offset 15872 */
+  "\xe7\xbf\xba\0" /* offset 15876 */
+  "\xf0\xa6\x93\x9a\0" /* offset 15880 */
+  "\xf0\xa6\x94\xa3\0" /* offset 15885 */
+  "\xe8\x81\xa0\0" /* offset 15890 */
+  "\xf0\xa6\x96\xa8\0" /* offset 15894 */
+  "\xe8\x81\xb0\0" /* offset 15899 */
+  "\xf0\xa3\x8d\x9f\0" /* offset 15903 */
+  "\xe4\x8f\x95\0" /* offset 15908 */
+  "\xe8\x82\xb2\0" /* offset 15912 */
+  "\xe8\x84\x83\0" /* offset 15916 */
+  "\xe4\x90\x8b\0" /* offset 15920 */
+  "\xe8\x84\xbe\0" /* offset 15924 */
+  "\xe5\xaa\xb5\0" /* offset 15928 */
+  "\xf0\xa6\x9e\xa7\0" /* offset 15932 */
+  "\xf0\xa6\x9e\xb5\0" /* offset 15937 */
+  "\xf0\xa3\x8e\x93\0" /* offset 15942 */
+  "\xf0\xa3\x8e\x9c\0" /* offset 15947 */
+  "\xe8\x88\x84\0" /* offset 15952 */
+  "\xe8\xbe\x9e\0" /* offset 15956 */
+  "\xe4\x91\xab\0" /* offset 15960 */
+  "\xe8\x8a\x91\0" /* offset 15964 */
+  "\xe8\x8a\x8b\0" /* offset 15968 */
+  "\xe8\x8a\x9d\0" /* offset 15972 */
+  "\xe5\x8a\xb3\0" /* offset 15976 */
+  "\xe8\x8a\xb1\0" /* offset 15980 */
+  "\xe8\x8a\xb3\0" /* offset 15984 */
+  "\xe8\x8a\xbd\0" /* offset 15988 */
+  "\xe8\x8b\xa6\0" /* offset 15992 */
+  "\xf0\xa6\xac\xbc\0" /* offset 15996 */
+  "\xe8\x8c\x9d\0" /* offset 16001 */
+  "\xe8\x8d\xa3\0" /* offset 16005 */
+  "\xe8\x8e\xad\0" /* offset 16009 */
+  "\xe8\x8c\xa3\0" /* offset 16013 */
+  "\xe8\x8e\xbd\0" /* offset 16017 */
+  "\xe8\x8f\xa7\0" /* offset 16021 */
+  "\xe8\x8d\x93\0" /* offset 16025 */
+  "\xe8\x8f\x8a\0" /* offset 16029 */
+  "\xe8\x8f\x8c\0" /* offset 16033 */
+  "\xe8\x8f\x9c\0" /* offset 16037 */
+  "\xf0\xa6\xb0\xb6\0" /* offset 16041 */
+  "\xf0\xa6\xb5\xab\0" /* offset 16046 */
+  "\xf0\xa6\xb3\x95\0" /* offset 16051 */
+  "\xe4\x94\xab\0" /* offset 16056 */
+  "\xe8\x93\xb1\0" /* offset 16060 */
+  "\xe8\x93\xb3\0" /* offset 16064 */
+  "\xe8\x94\x96\0" /* offset 16068 */
+  "\xf0\xa7\x8f\x8a\0" /* offset 16072 */
+  "\xe8\x95\xa4\0" /* offset 16077 */
+  "\xf0\xa6\xbc\xac\0" /* offset 16081 */
+  "\xe4\x95\x9d\0" /* offset 16086 */
+  "\xe4\x95\xa1\0" /* offset 16090 */
+  "\xf0\xa6\xbe\xb1\0" /* offset 16094 */
+  "\xf0\xa7\x83\x92\0" /* offset 16099 */
+  "\xe4\x95\xab\0" /* offset 16104 */
+  "\xe8\x99\x90\0" /* offset 16108 */
+  "\xe8\x99\xa7\0" /* offset 16112 */
+  "\xe8\x99\xa9\0" /* offset 16116 */
+  "\xe8\x9a\xa9\0" /* offset 16120 */
+  "\xe8\x9a\x88\0" /* offset 16124 */
+  "\xe8\x9c\x8e\0" /* offset 16128 */
+  "\xe8\x9b\xa2\0" /* offset 16132 */
+  "\xe8\x9d\xb9\0" /* offset 16136 */
+  "\xe8\x9c\xa8\0" /* offset 16140 */
+  "\xe8\x9d\xab\0" /* offset 16144 */
+  "\xe8\x9e\x86\0" /* offset 16148 */
+  "\xe4\xb5\x97\0" /* offset 16152 */
+  "\xe8\x9f\xa1\0" /* offset 16156 */
+  "\xe8\xa0\x81\0" /* offset 16160 */
+  "\xe4\x97\xb9\0" /* offset 16164 */
+  "\xe8\xa1\xa0\0" /* offset 16168 */
+  "\xf0\xa7\x99\xa7\0" /* offset 16172 */
+  "\xe8\xa3\x97\0" /* offset 16177 */
+  "\xe8\xa3\x9e\0" /* offset 16181 */
+  "\xe4\x98\xb5\0" /* offset 16185 */
+  "\xe8\xa3\xba\0" /* offset 16189 */
+  "\xe3\x92\xbb\0" /* offset 16193 */
+  "\xf0\xa7\xa2\xae\0" /* offset 16197 */
+  "\xf0\xa7\xa5\xa6\0" /* offset 16202 */
+  "\xe4\x9a\xbe\0" /* offset 16207 */
+  "\xe4\x9b\x87\0" /* offset 16211 */
+  "\xe8\xaa\xa0\0" /* offset 16215 */
+  "\xe8\xab\xad\0" /* offset 16219 */
+  "\xe8\xae\x8a\0" /* offset 16223 */
+  "\xf0\xa7\xb2\xa8\0" /* offset 16227 */
+  "\xe8\xb2\xab\0" /* offset 16232 */
+  "\xe8\xb3\x81\0" /* offset 16236 */
+  "\xe8\xb4\x9b\0" /* offset 16240 */
+  "\xe8\xb5\xb7\0" /* offset 16244 */
+  "\xf0\xa7\xbc\xaf\0" /* offset 16248 */
+  "\xf0\xa0\xa0\x84\0" /* offset 16253 */
+  "\xe8\xb7\x8b\0" /* offset 16258 */
+  "\xe8\xb6\xbc\0" /* offset 16262 */
+  "\xe8\xb7\xb0\0" /* offset 16266 */
+  "\xf0\xa0\xa3\x9e\0" /* offset 16270 */
+  "\xe8\xbb\x94\0" /* offset 16275 */
+  "\xe8\xbc\xb8\0" /* offset 16279 */
+  "\xf0\xa8\x97\x92\0" /* offset 16283 */
+  "\xf0\xa8\x97\xad\0" /* offset 16288 */
+  "\xe9\x82\x94\0" /* offset 16293 */
+  "\xe9\x83\xb1\0" /* offset 16297 */
+  "\xe9\x84\x91\0" /* offset 16301 */
+  "\xf0\xa8\x9c\xae\0" /* offset 16305 */
+  "\xe9\x84\x9b\0" /* offset 16310 */
+  "\xe9\x88\xb8\0" /* offset 16314 */
+  "\xe9\x8b\x97\0" /* offset 16318 */
+  "\xe9\x8b\x98\0" /* offset 16322 */
+  "\xe9\x89\xbc\0" /* offset 16326 */
+  "\xe9\x8f\xb9\0" /* offset 16330 */
+  "\xe9\x90\x95\0" /* offset 16334 */
+  "\xf0\xa8\xaf\xba\0" /* offset 16338 */
+  "\xe9\x96\x8b\0" /* offset 16343 */
+  "\xe4\xa6\x95\0" /* offset 16347 */
+  "\xe9\x96\xb7\0" /* offset 16351 */
+  "\xf0\xa8\xb5\xb7\0" /* offset 16355 */
+  "\xe4\xa7\xa6\0" /* offset 16360 */
+  "\xe9\x9b\x83\0" /* offset 16364 */
+  "\xe5\xb6\xb2\0" /* offset 16368 */
+  "\xe9\x9c\xa3\0" /* offset 16372 */
+  "\xf0\xa9\x85\x85\0" /* offset 16376 */
+  "\xf0\xa9\x88\x9a\0" /* offset 16381 */
+  "\xe4\xa9\xae\0" /* offset 16386 */
+  "\xe4\xa9\xb6\0" /* offset 16390 */
+  "\xe9\x9f\xa0\0" /* offset 16394 */
+  "\xf0\xa9\x90\x8a\0" /* offset 16398 */
+  "\xe4\xaa\xb2\0" /* offset 16403 */
+  "\xf0\xa9\x92\x96\0" /* offset 16407 */
+  "\xe9\xa0\x8b\0" /* offset 16412 */
+  "\xe9\xa0\xa9\0" /* offset 16416 */
+  "\xf0\xa9\x96\xb6\0" /* offset 16420 */
+  "\xe9\xa3\xa2\0" /* offset 16425 */
+  "\xe4\xac\xb3\0" /* offset 16429 */
+  "\xe9\xa4\xa9\0" /* offset 16433 */
+  "\xe9\xa6\xa7\0" /* offset 16437 */
+  "\xe9\xa7\x82\0" /* offset 16441 */
+  "\xe9\xa7\xbe\0" /* offset 16445 */
+  "\xe4\xaf\x8e\0" /* offset 16449 */
+  "\xf0\xa9\xac\xb0\0" /* offset 16453 */
+  "\xe9\xac\x92\0" /* offset 16458 */
+  "\xe9\xb1\x80\0" /* offset 16462 */
+  "\xe9\xb3\xbd\0" /* offset 16466 */
+  "\xe4\xb3\x8e\0" /* offset 16470 */
+  "\xe4\xb3\xad\0" /* offset 16474 */
+  "\xe9\xb5\xa7\0" /* offset 16478 */
+  "\xf0\xaa\x83\x8e\0" /* offset 16482 */
+  "\xe4\xb3\xb8\0" /* offset 16487 */
+  "\xf0\xaa\x84\x85\0" /* offset 16491 */
+  "\xf0\xaa\x88\x8e\0" /* offset 16496 */
+  "\xf0\xaa\x8a\x91\0" /* offset 16501 */
+  "\xe4\xb5\x96\0" /* offset 16506 */
+  "\xe9\xbb\xbe\0" /* offset 16510 */
+  "\xe9\xbc\x85\0" /* offset 16514 */
+  "\xe9\xbc\x8f\0" /* offset 16518 */
+  "\xe9\xbc\x96\0" /* offset 16522 */
+  "\xf0\xaa\x98\x80\0" /* offset 16526 */;
+
+#endif /* DECOMP_H */
Index: psi/tags/0.10-os2-b1/iris/libidn/idn-int.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/idn-int.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/idn-int.h	(revision 38)
@@ -0,0 +1,293 @@
+#ifndef _LIBIDN_LIB_IDN_INT_H
+#define _LIBIDN_LIB_IDN_INT_H 1
+#ifndef _GENERATED_STDINT_H
+#define _GENERATED_STDINT_H "libidn 0.3.1"
+/* generated using gnu compiler gcc (GCC) 3.2.3 20030422 (Gentoo Linux 1.4 3.2.3-r1, propolice) */
+#define _STDINT_HAVE_STDINT_H 1
+
+/* ................... shortcircuit part ........................... */
+
+#if defined HAVE_STDINT_H || defined _STDINT_HAVE_STDINT_H
+
+//#include<qglobal.h>
+//#ifdef Q_OS_WIN32
+#include "qint.h"
+//#else
+//#include <stdint.h>
+//#endif
+
+#else
+#include <stddef.h>
+
+/* .................... configured part ............................ */
+
+/* whether we have a C99 compatible stdint header file */
+/* #undef _STDINT_HEADER_INTPTR */
+/* whether we have a C96 compatible inttypes header file */
+/* #undef _STDINT_HEADER_UINT32 */
+/* whether we have a BSD compatible inet types header */
+/* #undef _STDINT_HEADER_U_INT32 */
+
+/* which 64bit typedef has been found */
+/* #undef _STDINT_HAVE_UINT64_T */
+/* #undef _STDINT_HAVE_U_INT64_T */
+
+/* which type model has been detected */
+/* #undef _STDINT_CHAR_MODEL // skipped */
+/* #undef _STDINT_LONG_MODEL // skipped */
+
+/* whether int_least types were detected */
+/* #undef _STDINT_HAVE_INT_LEAST32_T */
+/* whether int_fast types were detected */
+/* #undef _STDINT_HAVE_INT_FAST32_T */
+/* whether intmax_t type was detected */
+/* #undef _STDINT_HAVE_INTMAX_T */
+
+/* .................... detections part ............................ */
+
+/* whether we need to define bitspecific types from compiler base types */
+#ifndef _STDINT_HEADER_INTPTR
+#ifndef _STDINT_HEADER_UINT32
+#ifndef _STDINT_HEADER_U_INT32
+#define _STDINT_NEED_INT_MODEL_T
+#else
+#define _STDINT_HAVE_U_INT_TYPES
+#endif
+#endif
+#endif
+
+#ifdef _STDINT_HAVE_U_INT_TYPES
+#undef _STDINT_NEED_INT_MODEL_T
+#endif
+
+#ifdef  _STDINT_CHAR_MODEL
+#if     _STDINT_CHAR_MODEL+0 == 122 || _STDINT_CHAR_MODEL+0 == 124
+#ifndef _STDINT_BYTE_MODEL
+#define _STDINT_BYTE_MODEL 12
+#endif
+#endif
+#endif
+
+#ifndef _STDINT_HAVE_INT_LEAST32_T
+#define _STDINT_NEED_INT_LEAST_T
+#endif
+
+#ifndef _STDINT_HAVE_INT_FAST32_T
+#define _STDINT_NEED_INT_FAST_T
+#endif
+
+#ifndef _STDINT_HEADER_INTPTR
+#define _STDINT_NEED_INTPTR_T
+#ifndef _STDINT_HAVE_INTMAX_T
+#define _STDINT_NEED_INTMAX_T
+#endif
+#endif
+
+
+/* .................... definition part ............................ */
+
+/* some system headers have good uint64_t */
+#ifndef _HAVE_UINT64_T
+#if     defined _STDINT_HAVE_UINT64_T  || defined HAVE_UINT64_T
+#define _HAVE_UINT64_T
+#elif   defined _STDINT_HAVE_U_INT64_T || defined HAVE_U_INT64_T
+#define _HAVE_UINT64_T
+typedef u_int64_t uint64_t;
+#endif
+#endif
+
+#ifndef _HAVE_UINT64_T
+/* .. here are some common heuristics using compiler runtime specifics */
+#if defined __STDC_VERSION__ && defined __STDC_VERSION__ >= 199901L
+#define _HAVE_UINT64_T
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+
+#elif !defined __STRICT_ANSI__
+#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__
+#define _HAVE_UINT64_T
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__
+/* note: all ELF-systems seem to have loff-support which needs 64-bit */
+#if !defined _NO_LONGLONG
+#define _HAVE_UINT64_T
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+#endif
+
+#elif defined __alpha || (defined __mips && defined _ABIN32)
+#if !defined _NO_LONGLONG
+typedef long int64_t;
+typedef unsigned long uint64_t;
+#endif
+  /* compiler/cpu type to define int64_t */
+#endif
+#endif
+#endif
+
+#if defined _STDINT_HAVE_U_INT_TYPES
+/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */
+typedef u_int8_t uint8_t;
+typedef u_int16_t uint16_t;
+typedef u_int32_t uint32_t;
+
+/* glibc compatibility */
+#ifndef __int8_t_defined
+#define __int8_t_defined
+#endif
+#endif
+
+#ifdef _STDINT_NEED_INT_MODEL_T
+/* we must guess all the basic types. Apart from byte-adressable system, */
+/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */
+/* (btw, those nibble-addressable systems are way off, or so we assume) */
+
+
+#if defined _STDINT_BYTE_MODEL
+#if _STDINT_LONG_MODEL+0 == 242
+/* 2:4:2 =  IP16 = a normal 16-bit system                */
+typedef unsigned char   uint8_t;
+typedef unsigned short  uint16_t;
+typedef unsigned long   uint32_t;
+#ifndef __int8_t_defined
+#define __int8_t_defined
+typedef          char    int8_t;
+typedef          short   int16_t;
+typedef          long    int32_t;
+#endif
+#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444
+/* 2:4:4 =  LP32 = a 32-bit system derived from a 16-bit */
+/* 4:4:4 = ILP32 = a normal 32-bit system                */
+typedef unsigned char   uint8_t;
+typedef unsigned short  uint16_t;
+typedef unsigned int    uint32_t;
+#ifndef __int8_t_defined
+#define __int8_t_defined
+typedef          char    int8_t;
+typedef          short   int16_t;
+typedef          int     int32_t;
+#endif
+#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488
+/* 4:8:4 =  IP32 = a 32-bit system prepared for 64-bit    */
+/* 4:8:8 =  LP64 = a normal 64-bit system                 */
+typedef unsigned char   uint8_t;
+typedef unsigned short  uint16_t;
+typedef unsigned int    uint32_t;
+#ifndef __int8_t_defined
+#define __int8_t_defined
+typedef          char    int8_t;
+typedef          short   int16_t;
+typedef          int     int32_t;
+#endif
+/* this system has a "long" of 64bit */
+#ifndef _HAVE_UINT64_T
+#define _HAVE_UINT64_T
+typedef unsigned long   uint64_t;
+typedef          long    int64_t;
+#endif
+#elif _STDINT_LONG_MODEL+0 == 448
+/*      LLP64   a 64-bit system derived from a 32-bit system */
+typedef unsigned char   uint8_t;
+typedef unsigned short  uint16_t;
+typedef unsigned int    uint32_t;
+#ifndef __int8_t_defined
+#define __int8_t_defined
+typedef          char    int8_t;
+typedef          short   int16_t;
+typedef          int     int32_t;
+#endif
+/* assuming the system has a "long long" */
+#ifndef _HAVE_UINT64_T
+#define _HAVE_UINT64_T
+typedef unsigned long long uint64_t;
+typedef          long long  int64_t;
+#endif
+#else
+#define _STDINT_NO_INT32_T
+#endif
+#else
+#define _STDINT_NO_INT8_T
+#define _STDINT_NO_INT32_T
+#endif
+#endif
+
+/*
+ * quote from SunOS-5.8 sys/inttypes.h:
+ * Use at your own risk.  As of February 1996, the committee is squarely
+ * behind the fixed sized types; the "least" and "fast" types are still being
+ * discussed.  The probability that the "fast" types may be removed before
+ * the standard is finalized is high enough that they are not currently
+ * implemented.
+ */
+
+#if defined _STDINT_NEED_INT_LEAST_T
+typedef  int8_t    int_least8_t;
+typedef  int16_t   int_least16_t;
+typedef  int32_t   int_least32_t;
+#ifdef _HAVE_UINT64_T
+typedef  int64_t   int_least64_t;
+#endif
+
+typedef uint8_t   uint_least8_t;
+typedef uint16_t  uint_least16_t;
+typedef uint32_t  uint_least32_t;
+#ifdef _HAVE_UINT64_T
+typedef uint64_t  uint_least64_t;
+#endif
+  /* least types */
+#endif
+
+#if defined _STDINT_NEED_INT_FAST_T
+typedef  int8_t    int_fast8_t;
+typedef  int       int_fast16_t;
+typedef  int32_t   int_fast32_t;
+#ifdef _HAVE_UINT64_T
+typedef  int64_t   int_fast64_t;
+#endif
+
+typedef uint8_t   uint_fast8_t;
+typedef unsigned  uint_fast16_t;
+typedef uint32_t  uint_fast32_t;
+#ifdef _HAVE_UINT64_T
+typedef uint64_t  uint_fast64_t;
+#endif
+  /* fast types */
+#endif
+
+#ifdef _STDINT_NEED_INTMAX_T
+#ifdef _HAVE_UINT64_T
+typedef  int64_t       intmax_t;
+typedef uint64_t      uintmax_t;
+#else
+typedef          long  intmax_t;
+typedef unsigned long uintmax_t;
+#endif
+#endif
+
+#ifdef _STDINT_NEED_INTPTR_T
+#ifndef __intptr_t_defined
+#define __intptr_t_defined
+/* we encourage using "long" to store pointer values, never use "int" ! */
+#if   _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484
+typedef  unsinged int   uintptr_t;
+typedef           int    intptr_t;
+#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444
+typedef  unsigned long  uintptr_t;
+typedef           long   intptr_t;
+#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T
+typedef        uint64_t uintptr_t;
+typedef         int64_t  intptr_t;
+#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */
+typedef  unsigned long  uintptr_t;
+typedef           long   intptr_t;
+#endif
+#endif
+#endif
+
+  /* shortcircuit*/
+#endif
+  /* once */
+#endif
+#endif
Index: psi/tags/0.10-os2-b1/iris/libidn/idna.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/idna.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/idna.h	(revision 38)
@@ -0,0 +1,95 @@
+/* idna.h	Declarations for IDNA.
+ * Copyright (C) 2002, 2003  Simon Josefsson
+ *
+ * This file is part of GNU Libidn.
+ *
+ * GNU Libidn is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GNU Libidn 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU Libidn; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _IDNA_H
+#define _IDNA_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>		/* size_t */
+#include <idn-int.h>		/* my_uint32_t */
+
+  /* Error codes. */
+  typedef enum
+  {
+    IDNA_SUCCESS = 0,
+    IDNA_STRINGPREP_ERROR = 1,
+    IDNA_PUNYCODE_ERROR = 2,
+    IDNA_CONTAINS_LDH = 3,
+    IDNA_CONTAINS_MINUS = 4,
+    IDNA_INVALID_LENGTH = 5,
+    IDNA_NO_ACE_PREFIX = 6,
+    IDNA_ROUNDTRIP_VERIFY_ERROR = 7,
+    IDNA_CONTAINS_ACE_PREFIX = 8,
+    IDNA_ICONV_ERROR = 9,
+    /* Internal errors. */
+    IDNA_MALLOC_ERROR = 201
+  } Idna_rc;
+
+  /* IDNA flags */
+  typedef enum
+  {
+    IDNA_ALLOW_UNASSIGNED = 0x0001,
+    IDNA_USE_STD3_ASCII_RULES = 0x0002
+  } Idna_flags;
+
+#ifndef IDNA_ACE_PREFIX
+#define IDNA_ACE_PREFIX "xn--"
+#endif
+
+  /* Core functions */
+  extern int idna_to_ascii_4i (const my_uint32_t * in, size_t inlen,
+			       char *out, int flags);
+  extern int idna_to_unicode_44i (const my_uint32_t * in, size_t inlen,
+				  my_uint32_t * out, size_t * outlen, int flags);
+
+  /* Wrappers that handle several labels */
+
+  extern int idna_to_ascii_4z (const my_uint32_t * input,
+			       char **output, int flags);
+
+  extern int idna_to_ascii_8z (const char *input, char **output, int flags);
+
+  extern int idna_to_ascii_lz (const char *input, char **output, int flags);
+
+
+  extern int idna_to_unicode_4z4z (const my_uint32_t * input,
+				   my_uint32_t ** output, int flags);
+
+  extern int idna_to_unicode_8z4z (const char *input,
+				   my_uint32_t ** output, int flags);
+
+  extern int idna_to_unicode_8z8z (const char *input,
+				   char **output, int flags);
+
+  extern int idna_to_unicode_8zlz (const char *input,
+				   char **output, int flags);
+
+  extern int idna_to_unicode_lzlz (const char *input,
+				   char **output, int flags);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* _PUNYCODE_H */
Index: psi/tags/0.10-os2-b1/iris/libidn/internal.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/internal.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/internal.h	(revision 38)
@@ -0,0 +1,133 @@
+/* internal.h	Internal header file for libidn.
+ * Copyright (C) 2002, 2003  Simon Josefsson
+ *
+ * This file is part of GNU Libidn.
+ *
+ * GNU Libidn is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GNU Libidn 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU Libidn; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _INTERNAL_H
+#define _INTERNAL_H
+
+#define STDC_HEADERS 1
+
+#ifdef _LIBC
+
+# include <stdio.h>
+# include <stdlib.h>
+# include <string.h>
+# include <errno.h>
+# include <string.h>
+
+//# define HAVE_ICONV 1
+//# define LOCALE_WORKS 1
+//# define ICONV_CONST
+
+#else /* _LIBC */
+
+# if HAVE_CONFIG_H
+#  include "config.h"
+# endif
+
+# if STDC_HEADERS
+#  include <stdio.h>
+#  include <stdlib.h>
+#  include <stdarg.h>
+#  include <ctype.h>
+#  include <string.h>
+# endif
+
+# if HAVE_UNISTD_H
+#  include <unistd.h>
+# endif
+
+# if HAVE_ERRNO_H
+#  include <errno.h>
+# endif
+
+# if defined(WITH_DMALLOC) && WITH_DMALLOC
+#  include <dmalloc.h>
+# endif
+
+#endif /* _LIBC */
+
+#include "stringprep.h"
+#include "punycode.h"
+#include "idna.h"
+
+/*! \mainpage GNU Internationalized Domain Name Library
+ *
+ * \section intro Introduction
+ *
+ * GNU Libidn is an implementation of the Stringprep, Punycode and IDNA
+ * specifications defined by the IETF Internationalized Domain Names
+ * (IDN) working group, used for internationalized domain names.  The
+ * package is available under the GNU Lesser General Public License.
+ *
+ * The library contains a generic Stringprep implementation that does
+ * Unicode 3.2 NFKC normalization, mapping and prohibitation of
+ * characters, and bidirectional character handling.  Profiles for iSCSI,
+ * Kerberos 5, Nameprep, SASL and XMPP are included.  Punycode and ASCII
+ * Compatible Encoding (ACE) via IDNA are supported.
+ *
+ * The Stringprep API consists of two main functions, one for converting
+ * data from the system's native representation into UTF-8, and one
+ * function to perform the Stringprep processing.  Adding a new
+ * Stringprep profile for your application within the API is
+ * straightforward.  The Punycode API consists of one encoding function
+ * and one decoding function.  The IDNA API consists of the ToASCII and
+ * ToUnicode functions, as well as an high-level interface for converting
+ * entire domain names to and from the ACE encoded form.
+ *
+ * The library is used by, e.g., GNU SASL and Shishi to process user
+ * names and passwords.  Libidn can be built into GNU Libc to enable a
+ * new system-wide getaddrinfo() flag for IDN processing.
+ *
+ * Libidn is developed for the GNU/Linux system, but runs on over 20 Unix
+ * platforms (including Solaris, IRIX, AIX, and Tru64) and Windows.
+ * Libidn is written in C and (parts of) the API is accessible from C,
+ * C++, Emacs Lisp, Python and Java.
+ *
+ * The project web page:\n
+ * http://www.gnu.org/software/libidn/
+ *
+ * The software archive:\n
+ * ftp://alpha.gnu.org/pub/gnu/libidn/
+ *
+ * For more information see:\n
+ * http://www.ietf.org/html.charters/idn-charter.html\n
+ * http://www.ietf.org/rfc/rfc3454.txt (stringprep specification)\n
+ * http://www.ietf.org/rfc/rfc3490.txt (idna specification)\n
+ * http://www.ietf.org/rfc/rfc3491.txt (nameprep specification)\n
+ * http://www.ietf.org/rfc/rfc3492.txt (punycode specification)\n
+ * http://www.ietf.org/internet-drafts/draft-ietf-ips-iscsi-string-prep-04.txt\n
+ * http://www.ietf.org/internet-drafts/draft-ietf-krb-wg-utf8-profile-01.txt\n
+ * http://www.ietf.org/internet-drafts/draft-ietf-sasl-anon-00.txt\n
+ * http://www.ietf.org/internet-drafts/draft-ietf-sasl-saslprep-00.txt\n
+ * http://www.ietf.org/internet-drafts/draft-ietf-xmpp-nodeprep-01.txt\n
+ * http://www.ietf.org/internet-drafts/draft-ietf-xmpp-resourceprep-01.txt\n
+ *
+ * Further information and paid contract development:\n
+ * Simon Josefsson <simon@josefsson.org>
+ *
+ * \section examples Examples
+ *
+ * \include example.c
+ * \include example3.c
+ * \include example4.c
+ */
+
+#endif /* _INTERNAL_H */
Index: psi/tags/0.10-os2-b1/iris/libidn/nfkc.c
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/nfkc.c	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/nfkc.c	(revision 38)
@@ -0,0 +1,1047 @@
+/* nfkc.c	Unicode normalization utilities.
+ * Copyright (C) 2002, 2003  Simon Josefsson
+ *
+ * This file is part of GNU Libidn.
+ *
+ * GNU Libidn is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GNU Libidn 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU Libidn; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "internal.h"
+
+/* This file contains functions from GLIB, including gutf8.c and
+ * gunidecomp.c, all licensed under LGPL and copyright hold by:
+ *
+ *  Copyright (C) 1999, 2000 Tom Tromey
+ *  Copyright 2000 Red Hat, Inc.
+ */
+
+/* Hacks to make syncing with GLIB code easier. */
+#define gboolean int
+#define gchar char
+#define guchar unsigned char
+#define glong long
+#define gint int
+#define guint unsigned int
+#define gushort unsigned short
+#define gint16 my_int16_t
+#define guint16 my_uint16_t
+#define gunichar my_uint32_t
+#define gsize size_t
+#define gssize ssize_t
+#define g_malloc malloc
+#define g_free free
+#define GError void
+#define g_set_error(a,b,c,d) 0
+#define g_new(struct_type, n_structs)					\
+  ((struct_type *) g_malloc (((gsize) sizeof (struct_type)) * ((gsize) (n_structs))))
+#  if defined (__GNUC__) && !defined (__STRICT_ANSI__) && !defined (__cplusplus)
+#    define G_STMT_START	(void)(
+#    define G_STMT_END		)
+#  else
+#    if (defined (sun) || defined (__sun__))
+#      define G_STMT_START	if (1)
+#      define G_STMT_END	else (void)0
+#    else
+#      define G_STMT_START	do
+#      define G_STMT_END	while (0)
+#    endif
+#  endif
+#define g_return_val_if_fail(expr,val)		G_STMT_START{ (void)0; }G_STMT_END
+#define G_N_ELEMENTS(arr)		(sizeof (arr) / sizeof ((arr)[0]))
+#define TRUE 1
+#define FALSE 0
+
+/* Code from GLIB gunicode.h starts here. */
+
+typedef enum
+{
+  G_NORMALIZE_DEFAULT,
+  G_NORMALIZE_NFD = G_NORMALIZE_DEFAULT,
+  G_NORMALIZE_DEFAULT_COMPOSE,
+  G_NORMALIZE_NFC = G_NORMALIZE_DEFAULT_COMPOSE,
+  G_NORMALIZE_ALL,
+  G_NORMALIZE_NFKD = G_NORMALIZE_ALL,
+  G_NORMALIZE_ALL_COMPOSE,
+  G_NORMALIZE_NFKC = G_NORMALIZE_ALL_COMPOSE
+}
+GNormalizeMode;
+
+/* Code from GLIB gutf8.c starts here. */
+
+#define UTF8_COMPUTE(Char, Mask, Len)		\
+  if (Char < 128)				\
+    {						\
+      Len = 1;					\
+      Mask = 0x7f;				\
+    }						\
+  else if ((Char & 0xe0) == 0xc0)		\
+    {						\
+      Len = 2;					\
+      Mask = 0x1f;				\
+    }						\
+  else if ((Char & 0xf0) == 0xe0)		\
+    {						\
+      Len = 3;					\
+      Mask = 0x0f;				\
+    }						\
+  else if ((Char & 0xf8) == 0xf0)		\
+    {						\
+      Len = 4;					\
+      Mask = 0x07;				\
+    }						\
+  else if ((Char & 0xfc) == 0xf8)		\
+    {						\
+      Len = 5;					\
+      Mask = 0x03;				\
+    }						\
+  else if ((Char & 0xfe) == 0xfc)		\
+    {						\
+      Len = 6;					\
+      Mask = 0x01;				\
+    }						\
+  else						\
+    Len = -1;
+
+#define UTF8_LENGTH(Char)			\
+  ((Char) < 0x80 ? 1 :				\
+   ((Char) < 0x800 ? 2 :			\
+    ((Char) < 0x10000 ? 3 :			\
+     ((Char) < 0x200000 ? 4 :			\
+      ((Char) < 0x4000000 ? 5 : 6)))))
+
+
+#define UTF8_GET(Result, Chars, Count, Mask, Len)	\
+  (Result) = (Chars)[0] & (Mask);			\
+  for ((Count) = 1; (Count) < (Len); ++(Count))		\
+    {							\
+      if (((Chars)[(Count)] & 0xc0) != 0x80)		\
+	{						\
+	  (Result) = -1;				\
+	  break;					\
+	}						\
+      (Result) <<= 6;					\
+      (Result) |= ((Chars)[(Count)] & 0x3f);		\
+    }
+
+#define UNICODE_VALID(Char)			\
+  ((Char) < 0x110000 &&				\
+   (((Char) & 0xFFFFF800) != 0xD800) &&		\
+   ((Char) < 0xFDD0 || (Char) > 0xFDEF) &&	\
+   ((Char) & 0xFFFE) != 0xFFFE)
+
+
+static const gchar utf8_skip_data[256] = {
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1,
+  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+  2, 2, 2, 2, 2, 2, 2,
+  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5,
+  5, 5, 5, 6, 6, 1, 1
+};
+
+const gchar *const g_utf8_skip = utf8_skip_data;
+
+#define g_utf8_next_char(p) (char *)((p) + g_utf8_skip[*(guchar *)(p)])
+
+/**
+ * g_utf8_strlen:
+ * @p: pointer to the start of a UTF-8 encoded string.
+ * @max: the maximum number of bytes to examine. If @max
+ *       is less than 0, then the string is assumed to be
+ *       nul-terminated. If @max is 0, @p will not be examined and
+ *       may be %NULL.
+ *
+ * Returns the length of the string in characters.
+ *
+ * Return value: the length of the string in characters
+ **/
+static glong
+g_utf8_strlen (const gchar * p, gssize max)
+{
+  glong len = 0;
+  const gchar *start = p;
+  g_return_val_if_fail (p != NULL || max == 0, 0);
+
+  if (max < 0)
+    {
+      while (*p)
+	{
+	  p = g_utf8_next_char (p);
+	  ++len;
+	}
+    }
+  else
+    {
+      if (max == 0 || !*p)
+	return 0;
+
+      p = g_utf8_next_char (p);
+
+      while (p - start < max && *p)
+	{
+	  ++len;
+	  p = g_utf8_next_char (p);
+	}
+
+      /* only do the last len increment if we got a complete
+       * char (don't count partial chars)
+       */
+      if (p - start == max)
+	++len;
+    }
+
+  return len;
+}
+
+/**
+ * g_utf8_get_char:
+ * @p: a pointer to Unicode character encoded as UTF-8
+ *
+ * Converts a sequence of bytes encoded as UTF-8 to a Unicode character.
+ * If @p does not point to a valid UTF-8 encoded character, results are
+ * undefined. If you are not sure that the bytes are complete
+ * valid Unicode characters, you should use g_utf8_get_char_validated()
+ * instead.
+ *
+ * Return value: the resulting character
+ **/
+static gunichar
+g_utf8_get_char (const gchar * p)
+{
+  int i, mask = 0, len;
+  gunichar result;
+  unsigned char c = (unsigned char) *p;
+
+  UTF8_COMPUTE (c, mask, len);
+  if (len == -1)
+    return (gunichar) - 1;
+  UTF8_GET (result, p, i, mask, len);
+
+  return result;
+}
+
+/**
+ * g_unichar_to_utf8:
+ * @c: a ISO10646 character code
+ * @outbuf: output buffer, must have at least 6 bytes of space.
+ *       If %NULL, the length will be computed and returned
+ *       and nothing will be written to @outbuf.
+ *
+ * Converts a single character to UTF-8.
+ *
+ * Return value: number of bytes written
+ **/
+static int
+g_unichar_to_utf8 (gunichar c, gchar * outbuf)
+{
+  guint len = 0;
+  int first;
+  int i;
+
+  if (c < 0x80)
+    {
+      first = 0;
+      len = 1;
+    }
+  else if (c < 0x800)
+    {
+      first = 0xc0;
+      len = 2;
+    }
+  else if (c < 0x10000)
+    {
+      first = 0xe0;
+      len = 3;
+    }
+  else if (c < 0x200000)
+    {
+      first = 0xf0;
+      len = 4;
+    }
+  else if (c < 0x4000000)
+    {
+      first = 0xf8;
+      len = 5;
+    }
+  else
+    {
+      first = 0xfc;
+      len = 6;
+    }
+
+  if (outbuf)
+    {
+      for (i = len - 1; i > 0; --i)
+	{
+	  outbuf[i] = (c & 0x3f) | 0x80;
+	  c >>= 6;
+	}
+      outbuf[0] = c | first;
+    }
+
+  return len;
+}
+
+/**
+ * g_utf8_to_ucs4_fast:
+ * @str: a UTF-8 encoded string
+ * @len: the maximum length of @str to use. If @len < 0, then
+ *       the string is nul-terminated.
+ * @items_written: location to store the number of characters in the
+ *                 result, or %NULL.
+ *
+ * Convert a string from UTF-8 to a 32-bit fixed width
+ * representation as UCS-4, assuming valid UTF-8 input.
+ * This function is roughly twice as fast as g_utf8_to_ucs4()
+ * but does no error checking on the input.
+ *
+ * Return value: a pointer to a newly allocated UCS-4 string.
+ *               This value must be freed with g_free().
+ **/
+static gunichar *
+g_utf8_to_ucs4_fast (const gchar * str, glong len, glong * items_written)
+{
+  gint j, charlen;
+  gunichar *result;
+  gint n_chars, i;
+  const gchar *p;
+
+  g_return_val_if_fail (str != NULL, NULL);
+
+  p = str;
+  n_chars = 0;
+  if (len < 0)
+    {
+      while (*p)
+	{
+	  p = g_utf8_next_char (p);
+	  ++n_chars;
+	}
+    }
+  else
+    {
+      while (p < str + len && *p)
+	{
+	  p = g_utf8_next_char (p);
+	  ++n_chars;
+	}
+    }
+
+  result = g_new (gunichar, n_chars + 1);
+
+  p = str;
+  for (i = 0; i < n_chars; i++)
+    {
+      gunichar wc = ((unsigned char *) p)[0];
+
+      if (wc < 0x80)
+	{
+	  result[i] = wc;
+	  p++;
+	}
+      else
+	{
+	  if (wc < 0xe0)
+	    {
+	      charlen = 2;
+	      wc &= 0x1f;
+	    }
+	  else if (wc < 0xf0)
+	    {
+	      charlen = 3;
+	      wc &= 0x0f;
+	    }
+	  else if (wc < 0xf8)
+	    {
+	      charlen = 4;
+	      wc &= 0x07;
+	    }
+	  else if (wc < 0xfc)
+	    {
+	      charlen = 5;
+	      wc &= 0x03;
+	    }
+	  else
+	    {
+	      charlen = 6;
+	      wc &= 0x01;
+	    }
+
+	  for (j = 1; j < charlen; j++)
+	    {
+	      wc <<= 6;
+	      wc |= ((unsigned char *) p)[j] & 0x3f;
+	    }
+
+	  result[i] = wc;
+	  p += charlen;
+	}
+    }
+  result[i] = 0;
+
+  if (items_written)
+    *items_written = i;
+
+  return result;
+}
+
+/**
+ * g_ucs4_to_utf8:
+ * @str: a UCS-4 encoded string
+ * @len: the maximum length of @str to use. If @len < 0, then
+ *       the string is terminated with a 0 character.
+ * @items_read: location to store number of characters read read, or %NULL.
+ * @items_written: location to store number of bytes written or %NULL.
+ *                 The value here stored does not include the trailing 0
+ *                 byte.
+ * @error: location to store the error occuring, or %NULL to ignore
+ *         errors. Any of the errors in #GConvertError other than
+ *         %G_CONVERT_ERROR_NO_CONVERSION may occur.
+ *
+ * Convert a string from a 32-bit fixed width representation as UCS-4.
+ * to UTF-8. The result will be terminated with a 0 byte.
+ *
+ * Return value: a pointer to a newly allocated UTF-8 string.
+ *               This value must be freed with g_free(). If an
+ *               error occurs, %NULL will be returned and
+ *               @error set.
+ **/
+static gchar *
+g_ucs4_to_utf8 (const gunichar * str,
+		glong len,
+		glong * items_read, glong * items_written, GError ** error)
+{
+  gint result_length;
+  gchar *result = NULL;
+  gchar *p;
+  gint i;
+
+  result_length = 0;
+  for (i = 0; len < 0 || i < len; i++)
+    {
+      if (!str[i])
+	break;
+
+      if (str[i] >= 0x80000000)
+	{
+	  if (items_read)
+	    *items_read = i;
+
+	  /*g_set_error (error, G_CONVERT_ERROR,
+		       G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+		       _("Character out of range for UTF-8"));*/
+	  goto err_out;
+	}
+
+      result_length += UTF8_LENGTH (str[i]);
+    }
+
+  result = g_malloc (result_length + 1);
+  p = result;
+
+  i = 0;
+  while (p < result + result_length)
+    p += g_unichar_to_utf8 (str[i++], p);
+
+  *p = '\0';
+
+  if (items_written)
+    *items_written = p - result;
+
+err_out:
+  if (items_read)
+    *items_read = i;
+
+  return result;
+}
+
+/* Code from GLIB gunidecomp.c starts here. */
+
+#include "gunidecomp.h"
+#include "gunicomp.h"
+
+#define CC_PART1(Page, Char) \
+  ((combining_class_table_part1[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
+   ? (combining_class_table_part1[Page] - G_UNICODE_MAX_TABLE_INDEX) \
+   : (cclass_data[combining_class_table_part1[Page]][Char]))
+
+#define CC_PART2(Page, Char) \
+  ((combining_class_table_part2[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
+   ? (combining_class_table_part2[Page] - G_UNICODE_MAX_TABLE_INDEX) \
+   : (cclass_data[combining_class_table_part2[Page]][Char]))
+
+#define COMBINING_CLASS(Char) \
+  (((Char) <= G_UNICODE_LAST_CHAR_PART1) \
+   ? CC_PART1 ((Char) >> 8, (Char) & 0xff) \
+   : (((Char) >= 0xe0000 && (Char) <= G_UNICODE_LAST_CHAR) \
+      ? CC_PART2 (((Char) - 0xe0000) >> 8, (Char) & 0xff) \
+      : 0))
+
+/* constants for hangul syllable [de]composition */
+#define SBase 0xAC00
+#define LBase 0x1100
+#define VBase 0x1161
+#define TBase 0x11A7
+#define LCount 19
+#define VCount 21
+#define TCount 28
+#define NCount (VCount * TCount)
+#define SCount (LCount * NCount)
+
+/**
+ * g_unicode_canonical_ordering:
+ * @string: a UCS-4 encoded string.
+ * @len: the maximum length of @string to use.
+ *
+ * Computes the canonical ordering of a string in-place. 
+ * This rearranges decomposed characters in the string
+ * according to their combining classes.  See the Unicode
+ * manual for more information.
+ **/
+static void
+g_unicode_canonical_ordering (gunichar * string, gsize len)
+{
+  gsize i;
+  int swap = 1;
+
+  while (swap)
+    {
+      int last;
+      swap = 0;
+      last = COMBINING_CLASS (string[0]);
+      for (i = 0; i < len - 1; ++i)
+	{
+	  int next = COMBINING_CLASS (string[i + 1]);
+	  if (next != 0 && last > next)
+	    {
+	      gsize j;
+	      /* Percolate item leftward through string.  */
+	      for (j = i + 1; j > 0; --j)
+		{
+		  gunichar t;
+		  if (COMBINING_CLASS (string[j - 1]) <= next)
+		    break;
+		  t = string[j];
+		  string[j] = string[j - 1];
+		  string[j - 1] = t;
+		  swap = 1;
+		}
+	      /* We're re-entering the loop looking at the old
+	         character again.  */
+	      next = last;
+	    }
+	  last = next;
+	}
+    }
+}
+
+/* http://www.unicode.org/unicode/reports/tr15/#Hangul
+ * r should be null or have sufficient space. Calling with r == NULL will
+ * only calculate the result_len; however, a buffer with space for three
+ * characters will always be big enough. */
+static void
+decompose_hangul (gunichar s, gunichar * r, gsize * result_len)
+{
+  gint SIndex = s - SBase;
+
+  /* not a hangul syllable */
+  if (SIndex < 0 || SIndex >= SCount)
+    {
+      if (r)
+	r[0] = s;
+      *result_len = 1;
+    }
+  else
+    {
+      gunichar L = LBase + SIndex / NCount;
+      gunichar V = VBase + (SIndex % NCount) / TCount;
+      gunichar T = TBase + SIndex % TCount;
+
+      if (r)
+	{
+	  r[0] = L;
+	  r[1] = V;
+	}
+
+      if (T != TBase)
+	{
+	  if (r)
+	    r[2] = T;
+	  *result_len = 3;
+	}
+      else
+	*result_len = 2;
+    }
+}
+
+/* returns a pointer to a null-terminated UTF-8 string */
+static const gchar *
+find_decomposition (gunichar ch, gboolean compat)
+{
+  int start = 0;
+  int end = G_N_ELEMENTS (decomp_table);
+
+  if (ch >= decomp_table[start].ch && ch <= decomp_table[end - 1].ch)
+    {
+      while (TRUE)
+	{
+	  int half = (start + end) / 2;
+	  if (ch == decomp_table[half].ch)
+	    {
+	      int offset;
+
+	      if (compat)
+		{
+		  offset = decomp_table[half].compat_offset;
+		  if (offset == G_UNICODE_NOT_PRESENT_OFFSET)
+		    offset = decomp_table[half].canon_offset;
+		}
+	      else
+		{
+		  offset = decomp_table[half].canon_offset;
+		  if (offset == G_UNICODE_NOT_PRESENT_OFFSET)
+		    return NULL;
+		}
+
+	      return &(decomp_expansion_string[offset]);
+	    }
+	  else if (half == start)
+	    break;
+	  else if (ch > decomp_table[half].ch)
+	    start = half;
+	  else
+	    end = half;
+	}
+    }
+
+  return NULL;
+}
+
+/* L,V => LV and LV,T => LVT  */
+static gboolean
+combine_hangul (gunichar a, gunichar b, gunichar * result)
+{
+  gint LIndex = a - LBase;
+  gint SIndex = a - SBase;
+
+  gint VIndex = b - VBase;
+  gint TIndex = b - TBase;
+
+  if (0 <= LIndex && LIndex < LCount && 0 <= VIndex && VIndex < VCount)
+    {
+      *result = SBase + (LIndex * VCount + VIndex) * TCount;
+      return TRUE;
+    }
+  else if (0 <= SIndex && SIndex < SCount && (SIndex % TCount) == 0
+	   && 0 <= TIndex && TIndex <= TCount)
+    {
+      *result = a + TIndex;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+#define CI(Page, Char) \
+  ((compose_table[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
+   ? (compose_table[Page] - G_UNICODE_MAX_TABLE_INDEX) \
+   : (compose_data[compose_table[Page]][Char]))
+
+#define COMPOSE_INDEX(Char) \
+     ((((Char) >> 8) > (COMPOSE_TABLE_LAST)) ? 0 : CI((Char) >> 8, (Char) & 0xff))
+
+static gboolean
+combine (gunichar a, gunichar b, gunichar * result)
+{
+  gushort index_a, index_b;
+
+  if (combine_hangul (a, b, result))
+    return TRUE;
+
+  index_a = COMPOSE_INDEX (a);
+
+  if (index_a >= COMPOSE_FIRST_SINGLE_START && index_a < COMPOSE_SECOND_START)
+    {
+      if (b == compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][0])
+	{
+	  *result =
+	    compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][1];
+	  return TRUE;
+	}
+      else
+	return FALSE;
+    }
+
+  index_b = COMPOSE_INDEX (b);
+
+  if (index_b >= COMPOSE_SECOND_SINGLE_START)
+    {
+      if (a ==
+	  compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][0])
+	{
+	  *result =
+	    compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][1];
+	  return TRUE;
+	}
+      else
+	return FALSE;
+    }
+
+  if (index_a >= COMPOSE_FIRST_START && index_a < COMPOSE_FIRST_SINGLE_START
+      && index_b >= COMPOSE_SECOND_START
+      && index_b < COMPOSE_SECOND_SINGLE_START)
+    {
+      gunichar res =
+	compose_array[index_a - COMPOSE_FIRST_START][index_b -
+						     COMPOSE_SECOND_START];
+
+      if (res)
+	{
+	  *result = res;
+	  return TRUE;
+	}
+    }
+
+  return FALSE;
+}
+
+static gunichar *
+_g_utf8_normalize_wc (const gchar * str, gssize max_len, GNormalizeMode mode)
+{
+  gsize n_wc;
+  gunichar *wc_buffer;
+  const char *p;
+  gsize last_start;
+  gboolean do_compat = (mode == G_NORMALIZE_NFKC || mode == G_NORMALIZE_NFKD);
+  gboolean do_compose = (mode == G_NORMALIZE_NFC || mode == G_NORMALIZE_NFKC);
+
+  n_wc = 0;
+  p = str;
+  while ((max_len < 0 || p < str + max_len) && *p)
+    {
+      const gchar *decomp;
+      gunichar wc = g_utf8_get_char (p);
+
+      if (wc >= 0xac00 && wc <= 0xd7af)
+	{
+	  gsize result_len;
+	  decompose_hangul (wc, NULL, &result_len);
+	  n_wc += result_len;
+	}
+      else
+	{
+	  decomp = find_decomposition (wc, do_compat);
+
+	  if (decomp)
+	    n_wc += g_utf8_strlen (decomp, -1);
+	  else
+	    n_wc++;
+	}
+
+      p = g_utf8_next_char (p);
+    }
+
+  wc_buffer = g_new (gunichar, n_wc + 1);
+
+  last_start = 0;
+  n_wc = 0;
+  p = str;
+  while ((max_len < 0 || p < str + max_len) && *p)
+    {
+      gunichar wc = g_utf8_get_char (p);
+      const gchar *decomp;
+      int cc;
+      gsize old_n_wc = n_wc;
+
+      if (wc >= 0xac00 && wc <= 0xd7af)
+	{
+	  gsize result_len;
+	  decompose_hangul (wc, wc_buffer + n_wc, &result_len);
+	  n_wc += result_len;
+	}
+      else
+	{
+	  decomp = find_decomposition (wc, do_compat);
+
+	  if (decomp)
+	    {
+	      const char *pd;
+	      for (pd = decomp; *pd != '\0'; pd = g_utf8_next_char (pd))
+		wc_buffer[n_wc++] = g_utf8_get_char (pd);
+	    }
+	  else
+	    wc_buffer[n_wc++] = wc;
+	}
+
+      if (n_wc > 0)
+	{
+	  cc = COMBINING_CLASS (wc_buffer[old_n_wc]);
+
+	  if (cc == 0)
+	    {
+	      g_unicode_canonical_ordering (wc_buffer + last_start,
+					    n_wc - last_start);
+	      last_start = old_n_wc;
+	    }
+	}
+
+      p = g_utf8_next_char (p);
+    }
+
+  if (n_wc > 0)
+    {
+      g_unicode_canonical_ordering (wc_buffer + last_start,
+				    n_wc - last_start);
+      last_start = n_wc;
+    }
+
+  wc_buffer[n_wc] = 0;
+
+  /* All decomposed and reordered */
+
+  if (do_compose && n_wc > 0)
+    {
+      gsize i, j;
+      int last_cc = 0;
+      last_start = 0;
+
+      for (i = 0; i < n_wc; i++)
+	{
+	  int cc = COMBINING_CLASS (wc_buffer[i]);
+
+	  if (i > 0 &&
+	      (last_cc == 0 || last_cc != cc) &&
+	      combine (wc_buffer[last_start], wc_buffer[i],
+		       &wc_buffer[last_start]))
+	    {
+	      for (j = i + 1; j < n_wc; j++)
+		wc_buffer[j - 1] = wc_buffer[j];
+	      n_wc--;
+	      i--;
+
+	      if (i == last_start)
+		last_cc = 0;
+	      else
+		last_cc = COMBINING_CLASS (wc_buffer[i - 1]);
+
+	      continue;
+	    }
+
+	  if (cc == 0)
+	    last_start = i;
+
+	  last_cc = cc;
+	}
+    }
+
+  wc_buffer[n_wc] = 0;
+
+  return wc_buffer;
+}
+
+/**
+ * g_utf8_normalize:
+ * @str: a UTF-8 encoded string.
+ * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
+ * @mode: the type of normalization to perform.
+ *
+ * Converts a string into canonical form, standardizing
+ * such issues as whether a character with an accent
+ * is represented as a base character and combining
+ * accent or as a single precomposed character. You
+ * should generally call g_utf8_normalize() before
+ * comparing two Unicode strings.
+ *
+ * The normalization mode %G_NORMALIZE_DEFAULT only
+ * standardizes differences that do not affect the
+ * text content, such as the above-mentioned accent
+ * representation. %G_NORMALIZE_ALL also standardizes
+ * the "compatibility" characters in Unicode, such
+ * as SUPERSCRIPT THREE to the standard forms
+ * (in this case DIGIT THREE). Formatting information
+ * may be lost but for most text operations such
+ * characters should be considered the same.
+ * For example, g_utf8_collate() normalizes
+ * with %G_NORMALIZE_ALL as its first step.
+ *
+ * %G_NORMALIZE_DEFAULT_COMPOSE and %G_NORMALIZE_ALL_COMPOSE
+ * are like %G_NORMALIZE_DEFAULT and %G_NORMALIZE_ALL,
+ * but returned a result with composed forms rather
+ * than a maximally decomposed form. This is often
+ * useful if you intend to convert the string to
+ * a legacy encoding or pass it to a system with
+ * less capable Unicode handling.
+ *
+ * Return value: a newly allocated string, that is the
+ *   normalized form of @str.
+ **/
+static gchar *
+g_utf8_normalize (const gchar * str, gssize len, GNormalizeMode mode)
+{
+  gunichar *result_wc = _g_utf8_normalize_wc (str, len, mode);
+  gchar *result;
+
+  result = g_ucs4_to_utf8 (result_wc, -1, NULL, NULL, NULL);
+  g_free (result_wc);
+
+  return result;
+}
+
+/* Public Libidn API starts here. */
+
+/**
+ * stringprep_utf8_to_unichar:
+ * @p: a pointer to Unicode character encoded as UTF-8
+ *
+ * Converts a sequence of bytes encoded as UTF-8 to a Unicode character.
+ * If @p does not point to a valid UTF-8 encoded character, results are
+ * undefined. If you are not sure that the bytes are complete
+ * valid Unicode characters, you should use g_utf8_get_char_validated()
+ * instead.
+ *
+ * Return value: the resulting character
+ **/
+my_uint32_t
+stringprep_utf8_to_unichar (const char *p)
+{
+  return g_utf8_get_char (p);
+}
+
+/**
+ * stringprep_unichar_to_utf8:
+ * @c: a ISO10646 character code
+ * @outbuf: output buffer, must have at least 6 bytes of space.
+ *       If %NULL, the length will be computed and returned
+ *       and nothing will be written to @outbuf.
+ *
+ * Converts a single character to UTF-8.
+ *
+ * Return value: number of bytes written
+ **/
+int
+stringprep_unichar_to_utf8 (my_uint32_t c, char *outbuf)
+{
+  return g_unichar_to_utf8 (c, outbuf);
+}
+
+/**
+ * stringprep_utf8_to_ucs4:
+ * @str: a UTF-8 encoded string
+ * @len: the maximum length of @str to use. If @len < 0, then
+ *       the string is nul-terminated.
+ * @items_written: location to store the number of characters in the
+ *                 result, or %NULL.
+ *
+ * Convert a string from UTF-8 to a 32-bit fixed width
+ * representation as UCS-4, assuming valid UTF-8 input.
+ * This function does no error checking on the input.
+ *
+ * Return value: a pointer to a newly allocated UCS-4 string.
+ *               This value must be freed with free().
+ **/
+my_uint32_t *
+stringprep_utf8_to_ucs4 (const char *str, ssize_t len, size_t * items_written)
+{
+  return g_utf8_to_ucs4_fast (str, (glong) len, (glong *) items_written);
+}
+
+/**
+ * stringprep_ucs4_to_utf8:
+ * @str: a UCS-4 encoded string
+ * @len: the maximum length of @str to use. If @len < 0, then
+ *       the string is terminated with a 0 character.
+ * @items_read: location to store number of characters read read, or %NULL.
+ * @items_written: location to store number of bytes written or %NULL.
+ *                 The value here stored does not include the trailing 0
+ *                 byte.
+ *
+ * Convert a string from a 32-bit fixed width representation as UCS-4.
+ * to UTF-8. The result will be terminated with a 0 byte.
+ *
+ * Return value: a pointer to a newly allocated UTF-8 string.
+ *               This value must be freed with free(). If an
+ *               error occurs, %NULL will be returned and
+ *               @error set.
+ **/
+char *
+stringprep_ucs4_to_utf8 (const my_uint32_t * str, ssize_t len,
+			 size_t * items_read, size_t * items_written)
+{
+  return g_ucs4_to_utf8 (str, len, (glong *) items_read,
+			 (glong *) items_written, NULL);
+}
+
+/**
+ * stringprep_utf8_nfkc_normalize:
+ * @str: a UTF-8 encoded string.
+ * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
+ *
+ * Converts a string into canonical form, standardizing
+ * such issues as whether a character with an accent
+ * is represented as a base character and combining
+ * accent or as a single precomposed character.
+ *
+ * The normalization mode is NFKC (ALL COMPOSE).  It standardizes
+ * differences that do not affect the text content, such as the
+ * above-mentioned accent representation. It standardizes the
+ * "compatibility" characters in Unicode, such as SUPERSCRIPT THREE to
+ * the standard forms (in this case DIGIT THREE). Formatting
+ * information may be lost but for most text operations such
+ * characters should be considered the same. It returns a result with
+ * composed forms rather than a maximally decomposed form.
+ *
+ * Return value: a newly allocated string, that is the
+ *   NFKC normalized form of @str.
+ **/
+char *
+stringprep_utf8_nfkc_normalize (const char *str, ssize_t len)
+{
+  return g_utf8_normalize (str, len, G_NORMALIZE_NFKC);
+}
+
+/**
+ * stringprep_ucs4_nfkc_normalize:
+ * @str: a Unicode string.
+ * @len: length of @str array, or -1 if @str is nul-terminated.
+ *
+ * Converts UCS4 string into UTF-8 and runs
+ * stringprep_utf8_nfkc_normalize().
+ *
+ * Return value: a newly allocated Unicode string, that is the NFKC
+ *   normalized form of @str.
+ **/
+my_uint32_t *
+stringprep_ucs4_nfkc_normalize (my_uint32_t * str, ssize_t len)
+{
+  char *p;
+  my_uint32_t *result_wc;
+
+  p = stringprep_ucs4_to_utf8 (str, len, 0, 0);
+  result_wc = _g_utf8_normalize_wc (p, -1, G_NORMALIZE_NFKC);
+  free (p);
+
+  return result_wc;
+}
Index: psi/tags/0.10-os2-b1/iris/libidn/profiles.c
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/profiles.c	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/profiles.c	(revision 38)
@@ -0,0 +1,610 @@
+/* profiles.c	Definitions of stringprep profiles.
+ * Copyright (C) 2002, 2003  Simon Josefsson
+ *
+ * This file is part of GNU Libidn.
+ *
+ * GNU Libidn is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GNU Libidn 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU Libidn; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "internal.h"
+
+Stringprep_profiles stringprep_profiles[] = {
+  {"generic", stringprep_generic}
+  ,
+  {"Nameprep", stringprep_nameprep}
+  ,
+  {"KRBprep", stringprep_kerberos5}
+  ,
+  {"Nodeprep", stringprep_xmpp_nodeprep}
+  ,
+  {"Resourceprep", stringprep_xmpp_resourceprep}
+  ,
+  {"plain", stringprep_plain}
+  ,
+  {"SASLprep", stringprep_saslprep}
+  ,
+  {"ISCSIprep", stringprep_iscsi}
+  ,
+  {NULL, NULL}
+};
+
+Stringprep_profile stringprep_generic[] = {
+  /* 1) Map -- For each character in the input, check if it has a
+     mapping and, if so, replace it with its mapping. This is
+     described in section 3. */
+
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1, "B.1"}
+  ,
+  {STRINGPREP_MAP_TABLE, ~STRINGPREP_NO_NFKC, stringprep_rfc3454_B_2, "B.2"}
+  ,
+  {STRINGPREP_MAP_TABLE, STRINGPREP_NO_NFKC, stringprep_rfc3454_B_3, "B.3"}
+  ,
+
+  /* 2) Normalize -- Possibly normalize the result of step 1 using
+     Unicode normalization. This is described in section 4. */
+
+  {STRINGPREP_NFKC, ~STRINGPREP_NO_NFKC, 0, "NFKC"}
+  ,
+
+  /* 3) Prohibit -- Check for any characters that are not allowed in
+     the output. If any are found, return an error. This is
+     described in section 5. */
+
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_1, "C.1.1"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_2, "C.1.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_1, "C.2.1"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.2.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3, "C.3"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4, "C.4"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5, "C.5"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6, "C.6"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_7, "C.7"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9, "C.9"}
+  ,
+
+  /* 4) Check bidi -- Possibly check for right-to-left characters, and
+     if any are found, make sure that the whole string satisfies
+     the requirements for bidirectional strings. If the string does
+     not satisfy the requirements for bidirectional strings, return
+     an error. This is described in section 6. */
+
+  {STRINGPREP_BIDI, ~STRINGPREP_NO_BIDI, 0, "BIDI"}
+  ,
+  {STRINGPREP_BIDI_PROHIBIT_TABLE, ~STRINGPREP_NO_BIDI,
+   stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_1,
+   "D.1"}
+  ,
+  {STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_2,
+   "D.2"}
+  ,
+
+  /* 5) Check unassigned code points -- Possibly check the output for
+     unassigned code points, according to the profile.  This is
+     described in section 7. */
+
+  {STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED,
+   stringprep_rfc3454_A_1, "A.1"}
+  ,
+
+  {0}
+};
+
+Stringprep_profile stringprep_nameprep[] = {
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1, "B.1"}
+  ,
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_2, "B.2"}
+  ,
+  {STRINGPREP_NFKC, 0, 0, "NFKC"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_2, "C.1.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.2.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3, "C.3"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4, "C.4"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5, "C.5"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6, "C.6"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_7, "C.7"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9, "C.9"}
+  ,
+  {STRINGPREP_BIDI, 0, 0, "BIDI"}
+  ,
+  {STRINGPREP_BIDI_PROHIBIT_TABLE, ~STRINGPREP_NO_BIDI,
+   stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_BIDI_RAL_TABLE, 0, stringprep_rfc3454_D_1, "D.1"}
+  ,
+  {STRINGPREP_BIDI_L_TABLE, 0, stringprep_rfc3454_D_2, "D.2"}
+  ,
+  {STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED,
+   stringprep_rfc3454_A_1, "A.1"}
+  ,
+  {0}
+};
+
+Stringprep_profile stringprep_kerberos5[] = {
+  /* XXX this is likely to be wrong as the specification is
+     a rough draft. */
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1, "B.1"}
+  ,
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_3, "B.2"}
+  ,
+  {STRINGPREP_NFKC, 0, 0, "NFKC"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_2, "C.1.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.2.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3, "C.3"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4, "C.4"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5, "C.5"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6, "C.6"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_7, "C.7"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9, "C.9"}
+  ,
+  {STRINGPREP_BIDI, 0, 0, "BIDI"}
+  ,
+  {STRINGPREP_BIDI_PROHIBIT_TABLE, ~STRINGPREP_NO_BIDI,
+   stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_BIDI_RAL_TABLE, 0, stringprep_rfc3454_D_1, "D.1"}
+  ,
+  {STRINGPREP_BIDI_L_TABLE, 0, stringprep_rfc3454_D_2, "D.2"}
+  ,
+  {STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED,
+   stringprep_rfc3454_A_1, "A.1"}
+  ,
+  {0}
+};
+
+Stringprep_table_element stringprep_xmpp_nodeprep_prohibit[] = {
+  {0x000022}
+  ,				/* #x22 (") */
+  {0x000026}
+  ,				/* #x26 (&) */
+  {0x000027}
+  ,				/* #x27 (') */
+  {0x00002F}
+  ,				/* #x2F (/) */
+  {0x00003A}
+  ,				/* #x3A (:) */
+  {0x00003C}
+  ,				/* #x3C (<) */
+  {0x00003E}
+  ,				/* #x3E (>) */
+  {0x000040}			/* #x40 (@) */
+  ,
+  {0}
+};
+
+Stringprep_profile stringprep_xmpp_nodeprep[] = {
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1, "B.1"}
+  ,
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_2, "B.2"}
+  ,
+  {STRINGPREP_NFKC, 0, 0, "NFKC"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_1, "C.1.1"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_2, "C.1.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_1, "C.2.1"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.2.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3, "C.3"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4, "C.4"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5, "C.5"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6, "C.6"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_7, "C.7"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9, "C.9"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_xmpp_nodeprep_prohibit,
+   "XMPP-PROHIBIT"}
+  ,
+  {STRINGPREP_BIDI, 0, 0, "BIDI"}
+  ,
+  {STRINGPREP_BIDI_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_BIDI_RAL_TABLE, 0, stringprep_rfc3454_D_1, "D.1"}
+  ,
+  {STRINGPREP_BIDI_L_TABLE, 0, stringprep_rfc3454_D_2, "D.2"}
+  ,
+  {STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED,
+   stringprep_rfc3454_A_1, "A.1"}
+  ,
+  {0}
+};
+
+Stringprep_profile stringprep_xmpp_resourceprep[] = {
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1, "B.1"}
+  ,
+  {STRINGPREP_NFKC, 0, 0, "NFKC"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_1_2, "C.1.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_1, "C.2.1"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.2.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3, "C.3"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4, "C.4"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5, "C.5"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6, "C.6"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_7, "C.7"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9, "C.9"}
+  ,
+  {STRINGPREP_BIDI, 0, 0, "BIDI"}
+  ,
+  {STRINGPREP_BIDI_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_1,
+   "D.1"}
+  ,
+  {STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_2,
+   "D.2"}
+  ,
+  {STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED,
+   stringprep_rfc3454_A_1, "A.1"}
+  ,
+  {0}
+};
+
+Stringprep_profile stringprep_plain[] = {
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_1, "C.2.1"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.2.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3, "C.3"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4, "C.4"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5, "C.5"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6, "C.6"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9, "C.9"}
+  ,
+  {STRINGPREP_BIDI, 0, 0, "BIDI"}
+  ,
+  {STRINGPREP_BIDI_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_1,
+   "D.1"}
+  ,
+  {STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_2,
+   "D.2"}
+  ,
+  {0}
+};
+
+Stringprep_table_element stringprep_iscsi_prohibit[] = {
+  {0x0000}
+  ,				/* [ASCII CONTROL CHARACTERS and SPACE through ,] */
+  {0x0001}
+  ,
+  {0x0002}
+  ,
+  {0x0003}
+  ,
+  {0x0004}
+  ,
+  {0x0005}
+  ,
+  {0x0006}
+  ,
+  {0x0007}
+  ,
+  {0x0008}
+  ,
+  {0x0009}
+  ,
+  {0x000A}
+  ,
+  {0x000B}
+  ,
+  {0x000C}
+  ,
+  {0x000D}
+  ,
+  {0x000E}
+  ,
+  {0x000F}
+  ,
+  {0x0010}
+  ,
+  {0x0011}
+  ,
+  {0x0012}
+  ,
+  {0x0013}
+  ,
+  {0x0014}
+  ,
+  {0x0015}
+  ,
+  {0x0016}
+  ,
+  {0x0017}
+  ,
+  {0x0018}
+  ,
+  {0x0019}
+  ,
+  {0x001A}
+  ,
+  {0x001B}
+  ,
+  {0x001C}
+  ,
+  {0x001D}
+  ,
+  {0x001E}
+  ,
+  {0x001F}
+  ,
+  {0x0020}
+  ,
+  {0x0021}
+  ,
+  {0x0022}
+  ,
+  {0x0023}
+  ,
+  {0x0024}
+  ,
+  {0x0025}
+  ,
+  {0x0026}
+  ,
+  {0x0027}
+  ,
+  {0x0028}
+  ,
+  {0x0029}
+  ,
+  {0x002A}
+  ,
+  {0x002B}
+  ,
+  {0x002C}
+  ,
+  {0x002F}
+  ,				/* [ASCII /] */
+  {0x003B}
+  ,				/* [ASCII ; through @] */
+  {0x003C}
+  ,
+  {0x003D}
+  ,
+  {0x003E}
+  ,
+  {0x003F}
+  ,
+  {0x0040}
+  ,
+  {0x005B}
+  ,				/* [ASCII [ through `] */
+  {0x005C}
+  ,
+  {0x005D}
+  ,
+  {0x005E}
+  ,
+  {0x005F}
+  ,
+  {0x0060}
+  ,
+  {0x007B}
+  ,				/* [ASCII { through DEL] */
+  {0x007C}
+  ,
+  {0x007D}
+  ,
+  {0x007E}
+  ,
+  {0x007F}
+  ,
+  {0x3002}
+  ,				/* ideographic full stop */
+  {0}
+};
+
+Stringprep_profile stringprep_iscsi[] = {
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1, "B.1"}
+  ,
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_2, "B.2"}
+  ,
+  {STRINGPREP_NFKC, 0, 0, "NFKC"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_1, "C.1.1"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.1.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_1, "C.2.1"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.2.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3, "C.3"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4, "C.4"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5, "C.5"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6, "C.6"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9, "C.9"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_iscsi_prohibit, "ISCSI-PROHIBIT"}
+  ,
+  {STRINGPREP_BIDI, 0, 0, "BIDI"}
+  ,
+  {STRINGPREP_BIDI_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_1,
+   "D.1"}
+  ,
+  {STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_2,
+   "D.2"}
+  ,
+  {STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED,
+   stringprep_rfc3454_A_1, "A.1"}
+  ,
+  {0}
+};
+
+Stringprep_table_element stringprep_saslprep_space_map[] = {
+  {0x0000A0, 0, {0x0020}
+   }
+  ,				/* 00A0; NO-BREAK SPACE */
+  {0x001680, 0, {0x0020}
+   }
+  ,				/* 1680; OGHAM SPACE MARK */
+  {0x002000, 0, {0x0020}
+   }
+  ,				/* 2000; EN QUAD */
+  {0x002001, 0, {0x0020}
+   }
+  ,				/* 2001; EM QUAD */
+  {0x002002, 0, {0x0020}
+   }
+  ,				/* 2002; EN SPACE */
+  {0x002003, 0, {0x0020}
+   }
+  ,				/* 2003; EM SPACE */
+  {0x002004, 0, {0x0020}
+   }
+  ,				/* 2004; THREE-PER-EM SPACE */
+  {0x002005, 0, {0x0020}
+   }
+  ,				/* 2005; FOUR-PER-EM SPACE */
+  {0x002006, 0, {0x0020}
+   }
+  ,				/* 2006; SIX-PER-EM SPACE */
+  {0x002007, 0, {0x0020}
+   }
+  ,				/* 2007; FIGURE SPACE */
+  {0x002008, 0, {0x0020}
+   }
+  ,				/* 2008; PUNCTUATION SPACE */
+  {0x002009, 0, {0x0020}
+   }
+  ,				/* 2009; THIN SPACE */
+  {0x00200A, 0, {0x0020}
+   }
+  ,				/* 200A; HAIR SPACE */
+  {0x00200B, 0, {0x0020}
+   }
+  ,				/* 200B; ZERO WIDTH SPACE */
+  {0x00202F, 0, {0x0020}
+   }
+  ,				/* 202F; NARROW NO-BREAK SPACE */
+  {0x00205F, 0, {0x0020}
+   }
+  ,				/* 205F; MEDIUM MATHEMATICAL SPACE */
+  {0x003000, 0, {0x0020}
+   }
+  ,				/* 3000; IDEOGRAPHIC SPACE */
+  {0}
+};
+
+Stringprep_profile stringprep_saslprep[] = {
+  {STRINGPREP_MAP_TABLE, 0, stringprep_saslprep_space_map, "SASL-SPACE-MAP"}
+  ,
+  {STRINGPREP_MAP_TABLE, 0, stringprep_rfc3454_B_1, "B.1"}
+  ,
+  {STRINGPREP_NFKC, 0, 0, "NFKC"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.1.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_1, "C.2.1"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_2_2, "C.2.2"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_3, "C.3"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_4, "C.4"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_5, "C.5"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_6, "C.6"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_7, "C.7"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_9, "C.9"}
+  ,
+  {STRINGPREP_BIDI, 0, 0, "BIDI"}
+  ,
+  {STRINGPREP_BIDI_PROHIBIT_TABLE, 0, stringprep_rfc3454_C_8, "C.8"}
+  ,
+  {STRINGPREP_BIDI_RAL_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_1,
+   "D.1"}
+  ,
+  {STRINGPREP_BIDI_L_TABLE, ~STRINGPREP_NO_BIDI, stringprep_rfc3454_D_2,
+   "D.2"}
+  ,
+  {STRINGPREP_UNASSIGNED_TABLE, ~STRINGPREP_NO_UNASSIGNED,
+   stringprep_rfc3454_A_1, "A.1"}
+  ,
+  {0}
+};
Index: psi/tags/0.10-os2-b1/iris/libidn/punycode.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/punycode.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/punycode.h	(revision 38)
@@ -0,0 +1,151 @@
+/* punycode.h	Declarations for punycode functions.
+ * Copyright (C) 2002, 2003  Simon Josefsson
+ *
+ * This file is part of GNU Libidn.
+ *
+ * GNU Libidn is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GNU Libidn 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU Libidn; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * This file is derived from RFC 3492 written by Adam M. Costello.
+ *
+ * Disclaimer and license: Regarding this entire document or any
+ * portion of it (including the pseudocode and C code), the author
+ * makes no guarantees and is not responsible for any damage resulting
+ * from its use.  The author grants irrevocable permission to anyone
+ * to use, modify, and distribute it in any way that does not diminish
+ * the rights of anyone else to use, modify, and distribute it,
+ * provided that redistributed derivative works do not contain
+ * misleading author or version information.  Derivative works need
+ * not be licensed under similar terms.
+ *
+ * Copyright (C) The Internet Society (2003).  All Rights Reserved.
+ *
+ * This document and translations of it may be copied and furnished to
+ * others, and derivative works that comment on or otherwise explain it
+ * or assist in its implementation may be prepared, copied, published
+ * and distributed, in whole or in part, without restriction of any
+ * kind, provided that the above copyright notice and this paragraph are
+ * included on all such copies and derivative works.  However, this
+ * document itself may not be modified in any way, such as by removing
+ * the copyright notice or references to the Internet Society or other
+ * Internet organizations, except as needed for the purpose of
+ * developing Internet standards in which case the procedures for
+ * copyrights defined in the Internet Standards process must be
+ * followed, or as required to translate it into languages other than
+ * English.
+ *
+ * The limited permissions granted above are perpetual and will not be
+ * revoked by the Internet Society or its successors or assigns.
+ *
+ * This document and the information contained herein is provided on an
+ * "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _PUNYCODE_H
+#define _PUNYCODE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>		/* size_t */
+#include <idn-int.h>		/* my_uint32_t */
+
+  typedef enum
+  {
+    PUNYCODE_SUCCESS = 0,
+    PUNYCODE_BAD_INPUT,		/* Input is invalid.                       */
+    PUNYCODE_BIG_OUTPUT,	/* Output would exceed the space provided. */
+    PUNYCODE_OVERFLOW		/* Input needs wider integers to process.  */
+  } Punycode_status;
+
+  /* For RFC compatibility. */
+  enum punycode_status
+  {
+    punycode_success = PUNYCODE_SUCCESS,
+    punycode_bad_input = PUNYCODE_BAD_INPUT,
+    punycode_big_output = PUNYCODE_BIG_OUTPUT,
+    punycode_overflow = PUNYCODE_OVERFLOW
+  };
+
+  typedef my_uint32_t punycode_uint;
+
+  int punycode_encode (size_t input_length,
+		       const punycode_uint input[],
+		       const unsigned char case_flags[],
+		       size_t * output_length, char output[]);
+
+  /* punycode_encode() converts Unicode to Punycode.  The input     */
+  /* is represented as an array of Unicode code points (not code    */
+  /* units; surrogate pairs are not allowed), and the output        */
+  /* will be represented as an array of ASCII code points.  The     */
+  /* output string is *not* null-terminated; it will contain        */
+  /* zeros if and only if the input contains zeros.  (Of course     */
+  /* the caller can leave room for a terminator and add one if      */
+  /* needed.)  The input_length is the number of code points in     */
+  /* the input.  The output_length is an in/out argument: the       */
+  /* caller passes in the maximum number of code points that it     */
+  /* can receive, and on successful return it will contain the      */
+  /* number of code points actually output.  The case_flags array   */
+  /* holds input_length boolean values, where nonzero suggests that */
+  /* the corresponding Unicode character be forced to uppercase     */
+  /* after being decoded (if possible), and zero suggests that      */
+  /* it be forced to lowercase (if possible).  ASCII code points    */
+  /* are encoded literally, except that ASCII letters are forced    */
+  /* to uppercase or lowercase according to the corresponding       */
+  /* uppercase flags.  If case_flags is a null pointer then ASCII   */
+  /* letters are left as they are, and other code points are        */
+  /* treated as if their uppercase flags were zero.  The return     */
+  /* value can be any of the punycode_status values defined above   */
+  /* except punycode_bad_input; if not punycode_success, then       */
+  /* output_size and output might contain garbage.                  */
+
+  int punycode_decode (size_t input_length,
+		       const char input[],
+		       size_t * output_length,
+		       punycode_uint output[], unsigned char case_flags[]);
+
+  /* punycode_decode() converts Punycode to Unicode.  The input is  */
+  /* represented as an array of ASCII code points, and the output   */
+  /* will be represented as an array of Unicode code points.  The   */
+  /* input_length is the number of code points in the input.  The   */
+  /* output_length is an in/out argument: the caller passes in      */
+  /* the maximum number of code points that it can receive, and     */
+  /* on successful return it will contain the actual number of      */
+  /* code points output.  The case_flags array needs room for at    */
+  /* least output_length values, or it can be a null pointer if the */
+  /* case information is not needed.  A nonzero flag suggests that  */
+  /* the corresponding Unicode character be forced to uppercase     */
+  /* by the caller (if possible), while zero suggests that it be    */
+  /* forced to lowercase (if possible).  ASCII code points are      */
+  /* output already in the proper case, but their flags will be set */
+  /* appropriately so that applying the flags would be harmless.    */
+  /* The return value can be any of the punycode_status values      */
+  /* defined above; if not punycode_success, then output_length,    */
+  /* output, and case_flags might contain garbage.  On success, the */
+  /* decoder will never need to write an output_length greater than */
+  /* input_length, because of how the encoding is defined.          */
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* _PUNYCODE_H */
Index: psi/tags/0.10-os2-b1/iris/libidn/qint.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/qint.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/qint.h	(revision 38)
@@ -0,0 +1,15 @@
+// simple stdint.h-style replacement without any conflicts
+
+#ifndef QINT_H
+#define QINT_H
+
+typedef unsigned char		my_uint8_t;
+typedef unsigned short int	my_uint16_t;
+typedef unsigned int		my_uint32_t;
+
+typedef char                    my_int8_t;
+typedef short int               my_int16_t;
+typedef int                     my_int32_t;
+
+#endif
+
Index: psi/tags/0.10-os2-b1/iris/libidn/rfc3454.c
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/rfc3454.c	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/rfc3454.c	(revision 38)
@@ -0,0 +1,3541 @@
+#include <stringprep.h>
+
+/*
+ * A.1 Unassigned code points in Unicode 3.2
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_A_1[] = {
+  { 0x000221           },                                            /* 0221 */
+  { 0x000234, 0x00024F },                                       /* 0234-024F */
+  { 0x0002AE, 0x0002AF },                                       /* 02AE-02AF */
+  { 0x0002EF, 0x0002FF },                                       /* 02EF-02FF */
+  { 0x000350, 0x00035F },                                       /* 0350-035F */
+  { 0x000370, 0x000373 },                                       /* 0370-0373 */
+  { 0x000376, 0x000379 },                                       /* 0376-0379 */
+  { 0x00037B, 0x00037D },                                       /* 037B-037D */
+  { 0x00037F, 0x000383 },                                       /* 037F-0383 */
+  { 0x00038B           },                                            /* 038B */
+  { 0x00038D           },                                            /* 038D */
+  { 0x0003A2           },                                            /* 03A2 */
+  { 0x0003CF           },                                            /* 03CF */
+  { 0x0003F7, 0x0003FF },                                       /* 03F7-03FF */
+  { 0x000487           },                                            /* 0487 */
+  { 0x0004CF           },                                            /* 04CF */
+  { 0x0004F6, 0x0004F7 },                                       /* 04F6-04F7 */
+  { 0x0004FA, 0x0004FF },                                       /* 04FA-04FF */
+  { 0x000510, 0x000530 },                                       /* 0510-0530 */
+  { 0x000557, 0x000558 },                                       /* 0557-0558 */
+  { 0x000560           },                                            /* 0560 */
+  { 0x000588           },                                            /* 0588 */
+  { 0x00058B, 0x000590 },                                       /* 058B-0590 */
+  { 0x0005A2           },                                            /* 05A2 */
+  { 0x0005BA           },                                            /* 05BA */
+  { 0x0005C5, 0x0005CF },                                       /* 05C5-05CF */
+  { 0x0005EB, 0x0005EF },                                       /* 05EB-05EF */
+  { 0x0005F5, 0x00060B },                                       /* 05F5-060B */
+  { 0x00060D, 0x00061A },                                       /* 060D-061A */
+  { 0x00061C, 0x00061E },                                       /* 061C-061E */
+  { 0x000620           },                                            /* 0620 */
+  { 0x00063B, 0x00063F },                                       /* 063B-063F */
+  { 0x000656, 0x00065F },                                       /* 0656-065F */
+  { 0x0006EE, 0x0006EF },                                       /* 06EE-06EF */
+  { 0x0006FF           },                                            /* 06FF */
+  { 0x00070E           },                                            /* 070E */
+  { 0x00072D, 0x00072F },                                       /* 072D-072F */
+  { 0x00074B, 0x00077F },                                       /* 074B-077F */
+  { 0x0007B2, 0x000900 },                                       /* 07B2-0900 */
+  { 0x000904           },                                            /* 0904 */
+  { 0x00093A, 0x00093B },                                       /* 093A-093B */
+  { 0x00094E, 0x00094F },                                       /* 094E-094F */
+  { 0x000955, 0x000957 },                                       /* 0955-0957 */
+  { 0x000971, 0x000980 },                                       /* 0971-0980 */
+  { 0x000984           },                                            /* 0984 */
+  { 0x00098D, 0x00098E },                                       /* 098D-098E */
+  { 0x000991, 0x000992 },                                       /* 0991-0992 */
+  { 0x0009A9           },                                            /* 09A9 */
+  { 0x0009B1           },                                            /* 09B1 */
+  { 0x0009B3, 0x0009B5 },                                       /* 09B3-09B5 */
+  { 0x0009BA, 0x0009BB },                                       /* 09BA-09BB */
+  { 0x0009BD           },                                            /* 09BD */
+  { 0x0009C5, 0x0009C6 },                                       /* 09C5-09C6 */
+  { 0x0009C9, 0x0009CA },                                       /* 09C9-09CA */
+  { 0x0009CE, 0x0009D6 },                                       /* 09CE-09D6 */
+  { 0x0009D8, 0x0009DB },                                       /* 09D8-09DB */
+  { 0x0009DE           },                                            /* 09DE */
+  { 0x0009E4, 0x0009E5 },                                       /* 09E4-09E5 */
+  { 0x0009FB, 0x000A01 },                                       /* 09FB-0A01 */
+  { 0x000A03, 0x000A04 },                                       /* 0A03-0A04 */
+  { 0x000A0B, 0x000A0E },                                       /* 0A0B-0A0E */
+  { 0x000A11, 0x000A12 },                                       /* 0A11-0A12 */
+  { 0x000A29           },                                            /* 0A29 */
+  { 0x000A31           },                                            /* 0A31 */
+  { 0x000A34           },                                            /* 0A34 */
+  { 0x000A37           },                                            /* 0A37 */
+  { 0x000A3A, 0x000A3B },                                       /* 0A3A-0A3B */
+  { 0x000A3D           },                                            /* 0A3D */
+  { 0x000A43, 0x000A46 },                                       /* 0A43-0A46 */
+  { 0x000A49, 0x000A4A },                                       /* 0A49-0A4A */
+  { 0x000A4E, 0x000A58 },                                       /* 0A4E-0A58 */
+  { 0x000A5D           },                                            /* 0A5D */
+  { 0x000A5F, 0x000A65 },                                       /* 0A5F-0A65 */
+  { 0x000A75, 0x000A80 },                                       /* 0A75-0A80 */
+  { 0x000A84           },                                            /* 0A84 */
+  { 0x000A8C           },                                            /* 0A8C */
+  { 0x000A8E           },                                            /* 0A8E */
+  { 0x000A92           },                                            /* 0A92 */
+  { 0x000AA9           },                                            /* 0AA9 */
+  { 0x000AB1           },                                            /* 0AB1 */
+  { 0x000AB4           },                                            /* 0AB4 */
+  { 0x000ABA, 0x000ABB },                                       /* 0ABA-0ABB */
+  { 0x000AC6           },                                            /* 0AC6 */
+  { 0x000ACA           },                                            /* 0ACA */
+  { 0x000ACE, 0x000ACF },                                       /* 0ACE-0ACF */
+  { 0x000AD1, 0x000ADF },                                       /* 0AD1-0ADF */
+  { 0x000AE1, 0x000AE5 },                                       /* 0AE1-0AE5 */
+  { 0x000AF0, 0x000B00 },                                       /* 0AF0-0B00 */
+  { 0x000B04           },                                            /* 0B04 */
+  { 0x000B0D, 0x000B0E },                                       /* 0B0D-0B0E */
+  { 0x000B11, 0x000B12 },                                       /* 0B11-0B12 */
+  { 0x000B29           },                                            /* 0B29 */
+  { 0x000B31           },                                            /* 0B31 */
+  { 0x000B34, 0x000B35 },                                       /* 0B34-0B35 */
+  { 0x000B3A, 0x000B3B },                                       /* 0B3A-0B3B */
+  { 0x000B44, 0x000B46 },                                       /* 0B44-0B46 */
+  { 0x000B49, 0x000B4A },                                       /* 0B49-0B4A */
+  { 0x000B4E, 0x000B55 },                                       /* 0B4E-0B55 */
+  { 0x000B58, 0x000B5B },                                       /* 0B58-0B5B */
+  { 0x000B5E           },                                            /* 0B5E */
+  { 0x000B62, 0x000B65 },                                       /* 0B62-0B65 */
+  { 0x000B71, 0x000B81 },                                       /* 0B71-0B81 */
+  { 0x000B84           },                                            /* 0B84 */
+  { 0x000B8B, 0x000B8D },                                       /* 0B8B-0B8D */
+  { 0x000B91           },                                            /* 0B91 */
+  { 0x000B96, 0x000B98 },                                       /* 0B96-0B98 */
+  { 0x000B9B           },                                            /* 0B9B */
+  { 0x000B9D           },                                            /* 0B9D */
+  { 0x000BA0, 0x000BA2 },                                       /* 0BA0-0BA2 */
+  { 0x000BA5, 0x000BA7 },                                       /* 0BA5-0BA7 */
+  { 0x000BAB, 0x000BAD },                                       /* 0BAB-0BAD */
+  { 0x000BB6           },                                            /* 0BB6 */
+  { 0x000BBA, 0x000BBD },                                       /* 0BBA-0BBD */
+  { 0x000BC3, 0x000BC5 },                                       /* 0BC3-0BC5 */
+  { 0x000BC9           },                                            /* 0BC9 */
+  { 0x000BCE, 0x000BD6 },                                       /* 0BCE-0BD6 */
+  { 0x000BD8, 0x000BE6 },                                       /* 0BD8-0BE6 */
+  { 0x000BF3, 0x000C00 },                                       /* 0BF3-0C00 */
+  { 0x000C04           },                                            /* 0C04 */
+  { 0x000C0D           },                                            /* 0C0D */
+  { 0x000C11           },                                            /* 0C11 */
+  { 0x000C29           },                                            /* 0C29 */
+  { 0x000C34           },                                            /* 0C34 */
+  { 0x000C3A, 0x000C3D },                                       /* 0C3A-0C3D */
+  { 0x000C45           },                                            /* 0C45 */
+  { 0x000C49           },                                            /* 0C49 */
+  { 0x000C4E, 0x000C54 },                                       /* 0C4E-0C54 */
+  { 0x000C57, 0x000C5F },                                       /* 0C57-0C5F */
+  { 0x000C62, 0x000C65 },                                       /* 0C62-0C65 */
+  { 0x000C70, 0x000C81 },                                       /* 0C70-0C81 */
+  { 0x000C84           },                                            /* 0C84 */
+  { 0x000C8D           },                                            /* 0C8D */
+  { 0x000C91           },                                            /* 0C91 */
+  { 0x000CA9           },                                            /* 0CA9 */
+  { 0x000CB4           },                                            /* 0CB4 */
+  { 0x000CBA, 0x000CBD },                                       /* 0CBA-0CBD */
+  { 0x000CC5           },                                            /* 0CC5 */
+  { 0x000CC9           },                                            /* 0CC9 */
+  { 0x000CCE, 0x000CD4 },                                       /* 0CCE-0CD4 */
+  { 0x000CD7, 0x000CDD },                                       /* 0CD7-0CDD */
+  { 0x000CDF           },                                            /* 0CDF */
+  { 0x000CE2, 0x000CE5 },                                       /* 0CE2-0CE5 */
+  { 0x000CF0, 0x000D01 },                                       /* 0CF0-0D01 */
+  { 0x000D04           },                                            /* 0D04 */
+  { 0x000D0D           },                                            /* 0D0D */
+  { 0x000D11           },                                            /* 0D11 */
+  { 0x000D29           },                                            /* 0D29 */
+  { 0x000D3A, 0x000D3D },                                       /* 0D3A-0D3D */
+  { 0x000D44, 0x000D45 },                                       /* 0D44-0D45 */
+  { 0x000D49           },                                            /* 0D49 */
+  { 0x000D4E, 0x000D56 },                                       /* 0D4E-0D56 */
+  { 0x000D58, 0x000D5F },                                       /* 0D58-0D5F */
+  { 0x000D62, 0x000D65 },                                       /* 0D62-0D65 */
+  { 0x000D70, 0x000D81 },                                       /* 0D70-0D81 */
+  { 0x000D84           },                                            /* 0D84 */
+  { 0x000D97, 0x000D99 },                                       /* 0D97-0D99 */
+  { 0x000DB2           },                                            /* 0DB2 */
+  { 0x000DBC           },                                            /* 0DBC */
+  { 0x000DBE, 0x000DBF },                                       /* 0DBE-0DBF */
+  { 0x000DC7, 0x000DC9 },                                       /* 0DC7-0DC9 */
+  { 0x000DCB, 0x000DCE },                                       /* 0DCB-0DCE */
+  { 0x000DD5           },                                            /* 0DD5 */
+  { 0x000DD7           },                                            /* 0DD7 */
+  { 0x000DE0, 0x000DF1 },                                       /* 0DE0-0DF1 */
+  { 0x000DF5, 0x000E00 },                                       /* 0DF5-0E00 */
+  { 0x000E3B, 0x000E3E },                                       /* 0E3B-0E3E */
+  { 0x000E5C, 0x000E80 },                                       /* 0E5C-0E80 */
+  { 0x000E83           },                                            /* 0E83 */
+  { 0x000E85, 0x000E86 },                                       /* 0E85-0E86 */
+  { 0x000E89           },                                            /* 0E89 */
+  { 0x000E8B, 0x000E8C },                                       /* 0E8B-0E8C */
+  { 0x000E8E, 0x000E93 },                                       /* 0E8E-0E93 */
+  { 0x000E98           },                                            /* 0E98 */
+  { 0x000EA0           },                                            /* 0EA0 */
+  { 0x000EA4           },                                            /* 0EA4 */
+  { 0x000EA6           },                                            /* 0EA6 */
+  { 0x000EA8, 0x000EA9 },                                       /* 0EA8-0EA9 */
+  { 0x000EAC           },                                            /* 0EAC */
+  { 0x000EBA           },                                            /* 0EBA */
+  { 0x000EBE, 0x000EBF },                                       /* 0EBE-0EBF */
+  { 0x000EC5           },                                            /* 0EC5 */
+  { 0x000EC7           },                                            /* 0EC7 */
+  { 0x000ECE, 0x000ECF },                                       /* 0ECE-0ECF */
+  { 0x000EDA, 0x000EDB },                                       /* 0EDA-0EDB */
+  { 0x000EDE, 0x000EFF },                                       /* 0EDE-0EFF */
+  { 0x000F48           },                                            /* 0F48 */
+  { 0x000F6B, 0x000F70 },                                       /* 0F6B-0F70 */
+  { 0x000F8C, 0x000F8F },                                       /* 0F8C-0F8F */
+  { 0x000F98           },                                            /* 0F98 */
+  { 0x000FBD           },                                            /* 0FBD */
+  { 0x000FCD, 0x000FCE },                                       /* 0FCD-0FCE */
+  { 0x000FD0, 0x000FFF },                                       /* 0FD0-0FFF */
+  { 0x001022           },                                            /* 1022 */
+  { 0x001028           },                                            /* 1028 */
+  { 0x00102B           },                                            /* 102B */
+  { 0x001033, 0x001035 },                                       /* 1033-1035 */
+  { 0x00103A, 0x00103F },                                       /* 103A-103F */
+  { 0x00105A, 0x00109F },                                       /* 105A-109F */
+  { 0x0010C6, 0x0010CF },                                       /* 10C6-10CF */
+  { 0x0010F9, 0x0010FA },                                       /* 10F9-10FA */
+  { 0x0010FC, 0x0010FF },                                       /* 10FC-10FF */
+  { 0x00115A, 0x00115E },                                       /* 115A-115E */
+  { 0x0011A3, 0x0011A7 },                                       /* 11A3-11A7 */
+  { 0x0011FA, 0x0011FF },                                       /* 11FA-11FF */
+  { 0x001207           },                                            /* 1207 */
+  { 0x001247           },                                            /* 1247 */
+  { 0x001249           },                                            /* 1249 */
+  { 0x00124E, 0x00124F },                                       /* 124E-124F */
+  { 0x001257           },                                            /* 1257 */
+  { 0x001259           },                                            /* 1259 */
+  { 0x00125E, 0x00125F },                                       /* 125E-125F */
+  { 0x001287           },                                            /* 1287 */
+  { 0x001289           },                                            /* 1289 */
+  { 0x00128E, 0x00128F },                                       /* 128E-128F */
+  { 0x0012AF           },                                            /* 12AF */
+  { 0x0012B1           },                                            /* 12B1 */
+  { 0x0012B6, 0x0012B7 },                                       /* 12B6-12B7 */
+  { 0x0012BF           },                                            /* 12BF */
+  { 0x0012C1           },                                            /* 12C1 */
+  { 0x0012C6, 0x0012C7 },                                       /* 12C6-12C7 */
+  { 0x0012CF           },                                            /* 12CF */
+  { 0x0012D7           },                                            /* 12D7 */
+  { 0x0012EF           },                                            /* 12EF */
+  { 0x00130F           },                                            /* 130F */
+  { 0x001311           },                                            /* 1311 */
+  { 0x001316, 0x001317 },                                       /* 1316-1317 */
+  { 0x00131F           },                                            /* 131F */
+  { 0x001347           },                                            /* 1347 */
+  { 0x00135B, 0x001360 },                                       /* 135B-1360 */
+  { 0x00137D, 0x00139F },                                       /* 137D-139F */
+  { 0x0013F5, 0x001400 },                                       /* 13F5-1400 */
+  { 0x001677, 0x00167F },                                       /* 1677-167F */
+  { 0x00169D, 0x00169F },                                       /* 169D-169F */
+  { 0x0016F1, 0x0016FF },                                       /* 16F1-16FF */
+  { 0x00170D           },                                            /* 170D */
+  { 0x001715, 0x00171F },                                       /* 1715-171F */
+  { 0x001737, 0x00173F },                                       /* 1737-173F */
+  { 0x001754, 0x00175F },                                       /* 1754-175F */
+  { 0x00176D           },                                            /* 176D */
+  { 0x001771           },                                            /* 1771 */
+  { 0x001774, 0x00177F },                                       /* 1774-177F */
+  { 0x0017DD, 0x0017DF },                                       /* 17DD-17DF */
+  { 0x0017EA, 0x0017FF },                                       /* 17EA-17FF */
+  { 0x00180F           },                                            /* 180F */
+  { 0x00181A, 0x00181F },                                       /* 181A-181F */
+  { 0x001878, 0x00187F },                                       /* 1878-187F */
+  { 0x0018AA, 0x001DFF },                                       /* 18AA-1DFF */
+  { 0x001E9C, 0x001E9F },                                       /* 1E9C-1E9F */
+  { 0x001EFA, 0x001EFF },                                       /* 1EFA-1EFF */
+  { 0x001F16, 0x001F17 },                                       /* 1F16-1F17 */
+  { 0x001F1E, 0x001F1F },                                       /* 1F1E-1F1F */
+  { 0x001F46, 0x001F47 },                                       /* 1F46-1F47 */
+  { 0x001F4E, 0x001F4F },                                       /* 1F4E-1F4F */
+  { 0x001F58           },                                            /* 1F58 */
+  { 0x001F5A           },                                            /* 1F5A */
+  { 0x001F5C           },                                            /* 1F5C */
+  { 0x001F5E           },                                            /* 1F5E */
+  { 0x001F7E, 0x001F7F },                                       /* 1F7E-1F7F */
+  { 0x001FB5           },                                            /* 1FB5 */
+  { 0x001FC5           },                                            /* 1FC5 */
+  { 0x001FD4, 0x001FD5 },                                       /* 1FD4-1FD5 */
+  { 0x001FDC           },                                            /* 1FDC */
+  { 0x001FF0, 0x001FF1 },                                       /* 1FF0-1FF1 */
+  { 0x001FF5           },                                            /* 1FF5 */
+  { 0x001FFF           },                                            /* 1FFF */
+  { 0x002053, 0x002056 },                                       /* 2053-2056 */
+  { 0x002058, 0x00205E },                                       /* 2058-205E */
+  { 0x002064, 0x002069 },                                       /* 2064-2069 */
+  { 0x002072, 0x002073 },                                       /* 2072-2073 */
+  { 0x00208F, 0x00209F },                                       /* 208F-209F */
+  { 0x0020B2, 0x0020CF },                                       /* 20B2-20CF */
+  { 0x0020EB, 0x0020FF },                                       /* 20EB-20FF */
+  { 0x00213B, 0x00213C },                                       /* 213B-213C */
+  { 0x00214C, 0x002152 },                                       /* 214C-2152 */
+  { 0x002184, 0x00218F },                                       /* 2184-218F */
+  { 0x0023CF, 0x0023FF },                                       /* 23CF-23FF */
+  { 0x002427, 0x00243F },                                       /* 2427-243F */
+  { 0x00244B, 0x00245F },                                       /* 244B-245F */
+  { 0x0024FF           },                                            /* 24FF */
+  { 0x002614, 0x002615 },                                       /* 2614-2615 */
+  { 0x002618           },                                            /* 2618 */
+  { 0x00267E, 0x00267F },                                       /* 267E-267F */
+  { 0x00268A, 0x002700 },                                       /* 268A-2700 */
+  { 0x002705           },                                            /* 2705 */
+  { 0x00270A, 0x00270B },                                       /* 270A-270B */
+  { 0x002728           },                                            /* 2728 */
+  { 0x00274C           },                                            /* 274C */
+  { 0x00274E           },                                            /* 274E */
+  { 0x002753, 0x002755 },                                       /* 2753-2755 */
+  { 0x002757           },                                            /* 2757 */
+  { 0x00275F, 0x002760 },                                       /* 275F-2760 */
+  { 0x002795, 0x002797 },                                       /* 2795-2797 */
+  { 0x0027B0           },                                            /* 27B0 */
+  { 0x0027BF, 0x0027CF },                                       /* 27BF-27CF */
+  { 0x0027EC, 0x0027EF },                                       /* 27EC-27EF */
+  { 0x002B00, 0x002E7F },                                       /* 2B00-2E7F */
+  { 0x002E9A           },                                            /* 2E9A */
+  { 0x002EF4, 0x002EFF },                                       /* 2EF4-2EFF */
+  { 0x002FD6, 0x002FEF },                                       /* 2FD6-2FEF */
+  { 0x002FFC, 0x002FFF },                                       /* 2FFC-2FFF */
+  { 0x003040           },                                            /* 3040 */
+  { 0x003097, 0x003098 },                                       /* 3097-3098 */
+  { 0x003100, 0x003104 },                                       /* 3100-3104 */
+  { 0x00312D, 0x003130 },                                       /* 312D-3130 */
+  { 0x00318F           },                                            /* 318F */
+  { 0x0031B8, 0x0031EF },                                       /* 31B8-31EF */
+  { 0x00321D, 0x00321F },                                       /* 321D-321F */
+  { 0x003244, 0x003250 },                                       /* 3244-3250 */
+  { 0x00327C, 0x00327E },                                       /* 327C-327E */
+  { 0x0032CC, 0x0032CF },                                       /* 32CC-32CF */
+  { 0x0032FF           },                                            /* 32FF */
+  { 0x003377, 0x00337A },                                       /* 3377-337A */
+  { 0x0033DE, 0x0033DF },                                       /* 33DE-33DF */
+  { 0x0033FF           },                                            /* 33FF */
+  { 0x004DB6, 0x004DFF },                                       /* 4DB6-4DFF */
+  { 0x009FA6, 0x009FFF },                                       /* 9FA6-9FFF */
+  { 0x00A48D, 0x00A48F },                                       /* A48D-A48F */
+  { 0x00A4C7, 0x00ABFF },                                       /* A4C7-ABFF */
+  { 0x00D7A4, 0x00D7FF },                                       /* D7A4-D7FF */
+  { 0x00FA2E, 0x00FA2F },                                       /* FA2E-FA2F */
+  { 0x00FA6B, 0x00FAFF },                                       /* FA6B-FAFF */
+  { 0x00FB07, 0x00FB12 },                                       /* FB07-FB12 */
+  { 0x00FB18, 0x00FB1C },                                       /* FB18-FB1C */
+  { 0x00FB37           },                                            /* FB37 */
+  { 0x00FB3D           },                                            /* FB3D */
+  { 0x00FB3F           },                                            /* FB3F */
+  { 0x00FB42           },                                            /* FB42 */
+  { 0x00FB45           },                                            /* FB45 */
+  { 0x00FBB2, 0x00FBD2 },                                       /* FBB2-FBD2 */
+  { 0x00FD40, 0x00FD4F },                                       /* FD40-FD4F */
+  { 0x00FD90, 0x00FD91 },                                       /* FD90-FD91 */
+  { 0x00FDC8, 0x00FDCF },                                       /* FDC8-FDCF */
+  { 0x00FDFD, 0x00FDFF },                                       /* FDFD-FDFF */
+  { 0x00FE10, 0x00FE1F },                                       /* FE10-FE1F */
+  { 0x00FE24, 0x00FE2F },                                       /* FE24-FE2F */
+  { 0x00FE47, 0x00FE48 },                                       /* FE47-FE48 */
+  { 0x00FE53           },                                            /* FE53 */
+  { 0x00FE67           },                                            /* FE67 */
+  { 0x00FE6C, 0x00FE6F },                                       /* FE6C-FE6F */
+  { 0x00FE75           },                                            /* FE75 */
+  { 0x00FEFD, 0x00FEFE },                                       /* FEFD-FEFE */
+  { 0x00FF00           },                                            /* FF00 */
+  { 0x00FFBF, 0x00FFC1 },                                       /* FFBF-FFC1 */
+  { 0x00FFC8, 0x00FFC9 },                                       /* FFC8-FFC9 */
+  { 0x00FFD0, 0x00FFD1 },                                       /* FFD0-FFD1 */
+  { 0x00FFD8, 0x00FFD9 },                                       /* FFD8-FFD9 */
+  { 0x00FFDD, 0x00FFDF },                                       /* FFDD-FFDF */
+  { 0x00FFE7           },                                            /* FFE7 */
+  { 0x00FFEF, 0x00FFF8 },                                       /* FFEF-FFF8 */
+  { 0x010000, 0x0102FF },                                     /* 10000-102FF */
+  { 0x01031F           },                                           /* 1031F */
+  { 0x010324, 0x01032F },                                     /* 10324-1032F */
+  { 0x01034B, 0x0103FF },                                     /* 1034B-103FF */
+  { 0x010426, 0x010427 },                                     /* 10426-10427 */
+  { 0x01044E, 0x01CFFF },                                     /* 1044E-1CFFF */
+  { 0x01D0F6, 0x01D0FF },                                     /* 1D0F6-1D0FF */
+  { 0x01D127, 0x01D129 },                                     /* 1D127-1D129 */
+  { 0x01D1DE, 0x01D3FF },                                     /* 1D1DE-1D3FF */
+  { 0x01D455           },                                           /* 1D455 */
+  { 0x01D49D           },                                           /* 1D49D */
+  { 0x01D4A0, 0x01D4A1 },                                     /* 1D4A0-1D4A1 */
+  { 0x01D4A3, 0x01D4A4 },                                     /* 1D4A3-1D4A4 */
+  { 0x01D4A7, 0x01D4A8 },                                     /* 1D4A7-1D4A8 */
+  { 0x01D4AD           },                                           /* 1D4AD */
+  { 0x01D4BA           },                                           /* 1D4BA */
+  { 0x01D4BC           },                                           /* 1D4BC */
+  { 0x01D4C1           },                                           /* 1D4C1 */
+  { 0x01D4C4           },                                           /* 1D4C4 */
+  { 0x01D506           },                                           /* 1D506 */
+  { 0x01D50B, 0x01D50C },                                     /* 1D50B-1D50C */
+  { 0x01D515           },                                           /* 1D515 */
+  { 0x01D51D           },                                           /* 1D51D */
+  { 0x01D53A           },                                           /* 1D53A */
+  { 0x01D53F           },                                           /* 1D53F */
+  { 0x01D545           },                                           /* 1D545 */
+  { 0x01D547, 0x01D549 },                                     /* 1D547-1D549 */
+  { 0x01D551           },                                           /* 1D551 */
+  { 0x01D6A4, 0x01D6A7 },                                     /* 1D6A4-1D6A7 */
+  { 0x01D7CA, 0x01D7CD },                                     /* 1D7CA-1D7CD */
+  { 0x01D800, 0x01FFFD },                                     /* 1D800-1FFFD */
+  { 0x02A6D7, 0x02F7FF },                                     /* 2A6D7-2F7FF */
+  { 0x02FA1E, 0x02FFFD },                                     /* 2FA1E-2FFFD */
+  { 0x030000, 0x03FFFD },                                     /* 30000-3FFFD */
+  { 0x040000, 0x04FFFD },                                     /* 40000-4FFFD */
+  { 0x050000, 0x05FFFD },                                     /* 50000-5FFFD */
+  { 0x060000, 0x06FFFD },                                     /* 60000-6FFFD */
+  { 0x070000, 0x07FFFD },                                     /* 70000-7FFFD */
+  { 0x080000, 0x08FFFD },                                     /* 80000-8FFFD */
+  { 0x090000, 0x09FFFD },                                     /* 90000-9FFFD */
+  { 0x0A0000, 0x0AFFFD },                                     /* A0000-AFFFD */
+  { 0x0B0000, 0x0BFFFD },                                     /* B0000-BFFFD */
+  { 0x0C0000, 0x0CFFFD },                                     /* C0000-CFFFD */
+  { 0x0D0000, 0x0DFFFD },                                     /* D0000-DFFFD */
+  { 0x0E0000           },                                           /* E0000 */
+  { 0x0E0002, 0x0E001F },                                     /* E0002-E001F */
+  { 0x0E0080, 0x0EFFFD },                                     /* E0080-EFFFD */
+  { 0 },
+};
+
+
+/*
+ * B.1 Commonly mapped to nothing
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_B_1[] = {
+  { 0x0000AD           },                          /* 00AD; ; Map to nothing */
+  { 0x00034F           },                          /* 034F; ; Map to nothing */
+  { 0x001806           },                          /* 1806; ; Map to nothing */
+  { 0x00180B           },                          /* 180B; ; Map to nothing */
+  { 0x00180C           },                          /* 180C; ; Map to nothing */
+  { 0x00180D           },                          /* 180D; ; Map to nothing */
+  { 0x00200B           },                          /* 200B; ; Map to nothing */
+  { 0x00200C           },                          /* 200C; ; Map to nothing */
+  { 0x00200D           },                          /* 200D; ; Map to nothing */
+  { 0x002060           },                          /* 2060; ; Map to nothing */
+  { 0x00FE00           },                          /* FE00; ; Map to nothing */
+  { 0x00FE01           },                          /* FE01; ; Map to nothing */
+  { 0x00FE02           },                          /* FE02; ; Map to nothing */
+  { 0x00FE03           },                          /* FE03; ; Map to nothing */
+  { 0x00FE04           },                          /* FE04; ; Map to nothing */
+  { 0x00FE05           },                          /* FE05; ; Map to nothing */
+  { 0x00FE06           },                          /* FE06; ; Map to nothing */
+  { 0x00FE07           },                          /* FE07; ; Map to nothing */
+  { 0x00FE08           },                          /* FE08; ; Map to nothing */
+  { 0x00FE09           },                          /* FE09; ; Map to nothing */
+  { 0x00FE0A           },                          /* FE0A; ; Map to nothing */
+  { 0x00FE0B           },                          /* FE0B; ; Map to nothing */
+  { 0x00FE0C           },                          /* FE0C; ; Map to nothing */
+  { 0x00FE0D           },                          /* FE0D; ; Map to nothing */
+  { 0x00FE0E           },                          /* FE0E; ; Map to nothing */
+  { 0x00FE0F           },                          /* FE0F; ; Map to nothing */
+  { 0x00FEFF           },                          /* FEFF; ; Map to nothing */
+  { 0 },
+};
+
+
+/*
+ * B.2 Mapping for case-folding used with NFKC
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_B_2[] = {
+  { 0x000041, 0, { 0x000061 }},                      /* 0041; 0061; Case map */
+  { 0x000042, 0, { 0x000062 }},                      /* 0042; 0062; Case map */
+  { 0x000043, 0, { 0x000063 }},                      /* 0043; 0063; Case map */
+  { 0x000044, 0, { 0x000064 }},                      /* 0044; 0064; Case map */
+  { 0x000045, 0, { 0x000065 }},                      /* 0045; 0065; Case map */
+  { 0x000046, 0, { 0x000066 }},                      /* 0046; 0066; Case map */
+  { 0x000047, 0, { 0x000067 }},                      /* 0047; 0067; Case map */
+  { 0x000048, 0, { 0x000068 }},                      /* 0048; 0068; Case map */
+  { 0x000049, 0, { 0x000069 }},                      /* 0049; 0069; Case map */
+  { 0x00004A, 0, { 0x00006A }},                      /* 004A; 006A; Case map */
+  { 0x00004B, 0, { 0x00006B }},                      /* 004B; 006B; Case map */
+  { 0x00004C, 0, { 0x00006C }},                      /* 004C; 006C; Case map */
+  { 0x00004D, 0, { 0x00006D }},                      /* 004D; 006D; Case map */
+  { 0x00004E, 0, { 0x00006E }},                      /* 004E; 006E; Case map */
+  { 0x00004F, 0, { 0x00006F }},                      /* 004F; 006F; Case map */
+  { 0x000050, 0, { 0x000070 }},                      /* 0050; 0070; Case map */
+  { 0x000051, 0, { 0x000071 }},                      /* 0051; 0071; Case map */
+  { 0x000052, 0, { 0x000072 }},                      /* 0052; 0072; Case map */
+  { 0x000053, 0, { 0x000073 }},                      /* 0053; 0073; Case map */
+  { 0x000054, 0, { 0x000074 }},                      /* 0054; 0074; Case map */
+  { 0x000055, 0, { 0x000075 }},                      /* 0055; 0075; Case map */
+  { 0x000056, 0, { 0x000076 }},                      /* 0056; 0076; Case map */
+  { 0x000057, 0, { 0x000077 }},                      /* 0057; 0077; Case map */
+  { 0x000058, 0, { 0x000078 }},                      /* 0058; 0078; Case map */
+  { 0x000059, 0, { 0x000079 }},                      /* 0059; 0079; Case map */
+  { 0x00005A, 0, { 0x00007A }},                      /* 005A; 007A; Case map */
+  { 0x0000B5, 0, { 0x0003BC }},                      /* 00B5; 03BC; Case map */
+  { 0x0000C0, 0, { 0x0000E0 }},                      /* 00C0; 00E0; Case map */
+  { 0x0000C1, 0, { 0x0000E1 }},                      /* 00C1; 00E1; Case map */
+  { 0x0000C2, 0, { 0x0000E2 }},                      /* 00C2; 00E2; Case map */
+  { 0x0000C3, 0, { 0x0000E3 }},                      /* 00C3; 00E3; Case map */
+  { 0x0000C4, 0, { 0x0000E4 }},                      /* 00C4; 00E4; Case map */
+  { 0x0000C5, 0, { 0x0000E5 }},                      /* 00C5; 00E5; Case map */
+  { 0x0000C6, 0, { 0x0000E6 }},                      /* 00C6; 00E6; Case map */
+  { 0x0000C7, 0, { 0x0000E7 }},                      /* 00C7; 00E7; Case map */
+  { 0x0000C8, 0, { 0x0000E8 }},                      /* 00C8; 00E8; Case map */
+  { 0x0000C9, 0, { 0x0000E9 }},                      /* 00C9; 00E9; Case map */
+  { 0x0000CA, 0, { 0x0000EA }},                      /* 00CA; 00EA; Case map */
+  { 0x0000CB, 0, { 0x0000EB }},                      /* 00CB; 00EB; Case map */
+  { 0x0000CC, 0, { 0x0000EC }},                      /* 00CC; 00EC; Case map */
+  { 0x0000CD, 0, { 0x0000ED }},                      /* 00CD; 00ED; Case map */
+  { 0x0000CE, 0, { 0x0000EE }},                      /* 00CE; 00EE; Case map */
+  { 0x0000CF, 0, { 0x0000EF }},                      /* 00CF; 00EF; Case map */
+  { 0x0000D0, 0, { 0x0000F0 }},                      /* 00D0; 00F0; Case map */
+  { 0x0000D1, 0, { 0x0000F1 }},                      /* 00D1; 00F1; Case map */
+  { 0x0000D2, 0, { 0x0000F2 }},                      /* 00D2; 00F2; Case map */
+  { 0x0000D3, 0, { 0x0000F3 }},                      /* 00D3; 00F3; Case map */
+  { 0x0000D4, 0, { 0x0000F4 }},                      /* 00D4; 00F4; Case map */
+  { 0x0000D5, 0, { 0x0000F5 }},                      /* 00D5; 00F5; Case map */
+  { 0x0000D6, 0, { 0x0000F6 }},                      /* 00D6; 00F6; Case map */
+  { 0x0000D8, 0, { 0x0000F8 }},                      /* 00D8; 00F8; Case map */
+  { 0x0000D9, 0, { 0x0000F9 }},                      /* 00D9; 00F9; Case map */
+  { 0x0000DA, 0, { 0x0000FA }},                      /* 00DA; 00FA; Case map */
+  { 0x0000DB, 0, { 0x0000FB }},                      /* 00DB; 00FB; Case map */
+  { 0x0000DC, 0, { 0x0000FC }},                      /* 00DC; 00FC; Case map */
+  { 0x0000DD, 0, { 0x0000FD }},                      /* 00DD; 00FD; Case map */
+  { 0x0000DE, 0, { 0x0000FE }},                      /* 00DE; 00FE; Case map */
+  { 0x0000DF, 0, { 0x000073,                    /* 00DF; 0073 0073; Case map */
+                   0x000073 }},
+  { 0x000100, 0, { 0x000101 }},                      /* 0100; 0101; Case map */
+  { 0x000102, 0, { 0x000103 }},                      /* 0102; 0103; Case map */
+  { 0x000104, 0, { 0x000105 }},                      /* 0104; 0105; Case map */
+  { 0x000106, 0, { 0x000107 }},                      /* 0106; 0107; Case map */
+  { 0x000108, 0, { 0x000109 }},                      /* 0108; 0109; Case map */
+  { 0x00010A, 0, { 0x00010B }},                      /* 010A; 010B; Case map */
+  { 0x00010C, 0, { 0x00010D }},                      /* 010C; 010D; Case map */
+  { 0x00010E, 0, { 0x00010F }},                      /* 010E; 010F; Case map */
+  { 0x000110, 0, { 0x000111 }},                      /* 0110; 0111; Case map */
+  { 0x000112, 0, { 0x000113 }},                      /* 0112; 0113; Case map */
+  { 0x000114, 0, { 0x000115 }},                      /* 0114; 0115; Case map */
+  { 0x000116, 0, { 0x000117 }},                      /* 0116; 0117; Case map */
+  { 0x000118, 0, { 0x000119 }},                      /* 0118; 0119; Case map */
+  { 0x00011A, 0, { 0x00011B }},                      /* 011A; 011B; Case map */
+  { 0x00011C, 0, { 0x00011D }},                      /* 011C; 011D; Case map */
+  { 0x00011E, 0, { 0x00011F }},                      /* 011E; 011F; Case map */
+  { 0x000120, 0, { 0x000121 }},                      /* 0120; 0121; Case map */
+  { 0x000122, 0, { 0x000123 }},                      /* 0122; 0123; Case map */
+  { 0x000124, 0, { 0x000125 }},                      /* 0124; 0125; Case map */
+  { 0x000126, 0, { 0x000127 }},                      /* 0126; 0127; Case map */
+  { 0x000128, 0, { 0x000129 }},                      /* 0128; 0129; Case map */
+  { 0x00012A, 0, { 0x00012B }},                      /* 012A; 012B; Case map */
+  { 0x00012C, 0, { 0x00012D }},                      /* 012C; 012D; Case map */
+  { 0x00012E, 0, { 0x00012F }},                      /* 012E; 012F; Case map */
+  { 0x000130, 0, { 0x000069,                    /* 0130; 0069 0307; Case map */
+                   0x000307 }},
+  { 0x000132, 0, { 0x000133 }},                      /* 0132; 0133; Case map */
+  { 0x000134, 0, { 0x000135 }},                      /* 0134; 0135; Case map */
+  { 0x000136, 0, { 0x000137 }},                      /* 0136; 0137; Case map */
+  { 0x000139, 0, { 0x00013A }},                      /* 0139; 013A; Case map */
+  { 0x00013B, 0, { 0x00013C }},                      /* 013B; 013C; Case map */
+  { 0x00013D, 0, { 0x00013E }},                      /* 013D; 013E; Case map */
+  { 0x00013F, 0, { 0x000140 }},                      /* 013F; 0140; Case map */
+  { 0x000141, 0, { 0x000142 }},                      /* 0141; 0142; Case map */
+  { 0x000143, 0, { 0x000144 }},                      /* 0143; 0144; Case map */
+  { 0x000145, 0, { 0x000146 }},                      /* 0145; 0146; Case map */
+  { 0x000147, 0, { 0x000148 }},                      /* 0147; 0148; Case map */
+  { 0x000149, 0, { 0x0002BC,                    /* 0149; 02BC 006E; Case map */
+                   0x00006E }},
+  { 0x00014A, 0, { 0x00014B }},                      /* 014A; 014B; Case map */
+  { 0x00014C, 0, { 0x00014D }},                      /* 014C; 014D; Case map */
+  { 0x00014E, 0, { 0x00014F }},                      /* 014E; 014F; Case map */
+  { 0x000150, 0, { 0x000151 }},                      /* 0150; 0151; Case map */
+  { 0x000152, 0, { 0x000153 }},                      /* 0152; 0153; Case map */
+  { 0x000154, 0, { 0x000155 }},                      /* 0154; 0155; Case map */
+  { 0x000156, 0, { 0x000157 }},                      /* 0156; 0157; Case map */
+  { 0x000158, 0, { 0x000159 }},                      /* 0158; 0159; Case map */
+  { 0x00015A, 0, { 0x00015B }},                      /* 015A; 015B; Case map */
+  { 0x00015C, 0, { 0x00015D }},                      /* 015C; 015D; Case map */
+  { 0x00015E, 0, { 0x00015F }},                      /* 015E; 015F; Case map */
+  { 0x000160, 0, { 0x000161 }},                      /* 0160; 0161; Case map */
+  { 0x000162, 0, { 0x000163 }},                      /* 0162; 0163; Case map */
+  { 0x000164, 0, { 0x000165 }},                      /* 0164; 0165; Case map */
+  { 0x000166, 0, { 0x000167 }},                      /* 0166; 0167; Case map */
+  { 0x000168, 0, { 0x000169 }},                      /* 0168; 0169; Case map */
+  { 0x00016A, 0, { 0x00016B }},                      /* 016A; 016B; Case map */
+  { 0x00016C, 0, { 0x00016D }},                      /* 016C; 016D; Case map */
+  { 0x00016E, 0, { 0x00016F }},                      /* 016E; 016F; Case map */
+  { 0x000170, 0, { 0x000171 }},                      /* 0170; 0171; Case map */
+  { 0x000172, 0, { 0x000173 }},                      /* 0172; 0173; Case map */
+  { 0x000174, 0, { 0x000175 }},                      /* 0174; 0175; Case map */
+  { 0x000176, 0, { 0x000177 }},                      /* 0176; 0177; Case map */
+  { 0x000178, 0, { 0x0000FF }},                      /* 0178; 00FF; Case map */
+  { 0x000179, 0, { 0x00017A }},                      /* 0179; 017A; Case map */
+  { 0x00017B, 0, { 0x00017C }},                      /* 017B; 017C; Case map */
+  { 0x00017D, 0, { 0x00017E }},                      /* 017D; 017E; Case map */
+  { 0x00017F, 0, { 0x000073 }},                      /* 017F; 0073; Case map */
+  { 0x000181, 0, { 0x000253 }},                      /* 0181; 0253; Case map */
+  { 0x000182, 0, { 0x000183 }},                      /* 0182; 0183; Case map */
+  { 0x000184, 0, { 0x000185 }},                      /* 0184; 0185; Case map */
+  { 0x000186, 0, { 0x000254 }},                      /* 0186; 0254; Case map */
+  { 0x000187, 0, { 0x000188 }},                      /* 0187; 0188; Case map */
+  { 0x000189, 0, { 0x000256 }},                      /* 0189; 0256; Case map */
+  { 0x00018A, 0, { 0x000257 }},                      /* 018A; 0257; Case map */
+  { 0x00018B, 0, { 0x00018C }},                      /* 018B; 018C; Case map */
+  { 0x00018E, 0, { 0x0001DD }},                      /* 018E; 01DD; Case map */
+  { 0x00018F, 0, { 0x000259 }},                      /* 018F; 0259; Case map */
+  { 0x000190, 0, { 0x00025B }},                      /* 0190; 025B; Case map */
+  { 0x000191, 0, { 0x000192 }},                      /* 0191; 0192; Case map */
+  { 0x000193, 0, { 0x000260 }},                      /* 0193; 0260; Case map */
+  { 0x000194, 0, { 0x000263 }},                      /* 0194; 0263; Case map */
+  { 0x000196, 0, { 0x000269 }},                      /* 0196; 0269; Case map */
+  { 0x000197, 0, { 0x000268 }},                      /* 0197; 0268; Case map */
+  { 0x000198, 0, { 0x000199 }},                      /* 0198; 0199; Case map */
+  { 0x00019C, 0, { 0x00026F }},                      /* 019C; 026F; Case map */
+  { 0x00019D, 0, { 0x000272 }},                      /* 019D; 0272; Case map */
+  { 0x00019F, 0, { 0x000275 }},                      /* 019F; 0275; Case map */
+  { 0x0001A0, 0, { 0x0001A1 }},                      /* 01A0; 01A1; Case map */
+  { 0x0001A2, 0, { 0x0001A3 }},                      /* 01A2; 01A3; Case map */
+  { 0x0001A4, 0, { 0x0001A5 }},                      /* 01A4; 01A5; Case map */
+  { 0x0001A6, 0, { 0x000280 }},                      /* 01A6; 0280; Case map */
+  { 0x0001A7, 0, { 0x0001A8 }},                      /* 01A7; 01A8; Case map */
+  { 0x0001A9, 0, { 0x000283 }},                      /* 01A9; 0283; Case map */
+  { 0x0001AC, 0, { 0x0001AD }},                      /* 01AC; 01AD; Case map */
+  { 0x0001AE, 0, { 0x000288 }},                      /* 01AE; 0288; Case map */
+  { 0x0001AF, 0, { 0x0001B0 }},                      /* 01AF; 01B0; Case map */
+  { 0x0001B1, 0, { 0x00028A }},                      /* 01B1; 028A; Case map */
+  { 0x0001B2, 0, { 0x00028B }},                      /* 01B2; 028B; Case map */
+  { 0x0001B3, 0, { 0x0001B4 }},                      /* 01B3; 01B4; Case map */
+  { 0x0001B5, 0, { 0x0001B6 }},                      /* 01B5; 01B6; Case map */
+  { 0x0001B7, 0, { 0x000292 }},                      /* 01B7; 0292; Case map */
+  { 0x0001B8, 0, { 0x0001B9 }},                      /* 01B8; 01B9; Case map */
+  { 0x0001BC, 0, { 0x0001BD }},                      /* 01BC; 01BD; Case map */
+  { 0x0001C4, 0, { 0x0001C6 }},                      /* 01C4; 01C6; Case map */
+  { 0x0001C5, 0, { 0x0001C6 }},                      /* 01C5; 01C6; Case map */
+  { 0x0001C7, 0, { 0x0001C9 }},                      /* 01C7; 01C9; Case map */
+  { 0x0001C8, 0, { 0x0001C9 }},                      /* 01C8; 01C9; Case map */
+  { 0x0001CA, 0, { 0x0001CC }},                      /* 01CA; 01CC; Case map */
+  { 0x0001CB, 0, { 0x0001CC }},                      /* 01CB; 01CC; Case map */
+  { 0x0001CD, 0, { 0x0001CE }},                      /* 01CD; 01CE; Case map */
+  { 0x0001CF, 0, { 0x0001D0 }},                      /* 01CF; 01D0; Case map */
+  { 0x0001D1, 0, { 0x0001D2 }},                      /* 01D1; 01D2; Case map */
+  { 0x0001D3, 0, { 0x0001D4 }},                      /* 01D3; 01D4; Case map */
+  { 0x0001D5, 0, { 0x0001D6 }},                      /* 01D5; 01D6; Case map */
+  { 0x0001D7, 0, { 0x0001D8 }},                      /* 01D7; 01D8; Case map */
+  { 0x0001D9, 0, { 0x0001DA }},                      /* 01D9; 01DA; Case map */
+  { 0x0001DB, 0, { 0x0001DC }},                      /* 01DB; 01DC; Case map */
+  { 0x0001DE, 0, { 0x0001DF }},                      /* 01DE; 01DF; Case map */
+  { 0x0001E0, 0, { 0x0001E1 }},                      /* 01E0; 01E1; Case map */
+  { 0x0001E2, 0, { 0x0001E3 }},                      /* 01E2; 01E3; Case map */
+  { 0x0001E4, 0, { 0x0001E5 }},                      /* 01E4; 01E5; Case map */
+  { 0x0001E6, 0, { 0x0001E7 }},                      /* 01E6; 01E7; Case map */
+  { 0x0001E8, 0, { 0x0001E9 }},                      /* 01E8; 01E9; Case map */
+  { 0x0001EA, 0, { 0x0001EB }},                      /* 01EA; 01EB; Case map */
+  { 0x0001EC, 0, { 0x0001ED }},                      /* 01EC; 01ED; Case map */
+  { 0x0001EE, 0, { 0x0001EF }},                      /* 01EE; 01EF; Case map */
+  { 0x0001F0, 0, { 0x00006A,                    /* 01F0; 006A 030C; Case map */
+                   0x00030C }},
+  { 0x0001F1, 0, { 0x0001F3 }},                      /* 01F1; 01F3; Case map */
+  { 0x0001F2, 0, { 0x0001F3 }},                      /* 01F2; 01F3; Case map */
+  { 0x0001F4, 0, { 0x0001F5 }},                      /* 01F4; 01F5; Case map */
+  { 0x0001F6, 0, { 0x000195 }},                      /* 01F6; 0195; Case map */
+  { 0x0001F7, 0, { 0x0001BF }},                      /* 01F7; 01BF; Case map */
+  { 0x0001F8, 0, { 0x0001F9 }},                      /* 01F8; 01F9; Case map */
+  { 0x0001FA, 0, { 0x0001FB }},                      /* 01FA; 01FB; Case map */
+  { 0x0001FC, 0, { 0x0001FD }},                      /* 01FC; 01FD; Case map */
+  { 0x0001FE, 0, { 0x0001FF }},                      /* 01FE; 01FF; Case map */
+  { 0x000200, 0, { 0x000201 }},                      /* 0200; 0201; Case map */
+  { 0x000202, 0, { 0x000203 }},                      /* 0202; 0203; Case map */
+  { 0x000204, 0, { 0x000205 }},                      /* 0204; 0205; Case map */
+  { 0x000206, 0, { 0x000207 }},                      /* 0206; 0207; Case map */
+  { 0x000208, 0, { 0x000209 }},                      /* 0208; 0209; Case map */
+  { 0x00020A, 0, { 0x00020B }},                      /* 020A; 020B; Case map */
+  { 0x00020C, 0, { 0x00020D }},                      /* 020C; 020D; Case map */
+  { 0x00020E, 0, { 0x00020F }},                      /* 020E; 020F; Case map */
+  { 0x000210, 0, { 0x000211 }},                      /* 0210; 0211; Case map */
+  { 0x000212, 0, { 0x000213 }},                      /* 0212; 0213; Case map */
+  { 0x000214, 0, { 0x000215 }},                      /* 0214; 0215; Case map */
+  { 0x000216, 0, { 0x000217 }},                      /* 0216; 0217; Case map */
+  { 0x000218, 0, { 0x000219 }},                      /* 0218; 0219; Case map */
+  { 0x00021A, 0, { 0x00021B }},                      /* 021A; 021B; Case map */
+  { 0x00021C, 0, { 0x00021D }},                      /* 021C; 021D; Case map */
+  { 0x00021E, 0, { 0x00021F }},                      /* 021E; 021F; Case map */
+  { 0x000220, 0, { 0x00019E }},                      /* 0220; 019E; Case map */
+  { 0x000222, 0, { 0x000223 }},                      /* 0222; 0223; Case map */
+  { 0x000224, 0, { 0x000225 }},                      /* 0224; 0225; Case map */
+  { 0x000226, 0, { 0x000227 }},                      /* 0226; 0227; Case map */
+  { 0x000228, 0, { 0x000229 }},                      /* 0228; 0229; Case map */
+  { 0x00022A, 0, { 0x00022B }},                      /* 022A; 022B; Case map */
+  { 0x00022C, 0, { 0x00022D }},                      /* 022C; 022D; Case map */
+  { 0x00022E, 0, { 0x00022F }},                      /* 022E; 022F; Case map */
+  { 0x000230, 0, { 0x000231 }},                      /* 0230; 0231; Case map */
+  { 0x000232, 0, { 0x000233 }},                      /* 0232; 0233; Case map */
+  { 0x000345, 0, { 0x0003B9 }},                      /* 0345; 03B9; Case map */
+  { 0x00037A, 0, { 0x000020,          /* 037A; 0020 03B9; Additional folding */
+                   0x0003B9 }},
+  { 0x000386, 0, { 0x0003AC }},                      /* 0386; 03AC; Case map */
+  { 0x000388, 0, { 0x0003AD }},                      /* 0388; 03AD; Case map */
+  { 0x000389, 0, { 0x0003AE }},                      /* 0389; 03AE; Case map */
+  { 0x00038A, 0, { 0x0003AF }},                      /* 038A; 03AF; Case map */
+  { 0x00038C, 0, { 0x0003CC }},                      /* 038C; 03CC; Case map */
+  { 0x00038E, 0, { 0x0003CD }},                      /* 038E; 03CD; Case map */
+  { 0x00038F, 0, { 0x0003CE }},                      /* 038F; 03CE; Case map */
+  { 0x000390, 0, { 0x0003B9,               /* 0390; 03B9 0308 0301; Case map */
+                   0x000308, 0x000301 }},
+  { 0x000391, 0, { 0x0003B1 }},                      /* 0391; 03B1; Case map */
+  { 0x000392, 0, { 0x0003B2 }},                      /* 0392; 03B2; Case map */
+  { 0x000393, 0, { 0x0003B3 }},                      /* 0393; 03B3; Case map */
+  { 0x000394, 0, { 0x0003B4 }},                      /* 0394; 03B4; Case map */
+  { 0x000395, 0, { 0x0003B5 }},                      /* 0395; 03B5; Case map */
+  { 0x000396, 0, { 0x0003B6 }},                      /* 0396; 03B6; Case map */
+  { 0x000397, 0, { 0x0003B7 }},                      /* 0397; 03B7; Case map */
+  { 0x000398, 0, { 0x0003B8 }},                      /* 0398; 03B8; Case map */
+  { 0x000399, 0, { 0x0003B9 }},                      /* 0399; 03B9; Case map */
+  { 0x00039A, 0, { 0x0003BA }},                      /* 039A; 03BA; Case map */
+  { 0x00039B, 0, { 0x0003BB }},                      /* 039B; 03BB; Case map */
+  { 0x00039C, 0, { 0x0003BC }},                      /* 039C; 03BC; Case map */
+  { 0x00039D, 0, { 0x0003BD }},                      /* 039D; 03BD; Case map */
+  { 0x00039E, 0, { 0x0003BE }},                      /* 039E; 03BE; Case map */
+  { 0x00039F, 0, { 0x0003BF }},                      /* 039F; 03BF; Case map */
+  { 0x0003A0, 0, { 0x0003C0 }},                      /* 03A0; 03C0; Case map */
+  { 0x0003A1, 0, { 0x0003C1 }},                      /* 03A1; 03C1; Case map */
+  { 0x0003A3, 0, { 0x0003C3 }},                      /* 03A3; 03C3; Case map */
+  { 0x0003A4, 0, { 0x0003C4 }},                      /* 03A4; 03C4; Case map */
+  { 0x0003A5, 0, { 0x0003C5 }},                      /* 03A5; 03C5; Case map */
+  { 0x0003A6, 0, { 0x0003C6 }},                      /* 03A6; 03C6; Case map */
+  { 0x0003A7, 0, { 0x0003C7 }},                      /* 03A7; 03C7; Case map */
+  { 0x0003A8, 0, { 0x0003C8 }},                      /* 03A8; 03C8; Case map */
+  { 0x0003A9, 0, { 0x0003C9 }},                      /* 03A9; 03C9; Case map */
+  { 0x0003AA, 0, { 0x0003CA }},                      /* 03AA; 03CA; Case map */
+  { 0x0003AB, 0, { 0x0003CB }},                      /* 03AB; 03CB; Case map */
+  { 0x0003B0, 0, { 0x0003C5,               /* 03B0; 03C5 0308 0301; Case map */
+                   0x000308, 0x000301 }},
+  { 0x0003C2, 0, { 0x0003C3 }},                      /* 03C2; 03C3; Case map */
+  { 0x0003D0, 0, { 0x0003B2 }},                      /* 03D0; 03B2; Case map */
+  { 0x0003D1, 0, { 0x0003B8 }},                      /* 03D1; 03B8; Case map */
+  { 0x0003D2, 0, { 0x0003C5 }},            /* 03D2; 03C5; Additional folding */
+  { 0x0003D3, 0, { 0x0003CD }},            /* 03D3; 03CD; Additional folding */
+  { 0x0003D4, 0, { 0x0003CB }},            /* 03D4; 03CB; Additional folding */
+  { 0x0003D5, 0, { 0x0003C6 }},                      /* 03D5; 03C6; Case map */
+  { 0x0003D6, 0, { 0x0003C0 }},                      /* 03D6; 03C0; Case map */
+  { 0x0003D8, 0, { 0x0003D9 }},                      /* 03D8; 03D9; Case map */
+  { 0x0003DA, 0, { 0x0003DB }},                      /* 03DA; 03DB; Case map */
+  { 0x0003DC, 0, { 0x0003DD }},                      /* 03DC; 03DD; Case map */
+  { 0x0003DE, 0, { 0x0003DF }},                      /* 03DE; 03DF; Case map */
+  { 0x0003E0, 0, { 0x0003E1 }},                      /* 03E0; 03E1; Case map */
+  { 0x0003E2, 0, { 0x0003E3 }},                      /* 03E2; 03E3; Case map */
+  { 0x0003E4, 0, { 0x0003E5 }},                      /* 03E4; 03E5; Case map */
+  { 0x0003E6, 0, { 0x0003E7 }},                      /* 03E6; 03E7; Case map */
+  { 0x0003E8, 0, { 0x0003E9 }},                      /* 03E8; 03E9; Case map */
+  { 0x0003EA, 0, { 0x0003EB }},                      /* 03EA; 03EB; Case map */
+  { 0x0003EC, 0, { 0x0003ED }},                      /* 03EC; 03ED; Case map */
+  { 0x0003EE, 0, { 0x0003EF }},                      /* 03EE; 03EF; Case map */
+  { 0x0003F0, 0, { 0x0003BA }},                      /* 03F0; 03BA; Case map */
+  { 0x0003F1, 0, { 0x0003C1 }},                      /* 03F1; 03C1; Case map */
+  { 0x0003F2, 0, { 0x0003C3 }},                      /* 03F2; 03C3; Case map */
+  { 0x0003F4, 0, { 0x0003B8 }},                      /* 03F4; 03B8; Case map */
+  { 0x0003F5, 0, { 0x0003B5 }},                      /* 03F5; 03B5; Case map */
+  { 0x000400, 0, { 0x000450 }},                      /* 0400; 0450; Case map */
+  { 0x000401, 0, { 0x000451 }},                      /* 0401; 0451; Case map */
+  { 0x000402, 0, { 0x000452 }},                      /* 0402; 0452; Case map */
+  { 0x000403, 0, { 0x000453 }},                      /* 0403; 0453; Case map */
+  { 0x000404, 0, { 0x000454 }},                      /* 0404; 0454; Case map */
+  { 0x000405, 0, { 0x000455 }},                      /* 0405; 0455; Case map */
+  { 0x000406, 0, { 0x000456 }},                      /* 0406; 0456; Case map */
+  { 0x000407, 0, { 0x000457 }},                      /* 0407; 0457; Case map */
+  { 0x000408, 0, { 0x000458 }},                      /* 0408; 0458; Case map */
+  { 0x000409, 0, { 0x000459 }},                      /* 0409; 0459; Case map */
+  { 0x00040A, 0, { 0x00045A }},                      /* 040A; 045A; Case map */
+  { 0x00040B, 0, { 0x00045B }},                      /* 040B; 045B; Case map */
+  { 0x00040C, 0, { 0x00045C }},                      /* 040C; 045C; Case map */
+  { 0x00040D, 0, { 0x00045D }},                      /* 040D; 045D; Case map */
+  { 0x00040E, 0, { 0x00045E }},                      /* 040E; 045E; Case map */
+  { 0x00040F, 0, { 0x00045F }},                      /* 040F; 045F; Case map */
+  { 0x000410, 0, { 0x000430 }},                      /* 0410; 0430; Case map */
+  { 0x000411, 0, { 0x000431 }},                      /* 0411; 0431; Case map */
+  { 0x000412, 0, { 0x000432 }},                      /* 0412; 0432; Case map */
+  { 0x000413, 0, { 0x000433 }},                      /* 0413; 0433; Case map */
+  { 0x000414, 0, { 0x000434 }},                      /* 0414; 0434; Case map */
+  { 0x000415, 0, { 0x000435 }},                      /* 0415; 0435; Case map */
+  { 0x000416, 0, { 0x000436 }},                      /* 0416; 0436; Case map */
+  { 0x000417, 0, { 0x000437 }},                      /* 0417; 0437; Case map */
+  { 0x000418, 0, { 0x000438 }},                      /* 0418; 0438; Case map */
+  { 0x000419, 0, { 0x000439 }},                      /* 0419; 0439; Case map */
+  { 0x00041A, 0, { 0x00043A }},                      /* 041A; 043A; Case map */
+  { 0x00041B, 0, { 0x00043B }},                      /* 041B; 043B; Case map */
+  { 0x00041C, 0, { 0x00043C }},                      /* 041C; 043C; Case map */
+  { 0x00041D, 0, { 0x00043D }},                      /* 041D; 043D; Case map */
+  { 0x00041E, 0, { 0x00043E }},                      /* 041E; 043E; Case map */
+  { 0x00041F, 0, { 0x00043F }},                      /* 041F; 043F; Case map */
+  { 0x000420, 0, { 0x000440 }},                      /* 0420; 0440; Case map */
+  { 0x000421, 0, { 0x000441 }},                      /* 0421; 0441; Case map */
+  { 0x000422, 0, { 0x000442 }},                      /* 0422; 0442; Case map */
+  { 0x000423, 0, { 0x000443 }},                      /* 0423; 0443; Case map */
+  { 0x000424, 0, { 0x000444 }},                      /* 0424; 0444; Case map */
+  { 0x000425, 0, { 0x000445 }},                      /* 0425; 0445; Case map */
+  { 0x000426, 0, { 0x000446 }},                      /* 0426; 0446; Case map */
+  { 0x000427, 0, { 0x000447 }},                      /* 0427; 0447; Case map */
+  { 0x000428, 0, { 0x000448 }},                      /* 0428; 0448; Case map */
+  { 0x000429, 0, { 0x000449 }},                      /* 0429; 0449; Case map */
+  { 0x00042A, 0, { 0x00044A }},                      /* 042A; 044A; Case map */
+  { 0x00042B, 0, { 0x00044B }},                      /* 042B; 044B; Case map */
+  { 0x00042C, 0, { 0x00044C }},                      /* 042C; 044C; Case map */
+  { 0x00042D, 0, { 0x00044D }},                      /* 042D; 044D; Case map */
+  { 0x00042E, 0, { 0x00044E }},                      /* 042E; 044E; Case map */
+  { 0x00042F, 0, { 0x00044F }},                      /* 042F; 044F; Case map */
+  { 0x000460, 0, { 0x000461 }},                      /* 0460; 0461; Case map */
+  { 0x000462, 0, { 0x000463 }},                      /* 0462; 0463; Case map */
+  { 0x000464, 0, { 0x000465 }},                      /* 0464; 0465; Case map */
+  { 0x000466, 0, { 0x000467 }},                      /* 0466; 0467; Case map */
+  { 0x000468, 0, { 0x000469 }},                      /* 0468; 0469; Case map */
+  { 0x00046A, 0, { 0x00046B }},                      /* 046A; 046B; Case map */
+  { 0x00046C, 0, { 0x00046D }},                      /* 046C; 046D; Case map */
+  { 0x00046E, 0, { 0x00046F }},                      /* 046E; 046F; Case map */
+  { 0x000470, 0, { 0x000471 }},                      /* 0470; 0471; Case map */
+  { 0x000472, 0, { 0x000473 }},                      /* 0472; 0473; Case map */
+  { 0x000474, 0, { 0x000475 }},                      /* 0474; 0475; Case map */
+  { 0x000476, 0, { 0x000477 }},                      /* 0476; 0477; Case map */
+  { 0x000478, 0, { 0x000479 }},                      /* 0478; 0479; Case map */
+  { 0x00047A, 0, { 0x00047B }},                      /* 047A; 047B; Case map */
+  { 0x00047C, 0, { 0x00047D }},                      /* 047C; 047D; Case map */
+  { 0x00047E, 0, { 0x00047F }},                      /* 047E; 047F; Case map */
+  { 0x000480, 0, { 0x000481 }},                      /* 0480; 0481; Case map */
+  { 0x00048A, 0, { 0x00048B }},                      /* 048A; 048B; Case map */
+  { 0x00048C, 0, { 0x00048D }},                      /* 048C; 048D; Case map */
+  { 0x00048E, 0, { 0x00048F }},                      /* 048E; 048F; Case map */
+  { 0x000490, 0, { 0x000491 }},                      /* 0490; 0491; Case map */
+  { 0x000492, 0, { 0x000493 }},                      /* 0492; 0493; Case map */
+  { 0x000494, 0, { 0x000495 }},                      /* 0494; 0495; Case map */
+  { 0x000496, 0, { 0x000497 }},                      /* 0496; 0497; Case map */
+  { 0x000498, 0, { 0x000499 }},                      /* 0498; 0499; Case map */
+  { 0x00049A, 0, { 0x00049B }},                      /* 049A; 049B; Case map */
+  { 0x00049C, 0, { 0x00049D }},                      /* 049C; 049D; Case map */
+  { 0x00049E, 0, { 0x00049F }},                      /* 049E; 049F; Case map */
+  { 0x0004A0, 0, { 0x0004A1 }},                      /* 04A0; 04A1; Case map */
+  { 0x0004A2, 0, { 0x0004A3 }},                      /* 04A2; 04A3; Case map */
+  { 0x0004A4, 0, { 0x0004A5 }},                      /* 04A4; 04A5; Case map */
+  { 0x0004A6, 0, { 0x0004A7 }},                      /* 04A6; 04A7; Case map */
+  { 0x0004A8, 0, { 0x0004A9 }},                      /* 04A8; 04A9; Case map */
+  { 0x0004AA, 0, { 0x0004AB }},                      /* 04AA; 04AB; Case map */
+  { 0x0004AC, 0, { 0x0004AD }},                      /* 04AC; 04AD; Case map */
+  { 0x0004AE, 0, { 0x0004AF }},                      /* 04AE; 04AF; Case map */
+  { 0x0004B0, 0, { 0x0004B1 }},                      /* 04B0; 04B1; Case map */
+  { 0x0004B2, 0, { 0x0004B3 }},                      /* 04B2; 04B3; Case map */
+  { 0x0004B4, 0, { 0x0004B5 }},                      /* 04B4; 04B5; Case map */
+  { 0x0004B6, 0, { 0x0004B7 }},                      /* 04B6; 04B7; Case map */
+  { 0x0004B8, 0, { 0x0004B9 }},                      /* 04B8; 04B9; Case map */
+  { 0x0004BA, 0, { 0x0004BB }},                      /* 04BA; 04BB; Case map */
+  { 0x0004BC, 0, { 0x0004BD }},                      /* 04BC; 04BD; Case map */
+  { 0x0004BE, 0, { 0x0004BF }},                      /* 04BE; 04BF; Case map */
+  { 0x0004C1, 0, { 0x0004C2 }},                      /* 04C1; 04C2; Case map */
+  { 0x0004C3, 0, { 0x0004C4 }},                      /* 04C3; 04C4; Case map */
+  { 0x0004C5, 0, { 0x0004C6 }},                      /* 04C5; 04C6; Case map */
+  { 0x0004C7, 0, { 0x0004C8 }},                      /* 04C7; 04C8; Case map */
+  { 0x0004C9, 0, { 0x0004CA }},                      /* 04C9; 04CA; Case map */
+  { 0x0004CB, 0, { 0x0004CC }},                      /* 04CB; 04CC; Case map */
+  { 0x0004CD, 0, { 0x0004CE }},                      /* 04CD; 04CE; Case map */
+  { 0x0004D0, 0, { 0x0004D1 }},                      /* 04D0; 04D1; Case map */
+  { 0x0004D2, 0, { 0x0004D3 }},                      /* 04D2; 04D3; Case map */
+  { 0x0004D4, 0, { 0x0004D5 }},                      /* 04D4; 04D5; Case map */
+  { 0x0004D6, 0, { 0x0004D7 }},                      /* 04D6; 04D7; Case map */
+  { 0x0004D8, 0, { 0x0004D9 }},                      /* 04D8; 04D9; Case map */
+  { 0x0004DA, 0, { 0x0004DB }},                      /* 04DA; 04DB; Case map */
+  { 0x0004DC, 0, { 0x0004DD }},                      /* 04DC; 04DD; Case map */
+  { 0x0004DE, 0, { 0x0004DF }},                      /* 04DE; 04DF; Case map */
+  { 0x0004E0, 0, { 0x0004E1 }},                      /* 04E0; 04E1; Case map */
+  { 0x0004E2, 0, { 0x0004E3 }},                      /* 04E2; 04E3; Case map */
+  { 0x0004E4, 0, { 0x0004E5 }},                      /* 04E4; 04E5; Case map */
+  { 0x0004E6, 0, { 0x0004E7 }},                      /* 04E6; 04E7; Case map */
+  { 0x0004E8, 0, { 0x0004E9 }},                      /* 04E8; 04E9; Case map */
+  { 0x0004EA, 0, { 0x0004EB }},                      /* 04EA; 04EB; Case map */
+  { 0x0004EC, 0, { 0x0004ED }},                      /* 04EC; 04ED; Case map */
+  { 0x0004EE, 0, { 0x0004EF }},                      /* 04EE; 04EF; Case map */
+  { 0x0004F0, 0, { 0x0004F1 }},                      /* 04F0; 04F1; Case map */
+  { 0x0004F2, 0, { 0x0004F3 }},                      /* 04F2; 04F3; Case map */
+  { 0x0004F4, 0, { 0x0004F5 }},                      /* 04F4; 04F5; Case map */
+  { 0x0004F8, 0, { 0x0004F9 }},                      /* 04F8; 04F9; Case map */
+  { 0x000500, 0, { 0x000501 }},                      /* 0500; 0501; Case map */
+  { 0x000502, 0, { 0x000503 }},                      /* 0502; 0503; Case map */
+  { 0x000504, 0, { 0x000505 }},                      /* 0504; 0505; Case map */
+  { 0x000506, 0, { 0x000507 }},                      /* 0506; 0507; Case map */
+  { 0x000508, 0, { 0x000509 }},                      /* 0508; 0509; Case map */
+  { 0x00050A, 0, { 0x00050B }},                      /* 050A; 050B; Case map */
+  { 0x00050C, 0, { 0x00050D }},                      /* 050C; 050D; Case map */
+  { 0x00050E, 0, { 0x00050F }},                      /* 050E; 050F; Case map */
+  { 0x000531, 0, { 0x000561 }},                      /* 0531; 0561; Case map */
+  { 0x000532, 0, { 0x000562 }},                      /* 0532; 0562; Case map */
+  { 0x000533, 0, { 0x000563 }},                      /* 0533; 0563; Case map */
+  { 0x000534, 0, { 0x000564 }},                      /* 0534; 0564; Case map */
+  { 0x000535, 0, { 0x000565 }},                      /* 0535; 0565; Case map */
+  { 0x000536, 0, { 0x000566 }},                      /* 0536; 0566; Case map */
+  { 0x000537, 0, { 0x000567 }},                      /* 0537; 0567; Case map */
+  { 0x000538, 0, { 0x000568 }},                      /* 0538; 0568; Case map */
+  { 0x000539, 0, { 0x000569 }},                      /* 0539; 0569; Case map */
+  { 0x00053A, 0, { 0x00056A }},                      /* 053A; 056A; Case map */
+  { 0x00053B, 0, { 0x00056B }},                      /* 053B; 056B; Case map */
+  { 0x00053C, 0, { 0x00056C }},                      /* 053C; 056C; Case map */
+  { 0x00053D, 0, { 0x00056D }},                      /* 053D; 056D; Case map */
+  { 0x00053E, 0, { 0x00056E }},                      /* 053E; 056E; Case map */
+  { 0x00053F, 0, { 0x00056F }},                      /* 053F; 056F; Case map */
+  { 0x000540, 0, { 0x000570 }},                      /* 0540; 0570; Case map */
+  { 0x000541, 0, { 0x000571 }},                      /* 0541; 0571; Case map */
+  { 0x000542, 0, { 0x000572 }},                      /* 0542; 0572; Case map */
+  { 0x000543, 0, { 0x000573 }},                      /* 0543; 0573; Case map */
+  { 0x000544, 0, { 0x000574 }},                      /* 0544; 0574; Case map */
+  { 0x000545, 0, { 0x000575 }},                      /* 0545; 0575; Case map */
+  { 0x000546, 0, { 0x000576 }},                      /* 0546; 0576; Case map */
+  { 0x000547, 0, { 0x000577 }},                      /* 0547; 0577; Case map */
+  { 0x000548, 0, { 0x000578 }},                      /* 0548; 0578; Case map */
+  { 0x000549, 0, { 0x000579 }},                      /* 0549; 0579; Case map */
+  { 0x00054A, 0, { 0x00057A }},                      /* 054A; 057A; Case map */
+  { 0x00054B, 0, { 0x00057B }},                      /* 054B; 057B; Case map */
+  { 0x00054C, 0, { 0x00057C }},                      /* 054C; 057C; Case map */
+  { 0x00054D, 0, { 0x00057D }},                      /* 054D; 057D; Case map */
+  { 0x00054E, 0, { 0x00057E }},                      /* 054E; 057E; Case map */
+  { 0x00054F, 0, { 0x00057F }},                      /* 054F; 057F; Case map */
+  { 0x000550, 0, { 0x000580 }},                      /* 0550; 0580; Case map */
+  { 0x000551, 0, { 0x000581 }},                      /* 0551; 0581; Case map */
+  { 0x000552, 0, { 0x000582 }},                      /* 0552; 0582; Case map */
+  { 0x000553, 0, { 0x000583 }},                      /* 0553; 0583; Case map */
+  { 0x000554, 0, { 0x000584 }},                      /* 0554; 0584; Case map */
+  { 0x000555, 0, { 0x000585 }},                      /* 0555; 0585; Case map */
+  { 0x000556, 0, { 0x000586 }},                      /* 0556; 0586; Case map */
+  { 0x000587, 0, { 0x000565,                    /* 0587; 0565 0582; Case map */
+                   0x000582 }},
+  { 0x001E00, 0, { 0x001E01 }},                      /* 1E00; 1E01; Case map */
+  { 0x001E02, 0, { 0x001E03 }},                      /* 1E02; 1E03; Case map */
+  { 0x001E04, 0, { 0x001E05 }},                      /* 1E04; 1E05; Case map */
+  { 0x001E06, 0, { 0x001E07 }},                      /* 1E06; 1E07; Case map */
+  { 0x001E08, 0, { 0x001E09 }},                      /* 1E08; 1E09; Case map */
+  { 0x001E0A, 0, { 0x001E0B }},                      /* 1E0A; 1E0B; Case map */
+  { 0x001E0C, 0, { 0x001E0D }},                      /* 1E0C; 1E0D; Case map */
+  { 0x001E0E, 0, { 0x001E0F }},                      /* 1E0E; 1E0F; Case map */
+  { 0x001E10, 0, { 0x001E11 }},                      /* 1E10; 1E11; Case map */
+  { 0x001E12, 0, { 0x001E13 }},                      /* 1E12; 1E13; Case map */
+  { 0x001E14, 0, { 0x001E15 }},                      /* 1E14; 1E15; Case map */
+  { 0x001E16, 0, { 0x001E17 }},                      /* 1E16; 1E17; Case map */
+  { 0x001E18, 0, { 0x001E19 }},                      /* 1E18; 1E19; Case map */
+  { 0x001E1A, 0, { 0x001E1B }},                      /* 1E1A; 1E1B; Case map */
+  { 0x001E1C, 0, { 0x001E1D }},                      /* 1E1C; 1E1D; Case map */
+  { 0x001E1E, 0, { 0x001E1F }},                      /* 1E1E; 1E1F; Case map */
+  { 0x001E20, 0, { 0x001E21 }},                      /* 1E20; 1E21; Case map */
+  { 0x001E22, 0, { 0x001E23 }},                      /* 1E22; 1E23; Case map */
+  { 0x001E24, 0, { 0x001E25 }},                      /* 1E24; 1E25; Case map */
+  { 0x001E26, 0, { 0x001E27 }},                      /* 1E26; 1E27; Case map */
+  { 0x001E28, 0, { 0x001E29 }},                      /* 1E28; 1E29; Case map */
+  { 0x001E2A, 0, { 0x001E2B }},                      /* 1E2A; 1E2B; Case map */
+  { 0x001E2C, 0, { 0x001E2D }},                      /* 1E2C; 1E2D; Case map */
+  { 0x001E2E, 0, { 0x001E2F }},                      /* 1E2E; 1E2F; Case map */
+  { 0x001E30, 0, { 0x001E31 }},                      /* 1E30; 1E31; Case map */
+  { 0x001E32, 0, { 0x001E33 }},                      /* 1E32; 1E33; Case map */
+  { 0x001E34, 0, { 0x001E35 }},                      /* 1E34; 1E35; Case map */
+  { 0x001E36, 0, { 0x001E37 }},                      /* 1E36; 1E37; Case map */
+  { 0x001E38, 0, { 0x001E39 }},                      /* 1E38; 1E39; Case map */
+  { 0x001E3A, 0, { 0x001E3B }},                      /* 1E3A; 1E3B; Case map */
+  { 0x001E3C, 0, { 0x001E3D }},                      /* 1E3C; 1E3D; Case map */
+  { 0x001E3E, 0, { 0x001E3F }},                      /* 1E3E; 1E3F; Case map */
+  { 0x001E40, 0, { 0x001E41 }},                      /* 1E40; 1E41; Case map */
+  { 0x001E42, 0, { 0x001E43 }},                      /* 1E42; 1E43; Case map */
+  { 0x001E44, 0, { 0x001E45 }},                      /* 1E44; 1E45; Case map */
+  { 0x001E46, 0, { 0x001E47 }},                      /* 1E46; 1E47; Case map */
+  { 0x001E48, 0, { 0x001E49 }},                      /* 1E48; 1E49; Case map */
+  { 0x001E4A, 0, { 0x001E4B }},                      /* 1E4A; 1E4B; Case map */
+  { 0x001E4C, 0, { 0x001E4D }},                      /* 1E4C; 1E4D; Case map */
+  { 0x001E4E, 0, { 0x001E4F }},                      /* 1E4E; 1E4F; Case map */
+  { 0x001E50, 0, { 0x001E51 }},                      /* 1E50; 1E51; Case map */
+  { 0x001E52, 0, { 0x001E53 }},                      /* 1E52; 1E53; Case map */
+  { 0x001E54, 0, { 0x001E55 }},                      /* 1E54; 1E55; Case map */
+  { 0x001E56, 0, { 0x001E57 }},                      /* 1E56; 1E57; Case map */
+  { 0x001E58, 0, { 0x001E59 }},                      /* 1E58; 1E59; Case map */
+  { 0x001E5A, 0, { 0x001E5B }},                      /* 1E5A; 1E5B; Case map */
+  { 0x001E5C, 0, { 0x001E5D }},                      /* 1E5C; 1E5D; Case map */
+  { 0x001E5E, 0, { 0x001E5F }},                      /* 1E5E; 1E5F; Case map */
+  { 0x001E60, 0, { 0x001E61 }},                      /* 1E60; 1E61; Case map */
+  { 0x001E62, 0, { 0x001E63 }},                      /* 1E62; 1E63; Case map */
+  { 0x001E64, 0, { 0x001E65 }},                      /* 1E64; 1E65; Case map */
+  { 0x001E66, 0, { 0x001E67 }},                      /* 1E66; 1E67; Case map */
+  { 0x001E68, 0, { 0x001E69 }},                      /* 1E68; 1E69; Case map */
+  { 0x001E6A, 0, { 0x001E6B }},                      /* 1E6A; 1E6B; Case map */
+  { 0x001E6C, 0, { 0x001E6D }},                      /* 1E6C; 1E6D; Case map */
+  { 0x001E6E, 0, { 0x001E6F }},                      /* 1E6E; 1E6F; Case map */
+  { 0x001E70, 0, { 0x001E71 }},                      /* 1E70; 1E71; Case map */
+  { 0x001E72, 0, { 0x001E73 }},                      /* 1E72; 1E73; Case map */
+  { 0x001E74, 0, { 0x001E75 }},                      /* 1E74; 1E75; Case map */
+  { 0x001E76, 0, { 0x001E77 }},                      /* 1E76; 1E77; Case map */
+  { 0x001E78, 0, { 0x001E79 }},                      /* 1E78; 1E79; Case map */
+  { 0x001E7A, 0, { 0x001E7B }},                      /* 1E7A; 1E7B; Case map */
+  { 0x001E7C, 0, { 0x001E7D }},                      /* 1E7C; 1E7D; Case map */
+  { 0x001E7E, 0, { 0x001E7F }},                      /* 1E7E; 1E7F; Case map */
+  { 0x001E80, 0, { 0x001E81 }},                      /* 1E80; 1E81; Case map */
+  { 0x001E82, 0, { 0x001E83 }},                      /* 1E82; 1E83; Case map */
+  { 0x001E84, 0, { 0x001E85 }},                      /* 1E84; 1E85; Case map */
+  { 0x001E86, 0, { 0x001E87 }},                      /* 1E86; 1E87; Case map */
+  { 0x001E88, 0, { 0x001E89 }},                      /* 1E88; 1E89; Case map */
+  { 0x001E8A, 0, { 0x001E8B }},                      /* 1E8A; 1E8B; Case map */
+  { 0x001E8C, 0, { 0x001E8D }},                      /* 1E8C; 1E8D; Case map */
+  { 0x001E8E, 0, { 0x001E8F }},                      /* 1E8E; 1E8F; Case map */
+  { 0x001E90, 0, { 0x001E91 }},                      /* 1E90; 1E91; Case map */
+  { 0x001E92, 0, { 0x001E93 }},                      /* 1E92; 1E93; Case map */
+  { 0x001E94, 0, { 0x001E95 }},                      /* 1E94; 1E95; Case map */
+  { 0x001E96, 0, { 0x000068,                    /* 1E96; 0068 0331; Case map */
+                   0x000331 }},
+  { 0x001E97, 0, { 0x000074,                    /* 1E97; 0074 0308; Case map */
+                   0x000308 }},
+  { 0x001E98, 0, { 0x000077,                    /* 1E98; 0077 030A; Case map */
+                   0x00030A }},
+  { 0x001E99, 0, { 0x000079,                    /* 1E99; 0079 030A; Case map */
+                   0x00030A }},
+  { 0x001E9A, 0, { 0x000061,                    /* 1E9A; 0061 02BE; Case map */
+                   0x0002BE }},
+  { 0x001E9B, 0, { 0x001E61 }},                      /* 1E9B; 1E61; Case map */
+  { 0x001EA0, 0, { 0x001EA1 }},                      /* 1EA0; 1EA1; Case map */
+  { 0x001EA2, 0, { 0x001EA3 }},                      /* 1EA2; 1EA3; Case map */
+  { 0x001EA4, 0, { 0x001EA5 }},                      /* 1EA4; 1EA5; Case map */
+  { 0x001EA6, 0, { 0x001EA7 }},                      /* 1EA6; 1EA7; Case map */
+  { 0x001EA8, 0, { 0x001EA9 }},                      /* 1EA8; 1EA9; Case map */
+  { 0x001EAA, 0, { 0x001EAB }},                      /* 1EAA; 1EAB; Case map */
+  { 0x001EAC, 0, { 0x001EAD }},                      /* 1EAC; 1EAD; Case map */
+  { 0x001EAE, 0, { 0x001EAF }},                      /* 1EAE; 1EAF; Case map */
+  { 0x001EB0, 0, { 0x001EB1 }},                      /* 1EB0; 1EB1; Case map */
+  { 0x001EB2, 0, { 0x001EB3 }},                      /* 1EB2; 1EB3; Case map */
+  { 0x001EB4, 0, { 0x001EB5 }},                      /* 1EB4; 1EB5; Case map */
+  { 0x001EB6, 0, { 0x001EB7 }},                      /* 1EB6; 1EB7; Case map */
+  { 0x001EB8, 0, { 0x001EB9 }},                      /* 1EB8; 1EB9; Case map */
+  { 0x001EBA, 0, { 0x001EBB }},                      /* 1EBA; 1EBB; Case map */
+  { 0x001EBC, 0, { 0x001EBD }},                      /* 1EBC; 1EBD; Case map */
+  { 0x001EBE, 0, { 0x001EBF }},                      /* 1EBE; 1EBF; Case map */
+  { 0x001EC0, 0, { 0x001EC1 }},                      /* 1EC0; 1EC1; Case map */
+  { 0x001EC2, 0, { 0x001EC3 }},                      /* 1EC2; 1EC3; Case map */
+  { 0x001EC4, 0, { 0x001EC5 }},                      /* 1EC4; 1EC5; Case map */
+  { 0x001EC6, 0, { 0x001EC7 }},                      /* 1EC6; 1EC7; Case map */
+  { 0x001EC8, 0, { 0x001EC9 }},                      /* 1EC8; 1EC9; Case map */
+  { 0x001ECA, 0, { 0x001ECB }},                      /* 1ECA; 1ECB; Case map */
+  { 0x001ECC, 0, { 0x001ECD }},                      /* 1ECC; 1ECD; Case map */
+  { 0x001ECE, 0, { 0x001ECF }},                      /* 1ECE; 1ECF; Case map */
+  { 0x001ED0, 0, { 0x001ED1 }},                      /* 1ED0; 1ED1; Case map */
+  { 0x001ED2, 0, { 0x001ED3 }},                      /* 1ED2; 1ED3; Case map */
+  { 0x001ED4, 0, { 0x001ED5 }},                      /* 1ED4; 1ED5; Case map */
+  { 0x001ED6, 0, { 0x001ED7 }},                      /* 1ED6; 1ED7; Case map */
+  { 0x001ED8, 0, { 0x001ED9 }},                      /* 1ED8; 1ED9; Case map */
+  { 0x001EDA, 0, { 0x001EDB }},                      /* 1EDA; 1EDB; Case map */
+  { 0x001EDC, 0, { 0x001EDD }},                      /* 1EDC; 1EDD; Case map */
+  { 0x001EDE, 0, { 0x001EDF }},                      /* 1EDE; 1EDF; Case map */
+  { 0x001EE0, 0, { 0x001EE1 }},                      /* 1EE0; 1EE1; Case map */
+  { 0x001EE2, 0, { 0x001EE3 }},                      /* 1EE2; 1EE3; Case map */
+  { 0x001EE4, 0, { 0x001EE5 }},                      /* 1EE4; 1EE5; Case map */
+  { 0x001EE6, 0, { 0x001EE7 }},                      /* 1EE6; 1EE7; Case map */
+  { 0x001EE8, 0, { 0x001EE9 }},                      /* 1EE8; 1EE9; Case map */
+  { 0x001EEA, 0, { 0x001EEB }},                      /* 1EEA; 1EEB; Case map */
+  { 0x001EEC, 0, { 0x001EED }},                      /* 1EEC; 1EED; Case map */
+  { 0x001EEE, 0, { 0x001EEF }},                      /* 1EEE; 1EEF; Case map */
+  { 0x001EF0, 0, { 0x001EF1 }},                      /* 1EF0; 1EF1; Case map */
+  { 0x001EF2, 0, { 0x001EF3 }},                      /* 1EF2; 1EF3; Case map */
+  { 0x001EF4, 0, { 0x001EF5 }},                      /* 1EF4; 1EF5; Case map */
+  { 0x001EF6, 0, { 0x001EF7 }},                      /* 1EF6; 1EF7; Case map */
+  { 0x001EF8, 0, { 0x001EF9 }},                      /* 1EF8; 1EF9; Case map */
+  { 0x001F08, 0, { 0x001F00 }},                      /* 1F08; 1F00; Case map */
+  { 0x001F09, 0, { 0x001F01 }},                      /* 1F09; 1F01; Case map */
+  { 0x001F0A, 0, { 0x001F02 }},                      /* 1F0A; 1F02; Case map */
+  { 0x001F0B, 0, { 0x001F03 }},                      /* 1F0B; 1F03; Case map */
+  { 0x001F0C, 0, { 0x001F04 }},                      /* 1F0C; 1F04; Case map */
+  { 0x001F0D, 0, { 0x001F05 }},                      /* 1F0D; 1F05; Case map */
+  { 0x001F0E, 0, { 0x001F06 }},                      /* 1F0E; 1F06; Case map */
+  { 0x001F0F, 0, { 0x001F07 }},                      /* 1F0F; 1F07; Case map */
+  { 0x001F18, 0, { 0x001F10 }},                      /* 1F18; 1F10; Case map */
+  { 0x001F19, 0, { 0x001F11 }},                      /* 1F19; 1F11; Case map */
+  { 0x001F1A, 0, { 0x001F12 }},                      /* 1F1A; 1F12; Case map */
+  { 0x001F1B, 0, { 0x001F13 }},                      /* 1F1B; 1F13; Case map */
+  { 0x001F1C, 0, { 0x001F14 }},                      /* 1F1C; 1F14; Case map */
+  { 0x001F1D, 0, { 0x001F15 }},                      /* 1F1D; 1F15; Case map */
+  { 0x001F28, 0, { 0x001F20 }},                      /* 1F28; 1F20; Case map */
+  { 0x001F29, 0, { 0x001F21 }},                      /* 1F29; 1F21; Case map */
+  { 0x001F2A, 0, { 0x001F22 }},                      /* 1F2A; 1F22; Case map */
+  { 0x001F2B, 0, { 0x001F23 }},                      /* 1F2B; 1F23; Case map */
+  { 0x001F2C, 0, { 0x001F24 }},                      /* 1F2C; 1F24; Case map */
+  { 0x001F2D, 0, { 0x001F25 }},                      /* 1F2D; 1F25; Case map */
+  { 0x001F2E, 0, { 0x001F26 }},                      /* 1F2E; 1F26; Case map */
+  { 0x001F2F, 0, { 0x001F27 }},                      /* 1F2F; 1F27; Case map */
+  { 0x001F38, 0, { 0x001F30 }},                      /* 1F38; 1F30; Case map */
+  { 0x001F39, 0, { 0x001F31 }},                      /* 1F39; 1F31; Case map */
+  { 0x001F3A, 0, { 0x001F32 }},                      /* 1F3A; 1F32; Case map */
+  { 0x001F3B, 0, { 0x001F33 }},                      /* 1F3B; 1F33; Case map */
+  { 0x001F3C, 0, { 0x001F34 }},                      /* 1F3C; 1F34; Case map */
+  { 0x001F3D, 0, { 0x001F35 }},                      /* 1F3D; 1F35; Case map */
+  { 0x001F3E, 0, { 0x001F36 }},                      /* 1F3E; 1F36; Case map */
+  { 0x001F3F, 0, { 0x001F37 }},                      /* 1F3F; 1F37; Case map */
+  { 0x001F48, 0, { 0x001F40 }},                      /* 1F48; 1F40; Case map */
+  { 0x001F49, 0, { 0x001F41 }},                      /* 1F49; 1F41; Case map */
+  { 0x001F4A, 0, { 0x001F42 }},                      /* 1F4A; 1F42; Case map */
+  { 0x001F4B, 0, { 0x001F43 }},                      /* 1F4B; 1F43; Case map */
+  { 0x001F4C, 0, { 0x001F44 }},                      /* 1F4C; 1F44; Case map */
+  { 0x001F4D, 0, { 0x001F45 }},                      /* 1F4D; 1F45; Case map */
+  { 0x001F50, 0, { 0x0003C5,                    /* 1F50; 03C5 0313; Case map */
+                   0x000313 }},
+  { 0x001F52, 0, { 0x0003C5,               /* 1F52; 03C5 0313 0300; Case map */
+                   0x000313, 0x000300 }},
+  { 0x001F54, 0, { 0x0003C5,               /* 1F54; 03C5 0313 0301; Case map */
+                   0x000313, 0x000301 }},
+  { 0x001F56, 0, { 0x0003C5,               /* 1F56; 03C5 0313 0342; Case map */
+                   0x000313, 0x000342 }},
+  { 0x001F59, 0, { 0x001F51 }},                      /* 1F59; 1F51; Case map */
+  { 0x001F5B, 0, { 0x001F53 }},                      /* 1F5B; 1F53; Case map */
+  { 0x001F5D, 0, { 0x001F55 }},                      /* 1F5D; 1F55; Case map */
+  { 0x001F5F, 0, { 0x001F57 }},                      /* 1F5F; 1F57; Case map */
+  { 0x001F68, 0, { 0x001F60 }},                      /* 1F68; 1F60; Case map */
+  { 0x001F69, 0, { 0x001F61 }},                      /* 1F69; 1F61; Case map */
+  { 0x001F6A, 0, { 0x001F62 }},                      /* 1F6A; 1F62; Case map */
+  { 0x001F6B, 0, { 0x001F63 }},                      /* 1F6B; 1F63; Case map */
+  { 0x001F6C, 0, { 0x001F64 }},                      /* 1F6C; 1F64; Case map */
+  { 0x001F6D, 0, { 0x001F65 }},                      /* 1F6D; 1F65; Case map */
+  { 0x001F6E, 0, { 0x001F66 }},                      /* 1F6E; 1F66; Case map */
+  { 0x001F6F, 0, { 0x001F67 }},                      /* 1F6F; 1F67; Case map */
+  { 0x001F80, 0, { 0x001F00,                    /* 1F80; 1F00 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F81, 0, { 0x001F01,                    /* 1F81; 1F01 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F82, 0, { 0x001F02,                    /* 1F82; 1F02 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F83, 0, { 0x001F03,                    /* 1F83; 1F03 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F84, 0, { 0x001F04,                    /* 1F84; 1F04 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F85, 0, { 0x001F05,                    /* 1F85; 1F05 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F86, 0, { 0x001F06,                    /* 1F86; 1F06 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F87, 0, { 0x001F07,                    /* 1F87; 1F07 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F88, 0, { 0x001F00,                    /* 1F88; 1F00 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F89, 0, { 0x001F01,                    /* 1F89; 1F01 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8A, 0, { 0x001F02,                    /* 1F8A; 1F02 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8B, 0, { 0x001F03,                    /* 1F8B; 1F03 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8C, 0, { 0x001F04,                    /* 1F8C; 1F04 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8D, 0, { 0x001F05,                    /* 1F8D; 1F05 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8E, 0, { 0x001F06,                    /* 1F8E; 1F06 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8F, 0, { 0x001F07,                    /* 1F8F; 1F07 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F90, 0, { 0x001F20,                    /* 1F90; 1F20 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F91, 0, { 0x001F21,                    /* 1F91; 1F21 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F92, 0, { 0x001F22,                    /* 1F92; 1F22 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F93, 0, { 0x001F23,                    /* 1F93; 1F23 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F94, 0, { 0x001F24,                    /* 1F94; 1F24 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F95, 0, { 0x001F25,                    /* 1F95; 1F25 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F96, 0, { 0x001F26,                    /* 1F96; 1F26 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F97, 0, { 0x001F27,                    /* 1F97; 1F27 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F98, 0, { 0x001F20,                    /* 1F98; 1F20 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F99, 0, { 0x001F21,                    /* 1F99; 1F21 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9A, 0, { 0x001F22,                    /* 1F9A; 1F22 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9B, 0, { 0x001F23,                    /* 1F9B; 1F23 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9C, 0, { 0x001F24,                    /* 1F9C; 1F24 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9D, 0, { 0x001F25,                    /* 1F9D; 1F25 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9E, 0, { 0x001F26,                    /* 1F9E; 1F26 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9F, 0, { 0x001F27,                    /* 1F9F; 1F27 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA0, 0, { 0x001F60,                    /* 1FA0; 1F60 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA1, 0, { 0x001F61,                    /* 1FA1; 1F61 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA2, 0, { 0x001F62,                    /* 1FA2; 1F62 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA3, 0, { 0x001F63,                    /* 1FA3; 1F63 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA4, 0, { 0x001F64,                    /* 1FA4; 1F64 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA5, 0, { 0x001F65,                    /* 1FA5; 1F65 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA6, 0, { 0x001F66,                    /* 1FA6; 1F66 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA7, 0, { 0x001F67,                    /* 1FA7; 1F67 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA8, 0, { 0x001F60,                    /* 1FA8; 1F60 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA9, 0, { 0x001F61,                    /* 1FA9; 1F61 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAA, 0, { 0x001F62,                    /* 1FAA; 1F62 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAB, 0, { 0x001F63,                    /* 1FAB; 1F63 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAC, 0, { 0x001F64,                    /* 1FAC; 1F64 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAD, 0, { 0x001F65,                    /* 1FAD; 1F65 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAE, 0, { 0x001F66,                    /* 1FAE; 1F66 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAF, 0, { 0x001F67,                    /* 1FAF; 1F67 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FB2, 0, { 0x001F70,                    /* 1FB2; 1F70 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FB3, 0, { 0x0003B1,                    /* 1FB3; 03B1 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FB4, 0, { 0x0003AC,                    /* 1FB4; 03AC 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FB6, 0, { 0x0003B1,                    /* 1FB6; 03B1 0342; Case map */
+                   0x000342 }},
+  { 0x001FB7, 0, { 0x0003B1,               /* 1FB7; 03B1 0342 03B9; Case map */
+                   0x000342, 0x0003B9 }},
+  { 0x001FB8, 0, { 0x001FB0 }},                      /* 1FB8; 1FB0; Case map */
+  { 0x001FB9, 0, { 0x001FB1 }},                      /* 1FB9; 1FB1; Case map */
+  { 0x001FBA, 0, { 0x001F70 }},                      /* 1FBA; 1F70; Case map */
+  { 0x001FBB, 0, { 0x001F71 }},                      /* 1FBB; 1F71; Case map */
+  { 0x001FBC, 0, { 0x0003B1,                    /* 1FBC; 03B1 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FBE, 0, { 0x0003B9 }},                      /* 1FBE; 03B9; Case map */
+  { 0x001FC2, 0, { 0x001F74,                    /* 1FC2; 1F74 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FC3, 0, { 0x0003B7,                    /* 1FC3; 03B7 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FC4, 0, { 0x0003AE,                    /* 1FC4; 03AE 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FC6, 0, { 0x0003B7,                    /* 1FC6; 03B7 0342; Case map */
+                   0x000342 }},
+  { 0x001FC7, 0, { 0x0003B7,               /* 1FC7; 03B7 0342 03B9; Case map */
+                   0x000342, 0x0003B9 }},
+  { 0x001FC8, 0, { 0x001F72 }},                      /* 1FC8; 1F72; Case map */
+  { 0x001FC9, 0, { 0x001F73 }},                      /* 1FC9; 1F73; Case map */
+  { 0x001FCA, 0, { 0x001F74 }},                      /* 1FCA; 1F74; Case map */
+  { 0x001FCB, 0, { 0x001F75 }},                      /* 1FCB; 1F75; Case map */
+  { 0x001FCC, 0, { 0x0003B7,                    /* 1FCC; 03B7 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FD2, 0, { 0x0003B9,               /* 1FD2; 03B9 0308 0300; Case map */
+                   0x000308, 0x000300 }},
+  { 0x001FD3, 0, { 0x0003B9,               /* 1FD3; 03B9 0308 0301; Case map */
+                   0x000308, 0x000301 }},
+  { 0x001FD6, 0, { 0x0003B9,                    /* 1FD6; 03B9 0342; Case map */
+                   0x000342 }},
+  { 0x001FD7, 0, { 0x0003B9,               /* 1FD7; 03B9 0308 0342; Case map */
+                   0x000308, 0x000342 }},
+  { 0x001FD8, 0, { 0x001FD0 }},                      /* 1FD8; 1FD0; Case map */
+  { 0x001FD9, 0, { 0x001FD1 }},                      /* 1FD9; 1FD1; Case map */
+  { 0x001FDA, 0, { 0x001F76 }},                      /* 1FDA; 1F76; Case map */
+  { 0x001FDB, 0, { 0x001F77 }},                      /* 1FDB; 1F77; Case map */
+  { 0x001FE2, 0, { 0x0003C5,               /* 1FE2; 03C5 0308 0300; Case map */
+                   0x000308, 0x000300 }},
+  { 0x001FE3, 0, { 0x0003C5,               /* 1FE3; 03C5 0308 0301; Case map */
+                   0x000308, 0x000301 }},
+  { 0x001FE4, 0, { 0x0003C1,                    /* 1FE4; 03C1 0313; Case map */
+                   0x000313 }},
+  { 0x001FE6, 0, { 0x0003C5,                    /* 1FE6; 03C5 0342; Case map */
+                   0x000342 }},
+  { 0x001FE7, 0, { 0x0003C5,               /* 1FE7; 03C5 0308 0342; Case map */
+                   0x000308, 0x000342 }},
+  { 0x001FE8, 0, { 0x001FE0 }},                      /* 1FE8; 1FE0; Case map */
+  { 0x001FE9, 0, { 0x001FE1 }},                      /* 1FE9; 1FE1; Case map */
+  { 0x001FEA, 0, { 0x001F7A }},                      /* 1FEA; 1F7A; Case map */
+  { 0x001FEB, 0, { 0x001F7B }},                      /* 1FEB; 1F7B; Case map */
+  { 0x001FEC, 0, { 0x001FE5 }},                      /* 1FEC; 1FE5; Case map */
+  { 0x001FF2, 0, { 0x001F7C,                    /* 1FF2; 1F7C 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FF3, 0, { 0x0003C9,                    /* 1FF3; 03C9 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FF4, 0, { 0x0003CE,                    /* 1FF4; 03CE 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FF6, 0, { 0x0003C9,                    /* 1FF6; 03C9 0342; Case map */
+                   0x000342 }},
+  { 0x001FF7, 0, { 0x0003C9,               /* 1FF7; 03C9 0342 03B9; Case map */
+                   0x000342, 0x0003B9 }},
+  { 0x001FF8, 0, { 0x001F78 }},                      /* 1FF8; 1F78; Case map */
+  { 0x001FF9, 0, { 0x001F79 }},                      /* 1FF9; 1F79; Case map */
+  { 0x001FFA, 0, { 0x001F7C }},                      /* 1FFA; 1F7C; Case map */
+  { 0x001FFB, 0, { 0x001F7D }},                      /* 1FFB; 1F7D; Case map */
+  { 0x001FFC, 0, { 0x0003C9,                    /* 1FFC; 03C9 03B9; Case map */
+                   0x0003B9 }},
+  { 0x0020A8, 0, { 0x000072,          /* 20A8; 0072 0073; Additional folding */
+                   0x000073 }},
+  { 0x002102, 0, { 0x000063 }},            /* 2102; 0063; Additional folding */
+  { 0x002103, 0, { 0x0000B0,          /* 2103; 00B0 0063; Additional folding */
+                   0x000063 }},
+  { 0x002107, 0, { 0x00025B }},            /* 2107; 025B; Additional folding */
+  { 0x002109, 0, { 0x0000B0,          /* 2109; 00B0 0066; Additional folding */
+                   0x000066 }},
+  { 0x00210B, 0, { 0x000068 }},            /* 210B; 0068; Additional folding */
+  { 0x00210C, 0, { 0x000068 }},            /* 210C; 0068; Additional folding */
+  { 0x00210D, 0, { 0x000068 }},            /* 210D; 0068; Additional folding */
+  { 0x002110, 0, { 0x000069 }},            /* 2110; 0069; Additional folding */
+  { 0x002111, 0, { 0x000069 }},            /* 2111; 0069; Additional folding */
+  { 0x002112, 0, { 0x00006C }},            /* 2112; 006C; Additional folding */
+  { 0x002115, 0, { 0x00006E }},            /* 2115; 006E; Additional folding */
+  { 0x002116, 0, { 0x00006E,          /* 2116; 006E 006F; Additional folding */
+                   0x00006F }},
+  { 0x002119, 0, { 0x000070 }},            /* 2119; 0070; Additional folding */
+  { 0x00211A, 0, { 0x000071 }},            /* 211A; 0071; Additional folding */
+  { 0x00211B, 0, { 0x000072 }},            /* 211B; 0072; Additional folding */
+  { 0x00211C, 0, { 0x000072 }},            /* 211C; 0072; Additional folding */
+  { 0x00211D, 0, { 0x000072 }},            /* 211D; 0072; Additional folding */
+  { 0x002120, 0, { 0x000073,          /* 2120; 0073 006D; Additional folding */
+                   0x00006D }},
+  { 0x002121, 0, { 0x000074,     /* 2121; 0074 0065 006C; Additional folding */
+                   0x000065, 0x00006C }},
+  { 0x002122, 0, { 0x000074,          /* 2122; 0074 006D; Additional folding */
+                   0x00006D }},
+  { 0x002124, 0, { 0x00007A }},            /* 2124; 007A; Additional folding */
+  { 0x002126, 0, { 0x0003C9 }},                      /* 2126; 03C9; Case map */
+  { 0x002128, 0, { 0x00007A }},            /* 2128; 007A; Additional folding */
+  { 0x00212A, 0, { 0x00006B }},                      /* 212A; 006B; Case map */
+  { 0x00212B, 0, { 0x0000E5 }},                      /* 212B; 00E5; Case map */
+  { 0x00212C, 0, { 0x000062 }},            /* 212C; 0062; Additional folding */
+  { 0x00212D, 0, { 0x000063 }},            /* 212D; 0063; Additional folding */
+  { 0x002130, 0, { 0x000065 }},            /* 2130; 0065; Additional folding */
+  { 0x002131, 0, { 0x000066 }},            /* 2131; 0066; Additional folding */
+  { 0x002133, 0, { 0x00006D }},            /* 2133; 006D; Additional folding */
+  { 0x00213E, 0, { 0x0003B3 }},            /* 213E; 03B3; Additional folding */
+  { 0x00213F, 0, { 0x0003C0 }},            /* 213F; 03C0; Additional folding */
+  { 0x002145, 0, { 0x000064 }},            /* 2145; 0064; Additional folding */
+  { 0x002160, 0, { 0x002170 }},                      /* 2160; 2170; Case map */
+  { 0x002161, 0, { 0x002171 }},                      /* 2161; 2171; Case map */
+  { 0x002162, 0, { 0x002172 }},                      /* 2162; 2172; Case map */
+  { 0x002163, 0, { 0x002173 }},                      /* 2163; 2173; Case map */
+  { 0x002164, 0, { 0x002174 }},                      /* 2164; 2174; Case map */
+  { 0x002165, 0, { 0x002175 }},                      /* 2165; 2175; Case map */
+  { 0x002166, 0, { 0x002176 }},                      /* 2166; 2176; Case map */
+  { 0x002167, 0, { 0x002177 }},                      /* 2167; 2177; Case map */
+  { 0x002168, 0, { 0x002178 }},                      /* 2168; 2178; Case map */
+  { 0x002169, 0, { 0x002179 }},                      /* 2169; 2179; Case map */
+  { 0x00216A, 0, { 0x00217A }},                      /* 216A; 217A; Case map */
+  { 0x00216B, 0, { 0x00217B }},                      /* 216B; 217B; Case map */
+  { 0x00216C, 0, { 0x00217C }},                      /* 216C; 217C; Case map */
+  { 0x00216D, 0, { 0x00217D }},                      /* 216D; 217D; Case map */
+  { 0x00216E, 0, { 0x00217E }},                      /* 216E; 217E; Case map */
+  { 0x00216F, 0, { 0x00217F }},                      /* 216F; 217F; Case map */
+  { 0x0024B6, 0, { 0x0024D0 }},                      /* 24B6; 24D0; Case map */
+  { 0x0024B7, 0, { 0x0024D1 }},                      /* 24B7; 24D1; Case map */
+  { 0x0024B8, 0, { 0x0024D2 }},                      /* 24B8; 24D2; Case map */
+  { 0x0024B9, 0, { 0x0024D3 }},                      /* 24B9; 24D3; Case map */
+  { 0x0024BA, 0, { 0x0024D4 }},                      /* 24BA; 24D4; Case map */
+  { 0x0024BB, 0, { 0x0024D5 }},                      /* 24BB; 24D5; Case map */
+  { 0x0024BC, 0, { 0x0024D6 }},                      /* 24BC; 24D6; Case map */
+  { 0x0024BD, 0, { 0x0024D7 }},                      /* 24BD; 24D7; Case map */
+  { 0x0024BE, 0, { 0x0024D8 }},                      /* 24BE; 24D8; Case map */
+  { 0x0024BF, 0, { 0x0024D9 }},                      /* 24BF; 24D9; Case map */
+  { 0x0024C0, 0, { 0x0024DA }},                      /* 24C0; 24DA; Case map */
+  { 0x0024C1, 0, { 0x0024DB }},                      /* 24C1; 24DB; Case map */
+  { 0x0024C2, 0, { 0x0024DC }},                      /* 24C2; 24DC; Case map */
+  { 0x0024C3, 0, { 0x0024DD }},                      /* 24C3; 24DD; Case map */
+  { 0x0024C4, 0, { 0x0024DE }},                      /* 24C4; 24DE; Case map */
+  { 0x0024C5, 0, { 0x0024DF }},                      /* 24C5; 24DF; Case map */
+  { 0x0024C6, 0, { 0x0024E0 }},                      /* 24C6; 24E0; Case map */
+  { 0x0024C7, 0, { 0x0024E1 }},                      /* 24C7; 24E1; Case map */
+  { 0x0024C8, 0, { 0x0024E2 }},                      /* 24C8; 24E2; Case map */
+  { 0x0024C9, 0, { 0x0024E3 }},                      /* 24C9; 24E3; Case map */
+  { 0x0024CA, 0, { 0x0024E4 }},                      /* 24CA; 24E4; Case map */
+  { 0x0024CB, 0, { 0x0024E5 }},                      /* 24CB; 24E5; Case map */
+  { 0x0024CC, 0, { 0x0024E6 }},                      /* 24CC; 24E6; Case map */
+  { 0x0024CD, 0, { 0x0024E7 }},                      /* 24CD; 24E7; Case map */
+  { 0x0024CE, 0, { 0x0024E8 }},                      /* 24CE; 24E8; Case map */
+  { 0x0024CF, 0, { 0x0024E9 }},                      /* 24CF; 24E9; Case map */
+  { 0x003371, 0, { 0x000068,     /* 3371; 0068 0070 0061; Additional folding */
+                   0x000070, 0x000061 }},
+  { 0x003373, 0, { 0x000061,          /* 3373; 0061 0075; Additional folding */
+                   0x000075 }},
+  { 0x003375, 0, { 0x00006F,          /* 3375; 006F 0076; Additional folding */
+                   0x000076 }},
+  { 0x003380, 0, { 0x000070,          /* 3380; 0070 0061; Additional folding */
+                   0x000061 }},
+  { 0x003381, 0, { 0x00006E,          /* 3381; 006E 0061; Additional folding */
+                   0x000061 }},
+  { 0x003382, 0, { 0x0003BC,          /* 3382; 03BC 0061; Additional folding */
+                   0x000061 }},
+  { 0x003383, 0, { 0x00006D,          /* 3383; 006D 0061; Additional folding */
+                   0x000061 }},
+  { 0x003384, 0, { 0x00006B,          /* 3384; 006B 0061; Additional folding */
+                   0x000061 }},
+  { 0x003385, 0, { 0x00006B,          /* 3385; 006B 0062; Additional folding */
+                   0x000062 }},
+  { 0x003386, 0, { 0x00006D,          /* 3386; 006D 0062; Additional folding */
+                   0x000062 }},
+  { 0x003387, 0, { 0x000067,          /* 3387; 0067 0062; Additional folding */
+                   0x000062 }},
+  { 0x00338A, 0, { 0x000070,          /* 338A; 0070 0066; Additional folding */
+                   0x000066 }},
+  { 0x00338B, 0, { 0x00006E,          /* 338B; 006E 0066; Additional folding */
+                   0x000066 }},
+  { 0x00338C, 0, { 0x0003BC,          /* 338C; 03BC 0066; Additional folding */
+                   0x000066 }},
+  { 0x003390, 0, { 0x000068,          /* 3390; 0068 007A; Additional folding */
+                   0x00007A }},
+  { 0x003391, 0, { 0x00006B,     /* 3391; 006B 0068 007A; Additional folding */
+                   0x000068, 0x00007A }},
+  { 0x003392, 0, { 0x00006D,     /* 3392; 006D 0068 007A; Additional folding */
+                   0x000068, 0x00007A }},
+  { 0x003393, 0, { 0x000067,     /* 3393; 0067 0068 007A; Additional folding */
+                   0x000068, 0x00007A }},
+  { 0x003394, 0, { 0x000074,     /* 3394; 0074 0068 007A; Additional folding */
+                   0x000068, 0x00007A }},
+  { 0x0033A9, 0, { 0x000070,          /* 33A9; 0070 0061; Additional folding */
+                   0x000061 }},
+  { 0x0033AA, 0, { 0x00006B,     /* 33AA; 006B 0070 0061; Additional folding */
+                   0x000070, 0x000061 }},
+  { 0x0033AB, 0, { 0x00006D,     /* 33AB; 006D 0070 0061; Additional folding */
+                   0x000070, 0x000061 }},
+  { 0x0033AC, 0, { 0x000067,     /* 33AC; 0067 0070 0061; Additional folding */
+                   0x000070, 0x000061 }},
+  { 0x0033B4, 0, { 0x000070,          /* 33B4; 0070 0076; Additional folding */
+                   0x000076 }},
+  { 0x0033B5, 0, { 0x00006E,          /* 33B5; 006E 0076; Additional folding */
+                   0x000076 }},
+  { 0x0033B6, 0, { 0x0003BC,          /* 33B6; 03BC 0076; Additional folding */
+                   0x000076 }},
+  { 0x0033B7, 0, { 0x00006D,          /* 33B7; 006D 0076; Additional folding */
+                   0x000076 }},
+  { 0x0033B8, 0, { 0x00006B,          /* 33B8; 006B 0076; Additional folding */
+                   0x000076 }},
+  { 0x0033B9, 0, { 0x00006D,          /* 33B9; 006D 0076; Additional folding */
+                   0x000076 }},
+  { 0x0033BA, 0, { 0x000070,          /* 33BA; 0070 0077; Additional folding */
+                   0x000077 }},
+  { 0x0033BB, 0, { 0x00006E,          /* 33BB; 006E 0077; Additional folding */
+                   0x000077 }},
+  { 0x0033BC, 0, { 0x0003BC,          /* 33BC; 03BC 0077; Additional folding */
+                   0x000077 }},
+  { 0x0033BD, 0, { 0x00006D,          /* 33BD; 006D 0077; Additional folding */
+                   0x000077 }},
+  { 0x0033BE, 0, { 0x00006B,          /* 33BE; 006B 0077; Additional folding */
+                   0x000077 }},
+  { 0x0033BF, 0, { 0x00006D,          /* 33BF; 006D 0077; Additional folding */
+                   0x000077 }},
+  { 0x0033C0, 0, { 0x00006B,          /* 33C0; 006B 03C9; Additional folding */
+                   0x0003C9 }},
+  { 0x0033C1, 0, { 0x00006D,          /* 33C1; 006D 03C9; Additional folding */
+                   0x0003C9 }},
+  { 0x0033C3, 0, { 0x000062,          /* 33C3; 0062 0071; Additional folding */
+                   0x000071 }},
+  { 0x0033C6, 0, { 0x000063, /* 33C6; 0063 2215 006B 0067; Additional folding */
+                   0x002215, 0x00006B, 0x000067 }},
+  { 0x0033C7, 0, { 0x000063,     /* 33C7; 0063 006F 002E; Additional folding */
+                   0x00006F, 0x00002E }},
+  { 0x0033C8, 0, { 0x000064,          /* 33C8; 0064 0062; Additional folding */
+                   0x000062 }},
+  { 0x0033C9, 0, { 0x000067,          /* 33C9; 0067 0079; Additional folding */
+                   0x000079 }},
+  { 0x0033CB, 0, { 0x000068,          /* 33CB; 0068 0070; Additional folding */
+                   0x000070 }},
+  { 0x0033CD, 0, { 0x00006B,          /* 33CD; 006B 006B; Additional folding */
+                   0x00006B }},
+  { 0x0033CE, 0, { 0x00006B,          /* 33CE; 006B 006D; Additional folding */
+                   0x00006D }},
+  { 0x0033D7, 0, { 0x000070,          /* 33D7; 0070 0068; Additional folding */
+                   0x000068 }},
+  { 0x0033D9, 0, { 0x000070,     /* 33D9; 0070 0070 006D; Additional folding */
+                   0x000070, 0x00006D }},
+  { 0x0033DA, 0, { 0x000070,          /* 33DA; 0070 0072; Additional folding */
+                   0x000072 }},
+  { 0x0033DC, 0, { 0x000073,          /* 33DC; 0073 0076; Additional folding */
+                   0x000076 }},
+  { 0x0033DD, 0, { 0x000077,          /* 33DD; 0077 0062; Additional folding */
+                   0x000062 }},
+  { 0x00FB00, 0, { 0x000066,                    /* FB00; 0066 0066; Case map */
+                   0x000066 }},
+  { 0x00FB01, 0, { 0x000066,                    /* FB01; 0066 0069; Case map */
+                   0x000069 }},
+  { 0x00FB02, 0, { 0x000066,                    /* FB02; 0066 006C; Case map */
+                   0x00006C }},
+  { 0x00FB03, 0, { 0x000066,               /* FB03; 0066 0066 0069; Case map */
+                   0x000066, 0x000069 }},
+  { 0x00FB04, 0, { 0x000066,               /* FB04; 0066 0066 006C; Case map */
+                   0x000066, 0x00006C }},
+  { 0x00FB05, 0, { 0x000073,                    /* FB05; 0073 0074; Case map */
+                   0x000074 }},
+  { 0x00FB06, 0, { 0x000073,                    /* FB06; 0073 0074; Case map */
+                   0x000074 }},
+  { 0x00FB13, 0, { 0x000574,                    /* FB13; 0574 0576; Case map */
+                   0x000576 }},
+  { 0x00FB14, 0, { 0x000574,                    /* FB14; 0574 0565; Case map */
+                   0x000565 }},
+  { 0x00FB15, 0, { 0x000574,                    /* FB15; 0574 056B; Case map */
+                   0x00056B }},
+  { 0x00FB16, 0, { 0x00057E,                    /* FB16; 057E 0576; Case map */
+                   0x000576 }},
+  { 0x00FB17, 0, { 0x000574,                    /* FB17; 0574 056D; Case map */
+                   0x00056D }},
+  { 0x00FF21, 0, { 0x00FF41 }},                      /* FF21; FF41; Case map */
+  { 0x00FF22, 0, { 0x00FF42 }},                      /* FF22; FF42; Case map */
+  { 0x00FF23, 0, { 0x00FF43 }},                      /* FF23; FF43; Case map */
+  { 0x00FF24, 0, { 0x00FF44 }},                      /* FF24; FF44; Case map */
+  { 0x00FF25, 0, { 0x00FF45 }},                      /* FF25; FF45; Case map */
+  { 0x00FF26, 0, { 0x00FF46 }},                      /* FF26; FF46; Case map */
+  { 0x00FF27, 0, { 0x00FF47 }},                      /* FF27; FF47; Case map */
+  { 0x00FF28, 0, { 0x00FF48 }},                      /* FF28; FF48; Case map */
+  { 0x00FF29, 0, { 0x00FF49 }},                      /* FF29; FF49; Case map */
+  { 0x00FF2A, 0, { 0x00FF4A }},                      /* FF2A; FF4A; Case map */
+  { 0x00FF2B, 0, { 0x00FF4B }},                      /* FF2B; FF4B; Case map */
+  { 0x00FF2C, 0, { 0x00FF4C }},                      /* FF2C; FF4C; Case map */
+  { 0x00FF2D, 0, { 0x00FF4D }},                      /* FF2D; FF4D; Case map */
+  { 0x00FF2E, 0, { 0x00FF4E }},                      /* FF2E; FF4E; Case map */
+  { 0x00FF2F, 0, { 0x00FF4F }},                      /* FF2F; FF4F; Case map */
+  { 0x00FF30, 0, { 0x00FF50 }},                      /* FF30; FF50; Case map */
+  { 0x00FF31, 0, { 0x00FF51 }},                      /* FF31; FF51; Case map */
+  { 0x00FF32, 0, { 0x00FF52 }},                      /* FF32; FF52; Case map */
+  { 0x00FF33, 0, { 0x00FF53 }},                      /* FF33; FF53; Case map */
+  { 0x00FF34, 0, { 0x00FF54 }},                      /* FF34; FF54; Case map */
+  { 0x00FF35, 0, { 0x00FF55 }},                      /* FF35; FF55; Case map */
+  { 0x00FF36, 0, { 0x00FF56 }},                      /* FF36; FF56; Case map */
+  { 0x00FF37, 0, { 0x00FF57 }},                      /* FF37; FF57; Case map */
+  { 0x00FF38, 0, { 0x00FF58 }},                      /* FF38; FF58; Case map */
+  { 0x00FF39, 0, { 0x00FF59 }},                      /* FF39; FF59; Case map */
+  { 0x00FF3A, 0, { 0x00FF5A }},                      /* FF3A; FF5A; Case map */
+  { 0x010400, 0, { 0x010428 }},                    /* 10400; 10428; Case map */
+  { 0x010401, 0, { 0x010429 }},                    /* 10401; 10429; Case map */
+  { 0x010402, 0, { 0x01042A }},                    /* 10402; 1042A; Case map */
+  { 0x010403, 0, { 0x01042B }},                    /* 10403; 1042B; Case map */
+  { 0x010404, 0, { 0x01042C }},                    /* 10404; 1042C; Case map */
+  { 0x010405, 0, { 0x01042D }},                    /* 10405; 1042D; Case map */
+  { 0x010406, 0, { 0x01042E }},                    /* 10406; 1042E; Case map */
+  { 0x010407, 0, { 0x01042F }},                    /* 10407; 1042F; Case map */
+  { 0x010408, 0, { 0x010430 }},                    /* 10408; 10430; Case map */
+  { 0x010409, 0, { 0x010431 }},                    /* 10409; 10431; Case map */
+  { 0x01040A, 0, { 0x010432 }},                    /* 1040A; 10432; Case map */
+  { 0x01040B, 0, { 0x010433 }},                    /* 1040B; 10433; Case map */
+  { 0x01040C, 0, { 0x010434 }},                    /* 1040C; 10434; Case map */
+  { 0x01040D, 0, { 0x010435 }},                    /* 1040D; 10435; Case map */
+  { 0x01040E, 0, { 0x010436 }},                    /* 1040E; 10436; Case map */
+  { 0x01040F, 0, { 0x010437 }},                    /* 1040F; 10437; Case map */
+  { 0x010410, 0, { 0x010438 }},                    /* 10410; 10438; Case map */
+  { 0x010411, 0, { 0x010439 }},                    /* 10411; 10439; Case map */
+  { 0x010412, 0, { 0x01043A }},                    /* 10412; 1043A; Case map */
+  { 0x010413, 0, { 0x01043B }},                    /* 10413; 1043B; Case map */
+  { 0x010414, 0, { 0x01043C }},                    /* 10414; 1043C; Case map */
+  { 0x010415, 0, { 0x01043D }},                    /* 10415; 1043D; Case map */
+  { 0x010416, 0, { 0x01043E }},                    /* 10416; 1043E; Case map */
+  { 0x010417, 0, { 0x01043F }},                    /* 10417; 1043F; Case map */
+  { 0x010418, 0, { 0x010440 }},                    /* 10418; 10440; Case map */
+  { 0x010419, 0, { 0x010441 }},                    /* 10419; 10441; Case map */
+  { 0x01041A, 0, { 0x010442 }},                    /* 1041A; 10442; Case map */
+  { 0x01041B, 0, { 0x010443 }},                    /* 1041B; 10443; Case map */
+  { 0x01041C, 0, { 0x010444 }},                    /* 1041C; 10444; Case map */
+  { 0x01041D, 0, { 0x010445 }},                    /* 1041D; 10445; Case map */
+  { 0x01041E, 0, { 0x010446 }},                    /* 1041E; 10446; Case map */
+  { 0x01041F, 0, { 0x010447 }},                    /* 1041F; 10447; Case map */
+  { 0x010420, 0, { 0x010448 }},                    /* 10420; 10448; Case map */
+  { 0x010421, 0, { 0x010449 }},                    /* 10421; 10449; Case map */
+  { 0x010422, 0, { 0x01044A }},                    /* 10422; 1044A; Case map */
+  { 0x010423, 0, { 0x01044B }},                    /* 10423; 1044B; Case map */
+  { 0x010424, 0, { 0x01044C }},                    /* 10424; 1044C; Case map */
+  { 0x010425, 0, { 0x01044D }},                    /* 10425; 1044D; Case map */
+  { 0x01D400, 0, { 0x000061 }},           /* 1D400; 0061; Additional folding */
+  { 0x01D401, 0, { 0x000062 }},           /* 1D401; 0062; Additional folding */
+  { 0x01D402, 0, { 0x000063 }},           /* 1D402; 0063; Additional folding */
+  { 0x01D403, 0, { 0x000064 }},           /* 1D403; 0064; Additional folding */
+  { 0x01D404, 0, { 0x000065 }},           /* 1D404; 0065; Additional folding */
+  { 0x01D405, 0, { 0x000066 }},           /* 1D405; 0066; Additional folding */
+  { 0x01D406, 0, { 0x000067 }},           /* 1D406; 0067; Additional folding */
+  { 0x01D407, 0, { 0x000068 }},           /* 1D407; 0068; Additional folding */
+  { 0x01D408, 0, { 0x000069 }},           /* 1D408; 0069; Additional folding */
+  { 0x01D409, 0, { 0x00006A }},           /* 1D409; 006A; Additional folding */
+  { 0x01D40A, 0, { 0x00006B }},           /* 1D40A; 006B; Additional folding */
+  { 0x01D40B, 0, { 0x00006C }},           /* 1D40B; 006C; Additional folding */
+  { 0x01D40C, 0, { 0x00006D }},           /* 1D40C; 006D; Additional folding */
+  { 0x01D40D, 0, { 0x00006E }},           /* 1D40D; 006E; Additional folding */
+  { 0x01D40E, 0, { 0x00006F }},           /* 1D40E; 006F; Additional folding */
+  { 0x01D40F, 0, { 0x000070 }},           /* 1D40F; 0070; Additional folding */
+  { 0x01D410, 0, { 0x000071 }},           /* 1D410; 0071; Additional folding */
+  { 0x01D411, 0, { 0x000072 }},           /* 1D411; 0072; Additional folding */
+  { 0x01D412, 0, { 0x000073 }},           /* 1D412; 0073; Additional folding */
+  { 0x01D413, 0, { 0x000074 }},           /* 1D413; 0074; Additional folding */
+  { 0x01D414, 0, { 0x000075 }},           /* 1D414; 0075; Additional folding */
+  { 0x01D415, 0, { 0x000076 }},           /* 1D415; 0076; Additional folding */
+  { 0x01D416, 0, { 0x000077 }},           /* 1D416; 0077; Additional folding */
+  { 0x01D417, 0, { 0x000078 }},           /* 1D417; 0078; Additional folding */
+  { 0x01D418, 0, { 0x000079 }},           /* 1D418; 0079; Additional folding */
+  { 0x01D419, 0, { 0x00007A }},           /* 1D419; 007A; Additional folding */
+  { 0x01D434, 0, { 0x000061 }},           /* 1D434; 0061; Additional folding */
+  { 0x01D435, 0, { 0x000062 }},           /* 1D435; 0062; Additional folding */
+  { 0x01D436, 0, { 0x000063 }},           /* 1D436; 0063; Additional folding */
+  { 0x01D437, 0, { 0x000064 }},           /* 1D437; 0064; Additional folding */
+  { 0x01D438, 0, { 0x000065 }},           /* 1D438; 0065; Additional folding */
+  { 0x01D439, 0, { 0x000066 }},           /* 1D439; 0066; Additional folding */
+  { 0x01D43A, 0, { 0x000067 }},           /* 1D43A; 0067; Additional folding */
+  { 0x01D43B, 0, { 0x000068 }},           /* 1D43B; 0068; Additional folding */
+  { 0x01D43C, 0, { 0x000069 }},           /* 1D43C; 0069; Additional folding */
+  { 0x01D43D, 0, { 0x00006A }},           /* 1D43D; 006A; Additional folding */
+  { 0x01D43E, 0, { 0x00006B }},           /* 1D43E; 006B; Additional folding */
+  { 0x01D43F, 0, { 0x00006C }},           /* 1D43F; 006C; Additional folding */
+  { 0x01D440, 0, { 0x00006D }},           /* 1D440; 006D; Additional folding */
+  { 0x01D441, 0, { 0x00006E }},           /* 1D441; 006E; Additional folding */
+  { 0x01D442, 0, { 0x00006F }},           /* 1D442; 006F; Additional folding */
+  { 0x01D443, 0, { 0x000070 }},           /* 1D443; 0070; Additional folding */
+  { 0x01D444, 0, { 0x000071 }},           /* 1D444; 0071; Additional folding */
+  { 0x01D445, 0, { 0x000072 }},           /* 1D445; 0072; Additional folding */
+  { 0x01D446, 0, { 0x000073 }},           /* 1D446; 0073; Additional folding */
+  { 0x01D447, 0, { 0x000074 }},           /* 1D447; 0074; Additional folding */
+  { 0x01D448, 0, { 0x000075 }},           /* 1D448; 0075; Additional folding */
+  { 0x01D449, 0, { 0x000076 }},           /* 1D449; 0076; Additional folding */
+  { 0x01D44A, 0, { 0x000077 }},           /* 1D44A; 0077; Additional folding */
+  { 0x01D44B, 0, { 0x000078 }},           /* 1D44B; 0078; Additional folding */
+  { 0x01D44C, 0, { 0x000079 }},           /* 1D44C; 0079; Additional folding */
+  { 0x01D44D, 0, { 0x00007A }},           /* 1D44D; 007A; Additional folding */
+  { 0x01D468, 0, { 0x000061 }},           /* 1D468; 0061; Additional folding */
+  { 0x01D469, 0, { 0x000062 }},           /* 1D469; 0062; Additional folding */
+  { 0x01D46A, 0, { 0x000063 }},           /* 1D46A; 0063; Additional folding */
+  { 0x01D46B, 0, { 0x000064 }},           /* 1D46B; 0064; Additional folding */
+  { 0x01D46C, 0, { 0x000065 }},           /* 1D46C; 0065; Additional folding */
+  { 0x01D46D, 0, { 0x000066 }},           /* 1D46D; 0066; Additional folding */
+  { 0x01D46E, 0, { 0x000067 }},           /* 1D46E; 0067; Additional folding */
+  { 0x01D46F, 0, { 0x000068 }},           /* 1D46F; 0068; Additional folding */
+  { 0x01D470, 0, { 0x000069 }},           /* 1D470; 0069; Additional folding */
+  { 0x01D471, 0, { 0x00006A }},           /* 1D471; 006A; Additional folding */
+  { 0x01D472, 0, { 0x00006B }},           /* 1D472; 006B; Additional folding */
+  { 0x01D473, 0, { 0x00006C }},           /* 1D473; 006C; Additional folding */
+  { 0x01D474, 0, { 0x00006D }},           /* 1D474; 006D; Additional folding */
+  { 0x01D475, 0, { 0x00006E }},           /* 1D475; 006E; Additional folding */
+  { 0x01D476, 0, { 0x00006F }},           /* 1D476; 006F; Additional folding */
+  { 0x01D477, 0, { 0x000070 }},           /* 1D477; 0070; Additional folding */
+  { 0x01D478, 0, { 0x000071 }},           /* 1D478; 0071; Additional folding */
+  { 0x01D479, 0, { 0x000072 }},           /* 1D479; 0072; Additional folding */
+  { 0x01D47A, 0, { 0x000073 }},           /* 1D47A; 0073; Additional folding */
+  { 0x01D47B, 0, { 0x000074 }},           /* 1D47B; 0074; Additional folding */
+  { 0x01D47C, 0, { 0x000075 }},           /* 1D47C; 0075; Additional folding */
+  { 0x01D47D, 0, { 0x000076 }},           /* 1D47D; 0076; Additional folding */
+  { 0x01D47E, 0, { 0x000077 }},           /* 1D47E; 0077; Additional folding */
+  { 0x01D47F, 0, { 0x000078 }},           /* 1D47F; 0078; Additional folding */
+  { 0x01D480, 0, { 0x000079 }},           /* 1D480; 0079; Additional folding */
+  { 0x01D481, 0, { 0x00007A }},           /* 1D481; 007A; Additional folding */
+  { 0x01D49C, 0, { 0x000061 }},           /* 1D49C; 0061; Additional folding */
+  { 0x01D49E, 0, { 0x000063 }},           /* 1D49E; 0063; Additional folding */
+  { 0x01D49F, 0, { 0x000064 }},           /* 1D49F; 0064; Additional folding */
+  { 0x01D4A2, 0, { 0x000067 }},           /* 1D4A2; 0067; Additional folding */
+  { 0x01D4A5, 0, { 0x00006A }},           /* 1D4A5; 006A; Additional folding */
+  { 0x01D4A6, 0, { 0x00006B }},           /* 1D4A6; 006B; Additional folding */
+  { 0x01D4A9, 0, { 0x00006E }},           /* 1D4A9; 006E; Additional folding */
+  { 0x01D4AA, 0, { 0x00006F }},           /* 1D4AA; 006F; Additional folding */
+  { 0x01D4AB, 0, { 0x000070 }},           /* 1D4AB; 0070; Additional folding */
+  { 0x01D4AC, 0, { 0x000071 }},           /* 1D4AC; 0071; Additional folding */
+  { 0x01D4AE, 0, { 0x000073 }},           /* 1D4AE; 0073; Additional folding */
+  { 0x01D4AF, 0, { 0x000074 }},           /* 1D4AF; 0074; Additional folding */
+  { 0x01D4B0, 0, { 0x000075 }},           /* 1D4B0; 0075; Additional folding */
+  { 0x01D4B1, 0, { 0x000076 }},           /* 1D4B1; 0076; Additional folding */
+  { 0x01D4B2, 0, { 0x000077 }},           /* 1D4B2; 0077; Additional folding */
+  { 0x01D4B3, 0, { 0x000078 }},           /* 1D4B3; 0078; Additional folding */
+  { 0x01D4B4, 0, { 0x000079 }},           /* 1D4B4; 0079; Additional folding */
+  { 0x01D4B5, 0, { 0x00007A }},           /* 1D4B5; 007A; Additional folding */
+  { 0x01D4D0, 0, { 0x000061 }},           /* 1D4D0; 0061; Additional folding */
+  { 0x01D4D1, 0, { 0x000062 }},           /* 1D4D1; 0062; Additional folding */
+  { 0x01D4D2, 0, { 0x000063 }},           /* 1D4D2; 0063; Additional folding */
+  { 0x01D4D3, 0, { 0x000064 }},           /* 1D4D3; 0064; Additional folding */
+  { 0x01D4D4, 0, { 0x000065 }},           /* 1D4D4; 0065; Additional folding */
+  { 0x01D4D5, 0, { 0x000066 }},           /* 1D4D5; 0066; Additional folding */
+  { 0x01D4D6, 0, { 0x000067 }},           /* 1D4D6; 0067; Additional folding */
+  { 0x01D4D7, 0, { 0x000068 }},           /* 1D4D7; 0068; Additional folding */
+  { 0x01D4D8, 0, { 0x000069 }},           /* 1D4D8; 0069; Additional folding */
+  { 0x01D4D9, 0, { 0x00006A }},           /* 1D4D9; 006A; Additional folding */
+  { 0x01D4DA, 0, { 0x00006B }},           /* 1D4DA; 006B; Additional folding */
+  { 0x01D4DB, 0, { 0x00006C }},           /* 1D4DB; 006C; Additional folding */
+  { 0x01D4DC, 0, { 0x00006D }},           /* 1D4DC; 006D; Additional folding */
+  { 0x01D4DD, 0, { 0x00006E }},           /* 1D4DD; 006E; Additional folding */
+  { 0x01D4DE, 0, { 0x00006F }},           /* 1D4DE; 006F; Additional folding */
+  { 0x01D4DF, 0, { 0x000070 }},           /* 1D4DF; 0070; Additional folding */
+  { 0x01D4E0, 0, { 0x000071 }},           /* 1D4E0; 0071; Additional folding */
+  { 0x01D4E1, 0, { 0x000072 }},           /* 1D4E1; 0072; Additional folding */
+  { 0x01D4E2, 0, { 0x000073 }},           /* 1D4E2; 0073; Additional folding */
+  { 0x01D4E3, 0, { 0x000074 }},           /* 1D4E3; 0074; Additional folding */
+  { 0x01D4E4, 0, { 0x000075 }},           /* 1D4E4; 0075; Additional folding */
+  { 0x01D4E5, 0, { 0x000076 }},           /* 1D4E5; 0076; Additional folding */
+  { 0x01D4E6, 0, { 0x000077 }},           /* 1D4E6; 0077; Additional folding */
+  { 0x01D4E7, 0, { 0x000078 }},           /* 1D4E7; 0078; Additional folding */
+  { 0x01D4E8, 0, { 0x000079 }},           /* 1D4E8; 0079; Additional folding */
+  { 0x01D4E9, 0, { 0x00007A }},           /* 1D4E9; 007A; Additional folding */
+  { 0x01D504, 0, { 0x000061 }},           /* 1D504; 0061; Additional folding */
+  { 0x01D505, 0, { 0x000062 }},           /* 1D505; 0062; Additional folding */
+  { 0x01D507, 0, { 0x000064 }},           /* 1D507; 0064; Additional folding */
+  { 0x01D508, 0, { 0x000065 }},           /* 1D508; 0065; Additional folding */
+  { 0x01D509, 0, { 0x000066 }},           /* 1D509; 0066; Additional folding */
+  { 0x01D50A, 0, { 0x000067 }},           /* 1D50A; 0067; Additional folding */
+  { 0x01D50D, 0, { 0x00006A }},           /* 1D50D; 006A; Additional folding */
+  { 0x01D50E, 0, { 0x00006B }},           /* 1D50E; 006B; Additional folding */
+  { 0x01D50F, 0, { 0x00006C }},           /* 1D50F; 006C; Additional folding */
+  { 0x01D510, 0, { 0x00006D }},           /* 1D510; 006D; Additional folding */
+  { 0x01D511, 0, { 0x00006E }},           /* 1D511; 006E; Additional folding */
+  { 0x01D512, 0, { 0x00006F }},           /* 1D512; 006F; Additional folding */
+  { 0x01D513, 0, { 0x000070 }},           /* 1D513; 0070; Additional folding */
+  { 0x01D514, 0, { 0x000071 }},           /* 1D514; 0071; Additional folding */
+  { 0x01D516, 0, { 0x000073 }},           /* 1D516; 0073; Additional folding */
+  { 0x01D517, 0, { 0x000074 }},           /* 1D517; 0074; Additional folding */
+  { 0x01D518, 0, { 0x000075 }},           /* 1D518; 0075; Additional folding */
+  { 0x01D519, 0, { 0x000076 }},           /* 1D519; 0076; Additional folding */
+  { 0x01D51A, 0, { 0x000077 }},           /* 1D51A; 0077; Additional folding */
+  { 0x01D51B, 0, { 0x000078 }},           /* 1D51B; 0078; Additional folding */
+  { 0x01D51C, 0, { 0x000079 }},           /* 1D51C; 0079; Additional folding */
+  { 0x01D538, 0, { 0x000061 }},           /* 1D538; 0061; Additional folding */
+  { 0x01D539, 0, { 0x000062 }},           /* 1D539; 0062; Additional folding */
+  { 0x01D53B, 0, { 0x000064 }},           /* 1D53B; 0064; Additional folding */
+  { 0x01D53C, 0, { 0x000065 }},           /* 1D53C; 0065; Additional folding */
+  { 0x01D53D, 0, { 0x000066 }},           /* 1D53D; 0066; Additional folding */
+  { 0x01D53E, 0, { 0x000067 }},           /* 1D53E; 0067; Additional folding */
+  { 0x01D540, 0, { 0x000069 }},           /* 1D540; 0069; Additional folding */
+  { 0x01D541, 0, { 0x00006A }},           /* 1D541; 006A; Additional folding */
+  { 0x01D542, 0, { 0x00006B }},           /* 1D542; 006B; Additional folding */
+  { 0x01D543, 0, { 0x00006C }},           /* 1D543; 006C; Additional folding */
+  { 0x01D544, 0, { 0x00006D }},           /* 1D544; 006D; Additional folding */
+  { 0x01D546, 0, { 0x00006F }},           /* 1D546; 006F; Additional folding */
+  { 0x01D54A, 0, { 0x000073 }},           /* 1D54A; 0073; Additional folding */
+  { 0x01D54B, 0, { 0x000074 }},           /* 1D54B; 0074; Additional folding */
+  { 0x01D54C, 0, { 0x000075 }},           /* 1D54C; 0075; Additional folding */
+  { 0x01D54D, 0, { 0x000076 }},           /* 1D54D; 0076; Additional folding */
+  { 0x01D54E, 0, { 0x000077 }},           /* 1D54E; 0077; Additional folding */
+  { 0x01D54F, 0, { 0x000078 }},           /* 1D54F; 0078; Additional folding */
+  { 0x01D550, 0, { 0x000079 }},           /* 1D550; 0079; Additional folding */
+  { 0x01D56C, 0, { 0x000061 }},           /* 1D56C; 0061; Additional folding */
+  { 0x01D56D, 0, { 0x000062 }},           /* 1D56D; 0062; Additional folding */
+  { 0x01D56E, 0, { 0x000063 }},           /* 1D56E; 0063; Additional folding */
+  { 0x01D56F, 0, { 0x000064 }},           /* 1D56F; 0064; Additional folding */
+  { 0x01D570, 0, { 0x000065 }},           /* 1D570; 0065; Additional folding */
+  { 0x01D571, 0, { 0x000066 }},           /* 1D571; 0066; Additional folding */
+  { 0x01D572, 0, { 0x000067 }},           /* 1D572; 0067; Additional folding */
+  { 0x01D573, 0, { 0x000068 }},           /* 1D573; 0068; Additional folding */
+  { 0x01D574, 0, { 0x000069 }},           /* 1D574; 0069; Additional folding */
+  { 0x01D575, 0, { 0x00006A }},           /* 1D575; 006A; Additional folding */
+  { 0x01D576, 0, { 0x00006B }},           /* 1D576; 006B; Additional folding */
+  { 0x01D577, 0, { 0x00006C }},           /* 1D577; 006C; Additional folding */
+  { 0x01D578, 0, { 0x00006D }},           /* 1D578; 006D; Additional folding */
+  { 0x01D579, 0, { 0x00006E }},           /* 1D579; 006E; Additional folding */
+  { 0x01D57A, 0, { 0x00006F }},           /* 1D57A; 006F; Additional folding */
+  { 0x01D57B, 0, { 0x000070 }},           /* 1D57B; 0070; Additional folding */
+  { 0x01D57C, 0, { 0x000071 }},           /* 1D57C; 0071; Additional folding */
+  { 0x01D57D, 0, { 0x000072 }},           /* 1D57D; 0072; Additional folding */
+  { 0x01D57E, 0, { 0x000073 }},           /* 1D57E; 0073; Additional folding */
+  { 0x01D57F, 0, { 0x000074 }},           /* 1D57F; 0074; Additional folding */
+  { 0x01D580, 0, { 0x000075 }},           /* 1D580; 0075; Additional folding */
+  { 0x01D581, 0, { 0x000076 }},           /* 1D581; 0076; Additional folding */
+  { 0x01D582, 0, { 0x000077 }},           /* 1D582; 0077; Additional folding */
+  { 0x01D583, 0, { 0x000078 }},           /* 1D583; 0078; Additional folding */
+  { 0x01D584, 0, { 0x000079 }},           /* 1D584; 0079; Additional folding */
+  { 0x01D585, 0, { 0x00007A }},           /* 1D585; 007A; Additional folding */
+  { 0x01D5A0, 0, { 0x000061 }},           /* 1D5A0; 0061; Additional folding */
+  { 0x01D5A1, 0, { 0x000062 }},           /* 1D5A1; 0062; Additional folding */
+  { 0x01D5A2, 0, { 0x000063 }},           /* 1D5A2; 0063; Additional folding */
+  { 0x01D5A3, 0, { 0x000064 }},           /* 1D5A3; 0064; Additional folding */
+  { 0x01D5A4, 0, { 0x000065 }},           /* 1D5A4; 0065; Additional folding */
+  { 0x01D5A5, 0, { 0x000066 }},           /* 1D5A5; 0066; Additional folding */
+  { 0x01D5A6, 0, { 0x000067 }},           /* 1D5A6; 0067; Additional folding */
+  { 0x01D5A7, 0, { 0x000068 }},           /* 1D5A7; 0068; Additional folding */
+  { 0x01D5A8, 0, { 0x000069 }},           /* 1D5A8; 0069; Additional folding */
+  { 0x01D5A9, 0, { 0x00006A }},           /* 1D5A9; 006A; Additional folding */
+  { 0x01D5AA, 0, { 0x00006B }},           /* 1D5AA; 006B; Additional folding */
+  { 0x01D5AB, 0, { 0x00006C }},           /* 1D5AB; 006C; Additional folding */
+  { 0x01D5AC, 0, { 0x00006D }},           /* 1D5AC; 006D; Additional folding */
+  { 0x01D5AD, 0, { 0x00006E }},           /* 1D5AD; 006E; Additional folding */
+  { 0x01D5AE, 0, { 0x00006F }},           /* 1D5AE; 006F; Additional folding */
+  { 0x01D5AF, 0, { 0x000070 }},           /* 1D5AF; 0070; Additional folding */
+  { 0x01D5B0, 0, { 0x000071 }},           /* 1D5B0; 0071; Additional folding */
+  { 0x01D5B1, 0, { 0x000072 }},           /* 1D5B1; 0072; Additional folding */
+  { 0x01D5B2, 0, { 0x000073 }},           /* 1D5B2; 0073; Additional folding */
+  { 0x01D5B3, 0, { 0x000074 }},           /* 1D5B3; 0074; Additional folding */
+  { 0x01D5B4, 0, { 0x000075 }},           /* 1D5B4; 0075; Additional folding */
+  { 0x01D5B5, 0, { 0x000076 }},           /* 1D5B5; 0076; Additional folding */
+  { 0x01D5B6, 0, { 0x000077 }},           /* 1D5B6; 0077; Additional folding */
+  { 0x01D5B7, 0, { 0x000078 }},           /* 1D5B7; 0078; Additional folding */
+  { 0x01D5B8, 0, { 0x000079 }},           /* 1D5B8; 0079; Additional folding */
+  { 0x01D5B9, 0, { 0x00007A }},           /* 1D5B9; 007A; Additional folding */
+  { 0x01D5D4, 0, { 0x000061 }},           /* 1D5D4; 0061; Additional folding */
+  { 0x01D5D5, 0, { 0x000062 }},           /* 1D5D5; 0062; Additional folding */
+  { 0x01D5D6, 0, { 0x000063 }},           /* 1D5D6; 0063; Additional folding */
+  { 0x01D5D7, 0, { 0x000064 }},           /* 1D5D7; 0064; Additional folding */
+  { 0x01D5D8, 0, { 0x000065 }},           /* 1D5D8; 0065; Additional folding */
+  { 0x01D5D9, 0, { 0x000066 }},           /* 1D5D9; 0066; Additional folding */
+  { 0x01D5DA, 0, { 0x000067 }},           /* 1D5DA; 0067; Additional folding */
+  { 0x01D5DB, 0, { 0x000068 }},           /* 1D5DB; 0068; Additional folding */
+  { 0x01D5DC, 0, { 0x000069 }},           /* 1D5DC; 0069; Additional folding */
+  { 0x01D5DD, 0, { 0x00006A }},           /* 1D5DD; 006A; Additional folding */
+  { 0x01D5DE, 0, { 0x00006B }},           /* 1D5DE; 006B; Additional folding */
+  { 0x01D5DF, 0, { 0x00006C }},           /* 1D5DF; 006C; Additional folding */
+  { 0x01D5E0, 0, { 0x00006D }},           /* 1D5E0; 006D; Additional folding */
+  { 0x01D5E1, 0, { 0x00006E }},           /* 1D5E1; 006E; Additional folding */
+  { 0x01D5E2, 0, { 0x00006F }},           /* 1D5E2; 006F; Additional folding */
+  { 0x01D5E3, 0, { 0x000070 }},           /* 1D5E3; 0070; Additional folding */
+  { 0x01D5E4, 0, { 0x000071 }},           /* 1D5E4; 0071; Additional folding */
+  { 0x01D5E5, 0, { 0x000072 }},           /* 1D5E5; 0072; Additional folding */
+  { 0x01D5E6, 0, { 0x000073 }},           /* 1D5E6; 0073; Additional folding */
+  { 0x01D5E7, 0, { 0x000074 }},           /* 1D5E7; 0074; Additional folding */
+  { 0x01D5E8, 0, { 0x000075 }},           /* 1D5E8; 0075; Additional folding */
+  { 0x01D5E9, 0, { 0x000076 }},           /* 1D5E9; 0076; Additional folding */
+  { 0x01D5EA, 0, { 0x000077 }},           /* 1D5EA; 0077; Additional folding */
+  { 0x01D5EB, 0, { 0x000078 }},           /* 1D5EB; 0078; Additional folding */
+  { 0x01D5EC, 0, { 0x000079 }},           /* 1D5EC; 0079; Additional folding */
+  { 0x01D5ED, 0, { 0x00007A }},           /* 1D5ED; 007A; Additional folding */
+  { 0x01D608, 0, { 0x000061 }},           /* 1D608; 0061; Additional folding */
+  { 0x01D609, 0, { 0x000062 }},           /* 1D609; 0062; Additional folding */
+  { 0x01D60A, 0, { 0x000063 }},           /* 1D60A; 0063; Additional folding */
+  { 0x01D60B, 0, { 0x000064 }},           /* 1D60B; 0064; Additional folding */
+  { 0x01D60C, 0, { 0x000065 }},           /* 1D60C; 0065; Additional folding */
+  { 0x01D60D, 0, { 0x000066 }},           /* 1D60D; 0066; Additional folding */
+  { 0x01D60E, 0, { 0x000067 }},           /* 1D60E; 0067; Additional folding */
+  { 0x01D60F, 0, { 0x000068 }},           /* 1D60F; 0068; Additional folding */
+  { 0x01D610, 0, { 0x000069 }},           /* 1D610; 0069; Additional folding */
+  { 0x01D611, 0, { 0x00006A }},           /* 1D611; 006A; Additional folding */
+  { 0x01D612, 0, { 0x00006B }},           /* 1D612; 006B; Additional folding */
+  { 0x01D613, 0, { 0x00006C }},           /* 1D613; 006C; Additional folding */
+  { 0x01D614, 0, { 0x00006D }},           /* 1D614; 006D; Additional folding */
+  { 0x01D615, 0, { 0x00006E }},           /* 1D615; 006E; Additional folding */
+  { 0x01D616, 0, { 0x00006F }},           /* 1D616; 006F; Additional folding */
+  { 0x01D617, 0, { 0x000070 }},           /* 1D617; 0070; Additional folding */
+  { 0x01D618, 0, { 0x000071 }},           /* 1D618; 0071; Additional folding */
+  { 0x01D619, 0, { 0x000072 }},           /* 1D619; 0072; Additional folding */
+  { 0x01D61A, 0, { 0x000073 }},           /* 1D61A; 0073; Additional folding */
+  { 0x01D61B, 0, { 0x000074 }},           /* 1D61B; 0074; Additional folding */
+  { 0x01D61C, 0, { 0x000075 }},           /* 1D61C; 0075; Additional folding */
+  { 0x01D61D, 0, { 0x000076 }},           /* 1D61D; 0076; Additional folding */
+  { 0x01D61E, 0, { 0x000077 }},           /* 1D61E; 0077; Additional folding */
+  { 0x01D61F, 0, { 0x000078 }},           /* 1D61F; 0078; Additional folding */
+  { 0x01D620, 0, { 0x000079 }},           /* 1D620; 0079; Additional folding */
+  { 0x01D621, 0, { 0x00007A }},           /* 1D621; 007A; Additional folding */
+  { 0x01D63C, 0, { 0x000061 }},           /* 1D63C; 0061; Additional folding */
+  { 0x01D63D, 0, { 0x000062 }},           /* 1D63D; 0062; Additional folding */
+  { 0x01D63E, 0, { 0x000063 }},           /* 1D63E; 0063; Additional folding */
+  { 0x01D63F, 0, { 0x000064 }},           /* 1D63F; 0064; Additional folding */
+  { 0x01D640, 0, { 0x000065 }},           /* 1D640; 0065; Additional folding */
+  { 0x01D641, 0, { 0x000066 }},           /* 1D641; 0066; Additional folding */
+  { 0x01D642, 0, { 0x000067 }},           /* 1D642; 0067; Additional folding */
+  { 0x01D643, 0, { 0x000068 }},           /* 1D643; 0068; Additional folding */
+  { 0x01D644, 0, { 0x000069 }},           /* 1D644; 0069; Additional folding */
+  { 0x01D645, 0, { 0x00006A }},           /* 1D645; 006A; Additional folding */
+  { 0x01D646, 0, { 0x00006B }},           /* 1D646; 006B; Additional folding */
+  { 0x01D647, 0, { 0x00006C }},           /* 1D647; 006C; Additional folding */
+  { 0x01D648, 0, { 0x00006D }},           /* 1D648; 006D; Additional folding */
+  { 0x01D649, 0, { 0x00006E }},           /* 1D649; 006E; Additional folding */
+  { 0x01D64A, 0, { 0x00006F }},           /* 1D64A; 006F; Additional folding */
+  { 0x01D64B, 0, { 0x000070 }},           /* 1D64B; 0070; Additional folding */
+  { 0x01D64C, 0, { 0x000071 }},           /* 1D64C; 0071; Additional folding */
+  { 0x01D64D, 0, { 0x000072 }},           /* 1D64D; 0072; Additional folding */
+  { 0x01D64E, 0, { 0x000073 }},           /* 1D64E; 0073; Additional folding */
+  { 0x01D64F, 0, { 0x000074 }},           /* 1D64F; 0074; Additional folding */
+  { 0x01D650, 0, { 0x000075 }},           /* 1D650; 0075; Additional folding */
+  { 0x01D651, 0, { 0x000076 }},           /* 1D651; 0076; Additional folding */
+  { 0x01D652, 0, { 0x000077 }},           /* 1D652; 0077; Additional folding */
+  { 0x01D653, 0, { 0x000078 }},           /* 1D653; 0078; Additional folding */
+  { 0x01D654, 0, { 0x000079 }},           /* 1D654; 0079; Additional folding */
+  { 0x01D655, 0, { 0x00007A }},           /* 1D655; 007A; Additional folding */
+  { 0x01D670, 0, { 0x000061 }},           /* 1D670; 0061; Additional folding */
+  { 0x01D671, 0, { 0x000062 }},           /* 1D671; 0062; Additional folding */
+  { 0x01D672, 0, { 0x000063 }},           /* 1D672; 0063; Additional folding */
+  { 0x01D673, 0, { 0x000064 }},           /* 1D673; 0064; Additional folding */
+  { 0x01D674, 0, { 0x000065 }},           /* 1D674; 0065; Additional folding */
+  { 0x01D675, 0, { 0x000066 }},           /* 1D675; 0066; Additional folding */
+  { 0x01D676, 0, { 0x000067 }},           /* 1D676; 0067; Additional folding */
+  { 0x01D677, 0, { 0x000068 }},           /* 1D677; 0068; Additional folding */
+  { 0x01D678, 0, { 0x000069 }},           /* 1D678; 0069; Additional folding */
+  { 0x01D679, 0, { 0x00006A }},           /* 1D679; 006A; Additional folding */
+  { 0x01D67A, 0, { 0x00006B }},           /* 1D67A; 006B; Additional folding */
+  { 0x01D67B, 0, { 0x00006C }},           /* 1D67B; 006C; Additional folding */
+  { 0x01D67C, 0, { 0x00006D }},           /* 1D67C; 006D; Additional folding */
+  { 0x01D67D, 0, { 0x00006E }},           /* 1D67D; 006E; Additional folding */
+  { 0x01D67E, 0, { 0x00006F }},           /* 1D67E; 006F; Additional folding */
+  { 0x01D67F, 0, { 0x000070 }},           /* 1D67F; 0070; Additional folding */
+  { 0x01D680, 0, { 0x000071 }},           /* 1D680; 0071; Additional folding */
+  { 0x01D681, 0, { 0x000072 }},           /* 1D681; 0072; Additional folding */
+  { 0x01D682, 0, { 0x000073 }},           /* 1D682; 0073; Additional folding */
+  { 0x01D683, 0, { 0x000074 }},           /* 1D683; 0074; Additional folding */
+  { 0x01D684, 0, { 0x000075 }},           /* 1D684; 0075; Additional folding */
+  { 0x01D685, 0, { 0x000076 }},           /* 1D685; 0076; Additional folding */
+  { 0x01D686, 0, { 0x000077 }},           /* 1D686; 0077; Additional folding */
+  { 0x01D687, 0, { 0x000078 }},           /* 1D687; 0078; Additional folding */
+  { 0x01D688, 0, { 0x000079 }},           /* 1D688; 0079; Additional folding */
+  { 0x01D689, 0, { 0x00007A }},           /* 1D689; 007A; Additional folding */
+  { 0x01D6A8, 0, { 0x0003B1 }},           /* 1D6A8; 03B1; Additional folding */
+  { 0x01D6A9, 0, { 0x0003B2 }},           /* 1D6A9; 03B2; Additional folding */
+  { 0x01D6AA, 0, { 0x0003B3 }},           /* 1D6AA; 03B3; Additional folding */
+  { 0x01D6AB, 0, { 0x0003B4 }},           /* 1D6AB; 03B4; Additional folding */
+  { 0x01D6AC, 0, { 0x0003B5 }},           /* 1D6AC; 03B5; Additional folding */
+  { 0x01D6AD, 0, { 0x0003B6 }},           /* 1D6AD; 03B6; Additional folding */
+  { 0x01D6AE, 0, { 0x0003B7 }},           /* 1D6AE; 03B7; Additional folding */
+  { 0x01D6AF, 0, { 0x0003B8 }},           /* 1D6AF; 03B8; Additional folding */
+  { 0x01D6B0, 0, { 0x0003B9 }},           /* 1D6B0; 03B9; Additional folding */
+  { 0x01D6B1, 0, { 0x0003BA }},           /* 1D6B1; 03BA; Additional folding */
+  { 0x01D6B2, 0, { 0x0003BB }},           /* 1D6B2; 03BB; Additional folding */
+  { 0x01D6B3, 0, { 0x0003BC }},           /* 1D6B3; 03BC; Additional folding */
+  { 0x01D6B4, 0, { 0x0003BD }},           /* 1D6B4; 03BD; Additional folding */
+  { 0x01D6B5, 0, { 0x0003BE }},           /* 1D6B5; 03BE; Additional folding */
+  { 0x01D6B6, 0, { 0x0003BF }},           /* 1D6B6; 03BF; Additional folding */
+  { 0x01D6B7, 0, { 0x0003C0 }},           /* 1D6B7; 03C0; Additional folding */
+  { 0x01D6B8, 0, { 0x0003C1 }},           /* 1D6B8; 03C1; Additional folding */
+  { 0x01D6B9, 0, { 0x0003B8 }},           /* 1D6B9; 03B8; Additional folding */
+  { 0x01D6BA, 0, { 0x0003C3 }},           /* 1D6BA; 03C3; Additional folding */
+  { 0x01D6BB, 0, { 0x0003C4 }},           /* 1D6BB; 03C4; Additional folding */
+  { 0x01D6BC, 0, { 0x0003C5 }},           /* 1D6BC; 03C5; Additional folding */
+  { 0x01D6BD, 0, { 0x0003C6 }},           /* 1D6BD; 03C6; Additional folding */
+  { 0x01D6BE, 0, { 0x0003C7 }},           /* 1D6BE; 03C7; Additional folding */
+  { 0x01D6BF, 0, { 0x0003C8 }},           /* 1D6BF; 03C8; Additional folding */
+  { 0x01D6C0, 0, { 0x0003C9 }},           /* 1D6C0; 03C9; Additional folding */
+  { 0x01D6D3, 0, { 0x0003C3 }},           /* 1D6D3; 03C3; Additional folding */
+  { 0x01D6E2, 0, { 0x0003B1 }},           /* 1D6E2; 03B1; Additional folding */
+  { 0x01D6E3, 0, { 0x0003B2 }},           /* 1D6E3; 03B2; Additional folding */
+  { 0x01D6E4, 0, { 0x0003B3 }},           /* 1D6E4; 03B3; Additional folding */
+  { 0x01D6E5, 0, { 0x0003B4 }},           /* 1D6E5; 03B4; Additional folding */
+  { 0x01D6E6, 0, { 0x0003B5 }},           /* 1D6E6; 03B5; Additional folding */
+  { 0x01D6E7, 0, { 0x0003B6 }},           /* 1D6E7; 03B6; Additional folding */
+  { 0x01D6E8, 0, { 0x0003B7 }},           /* 1D6E8; 03B7; Additional folding */
+  { 0x01D6E9, 0, { 0x0003B8 }},           /* 1D6E9; 03B8; Additional folding */
+  { 0x01D6EA, 0, { 0x0003B9 }},           /* 1D6EA; 03B9; Additional folding */
+  { 0x01D6EB, 0, { 0x0003BA }},           /* 1D6EB; 03BA; Additional folding */
+  { 0x01D6EC, 0, { 0x0003BB }},           /* 1D6EC; 03BB; Additional folding */
+  { 0x01D6ED, 0, { 0x0003BC }},           /* 1D6ED; 03BC; Additional folding */
+  { 0x01D6EE, 0, { 0x0003BD }},           /* 1D6EE; 03BD; Additional folding */
+  { 0x01D6EF, 0, { 0x0003BE }},           /* 1D6EF; 03BE; Additional folding */
+  { 0x01D6F0, 0, { 0x0003BF }},           /* 1D6F0; 03BF; Additional folding */
+  { 0x01D6F1, 0, { 0x0003C0 }},           /* 1D6F1; 03C0; Additional folding */
+  { 0x01D6F2, 0, { 0x0003C1 }},           /* 1D6F2; 03C1; Additional folding */
+  { 0x01D6F3, 0, { 0x0003B8 }},           /* 1D6F3; 03B8; Additional folding */
+  { 0x01D6F4, 0, { 0x0003C3 }},           /* 1D6F4; 03C3; Additional folding */
+  { 0x01D6F5, 0, { 0x0003C4 }},           /* 1D6F5; 03C4; Additional folding */
+  { 0x01D6F6, 0, { 0x0003C5 }},           /* 1D6F6; 03C5; Additional folding */
+  { 0x01D6F7, 0, { 0x0003C6 }},           /* 1D6F7; 03C6; Additional folding */
+  { 0x01D6F8, 0, { 0x0003C7 }},           /* 1D6F8; 03C7; Additional folding */
+  { 0x01D6F9, 0, { 0x0003C8 }},           /* 1D6F9; 03C8; Additional folding */
+  { 0x01D6FA, 0, { 0x0003C9 }},           /* 1D6FA; 03C9; Additional folding */
+  { 0x01D70D, 0, { 0x0003C3 }},           /* 1D70D; 03C3; Additional folding */
+  { 0x01D71C, 0, { 0x0003B1 }},           /* 1D71C; 03B1; Additional folding */
+  { 0x01D71D, 0, { 0x0003B2 }},           /* 1D71D; 03B2; Additional folding */
+  { 0x01D71E, 0, { 0x0003B3 }},           /* 1D71E; 03B3; Additional folding */
+  { 0x01D71F, 0, { 0x0003B4 }},           /* 1D71F; 03B4; Additional folding */
+  { 0x01D720, 0, { 0x0003B5 }},           /* 1D720; 03B5; Additional folding */
+  { 0x01D721, 0, { 0x0003B6 }},           /* 1D721; 03B6; Additional folding */
+  { 0x01D722, 0, { 0x0003B7 }},           /* 1D722; 03B7; Additional folding */
+  { 0x01D723, 0, { 0x0003B8 }},           /* 1D723; 03B8; Additional folding */
+  { 0x01D724, 0, { 0x0003B9 }},           /* 1D724; 03B9; Additional folding */
+  { 0x01D725, 0, { 0x0003BA }},           /* 1D725; 03BA; Additional folding */
+  { 0x01D726, 0, { 0x0003BB }},           /* 1D726; 03BB; Additional folding */
+  { 0x01D727, 0, { 0x0003BC }},           /* 1D727; 03BC; Additional folding */
+  { 0x01D728, 0, { 0x0003BD }},           /* 1D728; 03BD; Additional folding */
+  { 0x01D729, 0, { 0x0003BE }},           /* 1D729; 03BE; Additional folding */
+  { 0x01D72A, 0, { 0x0003BF }},           /* 1D72A; 03BF; Additional folding */
+  { 0x01D72B, 0, { 0x0003C0 }},           /* 1D72B; 03C0; Additional folding */
+  { 0x01D72C, 0, { 0x0003C1 }},           /* 1D72C; 03C1; Additional folding */
+  { 0x01D72D, 0, { 0x0003B8 }},           /* 1D72D; 03B8; Additional folding */
+  { 0x01D72E, 0, { 0x0003C3 }},           /* 1D72E; 03C3; Additional folding */
+  { 0x01D72F, 0, { 0x0003C4 }},           /* 1D72F; 03C4; Additional folding */
+  { 0x01D730, 0, { 0x0003C5 }},           /* 1D730; 03C5; Additional folding */
+  { 0x01D731, 0, { 0x0003C6 }},           /* 1D731; 03C6; Additional folding */
+  { 0x01D732, 0, { 0x0003C7 }},           /* 1D732; 03C7; Additional folding */
+  { 0x01D733, 0, { 0x0003C8 }},           /* 1D733; 03C8; Additional folding */
+  { 0x01D734, 0, { 0x0003C9 }},           /* 1D734; 03C9; Additional folding */
+  { 0x01D747, 0, { 0x0003C3 }},           /* 1D747; 03C3; Additional folding */
+  { 0x01D756, 0, { 0x0003B1 }},           /* 1D756; 03B1; Additional folding */
+  { 0x01D757, 0, { 0x0003B2 }},           /* 1D757; 03B2; Additional folding */
+  { 0x01D758, 0, { 0x0003B3 }},           /* 1D758; 03B3; Additional folding */
+  { 0x01D759, 0, { 0x0003B4 }},           /* 1D759; 03B4; Additional folding */
+  { 0x01D75A, 0, { 0x0003B5 }},           /* 1D75A; 03B5; Additional folding */
+  { 0x01D75B, 0, { 0x0003B6 }},           /* 1D75B; 03B6; Additional folding */
+  { 0x01D75C, 0, { 0x0003B7 }},           /* 1D75C; 03B7; Additional folding */
+  { 0x01D75D, 0, { 0x0003B8 }},           /* 1D75D; 03B8; Additional folding */
+  { 0x01D75E, 0, { 0x0003B9 }},           /* 1D75E; 03B9; Additional folding */
+  { 0x01D75F, 0, { 0x0003BA }},           /* 1D75F; 03BA; Additional folding */
+  { 0x01D760, 0, { 0x0003BB }},           /* 1D760; 03BB; Additional folding */
+  { 0x01D761, 0, { 0x0003BC }},           /* 1D761; 03BC; Additional folding */
+  { 0x01D762, 0, { 0x0003BD }},           /* 1D762; 03BD; Additional folding */
+  { 0x01D763, 0, { 0x0003BE }},           /* 1D763; 03BE; Additional folding */
+  { 0x01D764, 0, { 0x0003BF }},           /* 1D764; 03BF; Additional folding */
+  { 0x01D765, 0, { 0x0003C0 }},           /* 1D765; 03C0; Additional folding */
+  { 0x01D766, 0, { 0x0003C1 }},           /* 1D766; 03C1; Additional folding */
+  { 0x01D767, 0, { 0x0003B8 }},           /* 1D767; 03B8; Additional folding */
+  { 0x01D768, 0, { 0x0003C3 }},           /* 1D768; 03C3; Additional folding */
+  { 0x01D769, 0, { 0x0003C4 }},           /* 1D769; 03C4; Additional folding */
+  { 0x01D76A, 0, { 0x0003C5 }},           /* 1D76A; 03C5; Additional folding */
+  { 0x01D76B, 0, { 0x0003C6 }},           /* 1D76B; 03C6; Additional folding */
+  { 0x01D76C, 0, { 0x0003C7 }},           /* 1D76C; 03C7; Additional folding */
+  { 0x01D76D, 0, { 0x0003C8 }},           /* 1D76D; 03C8; Additional folding */
+  { 0x01D76E, 0, { 0x0003C9 }},           /* 1D76E; 03C9; Additional folding */
+  { 0x01D781, 0, { 0x0003C3 }},           /* 1D781; 03C3; Additional folding */
+  { 0x01D790, 0, { 0x0003B1 }},           /* 1D790; 03B1; Additional folding */
+  { 0x01D791, 0, { 0x0003B2 }},           /* 1D791; 03B2; Additional folding */
+  { 0x01D792, 0, { 0x0003B3 }},           /* 1D792; 03B3; Additional folding */
+  { 0x01D793, 0, { 0x0003B4 }},           /* 1D793; 03B4; Additional folding */
+  { 0x01D794, 0, { 0x0003B5 }},           /* 1D794; 03B5; Additional folding */
+  { 0x01D795, 0, { 0x0003B6 }},           /* 1D795; 03B6; Additional folding */
+  { 0x01D796, 0, { 0x0003B7 }},           /* 1D796; 03B7; Additional folding */
+  { 0x01D797, 0, { 0x0003B8 }},           /* 1D797; 03B8; Additional folding */
+  { 0x01D798, 0, { 0x0003B9 }},           /* 1D798; 03B9; Additional folding */
+  { 0x01D799, 0, { 0x0003BA }},           /* 1D799; 03BA; Additional folding */
+  { 0x01D79A, 0, { 0x0003BB }},           /* 1D79A; 03BB; Additional folding */
+  { 0x01D79B, 0, { 0x0003BC }},           /* 1D79B; 03BC; Additional folding */
+  { 0x01D79C, 0, { 0x0003BD }},           /* 1D79C; 03BD; Additional folding */
+  { 0x01D79D, 0, { 0x0003BE }},           /* 1D79D; 03BE; Additional folding */
+  { 0x01D79E, 0, { 0x0003BF }},           /* 1D79E; 03BF; Additional folding */
+  { 0x01D79F, 0, { 0x0003C0 }},           /* 1D79F; 03C0; Additional folding */
+  { 0x01D7A0, 0, { 0x0003C1 }},           /* 1D7A0; 03C1; Additional folding */
+  { 0x01D7A1, 0, { 0x0003B8 }},           /* 1D7A1; 03B8; Additional folding */
+  { 0x01D7A2, 0, { 0x0003C3 }},           /* 1D7A2; 03C3; Additional folding */
+  { 0x01D7A3, 0, { 0x0003C4 }},           /* 1D7A3; 03C4; Additional folding */
+  { 0x01D7A4, 0, { 0x0003C5 }},           /* 1D7A4; 03C5; Additional folding */
+  { 0x01D7A5, 0, { 0x0003C6 }},           /* 1D7A5; 03C6; Additional folding */
+  { 0x01D7A6, 0, { 0x0003C7 }},           /* 1D7A6; 03C7; Additional folding */
+  { 0x01D7A7, 0, { 0x0003C8 }},           /* 1D7A7; 03C8; Additional folding */
+  { 0x01D7A8, 0, { 0x0003C9 }},           /* 1D7A8; 03C9; Additional folding */
+  { 0x01D7BB, 0, { 0x0003C3 }},           /* 1D7BB; 03C3; Additional folding */
+  { 0 },
+};
+
+
+/*
+ * B.3 Mapping for case-folding used with no normalization
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_B_3[] = {
+  { 0x000041, 0, { 0x000061 }},                      /* 0041; 0061; Case map */
+  { 0x000042, 0, { 0x000062 }},                      /* 0042; 0062; Case map */
+  { 0x000043, 0, { 0x000063 }},                      /* 0043; 0063; Case map */
+  { 0x000044, 0, { 0x000064 }},                      /* 0044; 0064; Case map */
+  { 0x000045, 0, { 0x000065 }},                      /* 0045; 0065; Case map */
+  { 0x000046, 0, { 0x000066 }},                      /* 0046; 0066; Case map */
+  { 0x000047, 0, { 0x000067 }},                      /* 0047; 0067; Case map */
+  { 0x000048, 0, { 0x000068 }},                      /* 0048; 0068; Case map */
+  { 0x000049, 0, { 0x000069 }},                      /* 0049; 0069; Case map */
+  { 0x00004A, 0, { 0x00006A }},                      /* 004A; 006A; Case map */
+  { 0x00004B, 0, { 0x00006B }},                      /* 004B; 006B; Case map */
+  { 0x00004C, 0, { 0x00006C }},                      /* 004C; 006C; Case map */
+  { 0x00004D, 0, { 0x00006D }},                      /* 004D; 006D; Case map */
+  { 0x00004E, 0, { 0x00006E }},                      /* 004E; 006E; Case map */
+  { 0x00004F, 0, { 0x00006F }},                      /* 004F; 006F; Case map */
+  { 0x000050, 0, { 0x000070 }},                      /* 0050; 0070; Case map */
+  { 0x000051, 0, { 0x000071 }},                      /* 0051; 0071; Case map */
+  { 0x000052, 0, { 0x000072 }},                      /* 0052; 0072; Case map */
+  { 0x000053, 0, { 0x000073 }},                      /* 0053; 0073; Case map */
+  { 0x000054, 0, { 0x000074 }},                      /* 0054; 0074; Case map */
+  { 0x000055, 0, { 0x000075 }},                      /* 0055; 0075; Case map */
+  { 0x000056, 0, { 0x000076 }},                      /* 0056; 0076; Case map */
+  { 0x000057, 0, { 0x000077 }},                      /* 0057; 0077; Case map */
+  { 0x000058, 0, { 0x000078 }},                      /* 0058; 0078; Case map */
+  { 0x000059, 0, { 0x000079 }},                      /* 0059; 0079; Case map */
+  { 0x00005A, 0, { 0x00007A }},                      /* 005A; 007A; Case map */
+  { 0x0000B5, 0, { 0x0003BC }},                      /* 00B5; 03BC; Case map */
+  { 0x0000C0, 0, { 0x0000E0 }},                      /* 00C0; 00E0; Case map */
+  { 0x0000C1, 0, { 0x0000E1 }},                      /* 00C1; 00E1; Case map */
+  { 0x0000C2, 0, { 0x0000E2 }},                      /* 00C2; 00E2; Case map */
+  { 0x0000C3, 0, { 0x0000E3 }},                      /* 00C3; 00E3; Case map */
+  { 0x0000C4, 0, { 0x0000E4 }},                      /* 00C4; 00E4; Case map */
+  { 0x0000C5, 0, { 0x0000E5 }},                      /* 00C5; 00E5; Case map */
+  { 0x0000C6, 0, { 0x0000E6 }},                      /* 00C6; 00E6; Case map */
+  { 0x0000C7, 0, { 0x0000E7 }},                      /* 00C7; 00E7; Case map */
+  { 0x0000C8, 0, { 0x0000E8 }},                      /* 00C8; 00E8; Case map */
+  { 0x0000C9, 0, { 0x0000E9 }},                      /* 00C9; 00E9; Case map */
+  { 0x0000CA, 0, { 0x0000EA }},                      /* 00CA; 00EA; Case map */
+  { 0x0000CB, 0, { 0x0000EB }},                      /* 00CB; 00EB; Case map */
+  { 0x0000CC, 0, { 0x0000EC }},                      /* 00CC; 00EC; Case map */
+  { 0x0000CD, 0, { 0x0000ED }},                      /* 00CD; 00ED; Case map */
+  { 0x0000CE, 0, { 0x0000EE }},                      /* 00CE; 00EE; Case map */
+  { 0x0000CF, 0, { 0x0000EF }},                      /* 00CF; 00EF; Case map */
+  { 0x0000D0, 0, { 0x0000F0 }},                      /* 00D0; 00F0; Case map */
+  { 0x0000D1, 0, { 0x0000F1 }},                      /* 00D1; 00F1; Case map */
+  { 0x0000D2, 0, { 0x0000F2 }},                      /* 00D2; 00F2; Case map */
+  { 0x0000D3, 0, { 0x0000F3 }},                      /* 00D3; 00F3; Case map */
+  { 0x0000D4, 0, { 0x0000F4 }},                      /* 00D4; 00F4; Case map */
+  { 0x0000D5, 0, { 0x0000F5 }},                      /* 00D5; 00F5; Case map */
+  { 0x0000D6, 0, { 0x0000F6 }},                      /* 00D6; 00F6; Case map */
+  { 0x0000D8, 0, { 0x0000F8 }},                      /* 00D8; 00F8; Case map */
+  { 0x0000D9, 0, { 0x0000F9 }},                      /* 00D9; 00F9; Case map */
+  { 0x0000DA, 0, { 0x0000FA }},                      /* 00DA; 00FA; Case map */
+  { 0x0000DB, 0, { 0x0000FB }},                      /* 00DB; 00FB; Case map */
+  { 0x0000DC, 0, { 0x0000FC }},                      /* 00DC; 00FC; Case map */
+  { 0x0000DD, 0, { 0x0000FD }},                      /* 00DD; 00FD; Case map */
+  { 0x0000DE, 0, { 0x0000FE }},                      /* 00DE; 00FE; Case map */
+  { 0x0000DF, 0, { 0x000073,                    /* 00DF; 0073 0073; Case map */
+                   0x000073 }},
+  { 0x000100, 0, { 0x000101 }},                      /* 0100; 0101; Case map */
+  { 0x000102, 0, { 0x000103 }},                      /* 0102; 0103; Case map */
+  { 0x000104, 0, { 0x000105 }},                      /* 0104; 0105; Case map */
+  { 0x000106, 0, { 0x000107 }},                      /* 0106; 0107; Case map */
+  { 0x000108, 0, { 0x000109 }},                      /* 0108; 0109; Case map */
+  { 0x00010A, 0, { 0x00010B }},                      /* 010A; 010B; Case map */
+  { 0x00010C, 0, { 0x00010D }},                      /* 010C; 010D; Case map */
+  { 0x00010E, 0, { 0x00010F }},                      /* 010E; 010F; Case map */
+  { 0x000110, 0, { 0x000111 }},                      /* 0110; 0111; Case map */
+  { 0x000112, 0, { 0x000113 }},                      /* 0112; 0113; Case map */
+  { 0x000114, 0, { 0x000115 }},                      /* 0114; 0115; Case map */
+  { 0x000116, 0, { 0x000117 }},                      /* 0116; 0117; Case map */
+  { 0x000118, 0, { 0x000119 }},                      /* 0118; 0119; Case map */
+  { 0x00011A, 0, { 0x00011B }},                      /* 011A; 011B; Case map */
+  { 0x00011C, 0, { 0x00011D }},                      /* 011C; 011D; Case map */
+  { 0x00011E, 0, { 0x00011F }},                      /* 011E; 011F; Case map */
+  { 0x000120, 0, { 0x000121 }},                      /* 0120; 0121; Case map */
+  { 0x000122, 0, { 0x000123 }},                      /* 0122; 0123; Case map */
+  { 0x000124, 0, { 0x000125 }},                      /* 0124; 0125; Case map */
+  { 0x000126, 0, { 0x000127 }},                      /* 0126; 0127; Case map */
+  { 0x000128, 0, { 0x000129 }},                      /* 0128; 0129; Case map */
+  { 0x00012A, 0, { 0x00012B }},                      /* 012A; 012B; Case map */
+  { 0x00012C, 0, { 0x00012D }},                      /* 012C; 012D; Case map */
+  { 0x00012E, 0, { 0x00012F }},                      /* 012E; 012F; Case map */
+  { 0x000130, 0, { 0x000069,                    /* 0130; 0069 0307; Case map */
+                   0x000307 }},
+  { 0x000132, 0, { 0x000133 }},                      /* 0132; 0133; Case map */
+  { 0x000134, 0, { 0x000135 }},                      /* 0134; 0135; Case map */
+  { 0x000136, 0, { 0x000137 }},                      /* 0136; 0137; Case map */
+  { 0x000139, 0, { 0x00013A }},                      /* 0139; 013A; Case map */
+  { 0x00013B, 0, { 0x00013C }},                      /* 013B; 013C; Case map */
+  { 0x00013D, 0, { 0x00013E }},                      /* 013D; 013E; Case map */
+  { 0x00013F, 0, { 0x000140 }},                      /* 013F; 0140; Case map */
+  { 0x000141, 0, { 0x000142 }},                      /* 0141; 0142; Case map */
+  { 0x000143, 0, { 0x000144 }},                      /* 0143; 0144; Case map */
+  { 0x000145, 0, { 0x000146 }},                      /* 0145; 0146; Case map */
+  { 0x000147, 0, { 0x000148 }},                      /* 0147; 0148; Case map */
+  { 0x000149, 0, { 0x0002BC,                    /* 0149; 02BC 006E; Case map */
+                   0x00006E }},
+  { 0x00014A, 0, { 0x00014B }},                      /* 014A; 014B; Case map */
+  { 0x00014C, 0, { 0x00014D }},                      /* 014C; 014D; Case map */
+  { 0x00014E, 0, { 0x00014F }},                      /* 014E; 014F; Case map */
+  { 0x000150, 0, { 0x000151 }},                      /* 0150; 0151; Case map */
+  { 0x000152, 0, { 0x000153 }},                      /* 0152; 0153; Case map */
+  { 0x000154, 0, { 0x000155 }},                      /* 0154; 0155; Case map */
+  { 0x000156, 0, { 0x000157 }},                      /* 0156; 0157; Case map */
+  { 0x000158, 0, { 0x000159 }},                      /* 0158; 0159; Case map */
+  { 0x00015A, 0, { 0x00015B }},                      /* 015A; 015B; Case map */
+  { 0x00015C, 0, { 0x00015D }},                      /* 015C; 015D; Case map */
+  { 0x00015E, 0, { 0x00015F }},                      /* 015E; 015F; Case map */
+  { 0x000160, 0, { 0x000161 }},                      /* 0160; 0161; Case map */
+  { 0x000162, 0, { 0x000163 }},                      /* 0162; 0163; Case map */
+  { 0x000164, 0, { 0x000165 }},                      /* 0164; 0165; Case map */
+  { 0x000166, 0, { 0x000167 }},                      /* 0166; 0167; Case map */
+  { 0x000168, 0, { 0x000169 }},                      /* 0168; 0169; Case map */
+  { 0x00016A, 0, { 0x00016B }},                      /* 016A; 016B; Case map */
+  { 0x00016C, 0, { 0x00016D }},                      /* 016C; 016D; Case map */
+  { 0x00016E, 0, { 0x00016F }},                      /* 016E; 016F; Case map */
+  { 0x000170, 0, { 0x000171 }},                      /* 0170; 0171; Case map */
+  { 0x000172, 0, { 0x000173 }},                      /* 0172; 0173; Case map */
+  { 0x000174, 0, { 0x000175 }},                      /* 0174; 0175; Case map */
+  { 0x000176, 0, { 0x000177 }},                      /* 0176; 0177; Case map */
+  { 0x000178, 0, { 0x0000FF }},                      /* 0178; 00FF; Case map */
+  { 0x000179, 0, { 0x00017A }},                      /* 0179; 017A; Case map */
+  { 0x00017B, 0, { 0x00017C }},                      /* 017B; 017C; Case map */
+  { 0x00017D, 0, { 0x00017E }},                      /* 017D; 017E; Case map */
+  { 0x00017F, 0, { 0x000073 }},                      /* 017F; 0073; Case map */
+  { 0x000181, 0, { 0x000253 }},                      /* 0181; 0253; Case map */
+  { 0x000182, 0, { 0x000183 }},                      /* 0182; 0183; Case map */
+  { 0x000184, 0, { 0x000185 }},                      /* 0184; 0185; Case map */
+  { 0x000186, 0, { 0x000254 }},                      /* 0186; 0254; Case map */
+  { 0x000187, 0, { 0x000188 }},                      /* 0187; 0188; Case map */
+  { 0x000189, 0, { 0x000256 }},                      /* 0189; 0256; Case map */
+  { 0x00018A, 0, { 0x000257 }},                      /* 018A; 0257; Case map */
+  { 0x00018B, 0, { 0x00018C }},                      /* 018B; 018C; Case map */
+  { 0x00018E, 0, { 0x0001DD }},                      /* 018E; 01DD; Case map */
+  { 0x00018F, 0, { 0x000259 }},                      /* 018F; 0259; Case map */
+  { 0x000190, 0, { 0x00025B }},                      /* 0190; 025B; Case map */
+  { 0x000191, 0, { 0x000192 }},                      /* 0191; 0192; Case map */
+  { 0x000193, 0, { 0x000260 }},                      /* 0193; 0260; Case map */
+  { 0x000194, 0, { 0x000263 }},                      /* 0194; 0263; Case map */
+  { 0x000196, 0, { 0x000269 }},                      /* 0196; 0269; Case map */
+  { 0x000197, 0, { 0x000268 }},                      /* 0197; 0268; Case map */
+  { 0x000198, 0, { 0x000199 }},                      /* 0198; 0199; Case map */
+  { 0x00019C, 0, { 0x00026F }},                      /* 019C; 026F; Case map */
+  { 0x00019D, 0, { 0x000272 }},                      /* 019D; 0272; Case map */
+  { 0x00019F, 0, { 0x000275 }},                      /* 019F; 0275; Case map */
+  { 0x0001A0, 0, { 0x0001A1 }},                      /* 01A0; 01A1; Case map */
+  { 0x0001A2, 0, { 0x0001A3 }},                      /* 01A2; 01A3; Case map */
+  { 0x0001A4, 0, { 0x0001A5 }},                      /* 01A4; 01A5; Case map */
+  { 0x0001A6, 0, { 0x000280 }},                      /* 01A6; 0280; Case map */
+  { 0x0001A7, 0, { 0x0001A8 }},                      /* 01A7; 01A8; Case map */
+  { 0x0001A9, 0, { 0x000283 }},                      /* 01A9; 0283; Case map */
+  { 0x0001AC, 0, { 0x0001AD }},                      /* 01AC; 01AD; Case map */
+  { 0x0001AE, 0, { 0x000288 }},                      /* 01AE; 0288; Case map */
+  { 0x0001AF, 0, { 0x0001B0 }},                      /* 01AF; 01B0; Case map */
+  { 0x0001B1, 0, { 0x00028A }},                      /* 01B1; 028A; Case map */
+  { 0x0001B2, 0, { 0x00028B }},                      /* 01B2; 028B; Case map */
+  { 0x0001B3, 0, { 0x0001B4 }},                      /* 01B3; 01B4; Case map */
+  { 0x0001B5, 0, { 0x0001B6 }},                      /* 01B5; 01B6; Case map */
+  { 0x0001B7, 0, { 0x000292 }},                      /* 01B7; 0292; Case map */
+  { 0x0001B8, 0, { 0x0001B9 }},                      /* 01B8; 01B9; Case map */
+  { 0x0001BC, 0, { 0x0001BD }},                      /* 01BC; 01BD; Case map */
+  { 0x0001C4, 0, { 0x0001C6 }},                      /* 01C4; 01C6; Case map */
+  { 0x0001C5, 0, { 0x0001C6 }},                      /* 01C5; 01C6; Case map */
+  { 0x0001C7, 0, { 0x0001C9 }},                      /* 01C7; 01C9; Case map */
+  { 0x0001C8, 0, { 0x0001C9 }},                      /* 01C8; 01C9; Case map */
+  { 0x0001CA, 0, { 0x0001CC }},                      /* 01CA; 01CC; Case map */
+  { 0x0001CB, 0, { 0x0001CC }},                      /* 01CB; 01CC; Case map */
+  { 0x0001CD, 0, { 0x0001CE }},                      /* 01CD; 01CE; Case map */
+  { 0x0001CF, 0, { 0x0001D0 }},                      /* 01CF; 01D0; Case map */
+  { 0x0001D1, 0, { 0x0001D2 }},                      /* 01D1; 01D2; Case map */
+  { 0x0001D3, 0, { 0x0001D4 }},                      /* 01D3; 01D4; Case map */
+  { 0x0001D5, 0, { 0x0001D6 }},                      /* 01D5; 01D6; Case map */
+  { 0x0001D7, 0, { 0x0001D8 }},                      /* 01D7; 01D8; Case map */
+  { 0x0001D9, 0, { 0x0001DA }},                      /* 01D9; 01DA; Case map */
+  { 0x0001DB, 0, { 0x0001DC }},                      /* 01DB; 01DC; Case map */
+  { 0x0001DE, 0, { 0x0001DF }},                      /* 01DE; 01DF; Case map */
+  { 0x0001E0, 0, { 0x0001E1 }},                      /* 01E0; 01E1; Case map */
+  { 0x0001E2, 0, { 0x0001E3 }},                      /* 01E2; 01E3; Case map */
+  { 0x0001E4, 0, { 0x0001E5 }},                      /* 01E4; 01E5; Case map */
+  { 0x0001E6, 0, { 0x0001E7 }},                      /* 01E6; 01E7; Case map */
+  { 0x0001E8, 0, { 0x0001E9 }},                      /* 01E8; 01E9; Case map */
+  { 0x0001EA, 0, { 0x0001EB }},                      /* 01EA; 01EB; Case map */
+  { 0x0001EC, 0, { 0x0001ED }},                      /* 01EC; 01ED; Case map */
+  { 0x0001EE, 0, { 0x0001EF }},                      /* 01EE; 01EF; Case map */
+  { 0x0001F0, 0, { 0x00006A,                    /* 01F0; 006A 030C; Case map */
+                   0x00030C }},
+  { 0x0001F1, 0, { 0x0001F3 }},                      /* 01F1; 01F3; Case map */
+  { 0x0001F2, 0, { 0x0001F3 }},                      /* 01F2; 01F3; Case map */
+  { 0x0001F4, 0, { 0x0001F5 }},                      /* 01F4; 01F5; Case map */
+  { 0x0001F6, 0, { 0x000195 }},                      /* 01F6; 0195; Case map */
+  { 0x0001F7, 0, { 0x0001BF }},                      /* 01F7; 01BF; Case map */
+  { 0x0001F8, 0, { 0x0001F9 }},                      /* 01F8; 01F9; Case map */
+  { 0x0001FA, 0, { 0x0001FB }},                      /* 01FA; 01FB; Case map */
+  { 0x0001FC, 0, { 0x0001FD }},                      /* 01FC; 01FD; Case map */
+  { 0x0001FE, 0, { 0x0001FF }},                      /* 01FE; 01FF; Case map */
+  { 0x000200, 0, { 0x000201 }},                      /* 0200; 0201; Case map */
+  { 0x000202, 0, { 0x000203 }},                      /* 0202; 0203; Case map */
+  { 0x000204, 0, { 0x000205 }},                      /* 0204; 0205; Case map */
+  { 0x000206, 0, { 0x000207 }},                      /* 0206; 0207; Case map */
+  { 0x000208, 0, { 0x000209 }},                      /* 0208; 0209; Case map */
+  { 0x00020A, 0, { 0x00020B }},                      /* 020A; 020B; Case map */
+  { 0x00020C, 0, { 0x00020D }},                      /* 020C; 020D; Case map */
+  { 0x00020E, 0, { 0x00020F }},                      /* 020E; 020F; Case map */
+  { 0x000210, 0, { 0x000211 }},                      /* 0210; 0211; Case map */
+  { 0x000212, 0, { 0x000213 }},                      /* 0212; 0213; Case map */
+  { 0x000214, 0, { 0x000215 }},                      /* 0214; 0215; Case map */
+  { 0x000216, 0, { 0x000217 }},                      /* 0216; 0217; Case map */
+  { 0x000218, 0, { 0x000219 }},                      /* 0218; 0219; Case map */
+  { 0x00021A, 0, { 0x00021B }},                      /* 021A; 021B; Case map */
+  { 0x00021C, 0, { 0x00021D }},                      /* 021C; 021D; Case map */
+  { 0x00021E, 0, { 0x00021F }},                      /* 021E; 021F; Case map */
+  { 0x000220, 0, { 0x00019E }},                      /* 0220; 019E; Case map */
+  { 0x000222, 0, { 0x000223 }},                      /* 0222; 0223; Case map */
+  { 0x000224, 0, { 0x000225 }},                      /* 0224; 0225; Case map */
+  { 0x000226, 0, { 0x000227 }},                      /* 0226; 0227; Case map */
+  { 0x000228, 0, { 0x000229 }},                      /* 0228; 0229; Case map */
+  { 0x00022A, 0, { 0x00022B }},                      /* 022A; 022B; Case map */
+  { 0x00022C, 0, { 0x00022D }},                      /* 022C; 022D; Case map */
+  { 0x00022E, 0, { 0x00022F }},                      /* 022E; 022F; Case map */
+  { 0x000230, 0, { 0x000231 }},                      /* 0230; 0231; Case map */
+  { 0x000232, 0, { 0x000233 }},                      /* 0232; 0233; Case map */
+  { 0x000345, 0, { 0x0003B9 }},                      /* 0345; 03B9; Case map */
+  { 0x000386, 0, { 0x0003AC }},                      /* 0386; 03AC; Case map */
+  { 0x000388, 0, { 0x0003AD }},                      /* 0388; 03AD; Case map */
+  { 0x000389, 0, { 0x0003AE }},                      /* 0389; 03AE; Case map */
+  { 0x00038A, 0, { 0x0003AF }},                      /* 038A; 03AF; Case map */
+  { 0x00038C, 0, { 0x0003CC }},                      /* 038C; 03CC; Case map */
+  { 0x00038E, 0, { 0x0003CD }},                      /* 038E; 03CD; Case map */
+  { 0x00038F, 0, { 0x0003CE }},                      /* 038F; 03CE; Case map */
+  { 0x000390, 0, { 0x0003B9,               /* 0390; 03B9 0308 0301; Case map */
+                   0x000308, 0x000301 }},
+  { 0x000391, 0, { 0x0003B1 }},                      /* 0391; 03B1; Case map */
+  { 0x000392, 0, { 0x0003B2 }},                      /* 0392; 03B2; Case map */
+  { 0x000393, 0, { 0x0003B3 }},                      /* 0393; 03B3; Case map */
+  { 0x000394, 0, { 0x0003B4 }},                      /* 0394; 03B4; Case map */
+  { 0x000395, 0, { 0x0003B5 }},                      /* 0395; 03B5; Case map */
+  { 0x000396, 0, { 0x0003B6 }},                      /* 0396; 03B6; Case map */
+  { 0x000397, 0, { 0x0003B7 }},                      /* 0397; 03B7; Case map */
+  { 0x000398, 0, { 0x0003B8 }},                      /* 0398; 03B8; Case map */
+  { 0x000399, 0, { 0x0003B9 }},                      /* 0399; 03B9; Case map */
+  { 0x00039A, 0, { 0x0003BA }},                      /* 039A; 03BA; Case map */
+  { 0x00039B, 0, { 0x0003BB }},                      /* 039B; 03BB; Case map */
+  { 0x00039C, 0, { 0x0003BC }},                      /* 039C; 03BC; Case map */
+  { 0x00039D, 0, { 0x0003BD }},                      /* 039D; 03BD; Case map */
+  { 0x00039E, 0, { 0x0003BE }},                      /* 039E; 03BE; Case map */
+  { 0x00039F, 0, { 0x0003BF }},                      /* 039F; 03BF; Case map */
+  { 0x0003A0, 0, { 0x0003C0 }},                      /* 03A0; 03C0; Case map */
+  { 0x0003A1, 0, { 0x0003C1 }},                      /* 03A1; 03C1; Case map */
+  { 0x0003A3, 0, { 0x0003C3 }},                      /* 03A3; 03C3; Case map */
+  { 0x0003A4, 0, { 0x0003C4 }},                      /* 03A4; 03C4; Case map */
+  { 0x0003A5, 0, { 0x0003C5 }},                      /* 03A5; 03C5; Case map */
+  { 0x0003A6, 0, { 0x0003C6 }},                      /* 03A6; 03C6; Case map */
+  { 0x0003A7, 0, { 0x0003C7 }},                      /* 03A7; 03C7; Case map */
+  { 0x0003A8, 0, { 0x0003C8 }},                      /* 03A8; 03C8; Case map */
+  { 0x0003A9, 0, { 0x0003C9 }},                      /* 03A9; 03C9; Case map */
+  { 0x0003AA, 0, { 0x0003CA }},                      /* 03AA; 03CA; Case map */
+  { 0x0003AB, 0, { 0x0003CB }},                      /* 03AB; 03CB; Case map */
+  { 0x0003B0, 0, { 0x0003C5,               /* 03B0; 03C5 0308 0301; Case map */
+                   0x000308, 0x000301 }},
+  { 0x0003C2, 0, { 0x0003C3 }},                      /* 03C2; 03C3; Case map */
+  { 0x0003D0, 0, { 0x0003B2 }},                      /* 03D0; 03B2; Case map */
+  { 0x0003D1, 0, { 0x0003B8 }},                      /* 03D1; 03B8; Case map */
+  { 0x0003D5, 0, { 0x0003C6 }},                      /* 03D5; 03C6; Case map */
+  { 0x0003D6, 0, { 0x0003C0 }},                      /* 03D6; 03C0; Case map */
+  { 0x0003D8, 0, { 0x0003D9 }},                      /* 03D8; 03D9; Case map */
+  { 0x0003DA, 0, { 0x0003DB }},                      /* 03DA; 03DB; Case map */
+  { 0x0003DC, 0, { 0x0003DD }},                      /* 03DC; 03DD; Case map */
+  { 0x0003DE, 0, { 0x0003DF }},                      /* 03DE; 03DF; Case map */
+  { 0x0003E0, 0, { 0x0003E1 }},                      /* 03E0; 03E1; Case map */
+  { 0x0003E2, 0, { 0x0003E3 }},                      /* 03E2; 03E3; Case map */
+  { 0x0003E4, 0, { 0x0003E5 }},                      /* 03E4; 03E5; Case map */
+  { 0x0003E6, 0, { 0x0003E7 }},                      /* 03E6; 03E7; Case map */
+  { 0x0003E8, 0, { 0x0003E9 }},                      /* 03E8; 03E9; Case map */
+  { 0x0003EA, 0, { 0x0003EB }},                      /* 03EA; 03EB; Case map */
+  { 0x0003EC, 0, { 0x0003ED }},                      /* 03EC; 03ED; Case map */
+  { 0x0003EE, 0, { 0x0003EF }},                      /* 03EE; 03EF; Case map */
+  { 0x0003F0, 0, { 0x0003BA }},                      /* 03F0; 03BA; Case map */
+  { 0x0003F1, 0, { 0x0003C1 }},                      /* 03F1; 03C1; Case map */
+  { 0x0003F2, 0, { 0x0003C3 }},                      /* 03F2; 03C3; Case map */
+  { 0x0003F4, 0, { 0x0003B8 }},                      /* 03F4; 03B8; Case map */
+  { 0x0003F5, 0, { 0x0003B5 }},                      /* 03F5; 03B5; Case map */
+  { 0x000400, 0, { 0x000450 }},                      /* 0400; 0450; Case map */
+  { 0x000401, 0, { 0x000451 }},                      /* 0401; 0451; Case map */
+  { 0x000402, 0, { 0x000452 }},                      /* 0402; 0452; Case map */
+  { 0x000403, 0, { 0x000453 }},                      /* 0403; 0453; Case map */
+  { 0x000404, 0, { 0x000454 }},                      /* 0404; 0454; Case map */
+  { 0x000405, 0, { 0x000455 }},                      /* 0405; 0455; Case map */
+  { 0x000406, 0, { 0x000456 }},                      /* 0406; 0456; Case map */
+  { 0x000407, 0, { 0x000457 }},                      /* 0407; 0457; Case map */
+  { 0x000408, 0, { 0x000458 }},                      /* 0408; 0458; Case map */
+  { 0x000409, 0, { 0x000459 }},                      /* 0409; 0459; Case map */
+  { 0x00040A, 0, { 0x00045A }},                      /* 040A; 045A; Case map */
+  { 0x00040B, 0, { 0x00045B }},                      /* 040B; 045B; Case map */
+  { 0x00040C, 0, { 0x00045C }},                      /* 040C; 045C; Case map */
+  { 0x00040D, 0, { 0x00045D }},                      /* 040D; 045D; Case map */
+  { 0x00040E, 0, { 0x00045E }},                      /* 040E; 045E; Case map */
+  { 0x00040F, 0, { 0x00045F }},                      /* 040F; 045F; Case map */
+  { 0x000410, 0, { 0x000430 }},                      /* 0410; 0430; Case map */
+  { 0x000411, 0, { 0x000431 }},                      /* 0411; 0431; Case map */
+  { 0x000412, 0, { 0x000432 }},                      /* 0412; 0432; Case map */
+  { 0x000413, 0, { 0x000433 }},                      /* 0413; 0433; Case map */
+  { 0x000414, 0, { 0x000434 }},                      /* 0414; 0434; Case map */
+  { 0x000415, 0, { 0x000435 }},                      /* 0415; 0435; Case map */
+  { 0x000416, 0, { 0x000436 }},                      /* 0416; 0436; Case map */
+  { 0x000417, 0, { 0x000437 }},                      /* 0417; 0437; Case map */
+  { 0x000418, 0, { 0x000438 }},                      /* 0418; 0438; Case map */
+  { 0x000419, 0, { 0x000439 }},                      /* 0419; 0439; Case map */
+  { 0x00041A, 0, { 0x00043A }},                      /* 041A; 043A; Case map */
+  { 0x00041B, 0, { 0x00043B }},                      /* 041B; 043B; Case map */
+  { 0x00041C, 0, { 0x00043C }},                      /* 041C; 043C; Case map */
+  { 0x00041D, 0, { 0x00043D }},                      /* 041D; 043D; Case map */
+  { 0x00041E, 0, { 0x00043E }},                      /* 041E; 043E; Case map */
+  { 0x00041F, 0, { 0x00043F }},                      /* 041F; 043F; Case map */
+  { 0x000420, 0, { 0x000440 }},                      /* 0420; 0440; Case map */
+  { 0x000421, 0, { 0x000441 }},                      /* 0421; 0441; Case map */
+  { 0x000422, 0, { 0x000442 }},                      /* 0422; 0442; Case map */
+  { 0x000423, 0, { 0x000443 }},                      /* 0423; 0443; Case map */
+  { 0x000424, 0, { 0x000444 }},                      /* 0424; 0444; Case map */
+  { 0x000425, 0, { 0x000445 }},                      /* 0425; 0445; Case map */
+  { 0x000426, 0, { 0x000446 }},                      /* 0426; 0446; Case map */
+  { 0x000427, 0, { 0x000447 }},                      /* 0427; 0447; Case map */
+  { 0x000428, 0, { 0x000448 }},                      /* 0428; 0448; Case map */
+  { 0x000429, 0, { 0x000449 }},                      /* 0429; 0449; Case map */
+  { 0x00042A, 0, { 0x00044A }},                      /* 042A; 044A; Case map */
+  { 0x00042B, 0, { 0x00044B }},                      /* 042B; 044B; Case map */
+  { 0x00042C, 0, { 0x00044C }},                      /* 042C; 044C; Case map */
+  { 0x00042D, 0, { 0x00044D }},                      /* 042D; 044D; Case map */
+  { 0x00042E, 0, { 0x00044E }},                      /* 042E; 044E; Case map */
+  { 0x00042F, 0, { 0x00044F }},                      /* 042F; 044F; Case map */
+  { 0x000460, 0, { 0x000461 }},                      /* 0460; 0461; Case map */
+  { 0x000462, 0, { 0x000463 }},                      /* 0462; 0463; Case map */
+  { 0x000464, 0, { 0x000465 }},                      /* 0464; 0465; Case map */
+  { 0x000466, 0, { 0x000467 }},                      /* 0466; 0467; Case map */
+  { 0x000468, 0, { 0x000469 }},                      /* 0468; 0469; Case map */
+  { 0x00046A, 0, { 0x00046B }},                      /* 046A; 046B; Case map */
+  { 0x00046C, 0, { 0x00046D }},                      /* 046C; 046D; Case map */
+  { 0x00046E, 0, { 0x00046F }},                      /* 046E; 046F; Case map */
+  { 0x000470, 0, { 0x000471 }},                      /* 0470; 0471; Case map */
+  { 0x000472, 0, { 0x000473 }},                      /* 0472; 0473; Case map */
+  { 0x000474, 0, { 0x000475 }},                      /* 0474; 0475; Case map */
+  { 0x000476, 0, { 0x000477 }},                      /* 0476; 0477; Case map */
+  { 0x000478, 0, { 0x000479 }},                      /* 0478; 0479; Case map */
+  { 0x00047A, 0, { 0x00047B }},                      /* 047A; 047B; Case map */
+  { 0x00047C, 0, { 0x00047D }},                      /* 047C; 047D; Case map */
+  { 0x00047E, 0, { 0x00047F }},                      /* 047E; 047F; Case map */
+  { 0x000480, 0, { 0x000481 }},                      /* 0480; 0481; Case map */
+  { 0x00048A, 0, { 0x00048B }},                      /* 048A; 048B; Case map */
+  { 0x00048C, 0, { 0x00048D }},                      /* 048C; 048D; Case map */
+  { 0x00048E, 0, { 0x00048F }},                      /* 048E; 048F; Case map */
+  { 0x000490, 0, { 0x000491 }},                      /* 0490; 0491; Case map */
+  { 0x000492, 0, { 0x000493 }},                      /* 0492; 0493; Case map */
+  { 0x000494, 0, { 0x000495 }},                      /* 0494; 0495; Case map */
+  { 0x000496, 0, { 0x000497 }},                      /* 0496; 0497; Case map */
+  { 0x000498, 0, { 0x000499 }},                      /* 0498; 0499; Case map */
+  { 0x00049A, 0, { 0x00049B }},                      /* 049A; 049B; Case map */
+  { 0x00049C, 0, { 0x00049D }},                      /* 049C; 049D; Case map */
+  { 0x00049E, 0, { 0x00049F }},                      /* 049E; 049F; Case map */
+  { 0x0004A0, 0, { 0x0004A1 }},                      /* 04A0; 04A1; Case map */
+  { 0x0004A2, 0, { 0x0004A3 }},                      /* 04A2; 04A3; Case map */
+  { 0x0004A4, 0, { 0x0004A5 }},                      /* 04A4; 04A5; Case map */
+  { 0x0004A6, 0, { 0x0004A7 }},                      /* 04A6; 04A7; Case map */
+  { 0x0004A8, 0, { 0x0004A9 }},                      /* 04A8; 04A9; Case map */
+  { 0x0004AA, 0, { 0x0004AB }},                      /* 04AA; 04AB; Case map */
+  { 0x0004AC, 0, { 0x0004AD }},                      /* 04AC; 04AD; Case map */
+  { 0x0004AE, 0, { 0x0004AF }},                      /* 04AE; 04AF; Case map */
+  { 0x0004B0, 0, { 0x0004B1 }},                      /* 04B0; 04B1; Case map */
+  { 0x0004B2, 0, { 0x0004B3 }},                      /* 04B2; 04B3; Case map */
+  { 0x0004B4, 0, { 0x0004B5 }},                      /* 04B4; 04B5; Case map */
+  { 0x0004B6, 0, { 0x0004B7 }},                      /* 04B6; 04B7; Case map */
+  { 0x0004B8, 0, { 0x0004B9 }},                      /* 04B8; 04B9; Case map */
+  { 0x0004BA, 0, { 0x0004BB }},                      /* 04BA; 04BB; Case map */
+  { 0x0004BC, 0, { 0x0004BD }},                      /* 04BC; 04BD; Case map */
+  { 0x0004BE, 0, { 0x0004BF }},                      /* 04BE; 04BF; Case map */
+  { 0x0004C1, 0, { 0x0004C2 }},                      /* 04C1; 04C2; Case map */
+  { 0x0004C3, 0, { 0x0004C4 }},                      /* 04C3; 04C4; Case map */
+  { 0x0004C5, 0, { 0x0004C6 }},                      /* 04C5; 04C6; Case map */
+  { 0x0004C7, 0, { 0x0004C8 }},                      /* 04C7; 04C8; Case map */
+  { 0x0004C9, 0, { 0x0004CA }},                      /* 04C9; 04CA; Case map */
+  { 0x0004CB, 0, { 0x0004CC }},                      /* 04CB; 04CC; Case map */
+  { 0x0004CD, 0, { 0x0004CE }},                      /* 04CD; 04CE; Case map */
+  { 0x0004D0, 0, { 0x0004D1 }},                      /* 04D0; 04D1; Case map */
+  { 0x0004D2, 0, { 0x0004D3 }},                      /* 04D2; 04D3; Case map */
+  { 0x0004D4, 0, { 0x0004D5 }},                      /* 04D4; 04D5; Case map */
+  { 0x0004D6, 0, { 0x0004D7 }},                      /* 04D6; 04D7; Case map */
+  { 0x0004D8, 0, { 0x0004D9 }},                      /* 04D8; 04D9; Case map */
+  { 0x0004DA, 0, { 0x0004DB }},                      /* 04DA; 04DB; Case map */
+  { 0x0004DC, 0, { 0x0004DD }},                      /* 04DC; 04DD; Case map */
+  { 0x0004DE, 0, { 0x0004DF }},                      /* 04DE; 04DF; Case map */
+  { 0x0004E0, 0, { 0x0004E1 }},                      /* 04E0; 04E1; Case map */
+  { 0x0004E2, 0, { 0x0004E3 }},                      /* 04E2; 04E3; Case map */
+  { 0x0004E4, 0, { 0x0004E5 }},                      /* 04E4; 04E5; Case map */
+  { 0x0004E6, 0, { 0x0004E7 }},                      /* 04E6; 04E7; Case map */
+  { 0x0004E8, 0, { 0x0004E9 }},                      /* 04E8; 04E9; Case map */
+  { 0x0004EA, 0, { 0x0004EB }},                      /* 04EA; 04EB; Case map */
+  { 0x0004EC, 0, { 0x0004ED }},                      /* 04EC; 04ED; Case map */
+  { 0x0004EE, 0, { 0x0004EF }},                      /* 04EE; 04EF; Case map */
+  { 0x0004F0, 0, { 0x0004F1 }},                      /* 04F0; 04F1; Case map */
+  { 0x0004F2, 0, { 0x0004F3 }},                      /* 04F2; 04F3; Case map */
+  { 0x0004F4, 0, { 0x0004F5 }},                      /* 04F4; 04F5; Case map */
+  { 0x0004F8, 0, { 0x0004F9 }},                      /* 04F8; 04F9; Case map */
+  { 0x000500, 0, { 0x000501 }},                      /* 0500; 0501; Case map */
+  { 0x000502, 0, { 0x000503 }},                      /* 0502; 0503; Case map */
+  { 0x000504, 0, { 0x000505 }},                      /* 0504; 0505; Case map */
+  { 0x000506, 0, { 0x000507 }},                      /* 0506; 0507; Case map */
+  { 0x000508, 0, { 0x000509 }},                      /* 0508; 0509; Case map */
+  { 0x00050A, 0, { 0x00050B }},                      /* 050A; 050B; Case map */
+  { 0x00050C, 0, { 0x00050D }},                      /* 050C; 050D; Case map */
+  { 0x00050E, 0, { 0x00050F }},                      /* 050E; 050F; Case map */
+  { 0x000531, 0, { 0x000561 }},                      /* 0531; 0561; Case map */
+  { 0x000532, 0, { 0x000562 }},                      /* 0532; 0562; Case map */
+  { 0x000533, 0, { 0x000563 }},                      /* 0533; 0563; Case map */
+  { 0x000534, 0, { 0x000564 }},                      /* 0534; 0564; Case map */
+  { 0x000535, 0, { 0x000565 }},                      /* 0535; 0565; Case map */
+  { 0x000536, 0, { 0x000566 }},                      /* 0536; 0566; Case map */
+  { 0x000537, 0, { 0x000567 }},                      /* 0537; 0567; Case map */
+  { 0x000538, 0, { 0x000568 }},                      /* 0538; 0568; Case map */
+  { 0x000539, 0, { 0x000569 }},                      /* 0539; 0569; Case map */
+  { 0x00053A, 0, { 0x00056A }},                      /* 053A; 056A; Case map */
+  { 0x00053B, 0, { 0x00056B }},                      /* 053B; 056B; Case map */
+  { 0x00053C, 0, { 0x00056C }},                      /* 053C; 056C; Case map */
+  { 0x00053D, 0, { 0x00056D }},                      /* 053D; 056D; Case map */
+  { 0x00053E, 0, { 0x00056E }},                      /* 053E; 056E; Case map */
+  { 0x00053F, 0, { 0x00056F }},                      /* 053F; 056F; Case map */
+  { 0x000540, 0, { 0x000570 }},                      /* 0540; 0570; Case map */
+  { 0x000541, 0, { 0x000571 }},                      /* 0541; 0571; Case map */
+  { 0x000542, 0, { 0x000572 }},                      /* 0542; 0572; Case map */
+  { 0x000543, 0, { 0x000573 }},                      /* 0543; 0573; Case map */
+  { 0x000544, 0, { 0x000574 }},                      /* 0544; 0574; Case map */
+  { 0x000545, 0, { 0x000575 }},                      /* 0545; 0575; Case map */
+  { 0x000546, 0, { 0x000576 }},                      /* 0546; 0576; Case map */
+  { 0x000547, 0, { 0x000577 }},                      /* 0547; 0577; Case map */
+  { 0x000548, 0, { 0x000578 }},                      /* 0548; 0578; Case map */
+  { 0x000549, 0, { 0x000579 }},                      /* 0549; 0579; Case map */
+  { 0x00054A, 0, { 0x00057A }},                      /* 054A; 057A; Case map */
+  { 0x00054B, 0, { 0x00057B }},                      /* 054B; 057B; Case map */
+  { 0x00054C, 0, { 0x00057C }},                      /* 054C; 057C; Case map */
+  { 0x00054D, 0, { 0x00057D }},                      /* 054D; 057D; Case map */
+  { 0x00054E, 0, { 0x00057E }},                      /* 054E; 057E; Case map */
+  { 0x00054F, 0, { 0x00057F }},                      /* 054F; 057F; Case map */
+  { 0x000550, 0, { 0x000580 }},                      /* 0550; 0580; Case map */
+  { 0x000551, 0, { 0x000581 }},                      /* 0551; 0581; Case map */
+  { 0x000552, 0, { 0x000582 }},                      /* 0552; 0582; Case map */
+  { 0x000553, 0, { 0x000583 }},                      /* 0553; 0583; Case map */
+  { 0x000554, 0, { 0x000584 }},                      /* 0554; 0584; Case map */
+  { 0x000555, 0, { 0x000585 }},                      /* 0555; 0585; Case map */
+  { 0x000556, 0, { 0x000586 }},                      /* 0556; 0586; Case map */
+  { 0x000587, 0, { 0x000565,                    /* 0587; 0565 0582; Case map */
+                   0x000582 }},
+  { 0x001E00, 0, { 0x001E01 }},                      /* 1E00; 1E01; Case map */
+  { 0x001E02, 0, { 0x001E03 }},                      /* 1E02; 1E03; Case map */
+  { 0x001E04, 0, { 0x001E05 }},                      /* 1E04; 1E05; Case map */
+  { 0x001E06, 0, { 0x001E07 }},                      /* 1E06; 1E07; Case map */
+  { 0x001E08, 0, { 0x001E09 }},                      /* 1E08; 1E09; Case map */
+  { 0x001E0A, 0, { 0x001E0B }},                      /* 1E0A; 1E0B; Case map */
+  { 0x001E0C, 0, { 0x001E0D }},                      /* 1E0C; 1E0D; Case map */
+  { 0x001E0E, 0, { 0x001E0F }},                      /* 1E0E; 1E0F; Case map */
+  { 0x001E10, 0, { 0x001E11 }},                      /* 1E10; 1E11; Case map */
+  { 0x001E12, 0, { 0x001E13 }},                      /* 1E12; 1E13; Case map */
+  { 0x001E14, 0, { 0x001E15 }},                      /* 1E14; 1E15; Case map */
+  { 0x001E16, 0, { 0x001E17 }},                      /* 1E16; 1E17; Case map */
+  { 0x001E18, 0, { 0x001E19 }},                      /* 1E18; 1E19; Case map */
+  { 0x001E1A, 0, { 0x001E1B }},                      /* 1E1A; 1E1B; Case map */
+  { 0x001E1C, 0, { 0x001E1D }},                      /* 1E1C; 1E1D; Case map */
+  { 0x001E1E, 0, { 0x001E1F }},                      /* 1E1E; 1E1F; Case map */
+  { 0x001E20, 0, { 0x001E21 }},                      /* 1E20; 1E21; Case map */
+  { 0x001E22, 0, { 0x001E23 }},                      /* 1E22; 1E23; Case map */
+  { 0x001E24, 0, { 0x001E25 }},                      /* 1E24; 1E25; Case map */
+  { 0x001E26, 0, { 0x001E27 }},                      /* 1E26; 1E27; Case map */
+  { 0x001E28, 0, { 0x001E29 }},                      /* 1E28; 1E29; Case map */
+  { 0x001E2A, 0, { 0x001E2B }},                      /* 1E2A; 1E2B; Case map */
+  { 0x001E2C, 0, { 0x001E2D }},                      /* 1E2C; 1E2D; Case map */
+  { 0x001E2E, 0, { 0x001E2F }},                      /* 1E2E; 1E2F; Case map */
+  { 0x001E30, 0, { 0x001E31 }},                      /* 1E30; 1E31; Case map */
+  { 0x001E32, 0, { 0x001E33 }},                      /* 1E32; 1E33; Case map */
+  { 0x001E34, 0, { 0x001E35 }},                      /* 1E34; 1E35; Case map */
+  { 0x001E36, 0, { 0x001E37 }},                      /* 1E36; 1E37; Case map */
+  { 0x001E38, 0, { 0x001E39 }},                      /* 1E38; 1E39; Case map */
+  { 0x001E3A, 0, { 0x001E3B }},                      /* 1E3A; 1E3B; Case map */
+  { 0x001E3C, 0, { 0x001E3D }},                      /* 1E3C; 1E3D; Case map */
+  { 0x001E3E, 0, { 0x001E3F }},                      /* 1E3E; 1E3F; Case map */
+  { 0x001E40, 0, { 0x001E41 }},                      /* 1E40; 1E41; Case map */
+  { 0x001E42, 0, { 0x001E43 }},                      /* 1E42; 1E43; Case map */
+  { 0x001E44, 0, { 0x001E45 }},                      /* 1E44; 1E45; Case map */
+  { 0x001E46, 0, { 0x001E47 }},                      /* 1E46; 1E47; Case map */
+  { 0x001E48, 0, { 0x001E49 }},                      /* 1E48; 1E49; Case map */
+  { 0x001E4A, 0, { 0x001E4B }},                      /* 1E4A; 1E4B; Case map */
+  { 0x001E4C, 0, { 0x001E4D }},                      /* 1E4C; 1E4D; Case map */
+  { 0x001E4E, 0, { 0x001E4F }},                      /* 1E4E; 1E4F; Case map */
+  { 0x001E50, 0, { 0x001E51 }},                      /* 1E50; 1E51; Case map */
+  { 0x001E52, 0, { 0x001E53 }},                      /* 1E52; 1E53; Case map */
+  { 0x001E54, 0, { 0x001E55 }},                      /* 1E54; 1E55; Case map */
+  { 0x001E56, 0, { 0x001E57 }},                      /* 1E56; 1E57; Case map */
+  { 0x001E58, 0, { 0x001E59 }},                      /* 1E58; 1E59; Case map */
+  { 0x001E5A, 0, { 0x001E5B }},                      /* 1E5A; 1E5B; Case map */
+  { 0x001E5C, 0, { 0x001E5D }},                      /* 1E5C; 1E5D; Case map */
+  { 0x001E5E, 0, { 0x001E5F }},                      /* 1E5E; 1E5F; Case map */
+  { 0x001E60, 0, { 0x001E61 }},                      /* 1E60; 1E61; Case map */
+  { 0x001E62, 0, { 0x001E63 }},                      /* 1E62; 1E63; Case map */
+  { 0x001E64, 0, { 0x001E65 }},                      /* 1E64; 1E65; Case map */
+  { 0x001E66, 0, { 0x001E67 }},                      /* 1E66; 1E67; Case map */
+  { 0x001E68, 0, { 0x001E69 }},                      /* 1E68; 1E69; Case map */
+  { 0x001E6A, 0, { 0x001E6B }},                      /* 1E6A; 1E6B; Case map */
+  { 0x001E6C, 0, { 0x001E6D }},                      /* 1E6C; 1E6D; Case map */
+  { 0x001E6E, 0, { 0x001E6F }},                      /* 1E6E; 1E6F; Case map */
+  { 0x001E70, 0, { 0x001E71 }},                      /* 1E70; 1E71; Case map */
+  { 0x001E72, 0, { 0x001E73 }},                      /* 1E72; 1E73; Case map */
+  { 0x001E74, 0, { 0x001E75 }},                      /* 1E74; 1E75; Case map */
+  { 0x001E76, 0, { 0x001E77 }},                      /* 1E76; 1E77; Case map */
+  { 0x001E78, 0, { 0x001E79 }},                      /* 1E78; 1E79; Case map */
+  { 0x001E7A, 0, { 0x001E7B }},                      /* 1E7A; 1E7B; Case map */
+  { 0x001E7C, 0, { 0x001E7D }},                      /* 1E7C; 1E7D; Case map */
+  { 0x001E7E, 0, { 0x001E7F }},                      /* 1E7E; 1E7F; Case map */
+  { 0x001E80, 0, { 0x001E81 }},                      /* 1E80; 1E81; Case map */
+  { 0x001E82, 0, { 0x001E83 }},                      /* 1E82; 1E83; Case map */
+  { 0x001E84, 0, { 0x001E85 }},                      /* 1E84; 1E85; Case map */
+  { 0x001E86, 0, { 0x001E87 }},                      /* 1E86; 1E87; Case map */
+  { 0x001E88, 0, { 0x001E89 }},                      /* 1E88; 1E89; Case map */
+  { 0x001E8A, 0, { 0x001E8B }},                      /* 1E8A; 1E8B; Case map */
+  { 0x001E8C, 0, { 0x001E8D }},                      /* 1E8C; 1E8D; Case map */
+  { 0x001E8E, 0, { 0x001E8F }},                      /* 1E8E; 1E8F; Case map */
+  { 0x001E90, 0, { 0x001E91 }},                      /* 1E90; 1E91; Case map */
+  { 0x001E92, 0, { 0x001E93 }},                      /* 1E92; 1E93; Case map */
+  { 0x001E94, 0, { 0x001E95 }},                      /* 1E94; 1E95; Case map */
+  { 0x001E96, 0, { 0x000068,                    /* 1E96; 0068 0331; Case map */
+                   0x000331 }},
+  { 0x001E97, 0, { 0x000074,                    /* 1E97; 0074 0308; Case map */
+                   0x000308 }},
+  { 0x001E98, 0, { 0x000077,                    /* 1E98; 0077 030A; Case map */
+                   0x00030A }},
+  { 0x001E99, 0, { 0x000079,                    /* 1E99; 0079 030A; Case map */
+                   0x00030A }},
+  { 0x001E9A, 0, { 0x000061,                    /* 1E9A; 0061 02BE; Case map */
+                   0x0002BE }},
+  { 0x001E9B, 0, { 0x001E61 }},                      /* 1E9B; 1E61; Case map */
+  { 0x001EA0, 0, { 0x001EA1 }},                      /* 1EA0; 1EA1; Case map */
+  { 0x001EA2, 0, { 0x001EA3 }},                      /* 1EA2; 1EA3; Case map */
+  { 0x001EA4, 0, { 0x001EA5 }},                      /* 1EA4; 1EA5; Case map */
+  { 0x001EA6, 0, { 0x001EA7 }},                      /* 1EA6; 1EA7; Case map */
+  { 0x001EA8, 0, { 0x001EA9 }},                      /* 1EA8; 1EA9; Case map */
+  { 0x001EAA, 0, { 0x001EAB }},                      /* 1EAA; 1EAB; Case map */
+  { 0x001EAC, 0, { 0x001EAD }},                      /* 1EAC; 1EAD; Case map */
+  { 0x001EAE, 0, { 0x001EAF }},                      /* 1EAE; 1EAF; Case map */
+  { 0x001EB0, 0, { 0x001EB1 }},                      /* 1EB0; 1EB1; Case map */
+  { 0x001EB2, 0, { 0x001EB3 }},                      /* 1EB2; 1EB3; Case map */
+  { 0x001EB4, 0, { 0x001EB5 }},                      /* 1EB4; 1EB5; Case map */
+  { 0x001EB6, 0, { 0x001EB7 }},                      /* 1EB6; 1EB7; Case map */
+  { 0x001EB8, 0, { 0x001EB9 }},                      /* 1EB8; 1EB9; Case map */
+  { 0x001EBA, 0, { 0x001EBB }},                      /* 1EBA; 1EBB; Case map */
+  { 0x001EBC, 0, { 0x001EBD }},                      /* 1EBC; 1EBD; Case map */
+  { 0x001EBE, 0, { 0x001EBF }},                      /* 1EBE; 1EBF; Case map */
+  { 0x001EC0, 0, { 0x001EC1 }},                      /* 1EC0; 1EC1; Case map */
+  { 0x001EC2, 0, { 0x001EC3 }},                      /* 1EC2; 1EC3; Case map */
+  { 0x001EC4, 0, { 0x001EC5 }},                      /* 1EC4; 1EC5; Case map */
+  { 0x001EC6, 0, { 0x001EC7 }},                      /* 1EC6; 1EC7; Case map */
+  { 0x001EC8, 0, { 0x001EC9 }},                      /* 1EC8; 1EC9; Case map */
+  { 0x001ECA, 0, { 0x001ECB }},                      /* 1ECA; 1ECB; Case map */
+  { 0x001ECC, 0, { 0x001ECD }},                      /* 1ECC; 1ECD; Case map */
+  { 0x001ECE, 0, { 0x001ECF }},                      /* 1ECE; 1ECF; Case map */
+  { 0x001ED0, 0, { 0x001ED1 }},                      /* 1ED0; 1ED1; Case map */
+  { 0x001ED2, 0, { 0x001ED3 }},                      /* 1ED2; 1ED3; Case map */
+  { 0x001ED4, 0, { 0x001ED5 }},                      /* 1ED4; 1ED5; Case map */
+  { 0x001ED6, 0, { 0x001ED7 }},                      /* 1ED6; 1ED7; Case map */
+  { 0x001ED8, 0, { 0x001ED9 }},                      /* 1ED8; 1ED9; Case map */
+  { 0x001EDA, 0, { 0x001EDB }},                      /* 1EDA; 1EDB; Case map */
+  { 0x001EDC, 0, { 0x001EDD }},                      /* 1EDC; 1EDD; Case map */
+  { 0x001EDE, 0, { 0x001EDF }},                      /* 1EDE; 1EDF; Case map */
+  { 0x001EE0, 0, { 0x001EE1 }},                      /* 1EE0; 1EE1; Case map */
+  { 0x001EE2, 0, { 0x001EE3 }},                      /* 1EE2; 1EE3; Case map */
+  { 0x001EE4, 0, { 0x001EE5 }},                      /* 1EE4; 1EE5; Case map */
+  { 0x001EE6, 0, { 0x001EE7 }},                      /* 1EE6; 1EE7; Case map */
+  { 0x001EE8, 0, { 0x001EE9 }},                      /* 1EE8; 1EE9; Case map */
+  { 0x001EEA, 0, { 0x001EEB }},                      /* 1EEA; 1EEB; Case map */
+  { 0x001EEC, 0, { 0x001EED }},                      /* 1EEC; 1EED; Case map */
+  { 0x001EEE, 0, { 0x001EEF }},                      /* 1EEE; 1EEF; Case map */
+  { 0x001EF0, 0, { 0x001EF1 }},                      /* 1EF0; 1EF1; Case map */
+  { 0x001EF2, 0, { 0x001EF3 }},                      /* 1EF2; 1EF3; Case map */
+  { 0x001EF4, 0, { 0x001EF5 }},                      /* 1EF4; 1EF5; Case map */
+  { 0x001EF6, 0, { 0x001EF7 }},                      /* 1EF6; 1EF7; Case map */
+  { 0x001EF8, 0, { 0x001EF9 }},                      /* 1EF8; 1EF9; Case map */
+  { 0x001F08, 0, { 0x001F00 }},                      /* 1F08; 1F00; Case map */
+  { 0x001F09, 0, { 0x001F01 }},                      /* 1F09; 1F01; Case map */
+  { 0x001F0A, 0, { 0x001F02 }},                      /* 1F0A; 1F02; Case map */
+  { 0x001F0B, 0, { 0x001F03 }},                      /* 1F0B; 1F03; Case map */
+  { 0x001F0C, 0, { 0x001F04 }},                      /* 1F0C; 1F04; Case map */
+  { 0x001F0D, 0, { 0x001F05 }},                      /* 1F0D; 1F05; Case map */
+  { 0x001F0E, 0, { 0x001F06 }},                      /* 1F0E; 1F06; Case map */
+  { 0x001F0F, 0, { 0x001F07 }},                      /* 1F0F; 1F07; Case map */
+  { 0x001F18, 0, { 0x001F10 }},                      /* 1F18; 1F10; Case map */
+  { 0x001F19, 0, { 0x001F11 }},                      /* 1F19; 1F11; Case map */
+  { 0x001F1A, 0, { 0x001F12 }},                      /* 1F1A; 1F12; Case map */
+  { 0x001F1B, 0, { 0x001F13 }},                      /* 1F1B; 1F13; Case map */
+  { 0x001F1C, 0, { 0x001F14 }},                      /* 1F1C; 1F14; Case map */
+  { 0x001F1D, 0, { 0x001F15 }},                      /* 1F1D; 1F15; Case map */
+  { 0x001F28, 0, { 0x001F20 }},                      /* 1F28; 1F20; Case map */
+  { 0x001F29, 0, { 0x001F21 }},                      /* 1F29; 1F21; Case map */
+  { 0x001F2A, 0, { 0x001F22 }},                      /* 1F2A; 1F22; Case map */
+  { 0x001F2B, 0, { 0x001F23 }},                      /* 1F2B; 1F23; Case map */
+  { 0x001F2C, 0, { 0x001F24 }},                      /* 1F2C; 1F24; Case map */
+  { 0x001F2D, 0, { 0x001F25 }},                      /* 1F2D; 1F25; Case map */
+  { 0x001F2E, 0, { 0x001F26 }},                      /* 1F2E; 1F26; Case map */
+  { 0x001F2F, 0, { 0x001F27 }},                      /* 1F2F; 1F27; Case map */
+  { 0x001F38, 0, { 0x001F30 }},                      /* 1F38; 1F30; Case map */
+  { 0x001F39, 0, { 0x001F31 }},                      /* 1F39; 1F31; Case map */
+  { 0x001F3A, 0, { 0x001F32 }},                      /* 1F3A; 1F32; Case map */
+  { 0x001F3B, 0, { 0x001F33 }},                      /* 1F3B; 1F33; Case map */
+  { 0x001F3C, 0, { 0x001F34 }},                      /* 1F3C; 1F34; Case map */
+  { 0x001F3D, 0, { 0x001F35 }},                      /* 1F3D; 1F35; Case map */
+  { 0x001F3E, 0, { 0x001F36 }},                      /* 1F3E; 1F36; Case map */
+  { 0x001F3F, 0, { 0x001F37 }},                      /* 1F3F; 1F37; Case map */
+  { 0x001F48, 0, { 0x001F40 }},                      /* 1F48; 1F40; Case map */
+  { 0x001F49, 0, { 0x001F41 }},                      /* 1F49; 1F41; Case map */
+  { 0x001F4A, 0, { 0x001F42 }},                      /* 1F4A; 1F42; Case map */
+  { 0x001F4B, 0, { 0x001F43 }},                      /* 1F4B; 1F43; Case map */
+  { 0x001F4C, 0, { 0x001F44 }},                      /* 1F4C; 1F44; Case map */
+  { 0x001F4D, 0, { 0x001F45 }},                      /* 1F4D; 1F45; Case map */
+  { 0x001F50, 0, { 0x0003C5,                    /* 1F50; 03C5 0313; Case map */
+                   0x000313 }},
+  { 0x001F52, 0, { 0x0003C5,               /* 1F52; 03C5 0313 0300; Case map */
+                   0x000313, 0x000300 }},
+  { 0x001F54, 0, { 0x0003C5,               /* 1F54; 03C5 0313 0301; Case map */
+                   0x000313, 0x000301 }},
+  { 0x001F56, 0, { 0x0003C5,               /* 1F56; 03C5 0313 0342; Case map */
+                   0x000313, 0x000342 }},
+  { 0x001F59, 0, { 0x001F51 }},                      /* 1F59; 1F51; Case map */
+  { 0x001F5B, 0, { 0x001F53 }},                      /* 1F5B; 1F53; Case map */
+  { 0x001F5D, 0, { 0x001F55 }},                      /* 1F5D; 1F55; Case map */
+  { 0x001F5F, 0, { 0x001F57 }},                      /* 1F5F; 1F57; Case map */
+  { 0x001F68, 0, { 0x001F60 }},                      /* 1F68; 1F60; Case map */
+  { 0x001F69, 0, { 0x001F61 }},                      /* 1F69; 1F61; Case map */
+  { 0x001F6A, 0, { 0x001F62 }},                      /* 1F6A; 1F62; Case map */
+  { 0x001F6B, 0, { 0x001F63 }},                      /* 1F6B; 1F63; Case map */
+  { 0x001F6C, 0, { 0x001F64 }},                      /* 1F6C; 1F64; Case map */
+  { 0x001F6D, 0, { 0x001F65 }},                      /* 1F6D; 1F65; Case map */
+  { 0x001F6E, 0, { 0x001F66 }},                      /* 1F6E; 1F66; Case map */
+  { 0x001F6F, 0, { 0x001F67 }},                      /* 1F6F; 1F67; Case map */
+  { 0x001F80, 0, { 0x001F00,                    /* 1F80; 1F00 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F81, 0, { 0x001F01,                    /* 1F81; 1F01 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F82, 0, { 0x001F02,                    /* 1F82; 1F02 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F83, 0, { 0x001F03,                    /* 1F83; 1F03 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F84, 0, { 0x001F04,                    /* 1F84; 1F04 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F85, 0, { 0x001F05,                    /* 1F85; 1F05 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F86, 0, { 0x001F06,                    /* 1F86; 1F06 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F87, 0, { 0x001F07,                    /* 1F87; 1F07 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F88, 0, { 0x001F00,                    /* 1F88; 1F00 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F89, 0, { 0x001F01,                    /* 1F89; 1F01 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8A, 0, { 0x001F02,                    /* 1F8A; 1F02 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8B, 0, { 0x001F03,                    /* 1F8B; 1F03 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8C, 0, { 0x001F04,                    /* 1F8C; 1F04 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8D, 0, { 0x001F05,                    /* 1F8D; 1F05 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8E, 0, { 0x001F06,                    /* 1F8E; 1F06 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F8F, 0, { 0x001F07,                    /* 1F8F; 1F07 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F90, 0, { 0x001F20,                    /* 1F90; 1F20 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F91, 0, { 0x001F21,                    /* 1F91; 1F21 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F92, 0, { 0x001F22,                    /* 1F92; 1F22 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F93, 0, { 0x001F23,                    /* 1F93; 1F23 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F94, 0, { 0x001F24,                    /* 1F94; 1F24 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F95, 0, { 0x001F25,                    /* 1F95; 1F25 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F96, 0, { 0x001F26,                    /* 1F96; 1F26 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F97, 0, { 0x001F27,                    /* 1F97; 1F27 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F98, 0, { 0x001F20,                    /* 1F98; 1F20 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F99, 0, { 0x001F21,                    /* 1F99; 1F21 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9A, 0, { 0x001F22,                    /* 1F9A; 1F22 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9B, 0, { 0x001F23,                    /* 1F9B; 1F23 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9C, 0, { 0x001F24,                    /* 1F9C; 1F24 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9D, 0, { 0x001F25,                    /* 1F9D; 1F25 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9E, 0, { 0x001F26,                    /* 1F9E; 1F26 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001F9F, 0, { 0x001F27,                    /* 1F9F; 1F27 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA0, 0, { 0x001F60,                    /* 1FA0; 1F60 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA1, 0, { 0x001F61,                    /* 1FA1; 1F61 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA2, 0, { 0x001F62,                    /* 1FA2; 1F62 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA3, 0, { 0x001F63,                    /* 1FA3; 1F63 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA4, 0, { 0x001F64,                    /* 1FA4; 1F64 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA5, 0, { 0x001F65,                    /* 1FA5; 1F65 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA6, 0, { 0x001F66,                    /* 1FA6; 1F66 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA7, 0, { 0x001F67,                    /* 1FA7; 1F67 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA8, 0, { 0x001F60,                    /* 1FA8; 1F60 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FA9, 0, { 0x001F61,                    /* 1FA9; 1F61 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAA, 0, { 0x001F62,                    /* 1FAA; 1F62 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAB, 0, { 0x001F63,                    /* 1FAB; 1F63 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAC, 0, { 0x001F64,                    /* 1FAC; 1F64 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAD, 0, { 0x001F65,                    /* 1FAD; 1F65 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAE, 0, { 0x001F66,                    /* 1FAE; 1F66 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FAF, 0, { 0x001F67,                    /* 1FAF; 1F67 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FB2, 0, { 0x001F70,                    /* 1FB2; 1F70 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FB3, 0, { 0x0003B1,                    /* 1FB3; 03B1 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FB4, 0, { 0x0003AC,                    /* 1FB4; 03AC 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FB6, 0, { 0x0003B1,                    /* 1FB6; 03B1 0342; Case map */
+                   0x000342 }},
+  { 0x001FB7, 0, { 0x0003B1,               /* 1FB7; 03B1 0342 03B9; Case map */
+                   0x000342, 0x0003B9 }},
+  { 0x001FB8, 0, { 0x001FB0 }},                      /* 1FB8; 1FB0; Case map */
+  { 0x001FB9, 0, { 0x001FB1 }},                      /* 1FB9; 1FB1; Case map */
+  { 0x001FBA, 0, { 0x001F70 }},                      /* 1FBA; 1F70; Case map */
+  { 0x001FBB, 0, { 0x001F71 }},                      /* 1FBB; 1F71; Case map */
+  { 0x001FBC, 0, { 0x0003B1,                    /* 1FBC; 03B1 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FBE, 0, { 0x0003B9 }},                      /* 1FBE; 03B9; Case map */
+  { 0x001FC2, 0, { 0x001F74,                    /* 1FC2; 1F74 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FC3, 0, { 0x0003B7,                    /* 1FC3; 03B7 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FC4, 0, { 0x0003AE,                    /* 1FC4; 03AE 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FC6, 0, { 0x0003B7,                    /* 1FC6; 03B7 0342; Case map */
+                   0x000342 }},
+  { 0x001FC7, 0, { 0x0003B7,               /* 1FC7; 03B7 0342 03B9; Case map */
+                   0x000342, 0x0003B9 }},
+  { 0x001FC8, 0, { 0x001F72 }},                      /* 1FC8; 1F72; Case map */
+  { 0x001FC9, 0, { 0x001F73 }},                      /* 1FC9; 1F73; Case map */
+  { 0x001FCA, 0, { 0x001F74 }},                      /* 1FCA; 1F74; Case map */
+  { 0x001FCB, 0, { 0x001F75 }},                      /* 1FCB; 1F75; Case map */
+  { 0x001FCC, 0, { 0x0003B7,                    /* 1FCC; 03B7 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FD2, 0, { 0x0003B9,               /* 1FD2; 03B9 0308 0300; Case map */
+                   0x000308, 0x000300 }},
+  { 0x001FD3, 0, { 0x0003B9,               /* 1FD3; 03B9 0308 0301; Case map */
+                   0x000308, 0x000301 }},
+  { 0x001FD6, 0, { 0x0003B9,                    /* 1FD6; 03B9 0342; Case map */
+                   0x000342 }},
+  { 0x001FD7, 0, { 0x0003B9,               /* 1FD7; 03B9 0308 0342; Case map */
+                   0x000308, 0x000342 }},
+  { 0x001FD8, 0, { 0x001FD0 }},                      /* 1FD8; 1FD0; Case map */
+  { 0x001FD9, 0, { 0x001FD1 }},                      /* 1FD9; 1FD1; Case map */
+  { 0x001FDA, 0, { 0x001F76 }},                      /* 1FDA; 1F76; Case map */
+  { 0x001FDB, 0, { 0x001F77 }},                      /* 1FDB; 1F77; Case map */
+  { 0x001FE2, 0, { 0x0003C5,               /* 1FE2; 03C5 0308 0300; Case map */
+                   0x000308, 0x000300 }},
+  { 0x001FE3, 0, { 0x0003C5,               /* 1FE3; 03C5 0308 0301; Case map */
+                   0x000308, 0x000301 }},
+  { 0x001FE4, 0, { 0x0003C1,                    /* 1FE4; 03C1 0313; Case map */
+                   0x000313 }},
+  { 0x001FE6, 0, { 0x0003C5,                    /* 1FE6; 03C5 0342; Case map */
+                   0x000342 }},
+  { 0x001FE7, 0, { 0x0003C5,               /* 1FE7; 03C5 0308 0342; Case map */
+                   0x000308, 0x000342 }},
+  { 0x001FE8, 0, { 0x001FE0 }},                      /* 1FE8; 1FE0; Case map */
+  { 0x001FE9, 0, { 0x001FE1 }},                      /* 1FE9; 1FE1; Case map */
+  { 0x001FEA, 0, { 0x001F7A }},                      /* 1FEA; 1F7A; Case map */
+  { 0x001FEB, 0, { 0x001F7B }},                      /* 1FEB; 1F7B; Case map */
+  { 0x001FEC, 0, { 0x001FE5 }},                      /* 1FEC; 1FE5; Case map */
+  { 0x001FF2, 0, { 0x001F7C,                    /* 1FF2; 1F7C 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FF3, 0, { 0x0003C9,                    /* 1FF3; 03C9 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FF4, 0, { 0x0003CE,                    /* 1FF4; 03CE 03B9; Case map */
+                   0x0003B9 }},
+  { 0x001FF6, 0, { 0x0003C9,                    /* 1FF6; 03C9 0342; Case map */
+                   0x000342 }},
+  { 0x001FF7, 0, { 0x0003C9,               /* 1FF7; 03C9 0342 03B9; Case map */
+                   0x000342, 0x0003B9 }},
+  { 0x001FF8, 0, { 0x001F78 }},                      /* 1FF8; 1F78; Case map */
+  { 0x001FF9, 0, { 0x001F79 }},                      /* 1FF9; 1F79; Case map */
+  { 0x001FFA, 0, { 0x001F7C }},                      /* 1FFA; 1F7C; Case map */
+  { 0x001FFB, 0, { 0x001F7D }},                      /* 1FFB; 1F7D; Case map */
+  { 0x001FFC, 0, { 0x0003C9,                    /* 1FFC; 03C9 03B9; Case map */
+                   0x0003B9 }},
+  { 0x002126, 0, { 0x0003C9 }},                      /* 2126; 03C9; Case map */
+  { 0x00212A, 0, { 0x00006B }},                      /* 212A; 006B; Case map */
+  { 0x00212B, 0, { 0x0000E5 }},                      /* 212B; 00E5; Case map */
+  { 0x002160, 0, { 0x002170 }},                      /* 2160; 2170; Case map */
+  { 0x002161, 0, { 0x002171 }},                      /* 2161; 2171; Case map */
+  { 0x002162, 0, { 0x002172 }},                      /* 2162; 2172; Case map */
+  { 0x002163, 0, { 0x002173 }},                      /* 2163; 2173; Case map */
+  { 0x002164, 0, { 0x002174 }},                      /* 2164; 2174; Case map */
+  { 0x002165, 0, { 0x002175 }},                      /* 2165; 2175; Case map */
+  { 0x002166, 0, { 0x002176 }},                      /* 2166; 2176; Case map */
+  { 0x002167, 0, { 0x002177 }},                      /* 2167; 2177; Case map */
+  { 0x002168, 0, { 0x002178 }},                      /* 2168; 2178; Case map */
+  { 0x002169, 0, { 0x002179 }},                      /* 2169; 2179; Case map */
+  { 0x00216A, 0, { 0x00217A }},                      /* 216A; 217A; Case map */
+  { 0x00216B, 0, { 0x00217B }},                      /* 216B; 217B; Case map */
+  { 0x00216C, 0, { 0x00217C }},                      /* 216C; 217C; Case map */
+  { 0x00216D, 0, { 0x00217D }},                      /* 216D; 217D; Case map */
+  { 0x00216E, 0, { 0x00217E }},                      /* 216E; 217E; Case map */
+  { 0x00216F, 0, { 0x00217F }},                      /* 216F; 217F; Case map */
+  { 0x0024B6, 0, { 0x0024D0 }},                      /* 24B6; 24D0; Case map */
+  { 0x0024B7, 0, { 0x0024D1 }},                      /* 24B7; 24D1; Case map */
+  { 0x0024B8, 0, { 0x0024D2 }},                      /* 24B8; 24D2; Case map */
+  { 0x0024B9, 0, { 0x0024D3 }},                      /* 24B9; 24D3; Case map */
+  { 0x0024BA, 0, { 0x0024D4 }},                      /* 24BA; 24D4; Case map */
+  { 0x0024BB, 0, { 0x0024D5 }},                      /* 24BB; 24D5; Case map */
+  { 0x0024BC, 0, { 0x0024D6 }},                      /* 24BC; 24D6; Case map */
+  { 0x0024BD, 0, { 0x0024D7 }},                      /* 24BD; 24D7; Case map */
+  { 0x0024BE, 0, { 0x0024D8 }},                      /* 24BE; 24D8; Case map */
+  { 0x0024BF, 0, { 0x0024D9 }},                      /* 24BF; 24D9; Case map */
+  { 0x0024C0, 0, { 0x0024DA }},                      /* 24C0; 24DA; Case map */
+  { 0x0024C1, 0, { 0x0024DB }},                      /* 24C1; 24DB; Case map */
+  { 0x0024C2, 0, { 0x0024DC }},                      /* 24C2; 24DC; Case map */
+  { 0x0024C3, 0, { 0x0024DD }},                      /* 24C3; 24DD; Case map */
+  { 0x0024C4, 0, { 0x0024DE }},                      /* 24C4; 24DE; Case map */
+  { 0x0024C5, 0, { 0x0024DF }},                      /* 24C5; 24DF; Case map */
+  { 0x0024C6, 0, { 0x0024E0 }},                      /* 24C6; 24E0; Case map */
+  { 0x0024C7, 0, { 0x0024E1 }},                      /* 24C7; 24E1; Case map */
+  { 0x0024C8, 0, { 0x0024E2 }},                      /* 24C8; 24E2; Case map */
+  { 0x0024C9, 0, { 0x0024E3 }},                      /* 24C9; 24E3; Case map */
+  { 0x0024CA, 0, { 0x0024E4 }},                      /* 24CA; 24E4; Case map */
+  { 0x0024CB, 0, { 0x0024E5 }},                      /* 24CB; 24E5; Case map */
+  { 0x0024CC, 0, { 0x0024E6 }},                      /* 24CC; 24E6; Case map */
+  { 0x0024CD, 0, { 0x0024E7 }},                      /* 24CD; 24E7; Case map */
+  { 0x0024CE, 0, { 0x0024E8 }},                      /* 24CE; 24E8; Case map */
+  { 0x0024CF, 0, { 0x0024E9 }},                      /* 24CF; 24E9; Case map */
+  { 0x00FB00, 0, { 0x000066,                    /* FB00; 0066 0066; Case map */
+                   0x000066 }},
+  { 0x00FB01, 0, { 0x000066,                    /* FB01; 0066 0069; Case map */
+                   0x000069 }},
+  { 0x00FB02, 0, { 0x000066,                    /* FB02; 0066 006C; Case map */
+                   0x00006C }},
+  { 0x00FB03, 0, { 0x000066,               /* FB03; 0066 0066 0069; Case map */
+                   0x000066, 0x000069 }},
+  { 0x00FB04, 0, { 0x000066,               /* FB04; 0066 0066 006C; Case map */
+                   0x000066, 0x00006C }},
+  { 0x00FB05, 0, { 0x000073,                    /* FB05; 0073 0074; Case map */
+                   0x000074 }},
+  { 0x00FB06, 0, { 0x000073,                    /* FB06; 0073 0074; Case map */
+                   0x000074 }},
+  { 0x00FB13, 0, { 0x000574,                    /* FB13; 0574 0576; Case map */
+                   0x000576 }},
+  { 0x00FB14, 0, { 0x000574,                    /* FB14; 0574 0565; Case map */
+                   0x000565 }},
+  { 0x00FB15, 0, { 0x000574,                    /* FB15; 0574 056B; Case map */
+                   0x00056B }},
+  { 0x00FB16, 0, { 0x00057E,                    /* FB16; 057E 0576; Case map */
+                   0x000576 }},
+  { 0x00FB17, 0, { 0x000574,                    /* FB17; 0574 056D; Case map */
+                   0x00056D }},
+  { 0x00FF21, 0, { 0x00FF41 }},                      /* FF21; FF41; Case map */
+  { 0x00FF22, 0, { 0x00FF42 }},                      /* FF22; FF42; Case map */
+  { 0x00FF23, 0, { 0x00FF43 }},                      /* FF23; FF43; Case map */
+  { 0x00FF24, 0, { 0x00FF44 }},                      /* FF24; FF44; Case map */
+  { 0x00FF25, 0, { 0x00FF45 }},                      /* FF25; FF45; Case map */
+  { 0x00FF26, 0, { 0x00FF46 }},                      /* FF26; FF46; Case map */
+  { 0x00FF27, 0, { 0x00FF47 }},                      /* FF27; FF47; Case map */
+  { 0x00FF28, 0, { 0x00FF48 }},                      /* FF28; FF48; Case map */
+  { 0x00FF29, 0, { 0x00FF49 }},                      /* FF29; FF49; Case map */
+  { 0x00FF2A, 0, { 0x00FF4A }},                      /* FF2A; FF4A; Case map */
+  { 0x00FF2B, 0, { 0x00FF4B }},                      /* FF2B; FF4B; Case map */
+  { 0x00FF2C, 0, { 0x00FF4C }},                      /* FF2C; FF4C; Case map */
+  { 0x00FF2D, 0, { 0x00FF4D }},                      /* FF2D; FF4D; Case map */
+  { 0x00FF2E, 0, { 0x00FF4E }},                      /* FF2E; FF4E; Case map */
+  { 0x00FF2F, 0, { 0x00FF4F }},                      /* FF2F; FF4F; Case map */
+  { 0x00FF30, 0, { 0x00FF50 }},                      /* FF30; FF50; Case map */
+  { 0x00FF31, 0, { 0x00FF51 }},                      /* FF31; FF51; Case map */
+  { 0x00FF32, 0, { 0x00FF52 }},                      /* FF32; FF52; Case map */
+  { 0x00FF33, 0, { 0x00FF53 }},                      /* FF33; FF53; Case map */
+  { 0x00FF34, 0, { 0x00FF54 }},                      /* FF34; FF54; Case map */
+  { 0x00FF35, 0, { 0x00FF55 }},                      /* FF35; FF55; Case map */
+  { 0x00FF36, 0, { 0x00FF56 }},                      /* FF36; FF56; Case map */
+  { 0x00FF37, 0, { 0x00FF57 }},                      /* FF37; FF57; Case map */
+  { 0x00FF38, 0, { 0x00FF58 }},                      /* FF38; FF58; Case map */
+  { 0x00FF39, 0, { 0x00FF59 }},                      /* FF39; FF59; Case map */
+  { 0x00FF3A, 0, { 0x00FF5A }},                      /* FF3A; FF5A; Case map */
+  { 0x010400, 0, { 0x010428 }},                    /* 10400; 10428; Case map */
+  { 0x010401, 0, { 0x010429 }},                    /* 10401; 10429; Case map */
+  { 0x010402, 0, { 0x01042A }},                    /* 10402; 1042A; Case map */
+  { 0x010403, 0, { 0x01042B }},                    /* 10403; 1042B; Case map */
+  { 0x010404, 0, { 0x01042C }},                    /* 10404; 1042C; Case map */
+  { 0x010405, 0, { 0x01042D }},                    /* 10405; 1042D; Case map */
+  { 0x010406, 0, { 0x01042E }},                    /* 10406; 1042E; Case map */
+  { 0x010407, 0, { 0x01042F }},                    /* 10407; 1042F; Case map */
+  { 0x010408, 0, { 0x010430 }},                    /* 10408; 10430; Case map */
+  { 0x010409, 0, { 0x010431 }},                    /* 10409; 10431; Case map */
+  { 0x01040A, 0, { 0x010432 }},                    /* 1040A; 10432; Case map */
+  { 0x01040B, 0, { 0x010433 }},                    /* 1040B; 10433; Case map */
+  { 0x01040C, 0, { 0x010434 }},                    /* 1040C; 10434; Case map */
+  { 0x01040D, 0, { 0x010435 }},                    /* 1040D; 10435; Case map */
+  { 0x01040E, 0, { 0x010436 }},                    /* 1040E; 10436; Case map */
+  { 0x01040F, 0, { 0x010437 }},                    /* 1040F; 10437; Case map */
+  { 0x010410, 0, { 0x010438 }},                    /* 10410; 10438; Case map */
+  { 0x010411, 0, { 0x010439 }},                    /* 10411; 10439; Case map */
+  { 0x010412, 0, { 0x01043A }},                    /* 10412; 1043A; Case map */
+  { 0x010413, 0, { 0x01043B }},                    /* 10413; 1043B; Case map */
+  { 0x010414, 0, { 0x01043C }},                    /* 10414; 1043C; Case map */
+  { 0x010415, 0, { 0x01043D }},                    /* 10415; 1043D; Case map */
+  { 0x010416, 0, { 0x01043E }},                    /* 10416; 1043E; Case map */
+  { 0x010417, 0, { 0x01043F }},                    /* 10417; 1043F; Case map */
+  { 0x010418, 0, { 0x010440 }},                    /* 10418; 10440; Case map */
+  { 0x010419, 0, { 0x010441 }},                    /* 10419; 10441; Case map */
+  { 0x01041A, 0, { 0x010442 }},                    /* 1041A; 10442; Case map */
+  { 0x01041B, 0, { 0x010443 }},                    /* 1041B; 10443; Case map */
+  { 0x01041C, 0, { 0x010444 }},                    /* 1041C; 10444; Case map */
+  { 0x01041D, 0, { 0x010445 }},                    /* 1041D; 10445; Case map */
+  { 0x01041E, 0, { 0x010446 }},                    /* 1041E; 10446; Case map */
+  { 0x01041F, 0, { 0x010447 }},                    /* 1041F; 10447; Case map */
+  { 0x010420, 0, { 0x010448 }},                    /* 10420; 10448; Case map */
+  { 0x010421, 0, { 0x010449 }},                    /* 10421; 10449; Case map */
+  { 0x010422, 0, { 0x01044A }},                    /* 10422; 1044A; Case map */
+  { 0x010423, 0, { 0x01044B }},                    /* 10423; 1044B; Case map */
+  { 0x010424, 0, { 0x01044C }},                    /* 10424; 1044C; Case map */
+  { 0x010425, 0, { 0x01044D }},                    /* 10425; 1044D; Case map */
+  { 0 },
+};
+
+
+/*
+ * C.1.1 ASCII space characters
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_1_1[] = {
+  { 0x000020           },                                     /* 0020; SPACE */
+  { 0 },
+};
+
+
+/*
+ * C.1.2 Non-ASCII space characters
+ *  */
+
+Stringprep_table_element stringprep_rfc3454_C_1_2[] = {
+  { 0x0000A0           },                            /* 00A0; NO-BREAK SPACE */
+  { 0x001680           },                          /* 1680; OGHAM SPACE MARK */
+  { 0x002000           },                                   /* 2000; EN QUAD */
+  { 0x002001           },                                   /* 2001; EM QUAD */
+  { 0x002002           },                                  /* 2002; EN SPACE */
+  { 0x002003           },                                  /* 2003; EM SPACE */
+  { 0x002004           },                        /* 2004; THREE-PER-EM SPACE */
+  { 0x002005           },                         /* 2005; FOUR-PER-EM SPACE */
+  { 0x002006           },                          /* 2006; SIX-PER-EM SPACE */
+  { 0x002007           },                              /* 2007; FIGURE SPACE */
+  { 0x002008           },                         /* 2008; PUNCTUATION SPACE */
+  { 0x002009           },                                /* 2009; THIN SPACE */
+  { 0x00200A           },                                /* 200A; HAIR SPACE */
+  { 0x00200B           },                          /* 200B; ZERO WIDTH SPACE */
+  { 0x00202F           },                     /* 202F; NARROW NO-BREAK SPACE */
+  { 0x00205F           },                 /* 205F; MEDIUM MATHEMATICAL SPACE */
+  { 0x003000           },                         /* 3000; IDEOGRAPHIC SPACE */
+  { 0 },
+};
+
+
+/*
+ * C.2.1 ASCII control characters
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_2_1[] = {
+  { 0x000000, 0x00001F },                 /* 0000-001F; [CONTROL CHARACTERS] */
+  { 0x00007F           },                                    /* 007F; DELETE */
+  { 0 },
+};
+
+
+/*
+ * C.2.2 Non-ASCII control characters
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_2_2[] = {
+  { 0x000080, 0x00009F },                 /* 0080-009F; [CONTROL CHARACTERS] */
+  { 0x0006DD           },                        /* 06DD; ARABIC END OF AYAH */
+  { 0x00070F           },                  /* 070F; SYRIAC ABBREVIATION MARK */
+  { 0x00180E           },                 /* 180E; MONGOLIAN VOWEL SEPARATOR */
+  { 0x00200C           },                     /* 200C; ZERO WIDTH NON-JOINER */
+  { 0x00200D           },                         /* 200D; ZERO WIDTH JOINER */
+  { 0x002028           },                            /* 2028; LINE SEPARATOR */
+  { 0x002029           },                       /* 2029; PARAGRAPH SEPARATOR */
+  { 0x002060           },                               /* 2060; WORD JOINER */
+  { 0x002061           },                      /* 2061; FUNCTION APPLICATION */
+  { 0x002062           },                           /* 2062; INVISIBLE TIMES */
+  { 0x002063           },                       /* 2063; INVISIBLE SEPARATOR */
+  { 0x00206A, 0x00206F },                 /* 206A-206F; [CONTROL CHARACTERS] */
+  { 0x00FEFF           },                 /* FEFF; ZERO WIDTH NO-BREAK SPACE */
+  { 0x00FFF9, 0x00FFFC },                 /* FFF9-FFFC; [CONTROL CHARACTERS] */
+  { 0x01D173, 0x01D17A },       /* 1D173-1D17A; [MUSICAL CONTROL CHARACTERS] */
+  { 0 },
+};
+
+
+/*
+ * C.3 Private use
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_3[] = {
+  { 0x00E000, 0x00F8FF },               /* E000-F8FF; [PRIVATE USE, PLANE 0] */
+  { 0x0F0000, 0x0FFFFD },            /* F0000-FFFFD; [PRIVATE USE, PLANE 15] */
+  { 0x100000, 0x10FFFD },          /* 100000-10FFFD; [PRIVATE USE, PLANE 16] */
+  { 0 },
+};
+
+
+/*
+ * C.4 Non-character code points
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_4[] = {
+  { 0x00FDD0, 0x00FDEF },           /* FDD0-FDEF; [NONCHARACTER CODE POINTS] */
+  { 0x00FFFE, 0x00FFFF },           /* FFFE-FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x01FFFE, 0x01FFFF },         /* 1FFFE-1FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x02FFFE, 0x02FFFF },         /* 2FFFE-2FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x03FFFE, 0x03FFFF },         /* 3FFFE-3FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x04FFFE, 0x04FFFF },         /* 4FFFE-4FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x05FFFE, 0x05FFFF },         /* 5FFFE-5FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x06FFFE, 0x06FFFF },         /* 6FFFE-6FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x07FFFE, 0x07FFFF },         /* 7FFFE-7FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x08FFFE, 0x08FFFF },         /* 8FFFE-8FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x09FFFE, 0x09FFFF },         /* 9FFFE-9FFFF; [NONCHARACTER CODE POINTS] */
+  { 0x0AFFFE, 0x0AFFFF },         /* AFFFE-AFFFF; [NONCHARACTER CODE POINTS] */
+  { 0x0BFFFE, 0x0BFFFF },         /* BFFFE-BFFFF; [NONCHARACTER CODE POINTS] */
+  { 0x0CFFFE, 0x0CFFFF },         /* CFFFE-CFFFF; [NONCHARACTER CODE POINTS] */
+  { 0x0DFFFE, 0x0DFFFF },         /* DFFFE-DFFFF; [NONCHARACTER CODE POINTS] */
+  { 0x0EFFFE, 0x0EFFFF },         /* EFFFE-EFFFF; [NONCHARACTER CODE POINTS] */
+  { 0x0FFFFE, 0x0FFFFF },         /* FFFFE-FFFFF; [NONCHARACTER CODE POINTS] */
+  { 0x10FFFE, 0x10FFFF },       /* 10FFFE-10FFFF; [NONCHARACTER CODE POINTS] */
+  { 0 },
+};
+
+
+/*
+ * C.5 Surrogate codes
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_5[] = {
+  { 0x00D800, 0x00DFFF },                    /* D800-DFFF; [SURROGATE CODES] */
+  { 0 },
+};
+
+
+/*
+ * C.6 Inappropriate for plain text
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_6[] = {
+  { 0x00FFF9           },             /* FFF9; INTERLINEAR ANNOTATION ANCHOR */
+  { 0x00FFFA           },          /* FFFA; INTERLINEAR ANNOTATION SEPARATOR */
+  { 0x00FFFB           },         /* FFFB; INTERLINEAR ANNOTATION TERMINATOR */
+  { 0x00FFFC           },              /* FFFC; OBJECT REPLACEMENT CHARACTER */
+  { 0x00FFFD           },                     /* FFFD; REPLACEMENT CHARACTER */
+  { 0 },
+};
+
+
+/*
+ * C.7 Inappropriate for canonical representation
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_7[] = {
+  { 0x002FF0, 0x002FFB }, /* 2FF0-2FFB; [IDEOGRAPHIC DESCRIPTION CHARACTERS] */
+  { 0 },
+};
+
+
+/*
+ * C.8 Change display properties or are deprecated
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_8[] = {
+  { 0x000340           },                 /* 0340; COMBINING GRAVE TONE MARK */
+  { 0x000341           },                 /* 0341; COMBINING ACUTE TONE MARK */
+  { 0x00200E           },                        /* 200E; LEFT-TO-RIGHT MARK */
+  { 0x00200F           },                        /* 200F; RIGHT-TO-LEFT MARK */
+  { 0x00202A           },                   /* 202A; LEFT-TO-RIGHT EMBEDDING */
+  { 0x00202B           },                   /* 202B; RIGHT-TO-LEFT EMBEDDING */
+  { 0x00202C           },                /* 202C; POP DIRECTIONAL FORMATTING */
+  { 0x00202D           },                    /* 202D; LEFT-TO-RIGHT OVERRIDE */
+  { 0x00202E           },                    /* 202E; RIGHT-TO-LEFT OVERRIDE */
+  { 0x00206A           },                /* 206A; INHIBIT SYMMETRIC SWAPPING */
+  { 0x00206B           },               /* 206B; ACTIVATE SYMMETRIC SWAPPING */
+  { 0x00206C           },               /* 206C; INHIBIT ARABIC FORM SHAPING */
+  { 0x00206D           },              /* 206D; ACTIVATE ARABIC FORM SHAPING */
+  { 0x00206E           },                     /* 206E; NATIONAL DIGIT SHAPES */
+  { 0x00206F           },                      /* 206F; NOMINAL DIGIT SHAPES */
+  { 0 },
+};
+
+
+/*
+ * C.9 Tagging characters
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_C_9[] = {
+  { 0x0E0001           },                             /* E0001; LANGUAGE TAG */
+  { 0x0E0020, 0x0E007F },               /* E0020-E007F; [TAGGING CHARACTERS] */
+  { 0 },
+};
+
+
+/*
+ * D.1 Characters with bidirectional property "R" or "AL"
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_D_1[] = {
+  { 0x0005BE           },                                            /* 05BE */
+  { 0x0005C0           },                                            /* 05C0 */
+  { 0x0005C3           },                                            /* 05C3 */
+  { 0x0005D0, 0x0005EA },                                       /* 05D0-05EA */
+  { 0x0005F0, 0x0005F4 },                                       /* 05F0-05F4 */
+  { 0x00061B           },                                            /* 061B */
+  { 0x00061F           },                                            /* 061F */
+  { 0x000621, 0x00063A },                                       /* 0621-063A */
+  { 0x000640, 0x00064A },                                       /* 0640-064A */
+  { 0x00066D, 0x00066F },                                       /* 066D-066F */
+  { 0x000671, 0x0006D5 },                                       /* 0671-06D5 */
+  { 0x0006DD           },                                            /* 06DD */
+  { 0x0006E5, 0x0006E6 },                                       /* 06E5-06E6 */
+  { 0x0006FA, 0x0006FE },                                       /* 06FA-06FE */
+  { 0x000700, 0x00070D },                                       /* 0700-070D */
+  { 0x000710           },                                            /* 0710 */
+  { 0x000712, 0x00072C },                                       /* 0712-072C */
+  { 0x000780, 0x0007A5 },                                       /* 0780-07A5 */
+  { 0x0007B1           },                                            /* 07B1 */
+  { 0x00200F           },                                            /* 200F */
+  { 0x00FB1D           },                                            /* FB1D */
+  { 0x00FB1F, 0x00FB28 },                                       /* FB1F-FB28 */
+  { 0x00FB2A, 0x00FB36 },                                       /* FB2A-FB36 */
+  { 0x00FB38, 0x00FB3C },                                       /* FB38-FB3C */
+  { 0x00FB3E           },                                            /* FB3E */
+  { 0x00FB40, 0x00FB41 },                                       /* FB40-FB41 */
+  { 0x00FB43, 0x00FB44 },                                       /* FB43-FB44 */
+  { 0x00FB46, 0x00FBB1 },                                       /* FB46-FBB1 */
+  { 0x00FBD3, 0x00FD3D },                                       /* FBD3-FD3D */
+  { 0x00FD50, 0x00FD8F },                                       /* FD50-FD8F */
+  { 0x00FD92, 0x00FDC7 },                                       /* FD92-FDC7 */
+  { 0x00FDF0, 0x00FDFC },                                       /* FDF0-FDFC */
+  { 0x00FE70, 0x00FE74 },                                       /* FE70-FE74 */
+  { 0x00FE76, 0x00FEFC },                                       /* FE76-FEFC */
+  { 0 },
+};
+
+
+/*
+ * D.2 Characters with bidirectional property "L"
+ * 
+ */
+
+Stringprep_table_element stringprep_rfc3454_D_2[] = {
+  { 0x000041, 0x00005A },                                       /* 0041-005A */
+  { 0x000061, 0x00007A },                                       /* 0061-007A */
+  { 0x0000AA           },                                            /* 00AA */
+  { 0x0000B5           },                                            /* 00B5 */
+  { 0x0000BA           },                                            /* 00BA */
+  { 0x0000C0, 0x0000D6 },                                       /* 00C0-00D6 */
+  { 0x0000D8, 0x0000F6 },                                       /* 00D8-00F6 */
+  { 0x0000F8, 0x000220 },                                       /* 00F8-0220 */
+  { 0x000222, 0x000233 },                                       /* 0222-0233 */
+  { 0x000250, 0x0002AD },                                       /* 0250-02AD */
+  { 0x0002B0, 0x0002B8 },                                       /* 02B0-02B8 */
+  { 0x0002BB, 0x0002C1 },                                       /* 02BB-02C1 */
+  { 0x0002D0, 0x0002D1 },                                       /* 02D0-02D1 */
+  { 0x0002E0, 0x0002E4 },                                       /* 02E0-02E4 */
+  { 0x0002EE           },                                            /* 02EE */
+  { 0x00037A           },                                            /* 037A */
+  { 0x000386           },                                            /* 0386 */
+  { 0x000388, 0x00038A },                                       /* 0388-038A */
+  { 0x00038C           },                                            /* 038C */
+  { 0x00038E, 0x0003A1 },                                       /* 038E-03A1 */
+  { 0x0003A3, 0x0003CE },                                       /* 03A3-03CE */
+  { 0x0003D0, 0x0003F5 },                                       /* 03D0-03F5 */
+  { 0x000400, 0x000482 },                                       /* 0400-0482 */
+  { 0x00048A, 0x0004CE },                                       /* 048A-04CE */
+  { 0x0004D0, 0x0004F5 },                                       /* 04D0-04F5 */
+  { 0x0004F8, 0x0004F9 },                                       /* 04F8-04F9 */
+  { 0x000500, 0x00050F },                                       /* 0500-050F */
+  { 0x000531, 0x000556 },                                       /* 0531-0556 */
+  { 0x000559, 0x00055F },                                       /* 0559-055F */
+  { 0x000561, 0x000587 },                                       /* 0561-0587 */
+  { 0x000589           },                                            /* 0589 */
+  { 0x000903           },                                            /* 0903 */
+  { 0x000905, 0x000939 },                                       /* 0905-0939 */
+  { 0x00093D, 0x000940 },                                       /* 093D-0940 */
+  { 0x000949, 0x00094C },                                       /* 0949-094C */
+  { 0x000950           },                                            /* 0950 */
+  { 0x000958, 0x000961 },                                       /* 0958-0961 */
+  { 0x000964, 0x000970 },                                       /* 0964-0970 */
+  { 0x000982, 0x000983 },                                       /* 0982-0983 */
+  { 0x000985, 0x00098C },                                       /* 0985-098C */
+  { 0x00098F, 0x000990 },                                       /* 098F-0990 */
+  { 0x000993, 0x0009A8 },                                       /* 0993-09A8 */
+  { 0x0009AA, 0x0009B0 },                                       /* 09AA-09B0 */
+  { 0x0009B2           },                                            /* 09B2 */
+  { 0x0009B6, 0x0009B9 },                                       /* 09B6-09B9 */
+  { 0x0009BE, 0x0009C0 },                                       /* 09BE-09C0 */
+  { 0x0009C7, 0x0009C8 },                                       /* 09C7-09C8 */
+  { 0x0009CB, 0x0009CC },                                       /* 09CB-09CC */
+  { 0x0009D7           },                                            /* 09D7 */
+  { 0x0009DC, 0x0009DD },                                       /* 09DC-09DD */
+  { 0x0009DF, 0x0009E1 },                                       /* 09DF-09E1 */
+  { 0x0009E6, 0x0009F1 },                                       /* 09E6-09F1 */
+  { 0x0009F4, 0x0009FA },                                       /* 09F4-09FA */
+  { 0x000A05, 0x000A0A },                                       /* 0A05-0A0A */
+  { 0x000A0F, 0x000A10 },                                       /* 0A0F-0A10 */
+  { 0x000A13, 0x000A28 },                                       /* 0A13-0A28 */
+  { 0x000A2A, 0x000A30 },                                       /* 0A2A-0A30 */
+  { 0x000A32, 0x000A33 },                                       /* 0A32-0A33 */
+  { 0x000A35, 0x000A36 },                                       /* 0A35-0A36 */
+  { 0x000A38, 0x000A39 },                                       /* 0A38-0A39 */
+  { 0x000A3E, 0x000A40 },                                       /* 0A3E-0A40 */
+  { 0x000A59, 0x000A5C },                                       /* 0A59-0A5C */
+  { 0x000A5E           },                                            /* 0A5E */
+  { 0x000A66, 0x000A6F },                                       /* 0A66-0A6F */
+  { 0x000A72, 0x000A74 },                                       /* 0A72-0A74 */
+  { 0x000A83           },                                            /* 0A83 */
+  { 0x000A85, 0x000A8B },                                       /* 0A85-0A8B */
+  { 0x000A8D           },                                            /* 0A8D */
+  { 0x000A8F, 0x000A91 },                                       /* 0A8F-0A91 */
+  { 0x000A93, 0x000AA8 },                                       /* 0A93-0AA8 */
+  { 0x000AAA, 0x000AB0 },                                       /* 0AAA-0AB0 */
+  { 0x000AB2, 0x000AB3 },                                       /* 0AB2-0AB3 */
+  { 0x000AB5, 0x000AB9 },                                       /* 0AB5-0AB9 */
+  { 0x000ABD, 0x000AC0 },                                       /* 0ABD-0AC0 */
+  { 0x000AC9           },                                            /* 0AC9 */
+  { 0x000ACB, 0x000ACC },                                       /* 0ACB-0ACC */
+  { 0x000AD0           },                                            /* 0AD0 */
+  { 0x000AE0           },                                            /* 0AE0 */
+  { 0x000AE6, 0x000AEF },                                       /* 0AE6-0AEF */
+  { 0x000B02, 0x000B03 },                                       /* 0B02-0B03 */
+  { 0x000B05, 0x000B0C },                                       /* 0B05-0B0C */
+  { 0x000B0F, 0x000B10 },                                       /* 0B0F-0B10 */
+  { 0x000B13, 0x000B28 },                                       /* 0B13-0B28 */
+  { 0x000B2A, 0x000B30 },                                       /* 0B2A-0B30 */
+  { 0x000B32, 0x000B33 },                                       /* 0B32-0B33 */
+  { 0x000B36, 0x000B39 },                                       /* 0B36-0B39 */
+  { 0x000B3D, 0x000B3E },                                       /* 0B3D-0B3E */
+  { 0x000B40           },                                            /* 0B40 */
+  { 0x000B47, 0x000B48 },                                       /* 0B47-0B48 */
+  { 0x000B4B, 0x000B4C },                                       /* 0B4B-0B4C */
+  { 0x000B57           },                                            /* 0B57 */
+  { 0x000B5C, 0x000B5D },                                       /* 0B5C-0B5D */
+  { 0x000B5F, 0x000B61 },                                       /* 0B5F-0B61 */
+  { 0x000B66, 0x000B70 },                                       /* 0B66-0B70 */
+  { 0x000B83           },                                            /* 0B83 */
+  { 0x000B85, 0x000B8A },                                       /* 0B85-0B8A */
+  { 0x000B8E, 0x000B90 },                                       /* 0B8E-0B90 */
+  { 0x000B92, 0x000B95 },                                       /* 0B92-0B95 */
+  { 0x000B99, 0x000B9A },                                       /* 0B99-0B9A */
+  { 0x000B9C           },                                            /* 0B9C */
+  { 0x000B9E, 0x000B9F },                                       /* 0B9E-0B9F */
+  { 0x000BA3, 0x000BA4 },                                       /* 0BA3-0BA4 */
+  { 0x000BA8, 0x000BAA },                                       /* 0BA8-0BAA */
+  { 0x000BAE, 0x000BB5 },                                       /* 0BAE-0BB5 */
+  { 0x000BB7, 0x000BB9 },                                       /* 0BB7-0BB9 */
+  { 0x000BBE, 0x000BBF },                                       /* 0BBE-0BBF */
+  { 0x000BC1, 0x000BC2 },                                       /* 0BC1-0BC2 */
+  { 0x000BC6, 0x000BC8 },                                       /* 0BC6-0BC8 */
+  { 0x000BCA, 0x000BCC },                                       /* 0BCA-0BCC */
+  { 0x000BD7           },                                            /* 0BD7 */
+  { 0x000BE7, 0x000BF2 },                                       /* 0BE7-0BF2 */
+  { 0x000C01, 0x000C03 },                                       /* 0C01-0C03 */
+  { 0x000C05, 0x000C0C },                                       /* 0C05-0C0C */
+  { 0x000C0E, 0x000C10 },                                       /* 0C0E-0C10 */
+  { 0x000C12, 0x000C28 },                                       /* 0C12-0C28 */
+  { 0x000C2A, 0x000C33 },                                       /* 0C2A-0C33 */
+  { 0x000C35, 0x000C39 },                                       /* 0C35-0C39 */
+  { 0x000C41, 0x000C44 },                                       /* 0C41-0C44 */
+  { 0x000C60, 0x000C61 },                                       /* 0C60-0C61 */
+  { 0x000C66, 0x000C6F },                                       /* 0C66-0C6F */
+  { 0x000C82, 0x000C83 },                                       /* 0C82-0C83 */
+  { 0x000C85, 0x000C8C },                                       /* 0C85-0C8C */
+  { 0x000C8E, 0x000C90 },                                       /* 0C8E-0C90 */
+  { 0x000C92, 0x000CA8 },                                       /* 0C92-0CA8 */
+  { 0x000CAA, 0x000CB3 },                                       /* 0CAA-0CB3 */
+  { 0x000CB5, 0x000CB9 },                                       /* 0CB5-0CB9 */
+  { 0x000CBE           },                                            /* 0CBE */
+  { 0x000CC0, 0x000CC4 },                                       /* 0CC0-0CC4 */
+  { 0x000CC7, 0x000CC8 },                                       /* 0CC7-0CC8 */
+  { 0x000CCA, 0x000CCB },                                       /* 0CCA-0CCB */
+  { 0x000CD5, 0x000CD6 },                                       /* 0CD5-0CD6 */
+  { 0x000CDE           },                                            /* 0CDE */
+  { 0x000CE0, 0x000CE1 },                                       /* 0CE0-0CE1 */
+  { 0x000CE6, 0x000CEF },                                       /* 0CE6-0CEF */
+  { 0x000D02, 0x000D03 },                                       /* 0D02-0D03 */
+  { 0x000D05, 0x000D0C },                                       /* 0D05-0D0C */
+  { 0x000D0E, 0x000D10 },                                       /* 0D0E-0D10 */
+  { 0x000D12, 0x000D28 },                                       /* 0D12-0D28 */
+  { 0x000D2A, 0x000D39 },                                       /* 0D2A-0D39 */
+  { 0x000D3E, 0x000D40 },                                       /* 0D3E-0D40 */
+  { 0x000D46, 0x000D48 },                                       /* 0D46-0D48 */
+  { 0x000D4A, 0x000D4C },                                       /* 0D4A-0D4C */
+  { 0x000D57           },                                            /* 0D57 */
+  { 0x000D60, 0x000D61 },                                       /* 0D60-0D61 */
+  { 0x000D66, 0x000D6F },                                       /* 0D66-0D6F */
+  { 0x000D82, 0x000D83 },                                       /* 0D82-0D83 */
+  { 0x000D85, 0x000D96 },                                       /* 0D85-0D96 */
+  { 0x000D9A, 0x000DB1 },                                       /* 0D9A-0DB1 */
+  { 0x000DB3, 0x000DBB },                                       /* 0DB3-0DBB */
+  { 0x000DBD           },                                            /* 0DBD */
+  { 0x000DC0, 0x000DC6 },                                       /* 0DC0-0DC6 */
+  { 0x000DCF, 0x000DD1 },                                       /* 0DCF-0DD1 */
+  { 0x000DD8, 0x000DDF },                                       /* 0DD8-0DDF */
+  { 0x000DF2, 0x000DF4 },                                       /* 0DF2-0DF4 */
+  { 0x000E01, 0x000E30 },                                       /* 0E01-0E30 */
+  { 0x000E32, 0x000E33 },                                       /* 0E32-0E33 */
+  { 0x000E40, 0x000E46 },                                       /* 0E40-0E46 */
+  { 0x000E4F, 0x000E5B },                                       /* 0E4F-0E5B */
+  { 0x000E81, 0x000E82 },                                       /* 0E81-0E82 */
+  { 0x000E84           },                                            /* 0E84 */
+  { 0x000E87, 0x000E88 },                                       /* 0E87-0E88 */
+  { 0x000E8A           },                                            /* 0E8A */
+  { 0x000E8D           },                                            /* 0E8D */
+  { 0x000E94, 0x000E97 },                                       /* 0E94-0E97 */
+  { 0x000E99, 0x000E9F },                                       /* 0E99-0E9F */
+  { 0x000EA1, 0x000EA3 },                                       /* 0EA1-0EA3 */
+  { 0x000EA5           },                                            /* 0EA5 */
+  { 0x000EA7           },                                            /* 0EA7 */
+  { 0x000EAA, 0x000EAB },                                       /* 0EAA-0EAB */
+  { 0x000EAD, 0x000EB0 },                                       /* 0EAD-0EB0 */
+  { 0x000EB2, 0x000EB3 },                                       /* 0EB2-0EB3 */
+  { 0x000EBD           },                                            /* 0EBD */
+  { 0x000EC0, 0x000EC4 },                                       /* 0EC0-0EC4 */
+  { 0x000EC6           },                                            /* 0EC6 */
+  { 0x000ED0, 0x000ED9 },                                       /* 0ED0-0ED9 */
+  { 0x000EDC, 0x000EDD },                                       /* 0EDC-0EDD */
+  { 0x000F00, 0x000F17 },                                       /* 0F00-0F17 */
+  { 0x000F1A, 0x000F34 },                                       /* 0F1A-0F34 */
+  { 0x000F36           },                                            /* 0F36 */
+  { 0x000F38           },                                            /* 0F38 */
+  { 0x000F3E, 0x000F47 },                                       /* 0F3E-0F47 */
+  { 0x000F49, 0x000F6A },                                       /* 0F49-0F6A */
+  { 0x000F7F           },                                            /* 0F7F */
+  { 0x000F85           },                                            /* 0F85 */
+  { 0x000F88, 0x000F8B },                                       /* 0F88-0F8B */
+  { 0x000FBE, 0x000FC5 },                                       /* 0FBE-0FC5 */
+  { 0x000FC7, 0x000FCC },                                       /* 0FC7-0FCC */
+  { 0x000FCF           },                                            /* 0FCF */
+  { 0x001000, 0x001021 },                                       /* 1000-1021 */
+  { 0x001023, 0x001027 },                                       /* 1023-1027 */
+  { 0x001029, 0x00102A },                                       /* 1029-102A */
+  { 0x00102C           },                                            /* 102C */
+  { 0x001031           },                                            /* 1031 */
+  { 0x001038           },                                            /* 1038 */
+  { 0x001040, 0x001057 },                                       /* 1040-1057 */
+  { 0x0010A0, 0x0010C5 },                                       /* 10A0-10C5 */
+  { 0x0010D0, 0x0010F8 },                                       /* 10D0-10F8 */
+  { 0x0010FB           },                                            /* 10FB */
+  { 0x001100, 0x001159 },                                       /* 1100-1159 */
+  { 0x00115F, 0x0011A2 },                                       /* 115F-11A2 */
+  { 0x0011A8, 0x0011F9 },                                       /* 11A8-11F9 */
+  { 0x001200, 0x001206 },                                       /* 1200-1206 */
+  { 0x001208, 0x001246 },                                       /* 1208-1246 */
+  { 0x001248           },                                            /* 1248 */
+  { 0x00124A, 0x00124D },                                       /* 124A-124D */
+  { 0x001250, 0x001256 },                                       /* 1250-1256 */
+  { 0x001258           },                                            /* 1258 */
+  { 0x00125A, 0x00125D },                                       /* 125A-125D */
+  { 0x001260, 0x001286 },                                       /* 1260-1286 */
+  { 0x001288           },                                            /* 1288 */
+  { 0x00128A, 0x00128D },                                       /* 128A-128D */
+  { 0x001290, 0x0012AE },                                       /* 1290-12AE */
+  { 0x0012B0           },                                            /* 12B0 */
+  { 0x0012B2, 0x0012B5 },                                       /* 12B2-12B5 */
+  { 0x0012B8, 0x0012BE },                                       /* 12B8-12BE */
+  { 0x0012C0           },                                            /* 12C0 */
+  { 0x0012C2, 0x0012C5 },                                       /* 12C2-12C5 */
+  { 0x0012C8, 0x0012CE },                                       /* 12C8-12CE */
+  { 0x0012D0, 0x0012D6 },                                       /* 12D0-12D6 */
+  { 0x0012D8, 0x0012EE },                                       /* 12D8-12EE */
+  { 0x0012F0, 0x00130E },                                       /* 12F0-130E */
+  { 0x001310           },                                            /* 1310 */
+  { 0x001312, 0x001315 },                                       /* 1312-1315 */
+  { 0x001318, 0x00131E },                                       /* 1318-131E */
+  { 0x001320, 0x001346 },                                       /* 1320-1346 */
+  { 0x001348, 0x00135A },                                       /* 1348-135A */
+  { 0x001361, 0x00137C },                                       /* 1361-137C */
+  { 0x0013A0, 0x0013F4 },                                       /* 13A0-13F4 */
+  { 0x001401, 0x001676 },                                       /* 1401-1676 */
+  { 0x001681, 0x00169A },                                       /* 1681-169A */
+  { 0x0016A0, 0x0016F0 },                                       /* 16A0-16F0 */
+  { 0x001700, 0x00170C },                                       /* 1700-170C */
+  { 0x00170E, 0x001711 },                                       /* 170E-1711 */
+  { 0x001720, 0x001731 },                                       /* 1720-1731 */
+  { 0x001735, 0x001736 },                                       /* 1735-1736 */
+  { 0x001740, 0x001751 },                                       /* 1740-1751 */
+  { 0x001760, 0x00176C },                                       /* 1760-176C */
+  { 0x00176E, 0x001770 },                                       /* 176E-1770 */
+  { 0x001780, 0x0017B6 },                                       /* 1780-17B6 */
+  { 0x0017BE, 0x0017C5 },                                       /* 17BE-17C5 */
+  { 0x0017C7, 0x0017C8 },                                       /* 17C7-17C8 */
+  { 0x0017D4, 0x0017DA },                                       /* 17D4-17DA */
+  { 0x0017DC           },                                            /* 17DC */
+  { 0x0017E0, 0x0017E9 },                                       /* 17E0-17E9 */
+  { 0x001810, 0x001819 },                                       /* 1810-1819 */
+  { 0x001820, 0x001877 },                                       /* 1820-1877 */
+  { 0x001880, 0x0018A8 },                                       /* 1880-18A8 */
+  { 0x001E00, 0x001E9B },                                       /* 1E00-1E9B */
+  { 0x001EA0, 0x001EF9 },                                       /* 1EA0-1EF9 */
+  { 0x001F00, 0x001F15 },                                       /* 1F00-1F15 */
+  { 0x001F18, 0x001F1D },                                       /* 1F18-1F1D */
+  { 0x001F20, 0x001F45 },                                       /* 1F20-1F45 */
+  { 0x001F48, 0x001F4D },                                       /* 1F48-1F4D */
+  { 0x001F50, 0x001F57 },                                       /* 1F50-1F57 */
+  { 0x001F59           },                                            /* 1F59 */
+  { 0x001F5B           },                                            /* 1F5B */
+  { 0x001F5D           },                                            /* 1F5D */
+  { 0x001F5F, 0x001F7D },                                       /* 1F5F-1F7D */
+  { 0x001F80, 0x001FB4 },                                       /* 1F80-1FB4 */
+  { 0x001FB6, 0x001FBC },                                       /* 1FB6-1FBC */
+  { 0x001FBE           },                                            /* 1FBE */
+  { 0x001FC2, 0x001FC4 },                                       /* 1FC2-1FC4 */
+  { 0x001FC6, 0x001FCC },                                       /* 1FC6-1FCC */
+  { 0x001FD0, 0x001FD3 },                                       /* 1FD0-1FD3 */
+  { 0x001FD6, 0x001FDB },                                       /* 1FD6-1FDB */
+  { 0x001FE0, 0x001FEC },                                       /* 1FE0-1FEC */
+  { 0x001FF2, 0x001FF4 },                                       /* 1FF2-1FF4 */
+  { 0x001FF6, 0x001FFC },                                       /* 1FF6-1FFC */
+  { 0x00200E           },                                            /* 200E */
+  { 0x002071           },                                            /* 2071 */
+  { 0x00207F           },                                            /* 207F */
+  { 0x002102           },                                            /* 2102 */
+  { 0x002107           },                                            /* 2107 */
+  { 0x00210A, 0x002113 },                                       /* 210A-2113 */
+  { 0x002115           },                                            /* 2115 */
+  { 0x002119, 0x00211D },                                       /* 2119-211D */
+  { 0x002124           },                                            /* 2124 */
+  { 0x002126           },                                            /* 2126 */
+  { 0x002128           },                                            /* 2128 */
+  { 0x00212A, 0x00212D },                                       /* 212A-212D */
+  { 0x00212F, 0x002131 },                                       /* 212F-2131 */
+  { 0x002133, 0x002139 },                                       /* 2133-2139 */
+  { 0x00213D, 0x00213F },                                       /* 213D-213F */
+  { 0x002145, 0x002149 },                                       /* 2145-2149 */
+  { 0x002160, 0x002183 },                                       /* 2160-2183 */
+  { 0x002336, 0x00237A },                                       /* 2336-237A */
+  { 0x002395           },                                            /* 2395 */
+  { 0x00249C, 0x0024E9 },                                       /* 249C-24E9 */
+  { 0x003005, 0x003007 },                                       /* 3005-3007 */
+  { 0x003021, 0x003029 },                                       /* 3021-3029 */
+  { 0x003031, 0x003035 },                                       /* 3031-3035 */
+  { 0x003038, 0x00303C },                                       /* 3038-303C */
+  { 0x003041, 0x003096 },                                       /* 3041-3096 */
+  { 0x00309D, 0x00309F },                                       /* 309D-309F */
+  { 0x0030A1, 0x0030FA },                                       /* 30A1-30FA */
+  { 0x0030FC, 0x0030FF },                                       /* 30FC-30FF */
+  { 0x003105, 0x00312C },                                       /* 3105-312C */
+  { 0x003131, 0x00318E },                                       /* 3131-318E */
+  { 0x003190, 0x0031B7 },                                       /* 3190-31B7 */
+  { 0x0031F0, 0x00321C },                                       /* 31F0-321C */
+  { 0x003220, 0x003243 },                                       /* 3220-3243 */
+  { 0x003260, 0x00327B },                                       /* 3260-327B */
+  { 0x00327F, 0x0032B0 },                                       /* 327F-32B0 */
+  { 0x0032C0, 0x0032CB },                                       /* 32C0-32CB */
+  { 0x0032D0, 0x0032FE },                                       /* 32D0-32FE */
+  { 0x003300, 0x003376 },                                       /* 3300-3376 */
+  { 0x00337B, 0x0033DD },                                       /* 337B-33DD */
+  { 0x0033E0, 0x0033FE },                                       /* 33E0-33FE */
+  { 0x003400, 0x004DB5 },                                       /* 3400-4DB5 */
+  { 0x004E00, 0x009FA5 },                                       /* 4E00-9FA5 */
+  { 0x00A000, 0x00A48C },                                       /* A000-A48C */
+  { 0x00AC00, 0x00D7A3 },                                       /* AC00-D7A3 */
+  { 0x00D800, 0x00FA2D },                                       /* D800-FA2D */
+  { 0x00FA30, 0x00FA6A },                                       /* FA30-FA6A */
+  { 0x00FB00, 0x00FB06 },                                       /* FB00-FB06 */
+  { 0x00FB13, 0x00FB17 },                                       /* FB13-FB17 */
+  { 0x00FF21, 0x00FF3A },                                       /* FF21-FF3A */
+  { 0x00FF41, 0x00FF5A },                                       /* FF41-FF5A */
+  { 0x00FF66, 0x00FFBE },                                       /* FF66-FFBE */
+  { 0x00FFC2, 0x00FFC7 },                                       /* FFC2-FFC7 */
+  { 0x00FFCA, 0x00FFCF },                                       /* FFCA-FFCF */
+  { 0x00FFD2, 0x00FFD7 },                                       /* FFD2-FFD7 */
+  { 0x00FFDA, 0x00FFDC },                                       /* FFDA-FFDC */
+  { 0x010300, 0x01031E },                                     /* 10300-1031E */
+  { 0x010320, 0x010323 },                                     /* 10320-10323 */
+  { 0x010330, 0x01034A },                                     /* 10330-1034A */
+  { 0x010400, 0x010425 },                                     /* 10400-10425 */
+  { 0x010428, 0x01044D },                                     /* 10428-1044D */
+  { 0x01D000, 0x01D0F5 },                                     /* 1D000-1D0F5 */
+  { 0x01D100, 0x01D126 },                                     /* 1D100-1D126 */
+  { 0x01D12A, 0x01D166 },                                     /* 1D12A-1D166 */
+  { 0x01D16A, 0x01D172 },                                     /* 1D16A-1D172 */
+  { 0x01D183, 0x01D184 },                                     /* 1D183-1D184 */
+  { 0x01D18C, 0x01D1A9 },                                     /* 1D18C-1D1A9 */
+  { 0x01D1AE, 0x01D1DD },                                     /* 1D1AE-1D1DD */
+  { 0x01D400, 0x01D454 },                                     /* 1D400-1D454 */
+  { 0x01D456, 0x01D49C },                                     /* 1D456-1D49C */
+  { 0x01D49E, 0x01D49F },                                     /* 1D49E-1D49F */
+  { 0x01D4A2           },                                           /* 1D4A2 */
+  { 0x01D4A5, 0x01D4A6 },                                     /* 1D4A5-1D4A6 */
+  { 0x01D4A9, 0x01D4AC },                                     /* 1D4A9-1D4AC */
+  { 0x01D4AE, 0x01D4B9 },                                     /* 1D4AE-1D4B9 */
+  { 0x01D4BB           },                                           /* 1D4BB */
+  { 0x01D4BD, 0x01D4C0 },                                     /* 1D4BD-1D4C0 */
+  { 0x01D4C2, 0x01D4C3 },                                     /* 1D4C2-1D4C3 */
+  { 0x01D4C5, 0x01D505 },                                     /* 1D4C5-1D505 */
+  { 0x01D507, 0x01D50A },                                     /* 1D507-1D50A */
+  { 0x01D50D, 0x01D514 },                                     /* 1D50D-1D514 */
+  { 0x01D516, 0x01D51C },                                     /* 1D516-1D51C */
+  { 0x01D51E, 0x01D539 },                                     /* 1D51E-1D539 */
+  { 0x01D53B, 0x01D53E },                                     /* 1D53B-1D53E */
+  { 0x01D540, 0x01D544 },                                     /* 1D540-1D544 */
+  { 0x01D546           },                                           /* 1D546 */
+  { 0x01D54A, 0x01D550 },                                     /* 1D54A-1D550 */
+  { 0x01D552, 0x01D6A3 },                                     /* 1D552-1D6A3 */
+  { 0x01D6A8, 0x01D7C9 },                                     /* 1D6A8-1D7C9 */
+  { 0x020000, 0x02A6D6 },                                     /* 20000-2A6D6 */
+  { 0x02F800, 0x02FA1D },                                     /* 2F800-2FA1D */
+  { 0x0F0000, 0x0FFFFD },                                     /* F0000-FFFFD */
+  { 0x100000, 0x10FFFD },                                   /* 100000-10FFFD */
+  { 0 },
+};
+
Index: psi/tags/0.10-os2-b1/iris/libidn/stringprep.c
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/stringprep.c	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/stringprep.c	(revision 38)
@@ -0,0 +1,475 @@
+/* stringprep.c		Core stringprep implementation.
+ * Copyright (C) 2002, 2003  Simon Josefsson
+ *
+ * This file is part of GNU Libidn.
+ *
+ * GNU Libidn is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GNU Libidn 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU Libidn; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "internal.h"
+
+static ssize_t
+stringprep_find_character_in_table (my_uint32_t ucs4,
+				    Stringprep_table_element * table)
+{
+  ssize_t i;
+
+  for (i = 0; table[i].start; i++)
+    if (ucs4 >= table[i].start &&
+	ucs4 <= (table[i].end ? table[i].end : table[i].start))
+      return i;
+
+  return -1;
+}
+
+static ssize_t
+stringprep_find_string_in_table (my_uint32_t * ucs4,
+				 size_t ucs4len,
+				 size_t * tablepos,
+				 Stringprep_table_element * table)
+{
+  size_t j;
+  ssize_t pos;
+
+  for (j = 0; j < ucs4len; j++)
+    if ((pos = stringprep_find_character_in_table (ucs4[j], table)) != -1)
+      {
+	if (tablepos)
+	  *tablepos = pos;
+	return j;
+      }
+
+  return -1;
+}
+
+static int
+stringprep_apply_table_to_string (my_uint32_t * ucs4,
+				  size_t * ucs4len,
+				  size_t maxucs4len,
+				  Stringprep_table_element * table,
+				  const char *tablename)
+{
+  ssize_t pos;
+  size_t i, maplen;
+
+  while ((pos = stringprep_find_string_in_table (ucs4, *ucs4len,
+						 &i, table)) != -1)
+    {
+      for (maplen = STRINGPREP_MAX_MAP_CHARS;
+	   maplen > 0 && table[i].map[maplen - 1] == 0; maplen--)
+	;
+
+      if (*ucs4len - 1 + maplen >= maxucs4len)
+	return STRINGPREP_TOO_SMALL_BUFFER;
+
+      memmove (&ucs4[pos + maplen], &ucs4[pos + 1],
+	       *ucs4len * sizeof (my_uint32_t) - (&ucs4[pos + 1] - ucs4));
+      memcpy (&ucs4[pos], table[i].map, sizeof (my_uint32_t) * maplen);
+      *ucs4len = *ucs4len - 1 + maplen;
+    }
+
+  return STRINGPREP_OK;
+}
+
+#define INVERTED(x) ((x) & ((~0UL) >> 1))
+#define UNAPPLICAPLEFLAGS(flags, profileflags) \
+  ((!INVERTED(profileflags) && !(profileflags & flags) && profileflags) || \
+   ( INVERTED(profileflags) && (profileflags & flags)))
+
+/**
+ * stringprep:
+ * @in: input/ouput array with string to prepare.
+ * @maxlen: maximum length of input/output array.
+ * @flags: optional stringprep profile flags.
+ * @profile: pointer to stringprep profile to use.
+ *
+ * Prepare the input UTF-8 string according to the stringprep profile.
+ * Normally application programmers use stringprep profile macros such
+ * as stringprep_nameprep(), stringprep_kerberos5() etc instead of
+ * calling this function directly.
+ *
+ * Since the stringprep operation can expand the string, @maxlen
+ * indicate how large the buffer holding the string is.  The @flags
+ * are one of Stringprep_profile_flags, or 0.  The profile indicates
+ * processing details specific to that profile.  Your application can
+ * define new profiles, possibly re-using the generic stringprep
+ * tables that always will be part of the library.
+ *
+ * Note that you must convert strings entered in the systems locale
+ * into UTF-8 before using this function.
+ *
+ * Return value: Returns 0 iff successful, or an error code.
+ **/
+int
+stringprep (char *in,
+	    size_t maxlen,
+	    Stringprep_profile_flags flags, Stringprep_profile * profile)
+{
+  size_t i, j;
+  ssize_t k;
+  int rc;
+  char *p = 0;
+  my_uint32_t *q = 0;
+  my_uint32_t *ucs4;
+  size_t ucs4len, maxucs4len;
+
+  ucs4 = stringprep_utf8_to_ucs4 (in, -1, &ucs4len);
+  maxucs4len = 4 * ucs4len + 10;	/* XXX */
+  ucs4 = realloc (ucs4, 1 + maxucs4len * sizeof (my_uint32_t));
+  if (!ucs4)
+    {
+      rc = STRINGPREP_MALLOC_ERROR;
+      goto done;
+    }
+
+  for (i = 0; profile[i].operation; i++)
+    {
+      switch (profile[i].operation)
+	{
+	case STRINGPREP_NFKC:
+	  if (UNAPPLICAPLEFLAGS (flags, profile[i].flags))
+	    {
+	      break;
+	    }
+
+	  if (flags & STRINGPREP_NO_NFKC && !profile[i].flags)
+	    {
+	      /* Profile requires NFKC, but callee asked for no NFKC. */
+	      rc = STRINGPREP_FLAG_ERROR;
+	      goto done;
+	    }
+
+	  q = stringprep_ucs4_nfkc_normalize (ucs4, ucs4len);
+
+	  if (!q)
+	    {
+	      rc = STRINGPREP_NFKC_FAILED;
+	      goto done;
+	    }
+
+	  for (j = 0; q[j]; j++)
+	    ;
+
+	  free (ucs4);
+	  ucs4 = q;
+	  ucs4len = j;
+	  q = 0;
+	  break;
+
+	case STRINGPREP_PROHIBIT_TABLE:
+	  k = stringprep_find_string_in_table (ucs4, ucs4len,
+					       NULL, profile[i].table);
+	  if (k != -1)
+	    {
+	      rc = STRINGPREP_CONTAINS_PROHIBITED;
+	      goto done;
+	    }
+	  break;
+
+	case STRINGPREP_UNASSIGNED_TABLE:
+	  if (UNAPPLICAPLEFLAGS (flags, profile[i].flags))
+	    break;
+	  if (flags & STRINGPREP_NO_UNASSIGNED)
+	    {
+	      k = stringprep_find_string_in_table
+		(ucs4, ucs4len, NULL, profile[i].table);
+	      if (k != -1)
+		{
+		  rc = STRINGPREP_CONTAINS_UNASSIGNED;
+		  goto done;
+		}
+	    }
+	  break;
+
+	case STRINGPREP_MAP_TABLE:
+	  if (UNAPPLICAPLEFLAGS (flags, profile[i].flags))
+	    break;
+	  rc = stringprep_apply_table_to_string
+	    (ucs4, &ucs4len, maxucs4len, profile[i].table, profile[i].name);
+	  if (rc != STRINGPREP_OK)
+	    goto done;
+	  break;
+
+	case STRINGPREP_BIDI_PROHIBIT_TABLE:
+	case STRINGPREP_BIDI_RAL_TABLE:
+	case STRINGPREP_BIDI_L_TABLE:
+	  break;
+
+	case STRINGPREP_BIDI:
+	  {
+	    int done_prohibited = 0;
+	    int done_ral = 0;
+	    int done_l = 0;
+	    int contains_ral = -1;
+	    int contains_l = -1;
+
+	    for (j = 0; profile[j].operation; j++)
+	      if (profile[j].operation == STRINGPREP_BIDI_PROHIBIT_TABLE)
+		{
+		  done_prohibited = 1;
+		  k = stringprep_find_string_in_table (ucs4, ucs4len,
+						       NULL,
+						       profile[j].table);
+		  if (k != -1)
+		    {
+		      rc = STRINGPREP_BIDI_CONTAINS_PROHIBITED;
+		      goto done;
+		    }
+		}
+	      else if (profile[j].operation == STRINGPREP_BIDI_RAL_TABLE)
+		{
+		  done_ral = 1;
+		  if (stringprep_find_string_in_table
+		      (ucs4, ucs4len, NULL, profile[j].table) != -1)
+		    contains_ral = j;
+		}
+	      else if (profile[j].operation == STRINGPREP_BIDI_L_TABLE)
+		{
+		  done_l = 1;
+		  if (stringprep_find_string_in_table
+		      (ucs4, ucs4len, NULL, profile[j].table) != -1)
+		    contains_l = j;
+		}
+
+	    if (!done_prohibited || !done_ral || !done_l)
+	      {
+		rc = STRINGPREP_PROFILE_ERROR;
+		goto done;
+	      }
+
+	    if (contains_ral != -1 && contains_l != -1)
+	      {
+		rc = STRINGPREP_BIDI_BOTH_L_AND_RAL;
+		goto done;
+	      }
+
+	    if (contains_ral != -1)
+	      {
+		if (!(stringprep_find_character_in_table
+		      (ucs4[0], profile[contains_ral].table) != -1 &&
+		      stringprep_find_character_in_table
+		      (ucs4[ucs4len - 1], profile[contains_ral].table) != -1))
+		  {
+		    rc = STRINGPREP_BIDI_LEADTRAIL_NOT_RAL;
+		    goto done;
+		  }
+	      }
+	  }
+	  break;
+
+	default:
+	  rc = STRINGPREP_PROFILE_ERROR;
+	  goto done;
+	  break;
+	}
+    }
+
+  p = stringprep_ucs4_to_utf8 (ucs4, ucs4len, 0, 0);
+
+  if (strlen (p) >= maxlen)
+    {
+      rc = STRINGPREP_TOO_SMALL_BUFFER;
+      goto done;
+    }
+
+  strcpy (in, p);		/* flawfinder: ignore */
+
+  rc = STRINGPREP_OK;
+
+done:
+  if (p)
+    free (p);
+  if (q)
+    free (q);
+  if (ucs4)
+    free (ucs4);
+  return rc;
+}
+
+/**
+ * stringprep_profile:
+ * @in: input/ouput array with string to prepare.
+ * @out: output variable with newly allocate string.
+ * @flags: optional stringprep profile flags.
+ * @profile: name of stringprep profile to use.
+ *
+ * Prepare the input UTF-8 string according to the stringprep profile.
+ * Normally application programmers use stringprep profile macros such
+ * as stringprep_nameprep(), stringprep_kerberos5() etc instead of
+ * calling this function directly.
+ *
+ * Note that you must convert strings entered in the systems locale
+ * into UTF-8 before using this function.
+ *
+ * The output @out variable must be deallocated by the caller.
+ *
+ * Return value: Returns 0 iff successful, or an error code.
+ **/
+int
+stringprep_profile (char *in,
+		    char **out, char *profile, Stringprep_profile_flags flags)
+{
+  Stringprep_profiles *p;
+  char *str;
+  size_t len;
+  int rc;
+
+  for (p = &stringprep_profiles[0]; p->name; p++)
+    if (strcmp (p->name, profile) == 0)
+      break;
+
+  if (!p || !p->name || !p->tables)
+    return STRINGPREP_UNKNOWN_PROFILE;
+
+  len = strlen (in) + BUFSIZ;
+  str = (char *) malloc (len);
+  if (str == NULL)
+    return STRINGPREP_MALLOC_ERROR;
+
+  strcpy (str, in);
+
+  rc = stringprep (str, len, flags, p->tables);
+
+  if (rc == STRINGPREP_OK)
+    *out = str;
+  else
+    free (str);
+
+  return rc;
+}
+
+/**
+ * STRINGPREP_VERSION
+ *
+ * String defined via CPP denoting the header file version number.
+ * Used together with stringprep_check_version() to verify header file
+ * and run-time library consistency.
+ */
+
+/**
+ * STRINGPREP_MAX_MAP_CHARS
+ *
+ * Maximum number of code points that can replace a single code point,
+ * during stringprep mapping.
+ */
+
+/**
+ * Stringprep_rc
+ *
+ * Enumerated return codes of stringprep(), stringprep_profile()
+ * functions (and macros using those functions).  The value 0 is
+ * guaranteed to always correspond to success.
+ */
+
+/**
+ * Stringprep_profile_flags:
+ * @STRINGPREP_NO_NFKC: Disable the NFKC normalization, as well as
+ *   selecting the non-NFKC case folding tables.  Usually the profile
+ *   specifies BIDI and NFKC settings, and applications should not
+ *   override it unless in special situations.
+ * @STRINGPREP_NO_BIDI: Disable the BIDI step.  Usually the profile
+ *   specifies BIDI and NFKC settings, and applications should not
+ *   override it unless in special situations.
+ * @STRINGPREP_NO_UNASSIGNED: Make the library return with an error if
+ *   string contains unassigned characters according to profile.
+ *
+ * Stringprep profile flags.
+ */
+
+/**
+ * Stringprep_profile_steps:
+ *
+ * Various steps in the stringprep algorithm.  You really want to
+ * study the source code to understand this one.  Only useful if you
+ * want to add another profile.
+ */
+
+/**
+ * stringprep_nameprep:
+ * @in: input/ouput array with string to prepare.
+ * @maxlen: maximum length of input/output array.
+ *
+ * Prepare the input UTF-8 string according to the nameprep profile.
+ * The AllowUnassigned flag is true, use
+ * stringprep_nameprep_no_unassigned() for false AllowUnassigned.
+ * Returns 0 iff successful, or an error code.
+ **/
+
+/**
+ * stringprep_nameprep_no_unassigned:
+ * @in: input/ouput array with string to prepare.
+ * @maxlen: maximum length of input/output array.
+ *
+ * Prepare the input UTF-8 string according to the nameprep profile.
+ * The AllowUnassigned flag is false, use stringprep_nameprep() for
+ * true AllowUnassigned.  Returns 0 iff successful, or an error code.
+ **/
+
+/**
+ * stringprep_iscsi:
+ * @in: input/ouput array with string to prepare.
+ * @maxlen: maximum length of input/output array.
+ *
+ * Prepare the input UTF-8 string according to the draft iSCSI
+ * stringprep profile.  Returns 0 iff successful, or an error code.
+ **/
+
+/**
+ * stringprep_kerberos5:
+ * @in: input/ouput array with string to prepare.
+ * @maxlen: maximum length of input/output array.
+ *
+ * Prepare the input UTF-8 string according to the draft Kerberos5
+ * stringprep profile.  Returns 0 iff successful, or an error code.
+ **/
+
+/**
+ * stringprep_plain:
+ * @in: input/ouput array with string to prepare.
+ * @maxlen: maximum length of input/output array.
+ *
+ * Prepare the input UTF-8 string according to the draft SASL
+ * ANONYMOUS profile.  Returns 0 iff successful, or an error code.
+ **/
+
+/**
+ * stringprep_xmpp_nodeprep:
+ * @in: input/ouput array with string to prepare.
+ * @maxlen: maximum length of input/output array.
+ *
+ * Prepare the input UTF-8 string according to the draft XMPP node
+ * identifier profile.  Returns 0 iff successful, or an error code.
+ **/
+
+/**
+ * stringprep_xmpp_resourceprep:
+ * @in: input/ouput array with string to prepare.
+ * @maxlen: maximum length of input/output array.
+ *
+ * Prepare the input UTF-8 string according to the draft XMPP resource
+ * identifier profile.  Returns 0 iff successful, or an error code.
+ **/
+
+/**
+ * stringprep_generic:
+ * @in: input/ouput array with string to prepare.
+ * @maxlen: maximum length of input/output array.
+ *
+ * Prepare the input UTF-8 string according to a hypotetical "generic"
+ * stringprep profile. This is mostly used for debugging or when
+ * constructing new stringprep profiles. Returns 0 iff successful, or
+ * an error code.
+ **/
Index: psi/tags/0.10-os2-b1/iris/libidn/stringprep.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/libidn/stringprep.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/libidn/stringprep.h	(revision 38)
@@ -0,0 +1,220 @@
+/* stringprep.h		Header file for stringprep functions.         -*- c -*-
+ * Copyright (C) 2002, 2003  Simon Josefsson
+ *
+ * This file is part of GNU Libidn.
+ *
+ * GNU Libidn is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GNU Libidn 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GNU Libidn; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _STRINGPREP_H
+#define _STRINGPREP_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>		/* size_t */
+
+#include<qglobal.h>
+#ifdef Q_OS_WIN32
+typedef int ssize_t;
+#else
+#include <unistd.h>		/* ssize_t */
+#endif
+
+#include <idn-int.h>		/* my_uint32_t */
+#include "qint.h"
+
+#define STRINGPREP_VERSION "0.3.1"
+
+/* Error codes. */
+  typedef enum
+  {
+    STRINGPREP_OK = 0,
+    /* Stringprep errors. */
+    STRINGPREP_CONTAINS_UNASSIGNED = 1,
+    STRINGPREP_CONTAINS_PROHIBITED = 2,
+    STRINGPREP_BIDI_BOTH_L_AND_RAL = 3,
+    STRINGPREP_BIDI_LEADTRAIL_NOT_RAL = 4,
+    STRINGPREP_BIDI_CONTAINS_PROHIBITED = 5,
+    /* Error in calling application. */
+    STRINGPREP_TOO_SMALL_BUFFER = 100,
+    STRINGPREP_PROFILE_ERROR = 101,
+    STRINGPREP_FLAG_ERROR = 102,
+    STRINGPREP_UNKNOWN_PROFILE = 103,
+    /* Internal errors. */
+    STRINGPREP_NFKC_FAILED = 200,
+    STRINGPREP_MALLOC_ERROR = 201
+  } Stringprep_rc;
+
+/* Flags used when calling stringprep(). */
+  typedef enum
+  {
+    STRINGPREP_NO_NFKC = 1,
+    STRINGPREP_NO_BIDI = 2,
+    STRINGPREP_NO_UNASSIGNED = 4
+  } Stringprep_profile_flags;
+
+/* Steps in a stringprep profile. */
+  typedef enum
+  {
+    STRINGPREP_NFKC = 1,
+    STRINGPREP_BIDI = 2,
+    STRINGPREP_MAP_TABLE = 3,
+    STRINGPREP_UNASSIGNED_TABLE = 4,
+    STRINGPREP_PROHIBIT_TABLE = 5,
+    STRINGPREP_BIDI_PROHIBIT_TABLE = 6,
+    STRINGPREP_BIDI_RAL_TABLE = 7,
+    STRINGPREP_BIDI_L_TABLE = 8
+  } Stringprep_profile_steps;
+
+#define STRINGPREP_MAX_MAP_CHARS 4
+
+  struct Stringprep_table_element
+  {
+    my_uint32_t start;
+    my_uint32_t end;		/* 0 if only one character */
+    my_uint32_t map[STRINGPREP_MAX_MAP_CHARS];	/* NULL if end is not 0 */
+  };
+  typedef struct Stringprep_table_element Stringprep_table_element;
+
+  struct Stringprep_table
+  {
+    Stringprep_profile_steps operation;
+    Stringprep_profile_flags flags;
+    Stringprep_table_element *table;
+    char *name;
+  };
+  typedef struct Stringprep_table Stringprep_profile;
+
+  struct Stringprep_profiles
+  {
+    char *name;
+    Stringprep_profile *tables;
+  };
+  typedef struct Stringprep_profiles Stringprep_profiles;
+
+  extern Stringprep_profiles stringprep_profiles[];
+
+/* Profiles */
+  extern Stringprep_table_element stringprep_rfc3454_A_1[];
+  extern Stringprep_table_element stringprep_rfc3454_B_1[];
+  extern Stringprep_table_element stringprep_rfc3454_B_2[];
+  extern Stringprep_table_element stringprep_rfc3454_B_3[];
+  extern Stringprep_table_element stringprep_rfc3454_C_1_1[];
+  extern Stringprep_table_element stringprep_rfc3454_C_1_2[];
+  extern Stringprep_table_element stringprep_rfc3454_C_2_1[];
+  extern Stringprep_table_element stringprep_rfc3454_C_2_2[];
+  extern Stringprep_table_element stringprep_rfc3454_C_3[];
+  extern Stringprep_table_element stringprep_rfc3454_C_4[];
+  extern Stringprep_table_element stringprep_rfc3454_C_5[];
+  extern Stringprep_table_element stringprep_rfc3454_C_6[];
+  extern Stringprep_table_element stringprep_rfc3454_C_7[];
+  extern Stringprep_table_element stringprep_rfc3454_C_8[];
+  extern Stringprep_table_element stringprep_rfc3454_C_9[];
+  extern Stringprep_table_element stringprep_rfc3454_D_1[];
+  extern Stringprep_table_element stringprep_rfc3454_D_2[];
+
+  /* Generic (for debugging) */
+
+  extern Stringprep_profile stringprep_generic[];
+
+#define stringprep_generic(in, maxlen)		\
+  stringprep(in, maxlen, 0, stringprep_generic)
+
+  /* Nameprep */
+
+  extern Stringprep_profile stringprep_nameprep[];
+
+#define stringprep_nameprep(in, maxlen)			\
+  stringprep(in, maxlen, 0, stringprep_nameprep)
+
+#define stringprep_nameprep_no_unassigned(in, maxlen)			\
+  stringprep(in, maxlen, STRINGPREP_NO_UNASSIGNED, stringprep_nameprep)
+
+  /* SASL */
+
+  extern Stringprep_profile stringprep_saslprep[];
+  extern Stringprep_profile stringprep_plain[];
+
+#define stringprep_plain(in, maxlen)		\
+  stringprep(in, maxlen, 0, stringprep_plain)
+
+  /* Kerberos */
+
+  extern Stringprep_profile stringprep_kerberos5[];
+
+#define stringprep_kerberos5(in, maxlen)		\
+  stringprep(in, maxlen, 0, stringprep_kerberos5)
+
+  /* XMPP */
+
+  extern Stringprep_profile stringprep_xmpp_nodeprep[];
+  extern Stringprep_profile stringprep_xmpp_resourceprep[];
+  extern Stringprep_table_element stringprep_xmpp_nodeprep_prohibit[];
+
+#define stringprep_xmpp_nodeprep(in, maxlen)		\
+  stringprep(in, maxlen, 0, stringprep_xmpp_nodeprep)
+#define stringprep_xmpp_resourceprep(in, maxlen)		\
+  stringprep(in, maxlen, 0, stringprep_xmpp_resourceprep)
+
+  /* iSCSI */
+
+  extern Stringprep_profile stringprep_iscsi[];
+
+#define stringprep_iscsi(in, maxlen)		\
+  stringprep(in, maxlen, 0, stringprep_iscsi)
+
+  /* API */
+
+  extern int stringprep (char *in, size_t maxlen,
+			 Stringprep_profile_flags flags,
+			 Stringprep_profile * profile);
+
+  extern int stringprep_profile (char *in,
+				 char **out,
+				 char *profile,
+				 Stringprep_profile_flags flags);
+
+  extern const char *stringprep_check_version (const char *req_version);
+
+/* Utility */
+
+  extern int stringprep_unichar_to_utf8 (my_uint32_t c, char *outbuf);
+  extern my_uint32_t stringprep_utf8_to_unichar (const char *p);
+
+  extern my_uint32_t *stringprep_utf8_to_ucs4 (const char *str, ssize_t len,
+					    size_t * items_written);
+  extern char *stringprep_ucs4_to_utf8 (const my_uint32_t * str, ssize_t len,
+					size_t * items_read,
+					size_t * items_written);
+
+  extern char *stringprep_utf8_nfkc_normalize (const char *str, ssize_t len);
+  extern my_uint32_t *stringprep_ucs4_nfkc_normalize (my_uint32_t * str,
+						   ssize_t len);
+
+  /*extern const char *stringprep_locale_charset (void);
+  extern char *stringprep_convert (const char *str,
+				   const char *to_codeset,
+				   const char *from_codeset);
+  extern char *stringprep_locale_to_utf8 (const char *str);
+  extern char *stringprep_utf8_to_locale (const char *str);*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* _STRINGPREP_H */
Index: psi/tags/0.10-os2-b1/iris/prepare
===================================================================
--- psi/tags/0.10-os2-b1/iris/prepare	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/prepare	(revision 38)
@@ -0,0 +1,26 @@
+CSNS=../cutestuff/csnsproc
+
+mkdir -p cutestuff
+mkdir -p cutestuff/util
+mkdir -p cutestuff/network
+
+$CSNS ../cutestuff/util/base64.h XMPP > cutestuff/util/base64.h
+$CSNS ../cutestuff/util/bytestream.h XMPP > cutestuff/util/bytestream.h
+$CSNS ../cutestuff/network/ndns.h XMPP > cutestuff/network/ndns.h
+$CSNS ../cutestuff/network/srvresolver.h XMPP > cutestuff/network/srvresolver.h
+$CSNS ../cutestuff/network/bsocket.h XMPP > cutestuff/network/bsocket.h
+$CSNS ../cutestuff/network/httpconnect.h XMPP > cutestuff/network/httpconnect.h
+$CSNS ../cutestuff/network/httppoll.h XMPP > cutestuff/network/httppoll.h
+$CSNS ../cutestuff/network/servsock.h XMPP > cutestuff/network/servsock.h
+$CSNS ../cutestuff/network/socks.h XMPP > cutestuff/network/socks.h
+
+$CSNS ../cutestuff/util/base64.cpp XMPP > cutestuff/util/base64.cpp
+$CSNS ../cutestuff/util/bytestream.cpp XMPP > cutestuff/util/bytestream.cpp
+$CSNS ../cutestuff/network/ndns.cpp XMPP > cutestuff/network/ndns.cpp
+$CSNS ../cutestuff/network/srvresolver.cpp XMPP > cutestuff/network/srvresolver.cpp
+$CSNS ../cutestuff/network/bsocket.cpp XMPP > cutestuff/network/bsocket.cpp
+$CSNS ../cutestuff/network/httpconnect.cpp XMPP > cutestuff/network/httpconnect.cpp
+$CSNS ../cutestuff/network/httppoll.cpp XMPP > cutestuff/network/httppoll.cpp
+$CSNS ../cutestuff/network/servsock.cpp XMPP > cutestuff/network/servsock.cpp
+$CSNS ../cutestuff/network/socks.cpp XMPP > cutestuff/network/socks.cpp
+
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/connector.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/connector.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/connector.cpp	(revision 38)
@@ -0,0 +1,719 @@
+/*
+ * connector.cpp - establish a connection to an XMPP server
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+  TODO:
+
+  - Test and analyze all possible branches
+
+  XMPP::AdvancedConnector is "good for now."  The only real issue is that
+  most of what it provides is just to work around the old Jabber/XMPP 0.9
+  connection behavior.  When XMPP 1.0 has taken over the world, we can
+  greatly simplify this class.  - Sep 3rd, 2003.
+*/
+
+#include"xmpp.h"
+
+#include<qguardedptr.h>
+#include<qca.h>
+#include"safedelete.h"
+
+#ifdef NO_NDNS
+#include<qdns.h>
+#else
+#include"ndns.h"
+#endif
+
+#include"srvresolver.h"
+#include"bsocket.h"
+#include"httpconnect.h"
+#include"httppoll.h"
+#include"socks.h"
+#include"hash.h"
+
+//#define XMPP_DEBUG
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// Connector
+//----------------------------------------------------------------------------
+Connector::Connector(QObject *parent)
+:QObject(parent)
+{
+	setUseSSL(false);
+	setPeerAddressNone();
+}
+
+Connector::~Connector()
+{
+}
+
+bool Connector::useSSL() const
+{
+	return ssl;
+}
+
+bool Connector::havePeerAddress() const
+{
+	return haveaddr;
+}
+
+QHostAddress Connector::peerAddress() const
+{
+	return addr;
+}
+
+Q_UINT16 Connector::peerPort() const
+{
+	return port;
+}
+
+void Connector::setUseSSL(bool b)
+{
+	ssl = b;
+}
+
+void Connector::setPeerAddressNone()
+{
+	haveaddr = false;
+	addr = QHostAddress();
+	port = 0;
+}
+
+void Connector::setPeerAddress(const QHostAddress &_addr, Q_UINT16 _port)
+{
+	haveaddr = true;
+	addr = _addr;
+	port = _port;
+}
+
+
+//----------------------------------------------------------------------------
+// AdvancedConnector::Proxy
+//----------------------------------------------------------------------------
+AdvancedConnector::Proxy::Proxy()
+{
+	t = None;
+	v_poll = 30;
+}
+
+AdvancedConnector::Proxy::~Proxy()
+{
+}
+
+int AdvancedConnector::Proxy::type() const
+{
+	return t;
+}
+
+QString AdvancedConnector::Proxy::host() const
+{
+	return v_host;
+}
+
+Q_UINT16 AdvancedConnector::Proxy::port() const
+{
+	return v_port;
+}
+
+QString AdvancedConnector::Proxy::url() const
+{
+	return v_url;
+}
+
+QString AdvancedConnector::Proxy::user() const
+{
+	return v_user;
+}
+
+QString AdvancedConnector::Proxy::pass() const
+{
+	return v_pass;
+}
+
+int AdvancedConnector::Proxy::pollInterval() const
+{
+	return v_poll;
+}
+
+void AdvancedConnector::Proxy::setHttpConnect(const QString &host, Q_UINT16 port)
+{
+	t = HttpConnect;
+	v_host = host;
+	v_port = port;
+}
+
+void AdvancedConnector::Proxy::setHttpPoll(const QString &host, Q_UINT16 port, const QString &url)
+{
+	t = HttpPoll;
+	v_host = host;
+	v_port = port;
+	v_url = url;
+}
+
+void AdvancedConnector::Proxy::setSocks(const QString &host, Q_UINT16 port)
+{
+	t = Socks;
+	v_host = host;
+	v_port = port;
+}
+
+void AdvancedConnector::Proxy::setUserPass(const QString &user, const QString &pass)
+{
+	v_user = user;
+	v_pass = pass;
+}
+
+void AdvancedConnector::Proxy::setPollInterval(int secs)
+{
+	v_poll = secs;
+}
+
+
+//----------------------------------------------------------------------------
+// AdvancedConnector
+//----------------------------------------------------------------------------
+enum { Idle, Connecting, Connected };
+class AdvancedConnector::Private
+{
+public:
+	int mode;
+	ByteStream *bs;
+#ifdef NO_NDNS
+	QDns *qdns;
+#else
+	NDns dns;
+#endif
+	SrvResolver srv;
+
+	QString server;
+	QString opt_host;
+	int opt_port;
+	bool opt_probe, opt_ssl;
+	Proxy proxy;
+
+	QString host;
+	int port;
+	QValueList<QDns::Server> servers;
+	int errorCode;
+
+	bool multi, using_srv;
+	bool will_be_ssl;
+	int probe_mode;
+
+	bool aaaa;
+	SafeDelete sd;
+};
+
+AdvancedConnector::AdvancedConnector(QObject *parent)
+:Connector(parent)
+{
+	d = new Private;
+	d->bs = 0;
+#ifdef NO_NDNS
+	d->qdns = 0;
+#else
+	connect(&d->dns, SIGNAL(resultsReady()), SLOT(dns_done()));
+#endif
+	connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done()));
+	d->opt_probe = false;
+	d->opt_ssl = false;
+	cleanup();
+	d->errorCode = 0;
+}
+
+AdvancedConnector::~AdvancedConnector()
+{
+	cleanup();
+	delete d;
+}
+
+void AdvancedConnector::cleanup()
+{
+	d->mode = Idle;
+
+	// stop any dns
+#ifdef NO_NDNS
+	if(d->qdns) {
+		d->qdns->disconnect(this);
+		d->qdns->deleteLater();
+		//d->sd.deleteLater(d->qdns);
+		d->qdns = 0;
+	}
+#else
+	if(d->dns.isBusy())
+		d->dns.stop();
+#endif
+	if(d->srv.isBusy())
+		d->srv.stop();
+
+	// destroy the bytestream, if there is one
+	delete d->bs;
+	d->bs = 0;
+
+	d->multi = false;
+	d->using_srv = false;
+	d->will_be_ssl = false;
+	d->probe_mode = -1;
+
+	setUseSSL(false);
+	setPeerAddressNone();
+}
+
+void AdvancedConnector::setProxy(const Proxy &proxy)
+{
+	if(d->mode != Idle)
+		return;
+	d->proxy = proxy;
+}
+
+void AdvancedConnector::setOptHostPort(const QString &host, Q_UINT16 _port)
+{
+	if(d->mode != Idle)
+		return;
+	d->opt_host = host;
+	d->opt_port = _port;
+}
+
+void AdvancedConnector::setOptProbe(bool b)
+{
+	if(d->mode != Idle)
+		return;
+	d->opt_probe = b;
+}
+
+void AdvancedConnector::setOptSSL(bool b)
+{
+	if(d->mode != Idle)
+		return;
+	d->opt_ssl = b;
+}
+
+void AdvancedConnector::connectToServer(const QString &server)
+{
+	if(d->mode != Idle)
+		return;
+	if(server.isEmpty())
+		return;
+
+	d->errorCode = 0;
+	d->server = server;
+	d->mode = Connecting;
+	d->aaaa = true;
+
+	if(d->proxy.type() == Proxy::HttpPoll) {
+		// need SHA1 here
+		if(!QCA::isSupported(QCA::CAP_SHA1))
+			QCA::insertProvider(createProviderHash());
+
+		HttpPoll *s = new HttpPoll;
+		d->bs = s;
+		connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+		connect(s, SIGNAL(syncStarted()), SLOT(http_syncStarted()));
+		connect(s, SIGNAL(syncFinished()), SLOT(http_syncFinished()));
+		connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+		if(!d->proxy.user().isEmpty())
+			s->setAuth(d->proxy.user(), d->proxy.pass());
+		s->setPollInterval(d->proxy.pollInterval());
+
+		if(d->proxy.host().isEmpty())
+			s->connectToUrl(d->proxy.url());
+		else
+			s->connectToHost(d->proxy.host(), d->proxy.port(), d->proxy.url());
+	}
+	else {
+		if(!d->opt_host.isEmpty()) {
+			d->host = d->opt_host;
+			d->port = d->opt_port;
+			do_resolve();
+		}
+		else {
+			d->multi = true;
+
+			QGuardedPtr<QObject> self = this;
+			srvLookup(d->server);
+			if(!self)
+				return;
+
+			d->srv.resolveSrvOnly(d->server, "xmpp-client", "tcp");
+		}
+	}
+}
+
+void AdvancedConnector::changePollInterval(int secs)
+{
+	if(d->bs && (d->bs->inherits("XMPP::HttpPoll") || d->bs->inherits("HttpPoll"))) {
+		HttpPoll *s = static_cast<HttpPoll*>(d->bs);
+		s->setPollInterval(secs);
+	}
+}
+
+ByteStream *AdvancedConnector::stream() const
+{
+	if(d->mode == Connected)
+		return d->bs;
+	else
+		return 0;
+}
+
+void AdvancedConnector::done()
+{
+	cleanup();
+}
+
+int AdvancedConnector::errorCode() const
+{
+	return d->errorCode;
+}
+
+void AdvancedConnector::do_resolve()
+{
+#ifdef NO_NDNS
+	printf("resolving (aaaa=%d)\n", d->aaaa);
+	d->qdns = new QDns;
+	connect(d->qdns, SIGNAL(resultsReady()), SLOT(dns_done()));
+	if(d->aaaa)
+		d->qdns->setRecordType(QDns::Aaaa); // IPv6
+	else
+		d->qdns->setRecordType(QDns::A); // IPv4
+	d->qdns->setLabel(d->host);
+#else
+	d->dns.resolve(d->host);
+#endif
+}
+
+void AdvancedConnector::dns_done()
+{
+	bool failed = false;
+	QHostAddress addr;
+
+#ifdef NO_NDNS
+	//if(!d->qdns)
+	//	return;
+
+	// apparently we sometimes get this signal even though the results aren' t ready
+	//if(d->qdns->isWorking())
+	//	return;
+
+	//SafeDeleteLock s(&d->sd);
+
+        // grab the address list and destroy the qdns object
+	QValueList<QHostAddress> list = d->qdns->addresses();
+	d->qdns->disconnect(this);
+	d->qdns->deleteLater();
+	//d->sd.deleteLater(d->qdns);
+	d->qdns = 0;
+
+	if(list.isEmpty()) {
+		if(d->aaaa) {
+			d->aaaa = false;
+			do_resolve();
+			return;
+		}
+		//do_resolve();
+		//return;
+		failed = true;
+	}
+	else
+		addr = list.first();
+#else
+	if(d->dns.result() == 0)
+		failed = true;
+	else
+		addr = QHostAddress(d->dns.result());
+#endif
+
+	if(failed) {
+#ifdef XMPP_DEBUG
+		printf("dns1\n");
+#endif
+		// using proxy?  then try the unresolved host through the proxy
+		if(d->proxy.type() != Proxy::None) {
+#ifdef XMPP_DEBUG
+			printf("dns1.1\n");
+#endif
+			do_connect();
+		}
+		else if(d->using_srv) {
+#ifdef XMPP_DEBUG
+			printf("dns1.2\n");
+#endif
+			if(d->servers.isEmpty()) {
+#ifdef XMPP_DEBUG
+				printf("dns1.2.1\n");
+#endif
+				cleanup();
+				d->errorCode = ErrConnectionRefused;
+				error();
+			}
+			else {
+#ifdef XMPP_DEBUG
+				printf("dns1.2.2\n");
+#endif
+				tryNextSrv();
+				return;
+			}
+		}
+		else {
+#ifdef XMPP_DEBUG
+			printf("dns1.3\n");
+#endif
+			cleanup();
+			d->errorCode = ErrHostNotFound;
+			error();
+		}
+	}
+	else {
+#ifdef XMPP_DEBUG
+		printf("dns2\n");
+#endif
+		d->host = addr.toString();
+		do_connect();
+	}
+}
+
+void AdvancedConnector::do_connect()
+{
+#ifdef XMPP_DEBUG
+	printf("trying %s:%d\n", d->host.latin1(), d->port);
+#endif
+	int t = d->proxy.type();
+	if(t == Proxy::None) {
+#ifdef XMPP_DEBUG
+		printf("do_connect1\n");
+#endif
+		BSocket *s = new BSocket;
+		d->bs = s;
+		connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+		connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+		s->connectToHost(d->host, d->port);
+	}
+	else if(t == Proxy::HttpConnect) {
+#ifdef XMPP_DEBUG
+		printf("do_connect2\n");
+#endif
+		HttpConnect *s = new HttpConnect;
+		d->bs = s;
+		connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+		connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+		if(!d->proxy.user().isEmpty())
+			s->setAuth(d->proxy.user(), d->proxy.pass());
+		s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port);
+	}
+	else if(t == Proxy::Socks) {
+#ifdef XMPP_DEBUG
+		printf("do_connect3\n");
+#endif
+		SocksClient *s = new SocksClient;
+		d->bs = s;
+		connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+		connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+		if(!d->proxy.user().isEmpty())
+			s->setAuth(d->proxy.user(), d->proxy.pass());
+		s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port);
+	}
+}
+
+void AdvancedConnector::tryNextSrv()
+{
+#ifdef XMPP_DEBUG
+	printf("trying next srv\n");
+#endif
+	d->host = d->servers.first().name;
+	d->port = d->servers.first().port;
+	d->servers.remove(d->servers.begin());
+	do_resolve();
+}
+
+void AdvancedConnector::srv_done()
+{
+	QGuardedPtr<QObject> self = this;
+#ifdef XMPP_DEBUG
+	printf("srv_done1\n");
+#endif
+	d->servers = d->srv.servers();
+	if(d->servers.isEmpty()) {
+		srvResult(false);
+		if(!self)
+			return;
+
+#ifdef XMPP_DEBUG
+		printf("srv_done1.1\n");
+#endif
+		// fall back to A record
+		d->using_srv = false;
+		d->host = d->server;
+		if(d->opt_probe) {
+#ifdef XMPP_DEBUG
+			printf("srv_done1.1.1\n");
+#endif
+			d->probe_mode = 0;
+			d->port = 5223;
+			d->will_be_ssl = true;
+		}
+		else {
+#ifdef XMPP_DEBUG
+			printf("srv_done1.1.2\n");
+#endif
+			d->probe_mode = 1;
+			d->port = 5222;
+		}
+		do_resolve();
+		return;
+	}
+
+	srvResult(true);
+	if(!self)
+		return;
+
+	d->using_srv = true;
+	tryNextSrv();
+}
+
+void AdvancedConnector::bs_connected()
+{
+	if(d->proxy.type() == Proxy::None) {
+		QHostAddress h = (static_cast<BSocket*>(d->bs))->peerAddress();
+		int p = (static_cast<BSocket*>(d->bs))->peerPort();
+		setPeerAddress(h, p);
+	}
+
+	// only allow ssl override if proxy==poll or host:port
+	if((d->proxy.type() == Proxy::HttpPoll || !d->opt_host.isEmpty()) && d->opt_ssl)
+		setUseSSL(true);
+	else if(d->will_be_ssl)
+		setUseSSL(true);
+
+	d->mode = Connected;
+	connected();
+}
+
+void AdvancedConnector::bs_error(int x)
+{
+	if(d->mode == Connected) {
+		d->errorCode = ErrStream;
+		error();
+		return;
+	}
+
+	bool proxyError = false;
+	int err = ErrConnectionRefused;
+	int t = d->proxy.type();
+
+#ifdef XMPP_DEBUG
+	printf("bse1\n");
+#endif
+
+	// figure out the error
+	if(t == Proxy::None) {
+		if(x == BSocket::ErrHostNotFound)
+			err = ErrHostNotFound;
+		else
+			err = ErrConnectionRefused;
+	}
+	else if(t == Proxy::HttpConnect) {
+		if(x == HttpConnect::ErrConnectionRefused)
+			err = ErrConnectionRefused;
+		else if(x == HttpConnect::ErrHostNotFound)
+			err = ErrHostNotFound;
+		else {
+			proxyError = true;
+			if(x == HttpConnect::ErrProxyAuth)
+				err = ErrProxyAuth;
+			else if(x == HttpConnect::ErrProxyNeg)
+				err = ErrProxyNeg;
+			else
+				err = ErrProxyConnect;
+		}
+	}
+	else if(t == Proxy::HttpPoll) {
+		if(x == HttpPoll::ErrConnectionRefused)
+			err = ErrConnectionRefused;
+		else if(x == HttpPoll::ErrHostNotFound)
+			err = ErrHostNotFound;
+		else {
+			proxyError = true;
+			if(x == HttpPoll::ErrProxyAuth)
+				err = ErrProxyAuth;
+			else if(x == HttpPoll::ErrProxyNeg)
+				err = ErrProxyNeg;
+			else
+				err = ErrProxyConnect;
+		}
+	}
+	else if(t == Proxy::Socks) {
+		if(x == SocksClient::ErrConnectionRefused)
+			err = ErrConnectionRefused;
+		else if(x == SocksClient::ErrHostNotFound)
+			err = ErrHostNotFound;
+		else {
+			proxyError = true;
+			if(x == SocksClient::ErrProxyAuth)
+				err = ErrProxyAuth;
+			else if(x == SocksClient::ErrProxyNeg)
+				err = ErrProxyNeg;
+			else
+				err = ErrProxyConnect;
+		}
+	}
+
+	// no-multi or proxy error means we quit
+	if(!d->multi || proxyError) {
+		cleanup();
+		d->errorCode = err;
+		error();
+		return;
+	}
+
+	if(d->using_srv && !d->servers.isEmpty()) {
+#ifdef XMPP_DEBUG
+		printf("bse1.1\n");
+#endif
+		tryNextSrv();
+	}
+	else if(!d->using_srv && d->opt_probe && d->probe_mode == 0) {
+#ifdef XMPP_DEBUG
+		printf("bse1.2\n");
+#endif
+		d->probe_mode = 1;
+		d->port = 5222;
+		d->will_be_ssl = false;
+		do_connect();
+	}
+	else {
+#ifdef XMPP_DEBUG
+		printf("bse1.3\n");
+#endif
+		cleanup();
+		d->errorCode = ErrConnectionRefused;
+		error();
+	}
+}
+
+void AdvancedConnector::http_syncStarted()
+{
+	httpSyncStarted();
+}
+
+void AdvancedConnector::http_syncFinished()
+{
+	httpSyncFinished();
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/hash.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/hash.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/hash.cpp	(revision 38)
@@ -0,0 +1,670 @@
+/*
+ * hash.cpp - hashing functions for SHA1 and MD5
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"hash.h"
+
+namespace XMPP
+{
+
+static bool bigEndian;
+static bool haveEndian = false;
+
+static void ensureEndian()
+{
+	if(!haveEndian) {
+		haveEndian = true;
+		int wordSize;
+		qSysInfo(&wordSize, &bigEndian);
+	}
+}
+
+//----------------------------------------------------------------------------
+// MD5
+//----------------------------------------------------------------------------
+
+/* NOTE: the following code was modified to not need BYTE_ORDER -- Justin */
+
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: hash.cpp,v 1.2 2003/11/09 12:32:04 justin Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+	http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+	either statically or dynamically; added missing #include <string.h>
+	in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+	type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+	unsigned in ANSI C, signed in traditional"; made test program
+	self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef Q_UINT8  md5_byte_t; /* 8-bit byte */
+typedef Q_UINT32 md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];	/* message length in bits, lsw first */
+    md5_word_t abcd[4];		/* digest buffer */
+    md5_byte_t buf[64];		/* accumulate block */
+} md5_state_t;
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+	md5_word_t
+	a = pms->abcd[0], b = pms->abcd[1],
+	c = pms->abcd[2], d = pms->abcd[3];
+	md5_word_t t;
+
+	/* Define storage for little-endian or both types of CPUs. */
+	md5_word_t xbuf[16];
+	const md5_word_t *X;
+
+	{
+		if(bigEndian)
+		{
+			/*
+			* On big-endian machines, we must arrange the bytes in the
+			* right order.
+			*/
+			const md5_byte_t *xp = data;
+			int i;
+
+			X = xbuf;		/* (dynamic only) */
+
+			for (i = 0; i < 16; ++i, xp += 4)
+				xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+		}
+		else			/* dynamic big-endian */
+		{
+			/*
+			* On little-endian machines, we can process properly aligned
+			* data without copying it.
+			*/
+			if (!((data - (const md5_byte_t *)0) & 3)) {
+				/* data are properly aligned */
+				X = (const md5_word_t *)data;
+			} else {
+				/* not aligned */
+				memcpy(xbuf, data, 64);
+				X = xbuf;
+			}
+		}
+	}
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+	return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+	pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+	int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+	memcpy(pms->buf + offset, p, copy);
+	if (offset + copy < 64)
+	    return;
+	p += copy;
+	left -= copy;
+	md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+	md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+	memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+
+//----------------------------------------------------------------------------
+// SHA1 - from a public domain implementation by Steve Reid (steve@edmweb.com)
+//----------------------------------------------------------------------------
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15]^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+struct SHA1_CONTEXT
+{
+	Q_UINT32 state[5];
+	Q_UINT32 count[2];
+	unsigned char buffer[64];
+};
+
+typedef union {
+	unsigned char c[64];
+	Q_UINT32 l[16];
+} CHAR64LONG16;
+
+class SHA1Context : public QCA_HashContext
+{
+public:
+	SHA1_CONTEXT _context;
+	CHAR64LONG16* block;
+
+	SHA1Context()
+	{
+		reset();
+	}
+
+	QCA_HashContext *clone()
+	{
+		return new SHA1Context(*this);
+	}
+
+	void reset()
+	{
+		sha1_init(&_context);
+	}
+
+	void update(const char *in, unsigned int len)
+	{
+		sha1_update(&_context, (unsigned char *)in, (unsigned int)len);
+	}
+
+	void final(QByteArray *out)
+	{
+		QByteArray b(20);
+		sha1_final((unsigned char *)b.data(), &_context);
+		*out = b;
+	}
+
+	unsigned long blk0(Q_UINT32 i)
+	{
+		if(bigEndian)
+			return block->l[i];
+		else
+			return (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) | (rol(block->l[i],8)&0x00FF00FF));
+	}
+
+	// Hash a single 512-bit block. This is the core of the algorithm.
+	void transform(Q_UINT32 state[5], unsigned char buffer[64])
+	{
+		Q_UINT32 a, b, c, d, e;
+
+		block = (CHAR64LONG16*)buffer;
+
+		// Copy context->state[] to working vars
+		a = state[0];
+		b = state[1];
+		c = state[2];
+		d = state[3];
+		e = state[4];
+
+		// 4 rounds of 20 operations each. Loop unrolled.
+		R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+		R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+		R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+		R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+		R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+		R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+		R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+		R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+		R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+		R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+		R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+		R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+		R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+		R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+		R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+		R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+		R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+		R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+		R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+		R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+		// Add the working vars back into context.state[]
+		state[0] += a;
+		state[1] += b;
+		state[2] += c;
+		state[3] += d;
+		state[4] += e;
+
+		// Wipe variables
+		a = b = c = d = e = 0;
+	}
+
+	// SHA1Init - Initialize new context
+	void sha1_init(SHA1_CONTEXT* context)
+	{
+		// SHA1 initialization constants
+		context->state[0] = 0x67452301;
+		context->state[1] = 0xEFCDAB89;
+		context->state[2] = 0x98BADCFE;
+		context->state[3] = 0x10325476;
+		context->state[4] = 0xC3D2E1F0;
+		context->count[0] = context->count[1] = 0;
+	}
+
+	// Run your data through this
+	void sha1_update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len)
+	{
+		Q_UINT32 i, j;
+
+		j = (context->count[0] >> 3) & 63;
+		if((context->count[0] += len << 3) < (len << 3))
+			context->count[1]++;
+
+		context->count[1] += (len >> 29);
+
+		if((j + len) > 63) {
+			memcpy(&context->buffer[j], data, (i = 64-j));
+			transform(context->state, context->buffer);
+			for ( ; i + 63 < len; i += 64) {
+				transform(context->state, &data[i]);
+			}
+			j = 0;
+		}
+		else i = 0;
+			memcpy(&context->buffer[j], &data[i], len - i);
+	}
+
+	// Add padding and return the message digest
+	void sha1_final(unsigned char digest[20], SHA1_CONTEXT* context)
+	{
+		Q_UINT32 i, j;
+		unsigned char finalcount[8];
+
+		for (i = 0; i < 8; i++) {
+			finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+			>> ((3-(i & 3)) * 8) ) & 255);  // Endian independent
+		}
+		sha1_update(context, (unsigned char *)"\200", 1);
+		while ((context->count[0] & 504) != 448) {
+			sha1_update(context, (unsigned char *)"\0", 1);
+		}
+		sha1_update(context, finalcount, 8);  // Should cause a transform()
+		for (i = 0; i < 20; i++) {
+			digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+		}
+
+		// Wipe variables
+		i = j = 0;
+		memset(context->buffer, 0, 64);
+		memset(context->state, 0, 20);
+		memset(context->count, 0, 8);
+		memset(&finalcount, 0, 8);
+	}
+};
+
+class MD5Context : public QCA_HashContext
+{
+public:
+	MD5Context()
+	{
+		reset();
+	}
+
+	QCA_HashContext *clone()
+	{
+		return new MD5Context(*this);
+	}
+
+	void reset()
+	{
+		md5_init(&md5);
+	}
+
+	void update(const char *in, unsigned int len)
+	{
+		md5_append(&md5, (const md5_byte_t *)in, len);
+	}
+
+	void final(QByteArray *out)
+	{
+		QByteArray b(16);
+		md5_finish(&md5, (md5_byte_t *)b.data());
+		*out = b;
+	}
+
+	md5_state_t md5;
+};
+
+class HashProvider : public QCAProvider
+{
+public:
+	HashProvider() {}
+	~HashProvider() {}
+
+	void init()
+	{
+		ensureEndian();
+	}
+
+	int qcaVersion() const
+	{
+		return QCA_PLUGIN_VERSION;
+	}
+
+	int capabilities() const
+	{
+		return (QCA::CAP_SHA1 | QCA::CAP_MD5);
+	}
+
+	void *context(int cap)
+	{
+		if(cap == QCA::CAP_SHA1)
+			return new SHA1Context;
+		if(cap == QCA::CAP_MD5)
+			return new MD5Context;
+		return 0;
+	}
+};
+
+QCAProvider *createProviderHash()
+{
+	return (new HashProvider);
+}
+
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/hash.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/hash.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/hash.h	(revision 38)
@@ -0,0 +1,31 @@
+/*
+ * hash.h - hashing functions for SHA1 and MD5
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+#include"qcaprovider.h"
+
+namespace XMPP
+{
+	QCAProvider *createProviderHash();
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/jid.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/jid.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/jid.cpp	(revision 38)
@@ -0,0 +1,406 @@
+/*
+ * jid.cpp - class for verifying and manipulating Jabber IDs
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"xmpp.h"
+
+#include<qdict.h>
+#include<stringprep.h>
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// StringPrepCache
+//----------------------------------------------------------------------------
+class StringPrepCache
+{
+public:
+	static bool nameprep(const QString &in, int maxbytes, QString *out)
+	{
+		if(in.isEmpty())
+		{
+			if(out)
+				*out = QString();
+			return true;
+		}
+
+		StringPrepCache *that = get_instance();
+
+		Result *r = that->nameprep_table.find(in);
+		if(r)
+		{
+			if(!r->norm)
+				return false;
+			if(out)
+				*out = *(r->norm);
+			return true;
+		}
+
+		QCString cs = in.utf8();
+		cs.resize(maxbytes);
+		if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_nameprep) != 0)
+		{
+			that->nameprep_table.insert(in, new Result);
+			return false;
+		}
+
+		QString norm = QString::fromUtf8(cs);
+		that->nameprep_table.insert(in, new Result(norm));
+		if(out)
+			*out = norm;
+		return true;
+	}
+
+	static bool nodeprep(const QString &in, int maxbytes, QString *out)
+	{
+		if(in.isEmpty())
+		{
+			if(out)
+				*out = QString();
+			return true;
+		}
+
+		StringPrepCache *that = get_instance();
+
+		Result *r = that->nodeprep_table.find(in);
+		if(r)
+		{
+			if(!r->norm)
+				return false;
+			if(out)
+				*out = *(r->norm);
+			return true;
+		}
+
+		QCString cs = in.utf8();
+		cs.resize(maxbytes);
+		if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0)
+		{
+			that->nodeprep_table.insert(in, new Result);
+			return false;
+		}
+
+		QString norm = QString::fromUtf8(cs);
+		that->nodeprep_table.insert(in, new Result(norm));
+		if(out)
+			*out = norm;
+		return true;
+	}
+
+	static bool resourceprep(const QString &in, int maxbytes, QString *out)
+	{
+		if(in.isEmpty())
+		{
+			if(out)
+				*out = QString();
+			return true;
+		}
+
+		StringPrepCache *that = get_instance();
+
+		Result *r = that->resourceprep_table.find(in);
+		if(r)
+		{
+			if(!r->norm)
+				return false;
+			if(out)
+				*out = *(r->norm);
+			return true;
+		}
+
+		QCString cs = in.utf8();
+		cs.resize(maxbytes);
+		if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0)
+		{
+			that->resourceprep_table.insert(in, new Result);
+			return false;
+		}
+
+		QString norm = QString::fromUtf8(cs);
+		that->resourceprep_table.insert(in, new Result(norm));
+		if(out)
+			*out = norm;
+		return true;
+	}
+
+private:
+	class Result
+	{
+	public:
+		QString *norm;
+
+		Result() : norm(0)
+		{
+		}
+
+		Result(const QString &s) : norm(new QString(s))
+		{
+		}
+
+		~Result()
+		{
+			delete norm;
+		}
+	};
+
+	QDict<Result> nameprep_table;
+	QDict<Result> nodeprep_table;
+	QDict<Result> resourceprep_table;
+
+	static StringPrepCache *instance;
+
+	static StringPrepCache *get_instance()
+	{
+		if(!instance)
+			instance = new StringPrepCache;
+		return instance;
+	}
+
+	StringPrepCache()
+	{
+		nameprep_table.setAutoDelete(true);
+		nodeprep_table.setAutoDelete(true);
+		resourceprep_table.setAutoDelete(true);
+	}
+};
+
+StringPrepCache *StringPrepCache::instance = 0;
+
+//----------------------------------------------------------------------------
+// Jid
+//----------------------------------------------------------------------------
+Jid::Jid()
+{
+	valid = false;
+}
+
+Jid::~Jid()
+{
+}
+
+Jid::Jid(const QString &s)
+{
+	set(s);
+}
+
+Jid::Jid(const char *s)
+{
+	set(QString(s));
+}
+
+Jid & Jid::operator=(const QString &s)
+{
+	set(s);
+	return *this;
+}
+
+Jid & Jid::operator=(const char *s)
+{
+	set(QString(s));
+	return *this;
+}
+
+void Jid::reset()
+{
+	f = QString();
+	b = QString();
+	d = QString();
+	n = QString();
+	r = QString();
+	valid = false;
+}
+
+void Jid::update()
+{
+	// build 'bare' and 'full' jids
+	if(n.isEmpty())
+		b = d;
+	else
+		b = n + '@' + d;
+	if(r.isEmpty())
+		f = b;
+	else
+		f = b + '/' + r;
+	if(f.isEmpty())
+		valid = false;
+}
+
+void Jid::set(const QString &s)
+{
+	QString rest, domain, node, resource;
+	QString norm_domain, norm_node, norm_resource;
+	int x = s.find('/');
+	if(x != -1) {
+		rest = s.mid(0, x);
+		resource = s.mid(x+1);
+	}
+	else {
+		rest = s;
+		resource = QString();
+	}
+	if(!validResource(resource, &norm_resource)) {
+		reset();
+		return;
+	}
+
+	x = rest.find('@');
+	if(x != -1) {
+		node = rest.mid(0, x);
+		domain = rest.mid(x+1);
+	}
+	else {
+		node = QString();
+		domain = rest;
+	}
+	if(!validDomain(domain, &norm_domain) || !validNode(node, &norm_node)) {
+		reset();
+		return;
+	}
+
+	valid = true;
+	d = norm_domain;
+	n = norm_node;
+	r = norm_resource;
+	update();
+}
+
+void Jid::set(const QString &domain, const QString &node, const QString &resource)
+{
+	QString norm_domain, norm_node, norm_resource;
+	if(!validDomain(domain, &norm_domain) || !validNode(node, &norm_node) || !validResource(resource, &norm_resource)) {
+		reset();
+		return;
+	}
+	valid = true;
+	d = norm_domain;
+	n = norm_node;
+	r = norm_resource;
+	update();
+}
+
+void Jid::setDomain(const QString &s)
+{
+	if(!valid)
+		return;
+	QString norm;
+	if(!validDomain(s, &norm)) {
+		reset();
+		return;
+	}
+	d = norm;
+	update();
+}
+
+void Jid::setNode(const QString &s)
+{
+	if(!valid)
+		return;
+	QString norm;
+	if(!validNode(s, &norm)) {
+		reset();
+		return;
+	}
+	n = norm;
+	update();
+}
+
+void Jid::setResource(const QString &s)
+{
+	if(!valid)
+		return;
+	QString norm;
+	if(!validResource(s, &norm)) {
+		reset();
+		return;
+	}
+	r = norm;
+	update();
+}
+
+Jid Jid::withNode(const QString &s) const
+{
+	Jid j = *this;
+	j.setNode(s);
+	return j;
+}
+
+Jid Jid::withResource(const QString &s) const
+{
+	Jid j = *this;
+	j.setResource(s);
+	return j;
+}
+
+bool Jid::isValid() const
+{
+	return valid;
+}
+
+bool Jid::isEmpty() const
+{
+	return f.isEmpty();
+}
+
+bool Jid::compare(const Jid &a, bool compareRes) const
+{
+	// only compare valid jids
+	if(!valid || !a.valid)
+		return false;
+
+	if(compareRes ? (f != a.f) : (b != a.b))
+		return false;
+
+	return true;
+}
+
+bool Jid::validDomain(const QString &s, QString *norm)
+{
+	/*QCString cs = s.utf8();
+	cs.resize(1024);
+	if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_nameprep) != 0)
+		return false;
+	if(norm)
+		*norm = QString::fromUtf8(cs);
+	return true;*/
+	return StringPrepCache::nameprep(s, 1024, norm);
+}
+
+bool Jid::validNode(const QString &s, QString *norm)
+{
+	/*QCString cs = s.utf8();
+	cs.resize(1024);
+	if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0)
+		return false;
+	if(norm)
+		*norm = QString::fromUtf8(cs);
+	return true;*/
+	return StringPrepCache::nodeprep(s, 1024, norm);
+}
+
+bool Jid::validResource(const QString &s, QString *norm)
+{
+	/*QCString cs = s.utf8();
+	cs.resize(1024);
+	if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0)
+		return false;
+	if(norm)
+		*norm = QString::fromUtf8(cs);
+	return true;*/
+	return StringPrepCache::resourceprep(s, 1024, norm);
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/parser.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/parser.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/parser.cpp	(revision 38)
@@ -0,0 +1,798 @@
+/*
+ * parser.cpp - parse an XMPP "document"
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+  TODO:
+
+  For XMPP::Parser to be "perfect", some things must be solved/changed in the
+  Qt library:
+
+  - Fix weird QDomElement::haveAttributeNS() bug (patch submitted to
+    Trolltech on Aug 31st, 2003).
+  - Fix weird behavior in QXmlSimpleReader of reporting endElement() when
+    the '/' character of a self-closing tag is reached, instead of when
+    the final '>' is reached.
+  - Fix incremental parsing bugs in QXmlSimpleReader.  At the moment, the
+    only bug I've found is related to attribute parsing, but there might
+    be more (search for '###' in $QTDIR/src/xml/qxml.cpp).
+
+  We have workarounds for all of the above problems in the code below.
+
+  - Deal with the <?xml?> processing instruction as an event type, so that we
+    can feed it back to the application properly.  Right now it is completely
+    untrackable and is simply tacked into the first event's actualString.  We
+    can't easily do this because QXmlSimpleReader eats an extra byte beyond
+    the processing instruction before reporting it.
+
+  - Make QXmlInputSource capable of accepting data incrementally, to ensure
+    proper text encoding detection and processing over a network.  This is
+    technically not a bug, as we have our own subclass below to do it, but
+    it would be nice if Qt had this already.
+*/
+
+#include"parser.h"
+
+#include<qtextcodec.h>
+#include<qptrlist.h>
+#include<string.h>
+
+using namespace XMPP;
+
+static bool qt_bug_check = false;
+static bool qt_bug_have;
+
+//----------------------------------------------------------------------------
+// StreamInput
+//----------------------------------------------------------------------------
+class StreamInput : public QXmlInputSource
+{
+public:
+	StreamInput()
+	{
+		dec = 0;
+		reset();
+	}
+
+	~StreamInput()
+	{
+		delete dec;
+	}
+
+	void reset()
+	{
+		delete dec;
+		dec = 0;
+		in.resize(0);
+		out = "";
+		at = 0;
+		paused = false;
+		mightChangeEncoding = true;
+		checkBad = true;
+		last = QChar();
+		v_encoding = "";
+		resetLastData();
+	}
+
+	void resetLastData()
+	{
+		last_string = "";
+	}
+
+	QString lastString() const
+	{
+		return last_string;
+	}
+
+	void appendData(const QByteArray &a)
+	{
+		int oldsize = in.size();
+		in.resize(oldsize + a.size());
+		memcpy(in.data() + oldsize, a.data(), a.size());
+		processBuf();
+	}
+
+	QChar lastRead()
+	{
+		return last;
+	}
+
+	QChar next()
+	{
+		if(paused)
+			return EndOfData;
+		else
+			return readNext();
+	}
+
+	// NOTE: setting 'peek' to true allows the same char to be read again,
+	//       however this still advances the internal byte processing.
+	QChar readNext(bool peek=false)
+	{
+		QChar c;
+		if(mightChangeEncoding)
+			c = EndOfData;
+		else {
+			if(out.isEmpty()) {
+				QString s;
+				if(!tryExtractPart(&s))
+					c = EndOfData;
+				else {
+					out = s;
+					c = out[0];
+				}
+			}
+			else
+				c = out[0];
+			if(!peek)
+				out.remove(0, 1);
+		}
+		if(c == EndOfData) {
+#ifdef XMPP_PARSER_DEBUG
+			printf("next() = EOD\n");
+#endif
+		}
+		else {
+#ifdef XMPP_PARSER_DEBUG
+			printf("next() = [%c]\n", c.latin1());
+#endif
+			last = c;
+		}
+
+		return c;
+	}
+
+	QByteArray unprocessed() const
+	{
+		QByteArray a(in.size() - at);
+		memcpy(a.data(), in.data() + at, a.size());
+		return a;
+	}
+
+	void pause(bool b)
+	{
+		paused = b;
+	}
+
+	bool isPaused()
+	{
+		return paused;
+	}
+
+	QString encoding() const
+	{
+		return v_encoding;
+	}
+
+private:
+	QTextDecoder *dec;
+	QByteArray in;
+	QString out;
+	int at;
+	bool paused;
+	bool mightChangeEncoding;
+	QChar last;
+	QString v_encoding;
+	QString last_string;
+	bool checkBad;
+
+	void processBuf()
+	{
+#ifdef XMPP_PARSER_DEBUG
+		printf("processing.  size=%d, at=%d\n", in.size(), at);
+#endif
+		if(!dec) {
+			QTextCodec *codec = 0;
+			uchar *p = (uchar *)in.data() + at;
+			int size = in.size() - at;
+
+			// do we have enough information to determine the encoding?
+			if(size == 0)
+				return;
+			bool utf16 = false;
+			if(p[0] == 0xfe || p[0] == 0xff) {
+				// probably going to be a UTF-16 byte order mark
+				if(size < 2)
+					return;
+				if((p[0] == 0xfe && p[1] == 0xff) || (p[0] == 0xff && p[1] == 0xfe)) {
+					// ok it is UTF-16
+					utf16 = true;
+				}
+			}
+			if(utf16)
+				codec = QTextCodec::codecForMib(1000); // UTF-16
+			else
+				codec = QTextCodec::codecForMib(106); // UTF-8
+
+			v_encoding = codec->name();
+			dec = codec->makeDecoder();
+
+			// for utf16, put in the byte order mark
+			if(utf16) {
+				out += dec->toUnicode((const char *)p, 2);
+				at += 2;
+			}
+		}
+
+		if(mightChangeEncoding) {
+			while(1) {
+				int n = out.find('<');
+				if(n != -1) {
+					// we need a closing bracket
+					int n2 = out.find('>', n);
+					if(n2 != -1) {
+						++n2;
+						QString h = out.mid(n, n2-n);
+						QString enc = processXmlHeader(h);
+						QTextCodec *codec = 0;
+						if(!enc.isEmpty())
+							codec = QTextCodec::codecForName(enc.latin1());
+
+						// changing codecs
+						if(codec) {
+							v_encoding = codec->name();
+							delete dec;
+							dec = codec->makeDecoder();
+						}
+						mightChangeEncoding = false;
+						out.truncate(0);
+						at = 0;
+						resetLastData();
+						break;
+					}
+				}
+				QString s;
+				if(!tryExtractPart(&s))
+					break;
+				if(checkBad && checkForBadChars(s)) {
+					// go to the parser
+					mightChangeEncoding = false;
+					out.truncate(0);
+					at = 0;
+					resetLastData();
+					break;
+				}
+				out += s;
+			}
+		}
+	}
+
+	QString processXmlHeader(const QString &h)
+	{
+		if(h.left(5) != "<?xml")
+			return "";
+
+		int endPos = h.find(">");
+		int startPos = h.find("encoding");
+		if(startPos < endPos && startPos != -1) {
+			QString encoding;
+			do {
+				startPos++;
+				if(startPos > endPos) {
+					return "";
+				}
+			} while(h[startPos] != '"' && h[startPos] != '\'');
+			startPos++;
+			while(h[startPos] != '"' && h[startPos] != '\'') {
+				encoding += h[startPos];
+				startPos++;
+				if(startPos > endPos) {
+					return "";
+				}
+			}
+			return encoding;
+		}
+		else
+			return "";
+	}
+
+	bool tryExtractPart(QString *s)
+	{
+		int size = in.size() - at;
+		if(size == 0)
+			return false;
+		uchar *p = (uchar *)in.data() + at;
+		QString nextChars;
+		while(1) {
+			nextChars = dec->toUnicode((const char *)p, 1);
+			++p;
+			++at;
+			if(!nextChars.isEmpty())
+				break;
+			if(at == (int)in.size())
+				return false;
+		}
+		last_string += nextChars;
+		*s = nextChars;
+
+		// free processed data?
+		if(at >= 1024) {
+			char *p = in.data();
+			int size = in.size() - at;
+			memmove(p, p + at, size);
+			in.resize(size);
+			at = 0;
+		}
+
+		return true;
+	}
+
+	bool checkForBadChars(const QString &s)
+	{
+		int len = s.find('<');
+		if(len == -1)
+			len = s.length();
+		else
+			checkBad = false;
+		for(int n = 0; n < len; ++n) {
+			if(!s.at(n).isSpace())
+				return true;
+		}
+		return false;
+	}
+};
+
+
+//----------------------------------------------------------------------------
+// ParserHandler
+//----------------------------------------------------------------------------
+namespace XMPP
+{
+	class ParserHandler : public QXmlDefaultHandler
+	{
+	public:
+		ParserHandler(StreamInput *_in, QDomDocument *_doc)
+		{
+			in = _in;
+			doc = _doc;
+			needMore = false;
+		}
+
+		~ParserHandler()
+		{
+			eventList.setAutoDelete(true);
+			eventList.clear();
+		}
+
+		bool startDocument()
+		{
+			depth = 0;
+			return true;
+		}
+
+		bool endDocument()
+		{
+			return true;
+		}
+
+		bool startPrefixMapping(const QString &prefix, const QString &uri)
+		{
+			if(depth == 0) {
+				nsnames += prefix;
+				nsvalues += uri;
+			}
+			return true;
+		}
+
+		bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts)
+		{
+			if(depth == 0) {
+				Parser::Event *e = new Parser::Event;
+				QXmlAttributes a;
+				for(int n = 0; n < atts.length(); ++n) {
+					QString uri = atts.uri(n);
+					QString ln = atts.localName(n);
+					if(a.index(uri, ln) == -1)
+						a.append(atts.qName(n), uri, ln, atts.value(n));
+				}
+				e->setDocumentOpen(namespaceURI, localName, qName, a, nsnames, nsvalues);
+				nsnames.clear();
+				nsvalues.clear();
+				e->setActualString(in->lastString());
+
+				in->resetLastData();
+				eventList.append(e);
+				in->pause(true);
+			}
+			else {
+				QDomElement e = doc->createElementNS(namespaceURI, qName);
+				for(int n = 0; n < atts.length(); ++n) {
+					QString uri = atts.uri(n);
+					QString ln = atts.localName(n);
+					bool have;
+					if(!uri.isEmpty()) {
+						have = e.hasAttributeNS(uri, ln);
+						if(qt_bug_have)
+							have = !have;
+					}
+					else
+						have = e.hasAttribute(ln);
+					if(!have)
+						e.setAttributeNS(uri, atts.qName(n), atts.value(n));
+				}
+
+				if(depth == 1) {
+					elem = e;
+					current = e;
+				}
+				else {
+					current.appendChild(e);
+					current = e;
+				}
+			}
+			++depth;
+			return true;
+		}
+
+		bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName)
+		{
+			--depth;
+			if(depth == 0) {
+				Parser::Event *e = new Parser::Event;
+				e->setDocumentClose(namespaceURI, localName, qName);
+				e->setActualString(in->lastString());
+				in->resetLastData();
+				eventList.append(e);
+				in->pause(true);
+			}
+			else {
+				// done with a depth 1 element?
+				if(depth == 1) {
+					Parser::Event *e = new Parser::Event;
+					e->setElement(elem);
+					e->setActualString(in->lastString());
+					in->resetLastData();
+					eventList.append(e);
+					in->pause(true);
+
+					elem = QDomElement();
+					current = QDomElement();
+				}
+				else
+					current = current.parentNode().toElement();
+			}
+
+			if(in->lastRead() == '/')
+				checkNeedMore();
+
+			return true;
+		}
+
+		bool characters(const QString &str)
+		{
+			if(depth >= 1) {
+				QString content = str;
+				if(content.isEmpty())
+					return true;
+
+				if(!current.isNull()) {
+					QDomText text = doc->createTextNode(content);
+					current.appendChild(text);
+				}
+			}
+			return true;
+		}
+
+		/*bool processingInstruction(const QString &target, const QString &data)
+		{
+			printf("Processing: [%s], [%s]\n", target.latin1(), data.latin1());
+			in->resetLastData();
+			return true;
+		}*/
+
+		void checkNeedMore()
+		{
+			// Here we will work around QXmlSimpleReader strangeness and self-closing tags.
+			// The problem is that endElement() is called when the '/' is read, not when
+			// the final '>' is read.  This is a potential problem when obtaining unprocessed
+			// bytes from StreamInput after this event, as the '>' character will end up
+			// in the unprocessed chunk.  To work around this, we need to advance StreamInput's
+			// internal byte processing, but not the xml character data.  This way, the '>'
+			// will get processed and will no longer be in the unprocessed return, but
+			// QXmlSimpleReader can still read it.  To do this, we call StreamInput::readNext
+			// with 'peek' mode.
+			QChar c = in->readNext(true); // peek
+			if(c == QXmlInputSource::EndOfData) {
+				needMore = true;
+			}
+			else {
+				// We'll assume the next char is a '>'.  If it isn't, then
+				// QXmlSimpleReader will deal with that problem on the next
+				// parse.  We don't need to take any action here.
+				needMore = false;
+
+				// there should have been a pending event
+				Parser::Event *e = eventList.getFirst();
+				if(e) {
+					e->setActualString(e->actualString() + '>');
+					in->resetLastData();
+				}
+			}
+		}
+
+		Parser::Event *takeEvent()
+		{
+			if(needMore)
+				return 0;
+			if(eventList.isEmpty())
+				return 0;
+
+			Parser::Event *e = eventList.getFirst();
+			eventList.removeRef(e);
+			in->pause(false);
+			return e;
+		}
+
+		StreamInput *in;
+		QDomDocument *doc;
+		int depth;
+		QStringList nsnames, nsvalues;
+		QDomElement elem, current;
+		QPtrList<Parser::Event> eventList;
+		bool needMore;
+	};
+};
+
+
+//----------------------------------------------------------------------------
+// Event
+//----------------------------------------------------------------------------
+class Parser::Event::Private
+{
+public:
+	int type;
+	QString ns, ln, qn;
+	QXmlAttributes a;
+	QDomElement e;
+	QString str;
+	QStringList nsnames, nsvalues;
+};
+
+Parser::Event::Event()
+{
+	d = 0;
+}
+
+Parser::Event::Event(const Event &from)
+{
+	d = 0;
+	*this = from;
+}
+
+Parser::Event & Parser::Event::operator=(const Event &from)
+{
+	delete d;
+	d = 0;
+	if(from.d)
+		d = new Private(*from.d);
+	return *this;
+}
+
+Parser::Event::~Event()
+{
+	delete d;
+}
+
+bool Parser::Event::isNull() const
+{
+	return (d ? false: true);
+}
+
+int Parser::Event::type() const
+{
+	if(isNull())
+		return -1;
+	return d->type;
+}
+
+QString Parser::Event::nsprefix(const QString &s) const
+{
+	QStringList::ConstIterator it = d->nsnames.begin();
+	QStringList::ConstIterator it2 = d->nsvalues.begin();
+	for(; it != d->nsnames.end(); ++it) {
+		if((*it) == s)
+			return (*it2);
+		++it2;
+	}
+	return QString::null;
+}
+
+QString Parser::Event::namespaceURI() const
+{
+	return d->ns;
+}
+
+QString Parser::Event::localName() const
+{
+	return d->ln;
+}
+
+QString Parser::Event::qName() const
+{
+	return d->qn;
+}
+
+QXmlAttributes Parser::Event::atts() const
+{
+	return d->a;
+}
+
+QString Parser::Event::actualString() const
+{
+	return d->str;
+}
+
+QDomElement Parser::Event::element() const
+{
+	return d->e;
+}
+
+void Parser::Event::setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues)
+{
+	if(!d)
+		d = new Private;
+	d->type = DocumentOpen;
+	d->ns = namespaceURI;
+	d->ln = localName;
+	d->qn = qName;
+	d->a = atts;
+	d->nsnames = nsnames;
+	d->nsvalues = nsvalues;
+}
+
+void Parser::Event::setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName)
+{
+	if(!d)
+		d = new Private;
+	d->type = DocumentClose;
+	d->ns = namespaceURI;
+	d->ln = localName;
+	d->qn = qName;
+}
+
+void Parser::Event::setElement(const QDomElement &elem)
+{
+	if(!d)
+		d = new Private;
+	d->type = Element;
+	d->e = elem;
+}
+
+void Parser::Event::setError()
+{
+	if(!d)
+		d = new Private;
+	d->type = Error;
+}
+
+void Parser::Event::setActualString(const QString &str)
+{
+	d->str = str;
+}
+
+//----------------------------------------------------------------------------
+// Parser
+//----------------------------------------------------------------------------
+class Parser::Private
+{
+public:
+	Private()
+	{
+		doc = 0;
+		in = 0;
+		handler = 0;
+		reader = 0;
+		reset();
+	}
+
+	~Private()
+	{
+		reset(false);
+	}
+
+	void reset(bool create=true)
+	{
+		delete reader;
+		delete handler;
+		delete in;
+		delete doc;
+
+		if(create) {
+			doc = new QDomDocument;
+			in = new StreamInput;
+			handler = new ParserHandler(in, doc);
+			reader = new QXmlSimpleReader;
+			reader->setContentHandler(handler);
+
+			// initialize the reader
+			in->pause(true);
+			reader->parse(in, true);
+			in->pause(false);
+		}
+	}
+
+	QDomDocument *doc;
+	StreamInput *in;
+	ParserHandler *handler;
+	QXmlSimpleReader *reader;
+};
+
+Parser::Parser()
+{
+	d = new Private;
+
+	// check for evil bug in Qt <= 3.2.1
+	if(!qt_bug_check) {
+		qt_bug_check = true;
+		QDomElement e = d->doc->createElementNS("someuri", "somename");
+		if(e.hasAttributeNS("someuri", "somename"))
+			qt_bug_have = true;
+		else
+			qt_bug_have = false;
+	}
+}
+
+Parser::~Parser()
+{
+	delete d;
+}
+
+void Parser::reset()
+{
+	d->reset();
+}
+
+void Parser::appendData(const QByteArray &a)
+{
+	d->in->appendData(a);
+
+	// if handler was waiting for more, give it a kick
+	if(d->handler->needMore)
+		d->handler->checkNeedMore();
+}
+
+Parser::Event Parser::readNext()
+{
+	Event e;
+	if(d->handler->needMore)
+		return e;
+	Event *ep = d->handler->takeEvent();
+	if(!ep) {
+		if(!d->reader->parseContinue()) {
+			e.setError();
+			return e;
+		}
+		ep = d->handler->takeEvent();
+		if(!ep)
+			return e;
+	}
+	e = *ep;
+	delete ep;
+	return e;
+}
+
+QByteArray Parser::unprocessed() const
+{
+	return d->in->unprocessed();
+}
+
+QString Parser::encoding() const
+{
+	return d->in->encoding();
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/parser.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/parser.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/parser.h	(revision 38)
@@ -0,0 +1,86 @@
+/*
+ * parser.h - parse an XMPP "document"
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include<qdom.h>
+#include<qxml.h>
+
+namespace XMPP
+{
+	class Parser
+	{
+	public:
+		Parser();
+		~Parser();
+
+		class Event
+		{
+		public:
+			enum Type { DocumentOpen, DocumentClose, Element, Error };
+			Event();
+			Event(const Event &);
+			Event & operator=(const Event &);
+			~Event();
+
+			bool isNull() const;
+			int type() const;
+
+			// for document open
+			QString nsprefix(const QString &s=QString::null) const;
+
+			// for document open / close
+			QString namespaceURI() const;
+			QString localName() const;
+			QString qName() const;
+			QXmlAttributes atts() const;
+
+			// for element
+			QDomElement element() const;
+
+			// for any
+			QString actualString() const;
+
+			// setup
+			void setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues);
+			void setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName);
+			void setElement(const QDomElement &elem);
+			void setError();
+			void setActualString(const QString &);
+
+		private:
+			class Private;
+			Private *d;
+		};
+
+		void reset();
+		void appendData(const QByteArray &a);
+		Event readNext();
+		QByteArray unprocessed() const;
+		QString encoding() const;
+
+	private:
+		class Private;
+		Private *d;
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/protocol.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/protocol.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/protocol.cpp	(revision 38)
@@ -0,0 +1,1595 @@
+/*
+ * protocol.cpp - XMPP-Core protocol state machine
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+// TODO: let the app know if tls is required
+//       require mutual auth for server out/in
+//       report ErrProtocol if server uses wrong NS
+//       use send() instead of writeElement() in CoreProtocol
+
+#include"protocol.h"
+
+#include<qca.h>
+#include"base64.h"
+#include"hash.h"
+
+#ifdef XMPP_TEST
+#include"td.h"
+#endif
+
+using namespace XMPP;
+
+// printArray
+//
+// This function prints out an array of bytes as latin characters, converting
+// non-printable bytes into hex values as necessary.  Useful for displaying
+// QByteArrays for debugging purposes.
+static QString printArray(const QByteArray &a)
+{
+	QString s;
+	for(uint n = 0; n < a.size(); ++n) {
+		unsigned char c = (unsigned char)a[(int)n];
+		if(c < 32 || c >= 127) {
+			QString str;
+			str.sprintf("[%02x]", c);
+			s += str;
+		}
+		else
+			s += c;
+	}
+	return s;
+}
+
+// firstChildElement
+//
+// Get an element's first child element
+static QDomElement firstChildElement(const QDomElement &e)
+{
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		if(n.isElement())
+			return n.toElement();
+	}
+	return QDomElement();
+}
+
+//----------------------------------------------------------------------------
+// Version
+//----------------------------------------------------------------------------
+Version::Version(int maj, int min)
+{
+	major = maj;
+	minor = min;
+}
+
+//----------------------------------------------------------------------------
+// StreamFeatures
+//----------------------------------------------------------------------------
+StreamFeatures::StreamFeatures()
+{
+	tls_supported = false;
+	sasl_supported = false;
+	bind_supported = false;
+	tls_required = false;
+}
+
+//----------------------------------------------------------------------------
+// BasicProtocol
+//----------------------------------------------------------------------------
+BasicProtocol::SASLCondEntry BasicProtocol::saslCondTable[] =
+{
+	{ "aborted",                Aborted },
+	{ "incorrect-encoding",     IncorrectEncoding },
+	{ "invalid-authzid",        InvalidAuthzid },
+	{ "invalid-mechanism",      InvalidMech },
+	{ "mechanism-too-weak",     MechTooWeak },
+	{ "not-authorized",         NotAuthorized },
+	{ "temporary-auth-failure", TemporaryAuthFailure },
+	{ 0, 0 },
+};
+
+BasicProtocol::StreamCondEntry BasicProtocol::streamCondTable[] =
+{
+	{ "bad-format",               BadFormat },
+	{ "bad-namespace-prefix",     BadNamespacePrefix },
+	{ "conflict",                 Conflict },
+	{ "connection-timeout",       ConnectionTimeout },
+	{ "host-gone",                HostGone },
+	{ "host-unknown",             HostUnknown },
+	{ "improper-addressing",      ImproperAddressing },
+	{ "internal-server-error",    InternalServerError },
+	{ "invalid-from",             InvalidFrom },
+	{ "invalid-id",               InvalidId },
+	{ "invalid-namespace",        InvalidNamespace },
+	{ "invalid-xml",              InvalidXml },
+	{ "not-authorized",           StreamNotAuthorized },
+	{ "policy-violation",         PolicyViolation },
+	{ "remote-connection-failed", RemoteConnectionFailed },
+	{ "resource-constraint",      ResourceConstraint },
+	{ "restricted-xml",           RestrictedXml },
+	{ "see-other-host",           SeeOtherHost },
+	{ "system-shutdown",          SystemShutdown },
+	{ "undefined-condition",      UndefinedCondition },
+	{ "unsupported-encoding",     UnsupportedEncoding },
+	{ "unsupported-stanza-type",  UnsupportedStanzaType },
+	{ "unsupported-version",      UnsupportedVersion },
+	{ "xml-not-well-formed",      XmlNotWellFormed },
+	{ 0, 0 },
+};
+
+BasicProtocol::BasicProtocol()
+:XmlProtocol()
+{
+	init();
+}
+
+BasicProtocol::~BasicProtocol()
+{
+}
+
+void BasicProtocol::init()
+{
+	errCond = -1;
+	sasl_authed = false;
+	doShutdown = false;
+	delayedError = false;
+	closeError = false;
+	ready = false;
+	stanzasPending = 0;
+	stanzasWritten = 0;
+}
+
+void BasicProtocol::reset()
+{
+	XmlProtocol::reset();
+	init();
+
+	to = QString();
+	from = QString();
+	id = QString();
+	lang = QString();
+	version = Version(1,0);
+	errText = QString();
+	errAppSpec = QDomElement();
+	otherHost = QString();
+	spare.resize(0);
+	sasl_mech = QString();
+	sasl_mechlist.clear();
+	sasl_step.resize(0);
+	stanzaToRecv = QDomElement();
+	sendList.clear();
+}
+
+void BasicProtocol::sendStanza(const QDomElement &e)
+{
+	SendItem i;
+	i.stanzaToSend = e;
+	sendList += i;
+}
+
+void BasicProtocol::sendDirect(const QString &s)
+{
+	SendItem i;
+	i.stringToSend = s;
+	sendList += i;
+}
+
+void BasicProtocol::sendWhitespace()
+{
+	SendItem i;
+	i.doWhitespace = true;
+	sendList += i;
+}
+
+QDomElement BasicProtocol::recvStanza()
+{
+	QDomElement e = stanzaToRecv;
+	stanzaToRecv = QDomElement();
+	return e;
+}
+
+void BasicProtocol::shutdown()
+{
+	doShutdown = true;
+}
+
+void BasicProtocol::shutdownWithError(int cond, const QString &str)
+{
+	otherHost = str;
+	delayErrorAndClose(cond);
+}
+
+bool BasicProtocol::isReady() const
+{
+	return ready;
+}
+
+void BasicProtocol::setReady(bool b)
+{
+	ready = b;
+}
+
+QString BasicProtocol::saslMech() const
+{
+	return sasl_mech;
+}
+
+QByteArray BasicProtocol::saslStep() const
+{
+	return sasl_step;
+}
+
+void BasicProtocol::setSASLMechList(const QStringList &list)
+{
+	sasl_mechlist = list;
+}
+
+void BasicProtocol::setSASLFirst(const QString &mech, const QByteArray &step)
+{
+	sasl_mech = mech;
+	sasl_step = step;
+}
+
+void BasicProtocol::setSASLNext(const QByteArray &step)
+{
+	sasl_step = step;
+}
+
+void BasicProtocol::setSASLAuthed()
+{
+	sasl_authed = true;
+}
+
+int BasicProtocol::stringToSASLCond(const QString &s)
+{
+	for(int n = 0; saslCondTable[n].str; ++n) {
+		if(s == saslCondTable[n].str)
+			return saslCondTable[n].cond;
+	}
+	return -1;
+}
+
+int BasicProtocol::stringToStreamCond(const QString &s)
+{
+	for(int n = 0; streamCondTable[n].str; ++n) {
+		if(s == streamCondTable[n].str)
+			return streamCondTable[n].cond;
+	}
+	return -1;
+}
+
+QString BasicProtocol::saslCondToString(int x)
+{
+	for(int n = 0; saslCondTable[n].str; ++n) {
+		if(x == saslCondTable[n].cond)
+			return saslCondTable[n].str;
+	}
+	return QString();
+}
+
+QString BasicProtocol::streamCondToString(int x)
+{
+	for(int n = 0; streamCondTable[n].str; ++n) {
+		if(x == streamCondTable[n].cond)
+			return streamCondTable[n].str;
+	}
+	return QString();
+}
+
+void BasicProtocol::extractStreamError(const QDomElement &e)
+{
+	QString text;
+	QDomElement appSpec;
+
+	QDomElement t = firstChildElement(e);
+	if(t.isNull() || t.namespaceURI() != NS_STREAMS) {
+		// probably old-style error
+		errCond = -1;
+		errText = e.text();
+	}
+	else
+		errCond = stringToStreamCond(t.tagName());
+
+	if(errCond != -1) {
+		if(errCond == SeeOtherHost)
+			otherHost = t.text();
+
+		t = e.elementsByTagNameNS(NS_STREAMS, "text").item(0).toElement();
+		if(!t.isNull())
+			text = t.text();
+
+		// find first non-standard namespaced element
+		QDomNodeList nl = e.childNodes();
+		for(uint n = 0; n < nl.count(); ++n) {
+			QDomNode i = nl.item(n);
+			if(i.isElement() && i.namespaceURI() != NS_STREAMS) {
+				appSpec = i.toElement();
+				break;
+			}
+		}
+
+		errText = text;
+		errAppSpec = appSpec;
+	}
+}
+
+void BasicProtocol::send(const QDomElement &e, bool clip)
+{
+	writeElement(e, TypeElement, false, clip);
+}
+
+void BasicProtocol::sendStreamError(int cond, const QString &text, const QDomElement &appSpec)
+{
+	QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
+	QDomElement err = doc.createElementNS(NS_STREAMS, streamCondToString(cond));
+	if(!otherHost.isEmpty())
+		err.appendChild(doc.createTextNode(otherHost));
+	se.appendChild(err);
+	if(!text.isEmpty()) {
+		QDomElement te = doc.createElementNS(NS_STREAMS, "text");
+		te.setAttributeNS(NS_XML, "xml:lang", "en");
+		te.appendChild(doc.createTextNode(text));
+		se.appendChild(te);
+	}
+	se.appendChild(appSpec);
+
+	writeElement(se, 100, false);
+}
+
+void BasicProtocol::sendStreamError(const QString &text)
+{
+	QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
+	se.appendChild(doc.createTextNode(text));
+
+	writeElement(se, 100, false);
+}
+
+bool BasicProtocol::errorAndClose(int cond, const QString &text, const QDomElement &appSpec)
+{
+	closeError = true;
+	errCond = cond;
+	errText = text;
+	errAppSpec = appSpec;
+	sendStreamError(cond, text, appSpec);
+	return close();
+}
+
+bool BasicProtocol::error(int code)
+{
+	event = EError;
+	errorCode = code;
+	return true;
+}
+
+void BasicProtocol::delayErrorAndClose(int cond, const QString &text, const QDomElement &appSpec)
+{
+	errorCode = ErrStream;
+	errCond = cond;
+	errText = text;
+	errAppSpec = appSpec;
+	delayedError = true;
+}
+
+void BasicProtocol::delayError(int code)
+{
+	errorCode = code;
+	delayedError = true;
+}
+
+QDomElement BasicProtocol::docElement()
+{
+	// create the root element
+	QDomElement e = doc.createElementNS(NS_ETHERX, "stream:stream");
+
+	QString defns = defaultNamespace();
+	QStringList list = extraNamespaces();
+
+	// HACK: using attributes seems to be the only way to get additional namespaces in here
+	if(!defns.isEmpty())
+		e.setAttribute("xmlns", defns);
+	for(QStringList::ConstIterator it = list.begin(); it != list.end();) {
+		QString prefix = *(it++);
+		QString uri = *(it++);
+		e.setAttribute(QString("xmlns:") + prefix, uri);
+	}
+
+	// additional attributes
+	if(!isIncoming() && !to.isEmpty())
+		e.setAttribute("to", to);
+	if(isIncoming() && !from.isEmpty())
+		e.setAttribute("from", from);
+	if(!id.isEmpty())
+		e.setAttribute("id", id);
+	if(!lang.isEmpty())
+		e.setAttributeNS(NS_XML, "xml:lang", lang);
+	if(version.major > 0 || version.minor > 0)
+		e.setAttribute("version", QString::number(version.major) + '.' + QString::number(version.minor));
+
+	return e;
+}
+
+void BasicProtocol::handleDocOpen(const Parser::Event &pe)
+{
+	if(isIncoming()) {
+		if(xmlEncoding() != "UTF-8") {
+			delayErrorAndClose(UnsupportedEncoding);
+			return;
+		}
+	}
+
+	if(pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") {
+		QXmlAttributes atts = pe.atts();
+
+		// grab the version
+		int major = 0;
+		int minor = 0;
+		QString verstr = atts.value("version");
+		if(!verstr.isEmpty()) {
+			int n = verstr.find('.');
+			if(n != -1) {
+				major = verstr.mid(0, n).toInt();
+				minor = verstr.mid(n+1).toInt();
+			}
+			else {
+				major = verstr.toInt();
+				minor = 0;
+			}
+		}
+		version = Version(major, minor);
+
+		if(isIncoming()) {
+			to = atts.value("to");
+			QString peerLang = atts.value(NS_XML, "lang");
+			if(!peerLang.isEmpty())
+				lang = peerLang;
+		}
+		// outgoing
+		else {
+			from = atts.value("from");
+			lang = atts.value(NS_XML, "lang");
+			id = atts.value("id");
+		}
+
+		handleStreamOpen(pe);
+	}
+	else {
+		if(isIncoming())
+			delayErrorAndClose(BadFormat);
+		else
+			delayError(ErrProtocol);
+	}
+}
+
+bool BasicProtocol::handleError()
+{
+	if(isIncoming())
+		return errorAndClose(XmlNotWellFormed);
+	else
+		return error(ErrParse);
+}
+
+bool BasicProtocol::handleCloseFinished()
+{
+	if(closeError) {
+		event = EError;
+		errorCode = ErrStream;
+		// note: errCond and friends are already set at this point
+	}
+	else
+		event = EClosed;
+	return true;
+}
+
+bool BasicProtocol::doStep(const QDomElement &e)
+{
+	// handle pending error
+	if(delayedError) {
+		if(isIncoming())
+			return errorAndClose(errCond, errText, errAppSpec);
+		else
+			return error(errorCode);
+	}
+
+	// shutdown?
+	if(doShutdown) {
+		doShutdown = false;
+		return close();
+	}
+
+	if(!e.isNull()) {
+		// check for error
+		if(e.namespaceURI() == NS_ETHERX && e.tagName() == "error") {
+			extractStreamError(e);
+			return error(ErrStream);
+		}
+	}
+
+	if(ready) {
+		// stanzas written?
+		if(stanzasWritten > 0) {
+			--stanzasWritten;
+			event = EStanzaSent;
+			return true;
+		}
+		// send items?
+		if(!sendList.isEmpty()) {
+			SendItem i;
+			{
+				QValueList<SendItem>::Iterator it = sendList.begin();
+				i = (*it);
+				sendList.remove(it);
+			}
+
+			// outgoing stanza?
+			if(!i.stanzaToSend.isNull()) {
+				++stanzasPending;
+				writeElement(i.stanzaToSend, TypeStanza, true);
+				event = ESend;
+			}
+			// direct send?
+			else if(!i.stringToSend.isEmpty()) {
+				writeString(i.stringToSend, TypeDirect, true);
+				event = ESend;
+			}
+			// whitespace keepalive?
+			else if(i.doWhitespace) {
+				writeString("\n", TypePing, false);
+				event = ESend;
+			}
+			return true;
+		}
+		else {
+			// if we have pending outgoing stanzas, ask for write notification
+			if(stanzasPending)
+				notify |= NSend;
+		}
+	}
+
+	return doStep2(e);
+}
+
+void BasicProtocol::itemWritten(int id, int)
+{
+	if(id == TypeStanza) {
+		--stanzasPending;
+		++stanzasWritten;
+	}
+}
+
+QString BasicProtocol::defaultNamespace()
+{
+	// default none
+	return QString();
+}
+
+QStringList BasicProtocol::extraNamespaces()
+{
+	// default none
+	return QStringList();
+}
+
+void BasicProtocol::handleStreamOpen(const Parser::Event &)
+{
+	// default does nothing
+}
+
+//----------------------------------------------------------------------------
+// CoreProtocol
+//----------------------------------------------------------------------------
+CoreProtocol::CoreProtocol()
+:BasicProtocol()
+{
+	init();
+}
+
+CoreProtocol::~CoreProtocol()
+{
+}
+
+void CoreProtocol::init()
+{
+	step = Start;
+
+	// ??
+	server = false;
+	dialback = false;
+	dialback_verify = false;
+
+	// settings
+	jid = Jid();
+	password = QString();
+	oldOnly = false;
+	allowPlain = false;
+	doTLS = true;
+	doAuth = true;
+	doBinding = true;
+
+	// input
+	user = QString();
+	host = QString();
+
+	// status
+	old = false;
+	digest = false;
+	tls_started = false;
+	sasl_started = false;
+}
+
+void CoreProtocol::reset()
+{
+	BasicProtocol::reset();
+	init();
+}
+
+void CoreProtocol::startClientOut(const Jid &_jid, bool _oldOnly, bool tlsActive, bool _doAuth)
+{
+	jid = _jid;
+	to = _jid.domain();
+	oldOnly = _oldOnly;
+	doAuth = _doAuth;
+	tls_started = tlsActive;
+
+	if(oldOnly)
+		version = Version(0,0);
+	startConnect();
+}
+
+void CoreProtocol::startServerOut(const QString &_to)
+{
+	server = true;
+	to = _to;
+	startConnect();
+}
+
+void CoreProtocol::startDialbackOut(const QString &_to, const QString &_from)
+{
+	server = true;
+	dialback = true;
+	to = _to;
+	self_from = _from;
+	startConnect();
+}
+
+void CoreProtocol::startDialbackVerifyOut(const QString &_to, const QString &_from, const QString &id, const QString &key)
+{
+	server = true;
+	dialback = true;
+	dialback_verify = true;
+	to = _to;
+	self_from = _from;
+	dialback_id = id;
+	dialback_key = key;
+	startConnect();
+}
+
+void CoreProtocol::startClientIn(const QString &_id)
+{
+	id = _id;
+	startAccept();
+}
+
+void CoreProtocol::startServerIn(const QString &_id)
+{
+	server = true;
+	id = _id;
+	startAccept();
+}
+
+void CoreProtocol::setLang(const QString &s)
+{
+	lang = s;
+}
+
+void CoreProtocol::setAllowTLS(bool b)
+{
+	doTLS = b;
+}
+
+void CoreProtocol::setAllowBind(bool b)
+{
+	doBinding = b;
+}
+
+void CoreProtocol::setAllowPlain(bool b)
+{
+	allowPlain = b;
+}
+
+void CoreProtocol::setPassword(const QString &s)
+{
+	password = s;
+}
+
+void CoreProtocol::setFrom(const QString &s)
+{
+	from = s;
+}
+
+void CoreProtocol::setDialbackKey(const QString &s)
+{
+	dialback_key = s;
+}
+
+bool CoreProtocol::loginComplete()
+{
+	setReady(true);
+
+	event = EReady;
+	step = Done;
+	return true;
+}
+
+int CoreProtocol::getOldErrorCode(const QDomElement &e)
+{
+	QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
+	if(err.isNull() || !err.hasAttribute("code"))
+		return -1;
+	return err.attribute("code").toInt();
+}
+
+/*QString CoreProtocol::xmlToString(const QDomElement &e, bool clip)
+{
+	// determine an appropriate 'fakeNS' to use
+	QString ns;
+	if(e.prefix() == "stream")
+		ns = NS_ETHERX;
+	else if(e.prefix() == "db")
+		ns = NS_DIALBACK;
+	else
+		ns = NS_CLIENT;
+	return ::xmlToString(e, ns, "stream:stream", clip);
+}*/
+
+bool CoreProtocol::stepAdvancesParser() const
+{
+	if(stepRequiresElement())
+		return true;
+	else if(isReady())
+		return true;
+	return false;
+}
+
+// all element-needing steps need to be registered here
+bool CoreProtocol::stepRequiresElement() const
+{
+	switch(step) {
+		case GetFeatures:
+		case GetTLSProceed:
+		case GetSASLChallenge:
+		case GetBindResponse:
+		case GetAuthGetResponse:
+		case GetAuthSetResponse:
+		case GetRequest:
+		case GetSASLResponse:
+			return true;
+	}
+	return false;
+}
+
+void CoreProtocol::stringSend(const QString &s)
+{
+#ifdef XMPP_TEST
+	TD::outgoingTag(s);
+#endif
+}
+
+void CoreProtocol::stringRecv(const QString &s)
+{
+#ifdef XMPP_TEST
+	TD::incomingTag(s);
+#endif
+}
+
+QString CoreProtocol::defaultNamespace()
+{
+	if(server)
+		return NS_SERVER;
+	else
+		return NS_CLIENT;
+}
+
+QStringList CoreProtocol::extraNamespaces()
+{
+	QStringList list;
+	if(dialback) {
+		list += "db";
+		list += NS_DIALBACK;
+	}
+	return list;
+}
+
+void CoreProtocol::handleStreamOpen(const Parser::Event &pe)
+{
+	if(isIncoming()) {
+		QString ns = pe.nsprefix();
+		QString db;
+		if(server) {
+			db = pe.nsprefix("db");
+			if(!db.isEmpty())
+				dialback = true;
+		}
+
+		// verify namespace
+		if((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) {
+			delayErrorAndClose(InvalidNamespace);
+			return;
+		}
+
+		// verify version
+		if(version.major < 1 && !dialback) {
+			delayErrorAndClose(UnsupportedVersion);
+			return;
+		}
+	}
+	else {
+		if(!dialback) {
+			if(version.major >= 1 && !oldOnly)
+				old = false;
+			else
+				old = true;
+		}
+	}
+}
+
+void CoreProtocol::elementSend(const QDomElement &e)
+{
+#ifdef XMPP_TEST
+	TD::outgoingXml(e);
+#endif
+}
+
+void CoreProtocol::elementRecv(const QDomElement &e)
+{
+#ifdef XMPP_TEST
+	TD::incomingXml(e);
+#endif
+}
+
+bool CoreProtocol::doStep2(const QDomElement &e)
+{
+	if(dialback)
+		return dialbackStep(e);
+	else
+		return normalStep(e);
+}
+
+bool CoreProtocol::isValidStanza(const QDomElement &e) const
+{
+	QString s = e.tagName();
+	if(e.namespaceURI() == (server ? NS_SERVER : NS_CLIENT) && (s == "message" || s == "presence" || s == "iq"))
+		return true;
+	else
+		return false;
+}
+
+bool CoreProtocol::grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item)
+{
+	for(QValueList<DBItem>::Iterator it = dbpending.begin(); it != dbpending.end(); ++it) {
+		const DBItem &i = *it;
+		if(i.type == type && i.to.compare(to) && i.from.compare(from)) {
+			const DBItem &i = (*it);
+			*item = i;
+			dbpending.remove(it);
+			return true;
+		}
+	}
+	return false;
+}
+
+bool CoreProtocol::dialbackStep(const QDomElement &e)
+{
+	if(step == Start) {
+		setReady(true);
+		step = Done;
+		event = EReady;
+		return true;
+	}
+
+	if(!dbrequests.isEmpty()) {
+		// process a request
+		DBItem i;
+		{
+			QValueList<DBItem>::Iterator it = dbrequests.begin();
+			i = (*it);
+			dbrequests.remove(it);
+		}
+
+		QDomElement r;
+		if(i.type == DBItem::ResultRequest) {
+			r = doc.createElementNS(NS_DIALBACK, "db:result");
+			r.setAttribute("to", i.to.full());
+			r.setAttribute("from", i.from.full());
+			r.appendChild(doc.createTextNode(i.key));
+			dbpending += i;
+		}
+		else if(i.type == DBItem::ResultGrant) {
+			r = doc.createElementNS(NS_DIALBACK, "db:result");
+			r.setAttribute("to", i.to.full());
+			r.setAttribute("from", i.from.full());
+			r.setAttribute("type", i.ok ? "valid" : "invalid");
+			if(i.ok) {
+				i.type = DBItem::Validated;
+				dbvalidated += i;
+			}
+			else {
+				// TODO: disconnect after writing element
+			}
+		}
+		else if(i.type == DBItem::VerifyRequest) {
+			r = doc.createElementNS(NS_DIALBACK, "db:verify");
+			r.setAttribute("to", i.to.full());
+			r.setAttribute("from", i.from.full());
+			r.setAttribute("id", i.id);
+			r.appendChild(doc.createTextNode(i.key));
+			dbpending += i;
+		}
+		// VerifyGrant
+		else {
+			r = doc.createElementNS(NS_DIALBACK, "db:verify");
+			r.setAttribute("to", i.to.full());
+			r.setAttribute("from", i.from.full());
+			r.setAttribute("id", i.id);
+			r.setAttribute("type", i.ok ? "valid" : "invalid");
+		}
+
+		writeElement(r, TypeElement, false);
+		event = ESend;
+		return true;
+	}
+
+	if(!e.isNull()) {
+		if(e.namespaceURI() == NS_DIALBACK) {
+			if(e.tagName() == "result") {
+				Jid to, from;
+				to.set(e.attribute("to"), "");
+				from.set(e.attribute("from"), "");
+				if(isIncoming()) {
+					QString key = e.text();
+					// TODO: report event
+				}
+				else {
+					bool ok = (e.attribute("type") == "valid") ? true: false;
+					DBItem i;
+					if(grabPendingItem(from, to, DBItem::ResultRequest, &i)) {
+						if(ok) {
+							i.type = DBItem::Validated;
+							i.ok = true;
+							dbvalidated += i;
+							// TODO: report event
+						}
+						else {
+							// TODO: report event
+						}
+					}
+				}
+			}
+			else if(e.tagName() == "verify") {
+				Jid to, from;
+				to.set(e.attribute("to"), "");
+				from.set(e.attribute("from"), "");
+				QString id = e.attribute("id");
+				if(isIncoming()) {
+					QString key = e.text();
+					// TODO: report event
+				}
+				else {
+					bool ok = (e.attribute("type") == "valid") ? true: false;
+					DBItem i;
+					if(grabPendingItem(from, to, DBItem::VerifyRequest, &i)) {
+						if(ok) {
+							// TODO: report event
+						}
+						else {
+							// TODO: report event
+						}
+					}
+				}
+			}
+		}
+		else {
+			if(isReady()) {
+				if(isValidStanza(e)) {
+					// TODO: disconnect if stanza is from unverified sender
+					// TODO: ignore packets from receiving servers
+					stanzaToRecv = e;
+					event = EStanzaReady;
+					return true;
+				}
+			}
+		}
+	}
+
+	need = NNotify;
+	notify |= NRecv;
+	return false;
+}
+
+bool CoreProtocol::normalStep(const QDomElement &e)
+{
+	if(step == Start) {
+		if(isIncoming()) {
+			need = NSASLMechs;
+			step = SendFeatures;
+			return false;
+		}
+		else {
+			if(old) {
+				if(doAuth)
+					step = HandleAuthGet;
+				else
+					return loginComplete();
+			}
+			else
+				step = GetFeatures;
+
+			return processStep();
+		}
+	}
+	else if(step == HandleFeatures) {
+		// deal with TLS?
+		if(doTLS && !tls_started && !sasl_authed && features.tls_supported) {
+			QDomElement e = doc.createElementNS(NS_TLS, "starttls");
+
+			send(e, true);
+			event = ESend;
+			step = GetTLSProceed;
+			return true;
+		}
+
+		// deal with SASL?
+		if(!sasl_authed) {
+			if(!features.sasl_supported) {
+				// SASL MUST be supported
+				event = EError;
+				errorCode = ErrProtocol;
+				return true;
+			}
+
+#ifdef XMPP_TEST
+			TD::msg("starting SASL authentication...");
+#endif
+			need = NSASLFirst;
+			step = GetSASLFirst;
+			return false;
+		}
+
+		if(server) {
+			return loginComplete();
+		}
+		else {
+			if(!doBinding)
+				return loginComplete();
+		}
+
+		// deal with bind
+		if(!features.bind_supported) {
+			// bind MUST be supported
+			event = EError;
+			errorCode = ErrProtocol;
+			return true;
+		}
+
+		QDomElement e = doc.createElement("iq");
+		e.setAttribute("type", "set");
+		e.setAttribute("id", "bind_1");
+		QDomElement b = doc.createElementNS(NS_BIND, "bind");
+
+		// request specific resource?
+		QString resource = jid.resource();
+		if(!resource.isEmpty()) {
+			QDomElement r = doc.createElement("resource");
+			r.appendChild(doc.createTextNode(jid.resource()));
+			b.appendChild(r);
+		}
+
+		e.appendChild(b);
+
+		send(e);
+		event = ESend;
+		step = GetBindResponse;
+		return true;
+	}
+	else if(step == GetSASLFirst) {
+		QDomElement e = doc.createElementNS(NS_SASL, "auth");
+		e.setAttribute("mechanism", sasl_mech);
+		if(!sasl_step.isEmpty()) {
+#ifdef XMPP_TEST
+			TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
+#endif
+			e.appendChild(doc.createTextNode(Base64::arrayToString(sasl_step)));
+		}
+
+		send(e, true);
+		event = ESend;
+		step = GetSASLChallenge;
+		return true;
+	}
+	else if(step == GetSASLNext) {
+		if(isIncoming()) {
+			if(sasl_authed) {
+				QDomElement e = doc.createElementNS(NS_SASL, "success");
+				writeElement(e, TypeElement, false, true);
+				event = ESend;
+				step = IncHandleSASLSuccess;
+				return true;
+			}
+			else {
+				QByteArray stepData = sasl_step;
+				QDomElement e = doc.createElementNS(NS_SASL, "challenge");
+				if(!stepData.isEmpty())
+					e.appendChild(doc.createTextNode(Base64::arrayToString(stepData)));
+
+				writeElement(e, TypeElement, false, true);
+				event = ESend;
+				step = GetSASLResponse;
+				return true;
+			}
+		}
+		else {
+			QByteArray stepData = sasl_step;
+#ifdef XMPP_TEST
+			TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
+#endif
+			QDomElement e = doc.createElementNS(NS_SASL, "response");
+			if(!stepData.isEmpty())
+				e.appendChild(doc.createTextNode(Base64::arrayToString(stepData)));
+
+			send(e, true);
+			event = ESend;
+			step = GetSASLChallenge;
+			return true;
+		}
+	}
+	else if(step == HandleSASLSuccess) {
+		need = NSASLLayer;
+		spare = resetStream();
+		step = Start;
+		return false;
+	}
+	else if(step == HandleAuthGet) {
+		QDomElement e = doc.createElement("iq");
+		e.setAttribute("to", to);
+		e.setAttribute("type", "get");
+		e.setAttribute("id", "auth_1");
+		QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
+		QDomElement u = doc.createElement("username");
+		u.appendChild(doc.createTextNode(jid.node()));
+		q.appendChild(u);
+		e.appendChild(q);
+
+		send(e);
+		event = ESend;
+		step = GetAuthGetResponse;
+		return true;
+	}
+	else if(step == HandleAuthSet) {
+		QDomElement e = doc.createElement("iq");
+		e.setAttribute("to", to);
+		e.setAttribute("type", "set");
+		e.setAttribute("id", "auth_2");
+		QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
+		QDomElement u = doc.createElement("username");
+		u.appendChild(doc.createTextNode(jid.node()));
+		q.appendChild(u);
+		QDomElement p;
+		if(digest) {
+			// need SHA1 here
+			if(!QCA::isSupported(QCA::CAP_SHA1))
+				QCA::insertProvider(createProviderHash());
+
+			p = doc.createElement("digest");
+			QCString cs = id.utf8() + password.utf8();
+			p.appendChild(doc.createTextNode(QCA::SHA1::hashToString(cs)));
+		}
+		else {
+			p = doc.createElement("password");
+			p.appendChild(doc.createTextNode(password));
+		}
+		q.appendChild(p);
+		QDomElement r = doc.createElement("resource");
+		r.appendChild(doc.createTextNode(jid.resource()));
+		q.appendChild(r);
+		e.appendChild(q);
+
+		send(e, true);
+		event = ESend;
+		step = GetAuthSetResponse;
+		return true;
+	}
+	// server
+	else if(step == SendFeatures) {
+		QDomElement f = doc.createElementNS(NS_ETHERX, "stream:features");
+		if(!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd
+			QDomElement tls = doc.createElementNS(NS_TLS, "starttls");
+			f.appendChild(tls);
+		}
+
+		if(sasl_authed) {
+			if(!server) {
+				QDomElement bind = doc.createElementNS(NS_BIND, "bind");
+				f.appendChild(bind);
+			}
+		}
+		else {
+			QDomElement mechs = doc.createElementNS(NS_SASL, "mechanisms");
+			for(QStringList::ConstIterator it = sasl_mechlist.begin(); it != sasl_mechlist.end(); ++it) {
+				QDomElement m = doc.createElement("mechanism");
+				m.appendChild(doc.createTextNode(*it));
+				mechs.appendChild(m);
+			}
+			f.appendChild(mechs);
+		}
+
+		writeElement(f, TypeElement, false);
+		event = ESend;
+		step = GetRequest;
+		return true;
+	}
+	// server
+	else if(step == HandleTLS) {
+		tls_started = true;
+		need = NStartTLS;
+		spare = resetStream();
+		step = Start;
+		return false;
+	}
+	// server
+	else if(step == IncHandleSASLSuccess) {
+		event = ESASLSuccess;
+		spare = resetStream();
+		step = Start;
+		printf("sasl success\n");
+		return true;
+	}
+	else if(step == GetFeatures) {
+		// we are waiting for stream features
+		if(e.namespaceURI() == NS_ETHERX && e.tagName() == "features") {
+			// extract features
+			StreamFeatures f;
+			QDomElement s = e.elementsByTagNameNS(NS_TLS, "starttls").item(0).toElement();
+			if(!s.isNull()) {
+				f.tls_supported = true;
+				f.tls_required = s.elementsByTagNameNS(NS_TLS, "required").count() > 0;
+			}
+			QDomElement m = e.elementsByTagNameNS(NS_SASL, "mechanisms").item(0).toElement();
+			if(!m.isNull()) {
+				f.sasl_supported = true;
+				QDomNodeList l = m.elementsByTagNameNS(NS_SASL, "mechanism");
+				for(uint n = 0; n < l.count(); ++n)
+					f.sasl_mechs += l.item(n).toElement().text();
+			}
+			QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
+			if(!b.isNull())
+				f.bind_supported = true;
+
+			if(f.tls_supported) {
+#ifdef XMPP_TEST
+				QString s = "STARTTLS is available";
+				if(f.tls_required)
+					s += " (required)";
+				TD::msg(s);
+#endif
+			}
+			if(f.sasl_supported) {
+#ifdef XMPP_TEST
+				QString s = "SASL mechs:";
+				for(QStringList::ConstIterator it = f.sasl_mechs.begin(); it != f.sasl_mechs.end(); ++it)
+					s += QString(" [%1]").arg((*it));
+				TD::msg(s);
+#endif
+			}
+
+			if(doAuth) {
+				event = EFeatures;
+				features = f;
+				step = HandleFeatures;
+				return true;
+			}
+			else
+				return loginComplete();
+		}
+		else {
+			// ignore
+		}
+	}
+	else if(step == GetTLSProceed) {
+		// waiting for proceed to starttls
+		if(e.namespaceURI() == NS_TLS) {
+			if(e.tagName() == "proceed") {
+#ifdef XMPP_TEST
+				TD::msg("Server wants us to proceed with ssl handshake");
+#endif
+				tls_started = true;
+				need = NStartTLS;
+				spare = resetStream();
+				step = Start;
+				return false;
+			}
+			else if(e.tagName() == "failure") {
+				event = EError;
+				errorCode = ErrStartTLS;
+				return true;
+			}
+			else {
+				event = EError;
+				errorCode = ErrProtocol;
+				return true;
+			}
+		}
+		else {
+			// ignore
+		}
+	}
+	else if(step == GetSASLChallenge) {
+		// waiting for sasl challenge/success/fail
+		if(e.namespaceURI() == NS_SASL) {
+			if(e.tagName() == "challenge") {
+				QByteArray a = Base64::stringToArray(e.text());
+#ifdef XMPP_TEST
+				TD::msg(QString("SASL IN: [%1]").arg(printArray(a)));
+#endif
+				sasl_step = a;
+				need = NSASLNext;
+				step = GetSASLNext;
+				return false;
+			}
+			else if(e.tagName() == "success") {
+				sasl_authed = true;
+				event = ESASLSuccess;
+				step = HandleSASLSuccess;
+				return true;
+			}
+			else if(e.tagName() == "failure") {
+				QDomElement t = firstChildElement(e);
+				if(t.isNull() || t.namespaceURI() != NS_SASL)
+					errCond = -1;
+				else
+					errCond = stringToSASLCond(t.tagName());
+
+				event = EError;
+				errorCode = ErrAuth;
+				return true;
+			}
+			else {
+				event = EError;
+				errorCode = ErrProtocol;
+				return true;
+			}
+		}
+	}
+	else if(step == GetBindResponse) {
+		if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+			QString type(e.attribute("type"));
+			QString id(e.attribute("id"));
+
+			if(id == "bind_1" && (type == "result" || type == "error")) {
+				if(type == "result") {
+					QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
+					Jid j;
+					if(!b.isNull()) {
+						QDomElement je = e.elementsByTagName("jid").item(0).toElement();
+						j = je.text();
+					}
+					if(!j.isValid()) {
+						event = EError;
+						errorCode = ErrProtocol;
+						return true;
+					}
+					jid = j;
+					return loginComplete();
+				}
+				else {
+					errCond = -1;
+
+					QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
+					if(!err.isNull()) {
+						// get error condition
+						QDomNodeList nl = err.childNodes();
+						QDomElement t;
+						for(uint n = 0; n < nl.count(); ++n) {
+							QDomNode i = nl.item(n);
+							if(i.isElement()) {
+								t = i.toElement();
+								break;
+							}
+						}
+						if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
+							QString cond = t.tagName();
+							if(cond == "not-allowed")
+								errCond = BindNotAllowed;
+							else if(cond == "conflict")
+								errCond = BindConflict;
+						}
+					}
+
+					event = EError;
+					errorCode = ErrBind;
+					return true;
+				}
+			}
+			else {
+				// ignore
+			}
+		}
+		else {
+			// ignore
+		}
+	}
+	else if(step == GetAuthGetResponse) {
+		// waiting for an iq
+		if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+			Jid from(e.attribute("from"));
+			QString type(e.attribute("type"));
+			QString id(e.attribute("id"));
+
+			bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
+			if(okfrom && id == "auth_1" && (type == "result" || type == "error")) {
+				if(type == "result") {
+					QDomElement q = e.elementsByTagNameNS("jabber:iq:auth", "query").item(0).toElement();
+					if(q.isNull() || q.elementsByTagName("username").item(0).isNull() || q.elementsByTagName("resource").item(0).isNull()) {
+						event = EError;
+						errorCode = ErrProtocol;
+						return true;
+					}
+					bool plain_supported = !q.elementsByTagName("password").item(0).isNull();
+					bool digest_supported = !q.elementsByTagName("digest").item(0).isNull();
+
+					if(!digest_supported && !plain_supported) {
+						event = EError;
+						errorCode = ErrProtocol;
+						return true;
+					}
+
+					// plain text not allowed?
+					if(!digest_supported && !allowPlain) {
+						event = EError;
+						errorCode = ErrPlain;
+						return true;
+					}
+
+					digest = digest_supported;
+					need = NPassword;
+					step = HandleAuthSet;
+					return false;
+				}
+				else {
+					errCond = getOldErrorCode(e);
+
+					event = EError;
+					errorCode = ErrAuth;
+					return true;
+				}
+			}
+			else {
+				// ignore
+			}
+		}
+		else {
+			// ignore
+		}
+	}
+	else if(step == GetAuthSetResponse) {
+		// waiting for an iq
+		if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+			Jid from(e.attribute("from"));
+			QString type(e.attribute("type"));
+			QString id(e.attribute("id"));
+
+			bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
+			if(okfrom && id == "auth_2" && (type == "result" || type == "error")) {
+				if(type == "result") {
+					return loginComplete();
+				}
+				else {
+					errCond = getOldErrorCode(e);
+
+					event = EError;
+					errorCode = ErrAuth;
+					return true;
+				}
+			}
+			else {
+				// ignore
+			}
+		}
+		else {
+			// ignore
+		}
+	}
+	// server
+	else if(step == GetRequest) {
+		printf("get request: [%s], %s\n", e.namespaceURI().latin1(), e.tagName().latin1());
+		if(e.namespaceURI() == NS_TLS && e.localName() == "starttls") {
+			// TODO: don't let this be done twice
+
+			QDomElement e = doc.createElementNS(NS_TLS, "proceed");
+			writeElement(e, TypeElement, false, true);
+			event = ESend;
+			step = HandleTLS;
+			return true;
+		}
+		if(e.namespaceURI() == NS_SASL) {
+			if(e.localName() == "auth") {
+				if(sasl_started) {
+					// TODO
+					printf("error\n");
+					return false;
+				}
+
+				sasl_started = true;
+				sasl_mech = e.attribute("mechanism");
+				// TODO: if child text missing, don't pass it
+				sasl_step = Base64::stringToArray(e.text());
+				need = NSASLFirst;
+				step = GetSASLNext;
+				return false;
+			}
+			else {
+				// TODO
+				printf("unknown sasl tag\n");
+				return false;
+			}
+		}
+		if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+			QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
+			if(!b.isNull()) {
+				QDomElement res = b.elementsByTagName("resource").item(0).toElement();
+				QString resource = res.text();
+
+				QDomElement r = doc.createElement("iq");
+				r.setAttribute("type", "result");
+				r.setAttribute("id", e.attribute("id"));
+				QDomElement bind = doc.createElementNS(NS_BIND, "bind");
+				QDomElement jid = doc.createElement("jid");
+				Jid j = user + '@' + host + '/' + resource;
+				jid.appendChild(doc.createTextNode(j.full()));
+				bind.appendChild(jid);
+				r.appendChild(bind);
+
+				writeElement(r, TypeElement, false);
+				event = ESend;
+				// TODO
+				return true;
+			}
+			else {
+				// TODO
+			}
+		}
+	}
+	else if(step == GetSASLResponse) {
+		if(e.namespaceURI() == NS_SASL && e.localName() == "response") {
+			sasl_step = Base64::stringToArray(e.text());
+			need = NSASLNext;
+			step = GetSASLNext;
+			return false;
+		}
+	}
+
+	if(isReady()) {
+		if(!e.isNull() && isValidStanza(e)) {
+			stanzaToRecv = e;
+			event = EStanzaReady;
+			setIncomingAsExternal();
+			return true;
+		}
+	}
+
+	need = NNotify;
+	notify |= NRecv;
+	return false;
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/protocol.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/protocol.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/protocol.h	(revision 38)
@@ -0,0 +1,352 @@
+/*
+ * protocol.h - XMPP-Core protocol state machine
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PROTOCOL_H
+#define PROTOCOL_H
+
+#include<qpair.h>
+#include"xmlprotocol.h"
+#include"xmpp.h"
+
+#define NS_ETHERX   "http://etherx.jabber.org/streams"
+#define NS_CLIENT   "jabber:client"
+#define NS_SERVER   "jabber:server"
+#define NS_DIALBACK "jabber:server:dialback"
+#define NS_STREAMS  "urn:ietf:params:xml:ns:xmpp-streams"
+#define NS_TLS      "urn:ietf:params:xml:ns:xmpp-tls"
+#define NS_SASL     "urn:ietf:params:xml:ns:xmpp-sasl"
+#define NS_SESSION  "urn:ietf:params:xml:ns:xmpp-session"
+#define NS_STANZAS  "urn:ietf:params:xml:ns:xmpp-stanzas"
+#define NS_BIND     "urn:ietf:params:xml:ns:xmpp-bind"
+
+namespace XMPP
+{
+	class Version
+	{
+	public:
+		Version(int maj=0, int min=0);
+
+		int major, minor;
+	};
+
+	class StreamFeatures
+	{
+	public:
+		StreamFeatures();
+
+		bool tls_supported, sasl_supported, bind_supported;
+		bool tls_required;
+		QStringList sasl_mechs;
+	};
+
+	class BasicProtocol : public XmlProtocol
+	{
+	public:
+		// xmpp 1.0 error conditions
+		enum SASLCond {
+			Aborted,
+			IncorrectEncoding,
+			InvalidAuthzid,
+			InvalidMech,
+			MechTooWeak,
+			NotAuthorized,
+			TemporaryAuthFailure
+		};
+		enum StreamCond {
+			BadFormat,
+			BadNamespacePrefix,
+			Conflict,
+			ConnectionTimeout,
+			HostGone,
+			HostUnknown,
+			ImproperAddressing,
+			InternalServerError,
+			InvalidFrom,
+			InvalidId,
+			InvalidNamespace,
+			InvalidXml,
+			StreamNotAuthorized,
+			PolicyViolation,
+			RemoteConnectionFailed,
+			ResourceConstraint,
+			RestrictedXml,
+			SeeOtherHost,
+			SystemShutdown,
+			UndefinedCondition,
+			UnsupportedEncoding,
+			UnsupportedStanzaType,
+			UnsupportedVersion,
+			XmlNotWellFormed
+		};
+		enum BindCond {
+			BindBadRequest,
+			BindNotAllowed,
+			BindConflict
+		};
+
+		// extend the XmlProtocol enums
+		enum Need {
+			NSASLMechs = XmlProtocol::NCustom, // need SASL mechlist
+			NStartTLS,  // need to switch on TLS layer
+			NSASLFirst, // need SASL first step
+			NSASLNext,  // need SASL next step
+			NSASLLayer, // need to switch on SASL layer
+			NCustom = XmlProtocol::NCustom+10
+		};
+		enum Event {
+			EFeatures = XmlProtocol::ECustom, // breakpoint after features packet is received
+			ESASLSuccess, // breakpoint after successful sasl auth
+			EStanzaReady, // a stanza was received
+			EStanzaSent,  // a stanza was sent
+			EReady,       // stream is ready for stanza use
+			ECustom = XmlProtocol::ECustom+10
+		};
+		enum Error {
+			ErrProtocol = XmlProtocol::ErrCustom, // there was an error in the xmpp-core protocol exchange
+			ErrStream,   // <stream:error>, see errCond, errText, and errAppSpec for details
+			ErrStartTLS, // server refused starttls
+			ErrAuth,     // authorization error.  errCond holds sasl condition (or numeric code for old-protocol)
+			ErrBind,     // server refused resource bind
+			ErrCustom = XmlProtocol::ErrCustom+10
+		};
+
+		BasicProtocol();
+		~BasicProtocol();
+
+		void reset();
+
+		// for outgoing xml
+		QDomDocument doc;
+
+		// sasl-related
+		QString saslMech() const;
+		QByteArray saslStep() const;
+		void setSASLMechList(const QStringList &list);
+		void setSASLFirst(const QString &mech, const QByteArray &step);
+		void setSASLNext(const QByteArray &step);
+		void setSASLAuthed();
+
+		// send / recv
+		void sendStanza(const QDomElement &e);
+		void sendDirect(const QString &s);
+		void sendWhitespace();
+		QDomElement recvStanza();
+
+		// shutdown
+		void shutdown();
+		void shutdownWithError(int cond, const QString &otherHost="");
+
+		// <stream> information
+		QString to, from, id, lang;
+		Version version;
+
+		// error output
+		int errCond;
+		QString errText;
+		QDomElement errAppSpec;
+		QString otherHost;
+
+		QByteArray spare; // filled with unprocessed data on NStartTLS and NSASLLayer
+
+		bool isReady() const;
+
+		enum { TypeElement, TypeStanza, TypeDirect, TypePing };
+
+	protected:
+		static int stringToSASLCond(const QString &s);
+		static int stringToStreamCond(const QString &s);
+		static QString saslCondToString(int);
+		static QString streamCondToString(int);
+
+		void send(const QDomElement &e, bool clip=false);
+		void sendStreamError(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
+		void sendStreamError(const QString &text); // old-style
+
+		bool errorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
+		bool error(int code);
+		void delayErrorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
+		void delayError(int code);
+
+		// reimplemented
+		QDomElement docElement();
+		void handleDocOpen(const Parser::Event &pe);
+		bool handleError();
+		bool handleCloseFinished();
+		bool doStep(const QDomElement &e);
+		void itemWritten(int id, int size);
+
+		virtual QString defaultNamespace();
+		virtual QStringList extraNamespaces(); // stringlist: prefix,uri,prefix,uri, [...]
+		virtual void handleStreamOpen(const Parser::Event &pe);
+		virtual bool doStep2(const QDomElement &e)=0;
+
+		void setReady(bool b);
+
+		QString sasl_mech;
+		QStringList sasl_mechlist;
+		QByteArray sasl_step;
+		bool sasl_authed;
+
+		QDomElement stanzaToRecv;
+
+	private:
+		struct SASLCondEntry
+		{
+			const char *str;
+			int cond;
+		};
+		static SASLCondEntry saslCondTable[];
+
+		struct StreamCondEntry
+		{
+			const char *str;
+			int cond;
+		};
+		static StreamCondEntry streamCondTable[];
+
+		struct SendItem
+		{
+			QDomElement stanzaToSend;
+			QString stringToSend;
+			bool doWhitespace;
+		};
+		QValueList<SendItem> sendList;
+
+		bool doShutdown, delayedError, closeError, ready;
+		int stanzasPending, stanzasWritten;
+
+		void init();
+		void extractStreamError(const QDomElement &e);
+	};
+
+	class CoreProtocol : public BasicProtocol
+	{
+	public:
+		enum {
+			NPassword = NCustom,  // need password for old-mode
+			EDBVerify = ECustom,  // breakpoint after db:verify request
+			ErrPlain = ErrCustom  // server only supports plain, but allowPlain is false locally
+		};
+
+		CoreProtocol();
+		~CoreProtocol();
+
+		void reset();
+
+		void startClientOut(const Jid &jid, bool oldOnly, bool tlsActive, bool doAuth);
+		void startServerOut(const QString &to);
+		void startDialbackOut(const QString &to, const QString &from);
+		void startDialbackVerifyOut(const QString &to, const QString &from, const QString &id, const QString &key);
+		void startClientIn(const QString &id);
+		void startServerIn(const QString &id);
+
+		void setLang(const QString &s);
+		void setAllowTLS(bool b);
+		void setAllowBind(bool b);
+		void setAllowPlain(bool b); // old-mode
+
+		void setPassword(const QString &s);
+		void setFrom(const QString &s);
+		void setDialbackKey(const QString &s);
+
+		// input
+		QString user, host;
+
+		// status
+		bool old;
+
+		StreamFeatures features;
+
+		//static QString xmlToString(const QDomElement &e, bool clip=false);
+
+		class DBItem
+		{
+		public:
+			enum { ResultRequest, ResultGrant, VerifyRequest, VerifyGrant, Validated };
+			int type;
+			Jid to, from;
+			QString key, id;
+			bool ok;
+		};
+
+	private:
+		enum Step {
+			Start,
+			Done,
+			SendFeatures,
+			GetRequest,
+			HandleTLS,
+			GetSASLResponse,
+			IncHandleSASLSuccess,
+			GetFeatures,        // read features packet
+			HandleFeatures,     // act on features, by initiating tls, sasl, or bind
+			GetTLSProceed,      // read <proceed/> tls response
+			GetSASLFirst,       // perform sasl first step using provided data
+			GetSASLChallenge,   // read server sasl challenge
+			GetSASLNext,        // perform sasl next step using provided data
+			HandleSASLSuccess,  // handle what must be done after reporting sasl success
+			GetBindResponse,    // read bind response
+			HandleAuthGet,      // send old-protocol auth-get
+			GetAuthGetResponse, // read auth-get response
+			HandleAuthSet,      // send old-protocol auth-set
+			GetAuthSetResponse  // read auth-set response
+		};
+
+		QValueList<DBItem> dbrequests, dbpending, dbvalidated;
+
+		bool server, dialback, dialback_verify;
+		int step;
+
+		bool digest;
+		bool tls_started, sasl_started;
+
+		Jid jid;
+		bool oldOnly;
+		bool allowPlain;
+		bool doTLS, doAuth, doBinding;
+		QString password;
+
+		QString dialback_id, dialback_key;
+		QString self_from;
+
+		void init();
+		static int getOldErrorCode(const QDomElement &e);
+		bool loginComplete();
+
+		bool isValidStanza(const QDomElement &e) const;
+		bool grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item);
+		bool normalStep(const QDomElement &e);
+		bool dialbackStep(const QDomElement &e);
+
+		// reimplemented
+		bool stepAdvancesParser() const;
+		bool stepRequiresElement() const;
+		void stringSend(const QString &s);
+		void stringRecv(const QString &s);
+		QString defaultNamespace();
+		QStringList extraNamespaces();
+		void handleStreamOpen(const Parser::Event &pe);
+		bool doStep2(const QDomElement &e);
+		void elementSend(const QDomElement &e);
+		void elementRecv(const QDomElement &e);
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/qcaprovider.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/qcaprovider.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/qcaprovider.h	(revision 38)
@@ -0,0 +1,191 @@
+/*
+ * qcaprovider.h - QCA Plugin API
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef QCAPROVIDER_H
+#define QCAPROVIDER_H
+
+#include<qglobal.h>
+#include<qstring.h>
+#include<qdatetime.h>
+#include<qobject.h>
+#include<qhostaddress.h>
+#include"qca.h"
+
+#define QCA_PLUGIN_VERSION 1
+
+class QCAProvider
+{
+public:
+	QCAProvider() {}
+	virtual ~QCAProvider() {}
+
+	virtual void init()=0;
+	virtual int qcaVersion() const=0;
+	virtual int capabilities() const=0;
+	virtual void *context(int cap)=0;
+};
+
+class QCA_HashContext
+{
+public:
+	virtual ~QCA_HashContext() {}
+
+	virtual QCA_HashContext *clone()=0;
+	virtual void reset()=0;
+	virtual void update(const char *in, unsigned int len)=0;
+	virtual void final(QByteArray *out)=0;
+};
+
+class QCA_CipherContext
+{
+public:
+	virtual ~QCA_CipherContext() {}
+
+	virtual QCA_CipherContext *clone()=0;
+	virtual int keySize()=0;
+	virtual int blockSize()=0;
+	virtual bool generateKey(char *out, int keysize=-1)=0;
+	virtual bool generateIV(char *out)=0;
+
+	virtual bool setup(int dir, int mode, const char *key, int keysize, const char *iv, bool pad)=0;
+	virtual bool update(const char *in, unsigned int len)=0;
+	virtual bool final(QByteArray *out)=0;
+};
+
+class QCA_RSAKeyContext
+{
+public:
+	virtual ~QCA_RSAKeyContext() {}
+
+	virtual QCA_RSAKeyContext *clone() const=0;
+	virtual bool isNull() const=0;
+	virtual bool havePublic() const=0;
+	virtual bool havePrivate() const=0;
+	virtual bool createFromDER(const char *in, unsigned int len)=0;
+	virtual bool createFromPEM(const char *in, unsigned int len)=0;
+	virtual bool createFromNative(void *in)=0;
+	virtual bool generate(unsigned int bits)=0;
+	virtual bool toDER(QByteArray *out, bool publicOnly)=0;
+	virtual bool toPEM(QByteArray *out, bool publicOnly)=0;
+
+	virtual bool encrypt(const QByteArray &in, QByteArray *out, bool oaep)=0;
+	virtual bool decrypt(const QByteArray &in, QByteArray *out, bool oaep)=0;
+};
+
+struct QCA_CertProperty
+{
+	QString var;
+	QString val;
+};
+
+class QCA_CertContext
+{
+public:
+	virtual ~QCA_CertContext() {}
+
+	virtual QCA_CertContext *clone() const=0;
+	virtual bool isNull() const=0;
+	virtual bool createFromDER(const char *in, unsigned int len)=0;
+	virtual bool createFromPEM(const char *in, unsigned int len)=0;
+	virtual bool toDER(QByteArray *out)=0;
+	virtual bool toPEM(QByteArray *out)=0;
+
+	virtual QString serialNumber() const=0;
+	virtual QString subjectString() const=0;
+	virtual QString issuerString() const=0;
+	virtual QValueList<QCA_CertProperty> subject() const=0;
+	virtual QValueList<QCA_CertProperty> issuer() const=0;
+	virtual QDateTime notBefore() const=0;
+	virtual QDateTime notAfter() const=0;
+	virtual bool matchesAddress(const QString &realHost) const=0;
+};
+
+class QCA_TLSContext
+{
+public:
+	enum Result { Success, Error, Continue };
+	virtual ~QCA_TLSContext() {}
+
+	virtual void reset()=0;
+	virtual bool startClient(const QPtrList<QCA_CertContext> &store, const QCA_CertContext &cert, const QCA_RSAKeyContext &key)=0;
+	virtual bool startServer(const QPtrList<QCA_CertContext> &store, const QCA_CertContext &cert, const QCA_RSAKeyContext &key)=0;
+
+	virtual int handshake(const QByteArray &in, QByteArray *out)=0;
+	virtual int shutdown(const QByteArray &in, QByteArray *out)=0;
+	virtual bool encode(const QByteArray &plain, QByteArray *to_net, int *encoded)=0;
+	virtual bool decode(const QByteArray &from_net, QByteArray *plain, QByteArray *to_net)=0;
+	virtual bool eof() const=0;
+	virtual QByteArray unprocessed()=0;
+
+	virtual QCA_CertContext *peerCertificate() const=0;
+	virtual int validityResult() const=0;
+};
+
+struct QCA_SASLHostPort
+{
+	QHostAddress addr;
+	Q_UINT16 port;
+};
+
+struct QCA_SASLNeedParams
+{
+	bool user, authzid, pass, realm;
+};
+
+class QCA_SASLContext
+{
+public:
+	enum Result { Success, Error, NeedParams, AuthCheck, Continue };
+	virtual ~QCA_SASLContext() {}
+
+	// common
+	virtual void reset()=0;
+	virtual void setCoreProps(const QString &service, const QString &host, QCA_SASLHostPort *local, QCA_SASLHostPort *remote)=0;
+	virtual void setSecurityProps(bool noPlain, bool noActive, bool noDict, bool noAnon, bool reqForward, bool reqCreds, bool reqMutual, int ssfMin, int ssfMax, const QString &_ext_authid, int _ext_ssf)=0;
+	virtual int security() const=0;
+	virtual int errorCond() const=0;
+
+	// init / first step
+	virtual bool clientStart(const QStringList &mechlist)=0;
+	virtual int clientFirstStep(bool allowClientSendFirst)=0;
+	virtual bool serverStart(const QString &realm, QStringList *mechlist, const QString &name)=0;
+	virtual int serverFirstStep(const QString &mech, const QByteArray *in)=0;
+
+	// get / set params
+	virtual QCA_SASLNeedParams clientParamsNeeded() const=0;
+	virtual void setClientParams(const QString *user, const QString *authzid, const QString *pass, const QString *realm)=0;
+	virtual QString username() const=0;
+	virtual QString authzid() const=0;
+
+	// continue steps
+	virtual int nextStep(const QByteArray &in)=0;
+	virtual int tryAgain()=0;
+
+	// results
+	virtual QString mech() const=0;
+	virtual const QByteArray *clientInit() const=0;
+	virtual QByteArray result() const=0;
+
+	// security layer
+	virtual bool encode(const QByteArray &in, QByteArray *out)=0;
+	virtual bool decode(const QByteArray &in, QByteArray *out)=0;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/securestream.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/securestream.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/securestream.cpp	(revision 38)
@@ -0,0 +1,589 @@
+/*
+ * securestream.cpp - combines a ByteStream with TLS and SASL
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+  Note: SecureStream depends on the underlying security layers to signal
+    plain-to-encrypted results immediately (as opposed to waiting for the
+    event loop) so that the user cannot add/remove security layers during
+    this conversion moment.  QCA::TLS and QCA::SASL behave as expected,
+    but future layers might not.
+*/
+
+#include"securestream.h"
+
+#include<qguardedptr.h>
+#include<qvaluelist.h>
+#include<qtimer.h>
+
+#ifdef USE_TLSHANDLER
+#include"xmpp.h"
+#endif
+
+//----------------------------------------------------------------------------
+// LayerTracker
+//----------------------------------------------------------------------------
+class LayerTracker
+{
+public:
+	struct Item
+	{
+		int plain;
+		int encoded;
+	};
+
+	LayerTracker();
+
+	void reset();
+	void addPlain(int plain);
+	void specifyEncoded(int encoded, int plain);
+	int finished(int encoded);
+
+	int p;
+	QValueList<Item> list;
+};
+
+LayerTracker::LayerTracker()
+{
+	p = 0;
+}
+
+void LayerTracker::reset()
+{
+	p = 0;
+	list.clear();
+}
+
+void LayerTracker::addPlain(int plain)
+{
+	p += plain;
+}
+
+void LayerTracker::specifyEncoded(int encoded, int plain)
+{
+	// can't specify more bytes than we have
+	if(plain > p)
+		plain = p;
+	p -= plain;
+	Item i;
+	i.plain = plain;
+	i.encoded = encoded;
+	list += i;
+}
+
+int LayerTracker::finished(int encoded)
+{
+	int plain = 0;
+	for(QValueList<Item>::Iterator it = list.begin(); it != list.end();) {
+		Item &i = *it;
+
+		// not enough?
+		if(encoded < i.encoded) {
+			i.encoded -= encoded;
+			break;
+		}
+
+		encoded -= i.encoded;
+		plain += i.plain;
+		it = list.remove(it);
+	}
+	return plain;
+}
+
+//----------------------------------------------------------------------------
+// SecureStream
+//----------------------------------------------------------------------------
+class SecureLayer : public QObject
+{
+	Q_OBJECT
+public:
+	enum { TLS, SASL, TLSH };
+	int type;
+	union {
+		QCA::TLS *tls;
+		QCA::SASL *sasl;
+#ifdef USE_TLSHANDLER
+		XMPP::TLSHandler *tlsHandler;
+#endif
+	} p;
+	LayerTracker layer;
+	bool tls_done;
+	int prebytes;
+
+	SecureLayer(QCA::TLS *t)
+	{
+		type = TLS;
+		p.tls = t;
+		init();
+		connect(p.tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
+		connect(p.tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
+		connect(p.tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int)));
+		connect(p.tls, SIGNAL(closed()), SLOT(tls_closed()));
+		connect(p.tls, SIGNAL(error(int)), SLOT(tls_error(int)));
+	}
+
+	SecureLayer(QCA::SASL *s)
+	{
+		type = SASL;
+		p.sasl = s;
+		init();
+		connect(p.sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead()));
+		connect(p.sasl, SIGNAL(readyReadOutgoing(int)), SLOT(sasl_readyReadOutgoing(int)));
+		connect(p.sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+	}
+
+#ifdef USE_TLSHANDLER
+	SecureLayer(XMPP::TLSHandler *t)
+	{
+		type = TLSH;
+		p.tlsHandler = t;
+		init();
+		connect(p.tlsHandler, SIGNAL(success()), SLOT(tlsHandler_success()));
+		connect(p.tlsHandler, SIGNAL(fail()), SLOT(tlsHandler_fail()));
+		connect(p.tlsHandler, SIGNAL(closed()), SLOT(tlsHandler_closed()));
+		connect(p.tlsHandler, SIGNAL(readyRead(const QByteArray &)), SLOT(tlsHandler_readyRead(const QByteArray &)));
+		connect(p.tlsHandler, SIGNAL(readyReadOutgoing(const QByteArray &, int)), SLOT(tlsHandler_readyReadOutgoing(const QByteArray &, int)));
+	}
+#endif
+
+	void init()
+	{
+		tls_done = false;
+		prebytes = 0;
+	}
+
+	void write(const QByteArray &a)
+	{
+		layer.addPlain(a.size());
+		switch(type) {
+			case TLS:  { p.tls->write(a); break; }
+			case SASL: { p.sasl->write(a); break; }
+#ifdef USE_TLSHANDLER
+			case TLSH: { p.tlsHandler->write(a); break; }
+#endif
+		}
+	}
+
+	void writeIncoming(const QByteArray &a)
+	{
+		switch(type) {
+			case TLS:  { p.tls->writeIncoming(a); break; }
+			case SASL: { p.sasl->writeIncoming(a); break; }
+#ifdef USE_TLSHANDLER
+			case TLSH: { p.tlsHandler->writeIncoming(a); break; }
+#endif
+		}
+	}
+
+	int finished(int plain)
+	{
+		int written = 0;
+
+		// deal with prebytes (bytes sent prior to this security layer)
+		if(prebytes > 0) {
+			if(prebytes >= plain) {
+				written += plain;
+				prebytes -= plain;
+				plain = 0;
+			}
+			else {
+				written += prebytes;
+				plain -= prebytes;
+				prebytes = 0;
+			}
+		}
+
+		// put remainder into the layer tracker
+		if(type == SASL || tls_done)
+			written += layer.finished(plain);
+
+		return written;
+	}
+
+signals:
+	void tlsHandshaken();
+	void tlsClosed(const QByteArray &);
+	void readyRead(const QByteArray &);
+	void needWrite(const QByteArray &);
+	void error(int);
+
+private slots:
+	void tls_handshaken()
+	{
+		tls_done = true;
+		tlsHandshaken();
+	}
+
+	void tls_readyRead()
+	{
+		QByteArray a = p.tls->read();
+		readyRead(a);
+	}
+
+	void tls_readyReadOutgoing(int plainBytes)
+	{
+		QByteArray a = p.tls->readOutgoing();
+		if(tls_done)
+			layer.specifyEncoded(a.size(), plainBytes);
+		needWrite(a);
+	}
+
+	void tls_closed()
+	{
+		QByteArray a = p.tls->readUnprocessed();
+		tlsClosed(a);
+	}
+
+	void tls_error(int x)
+	{
+		error(x);
+	}
+
+	void sasl_readyRead()
+	{
+		QByteArray a = p.sasl->read();
+		readyRead(a);
+	}
+
+	void sasl_readyReadOutgoing(int plainBytes)
+	{
+		QByteArray a = p.sasl->readOutgoing();
+		layer.specifyEncoded(a.size(), plainBytes);
+		needWrite(a);
+	}
+
+	void sasl_error(int x)
+	{
+		error(x);
+	}
+
+#ifdef USE_TLSHANDLER
+	void tlsHandler_success()
+	{
+		tls_done = true;
+		tlsHandshaken();
+	}
+
+	void tlsHandler_fail()
+	{
+		error(0);
+	}
+
+	void tlsHandler_closed()
+	{
+		tlsClosed(QByteArray());
+	}
+
+	void tlsHandler_readyRead(const QByteArray &a)
+	{
+		readyRead(a);
+	}
+
+	void tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes)
+	{
+		if(tls_done)
+			layer.specifyEncoded(a.size(), plainBytes);
+		needWrite(a);
+	}
+#endif
+};
+
+#include"securestream.moc"
+
+class SecureStream::Private
+{
+public:
+	ByteStream *bs;
+	QPtrList<SecureLayer> layers;
+	int pending;
+	int errorCode;
+	bool active;
+	bool topInProgress;
+
+	bool haveTLS() const
+	{
+		QPtrListIterator<SecureLayer> it(layers);
+		for(SecureLayer *s; (s = it.current()); ++it) {
+			if(s->type == SecureLayer::TLS
+#ifdef USE_TLSHANDLER
+			|| s->type == SecureLayer::TLSH
+#endif
+			) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	bool haveSASL() const
+	{
+		QPtrListIterator<SecureLayer> it(layers);
+		for(SecureLayer *s; (s = it.current()); ++it) {
+			if(s->type == SecureLayer::SASL)
+				return true;
+		}
+		return false;
+	}
+};
+
+SecureStream::SecureStream(ByteStream *s)
+:ByteStream(0)
+{
+	d = new Private;
+
+	d->bs = s;
+	connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+	connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+
+	d->layers.setAutoDelete(true);
+	d->pending = 0;
+	d->active = true;
+	d->topInProgress = false;
+}
+
+SecureStream::~SecureStream()
+{
+	delete d;
+}
+
+void SecureStream::linkLayer(QObject *s)
+{
+	connect(s, SIGNAL(tlsHandshaken()), SLOT(layer_tlsHandshaken()));
+	connect(s, SIGNAL(tlsClosed(const QByteArray &)), SLOT(layer_tlsClosed(const QByteArray &)));
+	connect(s, SIGNAL(readyRead(const QByteArray &)), SLOT(layer_readyRead(const QByteArray &)));
+	connect(s, SIGNAL(needWrite(const QByteArray &)), SLOT(layer_needWrite(const QByteArray &)));
+	connect(s, SIGNAL(error(int)), SLOT(layer_error(int)));
+}
+
+int SecureStream::calcPrebytes() const
+{
+	int x = 0;
+	QPtrListIterator<SecureLayer> it(d->layers);
+	for(SecureLayer *s; (s = it.current()); ++it)
+		x += s->prebytes;
+	return (d->pending - x);
+}
+
+void SecureStream::startTLSClient(QCA::TLS *t, const QByteArray &spare)
+{
+	if(!d->active || d->topInProgress || d->haveTLS())
+		return;
+
+	SecureLayer *s = new SecureLayer(t);
+	s->prebytes = calcPrebytes();
+	linkLayer(s);
+	d->layers.append(s);
+	d->topInProgress = true;
+
+	insertData(spare);
+}
+
+void SecureStream::startTLSServer(QCA::TLS *t, const QByteArray &spare)
+{
+	if(!d->active || d->topInProgress || d->haveTLS())
+		return;
+
+	SecureLayer *s = new SecureLayer(t);
+	s->prebytes = calcPrebytes();
+	linkLayer(s);
+	d->layers.append(s);
+	d->topInProgress = true;
+
+	insertData(spare);
+}
+
+void SecureStream::setLayerSASL(QCA::SASL *sasl, const QByteArray &spare)
+{
+	if(!d->active || d->topInProgress || d->haveSASL())
+		return;
+
+	SecureLayer *s = new SecureLayer(sasl);
+	s->prebytes = calcPrebytes();
+	linkLayer(s);
+	d->layers.append(s);
+
+	insertData(spare);
+}
+
+#ifdef USE_TLSHANDLER
+void SecureStream::startTLSClient(XMPP::TLSHandler *t, const QString &server, const QByteArray &spare)
+{
+	if(!d->active || d->topInProgress || d->haveTLS())
+		return;
+
+	SecureLayer *s = new SecureLayer(t);
+	s->prebytes = calcPrebytes();
+	linkLayer(s);
+	d->layers.append(s);
+	d->topInProgress = true;
+
+	// unlike QCA::TLS, XMPP::TLSHandler has no return value
+	s->p.tlsHandler->startClient(server);
+
+	insertData(spare);
+}
+#endif
+
+void SecureStream::closeTLS()
+{
+	SecureLayer *s = d->layers.getLast();
+	if(s) {
+		if(s->type == SecureLayer::TLS)
+			s->p.tls->close();
+	}
+}
+
+int SecureStream::errorCode() const
+{
+	return d->errorCode;
+}
+
+bool SecureStream::isOpen() const
+{
+	return d->active;
+}
+
+void SecureStream::write(const QByteArray &a)
+{
+	if(!isOpen())
+		return;
+
+	d->pending += a.size();
+
+	// send to the last layer
+	SecureLayer *s = d->layers.getLast();
+	if(s)
+		s->write(a);
+	else
+		writeRawData(a);
+}
+
+int SecureStream::bytesToWrite() const
+{
+	return d->pending;
+}
+
+void SecureStream::bs_readyRead()
+{
+	QByteArray a = d->bs->read();
+
+	// send to the first layer
+	SecureLayer *s = d->layers.getFirst();
+	if(s)
+		s->writeIncoming(a);
+	else
+		incomingData(a);
+}
+
+void SecureStream::bs_bytesWritten(int bytes)
+{
+	QPtrListIterator<SecureLayer> it(d->layers);
+	for(SecureLayer *s; (s = it.current()); ++it)
+		bytes = s->finished(bytes);
+
+	if(bytes > 0) {
+		d->pending -= bytes;
+		bytesWritten(bytes);
+	}
+}
+
+void SecureStream::layer_tlsHandshaken()
+{
+	d->topInProgress = false;
+	tlsHandshaken();
+}
+
+void SecureStream::layer_tlsClosed(const QByteArray &)
+{
+	d->active = false;
+	d->layers.clear();
+	tlsClosed();
+}
+
+void SecureStream::layer_readyRead(const QByteArray &a)
+{
+	SecureLayer *s = (SecureLayer *)sender();
+	QPtrListIterator<SecureLayer> it(d->layers);
+	while(it.current() != s)
+		++it;
+
+	// pass upwards
+	++it;
+	s = it.current();
+	if(s)
+		s->writeIncoming(a);
+	else
+		incomingData(a);
+}
+
+void SecureStream::layer_needWrite(const QByteArray &a)
+{
+	SecureLayer *s = (SecureLayer *)sender();
+	QPtrListIterator<SecureLayer> it(d->layers);
+	while(it.current() != s)
+		++it;
+
+	// pass downwards
+	--it;
+	s = it.current();
+	if(s)
+		s->write(a);
+	else
+		writeRawData(a);
+}
+
+void SecureStream::layer_error(int x)
+{
+	SecureLayer *s = (SecureLayer *)sender();
+	int type = s->type;
+	d->errorCode = x;
+	d->active = false;
+	d->layers.clear();
+	if(type == SecureLayer::TLS)
+		error(ErrTLS);
+	else if(type == SecureLayer::SASL)
+		error(ErrSASL);
+#ifdef USE_TLSHANDLER
+	else if(type == SecureLayer::TLSH)
+		error(ErrTLS);
+#endif
+}
+
+void SecureStream::insertData(const QByteArray &a)
+{
+	if(!a.isEmpty()) {
+		SecureLayer *s = d->layers.getLast();
+		if(s)
+			s->writeIncoming(a);
+		else
+			incomingData(a);
+	}
+}
+
+void SecureStream::writeRawData(const QByteArray &a)
+{
+	d->bs->write(a);
+}
+
+void SecureStream::incomingData(const QByteArray &a)
+{
+	appendRead(a);
+	if(bytesAvailable())
+		readyRead();
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/securestream.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/securestream.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/securestream.h	(revision 38)
@@ -0,0 +1,84 @@
+/*
+ * securestream.h - combines a ByteStream with TLS and SASL
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef SECURESTREAM_H
+#define SECURESTREAM_H
+
+#include<qca.h>
+#include"bytestream.h"
+
+#define USE_TLSHANDLER
+
+#ifdef USE_TLSHANDLER
+namespace XMPP
+{
+	class TLSHandler;
+}
+#endif
+
+class SecureStream : public ByteStream
+{
+	Q_OBJECT
+public:
+	enum Error { ErrTLS = ErrCustom, ErrSASL };
+	SecureStream(ByteStream *s);
+	~SecureStream();
+
+	void startTLSClient(QCA::TLS *t, const QByteArray &spare=QByteArray());
+	void startTLSServer(QCA::TLS *t, const QByteArray &spare=QByteArray());
+	void setLayerSASL(QCA::SASL *s, const QByteArray &spare=QByteArray());
+#ifdef USE_TLSHANDLER
+	void startTLSClient(XMPP::TLSHandler *t, const QString &server, const QByteArray &spare=QByteArray());
+#endif
+
+	void closeTLS();
+	int errorCode() const;
+
+	// reimplemented
+	bool isOpen() const;
+	void write(const QByteArray &);
+	int bytesToWrite() const;
+
+signals:
+	void tlsHandshaken();
+	void tlsClosed();
+
+private slots:
+	void bs_readyRead();
+	void bs_bytesWritten(int);
+
+	void layer_tlsHandshaken();
+	void layer_tlsClosed(const QByteArray &);
+	void layer_readyRead(const QByteArray &);
+	void layer_needWrite(const QByteArray &);
+	void layer_error(int);
+
+private:
+	void linkLayer(QObject *);
+	int calcPrebytes() const;
+	void insertData(const QByteArray &a);
+	void writeRawData(const QByteArray &a);
+	void incomingData(const QByteArray &a);
+
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/simplesasl.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/simplesasl.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/simplesasl.cpp	(revision 38)
@@ -0,0 +1,459 @@
+/*
+ * simplesasl.cpp - Simple SASL implementation
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"simplesasl.h"
+
+#include<qhostaddress.h>
+#include<qstringlist.h>
+#include<qptrlist.h>
+#include<qvaluelist.h>
+#include<qca.h>
+#include<stdlib.h>
+#include"base64.h"
+
+namespace XMPP
+{
+
+struct Prop
+{
+	QCString var, val;
+};
+
+class PropList : public QValueList<Prop>
+{
+public:
+	PropList() : QValueList<Prop>()
+	{
+	}
+
+	void set(const QCString &var, const QCString &val)
+	{
+		Prop p;
+		p.var = var;
+		p.val = val;
+		append(p);
+	}
+
+	QCString get(const QCString &var)
+	{
+		for(ConstIterator it = begin(); it != end(); ++it) {
+			if((*it).var == var)
+				return (*it).val;
+		}
+		return QCString();
+	}
+
+	QCString toString() const
+	{
+		QCString str;
+		bool first = true;
+		for(ConstIterator it = begin(); it != end(); ++it) {
+			if(!first)
+				str += ',';
+			str += (*it).var + "=\"" + (*it).val + '\"';
+			first = false;
+		}
+		return str;
+	}
+
+	bool fromString(const QCString &str)
+	{
+		PropList list;
+		int at = 0;
+		while(1) {
+			int n = str.find('=', at);
+			if(n == -1)
+				break;
+			QCString var, val;
+			var = str.mid(at, n-at);
+			at = n + 1;
+			if(str[at] == '\"') {
+				++at;
+				n = str.find('\"', at);
+				if(n == -1)
+					break;
+				val = str.mid(at, n-at);
+				at = n + 1;
+			}
+			else {
+				n = str.find(',', at);
+				if(n != -1) {
+					val = str.mid(at, n-at);
+					at = n;
+				}
+				else {
+					val = str.mid(at);
+					at = str.length()-1;
+				}
+			}
+			Prop prop;
+			prop.var = var;
+			prop.val = val;
+			list.append(prop);
+
+			if(str[at] != ',')
+				break;
+			++at;
+		}
+
+		// integrity check
+		if(list.varCount("nonce") != 1)
+			return false;
+		if(list.varCount("algorithm") != 1)
+			return false;
+		*this = list;
+		return true;
+	}
+
+	int varCount(const QCString &var)
+	{
+		int n = 0;
+		for(ConstIterator it = begin(); it != end(); ++it) {
+			if((*it).var == var)
+				++n;
+		}
+		return n;
+	}
+
+	QStringList getValues(const QCString &var)
+	{
+		QStringList list;
+		for(ConstIterator it = begin(); it != end(); ++it) {
+			if((*it).var == var)
+				list += (*it).val;
+		}
+		return list;
+	}
+};
+
+class SimpleSASLContext : public QCA_SASLContext
+{
+public:
+	// core props
+	QString service, host;
+
+	// state
+	int step;
+	QByteArray in_buf;
+	QString out_mech;
+	QByteArray out_buf;
+	bool capable;
+	int err;
+
+	QCA_SASLNeedParams need;
+	QCA_SASLNeedParams have;
+	QString user, authz, pass, realm;
+
+	SimpleSASLContext()
+	{
+		reset();
+	}
+
+	~SimpleSASLContext()
+	{
+		reset();
+	}
+
+	void reset()
+	{
+		resetState();
+		resetParams();
+	}
+
+	void resetState()
+	{
+		out_mech = QString();
+		out_buf.resize(0);
+		err = -1;
+	}
+
+	void resetParams()
+	{
+		capable = true;
+		need.user = false;
+		need.authzid = false;
+		need.pass = false;
+		need.realm = false;
+		have.user = false;
+		have.authzid = false;
+		have.pass = false;
+		have.realm = false;
+		user = QString();
+		authz = QString();
+		pass = QString();
+		realm = QString();
+	}
+
+	void setCoreProps(const QString &_service, const QString &_host, QCA_SASLHostPort *, QCA_SASLHostPort *)
+	{
+		service = _service;
+		host = _host;
+	}
+
+	void setSecurityProps(bool, bool, bool, bool, bool reqForward, bool reqCreds, bool reqMutual, int ssfMin, int, const QString &, int)
+	{
+		if(reqForward || reqCreds || reqMutual || ssfMin > 0)
+			capable = false;
+		else
+			capable = true;
+	}
+
+	int security() const
+	{
+		return 0;
+	}
+
+	int errorCond() const
+	{
+		return err;
+	}
+
+	bool clientStart(const QStringList &mechlist)
+	{
+		bool haveMech = false;
+		for(QStringList::ConstIterator it = mechlist.begin(); it != mechlist.end(); ++it) {
+			if((*it) == "DIGEST-MD5") {
+				haveMech = true;
+				break;
+			}
+		}
+		if(!capable || !haveMech) {
+			err = QCA::SASL::NoMech;
+			return false;
+		}
+
+		resetState();
+		step = 0;
+		return true;
+	}
+
+	int clientFirstStep(bool)
+	{
+		return clientTryAgain();
+	}
+
+	bool serverStart(const QString &, QStringList *, const QString &)
+	{
+		return false;
+	}
+
+	int serverFirstStep(const QString &, const QByteArray *)
+	{
+		return Error;
+	}
+
+	QCA_SASLNeedParams clientParamsNeeded() const
+	{
+		return need;
+	}
+
+	void setClientParams(const QString *_user, const QString *_authzid, const QString *_pass, const QString *_realm)
+	{
+		if(_user) {
+			user = *_user;
+			need.user = false;
+			have.user = true;
+		}
+		if(_authzid) {
+			authz = *_authzid;
+			need.authzid = false;
+			have.authzid = true;
+		}
+		if(_pass) {
+			pass = *_pass;
+			need.pass = false;
+			have.pass = true;
+		}
+		if(_realm) {
+			realm = *_realm;
+			need.realm = false;
+			have.realm = true;
+		}
+	}
+
+	QString username() const
+	{
+		return QString();
+	}
+
+	QString authzid() const
+	{
+		return QString();
+	}
+
+	int nextStep(const QByteArray &in)
+	{
+		in_buf = in.copy();
+		return tryAgain();
+	}
+
+	int tryAgain()
+	{
+		return clientTryAgain();
+	}
+
+	QString mech() const
+	{
+		return out_mech;
+	}
+
+	const QByteArray *clientInit() const
+	{
+		return 0;
+	}
+
+	QByteArray result() const
+	{
+		return out_buf;
+	}
+
+	int clientTryAgain()
+	{
+		if(step == 0) {
+			out_mech = "DIGEST-MD5";
+			++step;
+			return Continue;
+		}
+		else if(step == 1) {
+			// if we still need params, then the app has failed us!
+			if(need.user || need.authzid || need.pass || need.realm) {
+				err = -1;
+				return Error;
+			}
+
+			// see if some params are needed
+			if(!have.user)
+				need.user = true;
+			if(!have.authzid)
+				need.authzid = true;
+			if(!have.pass)
+				need.pass = true;
+			if(need.user || need.authzid || need.pass)
+				return NeedParams;
+
+			// get props
+			QCString cs(in_buf.data(), in_buf.size()+1);
+			PropList in;
+			if(!in.fromString(cs)) {
+				err = QCA::SASL::BadProto;
+				return Error;
+			}
+
+			// make a cnonce
+			QByteArray a(32);
+			for(int n = 0; n < (int)a.size(); ++n)
+				a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
+			QCString cnonce = Base64::arrayToString(a).latin1();
+
+			// make other variables
+			realm = host;
+			QCString nonce = in.get("nonce");
+			QCString nc = "00000001";
+			QCString uri = service.utf8() + '/' + host.utf8();
+			QCString qop = "auth";
+
+			// build 'response'
+			QCString X = user.utf8() + ':' + realm.utf8() + ':' + pass.utf8();
+			QByteArray Y = QCA::MD5::hash(X);
+			QCString tmp = QCString(":") + nonce + ':' + cnonce + ':' + authz.utf8();
+			QByteArray A1(Y.size() + tmp.length());
+			memcpy(A1.data(), Y.data(), Y.size());
+			memcpy(A1.data() + Y.size(), tmp.data(), tmp.length());
+			QCString A2 = "AUTHENTICATE:" + uri;
+			QCString HA1 = QCA::MD5::hashToString(A1).latin1();
+			QCString HA2 = QCA::MD5::hashToString(A2).latin1();
+			QCString KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + HA2;
+			QCString Z = QCA::MD5::hashToString(KD).latin1();
+
+			// build output
+			PropList out;
+			out.set("username", user.utf8());
+			out.set("realm", host.utf8());
+			out.set("nonce", nonce);
+			out.set("cnonce", cnonce);
+			out.set("nc", nc);
+			out.set("serv-type", service.utf8());
+			out.set("host", host.utf8());
+			out.set("digest-uri", uri);
+			out.set("qop", qop);
+			out.set("response", Z);
+			out.set("charset", "utf-8");
+			out.set("authzid", authz.utf8());
+			QCString s = out.toString();
+
+			// done
+			out_buf.resize(s.length());
+			memcpy(out_buf.data(), s.data(), out_buf.size());
+			++step;
+			return Continue;
+		}
+		else {
+			out_buf.resize(0);
+			return Success;
+		}
+	}
+
+	bool encode(const QByteArray &a, QByteArray *b)
+	{
+		*b = a.copy();
+		return true;
+	}
+
+	bool decode(const QByteArray &a, QByteArray *b)
+	{
+		*b = a.copy();
+		return true;
+	}
+};
+
+class QCASimpleSASL : public QCAProvider
+{
+public:
+	QCASimpleSASL() {}
+	~QCASimpleSASL() {}
+
+	void init()
+	{
+	}
+
+	int qcaVersion() const
+	{
+		return QCA_PLUGIN_VERSION;
+	}
+
+	int capabilities() const
+	{
+		return QCA::CAP_SASL;
+	}
+
+	void *context(int cap)
+	{
+		if(cap == QCA::CAP_SASL)
+			return new SimpleSASLContext;
+		return 0;
+	}
+};
+
+QCAProvider *createProviderSimpleSASL()
+{
+	return (new QCASimpleSASL);
+}
+
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/simplesasl.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/simplesasl.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/simplesasl.h	(revision 38)
@@ -0,0 +1,31 @@
+/*
+ * simplesasl.h - Simple SASL implementation
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef SIMPLESASL_H
+#define SIMPLESASL_H
+
+#include"qcaprovider.h"
+
+namespace XMPP
+{
+	QCAProvider *createProviderSimpleSASL();
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/stream.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/stream.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/stream.cpp	(revision 38)
@@ -0,0 +1,1732 @@
+/*
+ * stream.cpp - handles a client stream
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+  Notes:
+    - For Non-SASL auth (JEP-0078), username and resource fields are required.
+
+  TODO:
+    - sasl needParams is totally jacked?  PLAIN requires authzid, etc
+    - server error handling
+      - reply with protocol errors if the client send something wrong
+      - don't necessarily disconnect on protocol error.  prepare for more.
+    - server function
+      - deal with stream 'to' attribute dynamically
+      - flag tls/sasl/binding support dynamically (have the ability to specify extra stream:features)
+      - inform the caller about the user authentication information
+      - sasl security settings
+      - resource-binding interaction
+      - timeouts
+    - allow exchanges of non-standard stanzas
+    - send </stream:stream> even if we close prematurely?
+    - ensure ClientStream and child classes are fully deletable after signals
+    - xml:lang in root (<stream>) element
+    - sasl external
+    - sasl anonymous
+*/
+
+#include"xmpp.h"
+
+#include<qtextstream.h>
+#include<qguardedptr.h>
+#include<qtimer.h>
+#include<qca.h>
+#include<stdlib.h>
+#include"bytestream.h"
+#include"base64.h"
+#include"hash.h"
+#include"simplesasl.h"
+#include"securestream.h"
+#include"protocol.h"
+
+#ifdef XMPP_TEST
+#include"td.h"
+#endif
+
+//#define XMPP_DEBUG
+
+using namespace XMPP;
+
+static Debug *debug_ptr = 0;
+void XMPP::setDebug(Debug *p)
+{
+	debug_ptr = p;
+}
+
+static QByteArray randomArray(int size)
+{
+	QByteArray a(size);
+	for(int n = 0; n < size; ++n)
+		a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
+	return a;
+}
+
+static QString genId()
+{
+	// need SHA1 here
+	if(!QCA::isSupported(QCA::CAP_SHA1))
+		QCA::insertProvider(createProviderHash());
+
+	return QCA::SHA1::hashToString(randomArray(128));
+}
+
+//----------------------------------------------------------------------------
+// Stanza
+//----------------------------------------------------------------------------
+Stanza::Error::Error(int _type, int _condition, const QString &_text, const QDomElement &_appSpec)
+{
+	type = _type;
+	condition = _condition;
+	text = _text;
+	appSpec = _appSpec;
+}
+
+class Stanza::Private
+{
+public:
+	struct ErrorTypeEntry
+	{
+		const char *str;
+		int type;
+	};
+	static ErrorTypeEntry errorTypeTable[];
+
+	struct ErrorCondEntry
+	{
+		const char *str;
+		int cond;
+	};
+	static ErrorCondEntry errorCondTable[];
+
+	static int stringToKind(const QString &s)
+	{
+		if(s == "message")
+			return Message;
+		else if(s == "presence")
+			return Presence;
+		else if(s == "iq")
+			return IQ;
+		else
+			return -1;
+	}
+
+	static QString kindToString(Kind k)
+	{
+		if(k == Message)
+			return "message";
+		else if(k == Presence)
+			return "presence";
+		else
+			return "iq";
+	}
+
+	static int stringToErrorType(const QString &s)
+	{
+		for(int n = 0; errorTypeTable[n].str; ++n) {
+			if(s == errorTypeTable[n].str)
+				return errorTypeTable[n].type;
+		}
+		return -1;
+	}
+
+	static QString errorTypeToString(int x)
+	{
+		for(int n = 0; errorTypeTable[n].str; ++n) {
+			if(x == errorTypeTable[n].type)
+				return errorTypeTable[n].str;
+		}
+		return QString();
+	}
+
+	static int stringToErrorCond(const QString &s)
+	{
+		for(int n = 0; errorCondTable[n].str; ++n) {
+			if(s == errorCondTable[n].str)
+				return errorCondTable[n].cond;
+		}
+		return -1;
+	}
+
+	static QString errorCondToString(int x)
+	{
+		for(int n = 0; errorCondTable[n].str; ++n) {
+			if(x == errorCondTable[n].cond)
+				return errorCondTable[n].str;
+		}
+		return QString();
+	}
+
+	Stream *s;
+	QDomElement e;
+};
+
+Stanza::Private::ErrorTypeEntry Stanza::Private::errorTypeTable[] =
+{
+	{ "cancel",   Cancel },
+	{ "continue", Continue },
+	{ "modify",   Modify },
+	{ "auth",     Auth },
+	{ "wait",     Wait },
+	{ 0, 0 },
+};
+
+Stanza::Private::ErrorCondEntry Stanza::Private::errorCondTable[] =
+{
+	{ "bad-request",             BadRequest },
+	{ "conflict",                Conflict },
+	{ "feature-not-implemented", FeatureNotImplemented },
+	{ "forbidden",               Forbidden },
+	{ "internal-server-error",   InternalServerError },
+	{ "item-not-found",          ItemNotFound },
+	{ "jid-malformed",           JidMalformed },
+	{ "not-allowed",             NotAllowed },
+	{ "payment-required",        PaymentRequired },
+	{ "recipient-unavailable",   RecipientUnavailable },
+	{ "registration-required",   RegistrationRequired },
+	{ "remote-server-not-found", ServerNotFound },
+	{ "remote-server-timeout",   ServerTimeout },
+	{ "resource-constraint",     ResourceConstraint },
+	{ "service-unavailable",     ServiceUnavailable },
+	{ "subscription-required",   SubscriptionRequired },
+	{ "undefined-condition",     UndefinedCondition },
+	{ "unexpected-request",      UnexpectedRequest },
+	{ 0, 0 },
+};
+
+Stanza::Stanza()
+{
+	d = 0;
+}
+
+Stanza::Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id)
+{
+	d = new Private;
+
+	Kind kind;
+	if(k == Message || k == Presence || k == IQ)
+		kind = k;
+	else
+		kind = Message;
+
+	d->s = s;
+	d->e = d->s->doc().createElementNS(s->baseNS(), Private::kindToString(kind));
+	if(to.isValid())
+		setTo(to);
+	if(!type.isEmpty())
+		setType(type);
+	if(!id.isEmpty())
+		setId(id);
+}
+
+Stanza::Stanza(Stream *s, const QDomElement &e)
+{
+	d = 0;
+	if(e.namespaceURI() != s->baseNS())
+		return;
+	int x = Private::stringToKind(e.tagName());
+	if(x == -1)
+		return;
+	d = new Private;
+	d->s = s;
+	d->e = e;
+}
+
+Stanza::Stanza(const Stanza &from)
+{
+	d = 0;
+	*this = from;
+}
+
+Stanza & Stanza::operator=(const Stanza &from)
+{
+	delete d;
+	d = 0;
+	if(from.d)
+		d = new Private(*from.d);
+	return *this;
+}
+
+Stanza::~Stanza()
+{
+	delete d;
+}
+
+bool Stanza::isNull() const
+{
+	return (d ? false: true);
+}
+
+QDomElement Stanza::element() const
+{
+	return d->e;
+}
+
+QString Stanza::toString() const
+{
+	return Stream::xmlToString(d->e);
+}
+
+QDomDocument & Stanza::doc() const
+{
+	return d->s->doc();
+}
+
+QString Stanza::baseNS() const
+{
+	return d->s->baseNS();
+}
+
+QDomElement Stanza::createElement(const QString &ns, const QString &tagName)
+{
+	return d->s->doc().createElementNS(ns, tagName);
+}
+
+QDomElement Stanza::createTextElement(const QString &ns, const QString &tagName, const QString &text)
+{
+	QDomElement e = d->s->doc().createElementNS(ns, tagName);
+	e.appendChild(d->s->doc().createTextNode(text));
+	return e;
+}
+
+void Stanza::appendChild(const QDomElement &e)
+{
+	d->e.appendChild(e);
+}
+
+Stanza::Kind Stanza::kind() const
+{
+	return (Kind)Private::stringToKind(d->e.tagName());
+}
+
+void Stanza::setKind(Kind k)
+{
+	d->e.setTagName(Private::kindToString(k));
+}
+
+Jid Stanza::to() const
+{
+	return Jid(d->e.attribute("to"));
+}
+
+Jid Stanza::from() const
+{
+	return Jid(d->e.attribute("from"));
+}
+
+QString Stanza::id() const
+{
+	return d->e.attribute("id");
+}
+
+QString Stanza::type() const
+{
+	return d->e.attribute("type");
+}
+
+QString Stanza::lang() const
+{
+	return d->e.attributeNS(NS_XML, "lang", QString());
+}
+
+void Stanza::setTo(const Jid &j)
+{
+	d->e.setAttribute("to", j.full());
+}
+
+void Stanza::setFrom(const Jid &j)
+{
+	d->e.setAttribute("from", j.full());
+}
+
+void Stanza::setId(const QString &id)
+{
+	d->e.setAttribute("id", id);
+}
+
+void Stanza::setType(const QString &type)
+{
+	d->e.setAttribute("type", type);
+}
+
+void Stanza::setLang(const QString &lang)
+{
+	d->e.setAttribute("xml:lang", lang);
+}
+
+Stanza::Error Stanza::error() const
+{
+	Error err;
+	QDomElement e = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
+	if(e.isNull())
+		return err;
+
+	// type
+	int x = Private::stringToErrorType(e.attribute("type"));
+	if(x != -1)
+		err.type = x;
+
+	// condition: find first element
+	QDomNodeList nl = e.childNodes();
+	QDomElement t;
+	uint n;
+	for(n = 0; n < nl.count(); ++n) {
+		QDomNode i = nl.item(n);
+		if(i.isElement()) {
+			t = i.toElement();
+			break;
+		}
+	}
+	if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
+		x = Private::stringToErrorCond(t.tagName());
+		if(x != -1)
+			err.condition = x;
+	}
+
+	// text
+	t = e.elementsByTagNameNS(NS_STANZAS, "text").item(0).toElement();
+	if(!t.isNull())
+		err.text = t.text();
+	else
+		err.text = e.text();
+
+	// appspec: find first non-standard namespaced element
+	nl = e.childNodes();
+	for(n = 0; n < nl.count(); ++n) {
+		QDomNode i = nl.item(n);
+		if(i.isElement() && i.namespaceURI() != NS_STANZAS) {
+			err.appSpec = i.toElement();
+			break;
+		}
+	}
+	return err;
+}
+
+void Stanza::setError(const Error &err)
+{
+	// create the element if necessary
+	QDomElement errElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
+	if(errElem.isNull()) {
+		errElem = d->e.ownerDocument().createElementNS(d->s->baseNS(), "error");
+		d->e.appendChild(errElem);
+	}
+
+	// error type/condition
+	if(d->s->old()) {
+		errElem.setAttribute("code", QString::number(err.condition));
+	}
+	else {
+		QString stype = Private::errorTypeToString(err.type);
+		if(stype.isEmpty())
+			return;
+		QString scond = Private::errorCondToString(err.condition);
+		if(scond.isEmpty())
+			return;
+
+		errElem.setAttribute("type", stype);
+		errElem.appendChild(d->e.ownerDocument().createElementNS(d->s->baseNS(), scond));
+	}
+
+	// text
+	if(d->s->old()) {
+		errElem.appendChild(d->e.ownerDocument().createTextNode(err.text));
+	}
+	else {
+		QDomElement te = d->e.ownerDocument().createElementNS(d->s->baseNS(), "text");
+		te.appendChild(d->e.ownerDocument().createTextNode(err.text));
+		errElem.appendChild(te);
+	}
+
+	// application specific
+	errElem.appendChild(err.appSpec);
+}
+
+void Stanza::clearError()
+{
+	QDomElement errElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
+	if(!errElem.isNull())
+		d->e.removeChild(errElem);
+}
+
+//----------------------------------------------------------------------------
+// Stream
+//----------------------------------------------------------------------------
+static XmlProtocol *foo = 0;
+Stream::Stream(QObject *parent)
+:QObject(parent)
+{
+}
+
+Stream::~Stream()
+{
+}
+
+Stanza Stream::createStanza(Stanza::Kind k, const Jid &to, const QString &type, const QString &id)
+{
+	return Stanza(this, k, to, type, id);
+}
+
+Stanza Stream::createStanza(const QDomElement &e)
+{
+	return Stanza(this, e);
+}
+
+QString Stream::xmlToString(const QDomElement &e, bool clip)
+{
+	if(!foo)
+		foo = new CoreProtocol;
+	return foo->elementToString(e, clip);
+}
+
+//----------------------------------------------------------------------------
+// ClientStream
+//----------------------------------------------------------------------------
+enum {
+	Idle,
+	Connecting,
+	WaitVersion,
+	WaitTLS,
+	NeedParams,
+	Active,
+	Closing
+};
+
+enum {
+	Client,
+	Server
+};
+
+class ClientStream::Private
+{
+public:
+	Private()
+	{
+		conn = 0;
+		bs = 0;
+		ss = 0;
+		tlsHandler = 0;
+		tls = 0;
+		sasl = 0;
+		in.setAutoDelete(true);
+
+		oldOnly = false;
+		allowPlain = false;
+		mutualAuth = false;
+		haveLocalAddr = false;
+		minimumSSF = 0;
+		maximumSSF = 0;
+		doBinding = true;
+
+		in_rrsig = false;
+
+		reset();
+	}
+
+	void reset()
+	{
+		state = Idle;
+		notify = 0;
+		newStanzas = false;
+		sasl_ssf = 0;
+		tls_warned = false;
+		using_tls = false;
+	}
+
+	Jid jid;
+	QString server;
+	bool oldOnly;
+	bool allowPlain, mutualAuth;
+	bool haveLocalAddr;
+	QHostAddress localAddr;
+	Q_UINT16 localPort;
+	int minimumSSF, maximumSSF;
+	QString sasl_mech;
+	bool doBinding;
+
+	bool in_rrsig;
+
+	Connector *conn;
+	ByteStream *bs;
+	TLSHandler *tlsHandler;
+	QCA::TLS *tls;
+	QCA::SASL *sasl;
+	SecureStream *ss;
+	CoreProtocol client;
+	CoreProtocol srv;
+
+	QString defRealm;
+
+	int mode;
+	int state;
+	int notify;
+	bool newStanzas;
+	int sasl_ssf;
+	bool tls_warned, using_tls;
+	bool doAuth;
+
+	QStringList sasl_mechlist;
+
+	int errCond;
+	QString errText;
+	QDomElement errAppSpec;
+
+	QPtrList<Stanza> in;
+
+	QTimer noopTimer;
+	int noop_time;
+};
+
+ClientStream::ClientStream(Connector *conn, TLSHandler *tlsHandler, QObject *parent)
+:Stream(parent)
+{
+	d = new Private;
+	d->mode = Client;
+	d->conn = conn;
+	connect(d->conn, SIGNAL(connected()), SLOT(cr_connected()));
+	connect(d->conn, SIGNAL(error()), SLOT(cr_error()));
+
+	d->noop_time = 0;
+	connect(&d->noopTimer, SIGNAL(timeout()), SLOT(doNoop()));
+
+	d->tlsHandler = tlsHandler;
+}
+
+ClientStream::ClientStream(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls, QObject *parent)
+:Stream(parent)
+{
+	d = new Private;
+	d->mode = Server;
+	d->bs = bs;
+	connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+	connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished()));
+	connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int)));
+
+	QByteArray spare = d->bs->read();
+
+	d->ss = new SecureStream(d->bs);
+	connect(d->ss, SIGNAL(readyRead()), SLOT(ss_readyRead()));
+	connect(d->ss, SIGNAL(bytesWritten(int)), SLOT(ss_bytesWritten(int)));
+	connect(d->ss, SIGNAL(tlsHandshaken()), SLOT(ss_tlsHandshaken()));
+	connect(d->ss, SIGNAL(tlsClosed()), SLOT(ss_tlsClosed()));
+	connect(d->ss, SIGNAL(error(int)), SLOT(ss_error(int)));
+
+	d->server = host;
+	d->defRealm = defRealm;
+
+	d->tls = tls;
+
+	d->srv.startClientIn(genId());
+	//d->srv.startServerIn(genId());
+	//d->state = Connecting;
+	//d->jid = Jid();
+	//d->server = QString();
+}
+
+ClientStream::~ClientStream()
+{
+	reset();
+	delete d;
+}
+
+void ClientStream::reset(bool all)
+{
+	d->reset();
+	d->noopTimer.stop();
+
+	// delete securestream
+	delete d->ss;
+	d->ss = 0;
+
+	// reset sasl
+	delete d->sasl;
+	d->sasl = 0;
+
+	// client
+	if(d->mode == Client) {
+		// reset tls
+		if(d->tlsHandler)
+			d->tlsHandler->reset();
+
+		// reset connector
+		if(d->bs) {
+			d->bs->close();
+			d->bs = 0;
+		}
+		d->conn->done();
+
+		// reset state machine
+		d->client.reset();
+	}
+	// server
+	else {
+		if(d->tls)
+			d->tls->reset();
+
+		if(d->bs) {
+			d->bs->close();
+			d->bs = 0;
+		}
+
+		d->srv.reset();
+	}
+
+	if(all)
+		d->in.clear();
+}
+
+Jid ClientStream::jid() const
+{
+	return d->jid;
+}
+
+void ClientStream::connectToServer(const Jid &jid, bool auth)
+{
+	reset(true);
+	d->state = Connecting;
+	d->jid = jid;
+	d->doAuth = auth;
+	d->server = d->jid.domain();
+
+	d->conn->connectToServer(d->server);
+}
+
+void ClientStream::continueAfterWarning()
+{
+	if(d->state == WaitVersion) {
+		// if we don't have TLS yet, then we're never going to get it
+		if(!d->tls_warned && !d->using_tls) {
+			d->tls_warned = true;
+			d->state = WaitTLS;
+			warning(WarnNoTLS);
+			return;
+		}
+		d->state = Connecting;
+		processNext();
+	}
+	else if(d->state == WaitTLS) {
+		d->state = Connecting;
+		processNext();
+	}
+}
+
+void ClientStream::accept()
+{
+	d->srv.host = d->server;
+	processNext();
+}
+
+bool ClientStream::isActive() const
+{
+	return (d->state != Idle) ? true: false;
+}
+
+bool ClientStream::isAuthenticated() const
+{
+	return (d->state == Active) ? true: false;
+}
+
+void ClientStream::setUsername(const QString &s)
+{
+	if(d->sasl)
+		d->sasl->setUsername(s);
+}
+
+void ClientStream::setPassword(const QString &s)
+{
+	if(d->client.old) {
+		d->client.setPassword(s);
+	}
+	else {
+		if(d->sasl)
+			d->sasl->setPassword(s);
+	}
+}
+
+void ClientStream::setRealm(const QString &s)
+{
+	if(d->sasl)
+		d->sasl->setRealm(s);
+}
+
+void ClientStream::continueAfterParams()
+{
+	if(d->state == NeedParams) {
+		d->state = Connecting;
+		if(d->client.old) {
+			processNext();
+		}
+		else {
+			if(d->sasl)
+				d->sasl->continueAfterParams();
+		}
+	}
+}
+
+void ClientStream::setResourceBinding(bool b)
+{
+	d->doBinding = b;
+}
+
+void ClientStream::setNoopTime(int mills)
+{
+	d->noop_time = mills;
+
+	if(d->state != Active)
+		return;
+
+	if(d->noop_time == 0) {
+		d->noopTimer.stop();
+		return;
+	}
+	d->noopTimer.start(d->noop_time);
+}
+
+QString ClientStream::saslMechanism() const
+{
+	return d->client.saslMech();
+}
+
+int ClientStream::saslSSF() const
+{
+	return d->sasl_ssf;
+}
+
+void ClientStream::setSASLMechanism(const QString &s)
+{
+	d->sasl_mech = s;
+}
+
+void ClientStream::setLocalAddr(const QHostAddress &addr, Q_UINT16 port)
+{
+	d->haveLocalAddr = true;
+	d->localAddr = addr;
+	d->localPort = port;
+}
+
+int ClientStream::errorCondition() const
+{
+	return d->errCond;
+}
+
+QString ClientStream::errorText() const
+{
+	return d->errText;
+}
+
+QDomElement ClientStream::errorAppSpec() const
+{
+	return d->errAppSpec;
+}
+
+bool ClientStream::old() const
+{
+	return d->client.old;
+}
+
+void ClientStream::close()
+{
+	if(d->state == Active) {
+		d->state = Closing;
+		d->client.shutdown();
+		processNext();
+	}
+	else if(d->state != Idle && d->state != Closing) {
+		reset();
+	}
+}
+
+QDomDocument & ClientStream::doc() const
+{
+	return d->client.doc;
+}
+
+QString ClientStream::baseNS() const
+{
+	return NS_CLIENT;
+}
+
+void ClientStream::setAllowPlain(bool b)
+{
+	d->allowPlain = b;
+}
+
+void ClientStream::setRequireMutualAuth(bool b)
+{
+	d->mutualAuth = b;
+}
+
+void ClientStream::setSSFRange(int low, int high)
+{
+	d->minimumSSF = low;
+	d->maximumSSF = high;
+}
+
+void ClientStream::setOldOnly(bool b)
+{
+	d->oldOnly = b;
+}
+
+bool ClientStream::stanzaAvailable() const
+{
+	return (!d->in.isEmpty());
+}
+
+Stanza ClientStream::read()
+{
+	if(d->in.isEmpty())
+		return Stanza();
+	else {
+		Stanza *sp = d->in.getFirst();
+		Stanza s = *sp;
+		d->in.removeRef(sp);
+		return s;
+	}
+}
+
+void ClientStream::write(const Stanza &s)
+{
+	if(d->state == Active) {
+		d->client.sendStanza(s.element());
+		processNext();
+	}
+}
+
+void ClientStream::cr_connected()
+{
+	d->bs = d->conn->stream();
+	connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+	connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished()));
+
+	QByteArray spare = d->bs->read();
+
+	d->ss = new SecureStream(d->bs);
+	connect(d->ss, SIGNAL(readyRead()), SLOT(ss_readyRead()));
+	connect(d->ss, SIGNAL(bytesWritten(int)), SLOT(ss_bytesWritten(int)));
+	connect(d->ss, SIGNAL(tlsHandshaken()), SLOT(ss_tlsHandshaken()));
+	connect(d->ss, SIGNAL(tlsClosed()), SLOT(ss_tlsClosed()));
+	connect(d->ss, SIGNAL(error(int)), SLOT(ss_error(int)));
+
+	//d->client.startDialbackOut("andbit.net", "im.pyxa.org");
+	//d->client.startServerOut(d->server);
+
+	d->client.startClientOut(d->jid, d->oldOnly, d->conn->useSSL(), d->doAuth);
+	d->client.setAllowTLS(d->tlsHandler ? true: false);
+	d->client.setAllowBind(d->doBinding);
+	d->client.setAllowPlain(d->allowPlain);
+
+	/*d->client.jid = d->jid;
+	d->client.server = d->server;
+	d->client.allowPlain = d->allowPlain;
+	d->client.oldOnly = d->oldOnly;
+	d->client.sasl_mech = d->sasl_mech;
+	d->client.doTLS = d->tlsHandler ? true: false;
+	d->client.doBinding = d->doBinding;*/
+
+	QGuardedPtr<QObject> self = this;
+	connected();
+	if(!self)
+		return;
+
+	// immediate SSL?
+	if(d->conn->useSSL()) {
+		d->using_tls = true;
+		d->ss->startTLSClient(d->tlsHandler, d->server, spare);
+	}
+	else {
+		d->client.addIncomingData(spare);
+		processNext();
+	}
+}
+
+void ClientStream::cr_error()
+{
+	reset();
+	error(ErrConnection);
+}
+
+void ClientStream::bs_connectionClosed()
+{
+	reset();
+	connectionClosed();
+}
+
+void ClientStream::bs_delayedCloseFinished()
+{
+	// we don't care about this (we track all important data ourself)
+}
+
+void ClientStream::bs_error(int)
+{
+	// TODO
+}
+
+void ClientStream::ss_readyRead()
+{
+	QByteArray a = d->ss->read();
+
+#ifdef XMPP_DEBUG
+	QCString cs(a.data(), a.size()+1);
+	fprintf(stderr, "ClientStream: recv: %d [%s]\n", a.size(), cs.data());
+#endif
+
+	if(d->mode == Client)
+		d->client.addIncomingData(a);
+	else
+		d->srv.addIncomingData(a);
+	if(d->notify & CoreProtocol::NRecv) {
+#ifdef XMPP_DEBUG
+		printf("We needed data, so let's process it\n");
+#endif
+		processNext();
+	}
+}
+
+void ClientStream::ss_bytesWritten(int bytes)
+{
+	if(d->mode == Client)
+		d->client.outgoingDataWritten(bytes);
+	else
+		d->srv.outgoingDataWritten(bytes);
+
+	if(d->notify & CoreProtocol::NSend) {
+#ifdef XMPP_DEBUG
+		printf("We were waiting for data to be written, so let's process\n");
+#endif
+		processNext();
+	}
+}
+
+void ClientStream::ss_tlsHandshaken()
+{
+	QGuardedPtr<QObject> self = this;
+	securityLayerActivated(LayerTLS);
+	if(!self)
+		return;
+	processNext();
+}
+
+void ClientStream::ss_tlsClosed()
+{
+	reset();
+	connectionClosed();
+}
+
+void ClientStream::ss_error(int x)
+{
+	if(x == SecureStream::ErrTLS) {
+		reset();
+		d->errCond = TLSFail;
+		error(ErrTLS);
+	}
+	else {
+		reset();
+		error(ErrSecurityLayer);
+	}
+}
+
+void ClientStream::sasl_clientFirstStep(const QString &mech, const QByteArray *stepData)
+{
+	d->client.setSASLFirst(mech, stepData ? *stepData : QByteArray());
+	//d->client.sasl_mech = mech;
+	//d->client.sasl_firstStep = stepData ? true : false;
+	//d->client.sasl_step = stepData ? *stepData : QByteArray();
+
+	processNext();
+}
+
+void ClientStream::sasl_nextStep(const QByteArray &stepData)
+{
+	if(d->mode == Client)
+		d->client.setSASLNext(stepData);
+		//d->client.sasl_step = stepData;
+	else
+		d->srv.setSASLNext(stepData);
+		//d->srv.sasl_step = stepData;
+
+	processNext();
+}
+
+void ClientStream::sasl_needParams(bool user, bool authzid, bool pass, bool realm)
+{
+#ifdef XMPP_DEBUG
+	printf("need params: %d,%d,%d,%d\n", user, authzid, pass, realm);
+#endif
+	if(authzid) {
+		d->sasl->setAuthzid(d->jid.bare());
+		//d->sasl->setAuthzid("infiniti.homelesshackers.org");
+	}
+	if(user || pass || realm) {
+		d->state = NeedParams;
+		needAuthParams(user, pass, realm);
+	}
+	else
+		d->sasl->continueAfterParams();
+}
+
+void ClientStream::sasl_authCheck(const QString &user, const QString &)
+{
+//#ifdef XMPP_DEBUG
+//	printf("authcheck: [%s], [%s]\n", user.latin1(), authzid.latin1());
+//#endif
+	QString u = user;
+	int n = u.find('@');
+	if(n != -1)
+		u.truncate(n);
+	d->srv.user = u;
+	d->sasl->continueAfterAuthCheck();
+}
+
+void ClientStream::sasl_authenticated()
+{
+#ifdef XMPP_DEBUG
+	printf("sasl authed!!\n");
+#endif
+	d->sasl_ssf = d->sasl->ssf();
+
+	if(d->mode == Server) {
+		d->srv.setSASLAuthed();
+		processNext();
+	}
+}
+
+void ClientStream::sasl_error(int)
+{
+//#ifdef XMPP_DEBUG
+//	printf("sasl error: %d\n", c);
+//#endif
+	// has to be auth error
+	int x = convertedSASLCond();
+	reset();
+	d->errCond = x;
+	error(ErrAuth);
+}
+
+void ClientStream::srvProcessNext()
+{
+	while(1) {
+		printf("Processing step...\n");
+		if(!d->srv.processStep()) {
+			int need = d->srv.need;
+			if(need == CoreProtocol::NNotify) {
+				d->notify = d->srv.notify;
+				if(d->notify & CoreProtocol::NSend)
+					printf("More data needs to be written to process next step\n");
+				if(d->notify & CoreProtocol::NRecv)
+					printf("More data is needed to process next step\n");
+			}
+			else if(need == CoreProtocol::NSASLMechs) {
+				if(!d->sasl) {
+					d->sasl = new QCA::SASL;
+					connect(d->sasl, SIGNAL(authCheck(const QString &, const QString &)), SLOT(sasl_authCheck(const QString &, const QString &)));
+					connect(d->sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &)));
+					connect(d->sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated()));
+					connect(d->sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+
+					//d->sasl->setAllowAnonymous(false);
+					//d->sasl->setRequirePassCredentials(true);
+					//d->sasl->setExternalAuthID("localhost");
+
+					d->sasl->setMinimumSSF(0);
+					d->sasl->setMaximumSSF(256);
+
+					QStringList list;
+					// TODO: d->server is probably wrong here
+					if(!d->sasl->startServer("xmpp", d->server, d->defRealm, &list)) {
+						printf("Error initializing SASL\n");
+						return;
+					}
+					d->sasl_mechlist = list;
+				}
+				d->srv.setSASLMechList(d->sasl_mechlist);
+				continue;
+			}
+			else if(need == CoreProtocol::NStartTLS) {
+				printf("Need StartTLS\n");
+				if(!d->tls->startServer()) {
+					printf("unable to start server!\n");
+					// TODO
+					return;
+				}
+				QByteArray a = d->srv.spare;
+				d->ss->startTLSServer(d->tls, a);
+			}
+			else if(need == CoreProtocol::NSASLFirst) {
+				printf("Need SASL First Step\n");
+				QByteArray a = d->srv.saslStep();
+				d->sasl->putServerFirstStep(d->srv.saslMech(), a);
+			}
+			else if(need == CoreProtocol::NSASLNext) {
+				printf("Need SASL Next Step\n");
+				QByteArray a = d->srv.saslStep();
+				QCString cs(a.data(), a.size()+1);
+				printf("[%s]\n", cs.data());
+				d->sasl->putStep(a);
+			}
+			else if(need == CoreProtocol::NSASLLayer) {
+			}
+
+			// now we can announce stanzas
+			//if(!d->in.isEmpty())
+			//	readyRead();
+			return;
+		}
+
+		d->notify = 0;
+
+		int event = d->srv.event;
+		printf("event: %d\n", event);
+		switch(event) {
+			case CoreProtocol::EError: {
+				printf("Error! Code=%d\n", d->srv.errorCode);
+				reset();
+				error(ErrProtocol);
+				//handleError();
+				return;
+			}
+			case CoreProtocol::ESend: {
+				QByteArray a = d->srv.takeOutgoingData();
+				QCString cs(a.size()+1);
+				memcpy(cs.data(), a.data(), a.size());
+				printf("Need Send: {%s}\n", cs.data());
+				d->ss->write(a);
+				break;
+			}
+			case CoreProtocol::ERecvOpen: {
+				printf("Break (RecvOpen)\n");
+
+				// calculate key
+				QCString str = QCA::SHA1::hashToString("secret").utf8();
+				str = QCA::SHA1::hashToString(str + "im.pyxa.org").utf8();
+				str = QCA::SHA1::hashToString(str + d->srv.id.utf8()).utf8();
+				d->srv.setDialbackKey(str);
+
+				//d->srv.setDialbackKey("3c5d721ea2fcc45b163a11420e4e358f87e3142a");
+
+				if(d->srv.to != d->server) {
+					// host-gone, host-unknown, see-other-host
+					d->srv.shutdownWithError(CoreProtocol::HostUnknown);
+				}
+				else
+					d->srv.setFrom(d->server);
+				break;
+			}
+			case CoreProtocol::ESASLSuccess: {
+				printf("Break SASL Success\n");
+				disconnect(d->sasl, SIGNAL(error(int)), this, SLOT(sasl_error(int)));
+				QByteArray a = d->srv.spare;
+				d->ss->setLayerSASL(d->sasl, a);
+				break;
+			}
+			case CoreProtocol::EPeerClosed: {
+				// TODO: this isn' an error
+				printf("peer closed\n");
+				reset();
+				error(ErrProtocol);
+				return;
+			}
+		}
+	}
+}
+
+void ClientStream::doReadyRead()
+{
+	//QGuardedPtr<QObject> self = this;
+	readyRead();
+	//if(!self)
+	//	return;
+	//d->in_rrsig = false;
+}
+
+void ClientStream::processNext()
+{
+	if(d->mode == Server) {
+		srvProcessNext();
+		return;
+	}
+
+	QGuardedPtr<QObject> self = this;
+
+	while(1) {
+#ifdef XMPP_DEBUG
+		printf("Processing step...\n");
+#endif
+		bool ok = d->client.processStep();
+		// deal with send/received items
+		for(QValueList<XmlProtocol::TransferItem>::ConstIterator it = d->client.transferItemList.begin(); it != d->client.transferItemList.end(); ++it) {
+			const XmlProtocol::TransferItem &i = *it;
+			if(i.isExternal)
+				continue;
+			QString str;
+			if(i.isString) {
+				// skip whitespace pings
+				if(i.str.stripWhiteSpace().isEmpty())
+					continue;
+				str = i.str;
+			}
+			else
+				str = d->client.elementToString(i.elem);
+			if(i.isSent)
+				outgoingXml(str);
+			else
+				incomingXml(str);
+		}
+
+		if(!ok) {
+			bool cont = handleNeed();
+
+			// now we can announce stanzas
+			//if(!d->in_rrsig && !d->in.isEmpty()) {
+			if(!d->in.isEmpty()) {
+				//d->in_rrsig = true;
+				QTimer::singleShot(0, this, SLOT(doReadyRead()));
+			}
+
+			if(cont)
+				continue;
+			return;
+		}
+
+		int event = d->client.event;
+		d->notify = 0;
+		switch(event) {
+			case CoreProtocol::EError: {
+#ifdef XMPP_DEBUG
+				printf("Error! Code=%d\n", d->client.errorCode);
+#endif
+				handleError();
+				return;
+			}
+			case CoreProtocol::ESend: {
+				QByteArray a = d->client.takeOutgoingData();
+#ifdef XMPP_DEBUG
+				QCString cs(a.size()+1);
+				memcpy(cs.data(), a.data(), a.size());
+				printf("Need Send: {%s}\n", cs.data());
+#endif
+				d->ss->write(a);
+				break;
+			}
+			case CoreProtocol::ERecvOpen: {
+#ifdef XMPP_DEBUG
+				printf("Break (RecvOpen)\n");
+#endif
+
+#ifdef XMPP_TEST
+				QString s = QString("handshake success (lang=[%1]").arg(d->client.lang);
+				if(!d->client.from.isEmpty())
+					s += QString(", from=[%1]").arg(d->client.from);
+				s += ')';
+				TD::msg(s);
+#endif
+
+				if(d->client.old) {
+					d->state = WaitVersion;
+					warning(WarnOldVersion);
+					return;
+				}
+				break;
+			}
+			case CoreProtocol::EFeatures: {
+#ifdef XMPP_DEBUG
+				printf("Break (Features)\n");
+#endif
+				if(!d->tls_warned && !d->using_tls && !d->client.features.tls_supported) {
+					d->tls_warned = true;
+					d->state = WaitTLS;
+					warning(WarnNoTLS);
+					return;
+				}
+				break;
+			}
+			case CoreProtocol::ESASLSuccess: {
+#ifdef XMPP_DEBUG
+				printf("Break SASL Success\n");
+#endif
+				break;
+			}
+			case CoreProtocol::EReady: {
+#ifdef XMPP_DEBUG
+				printf("Done!\n");
+#endif
+				// grab the JID, in case it changed
+				// TODO: d->jid = d->client.jid;
+				d->state = Active;
+				setNoopTime(d->noop_time);
+				authenticated();
+				if(!self)
+					return;
+				break;
+			}
+			case CoreProtocol::EPeerClosed: {
+#ifdef XMPP_DEBUG
+				printf("DocumentClosed\n");
+#endif
+				reset();
+				connectionClosed();
+				return;
+			}
+			case CoreProtocol::EStanzaReady: {
+#ifdef XMPP_DEBUG
+				printf("StanzaReady\n");
+#endif
+				// store the stanza for now, announce after processing all events
+				Stanza s = createStanza(d->client.recvStanza());
+				if(s.isNull())
+					break;
+				d->in.append(new Stanza(s));
+				break;
+			}
+			case CoreProtocol::EStanzaSent: {
+#ifdef XMPP_DEBUG
+				printf("StanzasSent\n");
+#endif
+				stanzaWritten();
+				if(!self)
+					return;
+				break;
+			}
+			case CoreProtocol::EClosed: {
+#ifdef XMPP_DEBUG
+				printf("Closed\n");
+#endif
+				reset();
+				delayedCloseFinished();
+				return;
+			}
+		}
+	}
+}
+
+bool ClientStream::handleNeed()
+{
+	int need = d->client.need;
+	if(need == CoreProtocol::NNotify) {
+		d->notify = d->client.notify;
+#ifdef XMPP_DEBUG
+		if(d->notify & CoreProtocol::NSend)
+			printf("More data needs to be written to process next step\n");
+		if(d->notify & CoreProtocol::NRecv)
+			printf("More data is needed to process next step\n");
+#endif
+		return false;
+	}
+
+	d->notify = 0;
+	switch(need) {
+		case CoreProtocol::NStartTLS: {
+#ifdef XMPP_DEBUG
+			printf("Need StartTLS\n");
+#endif
+			d->using_tls = true;
+			d->ss->startTLSClient(d->tlsHandler, d->server, d->client.spare);
+			return false;
+		}
+		case CoreProtocol::NSASLFirst: {
+#ifdef XMPP_DEBUG
+			printf("Need SASL First Step\n");
+#endif
+			// no SASL plugin?  fall back to Simple SASL
+			if(!QCA::isSupported(QCA::CAP_SASL)) {
+				// Simple SASL needs MD5.  do we have that either?
+				if(!QCA::isSupported(QCA::CAP_MD5))
+					QCA::insertProvider(createProviderHash());
+				QCA::insertProvider(createProviderSimpleSASL());
+			}
+
+			d->sasl = new QCA::SASL;
+			connect(d->sasl, SIGNAL(clientFirstStep(const QString &, const QByteArray *)), SLOT(sasl_clientFirstStep(const QString &, const QByteArray *)));
+			connect(d->sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &)));
+			connect(d->sasl, SIGNAL(needParams(bool, bool, bool, bool)), SLOT(sasl_needParams(bool, bool, bool, bool)));
+			connect(d->sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated()));
+			connect(d->sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+
+			if(d->haveLocalAddr)
+				d->sasl->setLocalAddr(d->localAddr, d->localPort);
+			if(d->conn->havePeerAddress())
+				d->sasl->setRemoteAddr(d->conn->peerAddress(), d->conn->peerPort());
+
+			d->sasl->setAllowAnonymous(false);
+
+			//d->sasl_mech = "ANONYMOUS";
+			//d->sasl->setRequirePassCredentials(true);
+
+			//d->sasl->setExternalAuthID("localhost");
+			//d->sasl->setExternalSSF(64);
+			//d->sasl_mech = "EXTERNAL";
+
+			d->sasl->setAllowPlain(d->allowPlain);
+			d->sasl->setRequireMutualAuth(d->mutualAuth);
+
+			d->sasl->setMinimumSSF(d->minimumSSF);
+			d->sasl->setMaximumSSF(d->maximumSSF);
+
+			QStringList ml;
+			if(!d->sasl_mech.isEmpty())
+				ml += d->sasl_mech;
+			else
+				ml = d->client.features.sasl_mechs;
+
+			if(!d->sasl->startClient("xmpp", d->server, ml, true)) {
+				int x = convertedSASLCond();
+				reset();
+				d->errCond = x;
+				error(ErrAuth);
+				return false;
+			}
+			return false;
+		}
+		case CoreProtocol::NSASLNext: {
+#ifdef XMPP_DEBUG
+			printf("Need SASL Next Step\n");
+#endif
+			QByteArray a = d->client.saslStep();
+			d->sasl->putStep(a);
+			return false;
+		}
+		case CoreProtocol::NSASLLayer: {
+			// SecureStream will handle the errors from this point
+			disconnect(d->sasl, SIGNAL(error(int)), this, SLOT(sasl_error(int)));
+			d->ss->setLayerSASL(d->sasl, d->client.spare);
+			if(d->sasl_ssf > 0) {
+				QGuardedPtr<QObject> self = this;
+				securityLayerActivated(LayerSASL);
+				if(!self)
+					return false;
+			}
+			break;
+		}
+		case CoreProtocol::NPassword: {
+#ifdef XMPP_DEBUG
+			printf("Need Password\n");
+#endif
+			d->state = NeedParams;
+			needAuthParams(false, true, false);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+int ClientStream::convertedSASLCond() const
+{
+	int x = d->sasl->errorCondition();
+	if(x == QCA::SASL::NoMech)
+		return NoMech;
+	else if(x == QCA::SASL::BadProto)
+		return BadProto;
+	else if(x == QCA::SASL::BadServ)
+		return BadServ;
+	else if(x == QCA::SASL::TooWeak)
+		return MechTooWeak;
+	else
+		return GenericAuthError;
+}
+
+void ClientStream::doNoop()
+{
+	if(d->state == Active) {
+#ifdef XMPP_DEBUG
+		printf("doPing\n");
+#endif
+		d->client.sendWhitespace();
+		processNext();
+	}
+}
+
+void ClientStream::writeDirect(const QString &s)
+{
+	if(d->state == Active) {
+#ifdef XMPP_DEBUG
+		printf("writeDirect\n");
+#endif
+		d->client.sendDirect(s);
+		processNext();
+	}
+}
+
+void ClientStream::handleError()
+{
+	int c = d->client.errorCode;
+	if(c == CoreProtocol::ErrParse) {
+		reset();
+		error(ErrParse);
+	}
+	else if(c == CoreProtocol::ErrProtocol) {
+		reset();
+		error(ErrProtocol);
+	}
+	else if(c == CoreProtocol::ErrStream) {
+		int x = d->client.errCond;
+		QString text = d->client.errText;
+		QDomElement appSpec = d->client.errAppSpec;
+
+		int connErr = -1;
+		int strErr = -1;
+
+		switch(x) {
+			case CoreProtocol::BadFormat: { break; } // should NOT happen (we send the right format)
+			case CoreProtocol::BadNamespacePrefix: { break; } // should NOT happen (we send prefixes)
+			case CoreProtocol::Conflict: { strErr = Conflict; break; }
+			case CoreProtocol::ConnectionTimeout: { strErr = ConnectionTimeout; break; }
+			case CoreProtocol::HostGone: { connErr = HostGone; break; }
+			case CoreProtocol::HostUnknown: { connErr = HostUnknown; break; }
+			case CoreProtocol::ImproperAddressing: { break; } // should NOT happen (we aren't a server)
+			case CoreProtocol::InternalServerError: { strErr = InternalServerError;  break; }
+			case CoreProtocol::InvalidFrom: { strErr = InvalidFrom; break; }
+			case CoreProtocol::InvalidId: { break; } // should NOT happen (clients don't specify id)
+			case CoreProtocol::InvalidNamespace: { break; } // should NOT happen (we set the right ns)
+			case CoreProtocol::InvalidXml: { strErr = InvalidXml; break; } // shouldn't happen either, but just in case ...
+			case CoreProtocol::StreamNotAuthorized: { break; } // should NOT happen (we're not stupid)
+			case CoreProtocol::PolicyViolation: { strErr = PolicyViolation; break; }
+			case CoreProtocol::RemoteConnectionFailed: { connErr = RemoteConnectionFailed; break; }
+			case CoreProtocol::ResourceConstraint: { strErr = ResourceConstraint; break; }
+			case CoreProtocol::RestrictedXml: { strErr = InvalidXml; break; } // group with this one
+			case CoreProtocol::SeeOtherHost: { connErr = SeeOtherHost; break; }
+			case CoreProtocol::SystemShutdown: { strErr = SystemShutdown; break; }
+			case CoreProtocol::UndefinedCondition: { break; } // leave as null error
+			case CoreProtocol::UnsupportedEncoding: { break; } // should NOT happen (we send good encoding)
+			case CoreProtocol::UnsupportedStanzaType: { break; } // should NOT happen (we're not stupid)
+			case CoreProtocol::UnsupportedVersion: { connErr = UnsupportedVersion; break; }
+			case CoreProtocol::XmlNotWellFormed: { strErr = InvalidXml; break; } // group with this one
+			default: { break; }
+		}
+
+		reset();
+
+		d->errText = text;
+		d->errAppSpec = appSpec;
+		if(connErr != -1) {
+			d->errCond = connErr;
+			error(ErrNeg);
+		}
+		else {
+			if(strErr != -1)
+				d->errCond = strErr;
+			else
+				d->errCond = GenericStreamError;
+			error(ErrStream);
+		}
+	}
+	else if(c == CoreProtocol::ErrStartTLS) {
+		reset();
+		d->errCond = TLSStart;
+		error(ErrTLS);
+	}
+	else if(c == CoreProtocol::ErrAuth) {
+		int x = d->client.errCond;
+		int r = GenericAuthError;
+		if(d->client.old) {
+			if(x == 401) // not authorized
+				r = NotAuthorized;
+			else if(x == 409) // conflict
+				r = GenericAuthError;
+			else if(x == 406) // not acceptable (this should NOT happen)
+				r = GenericAuthError;
+		}
+		else {
+			switch(x) {
+				case CoreProtocol::Aborted: { r = GenericAuthError; break; } // should NOT happen (we never send <abort/>)
+				case CoreProtocol::IncorrectEncoding: { r = GenericAuthError; break; } // should NOT happen
+				case CoreProtocol::InvalidAuthzid: { r = InvalidAuthzid; break; }
+				case CoreProtocol::InvalidMech: { r = InvalidMech; break; }
+				case CoreProtocol::MechTooWeak: { r = MechTooWeak; break; }
+				case CoreProtocol::NotAuthorized: { r = NotAuthorized; break; }
+				case CoreProtocol::TemporaryAuthFailure: { r = TemporaryAuthFailure; break; }
+			}
+		}
+		reset();
+		d->errCond = r;
+		error(ErrAuth);
+	}
+	else if(c == CoreProtocol::ErrPlain) {
+		reset();
+		d->errCond = NoMech;
+		error(ErrAuth);
+	}
+	else if(c == CoreProtocol::ErrBind) {
+		int r = -1;
+		if(d->client.errCond == CoreProtocol::BindBadRequest) {
+			// should NOT happen
+		}
+		else if(d->client.errCond == CoreProtocol::BindNotAllowed) {
+			r = BindNotAllowed;
+		}
+		else if(d->client.errCond == CoreProtocol::BindConflict) {
+			r = BindConflict;
+		}
+
+		if(r != -1) {
+			reset();
+			d->errCond = r;
+			error(ErrBind);
+		}
+		else {
+			reset();
+			error(ErrProtocol);
+		}
+	}
+}
+
+//----------------------------------------------------------------------------
+// Debug
+//----------------------------------------------------------------------------
+Debug::~Debug()
+{
+}
+
+#ifdef XMPP_TEST
+TD::TD()
+{
+}
+
+TD::~TD()
+{
+}
+
+void TD::msg(const QString &s)
+{
+	if(debug_ptr)
+		debug_ptr->msg(s);
+}
+
+void TD::outgoingTag(const QString &s)
+{
+	if(debug_ptr)
+		debug_ptr->outgoingTag(s);
+}
+
+void TD::incomingTag(const QString &s)
+{
+	if(debug_ptr)
+		debug_ptr->incomingTag(s);
+}
+
+void TD::outgoingXml(const QDomElement &e)
+{
+	if(debug_ptr)
+		debug_ptr->outgoingXml(e);
+}
+
+void TD::incomingXml(const QDomElement &e)
+{
+	if(debug_ptr)
+		debug_ptr->incomingXml(e);
+}
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/td.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/td.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/td.h	(revision 38)
@@ -0,0 +1,20 @@
+#ifndef TESTDEBUG_H
+#define TESTDEBUG_H
+
+#include<qdom.h>
+
+class TD
+{
+public:
+	TD();
+	~TD();
+
+	static void msg(const QString &);
+	static void outgoingTag(const QString &);
+	static void incomingTag(const QString &);
+	static void outgoingXml(const QDomElement &);
+	static void incomingXml(const QDomElement &);
+};
+
+#endif
+
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/tlshandler.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/tlshandler.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/tlshandler.cpp	(revision 38)
@@ -0,0 +1,138 @@
+/*
+ * tlshandler.cpp - abstract wrapper for TLS
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"xmpp.h"
+
+#include<qtimer.h>
+#include"qca.h"
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// TLSHandler
+//----------------------------------------------------------------------------
+TLSHandler::TLSHandler(QObject *parent)
+:QObject(parent)
+{
+}
+
+TLSHandler::~TLSHandler()
+{
+}
+
+
+//----------------------------------------------------------------------------
+// QCATLSHandler
+//----------------------------------------------------------------------------
+class QCATLSHandler::Private
+{
+public:
+	QCA::TLS *tls;
+	int state, err;
+};
+
+QCATLSHandler::QCATLSHandler(QCA::TLS *parent)
+:TLSHandler(parent)
+{
+	d = new Private;
+	d->tls = parent;
+	connect(d->tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
+	connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
+	connect(d->tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int)));
+	connect(d->tls, SIGNAL(closed()), SLOT(tls_closed()));
+	connect(d->tls, SIGNAL(error(int)), SLOT(tls_error(int)));
+	d->state = 0;
+	d->err = -1;
+}
+
+QCATLSHandler::~QCATLSHandler()
+{
+	delete d;
+}
+
+QCA::TLS *QCATLSHandler::tls() const
+{
+	return d->tls;
+}
+
+int QCATLSHandler::tlsError() const
+{
+	return d->err;
+}
+
+void QCATLSHandler::reset()
+{
+	d->tls->reset();
+	d->state = 0;
+}
+
+void QCATLSHandler::startClient(const QString &host)
+{
+	d->state = 0;
+	d->err = -1;
+	if(!d->tls->startClient(host))
+		QTimer::singleShot(0, this, SIGNAL(fail()));
+}
+
+void QCATLSHandler::write(const QByteArray &a)
+{
+	d->tls->write(a);
+}
+
+void QCATLSHandler::writeIncoming(const QByteArray &a)
+{
+	d->tls->writeIncoming(a);
+}
+
+void QCATLSHandler::continueAfterHandshake()
+{
+	if(d->state == 2) {
+		success();
+		d->state = 3;
+	}
+}
+
+void QCATLSHandler::tls_handshaken()
+{
+	d->state = 2;
+	tlsHandshaken();
+}
+
+void QCATLSHandler::tls_readyRead()
+{
+	readyRead(d->tls->read());
+}
+
+void QCATLSHandler::tls_readyReadOutgoing(int plainBytes)
+{
+	readyReadOutgoing(d->tls->readOutgoing(), plainBytes);
+}
+
+void QCATLSHandler::tls_closed()
+{
+	closed();
+}
+
+void QCATLSHandler::tls_error(int x)
+{
+	d->err = x;
+	d->state = 0;
+	fail();
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/xmlprotocol.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/xmlprotocol.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/xmlprotocol.cpp	(revision 38)
@@ -0,0 +1,543 @@
+/*
+ * xmlprotocol.cpp - state machine for 'jabber-like' protocols
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"xmlprotocol.h"
+
+#include"bytestream.h"
+
+using namespace XMPP;
+
+// stripExtraNS
+//
+// This function removes namespace information from various nodes for
+// display purposes only (the element is pretty much useless for processing
+// after this).  We do this because QXml is a bit overzealous about outputting
+// redundant namespaces.
+static QDomElement stripExtraNS(const QDomElement &e)
+{
+	// find closest parent with a namespace
+	QDomNode par = e.parentNode();
+	while(!par.isNull() && par.namespaceURI().isNull())
+		par = par.parentNode();
+	bool noShowNS = false;
+	if(!par.isNull() && par.namespaceURI() == e.namespaceURI())
+		noShowNS = true;
+
+	// build qName (prefix:localName)
+	QString qName;
+	if(!e.prefix().isEmpty())
+		qName = e.prefix() + ':' + e.localName();
+	else
+		qName = e.tagName();
+
+	QDomElement i;
+	uint x;
+	if(noShowNS)
+		i = e.ownerDocument().createElement(qName);
+	else
+		i = e.ownerDocument().createElementNS(e.namespaceURI(), qName);
+
+	// copy attributes
+	QDomNamedNodeMap al = e.attributes();
+	for(x = 0; x < al.count(); ++x) {
+		QDomAttr a = al.item(x).cloneNode().toAttr();
+
+		// don't show xml namespace
+		if(a.namespaceURI() == NS_XML)
+			i.setAttribute(QString("xml:") + a.name(), a.value());
+		else
+			i.setAttributeNodeNS(a);
+	}
+
+	// copy children
+	QDomNodeList nl = e.childNodes();
+	for(x = 0; x < nl.count(); ++x) {
+		QDomNode n = nl.item(x);
+		if(n.isElement())
+			i.appendChild(stripExtraNS(n.toElement()));
+		else
+			i.appendChild(n.cloneNode());
+	}
+	return i;
+}
+
+// xmlToString
+//
+// This function converts a QDomElement into a QString, using stripExtraNS
+// to make it pretty.
+static QString xmlToString(const QDomElement &e, const QString &fakeNS, const QString &fakeQName, bool clip)
+{
+	QDomElement i = e.cloneNode().toElement();
+
+	// It seems QDom can only have one namespace attribute at a time (see docElement 'HACK').
+	// Fortunately we only need one kind depending on the input, so it is specified here.
+	QDomElement fake = e.ownerDocument().createElementNS(fakeNS, fakeQName);
+	fake.appendChild(i);
+	fake = stripExtraNS(fake);
+	QString out;
+	{
+		QTextStream ts(&out, IO_WriteOnly);
+		fake.firstChild().save(ts, 0);
+	}
+	// 'clip' means to remove any unwanted (and unneeded) characters, such as a trailing newline
+	if(clip) {
+		int n = out.findRev('>');
+		out.truncate(n+1);
+	}
+	return out;
+}
+
+// createRootXmlTags
+//
+// This function creates three QStrings, one being an <?xml .. ?> processing
+// instruction, and the others being the opening and closing tags of an
+// element, <foo> and </foo>.  This basically allows us to get the raw XML
+// text needed to open/close an XML stream, without resorting to generating
+// the XML ourselves.  This function uses QDom to do the generation, which
+// ensures proper encoding and entity output.
+static void createRootXmlTags(const QDomElement &root, QString *xmlHeader, QString *tagOpen, QString *tagClose)
+{
+	QDomElement e = root.cloneNode(false).toElement();
+
+	// insert a dummy element to ensure open and closing tags are generated
+	QDomElement dummy = e.ownerDocument().createElement("dummy");
+	e.appendChild(dummy);
+
+	// convert to xml->text
+	QString str;
+	{
+		QTextStream ts(&str, IO_WriteOnly);
+		e.save(ts, 0);
+	}
+
+	// parse the tags out
+	int n = str.find('<');
+	int n2 = str.find('>', n);
+	++n2;
+	*tagOpen = str.mid(n, n2-n);
+	n2 = str.findRev('>');
+	n = str.findRev('<');
+	++n2;
+	*tagClose = str.mid(n, n2-n);
+
+	// generate a nice xml processing header
+	*xmlHeader = "<?xml version=\"1.0\"?>";
+}
+
+//----------------------------------------------------------------------------
+// Protocol
+//----------------------------------------------------------------------------
+XmlProtocol::TransferItem::TransferItem()
+{
+}
+
+XmlProtocol::TransferItem::TransferItem(const QString &_str, bool sent, bool external)
+{
+	isString = true;
+	isSent = sent;
+	isExternal = external;
+	str = _str;
+}
+
+XmlProtocol::TransferItem::TransferItem(const QDomElement &_elem, bool sent, bool external)
+{
+	isString = false;
+	isSent = sent;
+	isExternal = external;
+	elem = _elem;
+}
+
+XmlProtocol::XmlProtocol()
+{
+	init();
+}
+
+XmlProtocol::~XmlProtocol()
+{
+}
+
+void XmlProtocol::init()
+{
+	incoming = false;
+	peerClosed = false;
+	closeWritten = false;
+}
+
+void XmlProtocol::reset()
+{
+	init();
+
+	elem = QDomElement();
+	tagOpen = QString();
+	tagClose = QString();
+	xml.reset();
+	outData.resize(0);
+	trackQueue.clear();
+	transferItemList.clear();
+}
+
+void XmlProtocol::addIncomingData(const QByteArray &a)
+{
+	xml.appendData(a);
+}
+
+QByteArray XmlProtocol::takeOutgoingData()
+{
+	QByteArray a = outData.copy();
+	outData.resize(0);
+	return a;
+}
+
+void XmlProtocol::outgoingDataWritten(int bytes)
+{
+	for(QValueList<TrackItem>::Iterator it = trackQueue.begin(); it != trackQueue.end();) {
+		TrackItem &i = *it;
+
+		// enough bytes?
+		if(bytes < i.size) {
+			i.size -= bytes;
+			break;
+		}
+		int type = i.type;
+		int id = i.id;
+		int size = i.size;
+		bytes -= i.size;
+		it = trackQueue.remove(it);
+
+		if(type == TrackItem::Raw) {
+			// do nothing
+		}
+		else if(type == TrackItem::Close) {
+			closeWritten = true;
+		}
+		else if(type == TrackItem::Custom) {
+			itemWritten(id, size);
+		}
+	}
+}
+
+bool XmlProtocol::processStep()
+{
+	Parser::Event pe;
+	notify = 0;
+	transferItemList.clear();
+
+	if(state != Closing && (state == RecvOpen || stepAdvancesParser())) {
+		// if we get here, then it's because we're in some step that advances the parser
+		pe = xml.readNext();
+		if(!pe.isNull()) {
+			// note: error/close events should be handled for ALL steps, so do them here
+			switch(pe.type()) {
+				case Parser::Event::DocumentOpen: {
+					transferItemList += TransferItem(pe.actualString(), false);
+
+					//stringRecv(pe.actualString());
+					break;
+				}
+				case Parser::Event::DocumentClose: {
+					transferItemList += TransferItem(pe.actualString(), false);
+
+					//stringRecv(pe.actualString());
+					if(incoming) {
+						sendTagClose();
+						event = ESend;
+						peerClosed = true;
+						state = Closing;
+					}
+					else {
+						event = EPeerClosed;
+					}
+					return true;
+				}
+				case Parser::Event::Element: {
+					transferItemList += TransferItem(pe.element(), false);
+
+					//elementRecv(pe.element());
+					break;
+				}
+				case Parser::Event::Error: {
+					if(incoming) {
+						// If we get a parse error during the initial element exchange,
+						// flip immediately into 'open' mode so that we can report an error.
+						if(state == RecvOpen) {
+							sendTagOpen();
+							state = Open;
+						}
+						return handleError();
+					}
+					else {
+						event = EError;
+						errorCode = ErrParse;
+						return true;
+					}
+				}
+			}
+		}
+		else {
+			if(state == RecvOpen || stepRequiresElement()) {
+				need = NNotify;
+				notify |= NRecv;
+				return false;
+			}
+		}
+	}
+
+	return baseStep(pe);
+}
+
+QString XmlProtocol::xmlEncoding() const
+{
+	return xml.encoding();
+}
+
+QString XmlProtocol::elementToString(const QDomElement &e, bool clip)
+{
+	if(elem.isNull())
+		elem = elemDoc.importNode(docElement(), true).toElement();
+
+	// Determine the appropriate 'fakeNS' to use
+	QString ns;
+
+	// first, check root namespace
+	QString pre = e.prefix();
+	if(pre.isNull())
+		pre = "";
+	if(pre == elem.prefix()) {
+		ns = elem.namespaceURI();
+	}
+	else {
+		// scan the root attributes for 'xmlns' (oh joyous hacks)
+		QDomNamedNodeMap al = elem.attributes();
+		uint n;
+		for(n = 0; n < al.count(); ++n) {
+			QDomAttr a = al.item(n).toAttr();
+			QString s = a.name();
+			int x = s.find(':');
+			if(x != -1)
+				s = s.mid(x+1);
+			else
+				s = "";
+			if(pre == s) {
+				ns = a.value();
+				break;
+			}
+		}
+		if(n >= al.count()) {
+			// if we get here, then no appropriate ns was found.  use root then..
+			ns = elem.namespaceURI();
+		}
+	}
+
+	// build qName
+	QString qn;
+	if(!elem.prefix().isEmpty())
+		qn = elem.prefix() + ':';
+	qn += elem.localName();
+
+	// make the string
+	return xmlToString(e, ns, qn, clip);
+}
+
+bool XmlProtocol::stepRequiresElement() const
+{
+	// default returns false
+	return false;
+}
+
+void XmlProtocol::itemWritten(int, int)
+{
+	// default does nothing
+}
+
+void XmlProtocol::stringSend(const QString &)
+{
+	// default does nothing
+}
+
+void XmlProtocol::stringRecv(const QString &)
+{
+	// default does nothing
+}
+
+void XmlProtocol::elementSend(const QDomElement &)
+{
+	// default does nothing
+}
+
+void XmlProtocol::elementRecv(const QDomElement &)
+{
+	// default does nothing
+}
+
+void XmlProtocol::startConnect()
+{
+	incoming = false;
+	state = SendOpen;
+}
+
+void XmlProtocol::startAccept()
+{
+	incoming = true;
+	state = RecvOpen;
+}
+
+bool XmlProtocol::close()
+{
+	sendTagClose();
+	event = ESend;
+	state = Closing;
+	return true;
+}
+
+int XmlProtocol::writeString(const QString &s, int id, bool external)
+{
+	transferItemList += TransferItem(s, true, external);
+	return internalWriteString(s, TrackItem::Custom, id);
+}
+
+int XmlProtocol::writeElement(const QDomElement &e, int id, bool external, bool clip)
+{
+	if(e.isNull())
+		return 0;
+	transferItemList += TransferItem(e, true, external);
+
+	//elementSend(e);
+	QString out = elementToString(e, clip);
+	return internalWriteString(out, TrackItem::Custom, id);
+}
+
+QByteArray XmlProtocol::resetStream()
+{
+	// reset the state
+	if(incoming)
+		state = RecvOpen;
+	else
+		state = SendOpen;
+
+	// grab unprocessed data before resetting
+	QByteArray spare = xml.unprocessed();
+	xml.reset();
+	return spare;
+}
+
+int XmlProtocol::internalWriteData(const QByteArray &a, TrackItem::Type t, int id)
+{
+	TrackItem i;
+	i.type = t;
+	i.id = id;
+	i.size = a.size();
+	trackQueue += i;
+
+	ByteStream::appendArray(&outData, a);
+	return a.size();
+}
+
+int XmlProtocol::internalWriteString(const QString &s, TrackItem::Type t, int id)
+{
+	QCString cs = s.utf8();
+	QByteArray a(cs.length());
+	memcpy(a.data(), cs.data(), a.size());
+	return internalWriteData(a, t, id);
+}
+
+void XmlProtocol::sendTagOpen()
+{
+	if(elem.isNull())
+		elem = elemDoc.importNode(docElement(), true).toElement();
+
+	QString xmlHeader;
+	createRootXmlTags(elem, &xmlHeader, &tagOpen, &tagClose);
+
+	QString s;
+	s += xmlHeader + '\n';
+	s += tagOpen + '\n';
+
+	transferItemList += TransferItem(xmlHeader, true);
+	transferItemList += TransferItem(tagOpen, true);
+
+	//stringSend(xmlHeader);
+	//stringSend(tagOpen);
+	internalWriteString(s, TrackItem::Raw);
+}
+
+void XmlProtocol::sendTagClose()
+{
+	transferItemList += TransferItem(tagClose, true);
+
+	//stringSend(tagClose);
+	internalWriteString(tagClose, TrackItem::Close);
+}
+
+bool XmlProtocol::baseStep(const Parser::Event &pe)
+{
+	// Basic
+	if(state == SendOpen) {
+		sendTagOpen();
+		event = ESend;
+		if(incoming)
+			state = Open;
+		else
+			state = RecvOpen;
+		return true;
+	}
+	else if(state == RecvOpen) {
+		if(incoming)
+			state = SendOpen;
+		else
+			state = Open;
+
+		// note: event will always be DocumentOpen here
+		handleDocOpen(pe);
+		event = ERecvOpen;
+		return true;
+	}
+	else if(state == Open) {
+		QDomElement e;
+		if(pe.type() == Parser::Event::Element)
+			e = pe.element();
+		return doStep(e);
+	}
+	// Closing
+	else {
+		if(closeWritten) {
+			if(peerClosed) {
+				event = EPeerClosed;
+				return true;
+			}
+			else
+				return handleCloseFinished();
+		}
+
+		need = NNotify;
+		notify = NSend;
+		return false;
+	}
+}
+
+void XmlProtocol::setIncomingAsExternal()
+{
+	for(QValueList<TransferItem>::Iterator it = transferItemList.begin(); it != transferItemList.end(); ++it) {
+		TransferItem &i = *it;
+		// look for elements received
+		if(!i.isString && !i.isSent)
+			i.isExternal = true;
+	}
+}
+
Index: psi/tags/0.10-os2-b1/iris/xmpp-core/xmlprotocol.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-core/xmlprotocol.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-core/xmlprotocol.h	(revision 38)
@@ -0,0 +1,145 @@
+/*
+ * xmlprotocol.h - state machine for 'jabber-like' protocols
+ * Copyright (C) 2004  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef XMLPROTOCOL_H
+#define XMLPROTOCOL_H
+
+#include<qdom.h>
+#include<qvaluelist.h>
+#include"parser.h"
+
+#define NS_XML "http://www.w3.org/XML/1998/namespace"
+
+namespace XMPP
+{
+	class XmlProtocol
+	{
+	public:
+		enum Need {
+			NNotify,      // need a data send and/or recv update
+			NCustom = 10
+		};
+		enum Event {
+			EError,       // unrecoverable error, see errorCode for details
+			ESend,        // data needs to be sent, use takeOutgoingData()
+			ERecvOpen,    // breakpoint after root element open tag is received
+			EPeerClosed,  // root element close tag received
+			EClosed,      // finished closing
+			ECustom = 10
+		};
+		enum Error {
+			ErrParse,     // there was an error parsing the xml
+			ErrCustom = 10
+		};
+		enum Notify {
+			NSend = 0x01, // need to know if data has been written
+			NRecv = 0x02  // need incoming data
+		};
+
+		XmlProtocol();
+		virtual ~XmlProtocol();
+
+		virtual void reset();
+
+		// byte I/O for the stream
+		void addIncomingData(const QByteArray &);
+		QByteArray takeOutgoingData();
+		void outgoingDataWritten(int);
+
+		// advance the state machine
+		bool processStep();
+
+		// set these before returning from a step
+		int need, event, errorCode, notify;
+
+		inline bool isIncoming() const { return incoming; }
+		QString xmlEncoding() const;
+		QString elementToString(const QDomElement &e, bool clip=false);
+
+		class TransferItem
+		{
+		public:
+			TransferItem();
+			TransferItem(const QString &str, bool sent, bool external=false);
+			TransferItem(const QDomElement &elem, bool sent, bool external=false);
+
+			bool isSent; // else, received
+			bool isString; // else, is element
+			bool isExternal; // not owned by protocol
+			QString str;
+			QDomElement elem;
+		};
+		QValueList<TransferItem> transferItemList;
+		void setIncomingAsExternal();
+
+	protected:
+		virtual QDomElement docElement()=0;
+		virtual void handleDocOpen(const Parser::Event &pe)=0;
+		virtual bool handleError()=0;
+		virtual bool handleCloseFinished()=0;
+		virtual bool stepAdvancesParser() const=0;
+		virtual bool stepRequiresElement() const;
+		virtual bool doStep(const QDomElement &e)=0;
+		virtual void itemWritten(int id, int size);
+
+		// 'debug'
+		virtual void stringSend(const QString &s);
+		virtual void stringRecv(const QString &s);
+		virtual void elementSend(const QDomElement &e);
+		virtual void elementRecv(const QDomElement &e);
+
+		void startConnect();
+		void startAccept();
+		bool close();
+		int writeString(const QString &s, int id, bool external);
+		int writeElement(const QDomElement &e, int id, bool external, bool clip=false);
+		QByteArray resetStream();
+
+	private:
+		enum { SendOpen, RecvOpen, Open, Closing };
+		class TrackItem
+		{
+		public:
+			enum Type { Raw, Close, Custom };
+			int type, id, size;
+		};
+
+		bool incoming;
+		QDomDocument elemDoc;
+		QDomElement elem;
+		QString tagOpen, tagClose;
+		int state;
+		bool peerClosed;
+		bool closeWritten;
+
+		Parser xml;
+		QByteArray outData;
+		QValueList<TrackItem> trackQueue;
+
+		void init();
+		int internalWriteData(const QByteArray &a, TrackItem::Type t, int id=-1);
+		int internalWriteString(const QString &s, TrackItem::Type t, int id=-1);
+		void sendTagOpen();
+		void sendTagClose();
+		bool baseStep(const Parser::Event &pe);
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-im/client.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-im/client.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-im/client.cpp	(revision 38)
@@ -0,0 +1,1435 @@
+/*
+ * client.cpp - IM Client
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"im.h"
+#include"safedelete.h"
+
+//! \class Client client.h
+//! \brief Communicates with the Jabber network.  Start here.
+//!
+//!  Client controls an active Jabber connection.  It allows you to connect,
+//!  authenticate, manipulate the roster, and send / receive messages and
+//!  presence.  It is the centerpiece of this library, and all Tasks must pass
+//!  through it.
+//!
+//!  For convenience, many Tasks are handled internally to Client (such as
+//!  JT_Auth).  However, for accessing features beyond the basics provided by
+//!  Client, you will need to manually invoke Tasks.  Fortunately, the
+//!  process is very simple.
+//!
+//!  The entire Task system is heavily founded on Qt.  All Tasks have a parent,
+//!  except for the root Task, and are considered QObjects.  By using Qt's RTTI
+//!  facilities (QObject::sender(), QObject::isA(), etc), you can use a
+//!  "fire and forget" approach with Tasks.
+//!
+//!  \code
+//!  #include "client.h"
+//!  using namespace Jabber;
+//!
+//!  ...
+//!
+//!  Client *client;
+//!
+//!  Session::Session()
+//!  {
+//!    client = new Client;
+//!    connect(client, SIGNAL(handshaken()), SLOT(clientHandshaken()));
+//!    connect(client, SIGNAL(authFinished(bool, int, const QString &)), SLOT(authFinished(bool, int, const QString &)));
+//!    client->connectToHost("jabber.org");
+//!  }
+//!
+//!  void Session::clientHandshaken()
+//!  {
+//!    client->authDigest("jabtest", "12345", "Psi");
+//!  }
+//!
+//!  void Session::authFinished(bool success, int, const QString &err)
+//!  {
+//!    if(success)
+//!      printf("Login success!");
+//!    else
+//!      printf("Login failed.  Here's why: %s\n", err.latin1());
+//!  }
+//!  \endcode
+
+#include<stdarg.h>
+#include<qobjectlist.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include"xmpp_tasks.h"
+#include"xmpp_xmlcommon.h"
+#include"s5b.h"
+#include"xmpp_ibb.h"
+#include"xmpp_jidlink.h"
+#include"filetransfer.h"
+
+/*#include<stdio.h>
+#include<stdarg.h>
+#include<qstring.h>
+#include<qdom.h>
+#include<qobjectlist.h>
+#include<qtimer.h>
+#include"xmpp_stream.h"
+#include"xmpp_tasks.h"
+#include"xmpp_xmlcommon.h"
+#include"xmpp_dtcp.h"
+#include"xmpp_ibb.h"
+#include"xmpp_jidlink.h"
+
+using namespace Jabber;*/
+
+#ifdef Q_WS_WIN
+#define vsnprintf _vsnprintf
+#endif
+
+namespace XMPP
+{
+
+//----------------------------------------------------------------------------
+// Client
+//----------------------------------------------------------------------------
+class Client::GroupChat
+{
+public:
+	enum { Connecting, Connected, Closing };
+	GroupChat() {}
+
+	Jid j;
+	int status;
+};
+
+class Client::ClientPrivate
+{
+public:
+	ClientPrivate() {}
+
+	ClientStream *stream;
+	QDomDocument doc;
+	int id_seed;
+	Task *root;
+	QString host, user, pass, resource;
+	QString osname, tzname, clientName, clientVersion;
+	int tzoffset;
+	bool active;
+
+	LiveRoster roster;
+	ResourceList resourceList;
+	S5BManager *s5bman;
+	IBBManager *ibbman;
+	JidLinkManager *jlman;
+	FileTransferManager *ftman;
+	bool ftEnabled;
+	QValueList<GroupChat> groupChatList;
+};
+
+
+Client::Client(QObject *par)
+:QObject(par)
+{
+	d = new ClientPrivate;
+	d->tzoffset = 0;
+	d->active = false;
+	d->osname = "N/A";
+	d->clientName = "N/A";
+	d->clientVersion = "0.0";
+
+	d->id_seed = 0xaaaa;
+	d->root = new Task(this, true);
+
+	d->stream = 0;
+
+	d->s5bman = new S5BManager(this);
+	connect(d->s5bman, SIGNAL(incomingReady()), SLOT(s5b_incomingReady()));
+
+	d->ibbman = new IBBManager(this);
+	connect(d->ibbman, SIGNAL(incomingReady()), SLOT(ibb_incomingReady()));
+
+	d->jlman = new JidLinkManager(this);
+
+	d->ftman = 0;
+}
+
+Client::~Client()
+{
+	close(true);
+
+	delete d->ftman;
+	delete d->jlman;
+	delete d->ibbman;
+	delete d->s5bman;
+	delete d->root;
+	//delete d->stream;
+	delete d;
+}
+
+void Client::connectToServer(ClientStream *s, const Jid &j, bool auth)
+{
+	d->stream = s;
+	//connect(d->stream, SIGNAL(connected()), SLOT(streamConnected()));
+	//connect(d->stream, SIGNAL(handshaken()), SLOT(streamHandshaken()));
+	connect(d->stream, SIGNAL(error(int)), SLOT(streamError(int)));
+	//connect(d->stream, SIGNAL(sslCertificateReady(const QSSLCert &)), SLOT(streamSSLCertificateReady(const QSSLCert &)));
+	connect(d->stream, SIGNAL(readyRead()), SLOT(streamReadyRead()));
+	//connect(d->stream, SIGNAL(closeFinished()), SLOT(streamCloseFinished()));
+	connect(d->stream, SIGNAL(incomingXml(const QString &)), SLOT(streamIncomingXml(const QString &)));
+	connect(d->stream, SIGNAL(outgoingXml(const QString &)), SLOT(streamOutgoingXml(const QString &)));
+
+	d->stream->connectToServer(j, auth);
+}
+
+void Client::start(const QString &host, const QString &user, const QString &pass, const QString &_resource)
+{
+	// TODO
+	d->host = host;
+	d->user = user;
+	d->pass = pass;
+	d->resource = _resource;
+
+	Status stat;
+	stat.setIsAvailable(false);
+	d->resourceList += Resource(resource(), stat);
+
+	JT_PushPresence *pp = new JT_PushPresence(rootTask());
+	connect(pp, SIGNAL(subscription(const Jid &, const QString &)), SLOT(ppSubscription(const Jid &, const QString &)));
+	connect(pp, SIGNAL(presence(const Jid &, const Status &)), SLOT(ppPresence(const Jid &, const Status &)));
+
+	JT_PushMessage *pm = new JT_PushMessage(rootTask());
+	connect(pm, SIGNAL(message(const Message &)), SLOT(pmMessage(const Message &)));
+
+	JT_PushRoster *pr = new JT_PushRoster(rootTask());
+	connect(pr, SIGNAL(roster(const Roster &)), SLOT(prRoster(const Roster &)));
+
+	new JT_ServInfo(rootTask());
+
+	d->active = true;
+}
+
+void Client::setFileTransferEnabled(bool b)
+{
+	if(b) {
+		if(!d->ftman)
+			d->ftman = new FileTransferManager(this);
+	}
+	else {
+		if(d->ftman) {
+			delete d->ftman;
+			d->ftman = 0;
+		}
+	}
+}
+
+FileTransferManager *Client::fileTransferManager() const
+{
+	return d->ftman;
+}
+
+JidLinkManager *Client::jidLinkManager() const
+{
+	return d->jlman;
+}
+
+S5BManager *Client::s5bManager() const
+{
+	return d->s5bman;
+}
+
+IBBManager *Client::ibbManager() const
+{
+	return d->ibbman;
+}
+
+bool Client::isActive() const
+{
+	return d->active;
+}
+
+void Client::groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &_s)
+{
+	Jid jid(room + "@" + host + "/" + nick);
+	for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+		GroupChat &i = *it;
+		if(i.j.compare(jid, false)) {
+			i.j = jid;
+
+			Status s = _s;
+			s.setIsAvailable(true);
+
+			JT_Presence *j = new JT_Presence(rootTask());
+			j->pres(jid, s);
+			j->go(true);
+
+			break;
+		}
+	}
+}
+
+bool Client::groupChatJoin(const QString &host, const QString &room, const QString &nick)
+{
+	Jid jid(room + "@" + host + "/" + nick);
+	for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end();) {
+		GroupChat &i = *it;
+		if(i.j.compare(jid, false)) {
+			// if this room is shutting down, then free it up
+			if(i.status == GroupChat::Closing)
+				it = d->groupChatList.remove(it);
+			else
+				return false;
+		}
+		else
+			++it;
+	}
+
+	debug(QString("Client: Joined: [%1]\n").arg(jid.full()));
+	GroupChat i;
+	i.j = jid;
+	i.status = GroupChat::Connecting;
+	d->groupChatList += i;
+
+	JT_Presence *j = new JT_Presence(rootTask());
+	j->pres(jid, Status());
+	j->go(true);
+
+	return true;
+}
+
+void Client::groupChatSetStatus(const QString &host, const QString &room, const Status &_s)
+{
+	Jid jid(room + "@" + host);
+	bool found = false;
+	for(QValueList<GroupChat>::ConstIterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+		const GroupChat &i = *it;
+		if(i.j.compare(jid, false)) {
+			found = true;
+			jid = i.j;
+			break;
+		}
+	}
+	if(!found)
+		return;
+
+	Status s = _s;
+	s.setIsAvailable(true);
+
+	JT_Presence *j = new JT_Presence(rootTask());
+	j->pres(jid, s);
+	j->go(true);
+}
+
+void Client::groupChatLeave(const QString &host, const QString &room)
+{
+	Jid jid(room + "@" + host);
+	for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+		GroupChat &i = *it;
+
+		if(!i.j.compare(jid, false))
+			continue;
+
+		i.status = GroupChat::Closing;
+		debug(QString("Client: Leaving: [%1]\n").arg(i.j.full()));
+
+		JT_Presence *j = new JT_Presence(rootTask());
+		Status s;
+		s.setIsAvailable(false);
+		j->pres(i.j, s);
+		j->go(true);
+	}
+}
+
+/*void Client::start()
+{
+	if(d->stream->old()) {
+		// old has no activation step
+		d->active = true;
+		activated();
+	}
+	else {
+		// TODO: IM session
+	}
+}*/
+
+// TODO: fast close
+void Client::close(bool)
+{
+	if(d->stream) {
+		if(d->active) {
+			for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+				GroupChat &i = *it;
+				i.status = GroupChat::Closing;
+
+				JT_Presence *j = new JT_Presence(rootTask());
+				Status s;
+				s.setIsAvailable(false);
+				j->pres(i.j, s);
+				j->go(true);
+			}
+		}
+
+		d->stream->disconnect(this);
+		d->stream->close();
+		d->stream = 0;
+	}
+	disconnected();
+	cleanup();
+}
+
+void Client::cleanup()
+{
+	d->active = false;
+	//d->authed = false;
+	d->groupChatList.clear();
+}
+
+/*void Client::continueAfterCert()
+{
+	d->stream->continueAfterCert();
+}
+
+void Client::streamConnected()
+{
+	connected();
+}
+
+void Client::streamHandshaken()
+{
+	handshaken();
+}*/
+
+void Client::streamError(int)
+{
+	//StreamError e = err;
+	//error(e);
+
+	//if(!e.isWarning()) {
+		disconnected();
+		cleanup();
+	//}
+}
+
+/*void Client::streamSSLCertificateReady(const QSSLCert &cert)
+{
+	sslCertReady(cert);
+}
+
+void Client::streamCloseFinished()
+{
+	closeFinished();
+}*/
+
+static QDomElement oldStyleNS(const QDomElement &e)
+{
+	// find closest parent with a namespace
+	QDomNode par = e.parentNode();
+	while(!par.isNull() && par.namespaceURI().isNull())
+		par = par.parentNode();
+	bool noShowNS = false;
+	if(!par.isNull() && par.namespaceURI() == e.namespaceURI())
+		noShowNS = true;
+
+	QDomElement i;
+	uint x;
+	//if(noShowNS)
+		i = e.ownerDocument().createElement(e.tagName());
+	//else
+	//	i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName());
+
+	// copy attributes
+	QDomNamedNodeMap al = e.attributes();
+	for(x = 0; x < al.count(); ++x)
+		i.setAttributeNode(al.item(x).cloneNode().toAttr());
+
+	if(!noShowNS)
+		i.setAttribute("xmlns", e.namespaceURI());
+
+	// copy children
+	QDomNodeList nl = e.childNodes();
+	for(x = 0; x < nl.count(); ++x) {
+		QDomNode n = nl.item(x);
+		if(n.isElement())
+			i.appendChild(oldStyleNS(n.toElement()));
+		else
+			i.appendChild(n.cloneNode());
+	}
+	return i;
+}
+
+void Client::streamReadyRead()
+{
+	/// @todo (dmik) when doing service discovery on some servers
+	//  (i.e. jabber.ru), this method's execution time can exceed up to
+	//  2,5 sec (on my Pentium D 820) that is obviously too much because
+	//  gives quite a noticeable GUI thread freeze (so that the Psi app
+	//  doesn't respond to system/input messages). Approx. 0.8 sec is taken by
+	//  s.toString(), and the rest is by QDomElement x = oldStyleNS(s.element()).
+	//  and distribute(). Ideally, parsing and distribution of lagre pieces of
+	//  data should be somehow done on a separate thread.
+	
+ 	// HACK HACK HACK
+	QGuardedPtr<ClientStream> pstream = d->stream;
+
+	while(pstream && d->stream->stanzaAvailable()) {
+		Stanza s = d->stream->read();
+
+		QString out = s.toString();
+		debug(QString("Client: incoming: [\n%1]\n").arg(out));
+		xmlIncoming(out);
+
+		QDomElement x = oldStyleNS(s.element());
+		distribute(x);
+	}
+}
+
+void Client::streamIncomingXml(const QString &s)
+{
+	QString str = s;
+	if(str.at(str.length()-1) != '\n')
+		str += '\n';
+	xmlIncoming(str);
+}
+
+void Client::streamOutgoingXml(const QString &s)
+{
+	QString str = s;
+	if(str.at(str.length()-1) != '\n')
+		str += '\n';
+	xmlOutgoing(str);
+}
+
+void Client::debug(const QString &str)
+{
+	debugText(str);
+}
+
+QString Client::genUniqueId()
+{
+	QString s;
+	s.sprintf("a%x", d->id_seed);
+	d->id_seed += 0x10;
+	return s;
+}
+
+Task *Client::rootTask()
+{
+	return d->root;
+}
+
+QDomDocument *Client::doc() const
+{
+	return &d->doc;
+}
+
+void Client::distribute(const QDomElement &x)
+{
+	if(x.hasAttribute("from")) {
+		Jid j(x.attribute("from"));
+		if(!j.isValid()) {
+			debug("Client: bad 'from' JID\n");
+			return;
+		}
+	}
+
+	if(!rootTask()->take(x)) {
+		debug("Client: packet was ignored.\n");
+	}
+}
+
+static QDomElement addCorrectNS(const QDomElement &e)
+{
+	uint x;
+
+	// grab child nodes
+	/*QDomDocumentFragment frag = e.ownerDocument().createDocumentFragment();
+	QDomNodeList nl = e.childNodes();
+	for(x = 0; x < nl.count(); ++x)
+		frag.appendChild(nl.item(x).cloneNode());*/
+
+	// find closest xmlns
+	QDomNode n = e;
+	while(!n.isNull() && !n.toElement().hasAttribute("xmlns"))
+		n = n.parentNode();
+	QString ns;
+	if(n.isNull() || !n.toElement().hasAttribute("xmlns"))
+		ns = "jabber:client";
+	else
+		ns = n.toElement().attribute("xmlns");
+
+	// make a new node
+	QDomElement i = e.ownerDocument().createElementNS(ns, e.tagName());
+
+	// copy attributes
+	QDomNamedNodeMap al = e.attributes();
+	for(x = 0; x < al.count(); ++x) {
+		QDomAttr a = al.item(x).toAttr();
+		if(a.name() != "xmlns")
+			i.setAttributeNodeNS(a.cloneNode().toAttr());
+	}
+
+	// copy children
+	QDomNodeList nl = e.childNodes();
+	for(x = 0; x < nl.count(); ++x) {
+		QDomNode n = nl.item(x);
+		if(n.isElement())
+			i.appendChild(addCorrectNS(n.toElement()));
+		else
+			i.appendChild(n.cloneNode());
+	}
+
+	//i.appendChild(frag);
+	return i;
+}
+
+void Client::send(const QDomElement &x)
+{
+	if(!d->stream)
+		return;
+
+	//QString out;
+	//QTextStream ts(&out, IO_WriteOnly);
+	//x.save(ts, 0);
+
+	//QString out = Stream::xmlToString(x);
+	//debug(QString("Client: outgoing: [\n%1]\n").arg(out));
+	//xmlOutgoing(out);
+
+	QDomElement e = addCorrectNS(x);
+	Stanza s = d->stream->createStanza(e);
+	if(s.isNull()) {
+		//printf("bad stanza??\n");
+		return;
+	}
+
+	QString out = s.toString();
+	debug(QString("Client: outgoing: [\n%1]\n").arg(out));
+	xmlOutgoing(out);
+
+	//printf("x[%s] x2[%s] s[%s]\n", Stream::xmlToString(x).latin1(), Stream::xmlToString(e).latin1(), s.toString().latin1());
+	d->stream->write(s);
+}
+
+void Client::send(const QString &str)
+{
+	if(!d->stream)
+		return;
+
+	debug(QString("Client: outgoing: [\n%1]\n").arg(str));
+	xmlOutgoing(str);
+	static_cast<ClientStream*>(d->stream)->writeDirect(str);
+}
+
+Stream & Client::stream()
+{
+	return *d->stream;
+}
+
+const LiveRoster & Client::roster() const
+{
+	return d->roster;
+}
+
+const ResourceList & Client::resourceList() const
+{
+	return d->resourceList;
+}
+
+QString Client::host() const
+{
+	return d->host;
+}
+
+QString Client::user() const
+{
+	return d->user;
+}
+
+QString Client::pass() const
+{
+	return d->pass;
+}
+
+QString Client::resource() const
+{
+	return d->resource;
+}
+
+Jid Client::jid() const
+{
+	QString s;
+	if(!d->user.isEmpty())
+		s += d->user + '@';
+	s += d->host;
+	if(!d->resource.isEmpty()) {
+		s += '/';
+		s += d->resource;
+	}
+
+	return Jid(s);
+}
+
+void Client::ppSubscription(const Jid &j, const QString &s)
+{
+	subscription(j, s);
+}
+
+void Client::ppPresence(const Jid &j, const Status &s)
+{
+	if(s.isAvailable())
+		debug(QString("Client: %1 is available.\n").arg(j.full()));
+	else
+		debug(QString("Client: %1 is unavailable.\n").arg(j.full()));
+
+	for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+		GroupChat &i = *it;
+
+		if(i.j.compare(j, false)) {
+			bool us = (i.j.resource() == j.resource() || j.resource().isEmpty()) ? true: false;
+
+			debug(QString("for groupchat i=[%1] pres=[%2], [us=%3].\n").arg(i.j.full()).arg(j.full()).arg(us));
+			switch(i.status) {
+				case GroupChat::Connecting:
+					if(us && s.hasError()) {
+						Jid j = i.j;
+						d->groupChatList.remove(it);
+						groupChatError(j, s.errorCode(), s.errorString());
+					}
+					else {
+						// don't signal success unless it is a non-error presence
+						if(!s.hasError()) {
+							i.status = GroupChat::Connected;
+							groupChatJoined(i.j);
+						}
+						groupChatPresence(j, s);
+					}
+					break;
+				case GroupChat::Connected:
+					groupChatPresence(j, s);
+					break;
+				case GroupChat::Closing:
+					if(us && !s.isAvailable()) {
+						Jid j = i.j;
+						d->groupChatList.remove(it);
+						groupChatLeft(j);
+					}
+					break;
+				default:
+					break;
+			}
+
+			return;
+		}
+	}
+
+	if(s.hasError()) {
+		presenceError(j, s.errorCode(), s.errorString());
+		return;
+	}
+
+	// is it me?
+	if(j.compare(jid(), false)) {
+		updateSelfPresence(j, s);
+	}
+	else {
+		// update all relavent roster entries
+		for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end(); ++it) {
+			LiveRosterItem &i = *it;
+
+			if(!i.jid().compare(j, false))
+				continue;
+
+			// roster item has its own resource?
+			if(!i.jid().resource().isEmpty()) {
+				if(i.jid().resource() != j.resource())
+					continue;
+			}
+
+			updatePresence(&i, j, s);
+		}
+	}
+}
+
+void Client::updateSelfPresence(const Jid &j, const Status &s)
+{
+	ResourceList::Iterator rit = d->resourceList.find(j.resource());
+	bool found = (rit == d->resourceList.end()) ? false: true;
+
+	// unavailable?  remove the resource
+	if(!s.isAvailable()) {
+		if(found) {
+			debug(QString("Client: Removing self resource: name=[%1]\n").arg(j.resource()));
+			(*rit).setStatus(s);
+			resourceUnavailable(j, *rit);
+			d->resourceList.remove(rit);
+		}
+	}
+	// available?  add/update the resource
+	else {
+		Resource r;
+		if(!found) {
+			r = Resource(j.resource(), s);
+			d->resourceList += r;
+			debug(QString("Client: Adding self resource: name=[%1]\n").arg(j.resource()));
+		}
+		else {
+			(*rit).setStatus(s);
+			r = *rit;
+			debug(QString("Client: Updating self resource: name=[%1]\n").arg(j.resource()));
+		}
+
+		resourceAvailable(j, r);
+	}
+}
+
+void Client::updatePresence(LiveRosterItem *i, const Jid &j, const Status &s)
+{
+	ResourceList::Iterator rit = i->resourceList().find(j.resource());
+	bool found = (rit == i->resourceList().end()) ? false: true;
+
+	// unavailable?  remove the resource
+	if(!s.isAvailable()) {
+		if(found) {
+			(*rit).setStatus(s);
+			debug(QString("Client: Removing resource from [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
+			resourceUnavailable(j, *rit);
+			i->resourceList().remove(rit);
+			i->setLastUnavailableStatus(s);
+		}
+	}
+	// available?  add/update the resource
+	else {
+		Resource r;
+		if(!found) {
+			r = Resource(j.resource(), s);
+			i->resourceList() += r;
+			debug(QString("Client: Adding resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
+		}
+		else {
+			(*rit).setStatus(s);
+			r = *rit;
+			debug(QString("Client: Updating resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
+		}
+
+		resourceAvailable(j, r);
+	}
+}
+
+void Client::pmMessage(const Message &m)
+{
+	debug(QString("Client: Message from %1\n").arg(m.from().full()));
+
+	if(m.type() == "groupchat") {
+		for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+			const GroupChat &i = *it;
+
+			if(!i.j.compare(m.from(), false))
+				continue;
+
+			if(i.status == GroupChat::Connected)
+				messageReceived(m);
+		}
+	}
+	else
+		messageReceived(m);
+}
+
+void Client::prRoster(const Roster &r)
+{
+	importRoster(r);
+}
+
+void Client::rosterRequest()
+{
+	if(!d->active)
+		return;
+
+	JT_Roster *r = new JT_Roster(rootTask());
+	connect(r, SIGNAL(finished()), SLOT(slotRosterRequestFinished()));
+	r->get();
+	d->roster.flagAllForDelete(); // mod_groups patch
+	r->go(true);
+}
+
+void Client::slotRosterRequestFinished()
+{
+	JT_Roster *r = (JT_Roster *)sender();
+	// on success, let's take it
+	if(r->success()) {
+		//d->roster.flagAllForDelete(); // mod_groups patch
+
+		importRoster(r->roster());
+
+		for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end();) {
+			LiveRosterItem &i = *it;
+			if(i.flagForDelete()) {
+				rosterItemRemoved(i);
+				it = d->roster.remove(it);
+			}
+			else
+				++it;
+		}
+	}
+	else {
+		// don't report a disconnect.  Client::error() will do that.
+		if(r->statusCode() == Task::ErrDisc)
+			return;
+	}
+
+	// report success / fail
+	rosterRequestFinished(r->success(), r->statusCode(), r->statusString());
+}
+
+void Client::importRoster(const Roster &r)
+{
+	for(Roster::ConstIterator it = r.begin(); it != r.end(); ++it) {
+		importRosterItem(*it);
+	}
+}
+
+void Client::importRosterItem(const RosterItem &item)
+{
+	QString substr;
+	switch(item.subscription().type()) {
+		case Subscription::Both:
+			substr = "<-->";  break;
+		case Subscription::From:
+			substr = "  ->";  break;
+		case Subscription::To:
+			substr = "<-  ";  break;
+		case Subscription::Remove:
+			substr = "xxxx";  break;
+		case Subscription::None:
+		default:
+			substr = "----";  break;
+	}
+
+	QString dstr, str;
+	str.sprintf("  %s %-32s", substr.latin1(), item.jid().full().latin1());
+	if(!item.name().isEmpty())
+		str += QString(" [") + item.name() + "]";
+	str += '\n';
+
+	// Remove
+	if(item.subscription().type() == Subscription::Remove) {
+		LiveRoster::Iterator it = d->roster.find(item.jid());
+		if(it != d->roster.end()) {
+			rosterItemRemoved(*it);
+			d->roster.remove(it);
+		}
+		dstr = "Client: (Removed) ";
+	}
+	// Add/Update
+	else {
+		LiveRoster::Iterator it = d->roster.find(item.jid());
+		if(it != d->roster.end()) {
+			LiveRosterItem &i = *it;
+			i.setFlagForDelete(false);
+			i.setRosterItem(item);
+			rosterItemUpdated(i);
+			dstr = "Client: (Updated) ";
+                }
+		else {
+			LiveRosterItem i(item);
+			d->roster += i;
+
+			// signal it
+			rosterItemAdded(i);
+			dstr = "Client: (Added)   ";
+		}
+	}
+
+	debug(dstr + str);
+}
+
+void Client::sendMessage(const Message &m)
+{
+	JT_Message *j = new JT_Message(rootTask(), m);
+	j->go(true);
+}
+
+void Client::sendSubscription(const Jid &jid, const QString &type)
+{
+	JT_Presence *j = new JT_Presence(rootTask());
+	j->sub(jid, type);
+	j->go(true);
+}
+
+void Client::setPresence(const Status &s)
+{
+	JT_Presence *j = new JT_Presence(rootTask());
+	j->pres(s);
+	j->go(true);
+
+	// update our resourceList
+	ppPresence(jid(), s);
+	//ResourceList::Iterator rit = d->resourceList.find(resource());
+	//Resource &r = *rit;
+	//r.setStatus(s);
+}
+
+QString Client::OSName() const
+{
+	return d->osname;
+}
+
+QString Client::timeZone() const
+{
+	return d->tzname;
+}
+
+int Client::timeZoneOffset() const
+{
+	return d->tzoffset;
+}
+
+QString Client::clientName() const
+{
+	return d->clientName;
+}
+
+QString Client::clientVersion() const
+{
+	return d->clientVersion;
+}
+
+void Client::setOSName(const QString &name)
+{
+	d->osname = name;
+}
+
+void Client::setTimeZone(const QString &name, int offset)
+{
+	d->tzname = name;
+	d->tzoffset = offset;
+}
+
+void Client::setClientName(const QString &s)
+{
+	d->clientName = s;
+}
+
+void Client::setClientVersion(const QString &s)
+{
+	d->clientVersion = s;
+}
+
+void Client::s5b_incomingReady()
+{
+	S5BConnection *c = d->s5bman->takeIncoming();
+	if(!c)
+		return;
+	if(!d->ftman) {
+		c->close();
+		c->deleteLater();
+		return;
+	}
+	d->ftman->s5b_incomingReady(c);
+	//d->jlman->insertStream(c);
+	//incomingJidLink();
+}
+
+void Client::ibb_incomingReady()
+{
+	IBBConnection *c = d->ibbman->takeIncoming();
+	if(!c)
+		return;
+	c->deleteLater();
+	//d->jlman->insertStream(c);
+	//incomingJidLink();
+}
+
+//----------------------------------------------------------------------------
+// Task
+//----------------------------------------------------------------------------
+class Task::TaskPrivate
+{
+public:
+	TaskPrivate() {}
+
+	QString id;
+	bool success;
+	int statusCode;
+	QString statusString;
+	Client *client;
+	bool insig, deleteme, autoDelete;
+	bool done;
+};
+
+Task::Task(Task *parent)
+:QObject(parent)
+{
+	init();
+
+	d->client = parent->client();
+	d->id = client()->genUniqueId();
+	connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
+}
+
+Task::Task(Client *parent, bool)
+:QObject(0)
+{
+	init();
+
+	d->client = parent;
+	connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
+}
+
+Task::~Task()
+{
+	delete d;
+}
+
+void Task::init()
+{
+	d = new TaskPrivate;
+	d->success = false;
+	d->insig = false;
+	d->deleteme = false;
+	d->autoDelete = false;
+	d->done = false;
+}
+
+Task *Task::parent() const
+{
+	return (Task *)QObject::parent();
+}
+
+Client *Task::client() const
+{
+	return d->client;
+}
+
+QDomDocument *Task::doc() const
+{
+	return client()->doc();
+}
+
+QString Task::id() const
+{
+	return d->id;
+}
+
+bool Task::success() const
+{
+	return d->success;
+}
+
+int Task::statusCode() const
+{
+	return d->statusCode;
+}
+
+const QString & Task::statusString() const
+{
+	return d->statusString;
+}
+
+void Task::go(bool autoDelete)
+{
+	d->autoDelete = autoDelete;
+
+	onGo();
+}
+
+bool Task::take(const QDomElement &x)
+{
+	const QObjectList *p = children();
+	if(!p)
+		return false;
+
+	// pass along the xml
+	QObjectListIt it(*p);
+	Task *t;
+	for(; it.current(); ++it) {
+		QObject *obj = it.current();
+		if(!obj->inherits("XMPP::Task"))
+			continue;
+
+		t = static_cast<Task*>(obj);
+		if(t->take(x))
+			return true;
+	}
+
+	return false;
+}
+
+void Task::safeDelete()
+{
+	if(d->deleteme)
+		return;
+
+	d->deleteme = true;
+	if(!d->insig)
+		SafeDelete::deleteSingle(this);
+}
+
+void Task::onGo()
+{
+}
+
+void Task::onDisconnect()
+{
+	if(!d->done) {
+		d->success = false;
+		d->statusCode = ErrDisc;
+		d->statusString = tr("Disconnected");
+
+		// delay this so that tasks that react don't block the shutdown
+		QTimer::singleShot(0, this, SLOT(done()));
+	}
+}
+
+void Task::send(const QDomElement &x)
+{
+	client()->send(x);
+}
+
+void Task::setSuccess(int code, const QString &str)
+{
+	if(!d->done) {
+		d->success = true;
+		d->statusCode = code;
+		d->statusString = str;
+		done();
+	}
+}
+
+void Task::setError(const QDomElement &e)
+{
+	if(!d->done) {
+		d->success = false;
+		getErrorFromElement(e, &d->statusCode, &d->statusString);
+		done();
+	}
+}
+
+void Task::setError(int code, const QString &str)
+{
+	if(!d->done) {
+		d->success = false;
+		d->statusCode = code;
+		d->statusString = str;
+		done();
+	}
+}
+
+void Task::done()
+{
+	if(d->done || d->insig)
+		return;
+	d->done = true;
+
+	if(d->deleteme || d->autoDelete)
+		d->deleteme = true;
+
+	d->insig = true;
+	finished();
+	d->insig = false;
+
+	if(d->deleteme)
+		SafeDelete::deleteSingle(this);
+}
+
+void Task::clientDisconnected()
+{
+	onDisconnect();
+}
+
+void Task::debug(const char *fmt, ...)
+{
+	char *buf;
+	QString str;
+	int size = 1024;
+	int r;
+
+	do {
+		buf = new char[size];
+		va_list ap;
+		va_start(ap, fmt);
+		r = vsnprintf(buf, size, fmt, ap);
+		va_end(ap);
+
+		if(r != -1)
+			str = QString(buf);
+
+		delete [] buf;
+
+		size *= 2;
+	} while(r == -1);
+
+	debug(str);
+}
+
+void Task::debug(const QString &str)
+{
+	client()->debug(QString("%1: ").arg(className()) + str);
+}
+
+bool Task::iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns)
+{
+	if(x.tagName() != "iq")
+		return false;
+
+	Jid from(x.attribute("from"));
+	Jid local = client()->jid();
+	Jid server = client()->host();
+
+	// empty 'from' ?
+	if(from.isEmpty()) {
+		// allowed if we are querying the server
+		if(!to.isEmpty() && !to.compare(server))
+			return false;
+	}
+	// from ourself?
+	else if(from.compare(local, false)) {
+		// allowed if we are querying ourself or the server
+		if(!to.isEmpty() && !to.compare(local, false) && !to.compare(server))
+			return false;
+	}
+	// from anywhere else?
+	else {
+		if(!from.compare(to))
+			return false;
+	}
+
+	if(!id.isEmpty()) {
+		if(x.attribute("id") != id)
+			return false;
+	}
+
+	if(!xmlns.isEmpty()) {
+		if(queryNS(x) != xmlns)
+			return false;
+	}
+
+	return true;
+}
+
+//---------------------------------------------------------------------------
+// LiveRosterItem
+//---------------------------------------------------------------------------
+LiveRosterItem::LiveRosterItem(const Jid &jid)
+:RosterItem(jid)
+{
+	setFlagForDelete(false);
+}
+
+LiveRosterItem::LiveRosterItem(const RosterItem &i)
+{
+	setRosterItem(i);
+	setFlagForDelete(false);
+}
+
+LiveRosterItem::~LiveRosterItem()
+{
+}
+
+void LiveRosterItem::setRosterItem(const RosterItem &i)
+{
+	setJid(i.jid());
+	setName(i.name());
+	setGroups(i.groups());
+	setSubscription(i.subscription());
+	setAsk(i.ask());
+	setIsPush(i.isPush());
+}
+
+ResourceList & LiveRosterItem::resourceList()
+{
+	return v_resourceList;
+}
+
+ResourceList::Iterator LiveRosterItem::priority()
+{
+	return v_resourceList.priority();
+}
+
+const ResourceList & LiveRosterItem::resourceList() const
+{
+	return v_resourceList;
+}
+
+ResourceList::ConstIterator LiveRosterItem::priority() const
+{
+	return v_resourceList.priority();
+}
+
+bool LiveRosterItem::isAvailable() const
+{
+	if(v_resourceList.count() > 0)
+		return true;
+	return false;
+}
+
+const Status & LiveRosterItem::lastUnavailableStatus() const
+{
+	return v_lastUnavailableStatus;
+}
+
+bool LiveRosterItem::flagForDelete() const
+{
+	return v_flagForDelete;
+}
+
+void LiveRosterItem::setLastUnavailableStatus(const Status &s)
+{
+	v_lastUnavailableStatus = s;
+}
+
+void LiveRosterItem::setFlagForDelete(bool b)
+{
+	v_flagForDelete = b;
+}
+
+//---------------------------------------------------------------------------
+// LiveRoster
+//---------------------------------------------------------------------------
+LiveRoster::LiveRoster()
+:QValueList<LiveRosterItem>()
+{
+}
+
+LiveRoster::~LiveRoster()
+{
+}
+
+void LiveRoster::flagAllForDelete()
+{
+	for(Iterator it = begin(); it != end(); ++it)
+		(*it).setFlagForDelete(true);
+}
+
+LiveRoster::Iterator LiveRoster::find(const Jid &j, bool compareRes)
+{
+	Iterator it;
+	for(it = begin(); it != end(); ++it) {
+		if((*it).jid().compare(j, compareRes))
+			break;
+	}
+	return it;
+}
+
+LiveRoster::ConstIterator LiveRoster::find(const Jid &j, bool compareRes) const
+{
+	ConstIterator it;
+	for(it = begin(); it != end(); ++it) {
+		if((*it).jid().compare(j, compareRes))
+			break;
+	}
+	return it;
+}
+
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-im/types.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-im/types.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-im/types.cpp	(revision 38)
@@ -0,0 +1,1738 @@
+/*
+ * types.cpp - IM data types
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"im.h"
+
+#include<qmap.h>
+#include<qapplication.h>
+
+#define NS_XML     "http://www.w3.org/XML/1998/namespace"
+
+static QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
+{
+	QDomElement tag = doc->createElement(name);
+	QDomText text = doc->createTextNode(content);
+	tag.appendChild(text);
+
+	return tag;
+}
+
+static QString tagContent(const QDomElement &e)
+{
+	// look for some tag content
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomText i = n.toText();
+		if(i.isNull())
+			continue;
+		return i.data();
+	}
+
+	return "";
+}
+
+static QDateTime stamp2TS(const QString &ts)
+{
+	if(ts.length() != 17)
+		return QDateTime();
+
+	int year  = ts.mid(0,4).toInt();
+	int month = ts.mid(4,2).toInt();
+	int day   = ts.mid(6,2).toInt();
+
+	int hour  = ts.mid(9,2).toInt();
+	int min   = ts.mid(12,2).toInt();
+	int sec   = ts.mid(15,2).toInt();
+
+	QDate xd;
+	xd.setYMD(year, month, day);
+	if(!xd.isValid())
+		return QDateTime();
+
+	QTime xt;
+	xt.setHMS(hour, min, sec);
+	if(!xt.isValid())
+		return QDateTime();
+
+	return QDateTime(xd, xt);
+}
+
+/*static QString TS2stamp(const QDateTime &d)
+{
+	QString str;
+
+	str.sprintf("%04d%02d%02dT%02d:%02d:%02d",
+		d.date().year(),
+		d.date().month(),
+		d.date().day(),
+		d.time().hour(),
+		d.time().minute(),
+		d.time().second());
+
+	return str;
+}*/
+
+namespace XMPP
+{
+
+//----------------------------------------------------------------------------
+// Url
+//----------------------------------------------------------------------------
+class Url::Private
+{
+public:
+	QString url;
+	QString desc;
+};
+
+//! \brief Construct Url object with a given URL and Description.
+//!
+//! This function will construct a Url object.
+//! \param QString - url (default: empty string)
+//! \param QString - description of url (default: empty string)
+//! \sa setUrl() setDesc()
+Url::Url(const QString &url, const QString &desc)
+{
+	d = new Private;
+	d->url = url;
+	d->desc = desc;
+}
+
+//! \brief Construct Url object.
+//!
+//! Overloaded constructor which will constructs a exact copy of the Url object that was passed to the constructor.
+//! \param Url - Url Object
+Url::Url(const Url &from)
+{
+	d = new Private;
+	*this = from;
+}
+
+//! \brief operator overloader needed for d pointer (Internel).
+Url & Url::operator=(const Url &from)
+{
+	*d = *from.d;
+	return *this;
+}
+
+//! \brief destroy Url object.
+Url::~Url()
+{
+	delete d;
+}
+
+//! \brief Get url information.
+//!
+//! Returns url information.
+QString Url::url() const
+{
+	return d->url;
+}
+
+//! \brief Get Description information.
+//!
+//! Returns desction of the URL.
+QString Url::desc() const
+{
+	return d->desc;
+}
+
+//! \brief Set Url information.
+//!
+//! Set url information.
+//! \param url - url string (eg: http://psi.affinix.com/)
+void Url::setUrl(const QString &url)
+{
+	d->url = url;
+}
+
+//! \brief Set Description information.
+//!
+//! Set description of the url.
+//! \param desc - description of url
+void Url::setDesc(const QString &desc)
+{
+	d->desc = desc;
+}
+
+//----------------------------------------------------------------------------
+// Message
+//----------------------------------------------------------------------------
+class Message::Private
+{
+public:
+	Jid to, from;
+	QString id, type, lang;
+
+	StringMap subject, body;
+	QString thread;
+	Stanza::Error error;
+
+	// extensions
+	QDateTime timeStamp;
+	UrlList urlList;
+	QValueList<MsgEvent> eventList;
+	QString eventId;
+	QString xencrypted, invite;
+
+	bool spooled, wasEncrypted;
+};
+
+//! \brief Constructs Message with given Jid information.
+//!
+//! This function will construct a Message container.
+//! \param to - specify reciver (default: empty string)
+Message::Message(const Jid &to)
+{
+	d = new Private;
+	d->to = to;
+	d->spooled = false;
+	d->wasEncrypted = false;
+	/*d->flag = false;
+	d->spooled = false;
+	d->wasEncrypted = false;
+	d->errorCode = -1;*/
+}
+
+//! \brief Constructs a copy of Message object
+//!
+//! Overloaded constructor which will constructs a exact copy of the Message
+//! object that was passed to the constructor.
+//! \param from - Message object you want to copy
+Message::Message(const Message &from)
+{
+	d = new Private;
+	*this = from;
+}
+
+//! \brief Required for internel use.
+Message & Message::operator=(const Message &from)
+{
+	*d = *from.d;
+	return *this;
+}
+
+//! \brief Destroy Message object.
+Message::~Message()
+{
+	delete d;
+}
+
+//! \brief Return receiver's Jid information.
+Jid Message::to() const
+{
+	return d->to;
+}
+
+//! \brief Return sender's Jid information.
+Jid Message::from() const
+{
+	return d->from;
+}
+
+QString Message::id() const
+{
+	return d->id;
+}
+
+//! \brief Return type information
+QString Message::type() const
+{
+	return d->type;
+}
+
+QString Message::lang() const
+{
+	return d->lang;
+}
+
+//! \brief Return subject information.
+QString Message::subject(const QString &lang) const
+{
+	return d->subject[lang];
+}
+
+//! \brief Return body information.
+//!
+//! This function will return a plain text or the Richtext version if it
+//! it exists.
+//! \param rich - Returns richtext if true and plain text if false. (default: false)
+//! \note Richtext is in Qt's richtext format and not in xhtml.
+QString Message::body(const QString &lang) const
+{
+	return d->body[lang];
+}
+
+QString Message::thread() const
+{
+	return d->thread;
+}
+
+Stanza::Error Message::error() const
+{
+	return d->error;
+}
+
+//! \brief Set receivers information
+//!
+//! \param to - Receivers Jabber id
+void Message::setTo(const Jid &j)
+{
+	d->to = j;
+	//d->flag = false;
+}
+
+void Message::setFrom(const Jid &j)
+{
+	d->from = j;
+	//d->flag = false;
+}
+
+void Message::setId(const QString &s)
+{
+	d->id = s;
+}
+
+//! \brief Set Type of message
+//!
+//! \param type - type of message your going to send
+void Message::setType(const QString &s)
+{
+	d->type = s;
+	//d->flag = false;
+}
+
+void Message::setLang(const QString &s)
+{
+	d->lang = s;
+}
+
+//! \brief Set subject
+//!
+//! \param subject - Subject information
+void Message::setSubject(const QString &s, const QString &lang)
+{
+	d->subject[lang] = s;
+	//d->flag = false;
+}
+
+//! \brief Set body
+//!
+//! \param body - body information
+//! \param rich - set richtext if true and set plaintext if false.
+//! \note Richtext support will be implemented in the future... Sorry.
+void Message::setBody(const QString &s, const QString &lang)
+{
+	d->body[lang] = s;
+	//d->flag = false;
+}
+
+void Message::setThread(const QString &s)
+{
+	d->thread = s;
+}
+
+void Message::setError(const Stanza::Error &err)
+{
+	d->error = err;
+}
+
+QDateTime Message::timeStamp() const
+{
+	return d->timeStamp;
+}
+
+void Message::setTimeStamp(const QDateTime &ts)
+{
+	d->timeStamp = ts;
+}
+
+//! \brief Return list of urls attached to message.
+UrlList Message::urlList() const
+{
+	return d->urlList;
+}
+
+//! \brief Add Url to the url list.
+//!
+//! \param url - url to append
+void Message::urlAdd(const Url &u)
+{
+	d->urlList += u;
+}
+
+//! \brief clear out the url list.
+void Message::urlsClear()
+{
+	d->urlList.clear();
+}
+
+//! \brief Set urls to send
+//!
+//! \param urlList - list of urls to send
+void Message::setUrlList(const UrlList &list)
+{
+	d->urlList = list;
+}
+
+QString Message::eventId() const
+{
+	return d->eventId;
+}
+
+void Message::setEventId(const QString& id)
+{
+	d->eventId = id;
+}
+
+bool Message::containsEvents() const
+{
+	return !d->eventList.isEmpty();
+}
+
+bool Message::containsEvent(MsgEvent e) const
+{
+	return d->eventList.contains(e);
+}
+
+void Message::addEvent(MsgEvent e)
+{
+	if (!d->eventList.contains(e)) {
+		if (e == CancelEvent || containsEvent(CancelEvent)) 
+			d->eventList.clear(); // Reset list
+		d->eventList += e;
+	}
+}
+
+QString Message::xencrypted() const
+{
+	return d->xencrypted;
+}
+
+void Message::setXEncrypted(const QString &s)
+{
+	d->xencrypted = s;
+}
+
+QString Message::invite() const
+{
+	return d->invite;
+}
+
+void Message::setInvite(const QString &s)
+{
+	d->invite = s;
+}
+
+bool Message::spooled() const
+{
+	return d->spooled;
+}
+
+void Message::setSpooled(bool b)
+{
+	d->spooled = b;
+}
+
+bool Message::wasEncrypted() const
+{
+	return d->wasEncrypted;
+}
+
+void Message::setWasEncrypted(bool b)
+{
+	d->wasEncrypted = b;
+}
+
+Stanza Message::toStanza(Stream *stream) const
+{
+	Stanza s = stream->createStanza(Stanza::Message, d->to, d->type);
+	if(!d->from.isEmpty())
+		s.setFrom(d->from);
+	if(!d->id.isEmpty())
+		s.setId(d->id);
+	if(!d->lang.isEmpty())
+		s.setLang(d->lang);
+
+	StringMap::ConstIterator it;
+	for(it = d->subject.begin(); it != d->subject.end(); ++it) {
+		const QString &str = it.data();
+		if(!str.isEmpty()) {
+			QDomElement e = s.createTextElement(s.baseNS(), "subject", str);
+			if(!it.key().isEmpty())
+				e.setAttributeNS(NS_XML, "xml:lang", it.key());
+			s.appendChild(e);
+		}
+	}
+	for(it = d->body.begin(); it != d->body.end(); ++it) {
+		const QString &str = it.data();
+		if(!str.isEmpty()) {
+			QDomElement e = s.createTextElement(s.baseNS(), "body", str);
+			if(!it.key().isEmpty())
+				e.setAttributeNS(NS_XML, "xml:lang", it.key());
+			s.appendChild(e);
+		}
+	}
+
+	if(d->type == "error")
+		s.setError(d->error);
+
+	// timestamp
+	/*if(!d->timeStamp.isNull()) {
+		QDomElement e = s.createElement("jabber:x:delay", "x");
+		e.setAttribute("stamp", TS2stamp(d->timeStamp));
+		s.appendChild(e);
+	}*/
+
+	// urls
+	for(QValueList<Url>::ConstIterator uit = d->urlList.begin(); uit != d->urlList.end(); ++uit) {
+		QDomElement x = s.createElement("jabber:x:oob", "x");
+		x.appendChild(s.createTextElement("jabber:x:oob", "url", (*uit).url()));
+		if(!(*uit).desc().isEmpty())
+			x.appendChild(s.createTextElement("jabber:x:oob", "desc", (*uit).desc()));
+		s.appendChild(x);
+	}
+
+	// events
+	if (!d->eventList.isEmpty()) {
+		QDomElement x = s.createElement("jabber:x:event", "x");
+
+		if (d->body.isEmpty()) {
+			if (d->eventId.isEmpty())
+				x.appendChild(s.createElement("jabber:x:event","id"));
+			else
+				x.appendChild(s.createTextElement("jabber:x:event","id",d->eventId));
+		}
+
+		for(QValueList<MsgEvent>::ConstIterator ev = d->eventList.begin(); ev != d->eventList.end(); ++ev) {
+			switch (*ev) {
+				case OfflineEvent:
+					x.appendChild(s.createElement("jabber:x:event", "offline"));
+					break;
+				case DeliveredEvent:
+					x.appendChild(s.createElement("jabber:x:event", "delivered"));
+					break;
+				case DisplayedEvent:
+					x.appendChild(s.createElement("jabber:x:event", "displayed"));
+					break;
+				case ComposingEvent: 
+					x.appendChild(s.createElement("jabber:x:event", "composing"));
+					break;
+				case CancelEvent:
+					// Add nothing
+					break;
+			}
+		}
+		s.appendChild(x);
+	} 
+
+	// xencrypted
+	if(!d->xencrypted.isEmpty())
+		s.appendChild(s.createTextElement("jabber:x:encrypted", "x", d->xencrypted));
+
+	// invite
+	if(!d->invite.isEmpty()) {
+		QDomElement e = s.createElement("jabber:x:conference", "x");
+		e.setAttribute("jid", d->invite);
+		s.appendChild(e);
+	}
+
+	return s;
+}
+
+bool Message::fromStanza(const Stanza &s, int timeZoneOffset)
+{
+	if(s.kind() != Stanza::Message)
+		return false;
+
+	setTo(s.to());
+	setFrom(s.from());
+	setId(s.id());
+	setType(s.type());
+	setLang(s.lang());
+
+	d->subject.clear();
+	d->body.clear();
+	d->thread = QString();
+
+	QDomElement root = s.element();
+
+	QDomNodeList nl = root.childNodes();
+	uint n;
+	for(n = 0; n < nl.count(); ++n) {
+		QDomNode i = nl.item(n);
+		if(i.isElement()) {
+			QDomElement e = i.toElement();
+			if(e.namespaceURI() == s.baseNS()) {
+				if(e.tagName() == "subject") {
+					QString lang = e.attributeNS(NS_XML, "lang", "");
+					d->subject[lang] = e.text();
+				}
+				else if(e.tagName() == "body") {
+					QString lang = e.attributeNS(NS_XML, "lang", "");
+					d->body[lang] = e.text();
+				}
+				else if(e.tagName() == "thread")
+					d->thread = e.text();
+			}
+			else {
+				//printf("extension element: [%s]\n", e.tagName().latin1());
+			}
+		}
+	}
+
+	if(s.type() == "error")
+		d->error = s.error();
+
+	// timestamp
+	QDomElement t = root.elementsByTagNameNS("jabber:x:delay", "x").item(0).toElement();
+	if(!t.isNull()) {
+		d->timeStamp = stamp2TS(t.attribute("stamp"));
+		d->timeStamp = d->timeStamp.addSecs(timeZoneOffset * 3600);
+		d->spooled = true;
+	}
+	else {
+		d->timeStamp = QDateTime::currentDateTime();
+		d->spooled = false;
+	}
+
+	// urls
+	d->urlList.clear();
+	nl = root.elementsByTagNameNS("jabber:x:oob", "x");
+	for(n = 0; n < nl.count(); ++n) {
+		QDomElement t = nl.item(n).toElement();
+		Url u;
+		u.setUrl(t.elementsByTagName("url").item(0).toElement().text());
+		u.setDesc(t.elementsByTagName("desc").item(0).toElement().text());
+		d->urlList += u;
+	}
+	
+    // events
+	d->eventList.clear();
+	nl = root.elementsByTagNameNS("jabber:x:event", "x");
+	if (nl.count()) {
+		nl = nl.item(0).childNodes();
+		for(n = 0; n < nl.count(); ++n) {
+			QString evtag = nl.item(n).toElement().tagName();
+			if (evtag == "id") {
+				d->eventId =  nl.item(n).toElement().text();
+			}
+			else if (evtag == "displayed")
+				d->eventList += DisplayedEvent;
+			else if (evtag == "composing")
+				d->eventList += ComposingEvent;
+			else if (evtag == "delivered")
+				d->eventList += DeliveredEvent;
+		}
+		if (d->eventList.isEmpty())
+			d->eventList += CancelEvent;
+	}
+
+	// xencrypted
+	t = root.elementsByTagNameNS("jabber:x:encrypted", "x").item(0).toElement();
+	if(!t.isNull())
+		d->xencrypted = t.text();
+	else
+		d->xencrypted = QString();
+
+	// invite
+	t = root.elementsByTagNameNS("jabber:x:conference", "x").item(0).toElement();
+	if(!t.isNull())
+		d->invite = t.attribute("jid");
+	else
+		d->invite = QString();
+
+	return true;
+}
+
+//---------------------------------------------------------------------------
+// Subscription
+//---------------------------------------------------------------------------
+Subscription::Subscription(SubType type)
+{
+	value = type;
+}
+
+int Subscription::type() const
+{
+	return value;
+}
+
+QString Subscription::toString() const
+{
+	switch(value) {
+		case Remove:
+			return "remove";
+		case Both:
+			return "both";
+		case From:
+			return "from";
+		case To:
+			return "to";
+		case None:
+		default:
+			return "none";
+	}
+}
+
+bool Subscription::fromString(const QString &s)
+{
+	if(s == "remove")
+		value = Remove;
+	else if(s == "both")
+		value = Both;
+	else if(s == "from")
+		value = From;
+	else if(s == "to")
+		value = To;
+	else if(s == "none")
+		value = None;
+	else
+		return false;
+
+	return true;
+}
+
+
+//---------------------------------------------------------------------------
+// Status
+//---------------------------------------------------------------------------
+Status::Status(const QString &show, const QString &status, int priority, bool available)
+{
+	v_isAvailable = available;
+	v_show = show;
+	v_status = status;
+	v_priority = priority;
+	v_timeStamp = QDateTime::currentDateTime();
+	v_isInvisible = false;
+	ecode = -1;
+}
+
+Status::~Status()
+{
+}
+
+bool Status::hasError() const
+{
+	return (ecode != -1);
+}
+
+void Status::setError(int code, const QString &str)
+{
+	ecode = code;
+	estr = str;
+}
+
+void Status::setIsAvailable(bool available)
+{
+	v_isAvailable = available;
+}
+
+void Status::setIsInvisible(bool invisible)
+{
+	v_isInvisible = invisible;
+}
+
+void Status::setPriority(int x)
+{
+	v_priority = x;
+}
+
+void Status::setShow(const QString & _show)
+{
+	v_show = _show;
+}
+
+void Status::setStatus(const QString & _status)
+{
+	v_status = _status;
+}
+
+void Status::setTimeStamp(const QDateTime & _timestamp)
+{
+	v_timeStamp = _timestamp;
+}
+
+void Status::setKeyID(const QString &key)
+{
+	v_key = key;
+}
+
+void Status::setXSigned(const QString &s)
+{
+	v_xsigned = s;
+}
+
+void Status::setSongTitle(const QString & _songtitle)
+{
+	v_songTitle = _songtitle;
+}
+
+bool Status::isAvailable() const
+{
+	return v_isAvailable;
+}
+
+bool Status::isAway() const
+{
+	if(v_show == "away" || v_show == "xa" || v_show == "dnd")
+		return true;
+
+	return false;
+}
+
+bool Status::isInvisible() const
+{
+	return v_isInvisible;
+}
+
+int Status::priority() const
+{
+	return v_priority;
+}
+
+const QString & Status::show() const
+{
+	return v_show;
+}
+const QString & Status::status() const
+{
+	return v_status;
+}
+
+QDateTime Status::timeStamp() const
+{
+	return v_timeStamp;
+}
+
+const QString & Status::keyID() const
+{
+	return v_key;
+}
+
+const QString & Status::xsigned() const
+{
+	return v_xsigned;
+}
+
+const QString & Status::songTitle() const
+{
+	return v_songTitle;
+}
+
+int Status::errorCode() const
+{
+	return ecode;
+}
+
+const QString & Status::errorString() const
+{
+	return estr;
+}
+
+
+//---------------------------------------------------------------------------
+// Resource
+//---------------------------------------------------------------------------
+Resource::Resource(const QString &name, const Status &stat)
+{
+	v_name = name;
+	v_status = stat;
+}
+
+Resource::~Resource()
+{
+}
+
+const QString & Resource::name() const
+{
+	return v_name;
+}
+
+int Resource::priority() const
+{
+	return v_status.priority();
+}
+
+const Status & Resource::status() const
+{
+	return v_status;
+}
+
+void Resource::setName(const QString & _name)
+{
+	v_name = _name;
+}
+
+void Resource::setStatus(const Status & _status)
+{
+	v_status = _status;
+}
+
+
+//---------------------------------------------------------------------------
+// ResourceList
+//---------------------------------------------------------------------------
+ResourceList::ResourceList()
+:QValueList<Resource>()
+{
+}
+
+ResourceList::~ResourceList()
+{
+}
+
+ResourceList::Iterator ResourceList::find(const QString & _find)
+{
+	for(ResourceList::Iterator it = begin(); it != end(); ++it) {
+		if((*it).name() == _find)
+			return it;
+	}
+
+	return end();
+}
+
+ResourceList::Iterator ResourceList::priority()
+{
+	ResourceList::Iterator highest = end();
+
+	for(ResourceList::Iterator it = begin(); it != end(); ++it) {
+		if(highest == end() || (*it).priority() > (*highest).priority())
+			highest = it;
+	}
+
+	return highest;
+}
+
+ResourceList::ConstIterator ResourceList::find(const QString & _find) const
+{
+	for(ResourceList::ConstIterator it = begin(); it != end(); ++it) {
+		if((*it).name() == _find)
+			return it;
+	}
+
+	return end();
+}
+
+ResourceList::ConstIterator ResourceList::priority() const
+{
+	ResourceList::ConstIterator highest = end();
+
+	for(ResourceList::ConstIterator it = begin(); it != end(); ++it) {
+		if(highest == end() || (*it).priority() > (*highest).priority())
+			highest = it;
+	}
+
+	return highest;
+}
+
+
+//---------------------------------------------------------------------------
+// RosterItem
+//---------------------------------------------------------------------------
+RosterItem::RosterItem(const Jid &_jid)
+{
+	v_jid = _jid;
+	v_push = false;
+}
+
+RosterItem::~RosterItem()
+{
+}
+
+const Jid & RosterItem::jid() const
+{
+	return v_jid;
+}
+
+const QString & RosterItem::name() const
+{
+	return v_name;
+}
+
+const QStringList & RosterItem::groups() const
+{
+	return v_groups;
+}
+
+const Subscription & RosterItem::subscription() const
+{
+	return v_subscription;
+}
+
+const QString & RosterItem::ask() const
+{
+	return v_ask;
+}
+
+bool RosterItem::isPush() const
+{
+	return v_push;
+}
+
+bool RosterItem::inGroup(const QString &g) const
+{
+	for(QStringList::ConstIterator it = v_groups.begin(); it != v_groups.end(); ++it) {
+		if(*it == g)
+			return true;
+	}
+	return false;
+}
+
+void RosterItem::setJid(const Jid &_jid)
+{
+	v_jid = _jid;
+}
+
+void RosterItem::setName(const QString &_name)
+{
+	v_name = _name;
+}
+
+void RosterItem::setGroups(const QStringList &_groups)
+{
+	v_groups = _groups;
+}
+
+void RosterItem::setSubscription(const Subscription &type)
+{
+	v_subscription = type;
+}
+
+void RosterItem::setAsk(const QString &_ask)
+{
+	v_ask = _ask;
+}
+
+void RosterItem::setIsPush(bool b)
+{
+	v_push = b;
+}
+
+bool RosterItem::addGroup(const QString &g)
+{
+	if(inGroup(g))
+		return false;
+
+	v_groups += g;
+	return true;
+}
+
+bool RosterItem::removeGroup(const QString &g)
+{
+	for(QStringList::Iterator it = v_groups.begin(); it != v_groups.end(); ++it) {
+		if(*it == g) {
+			v_groups.remove(it);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+QDomElement RosterItem::toXml(QDomDocument *doc) const
+{
+	QDomElement item = doc->createElement("item");
+	item.setAttribute("jid", v_jid.full());
+	item.setAttribute("name", v_name);
+	item.setAttribute("subscription", v_subscription.toString());
+	if(!v_ask.isEmpty())
+		item.setAttribute("ask", v_ask);
+	for(QStringList::ConstIterator it = v_groups.begin(); it != v_groups.end(); ++it)
+		item.appendChild(textTag(doc, "group", *it));
+
+	return item;
+}
+
+bool RosterItem::fromXml(const QDomElement &item)
+{
+	if(item.tagName() != "item")
+		return false;
+	Jid j(item.attribute("jid"));
+	if(!j.isValid())
+		return false;
+	QString na = item.attribute("name");
+	Subscription s;
+	if(!s.fromString(item.attribute("subscription")) )
+		return false;
+	QStringList g;
+	for(QDomNode n = item.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+		if(i.tagName() == "group")
+			g += tagContent(i);
+	}
+	QString a = item.attribute("ask");
+
+	v_jid = j;
+	v_name = na;
+	v_subscription = s;
+	v_groups = g;
+	v_ask = a;
+
+	return true;
+}
+
+
+//---------------------------------------------------------------------------
+// Roster
+//---------------------------------------------------------------------------
+Roster::Roster()
+:QValueList<RosterItem>()
+{
+}
+
+Roster::~Roster()
+{
+}
+
+Roster::Iterator Roster::find(const Jid &j)
+{
+	for(Roster::Iterator it = begin(); it != end(); ++it) {
+		if((*it).jid().compare(j))
+			return it;
+	}
+
+	return end();
+}
+
+Roster::ConstIterator Roster::find(const Jid &j) const
+{
+	for(Roster::ConstIterator it = begin(); it != end(); ++it) {
+		if((*it).jid().compare(j))
+			return it;
+	}
+
+	return end();
+}
+
+
+//---------------------------------------------------------------------------
+// FormField
+//---------------------------------------------------------------------------
+FormField::FormField(const QString &type, const QString &value)
+{
+	v_type = misc;
+	if(!type.isEmpty()) {
+		int x = tagNameToType(type);
+		if(x != -1)
+			v_type = x;
+	}
+	v_value = value;
+}
+
+FormField::~FormField()
+{
+}
+
+int FormField::type() const
+{
+	return v_type;
+}
+
+QString FormField::realName() const
+{
+	return typeToTagName(v_type);
+}
+
+QString FormField::fieldName() const
+{
+	switch(v_type) {
+		case username:  return QObject::tr("Username");
+		case nick:      return QObject::tr("Nickname");
+		case password:  return QObject::tr("Password");
+		case name:      return QObject::tr("Name");
+		case first:     return QObject::tr("First Name");
+		case last:      return QObject::tr("Last Name");
+		case email:     return QObject::tr("E-mail");
+		case address:   return QObject::tr("Address");
+		case city:      return QObject::tr("City");
+		case state:     return QObject::tr("State");
+		case zip:       return QObject::tr("Zipcode");
+		case phone:     return QObject::tr("Phone");
+		case url:       return QObject::tr("URL");
+		case date:      return QObject::tr("Date");
+		case misc:      return QObject::tr("Misc");
+		default:        return "";
+	};
+}
+
+bool FormField::isSecret() const
+{
+	return (type() == password);
+}
+
+const QString & FormField::value() const
+{
+	return v_value;
+}
+
+void FormField::setType(int x)
+{
+	v_type = x;
+}
+
+bool FormField::setType(const QString &in)
+{
+	int x = tagNameToType(in);
+	if(x == -1)
+		return false;
+
+	v_type = x;
+	return true;
+}
+
+void FormField::setValue(const QString &in)
+{
+	v_value = in;
+}
+
+int FormField::tagNameToType(const QString &in) const
+{
+	if(!in.compare("username")) return username;
+	if(!in.compare("nick"))     return nick;
+	if(!in.compare("password")) return password;
+	if(!in.compare("name"))     return name;
+	if(!in.compare("first"))    return first;
+	if(!in.compare("last"))     return last;
+	if(!in.compare("email"))    return email;
+	if(!in.compare("address"))  return address;
+	if(!in.compare("city"))     return city;
+	if(!in.compare("state"))    return state;
+	if(!in.compare("zip"))      return zip;
+	if(!in.compare("phone"))    return phone;
+	if(!in.compare("url"))      return url;
+	if(!in.compare("date"))     return date;
+	if(!in.compare("misc"))     return misc;
+
+	return -1;
+}
+
+QString FormField::typeToTagName(int type) const
+{
+	switch(type) {
+		case username:  return "username";
+		case nick:      return "nick";
+		case password:  return "password";
+		case name:      return "name";
+		case first:     return "first";
+		case last:      return "last";
+		case email:     return "email";
+		case address:   return "address";
+		case city:      return "city";
+		case state:     return "state";
+		case zip:       return "zipcode";
+		case phone:     return "phone";
+		case url:       return "url";
+		case date:      return "date";
+		case misc:      return "misc";
+		default:        return "";
+	};
+}
+
+
+//---------------------------------------------------------------------------
+// Form
+//---------------------------------------------------------------------------
+Form::Form(const Jid &j)
+:QValueList<FormField>()
+{
+	setJid(j);
+}
+
+Form::~Form()
+{
+}
+
+Jid Form::jid() const
+{
+	return v_jid;
+}
+
+QString Form::instructions() const
+{
+	return v_instructions;
+}
+
+QString Form::key() const
+{
+	return v_key;
+}
+
+void Form::setJid(const Jid &j)
+{
+	v_jid = j;
+}
+
+void Form::setInstructions(const QString &s)
+{
+	v_instructions = s;
+}
+
+void Form::setKey(const QString &s)
+{
+	v_key = s;
+}
+
+
+//---------------------------------------------------------------------------
+// SearchResult
+//---------------------------------------------------------------------------
+SearchResult::SearchResult(const Jid &jid)
+{
+	setJid(jid);
+}
+
+SearchResult::~SearchResult()
+{
+}
+
+const Jid & SearchResult::jid() const
+{
+	return v_jid;
+}
+
+const QString & SearchResult::nick() const
+{
+	return v_nick;
+}
+
+const QString & SearchResult::first() const
+{
+	return v_first;
+}
+
+const QString & SearchResult::last() const
+{
+	return v_last;
+}
+
+const QString & SearchResult::email() const
+{
+	return v_email;
+}
+
+void SearchResult::setJid(const Jid &jid)
+{
+	v_jid = jid;
+}
+
+void SearchResult::setNick(const QString &nick)
+{
+	v_nick = nick;
+}
+
+void SearchResult::setFirst(const QString &first)
+{
+	v_first = first;
+}
+
+void SearchResult::setLast(const QString &last)
+{
+	v_last = last;
+}
+
+void SearchResult::setEmail(const QString &email)
+{
+	v_email = email;
+}
+
+//---------------------------------------------------------------------------
+// Features
+//---------------------------------------------------------------------------
+
+Features::Features()
+{
+}
+
+Features::Features(const QStringList &l)
+{
+	setList(l);
+}
+
+Features::Features(const QString &str)
+{
+	QStringList l;
+	l << str;
+
+	setList(l);
+}
+
+Features::~Features()
+{
+}
+
+QStringList Features::list() const
+{
+	return _list;
+}
+
+void Features::setList(const QStringList &l)
+{
+	_list = l;
+}
+
+bool Features::test(const QStringList &ns) const
+{
+	QStringList::ConstIterator it = ns.begin();
+	for ( ; it != ns.end(); ++it)
+		if ( _list.find( *it ) != _list.end() )
+			return true;
+
+	return false;
+}
+
+#define FID_REGISTER "jabber:iq:register"
+bool Features::canRegister() const
+{
+	QStringList ns;
+	ns << FID_REGISTER;
+
+	return test(ns);
+}
+
+#define FID_SEARCH "jabber:iq:search"
+bool Features::canSearch() const
+{
+	QStringList ns;
+	ns << FID_SEARCH;
+
+	return test(ns);
+}
+
+#define FID_GROUPCHAT "jabber:iq:conference"
+bool Features::canGroupchat() const
+{
+	QStringList ns;
+	ns << "http://jabber.org/protocol/muc";
+	ns << FID_GROUPCHAT;
+
+	return test(ns);
+}
+
+#define FID_GATEWAY "jabber:iq:gateway"
+bool Features::isGateway() const
+{
+	QStringList ns;
+	ns << FID_GATEWAY;
+
+	return test(ns);
+}
+
+#define FID_DISCO "http://jabber.org/protocol/disco"
+bool Features::canDisco() const
+{
+	QStringList ns;
+	ns << FID_DISCO;
+	ns << "http://jabber.org/protocol/disco#info";
+	ns << "http://jabber.org/protocol/disco#items";
+
+	return test(ns);
+}
+
+#define FID_VCARD "vcard-temp"
+bool Features::haveVCard() const
+{
+	QStringList ns;
+	ns << FID_VCARD;
+
+	return test(ns);
+}
+
+// custom Psi acitons
+#define FID_ADD "psi:add"
+
+class Features::FeatureName : public QObject
+{
+	Q_OBJECT
+public:
+	FeatureName()
+	: QObject(qApp)
+	{
+		id2s[FID_Invalid]	= tr("ERROR: Incorrect usage of Features class");
+		id2s[FID_None]		= tr("None");
+		id2s[FID_Register]	= tr("Register");
+		id2s[FID_Search]	= tr("Search");
+		id2s[FID_Groupchat]	= tr("Groupchat");
+		id2s[FID_Gateway]	= tr("Gateway");
+		id2s[FID_Disco]		= tr("Service Discovery");
+		id2s[FID_VCard]		= tr("VCard");
+
+		// custom Psi actions
+		id2s[FID_Add]		= tr("Add to roster");
+
+		// compute reverse map
+		//QMap<QString, long>::Iterator it = id2s.begin();
+		//for ( ; it != id2s.end(); ++it)
+		//	s2id[it.data()] = it.key();
+
+		id2f[FID_Register]	= FID_REGISTER;
+		id2f[FID_Search]	= FID_SEARCH;
+		id2f[FID_Groupchat]	= FID_GROUPCHAT;
+		id2f[FID_Gateway]	= FID_GATEWAY;
+		id2f[FID_Disco]		= FID_DISCO;
+		id2f[FID_VCard]		= FID_VCARD;
+
+		// custom Psi actions
+		id2f[FID_Add]		= FID_ADD;
+	}
+
+	//QMap<QString, long> s2id;
+	QMap<long, QString> id2s;
+	QMap<long, QString> id2f;
+};
+
+static Features::FeatureName *featureName = 0;
+
+long Features::id() const
+{
+	if ( _list.count() > 1 )
+		return FID_Invalid;
+	else if ( canRegister() )
+		return FID_Register;
+	else if ( canSearch() )
+		return FID_Search;
+	else if ( canGroupchat() )
+		return FID_Groupchat;
+	else if ( isGateway() )
+		return FID_Gateway;
+	else if ( canDisco() )
+		return FID_Disco;
+	else if ( haveVCard() )
+		return FID_VCard;
+	else if ( test(FID_ADD) )
+		return FID_Add;
+
+	return FID_None;
+}
+
+long Features::id(const QString &feature)
+{
+	Features f(feature);
+	return f.id();
+}
+
+QString Features::feature(long id)
+{
+	if ( !featureName )
+		featureName = new FeatureName();
+
+	return featureName->id2f[id];
+}
+
+QString Features::name(long id)
+{
+	if ( !featureName )
+		featureName = new FeatureName();
+
+	return featureName->id2s[id];
+}
+
+QString Features::name() const
+{
+	return name(id());
+}
+
+QString Features::name(const QString &feature)
+{
+	Features f(feature);
+	return f.name(f.id());
+}
+
+//---------------------------------------------------------------------------
+// DiscoItem
+//---------------------------------------------------------------------------
+class DiscoItem::Private
+{
+public:
+	Private()
+	{
+		action = None;
+	}
+
+	Jid jid;
+	QString name;
+	QString node;
+	Action action;
+
+	Features features;
+	Identities identities;
+};
+
+DiscoItem::DiscoItem()
+{
+	d = new Private;
+}
+
+DiscoItem::DiscoItem(const DiscoItem &from)
+{
+	d = new Private;
+	*this = from;
+}
+
+DiscoItem & DiscoItem::operator= (const DiscoItem &from)
+{
+	d->jid = from.d->jid;
+	d->name = from.d->name;
+	d->node = from.d->node;
+	d->action = from.d->action;
+	d->features = from.d->features;
+	d->identities = from.d->identities;
+
+	return *this;
+}
+
+DiscoItem::~DiscoItem()
+{
+	delete d;
+}
+
+AgentItem DiscoItem::toAgentItem() const
+{
+	AgentItem ai;
+
+	ai.setJid( jid() );
+	ai.setName( name() );
+
+	Identity id;
+	if ( !identities().isEmpty() )
+		id = identities().first();
+
+	ai.setCategory( id.category );
+	ai.setType( id.type );
+
+	ai.setFeatures( d->features );
+
+	return ai;
+}
+
+void DiscoItem::fromAgentItem(const AgentItem &ai)
+{
+	setJid( ai.jid() );
+	setName( ai.name() );
+
+	Identity id;
+	id.category = ai.category();
+	id.type = ai.type();
+	id.name = ai.name();
+
+	Identities idList;
+	idList << id;
+
+	setIdentities( idList );
+
+	setFeatures( ai.features() );
+}
+
+const Jid &DiscoItem::jid() const
+{
+	return d->jid;
+}
+
+void DiscoItem::setJid(const Jid &j)
+{
+	d->jid = j;
+}
+
+const QString &DiscoItem::name() const
+{
+	return d->name;
+}
+
+void DiscoItem::setName(const QString &n)
+{
+	d->name = n;
+}
+
+const QString &DiscoItem::node() const
+{
+	return d->node;
+}
+
+void DiscoItem::setNode(const QString &n)
+{
+	d->node = n;
+}
+
+DiscoItem::Action DiscoItem::action() const
+{
+	return d->action;
+}
+
+void DiscoItem::setAction(Action a)
+{
+	d->action = a;
+}
+
+const Features &DiscoItem::features() const
+{
+	return d->features;
+}
+
+void DiscoItem::setFeatures(const Features &f)
+{
+	d->features = f;
+}
+
+const DiscoItem::Identities &DiscoItem::identities() const
+{
+	return d->identities;
+}
+
+void DiscoItem::setIdentities(const Identities &i)
+{
+	d->identities = i;
+
+	if ( name().isEmpty() && i.count() )
+		setName( i.first().name );
+}
+
+
+DiscoItem::Action DiscoItem::string2action(QString s)
+{
+	Action a;
+
+	if ( s == "update" )
+		a = Update;
+	else if ( s == "remove" )
+		a = Remove;
+	else
+		a = None;
+
+	return a;
+}
+
+QString DiscoItem::action2string(Action a)
+{
+	QString s;
+
+	if ( a == Update )
+		s = "update";
+	else if ( a == Remove )
+		s = "remove";
+	else
+		s = QString::null;
+
+	return s;
+}
+
+}
+
+#include"types.moc"
Index: psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_tasks.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_tasks.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_tasks.cpp	(revision 38)
@@ -0,0 +1,1783 @@
+/*
+ * tasks.cpp - basic tasks
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"xmpp_tasks.h"
+
+#include"base64.h"
+//#include"sha1.h"
+#include"xmpp_xmlcommon.h"
+//#include"xmpp_stream.h"
+//#include"xmpp_types.h"
+#include"xmpp_vcard.h"
+
+#include<qregexp.h>
+#include<qvaluelist.h>
+
+using namespace XMPP;
+
+
+static QString lineEncode(QString str)
+{
+	str.replace(QRegExp("\\\\"), "\\\\");   // backslash to double-backslash
+	str.replace(QRegExp("\\|"), "\\p");     // pipe to \p
+	str.replace(QRegExp("\n"), "\\n");      // newline to \n
+	return str;
+}
+
+static QString lineDecode(const QString &str)
+{
+	QString ret;
+
+	for(unsigned int n = 0; n < str.length(); ++n) {
+		if(str.at(n) == '\\') {
+			++n;
+			if(n >= str.length())
+				break;
+
+			if(str.at(n) == 'n')
+				ret.append('\n');
+			if(str.at(n) == 'p')
+				ret.append('|');
+			if(str.at(n) == '\\')
+				ret.append('\\');
+		}
+		else {
+			ret.append(str.at(n));
+		}
+	}
+
+	return ret;
+}
+
+static Roster xmlReadRoster(const QDomElement &q, bool push)
+{
+	Roster r;
+
+	for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+
+		if(i.tagName() == "item") {
+			RosterItem item;
+			item.fromXml(i);
+
+			if(push)
+				item.setIsPush(true);
+
+			r += item;
+		}
+	}
+
+	return r;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Register
+//----------------------------------------------------------------------------
+class JT_Register::Private
+{
+public:
+	Private() {}
+
+	Form form;
+	Jid jid;
+	int type;
+};
+
+JT_Register::JT_Register(Task *parent)
+:Task(parent)
+{
+	d = new Private;
+	d->type = -1;
+}
+
+JT_Register::~JT_Register()
+{
+	delete d;
+}
+
+void JT_Register::reg(const QString &user, const QString &pass)
+{
+	d->type = 0;
+	to = client()->host();
+	iq = createIQ(doc(), "set", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:register");
+	iq.appendChild(query);
+	query.appendChild(textTag(doc(), "username", user));
+	query.appendChild(textTag(doc(), "password", pass));
+}
+
+void JT_Register::changepw(const QString &pass)
+{
+	d->type = 1;
+	to = client()->host();
+	iq = createIQ(doc(), "set", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:register");
+	iq.appendChild(query);
+	query.appendChild(textTag(doc(), "username", client()->user()));
+	query.appendChild(textTag(doc(), "password", pass));
+}
+
+void JT_Register::unreg(const Jid &j)
+{
+	d->type = 2;
+	to = j.isEmpty() ? client()->host() : j.full();
+	iq = createIQ(doc(), "set", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:register");
+	iq.appendChild(query);
+
+	// this may be useful
+	if(!d->form.key().isEmpty())
+		query.appendChild(textTag(doc(), "key", d->form.key()));
+
+	query.appendChild(doc()->createElement("remove"));
+}
+
+void JT_Register::getForm(const Jid &j)
+{
+	d->type = 3;
+	to = j;
+	iq = createIQ(doc(), "get", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:register");
+	iq.appendChild(query);
+}
+
+void JT_Register::setForm(const Form &form)
+{
+	d->type = 4;
+	to = form.jid();
+	iq = createIQ(doc(), "set", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:register");
+	iq.appendChild(query);
+
+	// key?
+	if(!form.key().isEmpty())
+		query.appendChild(textTag(doc(), "key", form.key()));
+
+	// fields
+	for(Form::ConstIterator it = form.begin(); it != form.end(); ++it) {
+		const FormField &f = *it;
+		query.appendChild(textTag(doc(), f.realName(), f.value()));
+	}
+}
+
+const Form & JT_Register::form() const
+{
+	return d->form;
+}
+
+void JT_Register::onGo()
+{
+	send(iq);
+}
+
+bool JT_Register::take(const QDomElement &x)
+{
+	if(!iqVerify(x, to, id()))
+		return false;
+
+	Jid from(x.attribute("from"));
+	if(x.attribute("type") == "result") {
+		if(d->type == 3) {
+			d->form.clear();
+			d->form.setJid(from);
+
+			QDomElement q = queryTag(x);
+			for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+				QDomElement i = n.toElement();
+				if(i.isNull())
+					continue;
+
+				if(i.tagName() == "instructions")
+					d->form.setInstructions(tagContent(i));
+				else if(i.tagName() == "key")
+					d->form.setKey(tagContent(i));
+				else {
+					FormField f;
+					if(f.setType(i.tagName())) {
+						f.setValue(tagContent(i));
+						d->form += f;
+					}
+				}
+			}
+		}
+
+		setSuccess();
+	}
+	else
+		setError(x);
+
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_UnRegister
+//----------------------------------------------------------------------------
+class JT_UnRegister::Private
+{
+public:
+	Private() { }
+
+	Jid j;
+	JT_Register *jt_reg;
+};
+
+JT_UnRegister::JT_UnRegister(Task *parent)
+: Task(parent)
+{
+	d = new Private;
+	d->jt_reg = 0;
+}
+
+JT_UnRegister::~JT_UnRegister()
+{
+	delete d->jt_reg;
+	delete d;
+}
+
+void JT_UnRegister::unreg(const Jid &j)
+{
+	d->j = j;
+}
+
+void JT_UnRegister::onGo()
+{
+	delete d->jt_reg;
+
+	d->jt_reg = new JT_Register(this);
+	d->jt_reg->getForm(d->j);
+	connect(d->jt_reg, SIGNAL(finished()), SLOT(getFormFinished()));
+	d->jt_reg->go(false);
+}
+
+void JT_UnRegister::getFormFinished()
+{
+	disconnect(d->jt_reg, 0, this, 0);
+
+	d->jt_reg->unreg(d->j);
+	connect(d->jt_reg, SIGNAL(finished()), SLOT(unregFinished()));
+	d->jt_reg->go(false);
+}
+
+void JT_UnRegister::unregFinished()
+{
+	if ( d->jt_reg->success() )
+		setSuccess();
+	else
+		setError(d->jt_reg->statusCode(), d->jt_reg->statusString());
+
+	delete d->jt_reg;
+	d->jt_reg = 0;
+}
+
+//----------------------------------------------------------------------------
+// JT_Roster
+//----------------------------------------------------------------------------
+class JT_Roster::Private
+{
+public:
+	Private() {}
+
+	Roster roster;
+	QValueList<QDomElement> itemList;
+};
+
+JT_Roster::JT_Roster(Task *parent)
+:Task(parent)
+{
+	type = -1;
+	d = new Private;
+}
+
+JT_Roster::~JT_Roster()
+{
+	delete d;
+}
+
+void JT_Roster::get()
+{
+	type = 0;
+	//to = client()->host();
+	iq = createIQ(doc(), "get", to.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:roster");
+	iq.appendChild(query);
+}
+
+void JT_Roster::set(const Jid &jid, const QString &name, const QStringList &groups)
+{
+	type = 1;
+	//to = client()->host();
+	QDomElement item = doc()->createElement("item");
+	item.setAttribute("jid", jid.full());
+	if(!name.isEmpty())
+		item.setAttribute("name", name);
+	for(QStringList::ConstIterator it = groups.begin(); it != groups.end(); ++it)
+		item.appendChild(textTag(doc(), "group", *it));
+	d->itemList += item;
+}
+
+void JT_Roster::remove(const Jid &jid)
+{
+	type = 1;
+	//to = client()->host();
+	QDomElement item = doc()->createElement("item");
+	item.setAttribute("jid", jid.full());
+	item.setAttribute("subscription", "remove");
+	d->itemList += item;
+}
+
+void JT_Roster::onGo()
+{
+	if(type == 0)
+		send(iq);
+	else if(type == 1) {
+		//to = client()->host();
+		iq = createIQ(doc(), "set", to.full(), id());
+		QDomElement query = doc()->createElement("query");
+		query.setAttribute("xmlns", "jabber:iq:roster");
+		iq.appendChild(query);
+		for(QValueList<QDomElement>::ConstIterator it = d->itemList.begin(); it != d->itemList.end(); ++it)
+			query.appendChild(*it);
+		send(iq);
+	}
+}
+
+const Roster & JT_Roster::roster() const
+{
+	return d->roster;
+}
+
+QString JT_Roster::toString() const
+{
+	if(type != 1)
+		return "";
+
+	QDomElement i = doc()->createElement("request");
+	i.setAttribute("type", "JT_Roster");
+	for(QValueList<QDomElement>::ConstIterator it = d->itemList.begin(); it != d->itemList.end(); ++it)
+		i.appendChild(*it);
+	return lineEncode(Stream::xmlToString(i));
+	return "";
+}
+
+bool JT_Roster::fromString(const QString &str)
+{
+	QDomDocument *dd = new QDomDocument;
+	if(!dd->setContent(lineDecode(str).utf8()))
+		return false;
+	QDomElement e = doc()->importNode(dd->documentElement(), true).toElement();
+	delete dd;
+
+	if(e.tagName() != "request" || e.attribute("type") != "JT_Roster")
+		return false;
+
+	type = 1;
+	d->itemList.clear();
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+		d->itemList += i;
+	}
+
+	return true;
+}
+
+bool JT_Roster::take(const QDomElement &x)
+{
+	if(!iqVerify(x, client()->host(), id()))
+		return false;
+
+	// get
+	if(type == 0) {
+		if(x.attribute("type") == "result") {
+			QDomElement q = queryTag(x);
+			d->roster = xmlReadRoster(q, false);
+			setSuccess();
+		}
+		else {
+			setError(x);
+		}
+
+		return true;
+	}
+	// set
+	else if(type == 1) {
+		if(x.attribute("type") == "result")
+			setSuccess();
+		else
+			setError(x);
+
+		return true;
+	}
+	// remove
+	else if(type == 2) {
+		setSuccess();
+		return true;
+	}
+
+	return false;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PushRoster
+//----------------------------------------------------------------------------
+JT_PushRoster::JT_PushRoster(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushRoster::~JT_PushRoster()
+{
+}
+
+bool JT_PushRoster::take(const QDomElement &e)
+{
+	// must be an iq-set tag
+	if(e.tagName() != "iq" || e.attribute("type") != "set")
+		return false;
+
+	if(!iqVerify(e, client()->host(), "", "jabber:iq:roster"))
+		return false;
+
+	roster(xmlReadRoster(queryTag(e), true));
+
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Presence
+//----------------------------------------------------------------------------
+JT_Presence::JT_Presence(Task *parent)
+:Task(parent)
+{
+	type = -1;
+}
+
+JT_Presence::~JT_Presence()
+{
+}
+
+void JT_Presence::pres(const Status &s)
+{
+	type = 0;
+
+	tag = doc()->createElement("presence");
+	if(!s.isAvailable()) {
+		tag.setAttribute("type", "unavailable");
+		if(!s.status().isEmpty())
+			tag.appendChild(textTag(doc(), "status", s.status()));
+	}
+	else {
+		if(s.isInvisible())
+			tag.setAttribute("type", "invisible");
+
+		if(!s.show().isEmpty())
+			tag.appendChild(textTag(doc(), "show", s.show()));
+		if(!s.status().isEmpty())
+			tag.appendChild(textTag(doc(), "status", s.status()));
+
+		tag.appendChild( textTag(doc(), "priority", QString("%1").arg(s.priority()) ) );
+
+		if(!s.keyID().isEmpty()) {
+			QDomElement x = textTag(doc(), "x", s.keyID());
+			x.setAttribute("xmlns", "http://jabber.org/protocol/e2e");
+			tag.appendChild(x);
+		}
+		if(!s.xsigned().isEmpty()) {
+			QDomElement x = textTag(doc(), "x", s.xsigned());
+			x.setAttribute("xmlns", "jabber:x:signed");
+			tag.appendChild(x);
+		}
+	}
+}
+
+void JT_Presence::pres(const Jid &to, const Status &s)
+{
+	pres(s);
+	tag.setAttribute("to", to.full());
+}
+
+void JT_Presence::sub(const Jid &to, const QString &subType)
+{
+	type = 1;
+
+	tag = doc()->createElement("presence");
+	tag.setAttribute("to", to.full());
+	tag.setAttribute("type", subType);
+}
+
+void JT_Presence::onGo()
+{
+	send(tag);
+	setSuccess();
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PushPresence
+//----------------------------------------------------------------------------
+JT_PushPresence::JT_PushPresence(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushPresence::~JT_PushPresence()
+{
+}
+
+bool JT_PushPresence::take(const QDomElement &e)
+{
+	if(e.tagName() != "presence")
+		return false;
+
+	Jid j(e.attribute("from"));
+	Status p;
+
+	if(e.hasAttribute("type")) {
+		QString type = e.attribute("type");
+		if(type == "unavailable") {
+			p.setIsAvailable(false);
+		}
+		else if(type == "error") {
+			QString str = "";
+			int code = 0;
+			getErrorFromElement(e, &code, &str);
+			p.setError(code, str);
+		}
+		else {
+			subscription(j, type);
+			return true;
+		}
+	}
+
+	QDomElement tag;
+	bool found;
+
+	tag = findSubTag(e, "status", &found);
+	if(found)
+		p.setStatus(tagContent(tag));
+	tag = findSubTag(e, "show", &found);
+	if(found)
+		p.setShow(tagContent(tag));
+	tag = findSubTag(e, "priority", &found);
+	if(found)
+		p.setPriority(tagContent(tag).toInt());
+
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+
+		if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:delay") {
+			if(i.hasAttribute("stamp")) {
+				QDateTime dt;
+				if(stamp2TS(i.attribute("stamp"), &dt))
+					dt = dt.addSecs(client()->timeZoneOffset() * 3600);
+				p.setTimeStamp(dt);
+			}
+		}
+		else if(i.tagName() == "x" && i.attribute("xmlns") == "gabber:x:music:info") {
+			QDomElement t;
+			bool found;
+			QString title, state;
+
+			t = findSubTag(i, "title", &found);
+			if(found)
+				title = tagContent(t);
+			t = findSubTag(i, "state", &found);
+			if(found)
+				state = tagContent(t);
+
+			if(!title.isEmpty() && state == "playing")
+				p.setSongTitle(title);
+		}
+		else if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:signed") {
+			p.setXSigned(tagContent(i));
+		}
+		else if(i.tagName() == "x" && i.attribute("xmlns") == "http://jabber.org/protocol/e2e") {
+			p.setKeyID(tagContent(i));
+		}
+	}
+
+	presence(j, p);
+
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Message
+//----------------------------------------------------------------------------
+static QDomElement oldStyleNS(const QDomElement &e)
+{
+	// find closest parent with a namespace
+	QDomNode par = e.parentNode();
+	while(!par.isNull() && par.namespaceURI().isNull())
+		par = par.parentNode();
+	bool noShowNS = false;
+	if(!par.isNull() && par.namespaceURI() == e.namespaceURI())
+		noShowNS = true;
+
+	QDomElement i;
+	uint x;
+	//if(noShowNS)
+		i = e.ownerDocument().createElement(e.tagName());
+	//else
+	//	i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName());
+
+	// copy attributes
+	QDomNamedNodeMap al = e.attributes();
+	for(x = 0; x < al.count(); ++x)
+		i.setAttributeNode(al.item(x).cloneNode().toAttr());
+
+	if(!noShowNS)
+		i.setAttribute("xmlns", e.namespaceURI());
+
+	// copy children
+	QDomNodeList nl = e.childNodes();
+	for(x = 0; x < nl.count(); ++x) {
+		QDomNode n = nl.item(x);
+		if(n.isElement())
+			i.appendChild(oldStyleNS(n.toElement()));
+		else
+			i.appendChild(n.cloneNode());
+	}
+	return i;
+}
+
+JT_Message::JT_Message(Task *parent, const Message &msg)
+:Task(parent)
+{
+	m = msg;
+	m.setId(id());
+}
+
+JT_Message::~JT_Message()
+{
+}
+
+void JT_Message::onGo()
+{
+	Stanza s = m.toStanza(&(client()->stream()));
+	QDomElement e = oldStyleNS(s.element());
+	send(e);
+	setSuccess();
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PushMessage
+//----------------------------------------------------------------------------
+static QDomElement addCorrectNS(const QDomElement &e)
+{
+	uint x;
+
+	// grab child nodes
+	/*QDomDocumentFragment frag = e.ownerDocument().createDocumentFragment();
+	QDomNodeList nl = e.childNodes();
+	for(x = 0; x < nl.count(); ++x)
+		frag.appendChild(nl.item(x).cloneNode());*/
+
+	// find closest xmlns
+	QDomNode n = e;
+	while(!n.isNull() && !n.toElement().hasAttribute("xmlns"))
+		n = n.parentNode();
+	QString ns;
+	if(n.isNull() || !n.toElement().hasAttribute("xmlns"))
+		ns = "jabber:client";
+	else
+		ns = n.toElement().attribute("xmlns");
+
+	// make a new node
+	QDomElement i = e.ownerDocument().createElementNS(ns, e.tagName());
+
+	// copy attributes
+	QDomNamedNodeMap al = e.attributes();
+	for(x = 0; x < al.count(); ++x) {
+		QDomAttr a = al.item(x).toAttr();
+		if(a.name() != "xmlns")
+			i.setAttributeNodeNS(al.item(x).cloneNode().toAttr());
+	}
+
+	// copy children
+	QDomNodeList nl = e.childNodes();
+	for(x = 0; x < nl.count(); ++x) {
+		QDomNode n = nl.item(x);
+		if(n.isElement())
+			i.appendChild(addCorrectNS(n.toElement()));
+		else
+			i.appendChild(n.cloneNode());
+	}
+
+	//i.appendChild(frag);
+	return i;
+}
+
+JT_PushMessage::JT_PushMessage(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushMessage::~JT_PushMessage()
+{
+}
+
+bool JT_PushMessage::take(const QDomElement &e)
+{
+	if(e.tagName() != "message")
+		return false;
+
+	Stanza s = client()->stream().createStanza(addCorrectNS(e));
+	if(s.isNull()) {
+		//printf("take: bad stanza??\n");
+		return false;
+	}
+
+	Message m;
+	if(!m.fromStanza(s, client()->timeZoneOffset())) {
+		//printf("bad message\n");
+		return false;
+	}
+
+	message(m);
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_GetServices
+//----------------------------------------------------------------------------
+JT_GetServices::JT_GetServices(Task *parent)
+:Task(parent)
+{
+}
+
+void JT_GetServices::get(const Jid &j)
+{
+	agentList.clear();
+
+	jid = j;
+	iq = createIQ(doc(), "get", jid.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:agents");
+	iq.appendChild(query);
+}
+
+const AgentList & JT_GetServices::agents() const
+{
+	return agentList;
+}
+
+void JT_GetServices::onGo()
+{
+	send(iq);
+}
+
+bool JT_GetServices::take(const QDomElement &x)
+{
+	if(!iqVerify(x, jid, id()))
+		return false;
+
+	if(x.attribute("type") == "result") {
+		QDomElement q = queryTag(x);
+
+		// agents
+		for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+			QDomElement i = n.toElement();
+			if(i.isNull())
+				continue;
+
+			if(i.tagName() == "agent") {
+				AgentItem a;
+
+				a.setJid(Jid(i.attribute("jid")));
+
+				QDomElement tag;
+				bool found;
+
+				tag = findSubTag(i, "name", &found);
+				if(found)
+					a.setName(tagContent(tag));
+
+				// determine which namespaces does item support
+				QStringList ns;
+
+				tag = findSubTag(i, "register", &found);
+				if(found)
+					ns << "jabber:iq:register";
+				tag = findSubTag(i, "search", &found);
+				if(found)
+					ns << "jabber:iq:search";
+				tag = findSubTag(i, "groupchat", &found);
+				if(found)
+					ns << "jabber:iq:conference";
+				tag = findSubTag(i, "transport", &found);
+				if(found)
+					ns << "jabber:iq:gateway";
+
+				a.setFeatures(ns);
+
+				agentList += a;
+			}
+		}
+
+		setSuccess(true);
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_VCard
+//----------------------------------------------------------------------------
+class JT_VCard::Private
+{
+public:
+	Private() {}
+
+	QDomElement iq;
+	Jid jid;
+	VCard vcard;
+};
+
+JT_VCard::JT_VCard(Task *parent)
+:Task(parent)
+{
+	type = -1;
+	d = new Private;
+}
+
+JT_VCard::~JT_VCard()
+{
+	delete d;
+}
+
+void JT_VCard::get(const Jid &_jid)
+{
+	type = 0;
+	d->jid = _jid;
+	d->iq = createIQ(doc(), "get", d->jid.full(), id());
+	QDomElement v = doc()->createElement("vCard");
+	v.setAttribute("xmlns", "vcard-temp");
+	v.setAttribute("version", "2.0");
+	v.setAttribute("prodid", "-//HandGen//NONSGML vGen v1.0//EN");
+	d->iq.appendChild(v);
+}
+
+const Jid & JT_VCard::jid() const
+{
+	return d->jid;
+}
+
+const VCard & JT_VCard::vcard() const
+{
+	return d->vcard;
+}
+
+void JT_VCard::set(const VCard &card)
+{
+	type = 1;
+	d->vcard = card;
+	d->jid = "";
+	d->iq = createIQ(doc(), "set", d->jid.full(), id());
+	d->iq.appendChild(card.toXml(doc()) );
+}
+
+void JT_VCard::onGo()
+{
+	send(d->iq);
+}
+
+bool JT_VCard::take(const QDomElement &x)
+{
+	Jid to = d->jid;
+	if (to.userHost() == client()->jid().userHost())
+		to = client()->host();
+	if(!iqVerify(x, to, id()))
+		return false;
+
+	if(x.attribute("type") == "result") {
+		if(type == 0) {
+			for(QDomNode n = x.firstChild(); !n.isNull(); n = n.nextSibling()) {
+				QDomElement q = n.toElement();
+				if(q.isNull())
+					continue;
+
+				if(q.tagName().upper() == "VCARD") {
+					if(d->vcard.fromXml(q)) {
+						setSuccess();
+						return true;
+					}
+				}
+			}
+
+			setError(ErrDisc + 1, tr("No VCard available"));
+			return true;
+		}
+		else {
+			setSuccess();
+			return true;
+		}
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Search
+//----------------------------------------------------------------------------
+class JT_Search::Private
+{
+public:
+	Private() {}
+
+	Jid jid;
+	Form form;
+	QValueList<SearchResult> resultList;
+};
+
+JT_Search::JT_Search(Task *parent)
+:Task(parent)
+{
+	d = new Private;
+	type = -1;
+}
+
+JT_Search::~JT_Search()
+{
+	delete d;
+}
+
+void JT_Search::get(const Jid &jid)
+{
+	type = 0;
+	d->jid = jid;
+	iq = createIQ(doc(), "get", d->jid.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:search");
+	iq.appendChild(query);
+}
+
+void JT_Search::set(const Form &form)
+{
+	type = 1;
+	d->jid = form.jid();
+	iq = createIQ(doc(), "set", d->jid.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:search");
+	iq.appendChild(query);
+
+	// key?
+	if(!form.key().isEmpty())
+		query.appendChild(textTag(doc(), "key", form.key()));
+
+	// fields
+	for(Form::ConstIterator it = form.begin(); it != form.end(); ++it) {
+		const FormField &f = *it;
+		query.appendChild(textTag(doc(), f.realName(), f.value()));
+	}
+}
+
+const Form & JT_Search::form() const
+{
+	return d->form;
+}
+
+const QValueList<SearchResult> & JT_Search::results() const
+{
+	return d->resultList;
+}
+
+void JT_Search::onGo()
+{
+	send(iq);
+}
+
+bool JT_Search::take(const QDomElement &x)
+{
+	if(!iqVerify(x, d->jid, id()))
+		return false;
+
+	Jid from(x.attribute("from"));
+	if(x.attribute("type") == "result") {
+		if(type == 0) {
+			d->form.clear();
+			d->form.setJid(from);
+
+			QDomElement q = queryTag(x);
+			for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+				QDomElement i = n.toElement();
+				if(i.isNull())
+					continue;
+
+				if(i.tagName() == "instructions")
+					d->form.setInstructions(tagContent(i));
+				else if(i.tagName() == "key")
+					d->form.setKey(tagContent(i));
+				else {
+					FormField f;
+					if(f.setType(i.tagName())) {
+						f.setValue(tagContent(i));
+						d->form += f;
+					}
+				}
+			}
+		}
+		else {
+			d->resultList.clear();
+
+			QDomElement q = queryTag(x);
+			for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+				QDomElement i = n.toElement();
+				if(i.isNull())
+					continue;
+
+				if(i.tagName() == "item") {
+					SearchResult r(Jid(i.attribute("jid")));
+
+					QDomElement tag;
+					bool found;
+
+					tag = findSubTag(i, "nick", &found);
+					if(found)
+						r.setNick(tagContent(tag));
+					tag = findSubTag(i, "first", &found);
+					if(found)
+						r.setFirst(tagContent(tag));
+					tag = findSubTag(i, "last", &found);
+					if(found)
+						r.setLast(tagContent(tag));
+					tag = findSubTag(i, "email", &found);
+					if(found)
+						r.setEmail(tagContent(tag));
+
+					d->resultList += r;
+				}
+			}
+		}
+		setSuccess();
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_ClientVersion
+//----------------------------------------------------------------------------
+JT_ClientVersion::JT_ClientVersion(Task *parent)
+:Task(parent)
+{
+}
+
+void JT_ClientVersion::get(const Jid &jid)
+{
+	j = jid;
+	iq = createIQ(doc(), "get", j.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:version");
+	iq.appendChild(query);
+}
+
+void JT_ClientVersion::onGo()
+{
+	send(iq);
+}
+
+bool JT_ClientVersion::take(const QDomElement &x)
+{
+	if(!iqVerify(x, j, id()))
+		return false;
+
+	if(x.attribute("type") == "result") {
+		bool found;
+		QDomElement q = queryTag(x);
+		QDomElement tag;
+		tag = findSubTag(q, "name", &found);
+		if(found)
+			v_name = tagContent(tag);
+		tag = findSubTag(q, "version", &found);
+		if(found)
+			v_ver = tagContent(tag);
+		tag = findSubTag(q, "os", &found);
+		if(found)
+			v_os = tagContent(tag);
+
+		setSuccess();
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+const Jid & JT_ClientVersion::jid() const
+{
+	return j;
+}
+
+const QString & JT_ClientVersion::name() const
+{
+	return v_name;
+}
+
+const QString & JT_ClientVersion::version() const
+{
+	return v_ver;
+}
+
+const QString & JT_ClientVersion::os() const
+{
+	return v_os;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_ClientTime
+//----------------------------------------------------------------------------
+/*JT_ClientTime::JT_ClientTime(Task *parent, const Jid &_j)
+:Task(parent)
+{
+	j = _j;
+	iq = createIQ("get", j.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:time");
+	iq.appendChild(query);
+}
+
+void JT_ClientTime::go()
+{
+	send(iq);
+}
+
+bool JT_ClientTime::take(const QDomElement &x)
+{
+	if(x.attribute("id") != id())
+		return FALSE;
+
+	if(x.attribute("type") == "result") {
+		bool found;
+		QDomElement q = queryTag(x);
+		QDomElement tag;
+		tag = findSubTag(q, "utc", &found);
+		if(found)
+			stamp2TS(tagContent(tag), &utc);
+		tag = findSubTag(q, "tz", &found);
+		if(found)
+			timezone = tagContent(tag);
+		tag = findSubTag(q, "display", &found);
+		if(found)
+			display = tagContent(tag);
+
+		setSuccess(TRUE);
+	}
+	else {
+		setError(getErrorString(x));
+		setSuccess(FALSE);
+	}
+
+	return TRUE;
+}
+*/
+
+
+//----------------------------------------------------------------------------
+// JT_ServInfo
+//----------------------------------------------------------------------------
+JT_ServInfo::JT_ServInfo(Task *parent)
+:Task(parent)
+{
+}
+
+JT_ServInfo::~JT_ServInfo()
+{
+}
+
+bool JT_ServInfo::take(const QDomElement &e)
+{
+	if(e.tagName() != "iq" || e.attribute("type") != "get")
+		return false;
+
+	QString ns = queryNS(e);
+	if(ns == "jabber:iq:version") {
+		QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+		QDomElement query = doc()->createElement("query");
+		query.setAttribute("xmlns", "jabber:iq:version");
+		iq.appendChild(query);
+		query.appendChild(textTag(doc(), "name", client()->clientName()));
+		query.appendChild(textTag(doc(), "version", client()->clientVersion()));
+		query.appendChild(textTag(doc(), "os", client()->OSName()));
+		send(iq);
+		return true;
+	}
+	//else if(ns == "jabber:iq:time") {
+	//	QDomElement iq = createIQ("result", e.attribute("from"), e.attribute("id"));
+	//	QDomElement query = doc()->createElement("query");
+	//	query.setAttribute("xmlns", "jabber:iq:time");
+	//	iq.appendChild(query);
+	//	QDateTime local = QDateTime::currentDateTime();
+	//	QDateTime utc = local.addSecs(-getTZOffset() * 3600);
+	//	QString str = getTZString();
+	//	query.appendChild(textTag("utc", TS2stamp(utc)));
+	//	query.appendChild(textTag("tz", str));
+	//	query.appendChild(textTag("display", QString("%1 %2").arg(local.toString()).arg(str)));
+	//	send(iq);
+	//	return TRUE;
+	//}
+	else if(ns == "http://jabber.org/protocol/disco#info") {
+		QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+		QDomElement query = doc()->createElement("query");
+		query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
+		iq.appendChild(query);
+		QDomElement feature;
+
+		feature = doc()->createElement("feature");
+		feature.setAttribute("var", "http://jabber.org/protocol/bytestreams");
+		query.appendChild(feature);
+
+		feature = doc()->createElement("feature");
+		feature.setAttribute("var", "http://jabber.org/protocol/si");
+		query.appendChild(feature);
+
+		feature = doc()->createElement("feature");
+		feature.setAttribute("var", "http://jabber.org/protocol/si/profile/file-transfer");
+		query.appendChild(feature);
+
+		send(iq);
+		return true;
+	}
+
+	return false;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Gateway
+//----------------------------------------------------------------------------
+JT_Gateway::JT_Gateway(Task *parent)
+:Task(parent)
+{
+	type = -1;
+}
+
+void JT_Gateway::get(const Jid &jid)
+{
+	type = 0;
+	v_jid = jid;
+	iq = createIQ(doc(), "get", v_jid.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:gateway");
+	iq.appendChild(query);
+}
+
+void JT_Gateway::set(const Jid &jid, const QString &prompt)
+{
+	type = 1;
+	v_jid = jid;
+	v_prompt = prompt;
+	iq = createIQ(doc(), "set", v_jid.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:gateway");
+	iq.appendChild(query);
+	query.appendChild(textTag(doc(), "prompt", v_prompt));
+}
+
+void JT_Gateway::onGo()
+{
+	send(iq);
+}
+
+Jid JT_Gateway::jid() const
+{
+	return v_jid;
+}
+
+QString JT_Gateway::desc() const
+{
+	return v_desc;
+}
+
+QString JT_Gateway::prompt() const
+{
+	return v_prompt;
+}
+
+bool JT_Gateway::take(const QDomElement &x)
+{
+	if(!iqVerify(x, v_jid, id()))
+		return false;
+
+	if(x.attribute("type") == "result") {
+		if(type == 0) {
+			QDomElement query = queryTag(x);
+			bool found;
+			QDomElement tag;
+			tag = findSubTag(query, "desc", &found);
+			if(found)
+				v_desc = tagContent(tag);
+			tag = findSubTag(query, "prompt", &found);
+			if(found)
+				v_prompt = tagContent(tag);
+		}
+		else {
+			QDomElement query = queryTag(x);
+			bool found;
+			QDomElement tag;
+			tag = findSubTag(query, "prompt", &found);
+			if(found)
+				v_prompt = tagContent(tag);
+		}
+
+		setSuccess();
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_Browse
+//----------------------------------------------------------------------------
+class JT_Browse::Private
+{
+public:
+	QDomElement iq;
+	Jid jid;
+	AgentList agentList;
+	AgentItem root;
+};
+
+JT_Browse::JT_Browse (Task *parent)
+:Task (parent)
+{
+	d = new Private;
+}
+
+JT_Browse::~JT_Browse ()
+{
+	delete d;
+}
+
+void JT_Browse::get (const Jid &j)
+{
+	d->agentList.clear();
+
+	d->jid = j;
+	d->iq = createIQ(doc(), "get", d->jid.full(), id());
+	QDomElement query = doc()->createElement("item");
+	query.setAttribute("xmlns", "jabber:iq:browse");
+	d->iq.appendChild(query);
+}
+
+const AgentList & JT_Browse::agents() const
+{
+	return d->agentList;
+}
+
+const AgentItem & JT_Browse::root() const
+{
+	return d->root;
+}
+
+void JT_Browse::onGo ()
+{
+	send(d->iq);
+}
+
+AgentItem JT_Browse::browseHelper (const QDomElement &i)
+{
+	AgentItem a;
+
+	if ( i.tagName() == "ns" )
+		return a;
+
+	a.setName ( i.attribute("name") );
+	a.setJid  ( i.attribute("jid") );
+
+	// there are two types of category/type specification:
+	//
+	//   1. <item category="category_name" type="type_name" />
+	//   2. <category_name type="type_name" />
+
+	if ( i.tagName() == "item" || i.tagName() == "query" )
+		a.setCategory ( i.attribute("category") );
+	else
+		a.setCategory ( i.tagName() );
+
+	a.setType ( i.attribute("type") );
+
+	QStringList ns;
+	for(QDomNode n = i.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+
+		if ( i.tagName() == "ns" )
+			ns << i.text();
+	}
+
+	// For now, conference.jabber.org returns proper namespace only
+	// when browsing individual rooms. So it's a quick client-side fix.
+	if ( !a.features().canGroupchat() && a.category() == "conference" )
+		ns << "jabber:iq:conference";
+
+	a.setFeatures (ns);
+
+	return a;
+}
+
+bool JT_Browse::take(const QDomElement &x)
+{
+	if(!iqVerify(x, d->jid, id()))
+		return false;
+
+	if(x.attribute("type") == "result") {
+		for(QDomNode n = x.firstChild(); !n.isNull(); n = n.nextSibling()) {
+			QDomElement i = n.toElement();
+			if(i.isNull())
+				continue;
+
+			d->root = browseHelper (i);
+
+			for(QDomNode nn = i.firstChild(); !nn.isNull(); nn = nn.nextSibling()) {
+				QDomElement e = nn.toElement();
+				if ( e.isNull() )
+					continue;
+				if ( e.tagName() == "ns" )
+					continue;
+
+				d->agentList += browseHelper (e);
+			}
+		}
+
+		setSuccess(true);
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_DiscoItems
+//----------------------------------------------------------------------------
+class JT_DiscoItems::Private
+{
+public:
+	Private() { }
+
+	QDomElement iq;
+	Jid jid;
+	DiscoList items;
+};
+
+JT_DiscoItems::JT_DiscoItems(Task *parent)
+: Task(parent)
+{
+	d = new Private;
+}
+
+JT_DiscoItems::~JT_DiscoItems()
+{
+	delete d;
+}
+
+void JT_DiscoItems::get(const DiscoItem &item)
+{
+	get(item.jid(), item.node());
+}
+
+void JT_DiscoItems::get (const Jid &j, const QString &node)
+{
+	d->items.clear();
+
+	d->jid = j;
+	d->iq = createIQ(doc(), "get", d->jid.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items");
+
+	if ( !node.isEmpty() )
+		query.setAttribute("node", node);
+
+	d->iq.appendChild(query);
+}
+
+const DiscoList &JT_DiscoItems::items() const
+{
+	return d->items;
+}
+
+void JT_DiscoItems::onGo ()
+{
+	send(d->iq);
+}
+
+bool JT_DiscoItems::take(const QDomElement &x)
+{
+	if(!iqVerify(x, d->jid, id()))
+		return false;
+
+	if(x.attribute("type") == "result") {
+		QDomElement q = queryTag(x);
+
+		for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+			QDomElement e = n.toElement();
+			if( e.isNull() )
+				continue;
+
+			if ( e.tagName() == "item" ) {
+				DiscoItem item;
+
+				item.setJid ( e.attribute("jid")  );
+				item.setName( e.attribute("name") );
+				item.setNode( e.attribute("node") );
+				item.setAction( DiscoItem::string2action(e.attribute("action")) );
+
+				d->items.append( item );
+			}
+		}
+
+		setSuccess(true);
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_DiscoInfo
+//----------------------------------------------------------------------------
+class JT_DiscoInfo::Private
+{
+public:
+	Private() { }
+
+	QDomElement iq;
+	Jid jid;
+	DiscoItem item;
+};
+
+JT_DiscoInfo::JT_DiscoInfo(Task *parent)
+: Task(parent)
+{
+	d = new Private;
+}
+
+JT_DiscoInfo::~JT_DiscoInfo()
+{
+	delete d;
+}
+
+void JT_DiscoInfo::get(const DiscoItem &item)
+{
+	DiscoItem::Identity id;
+	if ( item.identities().count() == 1 )
+		id = item.identities().first();
+	get(item.jid(), item.node(), id);
+}
+
+void JT_DiscoInfo::get (const Jid &j, const QString &node, DiscoItem::Identity ident)
+{
+	d->item = DiscoItem(); // clear item
+
+	d->jid = j;
+	d->iq = createIQ(doc(), "get", d->jid.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
+
+	if ( !node.isEmpty() )
+		query.setAttribute("node", node);
+
+	if ( !ident.category.isEmpty() && !ident.type.isEmpty() ) {
+		QDomElement i = doc()->createElement("item");
+
+		i.setAttribute("category", ident.category);
+		i.setAttribute("type", ident.type);
+		if ( !ident.name.isEmpty() )
+			i.setAttribute("name", ident.name);
+
+		query.appendChild( i );
+
+	}
+
+	d->iq.appendChild(query);
+}
+
+const DiscoItem &JT_DiscoInfo::item() const
+{
+	return d->item;
+}
+
+void JT_DiscoInfo::onGo ()
+{
+	send(d->iq);
+}
+
+bool JT_DiscoInfo::take(const QDomElement &x)
+{
+	if(!iqVerify(x, d->jid, id()))
+		return false;
+
+	if(x.attribute("type") == "result") {
+		QDomElement q = queryTag(x);
+
+		DiscoItem item;
+
+		item.setJid( d->jid );
+		item.setNode( q.attribute("node") );
+
+		QStringList features;
+		DiscoItem::Identities identities;
+
+		for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+			QDomElement e = n.toElement();
+			if( e.isNull() )
+				continue;
+
+			if ( e.tagName() == "feature" ) {
+				features << e.attribute("var");
+			}
+			else if ( e.tagName() == "identity" ) {
+				DiscoItem::Identity id;
+
+				id.category = e.attribute("category");
+				id.name     = e.attribute("name");
+				id.type     = e.attribute("type");
+
+				identities.append( id );
+			}
+		}
+
+		item.setFeatures( features );
+		item.setIdentities( identities );
+
+		d->item = item;
+
+		setSuccess(true);
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_DiscoPublish
+//----------------------------------------------------------------------------
+class JT_DiscoPublish::Private
+{
+public:
+	Private() { }
+
+	QDomElement iq;
+	Jid jid;
+	DiscoList list;
+};
+
+JT_DiscoPublish::JT_DiscoPublish(Task *parent)
+: Task(parent)
+{
+	d = new Private;
+}
+
+JT_DiscoPublish::~JT_DiscoPublish()
+{
+	delete d;
+}
+
+void JT_DiscoPublish::set(const Jid &j, const DiscoList &list)
+{
+	d->list = list;
+	d->jid = j;
+
+	d->iq = createIQ(doc(), "set", d->jid.full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items");
+
+	// FIXME: unsure about this
+	//if ( !node.isEmpty() )
+	//	query.setAttribute("node", node);
+
+	DiscoList::ConstIterator it = list.begin();
+	for ( ; it != list.end(); ++it) {
+		QDomElement w = doc()->createElement("item");
+
+		w.setAttribute("jid", (*it).jid().full());
+		if ( !(*it).name().isEmpty() )
+			w.setAttribute("name", (*it).name());
+		if ( !(*it).node().isEmpty() )
+		w.setAttribute("node", (*it).node());
+		w.setAttribute("action", DiscoItem::action2string((*it).action()));
+
+		query.appendChild( w );
+	}
+
+	d->iq.appendChild(query);
+}
+
+void JT_DiscoPublish::onGo ()
+{
+	send(d->iq);
+}
+
+bool JT_DiscoPublish::take(const QDomElement &x)
+{
+	if(!iqVerify(x, d->jid, id()))
+		return false;
+
+	if(x.attribute("type") == "result") {
+		setSuccess(true);
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
Index: psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_tasks.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_tasks.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_tasks.h	(revision 38)
@@ -0,0 +1,419 @@
+/*
+ * tasks.h - basic tasks
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef JABBER_TASKS_H
+#define JABBER_TASKS_H
+
+#include<qstring.h>
+#include<qdom.h>
+
+#include"im.h"
+#include"xmpp_vcard.h"
+
+namespace XMPP
+{
+	class Roster;
+	class Status;
+
+	class JT_Register : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_Register(Task *parent);
+		~JT_Register();
+
+		void reg(const QString &user, const QString &pass);
+		void changepw(const QString &pass);
+		void unreg(const Jid &j="");
+
+		const Form & form() const;
+		void getForm(const Jid &);
+		void setForm(const Form &);
+
+		void onGo();
+		bool take(const QDomElement &);
+
+	private:
+		QDomElement iq;
+		Jid to;
+
+		class Private;
+		Private *d;
+	};
+
+	class JT_UnRegister : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_UnRegister(Task *parent);
+		~JT_UnRegister();
+
+		void unreg(const Jid &);
+
+		void onGo();
+
+	private slots:
+		void getFormFinished();
+		void unregFinished();
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	class JT_Roster : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_Roster(Task *parent);
+		~JT_Roster();
+
+		void get();
+		void set(const Jid &, const QString &name, const QStringList &groups);
+		void remove(const Jid &);
+
+		const Roster & roster() const;
+
+		QString toString() const;
+		bool fromString(const QString &);
+
+		void onGo();
+		bool take(const QDomElement &x);
+
+	private:
+		int type;
+		QDomElement iq;
+		Jid to;
+
+		class Private;
+		Private *d;
+	};
+
+	class JT_PushRoster : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_PushRoster(Task *parent);
+		~JT_PushRoster();
+
+		bool take(const QDomElement &);
+
+	signals:
+		void roster(const Roster &);
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	class JT_Presence : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_Presence(Task *parent);
+		~JT_Presence();
+
+		void pres(const Status &);
+		void pres(const Jid &, const Status &);
+		void sub(const Jid &, const QString &subType);
+
+		void onGo();
+
+	private:
+		QDomElement tag;
+		int type;
+
+		class Private;
+		Private *d;
+	};
+
+	class JT_PushPresence : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_PushPresence(Task *parent);
+		~JT_PushPresence();
+
+		bool take(const QDomElement &);
+
+	signals:
+		void presence(const Jid &, const Status &);
+		void subscription(const Jid &, const QString &);
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	class JT_Message : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_Message(Task *parent, const Message &);
+		~JT_Message();
+
+		void onGo();
+
+	private:
+		Message m;
+
+		class Private;
+		Private *d;
+	};
+
+	class JT_PushMessage : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_PushMessage(Task *parent);
+		~JT_PushMessage();
+
+		bool take(const QDomElement &);
+
+	signals:
+		void message(const Message &);
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+	class JT_GetServices : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_GetServices(Task *);
+
+		void get(const Jid &);
+
+		const AgentList & agents() const;
+
+		void onGo();
+		bool take(const QDomElement &x);
+
+	private:
+		class Private;
+		Private *d;
+
+		QDomElement iq;
+		Jid jid;
+		AgentList agentList;
+	};
+
+	class JT_VCard : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_VCard(Task *parent);
+		~JT_VCard();
+
+		void get(const Jid &);
+		void set(const VCard &);
+
+		const Jid & jid() const;
+		const VCard  & vcard() const;
+		
+		void onGo();
+		bool take(const QDomElement &x);
+
+	private:
+		int type;
+
+		class Private;
+		Private *d;
+	};
+
+	class JT_Search : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_Search(Task *parent);
+		~JT_Search();
+
+		const Form & form() const;
+		const QValueList<SearchResult> & results() const;
+
+		void get(const Jid &);
+		void set(const Form &);
+
+		void onGo();
+		bool take(const QDomElement &x);
+
+	private:
+		QDomElement iq;
+		int type;
+
+		class Private;
+		Private *d;
+	};
+
+	class JT_ClientVersion : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_ClientVersion(Task *);
+
+		void get(const Jid &);
+		void onGo();
+		bool take(const QDomElement &);
+
+		const Jid & jid() const;
+		const QString & name() const;
+		const QString & version() const;
+		const QString & os() const;
+
+	private:
+		QDomElement iq;
+
+		Jid j;
+		QString v_name, v_ver, v_os;
+	};
+/*
+	class JT_ClientTime : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_ClientTime(Task *, const Jid &);
+
+		void go();
+		bool take(const QDomElement &);
+
+		Jid j;
+		QDateTime utc;
+		QString timezone, display;
+
+	private:
+		QDomElement iq;
+	};
+*/
+	class JT_ServInfo : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_ServInfo(Task *);
+		~JT_ServInfo();
+
+		bool take(const QDomElement &);
+	};
+
+	class JT_Gateway : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_Gateway(Task *);
+
+		void get(const Jid &);
+		void set(const Jid &, const QString &prompt);
+		void onGo();
+		bool take(const QDomElement &);
+
+		Jid jid() const;
+		QString desc() const;
+		QString prompt() const;
+
+	private:
+		QDomElement iq;
+
+		int type;
+		Jid v_jid;
+		QString v_prompt, v_desc;
+	};
+
+	class JT_Browse : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_Browse(Task *);
+		~JT_Browse();
+
+		void get(const Jid &);
+
+		const AgentList & agents() const;
+		const AgentItem & root() const;
+
+		void onGo();
+		bool take(const QDomElement &);
+
+	private:
+		class Private;
+		Private *d;
+
+		AgentItem browseHelper (const QDomElement &i);
+	};
+
+	class JT_DiscoItems : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_DiscoItems(Task *);
+		~JT_DiscoItems();
+	
+		void get(const Jid &, const QString &node = QString::null);
+		void get(const DiscoItem &);
+	
+		const DiscoList &items() const;
+	
+		void onGo();
+		bool take(const QDomElement &);
+	
+	private:
+		class Private;
+		Private *d;
+	};
+
+	class JT_DiscoInfo : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_DiscoInfo(Task *);
+		~JT_DiscoInfo();
+	
+		void get(const Jid &, const QString &node = QString::null, const DiscoItem::Identity = DiscoItem::Identity());
+		void get(const DiscoItem &);
+	
+		const DiscoItem &item() const;
+	
+		void onGo();
+		bool take(const QDomElement &);
+	
+	private:
+		class Private;
+		Private *d;
+	};
+
+	class JT_DiscoPublish : public Task
+	{
+		Q_OBJECT
+	public:
+		JT_DiscoPublish(Task *);
+		~JT_DiscoPublish();
+	
+		void set(const Jid &, const DiscoList &);
+	
+		void onGo();
+		bool take(const QDomElement &);
+	
+	private:
+		class Private;
+		Private *d;
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_vcard.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_vcard.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_vcard.cpp	(revision 38)
@@ -0,0 +1,1241 @@
+/*
+ * xmpp_vcard.cpp - classes for handling vCards
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "xmpp_vcard.h"
+
+#include "base64.h"
+
+#include <qdom.h>
+#include <qdatetime.h>
+
+#include <qimage.h> // needed for image format recognition
+#include <qbuffer.h>
+
+// Justin's XML helper functions
+
+static QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
+{
+	QDomElement tag = doc->createElement(name);
+	QDomText text = doc->createTextNode(content);
+	tag.appendChild(text);
+
+	return tag;
+}
+
+static QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+	if(found)
+		*found = FALSE;
+
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+		if(i.tagName().upper() == name.upper()) { // mblsha: ignore case when searching
+			if(found)
+				*found = TRUE;
+			return i;
+		}
+	}
+
+	QDomElement tmp;
+	return tmp;
+}
+
+// mblsha's own functions
+
+static QDomElement emptyTag(QDomDocument *doc, const QString &name)
+{
+	QDomElement tag = doc->createElement(name);
+
+	return tag;
+}
+
+static bool hasSubTag(const QDomElement &e, const QString &name)
+{
+	bool found;
+	findSubTag(e, name, &found);
+	return found;
+}
+
+static QString subTagText(const QDomElement &e, const QString &name)
+{
+	bool found;
+	QDomElement i = findSubTag(e, name, &found);
+	if ( found )
+		return i.text().stripWhiteSpace();
+	return QString::null;
+}
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// VCard
+//----------------------------------------------------------------------------
+static QString image2type(const QByteArray &ba)
+{
+	QBuffer buf(ba);
+	buf.open(IO_ReadOnly);
+	QString format = QImageIO::imageFormat( &buf );
+
+	// TODO: add more formats
+	if ( format == "PNG" || format == "PsiPNG" )
+		return "image/png";
+	if ( format == "MNG" )
+		return "video/x-mng";
+	if ( format == "GIF" )
+		return "image/gif";
+	if ( format == "BMP" )
+		return "image/bmp";
+	if ( format == "XPM" )
+		return "image/x-xpm";
+	if ( format == "SVG" )
+		return "image/svg+xml";
+	if ( format == "JPEG" )
+		return "image/jpeg";
+
+	qWarning("WARNING! VCard::image2type: unknown format = '%s'", format.latin1());
+
+	return "image/unknown";
+}
+
+// Long lines of encoded binary data SHOULD BE folded to 75 characters using the folding method defined in [MIME-DIR].
+static QString foldString(const QString &s)
+{
+	QString ret;
+
+	for (int i = 0; i < (int)s.length(); i++) {
+		if ( !(i % 75) )
+			ret += '\n';
+		ret += s[i];
+	}
+
+	return ret;
+}
+
+class VCard::Private
+{
+public:
+	Private();
+	~Private();
+
+	QString version;
+	QString fullName;
+	QString familyName, givenName, middleName, prefixName, suffixName;
+	QString nickName;
+
+	QByteArray photo;
+	QString photoURI;
+
+	QString bday;
+	AddressList addressList;
+	LabelList labelList;
+	PhoneList phoneList;
+	EmailList emailList;
+	QString jid;
+	QString mailer;
+	QString timezone;
+	Geo geo;
+	QString title;
+	QString role;
+
+	QByteArray logo;
+	QString logoURI;
+
+	VCard *agent;
+	QString agentURI;
+
+	Org org;
+	QStringList categories;
+	QString note;
+	QString prodId;
+	QString rev;
+	QString sortString;
+
+	QByteArray sound;
+	QString soundURI, soundPhonetic;
+
+	QString uid;
+	QString url;
+	QString desc;
+	PrivacyClass privacyClass;
+	QByteArray key;
+
+	bool isEmpty();
+};
+
+VCard::Private::Private()
+{
+	privacyClass = pcNone;
+	agent = 0;
+}
+
+VCard::Private::~Private()
+{
+	delete agent;
+}
+
+bool VCard::Private::isEmpty()
+{
+	if (	!version.isEmpty() ||
+		!fullName.isEmpty() ||
+		!familyName.isEmpty() || !givenName.isEmpty() || !middleName.isEmpty() || !prefixName.isEmpty() || !suffixName.isEmpty() ||
+		!nickName.isEmpty() ||
+		!photo.isEmpty() || !photoURI.isEmpty() ||
+		!bday.isEmpty() ||
+		!addressList.isEmpty() ||
+		!labelList.isEmpty() ||
+		!phoneList.isEmpty() ||
+		!emailList.isEmpty() ||
+		!jid.isEmpty() ||
+		!mailer.isEmpty() ||
+		!timezone.isEmpty() ||
+		!geo.lat.isEmpty() || !geo.lon.isEmpty() ||
+		!title.isEmpty() ||
+		!role.isEmpty() ||
+		!logo.isEmpty() || !logoURI.isEmpty() ||
+		(agent && !agent->isEmpty()) || !agentURI.isEmpty() ||
+		!org.name.isEmpty() || !org.unit.isEmpty() ||
+		!categories.isEmpty() ||
+		!note.isEmpty() ||
+		!prodId.isEmpty() ||
+		!rev.isEmpty() ||
+		!sortString.isEmpty() ||
+		!sound.isEmpty() || !soundURI.isEmpty() || !soundPhonetic.isEmpty() ||
+		!uid.isEmpty() ||
+		!url.isEmpty() ||
+		!desc.isEmpty() ||
+		(privacyClass != pcNone) ||
+		!key.isEmpty() )
+	{
+		return false;
+	}
+	return true;
+}
+
+VCard::VCard()
+{
+	d = new Private;
+}
+
+VCard::VCard(const VCard &from)
+{
+	d = new Private;
+	*this = from;
+}
+
+VCard & VCard::operator=(const VCard &from)
+{
+	if(d->agent) {
+		delete d->agent;
+		d->agent = 0;
+	}
+
+	*d = *from.d;
+
+	if(from.d->agent) {
+		// dup the agent
+		d->agent = new VCard(*from.d->agent);
+	}
+
+	return *this;
+}
+
+VCard::~VCard()
+{
+	delete d;
+}
+
+QDomElement VCard::toXml(QDomDocument *doc) const
+{
+	QDomElement v = doc->createElement("vCard");
+	v.setAttribute("version", "2.0");
+	v.setAttribute("prodid", "-//HandGen//NONSGML vGen v1.0//EN");
+	v.setAttribute("xmlns", "vcard-temp");
+
+	if ( !d->version.isEmpty() )
+		v.appendChild( textTag(doc, "VERSION",	d->version) );
+	if ( !d->fullName.isEmpty() )
+		v.appendChild( textTag(doc, "FN",	d->fullName) );
+
+	if ( !d->familyName.isEmpty() || !d->givenName.isEmpty() || !d->middleName.isEmpty() ||
+	     !d->prefixName.isEmpty() || !d->suffixName.isEmpty() ) {
+		QDomElement w = doc->createElement("N");
+
+		if ( !d->familyName.isEmpty() )
+			w.appendChild( textTag(doc, "FAMILY",	d->familyName) );
+		if ( !d->givenName.isEmpty() )
+			w.appendChild( textTag(doc, "GIVEN",	d->givenName) );
+		if ( !d->middleName.isEmpty() )
+			w.appendChild( textTag(doc, "MIDDLE",	d->middleName) );
+		if ( !d->prefixName.isEmpty() )
+			w.appendChild( textTag(doc, "PREFIX",	d->prefixName) );
+		if ( !d->suffixName.isEmpty() )
+			w.appendChild( textTag(doc, "SUFFIX",	d->suffixName) );
+
+		v.appendChild(w);
+	}
+
+	if ( !d->nickName.isEmpty() )
+		v.appendChild( textTag(doc, "NICKNAME",	d->nickName) );
+
+	if ( !d->photo.isEmpty() || !d->photoURI.isEmpty() ) {
+		QDomElement w = doc->createElement("PHOTO");
+
+		if ( !d->photo.isEmpty() ) {
+			w.appendChild( textTag(doc, "TYPE",	image2type(d->photo)) );
+			w.appendChild( textTag(doc, "BINVAL",	foldString( Base64::arrayToString(d->photo)) ) );
+		}
+		else if ( !d->photoURI.isEmpty() )
+			w.appendChild( textTag(doc, "EXTVAL",	d->photoURI) );
+
+		v.appendChild(w);
+	}
+
+	if ( !d->bday.isEmpty() )
+		v.appendChild( textTag(doc, "BDAY",	d->bday) );
+
+	if ( !d->addressList.isEmpty() ) {
+		AddressList::Iterator it = d->addressList.begin();
+		for ( ; it != d->addressList.end(); ++it ) {
+			QDomElement w = doc->createElement("ADR");
+			Address a = *it;
+
+			if ( a.home )
+				w.appendChild( emptyTag(doc, "HOME") );
+			if ( a.work )
+				w.appendChild( emptyTag(doc, "WORK") );
+			if ( a.postal )
+				w.appendChild( emptyTag(doc, "POSTAL") );
+			if ( a.parcel )
+				w.appendChild( emptyTag(doc, "PARCEL") );
+			if ( a.dom )
+				w.appendChild( emptyTag(doc, "DOM") );
+			if ( a.intl )
+				w.appendChild( emptyTag(doc, "INTL") );
+			if ( a.pref )
+				w.appendChild( emptyTag(doc, "PREF") );
+
+			if ( !a.pobox.isEmpty() )
+				w.appendChild( textTag(doc, "POBOX",	a.pobox) );
+			if ( !a.extaddr.isEmpty() )
+				w.appendChild( textTag(doc, "EXTADR",	a.extaddr) );
+			if ( !a.street.isEmpty() )
+				w.appendChild( textTag(doc, "STREET",	a.street) );
+			if ( !a.locality.isEmpty() )
+				w.appendChild( textTag(doc, "LOCALITY",	a.locality) );
+			if ( !a.region.isEmpty() )
+				w.appendChild( textTag(doc, "REGION",	a.region) );
+			if ( !a.pcode.isEmpty() )
+				w.appendChild( textTag(doc, "PCODE",	a.pcode) );
+			if ( !a.country.isEmpty() )
+				w.appendChild( textTag(doc, "CTRY",	a.country) );
+
+			v.appendChild(w);
+		}
+	}
+
+	if ( !d->labelList.isEmpty() ) {
+		LabelList::Iterator it = d->labelList.begin();
+		for ( ; it != d->labelList.end(); ++it ) {
+			QDomElement w = doc->createElement("LABEL");
+			Label l = *it;
+
+			if ( l.home )
+				w.appendChild( emptyTag(doc, "HOME") );
+			if ( l.work )
+				w.appendChild( emptyTag(doc, "WORK") );
+			if ( l.postal )
+				w.appendChild( emptyTag(doc, "POSTAL") );
+			if ( l.parcel )
+				w.appendChild( emptyTag(doc, "PARCEL") );
+			if ( l.dom )
+				w.appendChild( emptyTag(doc, "DOM") );
+			if ( l.intl )
+				w.appendChild( emptyTag(doc, "INTL") );
+			if ( l.pref )
+				w.appendChild( emptyTag(doc, "PREF") );
+
+			if ( !l.lines.isEmpty() ) {
+				QStringList::Iterator it = l.lines.begin();
+				for ( ; it != l.lines.end(); ++it )
+					w.appendChild( textTag(doc, "LINE", *it) );
+			}
+
+			v.appendChild(w);
+		}
+	}
+
+	if ( !d->phoneList.isEmpty() ) {
+		PhoneList::Iterator it = d->phoneList.begin();
+		for ( ; it != d->phoneList.end(); ++it ) {
+			QDomElement w = doc->createElement("TEL");
+			Phone p = *it;
+
+			if ( p.home )
+				w.appendChild( emptyTag(doc, "HOME") );
+			if ( p.work )
+				w.appendChild( emptyTag(doc, "WORK") );
+			if ( p.voice )
+				w.appendChild( emptyTag(doc, "VOICE") );
+			if ( p.fax )
+				w.appendChild( emptyTag(doc, "FAX") );
+			if ( p.pager )
+				w.appendChild( emptyTag(doc, "PAGER") );
+			if ( p.msg )
+				w.appendChild( emptyTag(doc, "MSG") );
+			if ( p.cell )
+				w.appendChild( emptyTag(doc, "CELL") );
+			if ( p.video )
+				w.appendChild( emptyTag(doc, "VIDEO") );
+			if ( p.bbs )
+				w.appendChild( emptyTag(doc, "BBS") );
+			if ( p.modem )
+				w.appendChild( emptyTag(doc, "MODEM") );
+			if ( p.isdn )
+				w.appendChild( emptyTag(doc, "ISDN") );
+			if ( p.pcs )
+				w.appendChild( emptyTag(doc, "PCS") );
+			if ( p.pref )
+				w.appendChild( emptyTag(doc, "PREF") );
+
+			if ( !p.number.isEmpty() )
+				w.appendChild( textTag(doc, "NUMBER",	p.number) );
+
+			v.appendChild(w);
+		}
+	}
+
+	if ( !d->emailList.isEmpty() ) {
+		EmailList::Iterator it = d->emailList.begin();
+		for ( ; it != d->emailList.end(); ++it ) {
+			QDomElement w = doc->createElement("EMAIL");
+			Email e = *it;
+
+			if ( e.home )
+				w.appendChild( emptyTag(doc, "HOME") );
+			if ( e.work )
+				w.appendChild( emptyTag(doc, "WORK") );
+			if ( e.internet )
+				w.appendChild( emptyTag(doc, "INTERNET") );
+			if ( e.x400 )
+				w.appendChild( emptyTag(doc, "X400") );
+
+			if ( !e.userid.isEmpty() )
+				w.appendChild( textTag(doc, "USERID",	e.userid) );
+
+			v.appendChild(w);
+		}
+	}
+
+	if ( !d->jid.isEmpty() )
+		v.appendChild( textTag(doc, "JABBERID",	d->jid) );
+	if ( !d->mailer.isEmpty() )
+		v.appendChild( textTag(doc, "MAILER",	d->mailer) );
+	if ( !d->timezone.isEmpty() )
+		v.appendChild( textTag(doc, "TZ",	d->timezone) );
+
+	if ( !d->geo.lat.isEmpty() || !d->geo.lon.isEmpty() ) {
+		QDomElement w = doc->createElement("GEO");
+
+		if ( !d->geo.lat.isEmpty() )
+			w.appendChild( textTag(doc, "LAT",	d->geo.lat) );
+		if ( !d->geo.lon.isEmpty() )
+			w.appendChild( textTag(doc, "LON",	d->geo.lon));
+
+		v.appendChild(w);
+	}
+
+	if ( !d->title.isEmpty() )
+		v.appendChild( textTag(doc, "TITLE",	d->title) );
+	if ( !d->role.isEmpty() )
+		v.appendChild( textTag(doc, "ROLE",	d->role) );
+
+	if ( !d->logo.isEmpty() || !d->logoURI.isEmpty() ) {
+		QDomElement w = doc->createElement("LOGO");
+
+		if ( !d->logo.isEmpty() ) {
+			w.appendChild( textTag(doc, "TYPE",	image2type(d->logo)) );
+			w.appendChild( textTag(doc, "BINVAL",	foldString( Base64::arrayToString(d->logo)) ) );
+		}
+		else if ( !d->logoURI.isEmpty() )
+			w.appendChild( textTag(doc, "EXTVAL",	d->logoURI) );
+
+		v.appendChild(w);
+	}
+
+	if ( !d->agentURI.isEmpty() || (d->agent && d->agent->isEmpty()) ) {
+		QDomElement w = doc->createElement("AGENT");
+
+		if ( d->agent && !d->agent->isEmpty() )
+			w.appendChild( d->agent->toXml(doc) );
+		else if ( !d->agentURI.isEmpty() )
+			w.appendChild( textTag(doc, "EXTVAL",	d->agentURI) );
+
+		v.appendChild(w);
+	}
+
+	if ( !d->org.name.isEmpty() || !d->org.unit.isEmpty() ) {
+		QDomElement w = doc->createElement("ORG");
+
+		if ( !d->org.name.isEmpty() )
+			w.appendChild( textTag(doc, "ORGNAME",	d->org.name) );
+
+		if ( !d->org.unit.isEmpty() ) {
+			QStringList::Iterator it = d->org.unit.begin();
+			for ( ; it != d->org.unit.end(); ++it )
+				w.appendChild( textTag(doc, "ORGUNIT",	*it) );
+		}
+
+		v.appendChild(w);
+	}
+
+	if ( !d->categories.isEmpty() ) {
+		QDomElement w = doc->createElement("CATEGORIES");
+
+		QStringList::Iterator it = d->categories.begin();
+		for ( ; it != d->categories.end(); ++it )
+			w.appendChild( textTag(doc, "KEYWORD", *it) );
+
+		v.appendChild(w);
+	}
+
+	if ( !d->note.isEmpty() )
+		v.appendChild( textTag(doc, "NOTE",	d->note) );
+	if ( !d->prodId.isEmpty() )
+		v.appendChild( textTag(doc, "PRODID",	d->prodId) );
+	if ( !d->rev.isEmpty() )
+		v.appendChild( textTag(doc, "REV",	d->rev) );
+	if ( !d->sortString.isEmpty() )
+		v.appendChild( textTag(doc, "SORT-STRING",	d->sortString) );
+
+	if ( !d->sound.isEmpty() || !d->soundURI.isEmpty() || !d->soundPhonetic.isEmpty() ) {
+		QDomElement w = doc->createElement("SOUND");
+
+		if ( !d->sound.isEmpty() )
+			w.appendChild( textTag(doc, "BINVAL",	foldString( Base64::arrayToString(d->sound)) ) );
+		else if ( !d->soundURI.isEmpty() )
+			w.appendChild( textTag(doc, "EXTVAL",	d->soundURI) );
+		else if ( !d->soundPhonetic.isEmpty() )
+			w.appendChild( textTag(doc, "PHONETIC",	d->soundPhonetic) );
+
+		v.appendChild(w);
+	}
+
+	if ( !d->uid.isEmpty() )
+		v.appendChild( textTag(doc, "UID",	d->uid) );
+	if ( !d->url.isEmpty() )
+		v.appendChild( textTag(doc, "URL",	d->url) );
+	if ( !d->desc.isEmpty() )
+		v.appendChild( textTag(doc, "DESC",	d->desc) );
+
+	if ( d->privacyClass != pcNone ) {
+		QDomElement w = doc->createElement("CLASS");
+
+		if ( d->privacyClass == pcPublic )
+			w.appendChild( emptyTag(doc, "PUBLIC") );
+		else if ( d->privacyClass == pcPrivate )
+			w.appendChild( emptyTag(doc, "PRIVATE") );
+		else if ( d->privacyClass == pcConfidential )
+			w.appendChild( emptyTag(doc, "CONFIDENTIAL") );
+
+		v.appendChild(w);
+	}
+
+	if ( !d->key.isEmpty() ) {
+		QDomElement w = doc->createElement("KEY");
+
+		// TODO: Justin, please check out this code
+		w.appendChild( textTag(doc, "TYPE", "text/plain")); // FIXME
+		w.appendChild( textTag(doc, "CRED", QString::fromUtf8(d->key)) ); // FIXME
+
+		v.appendChild(w);
+	}
+
+	return v;
+}
+
+bool VCard::fromXml(const QDomElement &q)
+{
+	if ( q.tagName().upper() != "VCARD" )
+		return false;
+
+	QDomNode n = q.firstChild();
+	for ( ; !n.isNull(); n = n.nextSibling() ) {
+		QDomElement i = n.toElement();
+		if ( i.isNull() )
+			continue;
+
+		QString tag = i.tagName().upper();
+
+		bool found;
+		QDomElement e;
+
+		if ( tag == "VERSION" )
+			d->version = i.text().stripWhiteSpace();
+		else if ( tag == "FN" )
+			d->fullName = i.text().stripWhiteSpace();
+		else if ( tag == "N" ) {
+			d->familyName = subTagText(i, "FAMILY");
+			d->givenName  = subTagText(i, "GIVEN");
+			d->middleName = subTagText(i, "MIDDLE");
+			d->prefixName = subTagText(i, "PREFIX");
+			d->suffixName = subTagText(i, "SUFFIX");
+		}
+		else if ( tag == "NICKNAME" )
+			d->nickName = i.text().stripWhiteSpace();
+		else if ( tag == "PHOTO" ) {
+			d->photo = Base64::stringToArray( subTagText(i, "BINVAL") );
+			d->photoURI = subTagText(i, "EXTVAL");
+		}
+		else if ( tag == "BDAY" )
+			d->bday = i.text().stripWhiteSpace();
+		else if ( tag == "ADR" ) {
+			Address a;
+
+			a.home   = hasSubTag(i, "HOME");
+			a.work   = hasSubTag(i, "WORK");
+			a.postal = hasSubTag(i, "POSTAL");
+			a.parcel = hasSubTag(i, "PARCEL");
+			a.dom    = hasSubTag(i, "DOM");
+			a.intl   = hasSubTag(i, "INTL");
+			a.pref   = hasSubTag(i, "PREF");
+
+			a.pobox    = subTagText(i, "POBOX");
+			a.extaddr  = subTagText(i, "EXTADR");
+			a.street   = subTagText(i, "STREET");
+			a.locality = subTagText(i, "LOCALITY");
+			a.region   = subTagText(i, "REGION");
+			a.pcode    = subTagText(i, "PCODE");
+			a.country  = subTagText(i, "CTRY");
+
+			if ( a.country.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+				if ( hasSubTag(i, "COUNTRY") )
+					a.country = subTagText(i, "COUNTRY");
+
+			if ( a.extaddr.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+				if ( hasSubTag(i, "EXTADD") )
+					a.extaddr = subTagText(i, "EXTADD");
+
+			d->addressList.append ( a );
+		}
+		else if ( tag == "LABEL" ) {
+			Label l;
+
+			l.home   = hasSubTag(i, "HOME");
+			l.work   = hasSubTag(i, "WORK");
+			l.postal = hasSubTag(i, "POSTAL");
+			l.parcel = hasSubTag(i, "PARCEL");
+			l.dom    = hasSubTag(i, "DOM");
+			l.intl   = hasSubTag(i, "INTL");
+			l.pref   = hasSubTag(i, "PREF");
+
+			QDomNode nn = i.firstChild();
+			for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+				QDomElement ii = nn.toElement();
+				if ( ii.isNull() )
+					continue;
+
+				if ( ii.tagName().upper() == "LINE" )
+					l.lines.append ( ii.text().stripWhiteSpace() );
+			}
+
+			d->labelList.append ( l );
+		}
+		else if ( tag == "TEL" ) {
+			Phone p;
+
+			p.home  = hasSubTag(i, "HOME");
+			p.work  = hasSubTag(i, "WORK");
+			p.voice = hasSubTag(i, "VOICE");
+			p.fax   = hasSubTag(i, "FAX");
+			p.pager = hasSubTag(i, "PAGER");
+			p.msg   = hasSubTag(i, "MSG");
+			p.cell  = hasSubTag(i, "CELL");
+			p.video = hasSubTag(i, "VIDEO");
+			p.bbs   = hasSubTag(i, "BBS");
+			p.modem = hasSubTag(i, "MODEM");
+			p.isdn  = hasSubTag(i, "ISDN");
+			p.pcs   = hasSubTag(i, "PCS");
+			p.pref  = hasSubTag(i, "PREF");
+
+			p.number = subTagText(i, "NUMBER");
+
+			if ( p.number.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+				if ( hasSubTag(i, "VOICE") )
+					p.number = subTagText(i, "VOICE");
+
+			d->phoneList.append ( p );
+		}
+		else if ( tag == "EMAIL" ) {
+			Email m;
+
+			m.home     = hasSubTag(i, "HOME");
+			m.work     = hasSubTag(i, "WORK");
+			m.internet = hasSubTag(i, "INTERNET");
+			m.x400     = hasSubTag(i, "X400");
+
+			m.userid = subTagText(i, "USERID");
+
+			if ( m.userid.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+				if ( !i.text().isEmpty() )
+					m.userid = i.text().stripWhiteSpace();
+
+			d->emailList.append ( m );
+		}
+		else if ( tag == "JABBERID" )
+			d->jid = i.text().stripWhiteSpace();
+		else if ( tag == "MAILER" )
+			d->mailer = i.text().stripWhiteSpace();
+		else if ( tag == "TZ" )
+			d->timezone = i.text().stripWhiteSpace();
+		else if ( tag == "GEO" ) {
+			d->geo.lat = subTagText(i, "LAT");
+			d->geo.lon = subTagText(i, "LON");
+		}
+		else if ( tag == "TITLE" )
+			d->title = i.text().stripWhiteSpace();
+		else if ( tag == "ROLE" )
+			d->role = i.text().stripWhiteSpace();
+		else if ( tag == "LOGO" ) {
+			d->logo = Base64::stringToArray( subTagText(i, "BINVAL") );
+			d->logoURI = subTagText(i, "EXTVAL");
+		}
+		else if ( tag == "AGENT" ) {
+			e = findSubTag(i, "VCARD", &found);
+			if ( found ) {
+				VCard a;
+				if ( a.fromXml(e) ) {
+					if ( !d->agent )
+						d->agent = new VCard;
+					*(d->agent) = a;
+				}
+			}
+
+			d->agentURI = subTagText(i, "EXTVAL");
+		}
+		else if ( tag == "ORG" ) {
+			d->org.name = subTagText(i, "ORGNAME");
+
+			QDomNode nn = i.firstChild();
+			for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+				QDomElement ii = nn.toElement();
+				if ( ii.isNull() )
+					continue;
+
+				if ( ii.tagName().upper() == "ORGUNIT" )
+					d->org.unit.append( ii.text().stripWhiteSpace() );
+			}
+		}
+		else if ( tag == "CATEGORIES") {
+			QDomNode nn = i.firstChild();
+			for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+				QDomElement ee = nn.toElement();
+				if ( ee.isNull() )
+					continue;
+
+				if ( ee.tagName().upper() == "KEYWORD" )
+					d->categories << ee.text().stripWhiteSpace();
+			}
+		}
+		else if ( tag == "NOTE" )
+			d->note = i.text().stripWhiteSpace();
+		else if ( tag == "PRODID" )
+			d->prodId = i.text().stripWhiteSpace();
+		else if ( tag == "REV" )
+			d->rev = i.text().stripWhiteSpace();
+		else if ( tag == "SORT-STRING" )
+			d->sortString = i.text().stripWhiteSpace();
+		else if ( tag == "SOUND" ) {
+			d->sound = Base64::stringToArray( subTagText(i, "BINVAL") );
+			d->soundURI      = subTagText(i, "EXTVAL");
+			d->soundPhonetic = subTagText(i, "PHONETIC");
+		}
+		else if ( tag == "UID" )
+			d->uid = i.text().stripWhiteSpace();
+		else if ( tag == "URL")
+			d->url = i.text().stripWhiteSpace();
+		else if ( tag == "DESC" )
+			d->desc = i.text().stripWhiteSpace();
+		else if ( tag == "CLASS" ) {
+			if ( hasSubTag(i, "PUBLIC") )
+				d->privacyClass = pcPublic;
+			else if ( hasSubTag(i, "PRIVATE") )
+				d->privacyClass = pcPrivate;
+			else if ( hasSubTag(i, "CONFIDENTIAL") )
+				d->privacyClass = pcConfidential;
+		}
+		else if ( tag == "KEY" ) {
+			// TODO: Justin, please check out this code
+			e = findSubTag(i, "TYPE", &found);
+			QString type = "text/plain";
+			if ( found )
+				type = e.text().stripWhiteSpace();
+
+			e = findSubTag(i, "CRED", &found );
+			if ( !found )
+				e = findSubTag(i, "BINVAL", &found); // case for very clever clients ;-)
+
+			if ( found )
+				d->key = e.text().utf8(); // FIXME
+		}
+	}
+
+	return true;
+}
+
+bool VCard::isEmpty() const
+{
+	return d->isEmpty();
+}
+
+// Some constructors
+
+VCard::Address::Address()
+{
+	home = work = postal = parcel = dom = intl = pref = false;
+}
+
+VCard::Label::Label()
+{
+	home = work = postal = parcel = dom = intl = pref = false;
+}
+
+VCard::Phone::Phone()
+{
+	home = work = voice = fax = pager = msg = cell = video = bbs = modem = isdn = pcs = pref = false;
+}
+
+VCard::Email::Email()
+{
+	home = work = internet = x400 = false;
+}
+
+VCard::Geo::Geo()
+{
+}
+
+VCard::Org::Org()
+{
+}
+
+// vCard properties...
+
+const QString &VCard::version() const
+{
+	return d->version;
+}
+
+void VCard::setVersion(const QString &v)
+{
+	d->version = v;
+}
+
+const QString &VCard::fullName() const
+{
+	return d->fullName;
+}
+
+void VCard::setFullName(const QString &n)
+{
+	d->fullName = n;
+}
+
+const QString &VCard::familyName() const
+{
+	return d->familyName;
+}
+
+void VCard::setFamilyName(const QString &n)
+{
+	d->familyName = n;
+}
+
+const QString &VCard::givenName() const
+{
+	return d->givenName;
+}
+
+void VCard::setGivenName(const QString &n)
+{
+	d->givenName = n;
+}
+
+const QString &VCard::middleName() const
+{
+	return d->middleName;
+}
+
+void VCard::setMiddleName(const QString &n)
+{
+	d->middleName = n;
+}
+
+const QString &VCard::prefixName() const
+{
+	return d->prefixName;
+}
+
+void VCard::setPrefixName(const QString &p)
+{
+	d->prefixName = p;
+}
+
+const QString &VCard::suffixName() const
+{
+	return d->suffixName;
+}
+
+void VCard::setSuffixName(const QString &s)
+{
+	d->suffixName = s;
+}
+
+const QString &VCard::nickName() const
+{
+	return d->nickName;
+}
+
+void VCard::setNickName(const QString &n)
+{
+	d->nickName = n;
+}
+
+const QByteArray &VCard::photo() const
+{
+	return d->photo;
+}
+
+void VCard::setPhoto(const QByteArray &i)
+{
+	d->photo = i;
+}
+
+const QString &VCard::photoURI() const
+{
+	return d->photoURI;
+}
+
+void VCard::setPhotoURI(const QString &p)
+{
+	d->photoURI = p;
+}
+
+const QDate VCard::bday() const
+{
+	return QDate::fromString(d->bday);
+}
+
+void VCard::setBday(const QDate &date)
+{
+	d->bday = date.toString();
+}
+
+const QString &VCard::bdayStr() const
+{
+	return d->bday;
+}
+
+void VCard::setBdayStr(const QString &date)
+{
+	d->bday = date;
+}
+
+const VCard::AddressList &VCard::addressList() const
+{
+	return d->addressList;
+}
+
+void VCard::setAddressList(const VCard::AddressList &a)
+{
+	d->addressList = a;
+}
+
+const VCard::LabelList &VCard::labelList() const
+{
+	return d->labelList;
+}
+
+void VCard::setLabelList(const VCard::LabelList &l)
+{
+	d->labelList = l;
+}
+
+const VCard::PhoneList &VCard::phoneList() const
+{
+	return d->phoneList;
+}
+
+void VCard::setPhoneList(const VCard::PhoneList &p)
+{
+	d->phoneList = p;
+}
+
+const VCard::EmailList &VCard::emailList() const
+{
+	return d->emailList;
+}
+
+void VCard::setEmailList(const VCard::EmailList &e)
+{
+	d->emailList = e;
+}
+
+const QString &VCard::jid() const
+{
+	return d->jid;
+}
+
+void VCard::setJid(const QString &j)
+{
+	d->jid = j;
+}
+
+const QString &VCard::mailer() const
+{
+	return d->mailer;
+}
+
+void VCard::setMailer(const QString &m)
+{
+	d->mailer = m;
+}
+
+const QString &VCard::timezone() const
+{
+	return d->timezone;
+}
+
+void VCard::setTimezone(const QString &t)
+{
+	d->timezone = t;
+}
+
+const VCard::Geo &VCard::geo() const
+{
+	return d->geo;
+}
+
+void VCard::setGeo(const VCard::Geo &g)
+{
+	d->geo = g;
+}
+
+const QString &VCard::title() const
+{
+	return d->title;
+}
+
+void VCard::setTitle(const QString &t)
+{
+	d->title = t;
+}
+
+const QString &VCard::role() const
+{
+	return d->role;
+}
+
+void VCard::setRole(const QString &r)
+{
+	d->role = r;
+}
+
+const QByteArray &VCard::logo() const
+{
+	return d->logo;
+}
+
+void VCard::setLogo(const QByteArray &i)
+{
+	d->logo = i;
+}
+
+const QString &VCard::logoURI() const
+{
+	return d->logoURI;
+}
+
+void VCard::setLogoURI(const QString &l)
+{
+	d->logoURI = l;
+}
+
+const VCard *VCard::agent() const
+{
+	return d->agent;
+}
+
+void VCard::setAgent(const VCard &v)
+{
+	if ( !d->agent )
+		d->agent = new VCard;
+	*(d->agent) = v;
+}
+
+const QString VCard::agentURI() const
+{
+	return d->agentURI;
+}
+
+void VCard::setAgentURI(const QString &a)
+{
+	d->agentURI = a;
+}
+
+const VCard::Org &VCard::org() const
+{
+	return d->org;
+}
+
+void VCard::setOrg(const VCard::Org &o)
+{
+	d->org = o;
+}
+
+const QStringList &VCard::categories() const
+{
+	return d->categories;
+}
+
+void VCard::setCategories(const QStringList &c)
+{
+	d->categories = c;
+}
+
+const QString &VCard::note() const
+{
+	return d->note;
+}
+
+void VCard::setNote(const QString &n)
+{
+	d->note = n;
+}
+
+const QString &VCard::prodId() const
+{
+	return d->prodId;
+}
+
+void VCard::setProdId(const QString &p)
+{
+	d->prodId = p;
+}
+
+const QString &VCard::rev() const
+{
+	return d->rev;
+}
+
+void VCard::setRev(const QString &r)
+{
+	d->rev = r;
+}
+
+const QString &VCard::sortString() const
+{
+	return d->sortString;
+}
+
+void VCard::setSortString(const QString &s)
+{
+	d->sortString = s;
+}
+
+const QByteArray &VCard::sound() const
+{
+	return d->sound;
+}
+
+void VCard::setSound(const QByteArray &s)
+{
+	d->sound = s;
+}
+
+const QString &VCard::soundURI() const
+{
+	return d->soundURI;
+}
+
+void VCard::setSoundURI(const QString &s)
+{
+	d->soundURI = s;
+}
+
+const QString &VCard::soundPhonetic() const
+{
+	return d->soundPhonetic;
+}
+
+void VCard::setSoundPhonetic(const QString &s)
+{
+	d->soundPhonetic = s;
+}
+
+const QString &VCard::uid() const
+{
+	return d->uid;
+}
+
+void VCard::setUid(const QString &u)
+{
+	d->uid = u;
+}
+
+const QString &VCard::url() const
+{
+	return d->url;
+}
+
+void VCard::setUrl(const QString &u)
+{
+	d->url = u;
+}
+
+const QString &VCard::desc() const
+{
+	return d->desc;
+}
+
+void VCard::setDesc(const QString &desc)
+{
+	d->desc = desc;
+}
+
+const VCard::PrivacyClass &VCard::privacyClass() const
+{
+	return d->privacyClass;
+}
+
+void VCard::setPrivacyClass(const VCard::PrivacyClass &c)
+{
+	d->privacyClass = c;
+}
+
+const QByteArray &VCard::key() const
+{
+	return d->key;
+}
+
+void VCard::setKey(const QByteArray &k)
+{
+	d->key = k;
+}
Index: psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_vcard.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_vcard.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_vcard.h	(revision 38)
@@ -0,0 +1,284 @@
+/*
+ * xmpp_vcard.h - classes for handling vCards
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef JABBER_VCARD_H
+#define JABBER_VCARD_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qcstring.h>
+
+#include <qvaluelist.h>
+#include <qdom.h>
+
+class QDate;
+
+namespace XMPP
+{
+	class VCard
+	{
+	public:
+		VCard();
+		VCard(const VCard &);
+		VCard & operator=(const VCard &);
+		~VCard();
+
+		QDomElement toXml(QDomDocument *) const;
+		bool fromXml(const QDomElement &);
+		bool isEmpty() const;
+
+		const QString &version() const;
+		void setVersion(const QString &);
+
+		const QString &fullName() const;
+		void setFullName(const QString &);
+
+
+		const QString &familyName() const;
+		void setFamilyName(const QString &);
+
+		const QString &givenName() const;
+		void setGivenName(const QString &);
+
+		const QString &middleName() const;
+		void setMiddleName(const QString &);
+
+		const QString &prefixName() const;
+		void setPrefixName(const QString &);
+
+		const QString &suffixName() const;
+		void setSuffixName(const QString &);
+
+
+		const QString &nickName() const;
+		void setNickName(const QString &);
+
+
+		const QByteArray &photo() const;
+		void setPhoto(const QByteArray &);
+
+		const QString &photoURI() const;
+		void setPhotoURI(const QString &);
+
+
+		const QDate bday() const;
+		void setBday(const QDate &);
+
+		const QString &bdayStr() const;
+		void setBdayStr(const QString &);
+
+
+		class Address {
+		public:
+			Address();
+
+			bool home;
+			bool work;
+			bool postal;
+			bool parcel;
+
+			bool dom;
+			bool intl;
+
+			bool pref;
+
+			QString pobox;
+			QString extaddr;
+			QString street;
+			QString locality;
+			QString region;
+			QString pcode;
+			QString country;
+		};
+		typedef QValueList<Address> AddressList;
+		const AddressList &addressList() const;
+		void setAddressList(const AddressList &);
+
+		class Label {
+		public:
+			Label();
+
+			bool home;
+			bool work;
+			bool postal;
+			bool parcel;
+
+			bool dom;
+			bool intl;
+
+			bool pref;
+
+			QStringList lines;
+		};
+		typedef QValueList<Label> LabelList;
+		const LabelList &labelList() const;
+		void setLabelList(const LabelList &);
+
+
+		class Phone {
+		public:
+			Phone();
+
+			bool home;
+			bool work;
+			bool voice;
+			bool fax;
+			bool pager;
+			bool msg;
+			bool cell;
+			bool video;
+			bool bbs;
+			bool modem;
+			bool isdn;
+			bool pcs;
+			bool pref;
+
+			QString number;
+		};
+		typedef QValueList<Phone> PhoneList;
+		const PhoneList &phoneList() const;
+		void setPhoneList(const PhoneList &);
+
+
+		class Email {
+		public:
+			Email();
+
+			bool home;
+			bool work;
+			bool internet;
+			bool x400;
+
+			QString userid;
+		};
+		typedef QValueList<Email> EmailList;
+		const EmailList &emailList() const;
+		void setEmailList(const EmailList &);
+
+
+		const QString &jid() const;
+		void setJid(const QString &);
+
+		const QString &mailer() const;
+		void setMailer(const QString &);
+
+		const QString &timezone() const;
+		void setTimezone(const QString &);
+
+
+		class Geo {
+		public:
+			Geo();
+
+			QString lat;
+			QString lon;
+		};
+		const Geo &geo() const;
+		void setGeo(const Geo &);
+
+
+		const QString &title() const;
+		void setTitle(const QString &);
+
+		const QString &role() const;
+		void setRole(const QString &);
+
+
+		const QByteArray &logo() const;
+		void setLogo(const QByteArray &);
+
+		const QString &logoURI() const;
+		void setLogoURI(const QString &);
+
+
+		const VCard *agent() const;
+		void setAgent(const VCard &);
+
+		const QString agentURI() const;
+		void setAgentURI(const QString &);
+
+
+		class Org {
+		public:
+			Org();
+
+			QString name;
+			QStringList unit;
+		};
+		const Org &org() const;
+		void setOrg(const Org &);
+
+
+		const QStringList &categories() const;
+		void setCategories(const QStringList &);
+
+		const QString &note() const;
+		void setNote(const QString &);
+
+		const QString &prodId() const; // it must equal to "Psi" ;-)
+		void setProdId(const QString &);
+
+		const QString &rev() const;
+		void setRev(const QString &);
+
+		const QString &sortString() const;
+		void setSortString(const QString &);
+
+
+		const QByteArray &sound() const;
+		void setSound(const QByteArray &);
+
+		const QString &soundURI() const;
+		void setSoundURI(const QString &);
+
+		const QString &soundPhonetic() const;
+		void setSoundPhonetic(const QString &);
+
+
+		const QString &uid() const;
+		void setUid(const QString &);
+
+		const QString &url() const;
+		void setUrl(const QString &);
+
+		const QString &desc() const;
+		void setDesc(const QString &);
+
+
+		enum PrivacyClass {
+			pcNone = 0,
+			pcPublic = 1,
+			pcPrivate,
+			pcConfidential
+		};
+		const PrivacyClass &privacyClass() const;
+		void setPrivacyClass(const PrivacyClass &);
+
+
+		const QByteArray &key() const;
+		void setKey(const QByteArray &);
+
+	private:
+		class Private;
+		Private *d;
+	};
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_xmlcommon.cpp
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_xmlcommon.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_xmlcommon.cpp	(revision 38)
@@ -0,0 +1,386 @@
+/*
+ * xmlcommon.cpp - helper functions for dealing with XML
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"xmpp_xmlcommon.h"
+
+#include <qstring.h>
+#include <qdom.h>
+#include <qdatetime.h>
+#include <qsize.h>
+#include <qrect.h>
+#include <qstringlist.h>
+#include <qcolor.h>
+
+#include"im.h"
+
+bool stamp2TS(const QString &ts, QDateTime *d)
+{
+	if(ts.length() != 17)
+		return false;
+
+	int year  = ts.mid(0,4).toInt();
+	int month = ts.mid(4,2).toInt();
+	int day   = ts.mid(6,2).toInt();
+
+	int hour  = ts.mid(9,2).toInt();
+	int min   = ts.mid(12,2).toInt();
+	int sec   = ts.mid(15,2).toInt();
+
+	QDate xd;
+	xd.setYMD(year, month, day);
+	if(!xd.isValid())
+		return false;
+
+	QTime xt;
+	xt.setHMS(hour, min, sec);
+	if(!xt.isValid())
+		return false;
+
+	d->setDate(xd);
+	d->setTime(xt);
+
+	return true;
+}
+
+QString TS2stamp(const QDateTime &d)
+{
+	QString str;
+
+	str.sprintf("%04d%02d%02dT%02d:%02d:%02d",
+		d.date().year(),
+		d.date().month(),
+		d.date().day(),
+		d.time().hour(),
+		d.time().minute(),
+		d.time().second());
+
+	return str;
+}
+
+QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
+{
+	QDomElement tag = doc->createElement(name);
+	QDomText text = doc->createTextNode(content);
+	tag.appendChild(text);
+
+	return tag;
+}
+
+QString tagContent(const QDomElement &e)
+{
+	// look for some tag content
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomText i = n.toText();
+		if(i.isNull())
+			continue;
+		return i.data();
+	}
+
+	return "";
+}
+
+QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+	if(found)
+		*found = false;
+
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+		if(i.tagName() == name) {
+			if(found)
+				*found = true;
+			return i;
+		}
+	}
+
+	QDomElement tmp;
+	return tmp;
+}
+
+QDomElement createIQ(QDomDocument *doc, const QString &type, const QString &to, const QString &id)
+{
+	QDomElement iq = doc->createElement("iq");
+	if(!type.isEmpty())
+		iq.setAttribute("type", type);
+	if(!to.isEmpty())
+		iq.setAttribute("to", to);
+	if(!id.isEmpty())
+		iq.setAttribute("id", id);
+
+	return iq;
+}
+
+QDomElement queryTag(const QDomElement &e)
+{
+	bool found;
+	QDomElement q = findSubTag(e, "query", &found);
+	return q;
+}
+
+QString queryNS(const QDomElement &e)
+{
+	bool found;
+	QDomElement q = findSubTag(e, "query", &found);
+	if(found)
+		return q.attribute("xmlns");
+
+	return "";
+}
+
+void getErrorFromElement(const QDomElement &e, int *code, QString *str)
+{
+	bool found;
+	QDomElement tag = findSubTag(e, "error", &found);
+	if(!found)
+		return;
+
+	if(code)
+		*code = tag.attribute("code").toInt();
+	if(str)
+		*str = tagContent(tag);
+}
+
+//----------------------------------------------------------------------------
+// XMLHelper
+//----------------------------------------------------------------------------
+
+namespace XMLHelper {
+
+QDomElement emptyTag(QDomDocument *doc, const QString &name)
+{
+	QDomElement tag = doc->createElement(name);
+
+	return tag;
+}
+
+bool hasSubTag(const QDomElement &e, const QString &name)
+{
+	bool found;
+	findSubTag(e, name, &found);
+	return found;
+}
+
+QString subTagText(const QDomElement &e, const QString &name)
+{
+	bool found;
+	QDomElement i = findSubTag(e, name, &found);
+	if ( found )
+		return i.text();
+	return QString::null;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, const QString &content)
+{
+	QDomElement tag = doc.createElement(name);
+	QDomText text = doc.createTextNode(content);
+	tag.appendChild(text);
+
+	return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, int content)
+{
+	QDomElement tag = doc.createElement(name);
+	QDomText text = doc.createTextNode(QString::number(content));
+	tag.appendChild(text);
+
+	return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, bool content)
+{
+	QDomElement tag = doc.createElement(name);
+	QDomText text = doc.createTextNode(content ? "true" : "false");
+	tag.appendChild(text);
+
+	return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, QSize &s)
+{
+	QString str;
+	str.sprintf("%d,%d", s.width(), s.height());
+
+	QDomElement tag = doc.createElement(name);
+	QDomText text = doc.createTextNode(str);
+	tag.appendChild(text);
+
+	return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, QRect &r)
+{
+	QString str;
+	str.sprintf("%d,%d,%d,%d", r.x(), r.y(), r.width(), r.height());
+
+	QDomElement tag = doc.createElement(name);
+	QDomText text = doc.createTextNode(str);
+	tag.appendChild(text);
+
+	return tag;
+}
+
+QDomElement stringListToXml(QDomDocument &doc, const QString &name, const QStringList &l)
+{
+	QDomElement tag = doc.createElement(name);
+	for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it)
+		tag.appendChild(textTag(doc, "item", *it));
+
+	return tag;
+}
+
+/*QString tagContent(const QDomElement &e)
+{
+	// look for some tag content
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomText i = n.toText();
+		if(i.isNull())
+			continue;
+		return i.data();
+	}
+
+	return "";
+}*/
+
+/*QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+	if(found)
+		*found = FALSE;
+
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+		if(i.tagName() == name) {
+			if(found)
+				*found = TRUE;
+			return i;
+		}
+	}
+
+	QDomElement tmp;
+	return tmp;
+}*/
+
+void readEntry(const QDomElement &e, const QString &name, QString *v)
+{
+	bool found = FALSE;
+	QDomElement tag = findSubTag(e, name, &found);
+	if(!found)
+		return;
+	*v = tagContent(tag);
+}
+
+void readNumEntry(const QDomElement &e, const QString &name, int *v)
+{
+	bool found = FALSE;
+	QDomElement tag = findSubTag(e, name, &found);
+	if(!found)
+		return;
+	*v = tagContent(tag).toInt();
+}
+
+void readBoolEntry(const QDomElement &e, const QString &name, bool *v)
+{
+	bool found = FALSE;
+	QDomElement tag = findSubTag(e, name, &found);
+	if(!found)
+		return;
+	*v = (tagContent(tag) == "true") ? TRUE: FALSE;
+}
+
+void readSizeEntry(const QDomElement &e, const QString &name, QSize *v)
+{
+	bool found = FALSE;
+	QDomElement tag = findSubTag(e, name, &found);
+	if(!found)
+		return;
+	QStringList list = QStringList::split(',', tagContent(tag));
+	if(list.count() != 2)
+		return;
+	QSize s;
+	s.setWidth(list[0].toInt());
+	s.setHeight(list[1].toInt());
+	*v = s;
+}
+
+void readRectEntry(const QDomElement &e, const QString &name, QRect *v)
+{
+	bool found = FALSE;
+	QDomElement tag = findSubTag(e, name, &found);
+	if(!found)
+		return;
+	QStringList list = QStringList::split(',', tagContent(tag));
+	if(list.count() != 4)
+		return;
+	QRect r;
+	r.setX(list[0].toInt());
+	r.setY(list[1].toInt());
+	r.setWidth(list[2].toInt());
+	r.setHeight(list[3].toInt());
+	*v = r;
+}
+
+void readColorEntry(const QDomElement &e, const QString &name, QColor *v)
+{
+	bool found = FALSE;
+	QDomElement tag = findSubTag(e, name, &found);
+	if(!found)
+		return;
+	QColor c;
+	c.setNamedColor(tagContent(tag));
+	if(c.isValid())
+		*v = c;
+}
+
+void xmlToStringList(const QDomElement &e, const QString &name, QStringList *v)
+{
+	bool found = false;
+	QDomElement tag = findSubTag(e, name, &found);
+	if(!found)
+		return;
+	QStringList list;
+	for(QDomNode n = tag.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+		if(i.tagName() == "item")
+			list += tagContent(i);
+	}
+	*v = list;
+}
+
+void setBoolAttribute(QDomElement e, const QString &name, bool b)
+{
+	e.setAttribute(name, b ? "true" : "false");
+}
+
+void readBoolAttribute(QDomElement e, const QString &name, bool *v)
+{
+	if(e.hasAttribute(name)) {
+		QString s = e.attribute(name);
+		*v = (s == "true") ? TRUE: FALSE;
+	}
+}
+
+};
+
Index: psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_xmlcommon.h
===================================================================
--- psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_xmlcommon.h	(revision 38)
+++ psi/tags/0.10-os2-b1/iris/xmpp-im/xmpp_xmlcommon.h	(revision 38)
@@ -0,0 +1,71 @@
+/*
+ * xmlcommon.h - helper functions for dealing with XML
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef JABBER_XMLCOMMON_H
+#define JABBER_XMLCOMMON_H
+
+#include<qdom.h>
+
+class QDateTime;
+class QRect;
+class QSize;
+class QColor;
+class QStringList;
+
+bool stamp2TS(const QString &ts, QDateTime *d);
+QString TS2stamp(const QDateTime &d);
+QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content);
+QString tagContent(const QDomElement &e);
+QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found);
+QDomElement createIQ(QDomDocument *doc, const QString &type, const QString &to, const QString &id);
+QDomElement queryTag(const QDomElement &e);
+QString queryNS(const QDomElement &e);
+void getErrorFromElement(const QDomElement &e, int *code, QString *str);
+
+namespace XMLHelper {
+	//QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found);
+	bool hasSubTag(const QDomElement &e, const QString &name);
+
+	QDomElement emptyTag(QDomDocument *doc, const QString &name);
+	QString subTagText(const QDomElement &e, const QString &name);
+
+	QDomElement textTag(QDomDocument &doc, const QString &name, const QString &content);
+	QDomElement textTag(QDomDocument &doc, const QString &name, int content);
+	QDomElement textTag(QDomDocument &doc, const QString &name, bool content);
+	QDomElement textTag(QDomDocument &doc, const QString &name, QSize &s);
+	QDomElement textTag(QDomDocument &doc, const QString &name, QRect &r);
+	QDomElement stringListToXml(QDomDocument &doc, const QString &name, const QStringList &l);
+
+	void readEntry(const QDomElement &e, const QString &name, QString *v);
+	void readNumEntry(const QDomElement &e, const QString &name, int *v);
+	void readBoolEntry(const QDomElement &e, const QString &name, bool *v);
+	void readSizeEntry(const QDomElement &e, const QString &name, QSize *v);
+	void readRectEntry(const QDomElement &e, const QString &name, QRect *v);
+	void readColorEntry(const QDomElement &e, const QString &name, QColor *v);
+
+	void xmlToStringList(const QDomElement &e, const QString &name, QStringList *v);
+
+	void setBoolAttribute(QDomElement e, const QString &name, bool b);
+	void readBoolAttribute(QDomElement e, const QString &name, bool *v);
+
+	//QString tagContent(const QDomElement &e); // obsolete;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi.pri
===================================================================
--- psi/tags/0.10-os2-b1/libpsi.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi.pri	(revision 38)
@@ -0,0 +1,17 @@
+libpsi {
+	# iconset
+	CONFIG += iconset
+	ICONSET_CPP = $$LIBPSI_CPP/iconset
+	include($$ICONSET_CPP/iconset.pri)
+
+	# psiwidgets
+	CONFIG += psiwidgets
+	WIDGETS_CPP = $$LIBPSI_CPP/psiwidgets
+	include($$WIDGETS_CPP/psiwidgets.pri)
+
+	# psipng
+	CONFIG += psipng
+	#CONFIG += psimng # uncomment this if you're really brave, and you have libmng installed
+	PSIPNG_CPP = $$LIBPSI_CPP/psipng
+	include($$PSIPNG_CPP/psipng.pri)
+}
Index: psi/tags/0.10-os2-b1/libpsi/iconset/ICONSET-HOWTO
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/iconset/ICONSET-HOWTO	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/iconset/ICONSET-HOWTO	(revision 38)
@@ -0,0 +1,127 @@
+Iconset-HOWTO
+~~~~~~~~~~~~~
+
+Step-by-step guide how to create an iconset.
+
+1. Create a directory, and name it somehow. For example "my-first-iconset".
+
+2. Create a file named "icondef.xml" in that directory.
+
+3. Place all icons and sounds you want to use in iconset, in that directory.
+
+4. Start editind "icondef.xml" with your favourite XML editor.
+
+5. Write the following tags in it:
+<?xml version='1.0' encoding='UTF-8'?>
+<icondef>
+</icondef>
+
+6. First, add the iconset meta information:
+<?xml version='1.0' encoding='UTF-8'?>
+<icondef>
+	<meta>
+		<name>Iconset Name</name>
+		<version>Iconset Version</version>
+		<description>Iconset Description</description>
+		<creation>2003-01-25</creation> <!-- Creation date yyyy-mm-dd -->
+		<home>http://www.myiconsethomepage.com</home>
+		<author	jid='mblsha@jabber.ru'
+			email='mblsha@spammeanddie.com'
+			www='hhtp://maz.sf.net'>Michail Pishchagin</author>
+		<author>I.M. Anonymous</author>
+	</meta>
+</icondef>
+
+7. Now, it's time to add some icons:
+<?xml version='1.0' encoding='UTF-8'?>
+<icondef>
+	<meta> <!-- ... --> </meta>
+
+	<icon>
+		<text>:-)</text>
+		<text>:)</text>
+		<text xml:lang='en'>:smiley:</text>
+
+		<object mime='image/png'>smiley.png</object>
+		<object mime='audio/x-wav'>smiley.wav</object>
+	</icon>
+</icondef>
+
+This iconset contains only one icon, that is associated with the following strings:
+':-)', ':)', ':smiley:'. When it is shown on screen, it will be displayed as
+'smiley.png' image, and will play the 'smiley.wav' sound.
+
+Possible mime-types are:
+	1. For images:
+		image/png -- preferred image format
+		video/x-mng (animated format)
+		image/gif   (animated format)
+		image/x-xpm
+		image/bmp
+		image/jpeg
+		image/svg+xml
+
+	2. For sounds:
+		audio/x-wav -- preferred sound format, as it can be played on all Psi platforms
+		audio/x-ogg
+		audio/x-mp3
+		audio/x-midi -- not really supported
+
+Multiple graphic mime types, and sound mime types can be specified simultaneously:
+
+	<!-- ... -->
+	<icon>
+		<object mime='image/png'>smiley.png</object>
+		<object mime='video/x-mng'>smiley.mng</object>
+		<object mime='image/gif'>smiley.gif</object>
+
+		<object mime='audio/x-wav'>smiley.wav</object>
+		<object mime='audio/x-ogg'>smiley.ogg</object>
+	</icon>
+	<!-- ... -->
+
+8. Advanced Psi icon tags:
+
+The following icon specifies its name. It is primately used in system Psi iconsets:
+
+	<!-- ... -->
+	<icon>
+		<object mime='image/png'>smiley.png</object>
+		<object mime='audio/x-wav'>smiley.wav</object>
+
+		<x xmlns='name'>emoticon/smiley</x>
+	</icon>
+	<!-- ... -->
+
+And it can be forced for icon to be animated, or not:
+
+	<!-- ... -->
+	<icon>
+		<object mime='image/png'>connect.png</object>
+
+		<x xmlns='name'>psi/connect</x>
+		<x xmlns='type'>animation</x>
+	</icon>
+	<!-- ... -->
+
+This way, icon will be animated. Animation frames are laid horizontally in that
+single .png graphic.
+
+	<!-- ... -->
+	<icon>
+		<object mime='image/png'>connect.png</object>
+
+		<x xmlns='name'>psi/connect</x>
+		<x xmlns='type'>image</x>
+	</icon>
+	<!-- ... -->
+
+And this way, it will be loaded as single graphic.
+
+9. Packing it all together: Pack your 'my-first-iconset' directory in a
+my-first-iconset.zip archive using your favourite archiver. Please note,
+that name of directory and name of archive MUST be the same. 
+Then, you should rename the resulting file to my-first-iconset.jisp and
+distribute that file.
+
+Good Luck! :-)
Index: psi/tags/0.10-os2-b1/libpsi/iconset/anim.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/iconset/anim.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/iconset/anim.cpp	(revision 38)
@@ -0,0 +1,437 @@
+/*
+ * anim.cpp - class for handling animations
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "anim.h"
+#include "iconset.h"
+
+#include <qobject.h>
+#include <qasyncimageio.h>
+#include <qptrvector.h>
+#include <qtimer.h>
+#include <qapplication.h>
+
+#include "psimng.h"
+
+/*!
+	\class Anim
+	\brief Class for handling animations
+
+	Anim is a class that can load animations. Generally, it looks like
+	QMovie but it loads animations in one pass and stores it in memory.
+
+	Each frame of Anim is stored as Impix.
+*/
+
+//! \if _hide_doc_
+class Anim::Private : public QObject, public QShared, private QImageConsumer
+{
+	Q_OBJECT
+public:
+	QImageDecoder *decoder;
+	QTimer *frametimer;
+
+	bool empty;
+	bool paused;
+
+	int speed;
+	int lasttimerinterval, frameperiod;
+
+	int looping, loop;
+
+	class Frame {
+	public:
+		Frame(Impix *i, int p)
+		{
+			impix = i;
+			period = p;
+		}
+		~Frame()
+		{
+			delete impix;
+		}
+		Frame(const Frame &from)
+		{
+			impix = new Impix( *from.impix );
+			period = from.period;
+		}
+
+		Impix *impix;
+		int period;
+	};
+
+	QPtrVector<Frame> frames;
+	int frame;
+
+public:
+	void init()
+	{
+		frames.setAutoDelete(true);
+
+		frametimer = new QTimer(this);
+		QObject::connect(frametimer, SIGNAL(timeout()), this, SLOT(refresh()));
+
+		speed = 120;
+		lasttimerinterval = -1;
+		frameperiod = 120;
+
+		looping = 0; // MNG movies doesn't contain loop flag?
+		loop = 0;
+		frame = 0;
+		paused = true;
+	}
+
+	Private()
+	{
+		init();
+	}
+
+	Private(const Private &from)
+	: QObject(), QShared(), QImageConsumer()
+	{
+		init();
+
+		speed = from.speed;
+		lasttimerinterval = from.lasttimerinterval;
+		frameperiod = from.frameperiod;
+		looping = from.looping;
+		loop = from.loop;
+		frame = from.frame;
+		paused = from.paused;
+
+		for ( uint i = 0; i < from.frames.count(); i++ ) {
+			frames.resize ( frames.count()+1 );
+			frames.insert ( frames.count(), new Frame( *from.frames[i] ) );
+		}
+
+		if ( !paused )
+			unpause();
+	}
+
+	Private(const QByteArray ba)
+	{
+		init();
+
+		qApp->lock();
+		decoder = new QImageDecoder(this);
+
+		uchar *buf = reinterpret_cast<uchar *>(ba.data());
+		int bytecount = ba.count();
+		bool finished = false;
+		while ( bytecount && !finished ) {
+			// if you want decent MNG support, please do initPsiMngIO() (../psipng/psimng.h)
+
+			// TODO: what number of cycles require MNG movies for normal display?
+			//for (int i = 0; i < 10; i++) { // the more is counter, more frames MNG animation will give :)
+				//int used = decoder->decode(buf, !i ? bytecount : 0);
+				int used = decoder->decode(buf, bytecount);
+				if ( used < 0 ) {
+					qWarning("Anim::loadAnim(): Unrecognized Format error");
+					finished = true;
+					break;
+				}
+				buf += used;
+				bytecount -= used;
+			//}
+		}
+
+		delete decoder;
+		qApp->unlock();
+		//qWarning("Anim:: %d frames", numFrames());
+	}
+
+	~Private()
+	{
+		if ( frametimer )
+			delete frametimer;
+	}
+
+	void pause()
+	{
+		paused = true;
+		frametimer->stop();
+	}
+
+	void unpause()
+	{
+		paused = false;
+		restartTimer();
+	}
+
+	void restart()
+	{
+		frame = 0;
+		if ( !paused )
+			restartTimer();
+	}
+
+	int numFrames() const
+	{
+		return frames.count();
+	}
+
+	void restartTimer()
+	{
+		if ( !paused && speed > 0 ) {
+			frameperiod = frames[frame]->period;
+			int i = frameperiod >= 0 ? frameperiod * 100/speed : 0;
+			if ( i != lasttimerinterval || !frametimer->isActive() ) {
+				lasttimerinterval = i;
+				frametimer->start( i );
+			}
+		} else {
+			frametimer->stop();
+		}
+	}
+
+	// QImageConsumer
+	void end()
+	{
+	}
+
+	void changed(const QRect &)
+	{
+	}
+
+	void frameDone()
+	{
+		frames.resize ( frames.count()+1 );
+		frames.insert ( frames.count(), new Frame(new Impix( decoder->image() ), frameperiod) );
+	}
+
+	void frameDone(const QPoint &, const QRect &)
+	{
+		frameDone();
+	}
+
+	void setLooping(int l)
+	{
+		looping = l;
+	}
+
+	void setFramePeriod(int period)
+	{
+		frameperiod = period;
+	}
+
+	void setSize(int, int)
+	{
+	}
+
+signals:
+	void areaChanged();
+
+public slots:
+	void refresh()
+	{
+		frame++;
+		if ( frame >= numFrames() ) {
+			frame = 0;
+
+			loop++;
+			if ( looping && loop >= looping ) {
+				frame = numFrames() - 1;
+				pause();
+				restart();
+			}
+		}
+
+		emit areaChanged();
+		restartTimer();
+	}
+};
+//! \endif
+
+//!
+//! Creates an empty animation.
+Anim::Anim()
+{
+	d = new Private();
+}
+
+//!
+//! Creates animation from QByteArray \a data.
+Anim::Anim(const QByteArray &data)
+{
+	d = new Private(data);
+}
+
+//!
+//! Creates shared copy of Anim \a anim.
+Anim::Anim(const Anim &anim)
+{
+	d = anim.d;
+	d->ref();
+}
+
+//!
+//! Deletes animation.
+Anim::~Anim()
+{
+	if ( d->deref() )
+		delete d;
+}
+
+//!
+//! Returns QPixmap of current frame.
+const QPixmap &Anim::framePixmap() const
+{
+	return d->frames[d->frame]->impix->pixmap();
+}
+
+//!
+//! Returns QImage of current frame.
+const QImage &Anim::frameImage() const
+{
+	return d->frames[d->frame]->impix->image();
+}
+
+//!
+//! Returns Impix of current frame.
+const Impix &Anim::frameImpix() const
+{
+	return *d->frames[d->frame]->impix;
+}
+
+//!
+//! Returns total number of frames in animation.
+int Anim::numFrames() const
+{
+	return d->frames.count();
+}
+
+//!
+//! Returns the number of current animation frame.
+int Anim::framenumber() const
+{
+	return d->frame;
+}
+
+//!
+//! Returns Impix of animation frame number \a n.
+const Impix &Anim::frame(int n) const
+{
+	return *d->frames[n]->impix;
+}
+
+//!
+//! Returns \c true if numFrames() == 0 and \c false otherwise.
+bool Anim::isNull() const
+{
+	return !numFrames();
+}
+
+//!
+//! Returns \c true when animation is paused.
+bool Anim::paused() const
+{
+	return d->paused;
+}
+
+//!
+//! Continues the animation playback.
+void Anim::unpause()
+{
+	if ( !isNull() && paused() )
+		d->unpause();
+}
+
+//!
+//! Pauses the animation.
+void Anim::pause()
+{
+	if ( !isNull() && !paused() )
+		d->pause();
+}
+
+//!
+//! Starts animation from the very beginning.
+void Anim::restart()
+{
+	if ( !isNull() )
+		d->restart();
+}
+
+/*!
+	Connects internal signal with specified slot \a member of object \a receiver, which
+	is emitted when animation changes its frame.
+
+	\code
+ class MyObject : public QObject {
+ 	Q_OBJECT
+ 	// ...
+ public slots:
+ 	void animUpdated();
+	// ...
+	void foo (Anim *anim) {
+		anim->connectUpdate( this, SLOT(animUpdated()) );
+	}
+ }
+	\endcode
+
+	\sa disconnectUpdate()
+*/
+void Anim::connectUpdate(QObject *receiver, const char *member)
+{
+	QObject::connect(d, SIGNAL(areaChanged()), receiver, member);
+}
+
+//!
+//! Disconnects slot \a member, which was prevously connected with connectUpdate().
+//! \sa connectUpdate()
+void Anim::disconnectUpdate(QObject *receiver, const char *member)
+{
+	QObject::disconnect(d, SIGNAL(areaChanged()), receiver, member);
+}
+
+Anim Anim::copy() const
+{
+	Anim anim( *this );
+	anim.d->deref();
+	anim.d = new Private( *this->d );
+
+	return anim;
+}
+
+void Anim::detach()
+{
+	if ( d->count > 1 )
+		*this = copy();
+}
+
+//!
+//! Strips the first animation frame, if there is more than one frame.
+void Anim::stripFirstFrame()
+{
+	if ( numFrames() > 1 ) {
+		QPtrVector<Private::Frame> frames;
+		for ( uint i = 1; i < d->frames.count(); i++ ) {
+			frames.resize ( frames.count()+1 );
+			frames.insert ( frames.count(), new Private::Frame( *d->frames[i] ) );
+		}
+
+		d->frames.clear();
+		d->frames = frames;
+
+		if ( !paused() )
+			restart();
+	}
+}
+
+#include "anim.moc"
Index: psi/tags/0.10-os2-b1/libpsi/iconset/anim.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/iconset/anim.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/iconset/anim.h	(revision 38)
@@ -0,0 +1,67 @@
+/*
+ * anim.h - class for handling animations
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ANIM_H
+#define ANIM_H
+
+#include <qcstring.h>
+
+class QObject;
+class QPixmap;
+class QImage;
+class Impix;
+
+class Anim
+{
+public:
+	Anim();
+	Anim(const QByteArray &data);
+	Anim(const Anim &anim);
+	~Anim();
+
+	const QPixmap &framePixmap() const;
+	const QImage &frameImage() const;
+	const Impix &frameImpix() const;
+	bool isNull() const;
+
+	int framenumber() const;
+	int numFrames() const;
+	const Impix &frame(int n) const;
+
+	bool paused() const;
+	void unpause();
+	void pause();
+
+	void restart();
+
+	void stripFirstFrame();
+
+	void connectUpdate(QObject *receiver, const char *member);
+	void disconnectUpdate(QObject *receiver, const char *member = 0);
+
+	Anim copy() const;
+	void detach();
+
+	class Private;
+private:
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/iconset/iconset.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/iconset/iconset.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/iconset/iconset.cpp	(revision 38)
@@ -0,0 +1,1582 @@
+/*
+ * iconset.cpp - various graphics handling classes
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "iconset.h"
+#include "zip.h"
+
+#include <qobject.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qtimer.h>
+
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qiconset.h>
+#include <qregexp.h>
+#include <qmime.h>
+#include <qdom.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qcstring.h>
+#include <qptrlist.h>
+
+#include "anim.h"
+
+// sound support
+#define ICONSET_SOUND
+#ifdef ICONSET_SOUND
+#	include <qdatastream.h>
+#	include <qapplication.h>
+#	include <qca.h>
+#endif
+
+#include<qapplication.h>
+
+//----------------------------------------------------------------------------
+// Impix
+//----------------------------------------------------------------------------
+
+//! \class Impix iconset.h
+//! \brief Combines a QPixmap and QImage into one class
+//!
+//!  Normally, it is common to use QPixmap for all application graphics.
+//!  However, sometimes it is necessary to access pixel data, which means
+//!  a time-costly conversion to QImage.  Impix does this conversion on
+//!  construction, and keeps a copy of both a QPixmap and QImage for fast
+//!  access to each.  What you gain in speed you pay in memory, as an Impix
+//!  should occupy twice the space.
+//!
+//!  An Impix can be conveniently created from either a QImage or QPixmap
+//!  source, and can be casted back to either type.
+//!
+//!  You may also call unload() to free the image data.
+//!
+//!  \code
+//!  Impix i = QImage("icon.png");
+//!  QLabel *iconLabel;
+//!  iconLabel->setPixmap(i.pixmap());
+//!  QImage image = i.image();
+//!  \endcode
+
+//static int impix_p_count = 0; // DEBUG
+//static int impix_count = 0;
+
+//! \if _hide_doc_
+class Impix::Private : public QShared
+{
+public:
+	Private()
+	{
+		//impix_p_count++; // DEBUG
+
+		pixmap = 0;
+		image = 0;
+	}
+
+	~Private()
+	{
+		unload();
+
+		//qWarning("impix_p_count = %d", --impix_p_count); // DEBUG
+	}
+
+	Private(const Private &from)
+	: QShared()
+	{
+		//impix_p_count++; // DEBUG
+
+		pixmap = from.pixmap ? new QPixmap(*from.pixmap) : 0;
+		image  = from.image  ? new QImage (*from.image)  : 0;
+	}
+
+	void unload()
+	{
+		if ( pixmap )
+			delete pixmap;
+		pixmap = 0;
+		if ( image )
+			delete image;
+		image = 0;
+	}
+
+	QPixmap *pixmap;
+	QImage *image;
+};
+//! \endif
+
+//! \brief Construct a null Impix
+//!
+//! Constructs an Impix without any image data.
+Impix::Impix()
+{
+	//impix_count++;
+
+	d = new Private;
+}
+
+//! \brief Construct an Impix based on a QPixmap
+//!
+//! Constructs an Impix by making a copy of a QPixmap and creating a QImage from it.
+//! \param from - source QPixmap
+Impix::Impix(const QPixmap &from)
+{
+	//impix_count++;
+
+	d = new Private;
+	*this = from;
+}
+
+//! \brief Construct an Impix based on a QImage
+//!
+//! Constructs an Impix by making a copy of a QImage and creating a QPixmap from it.
+//! \param from - source QImage
+Impix::Impix(const QImage &from)
+{
+	//impix_count++;
+
+	d = new Private;
+	*this = from;
+}
+
+//!
+//! \brief Copy constructor
+Impix::Impix(const Impix &from)
+{
+	//impix_count++;
+
+	d = from.d;
+	d->ref();
+}
+
+//!
+//! Sets the Impix to \a from
+Impix & Impix::operator=(const Impix &from)
+{
+	if ( d->deref() )
+		delete d;
+
+	d = from.d;
+	d->ref();
+
+	return *this;
+}
+
+//!
+//! Unloads image data, making it null.
+void Impix::unload()
+{
+	if ( isNull() )
+		return;
+
+	detach();
+	d->unload();
+}
+
+//!
+//! Destroys the image.
+Impix::~Impix()
+{
+	//qWarning("impix_count = %d", --impix_count);
+
+	if ( d->deref() )
+		delete d;
+}
+
+bool Impix::isNull() const
+{
+	return d->image || d->pixmap ? false: true;
+}
+
+const QPixmap & Impix::pixmap() const
+{
+	if(!d->pixmap)
+		d->pixmap = new QPixmap;
+	return *d->pixmap;
+}
+
+const QImage & Impix::image() const
+{
+	if(!d->image)
+		d->image = new QImage;
+	return *d->image;
+}
+
+void Impix::setPixmap(const QPixmap &x)
+{
+	detach();
+
+	d->unload();
+	d->pixmap = new QPixmap(x);
+	d->image = new QImage(x.convertToImage());
+}
+
+void Impix::setImage(const QImage &x)
+{
+	detach();
+
+	d->unload();
+	d->pixmap = new QPixmap;
+	d->pixmap->convertFromImage(x);
+	d->image = new QImage(x);
+}
+
+bool Impix::loadFromData(const QByteArray &ba)
+{
+	qApp->lock();
+
+	bool ret = false;
+	QImage img;
+	if ( img.loadFromData( ba ) ) {
+		setImage ( img );
+		ret = true;
+	}
+
+	qApp->unlock();
+
+	return ret;
+}
+
+Impix Impix::copy() const
+{
+	Impix impix;
+	delete impix.d;
+	impix.d = new Private( *this->d );
+
+	return impix;
+}
+
+void Impix::detach()
+{
+	if ( d->count > 1 )
+		*this = copy();
+}
+
+//----------------------------------------------------------------------------
+// IconSharedObject
+//----------------------------------------------------------------------------
+
+class IconSharedObject : public QObject
+{
+	Q_OBJECT
+public:
+	IconSharedObject()
+#ifdef ICONSET_SOUND
+	: QObject(qApp, "IconSharedObject")
+#endif
+	{ }
+
+	QString unpackPath;
+
+signals:
+	void playSound(QString);
+
+private:
+	friend class Icon;
+};
+
+static IconSharedObject *iconSharedObject = 0;
+
+//----------------------------------------------------------------------------
+// Icon
+//----------------------------------------------------------------------------
+
+/*!
+	\class Icon
+	\brief Can contain Anim and stuff
+
+	This class can be used for storing application icons as well as emoticons
+	(it has special functions in order to do that).
+
+	Icons can contain animation, associated sound files, its own names.
+
+	For implementing emoticon functionality, Icon can have associated text
+	values and QRegExp for easy searching.
+*/
+
+//! \if _hide_doc_
+class Icon::Private : public QObject, public QShared
+{
+	Q_OBJECT
+public:
+	Private()
+	{
+		anim = 0;
+		iconSet = 0;
+		activatedCount = 0;
+		text.setAutoDelete(true);
+	}
+
+	~Private()
+	{
+		unloadAnim();
+		if ( iconSet )
+			delete iconSet;
+	}
+
+	Private(const Private &from)
+	: QObject(), QShared()
+	{
+		name = from.name;
+		regExp = from.regExp;
+		text = from.text;
+		sound = from.sound;
+		impix = from.impix;
+		anim = from.anim ? new Anim ( *from.anim ) : 0;
+		iconSet = 0;
+	}
+
+	void unloadAnim()
+	{
+		if ( anim )
+			delete anim;
+		anim = 0;
+	}
+
+signals:
+	void pixmapChanged(const QPixmap &);
+	void iconModified(const QPixmap &);
+
+public:
+	const QPixmap &pixmap() const
+	{
+		if ( anim )
+			return anim->framePixmap();
+		return impix.pixmap();
+	}
+
+public slots:
+	void animUpdate() { emit pixmapChanged( pixmap() ); }
+
+public:
+	QString name;
+	QRegExp regExp;
+	QDict<QString> text;
+	QString sound;
+
+	Impix impix;
+	Anim *anim;
+	QIconSet *iconSet;
+
+	int activatedCount;
+	friend class Icon;
+};
+//! \endif
+
+//static int icon_count = 0;
+
+//!
+//! Constructs empty Icon.
+Icon::Icon()
+: QObject(0, 0)
+{
+	//icon_count++;
+
+	d = new Private;
+
+	connect(d, SIGNAL(pixmapChanged(const QPixmap &)), SIGNAL(pixmapChanged(const QPixmap &)));
+	connect(d, SIGNAL(iconModified(const QPixmap &)), SIGNAL(iconModified(const QPixmap &)));
+}
+
+//!
+//! Destroys Icon.
+Icon::~Icon()
+{
+	//qWarning("icon_count = %d", --icon_count);
+
+	if ( d->deref() )
+		delete d;
+}
+
+//!
+//! Creates new icon, that is a copy of \a from. Note, that if one icon will be changed,
+//! other will be changed as well. (that's because image data is shared)
+Icon::Icon(const Icon &from)
+: QObject(0, 0)
+{
+	//icon_count++;
+
+	d = from.d;
+	d->ref();
+
+	connect(d, SIGNAL(pixmapChanged(const QPixmap &)), SIGNAL(pixmapChanged(const QPixmap &)));
+	connect(d, SIGNAL(iconModified(const QPixmap &)), SIGNAL(iconModified(const QPixmap &)));
+}
+
+//!
+//! Creates new icon, that is a copy of \a from. Note, that if one icon will be changed,
+//! other will be changed as well. (that's because image data is shared)
+Icon & Icon::operator= (const Icon &from)
+{
+	if ( d->deref() )
+		delete d;
+
+	d = from.d;
+	d->ref();
+
+	connect(d, SIGNAL(pixmapChanged(const QPixmap &)), SIGNAL(pixmapChanged(const QPixmap &)));
+	connect(d, SIGNAL(iconModified(const QPixmap &)), SIGNAL(iconModified(const QPixmap &)));
+
+	return *this;
+}
+
+Icon Icon::copy() const
+{
+	Icon icon;
+	delete icon.d;
+	icon.d = new Private( *this->d );
+
+	connect(icon.d, SIGNAL(pixmapChanged(const QPixmap &)), &icon, SIGNAL(pixmapChanged(const QPixmap &)));
+	connect(icon.d, SIGNAL(iconModified(const QPixmap &)),  &icon, SIGNAL(iconModified(const QPixmap &)));
+
+	return icon;
+}
+
+void Icon::detach()
+{
+	if ( d->count != 1 ) // only if >1 reference
+		*this = copy();
+}
+
+//!
+//! Returns \c true when icon contains animation.
+bool Icon::isAnimated() const
+{
+	return d->anim != 0;
+}
+
+//!
+//! Returns QPixmap of current frame.
+const QPixmap &Icon::pixmap() const
+{
+	return d->pixmap();
+}
+
+//!
+//! Returns QImage of current frame.
+const QImage &Icon::image() const
+{
+	if ( d->anim )
+		return d->anim->frameImage();
+	return d->impix.image();
+}
+
+//!
+//! Returns Impix of first animation frame.
+//! \sa setImpix()
+const Impix &Icon::impix() const
+{
+	return d->impix;
+}
+
+//!
+//! Returns Impix of current animation frame.
+//! \sa impix()
+const Impix &Icon::frameImpix() const
+{
+	if ( d->anim )
+		return d->anim->frameImpix();
+	return d->impix;
+}
+
+//!
+//! Returns QIconSet of first animation frame.
+//! TODO: Add automatic greyscale icon generation.
+const QIconSet &Icon::iconSet() const
+{
+	if ( d->iconSet )
+		return *d->iconSet;
+
+	d->iconSet = new QIconSet( d->impix.pixmap() );
+	return *d->iconSet;
+}
+
+//!
+//! Sets the Icon impix to \a impix.
+//! \sa impix()
+void Icon::setImpix(const Impix &impix, bool doDetach)
+{
+	if ( doDetach )
+		detach();
+
+	d->impix = impix;
+	emit d->pixmapChanged( pixmap() );
+	emit d->iconModified( d->impix.pixmap() );
+}
+
+//!
+//! Returns pointer to Anim object, or \a 0 if Icon doesn't contain an animation.
+const Anim *Icon::anim() const
+{
+	return d->anim;
+}
+
+//!
+//! Sets the animation for icon to \a anim. Also sets Impix to be the first frame of animation.
+//! If animation have less than two frames, it is deleted.
+//! \sa anim()
+void Icon::setAnim(const Anim &anim, bool doDetach)
+{
+	if ( doDetach )
+		detach();
+
+	d->unloadAnim();
+	d->anim = new Anim(anim);
+
+	if ( d->anim->numFrames() > 0 )
+		setImpix( d->anim->frame(0) );
+
+	if ( d->anim->numFrames() < 2 ) {
+		delete d->anim;
+		d->anim = 0;
+	}
+
+	if ( d->anim && d->activatedCount > 0 ) {
+		d->activatedCount = 0;
+		activated(false); // restart the animation, but don't play the sound
+	}
+
+	emit d->pixmapChanged( pixmap() );
+	emit d->iconModified( d->impix.pixmap() );
+}
+
+//!
+//! Removes animation from icon.
+//! \sa setAnim()
+void Icon::removeAnim(bool doDetach)
+{
+	if ( doDetach )
+		detach();
+
+	if ( !d->anim )
+		return;
+
+	d->activatedCount = 0;
+	stop();
+
+	delete d->anim;
+	d->anim = 0;
+
+	emit d->pixmapChanged( pixmap() );
+	//emit d->iconModified( d->impix.pixmap() );
+}
+
+//!
+//! Returns the number of current animation frame.
+//! \sa setAnim()
+int Icon::framenumber() const
+{
+	if ( d->anim )
+		return d->anim->framenumber();
+
+	return 0;
+}
+
+//!
+//! Returns name of the Icon.
+//! \sa setName()
+const QString &Icon::name() const
+{
+	return d->name;
+}
+
+//!
+//! Sets the Icon name to \a name
+//! \sa name()
+void Icon::setName(const QString &name)
+{
+	detach();
+
+	d->name = name;
+}
+
+void Icon::setName(const char *s)
+{
+	QObject::setName(s);
+}
+
+//!
+//! Returns Icon's QRegExp. It is used to store information for emoticons.
+//! \sa setRegExp()
+const QRegExp &Icon::regExp() const
+{
+	return d->regExp;
+}
+
+//!
+//! Sets the Icon QRegExp to \a regExp.
+//! \sa regExp()
+//! \sa text()
+void Icon::setRegExp(const QRegExp &regExp)
+{
+	detach();
+
+	d->regExp = regExp;
+}
+
+//!
+//! Returns Icon's text. It is used to store information for emoticons.
+//! \sa setText()
+//! \sa regExp()
+const QDict<QString> &Icon::text() const
+{
+	return d->text;
+}
+
+//!
+//! Sets the Icon text to \a t.
+//! \sa text()
+void Icon::setText(const QDict<QString> &t)
+{
+	detach();
+
+	d->text = t;
+}
+
+//!
+//! Returns file name of associated sound.
+//! \sa setSound()
+//! \sa activated()
+const QString &Icon::sound() const
+{
+	return d->sound;
+}
+
+//!
+//! Sets the sound file name to be associated with this Icon.
+//! \sa sound()
+//! \sa activated()
+void Icon::setSound(const QString &sound)
+{
+	detach();
+
+	d->sound = sound;
+}
+
+//!
+//! Initializes Icon's Impix (or Anim, if \a isAnim equals \c true).
+//! Iconset::load uses this function.
+bool Icon::loadFromData(const QByteArray &ba, bool isAnim)
+{
+	detach();
+
+	bool ret = false;
+	if ( isAnim ) {
+		Anim *anim = new Anim(ba);
+		setAnim(*anim);
+		ret = anim->numFrames();
+		delete anim; // shared data rules ;)
+	}
+
+	if ( !ret && d->impix.loadFromData(ba) )
+		ret = true;
+
+	if ( ret ) {
+		emit d->pixmapChanged( pixmap() );
+		emit d->iconModified( d->impix.pixmap() );
+	}
+
+	return ret;
+}
+
+//!
+//! You need to call this function, when Icon is \e triggered, i.e. it is shown on screen
+//! and it must start animation (if it has not animation, this function will do nothing).
+//! When icon is no longer shown on screen you MUST call stop().
+//! NOTE: For EACH activated() function call there must be associated stop() call, or the
+//! animation will go crazy. You've been warned.
+//! If \a playSound equals \c true, Icon will play associated sound file.
+//! \sa stop()
+void Icon::activated(bool playSound)
+{
+	d->activatedCount++;
+	//qWarning("%-25s Icon::activated(): count = %d", name().latin1(), d->activatedCount);
+
+#ifdef ICONSET_SOUND
+	if ( playSound && !d->sound.isNull() ) {
+		if ( !iconSharedObject )
+			iconSharedObject = new IconSharedObject();
+
+		emit iconSharedObject->playSound(d->sound);
+	}
+#endif
+
+	if ( d->anim ) {
+		d->anim->unpause();
+
+		d->anim->disconnectUpdate (d, SLOT(animUpdate())); // ensure, that we're connected to signal exactly one time
+		d->anim->connectUpdate (d, SLOT(animUpdate()));
+	}
+}
+
+//!
+//! You need to call this function when Icon is no more shown on screen. It would save
+//! processor time, if Icon has animation.
+//! NOTE: For EACH activated() function call there must be associated stop() call, or the
+//! animation will go crazy. You've been warned.
+//! \sa activated()
+void Icon::stop()
+{
+	d->activatedCount--;
+	//qWarning("%-25s Icon::stop(): count = %d", name().latin1(), d->activatedCount);
+
+	if ( d->activatedCount <= 0 ) {
+		d->activatedCount = 0;
+		if ( d->anim ) {
+			d->anim->pause();
+			d->anim->restart();
+		}
+	}
+}
+
+//!
+//! As the name says, this function removes the first animation frame. This is used to
+//! create system Psi iconsets.
+void Icon::stripFirstAnimFrame()
+{
+	if ( d && d->anim )
+		d->anim->stripFirstFrame();
+}
+
+//----------------------------------------------------------------------------
+// IconsetFactory
+//----------------------------------------------------------------------------
+
+/*!
+	\class IconsetFactory
+	\brief Class for easy application-wide Icon searching
+
+	You can add several Iconsets to IconsetFactory to use multiple Icons
+	application-wide with ease.
+
+	You can reference Icons by their name from any place from your application.
+*/
+
+//! \if _hide_doc_
+class IconsetFactoryPrivate
+{
+private:
+	static QPtrList<Iconset> *iconsets;
+	static QPixmap *emptyPixmap;
+
+public:
+	static void registerIconset(const Iconset *);
+	static void unregisterIconset(const Iconset *);
+
+public:
+	static const Icon *icon(const QString &name);
+
+	friend class IconsetFactory;
+};
+//! \endif
+
+QPtrList<Iconset> *IconsetFactoryPrivate::iconsets = 0;
+QPixmap *IconsetFactoryPrivate::emptyPixmap = 0;
+
+void IconsetFactoryPrivate::registerIconset(const Iconset *i)
+{
+	if ( !iconsets )
+		iconsets = new QPtrList<Iconset>;
+
+	if ( iconsets->find(i) < 0 ) {
+		//qWarning("IconsetFactoryPrivate::registerIconset(): %s", i->fileName().latin1());
+		iconsets->append ( i );
+	}
+}
+
+void IconsetFactoryPrivate::unregisterIconset(const Iconset *i)
+{
+	if ( iconsets && iconsets->find(i) >= 0 ) {
+		//qWarning("IconsetFactoryPrivate::unregisterIconset(): %s", i->fileName().latin1());
+		iconsets->remove ( i );
+	}
+
+	if ( !iconsets || !iconsets->count() ) {
+		if ( iconsets ) {
+			delete iconsets;
+			iconsets = 0;
+		}
+
+		if ( emptyPixmap ) {
+			delete emptyPixmap;
+			emptyPixmap = 0;
+		}
+	}
+}
+
+const Icon *IconsetFactoryPrivate::icon(const QString &name)
+{
+	if ( !iconsets )
+		return 0;
+
+	const Icon *i = 0;
+	QPtrListIterator<Iconset> it( *iconsets );
+	for (; it.current(); ++it) {
+		Iconset *iconset = *it;
+		if ( iconset )
+			i = iconset->icon(name);
+
+		if ( i )
+			break;
+	}
+	return i;
+}
+
+//!
+//! Returns pointer to Icon with name \a name, or \a 0 if Icon with that name wasn't
+//! found in IconsetFactory.
+const Icon *IconsetFactory::iconPtr(const QString &name)
+{
+	const Icon *i = IconsetFactoryPrivate::icon(name);
+	if ( !i ) {
+		qDebug("WARNING: IconsetFactory::icon(\"%s\"): icon not found", name.latin1());
+	}
+	return i;
+}
+
+//!
+//! Returns Icon with name \a name, or empty Icon if Icon with that name wasn't
+//! found in IconsetFactory.
+Icon IconsetFactory::icon(const QString &name)
+{
+	const Icon *i = iconPtr(name);
+	if ( i )
+		return *i;
+	return Icon();
+}
+
+//!
+//! Returns QPixmap of first animation frame of the specified Icon, or the empty
+//! QPixmap, if that Icon wasn't found in IconsetFactory.
+//! This function is faster than the call to IconsetFactory::icon() and cast to QPixmap,
+//! because the intermediate Icon object is not created and destroyed.
+const QPixmap &IconsetFactory::iconPixmap(const QString &name)
+{
+	const Icon *i = iconPtr(name);
+	if ( i )
+		return i->impix().pixmap();
+
+	if ( !IconsetFactoryPrivate::emptyPixmap )
+		IconsetFactoryPrivate::emptyPixmap = new QPixmap();
+	return *IconsetFactoryPrivate::emptyPixmap;
+}
+
+//!
+//! Returns list of all Icon names that are in IconsetFactory.
+const QStringList IconsetFactory::icons()
+{
+	QStringList list;
+
+	QPtrList<Iconset> *iconsets = IconsetFactoryPrivate::iconsets;
+	uint count = 0;
+	if ( iconsets )
+		count = iconsets->count();
+	for (uint i = 0; i < count; i++) {
+		QPtrListIterator<Icon> it = iconsets->at(i)->iterator();
+		for ( ; it.current(); ++it)
+			list << (*it)->name();
+	}
+
+	return list;
+
+}
+
+//----------------------------------------------------------------------------
+// Iconset
+//----------------------------------------------------------------------------
+
+/*!
+	\class Iconset
+	\brief Class for easy Icon grouping
+
+	This class supports loading Icons from .zip arhives. It also provides additional
+	information: name(), authors(), version(), description() and creation() date.
+
+	This class is very handy to manage emoticon sets as well as usual application icons.
+
+	\sa IconsetFactory
+*/
+
+//! \if _hide_doc_
+class Iconset::Private : public QShared
+{
+private:
+	void init()
+	{
+		name = "Unnamed";
+		//version = "1.0";
+		//description = "No description";
+		//authors << "I. M. Anonymous";
+		//creation = "1900-01-01";
+		homeUrl = QString::null;
+
+		dict.setAutoDelete(true);
+	}
+
+public:
+	QString name, version, description, creation, homeUrl, filename;
+	QStringList authors;
+	QDict<Icon> dict;
+	QPtrList<Icon> list;
+	QDict<QString> info;
+	//Icon nullIcon;
+
+public:
+	Private()
+	{
+		init();
+	}
+
+	Private(const Private &from)
+	: QShared()
+	{
+		init();
+
+		setInformation(from);
+
+		QDictIterator<Icon> it( from.dict );
+		for ( ; it.current(); ++it)
+			dict.insert(it.currentKey(), new Icon(*it.current()));
+
+		QPtrListIterator<Icon> it2( from.list );
+		for ( ; it2.current(); ++it2)
+			list.append(new Icon(*it2.current()));
+	}
+
+	void append(QString n, Icon *icon)
+	{
+		// all Icon names in Iconset must be unique
+		if ( !dict.find(n) ) {
+			dict.replace (n, icon);
+			list.append (icon);
+		}
+	}
+
+	QByteArray loadData(const QString &fileName, const QString &dir)
+	{
+		QByteArray ba;
+
+		QFileInfo fi(dir);
+		if ( fi.isDir() ) {
+			QFile file ( dir + "/" + fileName );
+			file.open (IO_ReadOnly);
+
+			ba = file.readAll();
+		}
+		else if ( fi.extension(false) == "jisp" || fi.extension(false) == "zip" ) {
+			UnZip z(dir);
+			if ( !z.open() )
+				return ba;
+
+			QString n = fi.baseName(true) + "/" + fileName;
+			if ( !z.readFile(n, &ba) ) {
+				n = "/" + fileName;
+				z.readFile(n, &ba);
+			}
+		}
+
+		return ba;
+	}
+
+	void loadMeta(const QDomElement &i, const QString &dir)
+	{
+		Q_UNUSED(dir);
+
+		for (QDomNode node = i.firstChild(); !node.isNull(); node = node.nextSibling()) {
+			QDomElement e = node.toElement();
+			if( e.isNull() )
+				continue;
+
+			QString tag = e.tagName();
+			if ( tag == "name" ) {
+				name = e.text();
+			}
+			else if ( tag == "version" ) {
+				version = e.text();
+			}
+			else if ( tag == "description" ) {
+				description = e.text();
+			}
+			else if ( tag == "author" ) {
+				QString n = e.text();
+				QString tmp = "<br>&nbsp;&nbsp;";
+				if ( !e.attribute("email").isEmpty() ) {
+					QString s = e.attribute("email");
+					n += tmp + QString("Email: <a href='mailto:%1'>%2</a>").arg( s ).arg( s );
+				}
+				if ( !e.attribute("jid").isEmpty() ) {
+					QString s = e.attribute("jid");
+					n += tmp + QString("JID: <a href='jabber:%1'>%2</a>").arg( s ).arg( s );
+				}
+				if ( !e.attribute("www").isEmpty() ) {
+					QString s = e.attribute("www");
+					n += tmp + QString("WWW: <a href='%1'>%2</a>").arg( s ).arg( s );
+				}
+				authors += n;
+			}
+			else if ( tag == "creation" ) {
+				creation = e.text();
+			}
+			else if ( tag == "home" ) {
+				homeUrl = e.text();
+			}
+		}
+	}
+
+	static int icon_counter; // used to give unique names to icons
+
+	// will return 'true' when icon is loaded ok
+	bool loadIcon(const QDomElement &i, const QString &dir)
+	{
+		Icon icon;
+
+		QDict<QString> text, graphic, sound, object;
+		graphic.setAutoDelete(true);
+		sound.setAutoDelete(true);
+		object.setAutoDelete(true);
+
+		QString name;
+		name.sprintf("icon_%04d", icon_counter++);
+		bool isAnimated = false;
+		bool isImage = false;
+
+		for(QDomNode n = i.firstChild(); !n.isNull(); n = n.nextSibling()) {
+			QDomElement e = n.toElement();
+			if ( e.isNull() )
+				continue;
+
+			QString tag = e.tagName();
+			if ( tag == "text" ) {
+				QString lang = e.attribute("xml:lang");
+				if ( lang.isEmpty() )
+					lang = ""; // otherwise there would be many warnings :-(
+				text.insert( lang, new QString(e.text()));
+			}
+			else if ( tag == "object" ) {
+				object.insert( e.attribute("mime"), new QString(e.text()));
+			}
+			else if ( tag == "x" ) {
+				QString attr = e.attribute("xmlns");
+				if ( attr == "name" ) {
+					name = e.text();
+				}
+				else if ( attr == "type" ) {
+					if ( e.text() == "animation" )
+						isAnimated = true;
+					else if ( e.text() == "image" )
+						isImage = true;
+				}
+			}
+			// leaved for compatibility with old JEP
+			else if ( tag == "graphic" ) {
+				graphic.insert( e.attribute("mime"), new QString(e.text()));
+			}
+			else if ( tag == "sound" ) {
+				sound.insert( e.attribute("mime"), new QString(e.text()));
+			}
+		}
+
+		icon.setText (text);
+		icon.setName (name);
+
+		QStringList graphicMime, soundMime, animationMime;
+		graphicMime << "image/png"; // first item have higher priority than latter
+		graphicMime << "video/x-mng";
+		graphicMime << "image/gif";
+		graphicMime << "image/x-xpm";
+		graphicMime << "image/bmp";
+		graphicMime << "image/jpeg";
+		// TODO: untested
+		graphicMime << "image/svg+xml";
+
+		soundMime << "audio/x-wav";
+		soundMime << "audio/x-ogg";
+		soundMime << "audio/x-mp3";
+		soundMime << "audio/x-midi";
+
+		// MIME-types, that support animations
+		animationMime << "image/gif";
+		//animationMime << "image/png";
+		animationMime << "image/x-mng";
+
+		if ( !object.isEmpty() ) {
+			// fill the graphic & sound tables, if there are some
+			// 'object' entries. inspect the supported mimetypes
+			// and copy mime info and file path to 'graphic' and
+			// 'sound' dictonaries.
+
+			QStringList::Iterator it = graphicMime.begin();
+			for ( ; it != graphicMime.end(); ++it)
+				if ( object[*it] && !object[*it]->isNull() )
+					graphic.insert( *it, new QString(*object[*it]));
+
+			it = soundMime.begin();
+			for ( ; it != soundMime.end(); ++it)
+				if ( object[*it] && !object[*it]->isNull() )
+					sound.insert( *it, new QString(*object[*it]));
+		}
+
+		bool loadSuccess = false;
+
+		{
+			QStringList::Iterator it = graphicMime.begin();
+			for ( ; it != graphicMime.end(); ++it) {
+				if ( graphic[*it] && !graphic[*it]->isNull() ) {
+					// if format supports animations, then load graphic as animation, and
+					// if there is only one frame, then later it would be converted to single Impix
+					if ( !isAnimated && !isImage ) {
+						QStringList::Iterator it2 = animationMime.begin();
+						for ( ; it2 != animationMime.end(); ++it2)
+							if ( *it == *it2 ) {
+								isAnimated = true;
+								break;
+							}
+					}
+
+					if ( icon.loadFromData( loadData(*graphic[*it], dir), isAnimated ) ) {
+						loadSuccess = true;
+						break;
+					}
+					else {
+						qDebug("Iconset::load(): Couldn't load %s graphic for the %s icon for the %s iconset", (*it).latin1(), name.latin1(), this->name.latin1());
+						loadSuccess = false;
+					}
+				}
+			}
+		}
+
+		{
+			QFileInfo fi(dir);
+			QStringList::Iterator it = soundMime.begin();
+			for ( ; it != soundMime.end(); ++it) {
+				if ( sound[*it] && !sound[*it]->isNull() ) {
+					if ( !fi.isDir() ) { // it is a .zip file then
+#ifdef ICONSET_SOUND
+						if ( !iconSharedObject )
+							iconSharedObject = new IconSharedObject();
+
+						QString path = iconSharedObject->unpackPath;
+						if ( path.isEmpty() )
+							break;
+
+						QFileInfo ext(*sound[*it]);
+						path += "/" + QCA::SHA1::hashToString(QString(fi.absFilePath() + "/" + *sound[*it]).utf8()) + "." + ext.extension();
+
+						QFile file ( path );
+						file.open ( IO_WriteOnly );
+						QDataStream out ( &file );
+
+						QByteArray data = loadData(*sound[*it], dir);
+						out.writeRawBytes (data, data.size());
+
+						icon.setSound ( path );
+						break;
+#endif
+					}
+					else {
+						icon.setSound ( fi.absFilePath() + "/" + *sound[*it] );
+						break;
+					}
+				}
+			}
+		}
+
+		if ( text.count() )
+		{	// construct RegExp
+			QString regexp;
+			QDictIterator<QString> it( text );
+			for ( ; it.current(); ++it ) {
+				if ( !regexp.isEmpty() )
+					regexp += '|';
+
+				regexp += QRegExp::escape(**it);
+			}
+
+			// make sure there is some form of whitespace on at least one side of the text string
+			//regexp = QString("(\\b(%1))|((%2)\\b)").arg(regexp).arg(regexp);
+			icon.setRegExp ( regexp );
+		}
+
+		if ( loadSuccess )
+			append( name, new Icon(icon) );
+
+		return loadSuccess;
+	}
+
+	// would return 'true' on success
+	bool load(const QDomDocument &doc, const QString dir)
+	{
+		QDomElement base = doc.documentElement();
+		if ( base.tagName() != "icondef" )
+			return false;
+
+		bool success = true;
+
+		for(QDomNode node = base.firstChild(); !node.isNull(); node = node.nextSibling()) {
+			QDomElement i = node.toElement();
+			if( i.isNull() )
+				continue;
+
+			QString tag = i.tagName();
+			if ( tag == "meta" ) {
+				loadMeta (i, dir);
+			}
+			else if ( tag == "icon" ) {
+				bool ret = loadIcon (i, dir);
+				if ( !ret )
+					success = false;
+			}
+			else if ( tag == "x" ) {
+				info.insert( i.attribute("xmlns"), new QString(i.text()) );
+			}
+		}
+
+		return success;
+	}
+	
+	void setInformation(const Private &from) {
+		name = from.name;
+		version = from.version;
+		description = from.description;
+		creation = from.creation;
+		homeUrl = from.homeUrl;
+		filename = from.filename;
+		authors = from.authors;
+		info = from.info;
+	}
+};
+//! \endif
+
+int Iconset::Private::icon_counter = 0;
+
+//static int iconset_counter = 0;
+
+//!
+//! Creates empty Iconset.
+Iconset::Iconset()
+{
+	//iconset_counter++;
+
+	d = new Private;
+}
+
+//!
+//! Creates shared copy of Iconset \a from.
+Iconset::Iconset(const Iconset &from)
+{
+	//iconset_counter++;
+
+	d = from.d;
+	d->ref();
+}
+
+//!
+//! Destroys Iconset, and frees all allocated Icons.
+Iconset::~Iconset()
+{
+	//qWarning("iconset_counter = %d", --iconset_counter);
+
+	if ( d->deref() )
+		delete d;
+
+	IconsetFactoryPrivate::unregisterIconset(this);
+}
+
+//!
+//! Copies all Icons as well as additional information from Iconset \a from.
+Iconset &Iconset::operator=(const Iconset &from)
+{
+	if ( d->deref() )
+		delete d;
+
+	d = from.d;
+	d->ref();
+
+	return *this;
+}
+
+Iconset Iconset::copy() const
+{
+	Iconset is;
+	delete is.d;
+	is.d = new Private( *this->d );
+
+	return is;
+}
+
+void Iconset::detach()
+{
+	if ( d->count > 1 )
+		*this = copy();
+}
+
+//!
+//! Appends icons from Iconset \a from to this Iconset.
+Iconset &Iconset::operator+=(const Iconset &i)
+{
+	detach();
+
+	QDictIterator<Icon> it( i.d->dict );
+	for ( ; it.current(); ++it)
+		d->dict.insert(it.currentKey(), new Icon(*it.current()));
+
+	QPtrListIterator<Icon> it2( i.d->list );
+	for ( ; it2.current(); ++it2)
+		d->list.append(new Icon(*it2.current()));
+
+	return *this;
+}
+
+//!
+//! Frees all allocated Icons.
+void Iconset::clear()
+{
+	detach();
+
+	d->list.clear();
+	d->dict.clear();
+}
+
+//!
+//! Returns the number of Icons in Iconset.
+uint Iconset::count() const
+{
+	return d->list.count();
+}
+
+//!
+//! Loads Icons and additional information from directory \a dir. Directory can usual directory,
+//! or a .zip/.jisp archive. There must exist file named \c icondef.xml in that directory.
+bool Iconset::load(const QString &dir)
+{
+	detach();
+
+	// make it run okay on windows 9.x (where the pixmap memory is limited)
+	QPixmap::Optimization optimization = QPixmap::defaultOptimization();
+	QPixmap::setDefaultOptimization( QPixmap::MemoryOptim );
+
+	bool ret = false;
+
+	QByteArray ba;
+	ba = d->loadData ("icondef.xml", dir);
+	if ( !ba.isEmpty() ) {
+		QDomDocument doc;
+		if ( doc.setContent(ba, false) ) {
+			if ( d->load(doc, dir) ) {
+				d->filename = dir;
+				ret = true;
+			}
+		}
+		else
+			qWarning("Iconset::load(): Failed to load iconset: icondef.xml is invalid XML");
+	}
+
+	QPixmap::setDefaultOptimization( optimization );
+
+	return ret;
+}
+
+//!
+//! Returns pointer to Icon, if Icon with name \a name was found in Iconset, or \a 0 otherwise.
+//! \sa setIcon()
+const Icon *Iconset::icon(const QString &name) const
+{
+	if ( !d || d->dict.isEmpty() )
+		return 0;
+
+	return d->dict.find(name);
+}
+
+//!
+//! Appends (or replaces, if Icon with that name already exists) Icon to Iconset.
+void Iconset::setIcon(const QString &name, const Icon &icon)
+{
+	detach();
+
+	d->dict.replace (name, new Icon(icon));
+
+	QPtrListIterator<Icon> it( d->list );
+	for ( ; it.current(); ++it)
+		if ( it.current()->name() == icon.name() ) {
+			Icon *icon = it.current();
+			d->list.remove(icon);
+			break;
+		}
+
+	d->list.append (new Icon(icon));
+}
+
+//!
+//! Removes Icon with the name \a name from Iconset.
+void Iconset::removeIcon(const QString &name)
+{
+	detach();
+
+	d->dict.remove (name);
+
+	QPtrListIterator<Icon> it( d->list );
+	for ( ; it.current(); ++it)
+		if ( it.current()->name() == name ) {
+			Icon *icon = it.current();
+			d->list.remove(icon);
+			break;
+		}
+}
+
+//!
+//! Returns the Iconset name.
+const QString &Iconset::name() const
+{
+	return d->name;
+}
+
+//!
+//! Returns the Iconset version.
+const QString &Iconset::version() const
+{
+	return d->version;
+}
+
+//!
+//! Returns the Iconset description string.
+const QString &Iconset::description() const
+{
+	return d->description;
+}
+
+//!
+//! Returns the Iconset authors list.
+const QStringList &Iconset::authors() const
+{
+	return d->authors;
+}
+
+//!
+//! Returns the Iconset creation date.
+const QString &Iconset::creation() const
+{
+	return d->creation;
+}
+
+//!
+//! Returns the Iconsets' home URL.
+const QString &Iconset::homeUrl() const
+{
+	return d->homeUrl;
+}
+
+QPtrListIterator<Icon> Iconset::iterator() const
+{
+	//detach(); // ???
+
+	QPtrListIterator<Icon> it( d->list );
+	return it;
+}
+
+//!
+//! Returns directory (or .zip/.jisp archive) name from which Iconset was loaded.
+const QString &Iconset::fileName() const
+{
+	return d->filename;
+}
+
+//!
+//! Sets the Iconset directory (.zip archive) name.
+void Iconset::setFileName(const QString &f)
+{
+	d->filename = f;
+}
+
+/**
+ * Sets the information (meta-data) of this iconset to the information from the given iconset.
+ */
+void Iconset::setInformation(const Iconset &from) {
+	detach();
+	d->setInformation( *(from.d) );
+}
+
+//!
+//! Returns additional Iconset information.
+//! \sa setInfo()
+const QDict<QString> Iconset::info() const
+{
+	return d->info;
+}
+
+//!
+//! Sets additional Iconset information.
+//! \sa info()
+void Iconset::setInfo(const QDict<QString> &i)
+{
+	d->info = i;
+}
+
+//!
+//! Created QMimeSourceFactory with names of icons and their images.
+QMimeSourceFactory *Iconset::createMimeSourceFactory() const
+{
+	QMimeSourceFactory *m = new QMimeSourceFactory;
+
+	QDictIterator<Icon> it( d->dict );
+	for ( ; it.current(); ++it)
+		m->setImage(it.currentKey(), it.current()->image());
+
+	return m;
+}
+
+//!
+//! Adds Iconset to IconsetFactory.
+void Iconset::addToFactory() const
+{
+	IconsetFactoryPrivate::registerIconset(this);
+}
+
+//!
+//! Removes Iconset from IconsetFactory.
+void Iconset::removeFromFactory() const
+{
+	IconsetFactoryPrivate::unregisterIconset(this);
+}
+
+//!
+//! Use this function before creation of Iconsets and it will enable the
+//! sound playing ability when Icons are activated().
+//!
+//! \a unpackPath is the path, to where the sound files will be unpacked, if
+//! required (i.e. when sound is stored inside packed iconset.zip file). Unpacked
+//! file name will be unique, and you can empty the unpack directory at the application
+//! exit. Calling application MUST ensure that the \a unpackPath is already created.
+//! If specified \a unpackPath is empty ("" or QString::null) then sounds from archives
+//! will not be unpacked, and only sounds from already unpacked iconsets (that are not
+//! stored in archives) will be played.
+//! \a receiver and \a slot are used to specify the object that will be connected to
+//! the signal 'playSound(QString fileName)'. Slot should play the specified sound
+//! file.
+void Iconset::setSoundPrefs(QString unpackPath, QObject *receiver, const char *slot)
+{
+#ifdef ICONSET_SOUND
+	if ( !iconSharedObject )
+		iconSharedObject = new IconSharedObject();
+
+	iconSharedObject->unpackPath = unpackPath;
+	QObject::connect(iconSharedObject, SIGNAL(playSound(QString)), receiver, slot);
+#endif
+}
+
+#include "iconset.moc"
Index: psi/tags/0.10-os2-b1/libpsi/iconset/iconset.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/iconset/iconset.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/iconset/iconset.h	(revision 38)
@@ -0,0 +1,205 @@
+/*
+ * iconset.h - various graphics handling classes
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ICONSET_H
+#define ICONSET_H
+
+#include <qobject.h>
+#include <qdict.h>
+#include <qptrlist.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvaluevector.h>
+
+class QImage;
+class QPixmap;
+class QIconSet;
+class QMimeSourceFactory;
+class Anim;
+
+class Impix
+{
+public:
+	Impix();
+	Impix(const QPixmap &);
+	Impix(const QImage &);
+	Impix(const Impix &);
+	Impix & operator=(const Impix &from);
+	~Impix();
+
+	void unload();
+	bool isNull() const;
+
+	const QPixmap & pixmap() const;
+	const QImage & image() const;
+	void setPixmap(const QPixmap &);
+	void setImage(const QImage &);
+
+	operator const QPixmap &() const { return pixmap(); }
+	operator const QImage &() const { return image(); }
+	Impix & operator=(const QPixmap &from) { setPixmap(from); return *this; }
+	Impix & operator=(const QImage &from) { setImage(from); return *this; }
+
+	bool loadFromData(const QByteArray &);
+
+	Impix copy() const;
+	void detach();
+
+private:
+	class Private;
+	Private *d;
+};
+
+class Icon : public QObject
+{
+	Q_OBJECT
+public:
+	Icon();
+	Icon(const Icon &);
+	~Icon();
+
+	Icon & operator= (const Icon &);
+
+	//!
+	//! Returns impix().pixmap().
+	operator const QPixmap &() const { return impix().pixmap(); }
+
+	//!
+	//! Returns impix().image().
+	operator const QImage &() const { return impix().image(); }
+
+	//!
+	//! see iconSet().
+	operator const QIconSet &() const { return iconSet(); }
+
+	virtual bool isAnimated() const;
+	virtual const QPixmap &pixmap() const;
+	virtual const QImage &image() const;
+	virtual const QIconSet & iconSet() const;
+
+	virtual const Impix &impix() const;
+	virtual const Impix &frameImpix() const;
+	void setImpix(const Impix &, bool doDetach = true);
+
+	const Anim *anim() const;
+	void setAnim(const Anim &, bool doDetach = true);
+	void removeAnim(bool doDetach = true);
+
+	virtual int framenumber() const;
+
+	virtual const QString &name() const;
+	void setName(const QString &);
+	void setName(const char *);
+
+	const QRegExp &regExp() const;
+	void setRegExp(const QRegExp &);
+
+	const QDict<QString> &text() const;
+	void setText(const QDict<QString> &);
+
+	const QString &sound() const;
+	void setSound(const QString &);
+
+	bool loadFromData(const QByteArray &, bool isAnimation);
+
+	void stripFirstAnimFrame();
+
+	Icon copy() const;
+	void detach();
+
+signals:
+	void pixmapChanged(const QPixmap &);
+	void iconModified(const QPixmap &);
+
+public slots:
+	virtual void activated(bool playSound = true);	// it just has been inserted in the text, or now it's being displayed by
+							// some widget. icon should play sound and start animation
+
+	virtual void stop();	// this icon is no more displaying. stop animation
+
+public:
+	class Private;
+private:
+	Private *d;
+};
+
+class Iconset
+{
+public:
+	Iconset();
+	Iconset(const Iconset &);
+	~Iconset();
+
+	Iconset &operator=(const Iconset &);
+	Iconset &operator+=(const Iconset &);
+
+	void clear();
+	uint count() const;
+
+	bool load(const QString &dir);
+
+	const Icon *icon(const QString &) const;
+	void setIcon(const QString &, const Icon &);
+	void removeIcon(const QString &);
+
+	const QString &name() const;
+	const QString &version() const;
+	const QString &description() const;
+	const QStringList &authors() const;
+	const QString &creation() const;
+	const QString &homeUrl() const;
+
+	const QString &fileName() const;
+	void setFileName(const QString &);
+	
+	void setInformation(const Iconset &from);
+
+	const QDict<QString> info() const;
+	void setInfo(const QDict<QString> &);
+
+	QPtrListIterator<Icon> iterator() const;
+
+	QMimeSourceFactory *createMimeSourceFactory() const;
+
+	void addToFactory() const;
+	void removeFromFactory() const;
+
+	static void setSoundPrefs(QString unpackPath, QObject *receiver, const char *slot);
+
+	Iconset copy() const;
+	void detach();
+
+private:
+	class Private;
+	Private *d;
+};
+
+class IconsetFactory
+{
+public:
+	static Icon icon(const QString &name);
+	static const QPixmap &iconPixmap(const QString &name);
+
+	static const Icon *iconPtr(const QString &name);
+	static const QStringList icons();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/iconset/iconset.pri
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/iconset/iconset.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/iconset/iconset.pri	(revision 38)
@@ -0,0 +1,11 @@
+iconset {
+	INCLUDEPATH += $$ICONSET_CPP
+
+	SOURCES += \
+		$$ICONSET_CPP/iconset.cpp \
+		$$ICONSET_CPP/anim.cpp
+
+	HEADERS += \
+		$$ICONSET_CPP/iconset.h \
+		$$ICONSET_CPP/anim.h
+}
Index: psi/tags/0.10-os2-b1/libpsi/psipng/psimng.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psipng/psimng.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psipng/psimng.cpp	(revision 38)
@@ -0,0 +1,309 @@
+/*
+ * psimng.cpp - QImageFormat for loading MNG animations at once
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "psimng.h"
+
+#include <qdatetime.h>
+
+#include <qimage.h>
+#include <qasyncimageio.h>
+#include <qiodevice.h>
+
+// Define XMD_H prohibits the included headers of libmng.h to typedef INT32.
+// This is needed for Borland with STL support, since in that case, INT32 is
+// already defined by some Borland header.
+#define XMD_H
+#if defined(Q_OS_UNIXWARE)
+#  define HAVE_BOOLEAN  // libjpeg under Unixware seems to need this
+#endif
+#include <libmng.h>
+#include <stdlib.h>
+
+class PsiMNGFormat : public QImageFormat {
+public:
+	PsiMNGFormat();
+	~PsiMNGFormat();
+
+	int decode(QImage& img, QImageConsumer *cons, const uchar *buf, int length);
+
+	bool       openstream();
+	bool       closestream();
+	bool       readdata(mng_ptr pBuf, mng_uint32 iBuflen, mng_uint32p pRead);
+	bool       errorproc(mng_int32 iErrorcode, mng_int8 /*iSeverity*/, mng_chunkid iChunkname, mng_uint32 /*iChunkseq*/, mng_int32 iExtra1, mng_int32 iExtra2, mng_pchar zErrortext);
+	bool       processheader(mng_uint32 iWidth, mng_uint32 iHeight);
+	mng_ptr    getcanvasline(mng_uint32 iLinenr);
+	mng_bool   refresh(mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h);
+	mng_uint32 gettickcount();
+	bool       settimer(mng_uint32 iMsecs);
+
+private:
+	// Animation-level information
+	enum {
+		MovieStart,
+		Data,
+	} state;
+
+	// Image-level information
+	mng_handle handle;
+
+	// Timing
+	int time;
+
+	// Temporary locals during single data-chunk processing
+	const uchar *data;
+	int dataSize, processedData;
+
+	QImageConsumer *consumer;
+	QImage *image;
+};
+
+//----------------------------------------------------------------------------
+// C-callback to C++-member-function conversion
+//----------------------------------------------------------------------------
+
+static mng_bool openstream( mng_handle handle )
+{
+	return ((PsiMNGFormat*)mng_get_userdata(handle))->openstream();
+}
+static mng_bool closestream( mng_handle handle )
+{
+	return ((PsiMNGFormat*)mng_get_userdata(handle))->closestream();
+}
+static mng_bool readdata( mng_handle handle, mng_ptr pBuf, mng_uint32 iBuflen, mng_uint32p pRead )
+{
+	return ((PsiMNGFormat*)mng_get_userdata(handle))->readdata(pBuf,iBuflen,pRead);
+}
+static mng_bool errorproc( mng_handle handle,
+			   mng_int32   iErrorcode,
+			   mng_int8    iSeverity,
+			   mng_chunkid iChunkname,
+			   mng_uint32  iChunkseq,
+			   mng_int32   iExtra1,
+			   mng_int32   iExtra2,
+			   mng_pchar   zErrortext )
+{
+	return ((PsiMNGFormat*)mng_get_userdata(handle))->errorproc(iErrorcode,
+								    iSeverity,iChunkname,iChunkseq,iExtra1,iExtra2,zErrortext);
+}
+static mng_bool processheader( mng_handle handle,
+			       mng_uint32 iWidth, mng_uint32 iHeight )
+{
+	return ((PsiMNGFormat*)mng_get_userdata(handle))->processheader(iWidth,iHeight);
+}
+static mng_ptr getcanvasline( mng_handle handle, mng_uint32 iLinenr )
+{
+	return ((PsiMNGFormat*)mng_get_userdata(handle))->getcanvasline(iLinenr);
+}
+static mng_bool refresh( mng_handle handle,
+			 mng_uint32  iTop,
+			 mng_uint32  iLeft,
+			 mng_uint32  iBottom,
+			 mng_uint32  iRight	)
+{
+	return ((PsiMNGFormat*)mng_get_userdata(handle))->refresh(iTop,iLeft,iBottom,iRight);
+}
+static mng_uint32 gettickcount( mng_handle handle )
+{
+	return ((PsiMNGFormat*)mng_get_userdata(handle))->gettickcount();
+}
+static mng_bool settimer( mng_handle handle, mng_uint32  iMsecs )
+{
+	return ((PsiMNGFormat*)mng_get_userdata(handle))->settimer(iMsecs);
+}
+
+static mng_ptr memalloc( mng_size_t iLen )
+{
+	return calloc(1,iLen);
+}
+static void memfree( mng_ptr iPtr, mng_size_t /*iLen*/ )
+{
+	free(iPtr);
+}
+
+//----------------------------------------------------------------------------
+
+PsiMNGFormat::PsiMNGFormat()
+{
+	state = MovieStart;
+	handle = 0;
+	time = 0;
+}
+
+PsiMNGFormat::~PsiMNGFormat()
+{
+}
+
+bool PsiMNGFormat::openstream()
+{
+	return true;
+}
+
+bool PsiMNGFormat::closestream()
+{
+	return true;
+}
+
+bool PsiMNGFormat::readdata(mng_ptr pBuf, mng_uint32 iBuflen, mng_uint32p pRead)
+{
+	if ( iBuflen <= (uint)dataSize ) {
+		memcpy(pBuf, data + processedData, iBuflen);
+		*pRead = iBuflen;
+	}
+
+	processedData += iBuflen;
+
+	return true;
+}
+
+bool PsiMNGFormat::errorproc( mng_int32 iErrorcode, mng_int8 /*iSeverity*/, mng_chunkid iChunkname, mng_uint32 /*iChunkseq*/, mng_int32 iExtra1, mng_int32 iExtra2, mng_pchar zErrortext )
+{
+	qWarning("MNG error %d: %s; chunk %c%c%c%c; subcode %d:%d",
+		 iErrorcode,zErrortext,
+		 (iChunkname>>24)&0xff,
+		 (iChunkname>>16)&0xff,
+		 (iChunkname>>8)&0xff,
+		 (iChunkname>>0)&0xff,
+		 iExtra1,iExtra2);
+	return TRUE;
+}
+
+bool PsiMNGFormat::processheader(mng_uint32 iWidth, mng_uint32 iHeight)
+{
+	image->create(iWidth, iHeight, 32);
+	image->setAlphaBuffer(TRUE);
+	memset(image->bits(), 0, iWidth*iHeight*4);
+	consumer->setSize(iWidth, iHeight);
+	mng_set_canvasstyle(handle,
+			    QImage::systemByteOrder() == QImage::LittleEndian
+			    ? MNG_CANVAS_BGRA8 : MNG_CANVAS_ARGB8 );
+	return TRUE;
+}
+
+mng_ptr PsiMNGFormat::getcanvasline(mng_uint32 iLinenr)
+{
+	return image->scanLine(iLinenr);
+}
+
+mng_bool PsiMNGFormat::refresh(mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h)
+{
+	QRect r(x,y,w,h);
+	consumer->changed(r);
+	consumer->setFramePeriod(0);
+	consumer->frameDone();
+	return TRUE;
+}
+
+mng_uint32 PsiMNGFormat::gettickcount()
+{
+	return time;
+}
+
+bool PsiMNGFormat::settimer(mng_uint32 iMsecs)
+{
+	consumer->setFramePeriod(iMsecs);
+	consumer->frameDone();
+	time += iMsecs;
+	return TRUE;
+}
+
+int PsiMNGFormat::decode(QImage &img, QImageConsumer *cons, const uchar *buf, int length)
+{
+	//if ( !length ) // there's no more data then
+	//	return 0;
+
+	consumer = cons;
+	image = &img;
+
+	data = buf;
+	dataSize = length;
+	processedData = 0;
+
+	if ( state == MovieStart ) {
+		handle = mng_initialize( (mng_ptr)this, ::memalloc, ::memfree, 0 );
+		//mng_set_suspensionmode( handle, MNG_FALSE );
+		mng_setcb_openstream( handle, ::openstream );
+		mng_setcb_closestream( handle, ::closestream );
+		mng_setcb_readdata( handle, ::readdata );
+		mng_setcb_errorproc( handle, ::errorproc );
+		mng_setcb_processheader( handle, ::processheader );
+		mng_setcb_getcanvasline( handle, ::getcanvasline );
+		mng_setcb_refresh( handle, ::refresh );
+		mng_setcb_gettickcount( handle, ::gettickcount );
+		mng_setcb_settimer( handle, ::settimer );
+		state = Data;
+
+		mng_readdisplay(handle);
+	}
+	else
+		mng_display_resume(handle);
+
+	image = 0;
+
+	return processedData;
+}
+
+//----------------------------------------------------------------------------
+
+class PsiMNGFormatType : public QImageFormatType
+{
+	QImageFormat *decoderFor(const uchar *buffer, int length)
+	{
+		if (length < 8)
+			return 0;
+
+		if (buffer[0]==138 // MNG signature
+		 && buffer[1]=='M'
+		 && buffer[2]=='N'
+		 && buffer[3]=='G'
+		 && buffer[4]==13
+		 && buffer[5]==10
+		 && buffer[6]==26
+		 && buffer[7]==10) {
+			return new PsiMNGFormat;
+		}
+
+		return 0;
+	}
+
+	const char *formatName() const
+	{
+		return "PsiMNG";
+	}
+};
+
+static PsiMNGFormatType *globalMngFormatTypeObject = 0;
+
+void cleanupPsiMngIO()
+{
+	if ( globalMngFormatTypeObject ) {
+		delete globalMngFormatTypeObject;
+		globalMngFormatTypeObject = 0;
+	}
+}
+
+void initPsiMngIO()
+{
+	static bool done = FALSE;
+	if ( !done ) {
+		done = TRUE;
+		globalMngFormatTypeObject = new PsiMNGFormatType;
+		qAddPostRoutine( cleanupPsiMngIO );
+	}
+}
Index: psi/tags/0.10-os2-b1/libpsi/psipng/psimng.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psipng/psimng.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psipng/psimng.h	(revision 38)
@@ -0,0 +1,26 @@
+/*
+ * psimng.h - QImageFormat for loading MNG animations at once
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIMNG_H
+#define PSIMNG_H
+
+void initPsiMngIO();
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psipng/psipng.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psipng/psipng.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psipng/psipng.cpp	(revision 38)
@@ -0,0 +1,146 @@
+/*
+ * psipng.cpp - QImageFormat for loading Psi PNG animations
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "psipng.h"
+
+#include <qasyncimageio.h>
+
+//! if _hide_doc_
+class PsiPNGFormat : public QImageFormat {
+private:
+	int w, h;
+	QImage image;
+	int numFrames;
+	int frame, frameW;
+	int frameBytes;
+
+	int maxLen, curLen;
+
+	static const int framePeriod;
+
+public:
+	PsiPNGFormat()
+	{
+	}
+
+	~PsiPNGFormat()
+	{
+	}
+
+	int decode(QImage &img, QImageConsumer *consumer, const uchar *buffer, int length)
+	{
+		if ( !length )
+			return 0;
+
+		if ( image.isNull() ) { // cache our image for speedup
+			if ( image.loadFromData( buffer, length ) ) {
+				w = image.width();
+				h = image.height();
+
+				numFrames = w / h;
+				frameBytes = length / numFrames;
+				frameW = w / numFrames;
+				frame = 0;
+
+				consumer->setLooping( numFrames > 1 ? 0 : 1 );
+				consumer->setFramePeriod(framePeriod);
+				consumer->setSize(frameW, h);
+
+				maxLen = length;
+				curLen = 0;
+			}
+			else {
+				if ( !image.isNull() )
+					qWarning("PsiPNGFormat::decode: WARNING: QImage is not loaded and is NOT null!!!");
+				return 0;
+			}
+		}
+
+		if ( frame < numFrames ) {
+			img = image.copy(frame++ * frameW, 0, frameW, h);
+			consumer->frameDone(QPoint(0, 0), QRect(0, 0, frameW, h));
+
+			if ( frame >= numFrames ) {
+				consumer->end();
+				frameBytes = maxLen - curLen;
+			}
+			else
+				consumer->setFramePeriod(framePeriod);
+		}
+
+		curLen += frameBytes;
+		return frameBytes;
+	}
+};
+//! \endif
+const int PsiPNGFormat::framePeriod = 120;
+
+class PsiPNGFormatType : public QImageFormatType
+{
+	QImageFormat *decoderFor(const uchar *buffer, int length)
+	{
+		if (length < 8)
+			return 0;
+
+		if (buffer[0]==137
+		 && buffer[1]=='P'
+		 && buffer[2]=='N'
+		 && buffer[3]=='G'
+		 && buffer[4]==13
+		 && buffer[5]==10
+		 && buffer[6]==26
+		 && buffer[7]==10)
+			return new PsiPNGFormat;
+
+		return 0;
+	}
+
+	const char *formatName() const
+	{
+		return "PsiPNG";
+	}
+};
+
+PsiPNGFormatType *globalPsiPngFormatTypeObject = 0;
+
+void cleanupPsiPngIO()
+{
+	if ( globalPsiPngFormatTypeObject ) {
+		delete globalPsiPngFormatTypeObject;
+		globalPsiPngFormatTypeObject = 0;
+	}
+}
+
+/*!
+	Call this function to register PsiPNG animation format.
+
+	In this format, all animation frames are stored in one .png file.
+	<tt>Frame width = Frame height</tt>, and
+	<tt>total .png width = tatal .png height * numFrames</tt>.
+*/
+void initPsiPngIO()
+{
+	static bool done = FALSE;
+	if ( !done ) {
+		done = TRUE;
+		globalPsiPngFormatTypeObject = new PsiPNGFormatType;
+		qAddPostRoutine( cleanupPsiPngIO );
+	}
+}
Index: psi/tags/0.10-os2-b1/libpsi/psipng/psipng.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psipng/psipng.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psipng/psipng.h	(revision 38)
@@ -0,0 +1,26 @@
+/*
+ * psipng.h - QImageFormat for loading Psi PNG animations
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIPNG_H
+#define PSIPNG_H
+
+void initPsiPngIO();
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psipng/psipng.pri
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psipng/psipng.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psipng/psipng.pri	(revision 38)
@@ -0,0 +1,115 @@
+psipng {
+	INCLUDEPATH += $$PSIPNG_CPP
+
+	SOURCES += \
+		$$PSIPNG_CPP/psipng.cpp
+
+	HEADERS += \
+		$$PSIPNG_CPP/psipng.h
+}
+
+psimng {
+	DEFINES += PSIMNG
+
+	SOURCES += \
+		$$PSIPNG_CPP/psimng.cpp
+
+	HEADERS += \
+		$$PSIPNG_CPP/psimng.h
+
+	CONFIG += system-mng system-jpeg
+        system-mng {
+		win32:LIBS += libmng.lib
+        	unix:LIBS  += -lmng
+        } else {
+		# TODO: include jpeg files too
+		MNGSRC = $(QTDIR)/src/3rdparty/libmng
+
+		INCLUDEPATH += $$MNGSRC
+		SOURCES += \
+			$$MNGSRC/libmng_callback_xs.c \
+			$$MNGSRC/libmng_chunk_io.c \
+			$$MNGSRC/libmng_chunk_prc.c \
+			$$MNGSRC/libmng_chunk_xs.c \
+			$$MNGSRC/libmng_cms.c \
+			$$MNGSRC/libmng_display.c \
+			$$MNGSRC/libmng_dither.c \
+			$$MNGSRC/libmng_error.c \
+			$$MNGSRC/libmng_filter.c \
+			$$MNGSRC/libmng_hlapi.c \
+			$$MNGSRC/libmng_jpeg.c \
+			$$MNGSRC/libmng_object_prc.c \
+			$$MNGSRC/libmng_pixels.c \
+			$$MNGSRC/libmng_prop_xs.c \
+			$$MNGSRC/libmng_read.c \
+			$$MNGSRC/libmng_trace.c \
+			$$MNGSRC/libmng_write.c \
+			$$MNGSRC/libmng_zlib.c
+
+		no-jpeg {
+			message(Use of mng requires support for jpeg)
+			CONFIG += jpeg
+		} else:!jpeg {
+			message(Use of mng requires support for jpeg)
+			CONFIG += jpeg
+		}
+
+		jpeg {
+			system-jpeg {
+				unix:LIBS += -ljpeg
+				win32:LIBS += libjpeg.lib
+		} else {
+			JPEGSRC = $(QTDIR)/src/3rdparty/libjpeg
+
+			INCLUDEPATH += $$JPEGSRC
+			SOURCES += \
+				$$JPEGSRC/jcapimin.c \
+				$$JPEGSRC/jcapistd.c \
+				$$JPEGSRC/jccoefct.c \
+				$$JPEGSRC/jccolor.c \
+				$$JPEGSRC/jcdctmgr.c \
+				$$JPEGSRC/jchuff.c \
+				$$JPEGSRC/jcinit.c \
+				$$JPEGSRC/jcmainct.c \
+				$$JPEGSRC/jcmarker.c \
+				$$JPEGSRC/jcmaster.c \
+				$$JPEGSRC/jcomapi.c \
+				$$JPEGSRC/jcparam.c \
+				$$JPEGSRC/jcphuff.c \
+				$$JPEGSRC/jcprepct.c \
+				$$JPEGSRC/jcsample.c \
+				$$JPEGSRC/jctrans.c \
+				$$JPEGSRC/jdapimin.c \
+				$$JPEGSRC/jdapistd.c \
+				$$JPEGSRC/jdatadst.c \
+				$$JPEGSRC/jdatasrc.c \
+				$$JPEGSRC/jdcoefct.c \
+				$$JPEGSRC/jdcolor.c \
+				$$JPEGSRC/jddctmgr.c \
+				$$JPEGSRC/jdhuff.c \
+				$$JPEGSRC/jdinput.c \
+				$$JPEGSRC/jdmainct.c \
+				$$JPEGSRC/jdmarker.c \
+				$$JPEGSRC/jdmaster.c \
+				$$JPEGSRC/jdmerge.c \
+				$$JPEGSRC/jdphuff.c \
+				$$JPEGSRC/jdpostct.c \
+				$$JPEGSRC/jdsample.c \
+				$$JPEGSRC/jdtrans.c \
+				$$JPEGSRC/jerror.c \
+				$$JPEGSRC/jfdctflt.c \
+				$$JPEGSRC/jfdctfst.c \
+				$$JPEGSRC/jfdctint.c \
+				$$JPEGSRC/jidctflt.c \
+				$$JPEGSRC/jidctfst.c \
+				$$JPEGSRC/jidctint.c \
+				$$JPEGSRC/jidctred.c \
+				$$JPEGSRC/jmemmgr.c \
+				$$JPEGSRC/jquant1.c \
+				$$JPEGSRC/jquant2.c \
+				$$JPEGSRC/jutils.c \
+				$$JPEGSRC/jmemansi.c
+        		}
+		}
+        }
+}
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/README
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/README	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/README	(revision 38)
@@ -0,0 +1,21 @@
+PsiWidgets README
+~~~~~~~~~~~~~~~~~
+
+  Introduction
+  ~~~~~~~~~~~~
+  This is a package of widgets, that are used in Psi Jabber client. You may be
+  interested in it, if you want to develop custom dialogs, or hack existing
+  ones. Read below for installation instructions.
+  
+  Installation
+  ~~~~~~~~~~~~
+  Requirements: Qt 3.1.x (http://www.trolltech.com)
+  
+  Type these commands in terminal:
+  
+  % qmake
+  % make
+  % su -c "make install"
+  
+  Now open Qt Designer and enjoy :-)
+  
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/busywidget.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/busywidget.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/busywidget.cpp	(revision 38)
@@ -0,0 +1,498 @@
+/*
+ * busywidget.cpp - cool animating widget
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *                           Hideaki Omuro
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"busywidget.h"
+
+#include<qtimer.h>
+#include<qpainter.h>
+#include<math.h>
+
+/////////////////////////////////////////////////////////////////////////////
+// common defines
+//
+#define FPS          20 // frequency of update
+
+#define SPINRATE     24 // 1024ths of a revolution per frame
+#define SPINOFFSET    4 // frames each panel is offset by
+
+#define COLORSTOPPED  0xFFFFFF // color when stopped
+#define COLORSPINNING 0xFFFFFF // color when spinning
+#define COLORSHADOW   0x000000 // color of shadow
+
+/////////////////////////////////////////////////////////////////////////////
+// derived defines
+//
+#define MSECSPERFRAME	(1000 / FPS)
+
+/////////////////////////////////////////////////////////////////////////////
+// declared later
+//
+extern char psigraphic[];
+
+// panel class
+class CPanel
+{
+public:
+	int angle;
+	int height;
+	bool spinning;
+	int alpha;
+
+	CPanel(int height = 1);
+	int GetModHeight();
+	int GetShade();
+	void Spin(int n);
+
+	void SetAngle(int _angle)	{ angle = _angle % 1024; }
+	void SetHeight(int _height)	{ height = _height; }
+	int GetAngle()				{ return angle % 1024; }
+	int GetHeight()				{ return height; }
+	int GetModOffset()			{ return (height - GetModHeight()) / 2; }
+};
+
+// color class
+class CColor
+{
+public:
+	int m_clr;
+
+	CColor(int _r, int _g, int _b)
+	{
+		SetColor(_r, _g, _b);
+	}
+	CColor(int _clr)
+	{
+		SetColor(_clr);
+	}
+	inline void SetColor(int _r, int _g, int _b)
+	{
+		SetColor((_r << 16) + (_g << 8) + _b);
+	}
+	inline void SetColor(int _clr)
+	{
+		m_clr = _clr;
+	}
+	inline short GetR()
+	{
+		return m_clr >> 16;
+	}
+	inline short GetG()
+	{
+		return (m_clr >> 8) & 255;
+	}
+	inline short GetB()
+	{
+		return m_clr & 255;
+	}
+	CColor Alpha(CColor clr, int alpha = 256);
+};
+
+class BusyWidget::Private : public QObject
+{
+	Q_OBJECT
+private:
+	BusyWidget *busy;
+
+public:
+	Private(BusyWidget *b)
+	{
+		t = 0;
+		busy = b;
+		stopInProgress = false;
+	}
+
+	bool stopInProgress;
+	bool isActive;
+	int frame;
+	int at;
+	QPixmap pix;
+	QTimer *t;
+
+	// data
+	CPanel panel[5];
+	int  pcountdown;
+	int  ocountdown;
+
+	void render()
+	{
+		QPainter p(busy);
+		p.drawPixmap(0,0, pix);
+	}
+
+	void renderPixmap()
+	{
+		pix.resize(busy->width(), busy->height());
+		pix.fill(QColor("#406080"));
+
+		QPainter p(&pix);
+
+		int i, j, k, l;
+		int row;
+
+		for(i = 0; i < 5; i++)
+		{
+			int o = panel[i].GetModOffset();
+
+			CColor c1(COLORSPINNING), c2(COLORSTOPPED), c3(COLORSHADOW);
+			CColor b = c1.Alpha(c2, panel[i].alpha * 8);
+
+			CColor a = b.Alpha(c3, panel[i].GetShade());
+
+			l = panel[i].GetModHeight();
+
+			double radangle = (double) 3.1415926f * (double) panel[i].GetAngle() / (double) 512;
+			int step = (int)((double)1024 / cos(radangle));
+			step = step < 0 ? -step : step;
+
+			int n = (int)((double)1024 * cos(radangle) * 17 / 2);
+			n = n < 0 ? -n : n;
+
+			row = 8192 - step * n / 1024;
+
+			QColor clr(a.GetR(), a.GetG(), a.GetB());
+
+			for(j = 0; j < l; j++)
+			{
+				int m = row / 1024 + 1;
+				for(k = 0; k < 16; k++)
+				{
+					p.setPen(psigraphic[i * 304 + m * 16 + k] ? Qt::black : clr);
+					p.drawPoint(i * 16 + k + 1, o + j + 1);
+				}
+				row += step;
+			}
+		}
+
+		p.setPen(Qt::black);
+		p.drawRect(0, 0, busy->width(), busy->height());
+	}
+
+public slots:
+	void stop()
+	{
+		if ( stopInProgress ) {
+			stopInProgress = false;
+
+			isActive = FALSE;
+			if( !ocountdown )
+				ocountdown = SPINOFFSET * 4 + 1;
+		}
+	}
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// code
+//
+BusyWidget::BusyWidget(QWidget *parent, const char *name)
+:QWidget(parent, name)
+{
+	d = new Private(this);
+
+	d->isActive = FALSE;
+	d->frame = 0;
+	d->at = 0;
+
+	d->pcountdown = 0;
+	d->ocountdown = 0;
+
+	setFixedWidth(82);
+	setFixedHeight(19);
+	setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+
+	int i;
+	for(i = 0; i < 5; i++)
+		d->panel[i].SetHeight(17);
+
+	d->renderPixmap();
+}
+
+BusyWidget::~BusyWidget()
+{
+	delete d;
+}
+
+bool BusyWidget::isActive() const
+{
+	return (d->isActive && !d->stopInProgress);
+}
+
+void BusyWidget::setActive(bool a)
+{
+	if ( a )
+		start();
+	else
+		stop();
+}
+
+void BusyWidget::start()
+{
+	d->stopInProgress = false;
+
+	if(d->isActive)
+		return;
+
+	d->isActive = TRUE;
+
+	if(!d->pcountdown)
+		d->pcountdown = SPINOFFSET * 4 + 1;
+
+	if(!d->t)
+	{
+		d->t = new QTimer(this);
+		connect(d->t, SIGNAL(timeout()), SLOT(animate()));
+		animate();
+		d->t->start(MSECSPERFRAME);
+	}
+}
+
+void BusyWidget::stop()
+{
+	if(!d->isActive)
+		return;
+
+	if ( d->stopInProgress )
+		return;
+
+	d->stopInProgress = true;
+	QTimer::singleShot(0, d, SLOT(stop()));
+}
+
+void BusyWidget::animate()
+{
+	int i;
+	for(i = 0; i < 5; i++)
+		d->panel[i].Spin(SPINRATE);
+
+	if(d->pcountdown)
+		if(!(--d->pcountdown % SPINOFFSET))
+			d->panel[d->pcountdown / SPINOFFSET].spinning = true;
+
+	if(d->ocountdown)
+		if(!(--d->ocountdown % SPINOFFSET))
+			d->panel[d->ocountdown / SPINOFFSET].spinning = false;
+
+	if(!d->isActive)
+	{
+		bool isValid = false;
+		for(i = 0; i < 5; i++)
+			if(d->panel[i].spinning || d->panel[i].GetAngle() != 0 || d->panel[i].alpha != 0)
+				isValid = true;
+
+		if(!isValid)
+		{
+			delete d->t;
+			d->t = 0;
+		}
+	}
+
+	d->renderPixmap();
+	d->render();
+}
+
+void BusyWidget::paintEvent(QPaintEvent *)
+{
+	d->render();
+}
+
+void BusyWidget::resizeEvent(QResizeEvent *)
+{
+	d->renderPixmap();
+}
+
+/////////////////////////////////////////////////////////////////////////
+// stuff beyond here for animating rotating psi panels
+//
+
+// color stuff
+CColor CColor::Alpha(CColor clr, int alpha)
+{
+	int ialpha = 256 - alpha;
+
+	int r, g, b;
+	r = (alpha * GetR() + ialpha * clr.GetR()) / 256;
+	g = (alpha * GetG() + ialpha * clr.GetG()) / 256;
+	b = (alpha * GetB() + ialpha * clr.GetB()) / 256;
+
+	return CColor(r, g, b);
+}
+
+// panel stuff
+CPanel::CPanel(int _height)
+{
+	height = _height;
+	spinning = false;
+	angle = 0;
+	alpha = 0;
+}
+
+int CPanel::GetModHeight()
+{
+	int l = GetAngle();
+	if(l > 512)
+		l = 1024 - l;
+	double radangle = (double) 3.1415926f * (double) l / (double) 512;
+	int tmp = (int)(cos(radangle)* (double) height);
+	return tmp < 0 ? -tmp : tmp;
+}
+
+int CPanel::GetShade()
+{
+	int l = GetAngle() + 128;
+	if(GetAngle() >= 256 && GetAngle() < 768)
+		l += 512;
+	if(l >= 1024)
+		l %= 1024;
+	if(l == 0)
+		l += 1024;
+	double radangle = (double) 3.1415926f * (double) l / (double) 512;
+	return 128 + (int)(cos(radangle)* (double) 128);
+}
+
+void CPanel::Spin(int n)
+{
+	int i = angle + n;
+	if(!spinning)
+	{
+		if(i >= 1024)
+			SetAngle(0);
+		if(angle < 512 && i >= 512)
+			SetAngle(0);
+		if(angle)
+			SetAngle(i);
+	}
+	else
+		SetAngle(i);
+	if(spinning)
+	{
+		if(alpha < 32)
+			alpha+=2;
+	}
+	else
+	{
+		if(alpha)
+			alpha-=2;
+	}
+}
+
+char psigraphic[304*5] =
+{
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
+	 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+#include "busywidget.moc"
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/busywidget.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/busywidget.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/busywidget.h	(revision 38)
@@ -0,0 +1,64 @@
+/*
+ * busywidget.h - cool animating widget
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *                           Hideaki Omuro
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef BUSYWIDGET_H
+#define BUSYWIDGET_H
+
+#include<qwidget.h>
+#include<qpixmap.h>
+
+class CColor;
+class CPanel;
+
+class BusyWidget : public QWidget
+{
+	Q_OBJECT
+	Q_PROPERTY( bool active READ isActive WRITE setActive )
+
+	Q_OVERRIDE( QSize minimumSize DESIGNABLE false SCRIPTABLE false )
+	Q_OVERRIDE( QSize maximumSize DESIGNABLE false SCRIPTABLE false )
+
+public:
+	BusyWidget(QWidget *parent=0, const char *name=0);
+	~BusyWidget();
+
+	bool isActive() const;
+
+public slots:
+	void start();
+	void stop();
+	void setActive(bool);
+
+protected:
+	void paintEvent(QPaintEvent *);
+	void resizeEvent(QResizeEvent *);
+
+private slots:
+	void animate();
+
+public:
+	class Private;
+private:
+	Private *d;
+	friend class Private;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancylabel.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancylabel.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancylabel.cpp	(revision 38)
@@ -0,0 +1,486 @@
+/*
+ * fancylabel.cpp - the FancyLabel widget
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "fancylabel.h"
+
+#include <qpixmap.h>
+#include <qcolor.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qframe.h>
+#include <qpainter.h>
+#include <qobjectlist.h>
+
+#ifndef WIDGET_PLUGIN
+#include "iconset.h"
+#endif
+
+//----------------------------------------------------------------------------
+// IconLabel
+//----------------------------------------------------------------------------
+
+class IconLabel::Private : public QObject
+{
+	Q_OBJECT
+
+public:
+
+	IconLabel *label;
+	Icon *icon;
+	bool copyIcon;
+#ifdef WIDGET_PLUGIN
+	QString iconName;
+#endif
+
+	Private(IconLabel *l)
+	{
+		label = l;
+		icon = 0;
+	}
+
+	~Private()
+	{
+		stopIcon();
+#ifndef WIDGET_PLUGIN
+		if ( icon && copyIcon )
+			delete icon;
+#endif
+	}
+
+	void setIcon(const Icon *i, bool _copyIcon)
+	{
+		copyIcon = _copyIcon;
+
+		stopIcon();
+#ifndef WIDGET_PLUGIN
+		if ( i && copyIcon )
+			icon = new Icon(*i);
+		else
+			icon = (Icon *)i;
+#endif
+		startIcon();
+	}
+
+protected:
+	void stopIcon()
+	{
+#ifndef WIDGET_PLUGIN
+		if ( icon ) {
+			disconnect(icon, 0, this, 0);
+			icon->stop();
+		}
+#endif
+	}
+
+	void startIcon()
+	{
+#ifndef WIDGET_PLUGIN
+		if ( icon ) {
+			connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
+			icon->activated(false); // TODO: should icon play sound when it's activated on icon?
+			iconUpdated( icon->pixmap() );
+		}
+		else
+			iconUpdated( QPixmap() );
+#endif
+	}
+
+private slots:
+	void iconUpdated(const QPixmap &pix)
+	{
+		label->setPixmap(pix);
+	}
+};
+
+IconLabel::IconLabel(QWidget *parent, const char *name)
+: QLabel(parent, name, WRepaintNoErase | WResizeNoErase)
+{
+	d = new Private(this);
+}
+
+IconLabel::~IconLabel()
+{
+	delete d;
+}
+
+void IconLabel::drawContents (QPainter *paint)
+{
+	const QPixmap *background = paletteBackgroundPixmap();
+	QRect r = contentsRect();
+	QPoint start( r.topLeft() );
+
+	QWidget *pw = (QWidget *)parent();
+	if ( pw && (!background || pw->inherits("MyFancyFrame")) ) { // nasty hack
+		background = pw->paletteBackgroundPixmap();
+		start = mapToParent( r.topLeft() );
+	}
+
+	if ( !background ) {
+		erase(0, 0, width(), height());
+		QLabel::drawContents(paint);
+		return;
+	}
+
+	// try to eliminate flicker
+	QPixmap pix(r.width(), r.height());
+	QPainter p;
+	p.begin(&pix);
+	p.drawTiledPixmap (r, *background, start);
+	QLabel::drawContents ( &p );
+	p.end();
+
+	paint->drawPixmap (r.topLeft(), pix );
+}
+
+const Icon *IconLabel::icon () const
+{
+	return d->icon;
+}
+
+const QString &IconLabel::iconName () const
+{
+#ifndef WIDGET_PLUGIN
+	if ( d->icon )
+		return d->icon->name();
+	return QString::null;
+#else
+	return d->iconName;
+#endif
+}
+
+void IconLabel::setIcon (const Icon *i, bool copyIcon)
+{
+	d->setIcon(i, copyIcon);
+}
+
+void IconLabel::setIcon (const QString &name)
+{
+#ifndef WIDGET_PLUGIN
+	setIcon( IconsetFactory::iconPtr(name) );
+#else
+	d->iconName = name;
+	setText("<qt>icon:<br><small>" + name + "</small></qt>");
+#endif
+}
+
+void IconLabel::setIcon(const QPixmap &p)
+{
+	QLabel::setIcon(p);
+}
+
+void IconLabel::setScaledContents(int width, int height)
+{
+	QLabel::setScaledContents(true);
+	setMinimumSize(width, height);
+	setMaximumSize(width, height);
+}
+
+//----------------------------------------------------------------------------
+// MyFancyFrame -- internal
+//----------------------------------------------------------------------------
+
+class MyFancyFrame : public QFrame
+{
+	Q_OBJECT
+protected:
+	void drawContents (QPainter *paint)
+	{
+		paint->drawPixmap (contentsRect().topLeft(), background);
+	}
+
+	void resizeEvent (QResizeEvent *e)
+	{
+		QFrame::resizeEvent (e);
+
+		QRect rect = contentsRect();
+		int w = rect.width();
+
+		if ( rect.height() <= 0 || w <= 0 )
+			return; // avoid crash
+
+		int r1, g1, b1;
+		from->rgb (&r1, &g1, &b1);
+		int r2, g2, b2;
+		to->rgb (&r2, &g2, &b2);
+
+		float stepR = (float)(r2 - r1) / w;
+		float stepG = (float)(g2 - g1) / w;
+		float stepB = (float)(b2 - b1) / w;
+
+		QPixmap pix (rect.width(), rect.height());
+		QPainter p;
+		p.begin (&pix);
+		for (int i = 0; i < w; i++) {
+			int r = (int)((float)r1 + stepR*i);
+			int g = (int)((float)g1 + stepG*i);
+			int b = (int)((float)b1 + stepB*i);
+
+			p.setPen ( QColor( r, g, b ) );
+			p.drawLine ( i, 0, i, rect.height() );
+		}
+		p.end ();
+
+		QObjectList *l = queryList( "QLabel" );
+		QObjectListIt it( *l );
+		while ( it.current() ) {
+			QLabel *child = (QLabel *)it.current();
+			child->update();
+
+			++it;
+		}
+		delete l;
+
+		setUpdatesEnabled(false);
+		setPaletteBackgroundPixmap(pix);
+		setUpdatesEnabled(true);
+
+		background = pix;
+		update ();
+	}
+
+private:
+	QColor *from, *to;
+	QPixmap background;
+
+public:
+	MyFancyFrame (QWidget *parent, QColor *_from, QColor *_to, const char *name = 0, WFlags f = 0)
+	: QFrame (parent, name, f)
+	{
+		from = _from;
+		to = _to;
+	}
+
+	void repaintBackground()
+	{
+		QResizeEvent e( size(), size() );
+		resizeEvent( &e );
+	}
+};
+
+//----------------------------------------------------------------------------
+// FancyLabel
+//----------------------------------------------------------------------------
+
+class FancyLabel::Private : public QObject
+{
+public:
+	MyFancyFrame *frame;
+	IconLabel *text, *help, *pix;
+	QColor from, to, font;
+	QString textStr, helpStr;
+	static int smallFontSize;
+
+	Private (FancyLabel *parent)
+	: QObject(parent), from(72, 172, 243), to(255, 255, 255), font(0, 0, 0)
+	{
+		QHBoxLayout *mainbox = new QHBoxLayout( parent, 0, 0 );
+
+		frame = new MyFancyFrame ( parent, &from, &to, "fancy_frame" );
+		frame->setFrameShape( QFrame::StyledPanel );
+		frame->setFrameShadow( QFrame::Raised );
+
+		QHBoxLayout *frameLayout = new QHBoxLayout( frame, 3, 0 );
+		QVBoxLayout *layout = new QVBoxLayout( 0, 0, 0 );
+		frameLayout->addLayout( layout );
+
+		text = new IconLabel( frame, "text_label" );
+		text->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)7, (QSizePolicy::SizeType)5, 0, 0, text->sizePolicy().hasHeightForWidth() ) );
+		layout->addWidget( text );
+
+		help = new IconLabel( frame, "help_label" );
+		layout->addWidget( help );
+
+		QFont font = help->font();
+		font.setPointSize(smallFontSize);
+		help->setFont(font);
+
+		pix = new IconLabel( frame, "pixmap_label" );
+		pix->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)1, (QSizePolicy::SizeType)5, 0, 0, help->sizePolicy().hasHeightForWidth() ) );
+		frameLayout->addWidget( pix );
+
+		mainbox->addWidget( frame );
+	}
+};
+
+int FancyLabel::Private::smallFontSize = 0;
+
+FancyLabel::FancyLabel (QWidget *parent, const char *name)
+: QWidget (parent, name)
+{
+	d = new Private (this);
+}
+
+FancyLabel::~FancyLabel ()
+{
+}
+
+void FancyLabel::setText (const QString &text)
+{
+	d->textStr = text;
+	d->text->setText (QString("<font color=\"%1\"><b>").arg(d->font.name()) + text + "</b></font>");
+}
+
+void FancyLabel::setHelp (const QString &help)
+{
+	d->helpStr = help;
+
+	QString f1 = "<small>";
+	QString f2 = "</small>";
+	if ( d->smallFontSize ) {
+		f1 = "<font>";
+		f2 = "</font>";
+	}
+
+	d->help->setText (QString("<font color=\"%1\">").arg(d->font.name()) + f1 + help + f2 + "</font>");
+}
+
+void FancyLabel::setPixmap (const QPixmap &pix)
+{
+	d->pix->setPixmap( pix );
+}
+
+void FancyLabel::setColorFrom (const QColor &col)
+{
+	d->from = col;
+	d->frame->repaintBackground();
+}
+
+void FancyLabel::setColorTo (const QColor &col)
+{
+	d->to = col;
+	d->frame->repaintBackground();
+}
+
+void FancyLabel::setColorFont (const QColor &col)
+{
+	d->font = col;
+	d->frame->repaintBackground();
+}
+
+const QString &FancyLabel::text () const
+{
+	return d->textStr;
+}
+
+const QString &FancyLabel::help () const
+{
+	return d->helpStr;
+}
+
+const QPixmap *FancyLabel::pixmap () const
+{
+	return d->pix->pixmap();
+}
+
+const QColor &FancyLabel::colorFrom () const
+{
+	return d->from;
+}
+
+const QColor &FancyLabel::colorTo () const
+{
+	return d->to;
+}
+
+const QColor &FancyLabel::colorFont () const
+{
+	return d->font;
+}
+
+const Icon *FancyLabel::icon () const
+{
+	return d->pix->icon();
+}
+
+void FancyLabel::setIcon (const Icon *i)
+{
+	d->pix->setIcon (i);
+}
+
+void FancyLabel::setIcon (const QString &name)
+{
+	d->pix->setIcon(name);
+}
+
+void FancyLabel::setIcon(const QPixmap &p)
+{
+	QWidget::setIcon(p);
+}
+
+const QString &FancyLabel::iconName () const
+{
+	return d->pix->iconName();
+}
+
+FancyLabel::Shape FancyLabel::frameShape () const
+{
+	return (FancyLabel::Shape)(int)d->frame->frameShape();
+}
+
+void FancyLabel::setFrameShape (FancyLabel::Shape v)
+{
+	d->frame->setFrameShape( (QFrame::Shape)(int)v );
+	d->frame->repaintBackground();
+}
+
+FancyLabel::Shadow FancyLabel::frameShadow () const
+{
+	return (FancyLabel::Shadow)(int)d->frame->frameShadow();
+}
+
+void FancyLabel::setFrameShadow (FancyLabel::Shadow v)
+{
+	d->frame->setFrameShadow( (QFrame::Shadow)(int)v );
+	d->frame->repaintBackground();
+}
+
+int FancyLabel::lineWidth () const
+{
+	return d->frame->lineWidth();
+}
+
+void FancyLabel::setLineWidth (int v)
+{
+	d->frame->setLineWidth(v);
+	d->frame->repaintBackground();
+}
+
+int FancyLabel::midLineWidth () const
+{
+	return d->frame->midLineWidth();
+}
+
+void FancyLabel::setMidLineWidth (int v)
+{
+	d->frame->setMidLineWidth(v);
+	d->frame->repaintBackground();
+}
+
+void FancyLabel::setScaledContents(int width, int height)
+{
+	d->pix->setScaledContents(width, height);
+}
+
+void FancyLabel::setSmallFontSize(int s)
+{
+	Private::smallFontSize = s;
+}
+
+#include "fancylabel.moc"
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancylabel.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancylabel.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancylabel.h	(revision 38)
@@ -0,0 +1,132 @@
+/*
+ * fancylabel.h - the FancyLabel widget
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef FANCYLABEL_H
+#define FANCYLABEL_H
+
+#include <qwidget.h>
+#include <qlabel.h>
+
+class QPixmap;
+class QColor;
+class Icon;
+
+class FancyLabel : public QWidget
+{
+	Q_OBJECT
+	Q_PROPERTY( QPixmap pixmap READ pixmap WRITE setPixmap )
+	Q_PROPERTY( QString text READ text WRITE setText )
+	Q_PROPERTY( QString help READ help WRITE setHelp )
+	Q_PROPERTY( QColor colorFrom READ colorFrom WRITE setColorFrom )
+	Q_PROPERTY( QColor colorTo READ colorTo WRITE setColorTo )
+	Q_PROPERTY( QColor colorFont READ colorFont WRITE setColorFont )
+	Q_PROPERTY( QString iconName READ iconName WRITE setIcon )
+
+	Q_ENUMS( Shape Shadow )
+	Q_PROPERTY( Shape frameShape READ frameShape WRITE setFrameShape )
+	Q_PROPERTY( Shadow frameShadow READ frameShadow WRITE setFrameShadow )
+	Q_PROPERTY( int lineWidth READ lineWidth WRITE setLineWidth )
+	Q_PROPERTY( int midLineWidth READ midLineWidth WRITE setMidLineWidth )
+
+public:
+	FancyLabel (QWidget *parent = 0, const char *name = 0);
+	~FancyLabel ();
+
+	const QString &text () const;
+	const QString &help () const;
+	const QPixmap *pixmap () const;
+	const QColor &colorFrom () const;
+	const QColor &colorTo () const;
+	const QColor &colorFont () const;
+	const Icon *icon () const;
+	const QString &iconName () const;
+	void setText (const QString &);
+	void setHelp (const QString &);
+	void setPixmap (const QPixmap &);
+	void setColorFrom (const QColor &);
+	void setColorTo (const QColor &);
+	void setColorFont (const QColor &);
+	void setIcon (const Icon *);
+	void setIcon (const QString &);
+	void setIcon (const QPixmap &);
+
+	enum Shape { NoFrame  = 0,                  // no frame
+		     Box      = 0x0001,             // rectangular box
+		     Panel    = 0x0002,             // rectangular panel
+		     WinPanel = 0x0003,             // rectangular panel (Windows)
+		     StyledPanel = 0x0006,          // rectangular panel depending on the GUI style
+		     PopupPanel = 0x0007,           // rectangular panel depending on the GUI style
+		     MenuBarPanel = 0x0008,
+		     ToolBarPanel = 0x0009,
+		     LineEditPanel = 0x000a,
+		     TabWidgetPanel = 0x000b,
+		     GroupBoxPanel = 0x000c,
+		     MShape   = 0x000f              // mask for the shape
+	};
+	enum Shadow { Plain    = 0x0010,            // plain line
+		      Raised   = 0x0020,            // raised shadow effect
+		      Sunken   = 0x0030,            // sunken shadow effect
+		      MShadow  = 0x00f0 };          // mask for the shadow
+
+	Shape frameShape () const;
+	void setFrameShape (Shape);
+
+	Shadow frameShadow () const;
+	void setFrameShadow (Shadow);
+
+	int lineWidth () const;
+	void setLineWidth (int);
+
+	int midLineWidth () const;
+	void setMidLineWidth (int);
+
+	void setScaledContents(int width, int height);
+	static void setSmallFontSize(int);
+
+private:
+	class Private;
+	Private *d;
+};
+
+class IconLabel : public QLabel
+{
+	Q_OBJECT
+	Q_PROPERTY( QString iconName READ iconName WRITE setIcon )
+
+public:
+	IconLabel(QWidget *parent = 0, const char *name = 0);
+	~IconLabel();
+
+	const Icon *icon () const;
+	const QString &iconName () const;
+	void setIcon (const Icon *, bool copyIcon = true);
+	void setIcon (const QString &);
+	void setIcon (const QPixmap &);
+
+	void setScaledContents(int width, int height);
+
+	class Private;
+private:
+	void drawContents (QPainter *paint);
+
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancypopup.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancypopup.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancypopup.cpp	(revision 38)
@@ -0,0 +1,361 @@
+/*
+ * fancypopup.cpp - the FancyPopup passive popup widget
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "fancypopup.h"
+
+#include <qpixmap.h>
+#include <qapplication.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qpointarray.h>
+#include <qpainter.h>
+#include <qptrlist.h>
+#include <qtoolbutton.h>
+#include <qstyle.h>
+
+#include "iconset.h"
+#include "fancylabel.h"
+
+#define BUTTON_WIDTH	16
+#define BUTTON_HEIGHT	14
+
+/*static int checkComponent(int b)
+{
+	int c = b;
+	if ( c > 0xFF )
+		c = 0xFF;
+	return c;
+}
+
+static QColor makeColor(QColor baseColor, int percent)
+{
+	float p = (float)percent/100;
+	int r = checkComponent( (int)(baseColor.red()   + ((float)p * baseColor.red())) );
+	int g = checkComponent( (int)(baseColor.green() + ((float)p * baseColor.green())) );
+	int b = checkComponent( (int)(baseColor.blue()  + ((float)p * baseColor.blue())) );
+
+	return QColor(r, g, b);
+}*/
+
+//----------------------------------------------------------------------------
+// FancyPopup::Private
+//----------------------------------------------------------------------------
+
+class FancyPopup::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(FancyPopup *p);
+	~Private();
+
+	QPoint position();
+
+public slots:
+	void hide();
+	void popupDestroyed(QObject *);
+
+public:
+	// parameters
+	static int hideTimeout;
+	static QColor backgroundColor;
+
+	enum PopupLayout {
+		TopToBottom = 1,
+		BottomToTop = -1
+	};
+	PopupLayout popupLayout;
+
+	QPtrList<FancyPopup> prevPopups, nextPopups;
+	QBoxLayout *layout;
+	FancyPopup *popup;
+	QTimer *hideTimer;
+};
+
+int  FancyPopup::Private::hideTimeout = 5 * 1000; // 5 seconds
+QColor FancyPopup::Private::backgroundColor = QColor (0x52, 0x97, 0xF9);
+
+FancyPopup::Private::Private(FancyPopup *p)
+: QObject(p)
+{
+	popup = p;
+
+	hideTimer = new QTimer(this);
+	connect(hideTimer, SIGNAL(timeout()), SLOT(hide()));
+}
+
+FancyPopup::Private::~Private()
+{
+}
+
+void FancyPopup::Private::hide()
+{
+	popup->hide();
+}
+
+void FancyPopup::Private::popupDestroyed(QObject *obj)
+{
+	if ( prevPopups.contains((FancyPopup *)obj) ) {
+		prevPopups.remove((FancyPopup *)obj);
+		popup->move( position() );
+	}
+	else if ( nextPopups.contains((FancyPopup *)obj) ) {
+		nextPopups.remove((FancyPopup *)obj);
+	}
+}
+
+QPoint FancyPopup::Private::position()
+{
+	QRect geom = qApp->desktop()->availableGeometry(popup);
+	QPoint destination(geom.x() + geom.width(), geom.y() + geom.height()); // in which corner popup should appear
+
+	if ( destination.y() > (qApp->desktop()->screenGeometry().height()/2) )
+		popupLayout = Private::BottomToTop;
+	else
+		popupLayout = Private::TopToBottom;
+
+	if ( (destination.x() + popup->width()) > (geom.x() + geom.width()) )
+		destination.setX( geom.x() + geom.width() - popup->width() );
+
+	if ( destination.x() < 0 )
+		destination.setX( 0 );
+
+	if ( (destination.y() + popup->height()) > (geom.y() + geom.height()) )
+		destination.setY( geom.y() + geom.height() - popup->height() );
+
+	if ( destination.y() < 0 )
+		destination.setY( 0 );
+
+	QPtrListIterator<FancyPopup> it ( prevPopups );
+	FancyPopup *p;
+	for ( ; (p = it.current()); ++it) {
+		destination.setY( destination.y() + popupLayout * p->height() );
+	}
+
+	return destination;
+}
+
+//----------------------------------------------------------------------------
+// FancyPopup
+//----------------------------------------------------------------------------
+
+static const int POPUP_FLAGS = Qt::WStyle_Customize | Qt::WDestructiveClose | Qt::WX11BypassWM
+                             | Qt::WStyle_StaysOnTop | Qt::WStyle_Tool | Qt::WStyle_NoBorder;
+
+FancyPopup::FancyPopup(QString title, const Icon *icon, FancyPopup *prev, bool copyIcon)
+#ifdef Q_WS_PM
+// r=dmik: Why should we specify WRepaintNoErase and WResizeNoErase flags?
+// middlehbox->addSpacing(5) below leaves unpainted areas not covered by child
+// widgets. On other platforms it works just because these flags are *ineffective*
+// there (see the WM_ERASEBKGND handler in Qt/Win32); on OS/2 they are not.
+: QFrame( 0, 0, POPUP_FLAGS )
+#else
+: QFrame( 0, 0, POPUP_FLAGS | WRepaintNoErase | WResizeNoErase | WDestructiveClose )
+#endif
+{
+	d = new Private(this);
+
+	if ( prev ) {
+		QPtrListIterator<FancyPopup> it ( prev->d->prevPopups );
+		FancyPopup *popup;
+		bool finished = false;
+		for ( ; !finished; ++it) {
+			popup = it.current();
+			if ( !popup ) {
+				popup = prev;
+				finished = true;
+			}
+
+			d->prevPopups.append( popup );
+			connect(popup, SIGNAL(destroyed(QObject *)), d, SLOT(popupDestroyed(QObject *)));
+		}
+
+		prev->d->nextPopups.append( this );
+		connect(this, SIGNAL(destroyed(QObject *)), prev->d, SLOT(popupDestroyed(QObject *)));
+	}
+
+	// TODO: use darker color on popup borders
+	QPixmap back(1, 1);
+	QPainter p(&back);
+	p.setPen( d->backgroundColor );
+	p.drawPoint( 0, 0 );
+	p.end();
+
+	QVBoxLayout *vbox = new QVBoxLayout(this, 0, 0);
+
+	// top row
+	QHBoxLayout *tophbox = new QHBoxLayout(vbox);
+	QLabel *top1 = new QLabel(this);
+	top1->setFixedWidth(3);
+	top1->setPaletteBackgroundPixmap(back);
+	tophbox->addWidget(top1);
+
+	QVBoxLayout *topvbox = new QVBoxLayout(tophbox);
+	QLabel *top2 = new QLabel(this);
+	top2->setFixedHeight(1);
+	top2->setPaletteBackgroundPixmap(back);
+	topvbox->addWidget(top2);
+
+	QHBoxLayout *tophbox2 = new QHBoxLayout(topvbox);
+	IconLabel *titleIcon = new IconLabel(this);
+	titleIcon->setIcon(icon, copyIcon);
+	titleIcon->setPaletteBackgroundPixmap(back);
+	tophbox2->addWidget(titleIcon);
+
+	QLabel *top5 = new QLabel(this);
+	top5->setFixedWidth(3);
+	top5->setPaletteBackgroundPixmap(back);
+	tophbox2->addWidget(top5);
+
+	QLabel *titleText = new QLabel(this);
+	QString backgroundColorStr;
+	if ( (d->backgroundColor.red() + d->backgroundColor.green() + d->backgroundColor.blue())/3 > 128 )
+		backgroundColorStr = "white";
+	else
+		backgroundColorStr = "black";
+	titleText->setText( QString("<font color='%1'><nobr><b>%1</b></nobr></font>").arg( backgroundColorStr ).arg(title) );
+	titleText->setBackgroundPixmap(back);
+	titleText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+	tophbox2->addWidget(titleText);
+
+	QWidget *closeButtonBack = new QWidget(this);
+	closeButtonBack->setPaletteBackgroundPixmap( back );
+	tophbox2->addWidget(closeButtonBack);
+
+	QVBoxLayout *closeButtonBackLayout = new QVBoxLayout(closeButtonBack);
+	closeButtonBackLayout->addStretch();
+
+	QToolButton *closeButton = new QToolButton( closeButtonBack, "close" );
+	QToolTip::add( closeButton, tr( "Close" ) );
+	closeButtonBackLayout->addWidget( closeButton );
+	closeButtonBackLayout->addStretch();
+	closeButton->setFocusPolicy( NoFocus );
+	closeButton->setIconSet( style().stylePixmap(QStyle::SP_TitleBarCloseButton) );
+	closeButton->setFixedSize(BUTTON_WIDTH, BUTTON_HEIGHT);
+	connect(closeButton, SIGNAL(clicked()), SLOT(hide()));
+
+	QLabel *top3 = new QLabel(this);
+	top3->setFixedHeight(1);
+	top3->setPaletteBackgroundPixmap(back);
+	topvbox->addWidget(top3);
+
+	QLabel *top4 = new QLabel(this);
+	top4->setFixedWidth(3);
+	top4->setPaletteBackgroundPixmap(back);
+	tophbox->addWidget(top4);
+
+	// middle row
+	QHBoxLayout *middlehbox = new QHBoxLayout(vbox);
+	QLabel *middle1 = new QLabel(this);
+	middle1->setFixedWidth(4);
+	middle1->setPaletteBackgroundPixmap(back);
+	middlehbox->addWidget(middle1);
+
+	middlehbox->addSpacing(5);
+	QVBoxLayout *middlevbox = new QVBoxLayout(middlehbox);
+	middlevbox->addSpacing(5);
+	d->layout = middlevbox; // we'll add more items later in addLayout()
+	middlehbox->addSpacing(5);
+
+	QLabel *middle3 = new QLabel(this);
+	middle3->setFixedWidth(4);
+	middle3->setPaletteBackgroundPixmap(back);
+	middlehbox->addWidget(middle3);
+
+	// bottom row
+	QHBoxLayout *bottomhbox = new QHBoxLayout(vbox);
+	QLabel *bottom1 = new QLabel(this);
+	bottom1->setFixedSize( 4, 4 );
+	bottom1->setPaletteBackgroundPixmap(back);
+	bottomhbox->addWidget(bottom1);
+
+	QLabel *bottom2 = new QLabel(this);
+	bottom2->setFixedHeight(4);
+	bottom2->setPaletteBackgroundPixmap(back);
+	bottomhbox->addWidget(bottom2);
+
+	QLabel *bottom3 = new QLabel(this);
+	bottom3->setFixedSize( 4, 4 );
+	bottom3->setPaletteBackgroundPixmap(back);
+	bottomhbox->addWidget(bottom3);
+}
+
+FancyPopup::~FancyPopup()
+{
+}
+
+void FancyPopup::addLayout(QLayout *layout, int stretch)
+{
+	d->layout->addLayout(layout, stretch);
+	d->layout->addSpacing(5);
+}
+
+void FancyPopup::show()
+{
+	if ( size() != sizeHint() )
+		resize( sizeHint() ); // minimumSizeHint()
+
+	// position popup
+	move ( d->position() );
+
+	// display popup
+	d->hideTimer->start( d->hideTimeout );
+	QFrame::show();
+}
+
+void FancyPopup::hideEvent(QHideEvent *e)
+{
+	d->hideTimer->stop();
+	deleteLater();
+
+	QFrame::hideEvent(e);
+}
+
+void FancyPopup::mouseReleaseEvent(QMouseEvent *e)
+{
+	QWidget *closeButton = (QWidget *)child("closeButton");
+	if ( closeButton ) {
+		QPoint p = closeButton->mapFromParent( e->pos() );
+		if ( p.x() >= 0 && p.y() >= 0 && p.x() < closeButton->width() && p.y() < closeButton->height() )
+			QFrame::mouseReleaseEvent(e);
+	}
+
+	emit clicked();
+	emit clicked((int)e->button());
+}
+
+void FancyPopup::restartHideTimer()
+{
+	d->hideTimer->stop();
+	d->hideTimer->start( d->hideTimeout );
+}
+
+void FancyPopup::setHideTimeout(int time)
+{
+	FancyPopup::Private::hideTimeout = time;
+}
+
+void FancyPopup::setBorderColor(QColor c)
+{
+	FancyPopup::Private::backgroundColor = c;
+}
+
+#include "fancypopup.moc"
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancypopup.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancypopup.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancypopup.h	(revision 38)
@@ -0,0 +1,59 @@
+/*
+ * fancypopup.h - the FancyPopup passive popup widget
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef FANCYPOPUP_H
+#define FANCYPOPUP_H
+
+#include <qframe.h>
+
+class Icon;
+class QTimer;
+
+class FancyPopup : public QFrame
+{
+	Q_OBJECT
+public:
+	FancyPopup(QString title, const Icon *icon = 0, FancyPopup *prev = 0, bool copyIcon = true);
+	~FancyPopup();
+
+	void addLayout(QLayout *layout, int stretch = 0);
+
+	static void setHideTimeout(int);
+	static void setBorderColor(QColor);
+
+	void show();
+	void restartHideTimer();
+
+signals:
+	void clicked();
+	void clicked(int);
+
+protected:
+	void hideEvent(QHideEvent *);
+	void mouseReleaseEvent(QMouseEvent *);
+
+public:
+	class Private;
+private:
+	Private *d;
+	friend class Private;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancypopuplist.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancypopuplist.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/fancypopuplist.h	(revision 38)
@@ -0,0 +1,71 @@
+/*
+ * fancypopuplist.h - the list class to hold all the FancyPopups
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef POPUPLIST_H
+#define POPUPLIST_H
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include "fancypopup.h"
+
+class FancyPopupList : public QObject, public QPtrList<FancyPopup>
+{
+	Q_OBJECT
+
+public:
+	FancyPopupList()
+	{
+		setAutoDelete(true);
+	}
+
+	FancyPopup *last()
+	{
+		if ( !count() )
+			return 0;
+		return QPtrList<FancyPopup>::first();
+	}
+
+	void prepend(const FancyPopup *d)
+	{
+		if ( isEmpty() )
+			emit started();
+
+		connect(d, SIGNAL(destroyed(QObject *)), SLOT(popupDestroyed(QObject *)));
+		QPtrList<FancyPopup>::prepend(d);
+	}
+
+signals:
+	void started();
+	void finished();
+
+private slots:
+	void popupDestroyed(QObject *p)
+	{
+		setAutoDelete(false);
+		remove((FancyPopup *)p);
+		setAutoDelete(true);
+
+		if ( isEmpty() )
+			emit finished();
+	}
+};
+
+#endif
+
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconaction.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconaction.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconaction.cpp	(revision 38)
@@ -0,0 +1,563 @@
+/*
+ * iconaction.cpp - the QAction subclass that uses Icons and supports animation
+ * Copyright (C) 2003-2004  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "iconaction.h"
+
+#include "iconwidget.h"
+#include "iconset.h"
+
+#include <qlayout.h>
+#include <qpopupmenu.h>
+#include <qtooltip.h>
+#include <qobjectlist.h>
+#include <qtimer.h>
+
+//----------------------------------------------------------------------------
+// IconAction
+//----------------------------------------------------------------------------
+
+class IconAction::Private : public QObject
+{
+	Q_OBJECT
+public:
+	QPtrList<IconToolButton> buttons;
+	QMap<QPopupMenu *, int> popups;
+	Icon *icon;
+	IconAction *action;
+	QPopupMenu *popup;
+
+	Private(IconAction *act) {
+		icon = 0;
+		action = act;
+		popup = 0;
+	}
+
+	QString menuText() {
+		QString txt = action->menuText();
+
+		if ( !action->accel().isEmpty() )
+			txt += '\t' + (QString)action->accel();
+
+		return txt;
+	}
+
+public slots:
+	void menuClicked() {
+		action->setOn( !action->isOn() );
+		emit action->setOn( action->isOn() );
+		emit action->activated(); // required for our lovely status change actions
+	}
+
+	void popupDestroyed( QObject *obj ) {
+		bool dirty = true;
+		while ( dirty ) {
+			dirty = false;
+
+			QMap<QPopupMenu *, int>::Iterator it = popups.begin();
+			for ( ; it != popups.end(); ++it ) {
+				if ( it.key() == obj ) {
+					dirty = true;
+					popups.remove( it );
+					break;
+				}
+			}
+		}
+	}
+};
+
+IconAction::IconAction(QObject *parent, const char *name)
+: QAction(parent, name)
+{
+	d = new Private(this);
+}
+
+IconAction::IconAction(const QString &text, const QString &icon, const QString &menuText, QKeySequence accel, QObject *parent, const char *name, bool toggle)
+: QAction(text, menuText, accel, parent, name, toggle)
+{
+	d = new Private(this);
+
+	setIcon(icon);
+}
+
+IconAction::IconAction(const QString &text, const QString &menuText, QKeySequence accel, QObject *parent, const char *name, bool toggle)
+: QAction(text, menuText, accel, parent, name, toggle)
+{
+	d = new Private(this);
+}
+
+IconAction::~IconAction()
+{
+	// delete the buttons list before our own destruction
+	d->buttons.setAutoDelete(true);
+	d->buttons.clear();
+
+	delete d;
+}
+
+const Icon *IconAction::icon() const
+{
+	return d->icon;
+}
+
+void IconAction::setIcon(const Icon *i)
+{
+	if ( d->icon ) {
+		disconnect(d->icon, 0, this, 0 );
+		d->icon->stop();
+		delete d->icon;
+		d->icon = 0;
+	}
+
+	QIconSet is;
+	if ( i ) {
+		d->icon = new Icon(*i);
+		connect(d->icon, SIGNAL(iconModified(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
+		d->icon->activated(true);
+
+		is = d->icon->iconSet();
+	}
+
+	QAction::setIconSet( is );
+
+	for ( QPtrListIterator<IconToolButton> it(d->buttons); it.current(); ++it ) {
+		IconToolButton *btn = it.current();
+		btn->setIcon ( d->icon );
+	}
+
+#ifndef Q_WS_MAC
+	is = QIconSet();
+#endif
+
+	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
+	for ( ; it2 != d->popups.end(); ++it2 ) {
+		it2.key()->changeItem( it2.data(), iconSet(), d->menuText() );
+	}
+}
+
+void IconAction::setIcon(const QString &name)
+{
+	setIcon( IconsetFactory::iconPtr(name) );
+}
+
+const QString &IconAction::iconName() const
+{
+	if ( d->icon )
+		return d->icon->name();
+	return QString::null;
+}
+
+bool IconAction::addTo(QWidget *w)
+{
+	if ( w->inherits("QToolBar") || w->isA("QWidget") ) {
+		QCString bname = name() + QCString("_action_button");
+		IconToolButton *btn = new IconToolButton ( w, bname );
+		d->buttons.append ( btn );
+
+		btn->setToggleButton ( isToggleAction() );
+		btn->setOn( isOn() );
+		btn->setTextLabel ( text() );
+		btn->setIcon ( d->icon, false );
+		btn->setEnabled ( isEnabled() );
+
+		if ( d->popup )
+			btn->setPopup( d->popup );
+		btn->setPopupDelay (1); // the popup will be displayed immediately
+
+		QToolTip::add (btn, toolTipFromMenuText());
+
+		if ( w->isA("QWidget") )
+			if ( w->layout() )
+				w->layout()->add( btn );
+
+		connect(btn, SIGNAL(clicked()), this, SIGNAL(activated()));
+		connect(btn, SIGNAL(toggled(bool)), this, SLOT(setOn(bool)));
+
+		connect(btn, SIGNAL(destroyed()), SLOT(objectDestroyed()));
+
+		addingToolButton(btn);
+	}
+	else if ( w->inherits("QPopupMenu") ) {
+		//if ( !isVisible() )
+		//	return true;
+
+		QPopupMenu *popup = (QPopupMenu *)w;
+		int id;
+		QIconSet iconset;
+#ifndef Q_WS_MAC
+		iconset = iconSet();
+#endif
+		if ( d->popup )
+			id = popup->insertItem (iconset, d->menuText(), d->popup);
+		else
+			id = popup->insertItem (iconset, d->menuText());
+
+		d->popups.insert( popup, id );
+		connect( popup, SIGNAL( destroyed( QObject * ) ), d, SLOT( popupDestroyed( QObject * ) ) );
+
+		popup->setItemEnabled(id, isEnabled());
+		popup->setWhatsThis(id, whatsThis());
+
+		popup->setItemVisible(id, isVisible());
+
+		// will not work for actions with d->popup :-/
+		if ( isToggleAction() ) {
+			popup->setCheckable( true );
+			popup->setItemChecked(id, isOn());
+			popup->connectItem(id, d, SLOT(menuClicked()));
+		}
+		else
+			popup->connectItem(id, this, SIGNAL(activated()));
+
+		addingMenuItem( popup, id );
+	}
+	else
+		return QAction::addTo(w);
+
+	return true;
+}
+
+void IconAction::objectDestroyed()
+{
+	const QObject *obj = sender();
+	d->buttons.removeRef((IconToolButton *)obj);
+}
+
+void IconAction::setOn(bool b)
+{
+	QAction::setOn(b);
+	for ( QPtrListIterator<IconToolButton> it(d->buttons); it.current(); ++it ) {
+		IconToolButton *btn = it.current();
+		btn->setOn(b);
+	}
+
+	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
+	for ( ; it2 != d->popups.end(); ++it2 ) {
+		it2.key()->setItemChecked( it2.data(), b );
+	}
+}
+
+void IconAction::toolButtonToggled(bool b)
+{
+	setOn(b);
+}
+
+void IconAction::setEnabled(bool e)
+{
+	QAction::setEnabled(e);
+	for ( QPtrListIterator<IconToolButton> it(d->buttons); it.current(); ++it ) {
+		IconToolButton *btn = it.current();
+		btn->setEnabled (e);
+	}
+
+	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
+	for ( ; it2 != d->popups.end(); ++it2 ) {
+		it2.key()->setItemEnabled( it2.data(), e );
+	}
+}
+
+void IconAction::setText(const QString &t)
+{
+	QAction::setText(t);
+	for ( QPtrListIterator<IconToolButton> it(d->buttons); it.current(); ++it ) {
+		IconToolButton *btn = it.current();
+		btn->setTextLabel(t);
+	}
+
+#ifndef Q_WS_MAC
+	QIconSet is = iconSet();
+#else
+	QIconSet is;
+#endif
+
+	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
+	for ( ; it2 != d->popups.end(); ++it2 ) {
+		it2.key()->changeItem( it2.data(), is, d->menuText() );
+	}
+}
+
+QPtrList<IconToolButton> IconAction::buttonList()
+{
+	return d->buttons;
+}
+
+void IconAction::iconUpdated(const QPixmap &pix)
+{
+	QAction::setIconSet( d->icon->iconSet() );
+
+#ifndef Q_WS_MAC
+	QPixmap p = pix;
+#else
+	Q_UNUSED( pix );
+	QPixmap p;
+#endif
+
+	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
+	for ( ; it2 != d->popups.end(); ++it2 ) {
+		it2.key()->changeItem( it2.data(), p, d->menuText() );
+	}
+}
+
+QString IconAction::toolTipFromMenuText() const
+{
+	QString tt, str = menuText();
+	for (int i = 0; i < (int)str.length(); i++)
+		if ( str[i] == '&' && str[i+1] != '&' )
+			continue;
+		else
+			tt += str[i];
+
+	return tt;
+}
+
+QPopupMenu *IconAction::popup() const
+{
+	return d->popup;
+}
+
+void IconAction::setPopup( QPopupMenu *p )
+{
+	d->popup = p;
+
+	QPtrList<IconToolButton> btns = buttonList();
+	for ( QPtrListIterator<IconToolButton> it(btns); it.current(); ++it ) {
+		QToolButton *btn = it.current();
+
+		btn->setPopup (0);
+
+		if ( p )
+			btn->setPopup ( p );
+	}
+}
+
+void IconAction::setIconSet( const QIconSet &ic )
+{
+	QAction::setIconSet( ic );
+
+	QPtrList<IconToolButton> btns = buttonList();
+	for ( QPtrListIterator<IconToolButton> it(btns); it.current(); ++it ) {
+		QToolButton *btn = it.current();
+
+		btn->setIconSet( ic );
+	}
+
+#ifndef Q_WS_MAC
+	QIconSet is = ic;
+#else
+	QIconSet is;
+#endif
+
+	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
+	for ( ; it2 != d->popups.end(); ++it2 ) {
+		it2.key()->changeItem( it2.data(), is, d->menuText() );
+	}
+}
+
+void IconAction::setVisible( bool b )
+{
+	QAction::setVisible( b );
+
+	QPtrList<IconToolButton> btns = buttonList();
+	for ( QPtrListIterator<IconToolButton> it(btns); it.current(); ++it ) {
+		QToolButton *btn = it.current();
+
+		if ( b )
+			btn->show();
+		else
+			btn->hide();
+	}
+
+	QMap<QPopupMenu *, int>::Iterator it2 = d->popups.begin();
+	for ( ; it2 != d->popups.end(); ++it2 ) {
+		it2.key()->setItemVisible( it2.data(), b );
+	}
+}
+
+IconAction *IconAction::copy() const
+{
+	IconAction *act = new IconAction(text(), iconName(), menuText(), accel(), 0, name(), isToggleAction());
+
+	*act = *this;
+
+	return act;
+}
+
+IconAction &IconAction::operator=( const IconAction &from )
+{
+	setText( from.text() );
+	setIcon( from.iconName() );
+	setMenuText( from.menuText() );
+	setAccel( from.accel() );
+	setName( from.name() );
+	setToggleAction( from.isToggleAction() );
+	setWhatsThis( whatsThis() );
+
+	// TODO: add more
+
+	return *this;
+}
+
+//----------------------------------------------------------------------------
+// IconActionGroup
+//----------------------------------------------------------------------------
+
+class IconActionGroup::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(IconActionGroup *_group) {
+		group = _group;
+		dirty = false;
+	}
+
+	IconActionGroup *group;
+
+	QPopupMenu *popup;
+
+	bool exclusive;
+	bool usesDropDown;
+
+	bool dirty;
+
+public slots:
+	void updatePopup();
+};
+
+void IconActionGroup::Private::updatePopup()
+{
+	if ( !dirty )
+		return;
+
+	if ( !usesDropDown )
+		qWarning("IconActionGroup does not support !usesDropDown yet");
+
+	popup->clear();
+
+	QObjectList *l = group->queryList("QAction");
+	QObjectListIt it( *l );
+	QObject *obj;
+	for ( ; (obj = it.current()); ++it) {
+		QAction *action = (QAction *)obj;
+
+		if ( !group->icon() && action->inherits( "IconAction" ) )
+			group->setIcon( ((IconAction * )action)->icon() );
+
+		action->addTo( popup );
+	}
+	delete l;
+
+	group->setPopup( popup );
+	dirty = false;
+}
+
+IconActionGroup::IconActionGroup(QObject *parent, const char *name, bool exclusive)
+	: IconAction( parent, name )
+{
+	d = new Private(this);
+	d->popup = new QPopupMenu();
+
+	d->exclusive = exclusive;
+}
+
+IconActionGroup::~IconActionGroup()
+{
+	delete d->popup;
+	delete d;
+}
+
+void IconActionGroup::insertChild( QObject *obj )
+{
+	IconAction::insertChild( obj );
+
+	d->dirty = true;
+	QTimer::singleShot( 0, d, SLOT( updatePopup() ) );
+}
+
+void IconActionGroup::removeChild( QObject *obj )
+{
+	IconAction::removeChild( obj );
+
+	d->dirty = true;
+	QTimer::singleShot( 0, d, SLOT( updatePopup() ) );
+}
+
+void IconActionGroup::add( QAction * )
+{
+	qWarning("IconActionGroup::add(): not implemented");
+}
+
+void IconActionGroup::addSeparator()
+{
+       QAction *separatorAction = new QAction(this, "qt_separator_action");
+       Q_UNUSED( separatorAction );
+}
+
+bool IconActionGroup::addTo( QWidget *w )
+{
+	if ( w->inherits("QPopupMenu") ) {
+		QPopupMenu *popup = (QPopupMenu *)w;
+
+		QObjectList *l = queryList("QAction");
+		QObjectListIt it( *l );
+		QObject *obj;
+		for ( ; (obj = it.current()); ++it) {
+			QAction *action = (QAction *)obj;
+
+			action->addTo( popup );
+		}
+		delete l;
+
+		return true;
+	}
+
+	return IconAction::addTo( w );
+}
+
+IconAction *IconActionGroup::copy() const
+{
+	qWarning("IconActionGroup::copy() doesn't work!");
+	return (IconAction *)this;
+}
+
+void IconActionGroup::setExclusive( bool e )
+{
+	d->exclusive = e;
+}
+
+bool IconActionGroup::isExclusive() const
+{
+	return d->exclusive;
+}
+
+void IconActionGroup::setUsesDropDown( bool u )
+{
+	d->usesDropDown = u;
+}
+
+bool IconActionGroup::usesDropDown() const
+{
+	return d->usesDropDown;
+}
+
+void IconActionGroup::addingToolButton(IconToolButton *btn)
+{
+	btn->setPopupDelay( 0 );
+}
+
+#include "iconaction.moc"
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconaction.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconaction.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconaction.h	(revision 38)
@@ -0,0 +1,114 @@
+/*
+ * iconaction.h - the QAction subclass that uses Icons and supports animation
+ * Copyright (C) 2003-2004  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ICONACTION_H
+#define ICONACTION_H
+
+#include <qaction.h>
+#include <qptrlist.h>
+
+class QToolButton;
+class QPixmap;
+class QIconSet;
+class Icon;
+class IconToolButton;
+
+class IconAction : public QAction
+{
+	Q_OBJECT
+public:
+	IconAction(QObject *parent, const char *name = 0);
+	IconAction(const QString &text, const QString &icon, const QString &menuText, QKeySequence accel, QObject *parent, const char *name = 0, bool toggle = FALSE);
+	IconAction(const QString &text, const QString &menuText, QKeySequence accel, QObject *parent, const char *name = 0, bool toggle = FALSE);
+	~IconAction();
+
+	virtual bool addTo(QWidget *);
+
+	const Icon *icon() const;
+	void setIcon(const Icon *);
+	void setIcon(const QString &);
+	const QString &iconName() const;
+
+	QPopupMenu *popup() const;
+	void setPopup( QPopupMenu * );
+
+	void setIconSet( const QIconSet & );
+	void setVisible( bool );
+
+	virtual IconAction *copy() const;
+	virtual IconAction &operator=( const IconAction & );
+
+public slots:
+	void setEnabled(bool);
+	void setOn(bool);
+	void setText(const QString &);
+
+protected:
+	virtual void addingToolButton(IconToolButton *) { }
+	virtual void addingMenuItem(QPopupMenu *, int id) { Q_UNUSED(id); }
+	QPtrList<IconToolButton> buttonList();
+
+	QString toolTipFromMenuText() const;
+
+private slots:
+	void objectDestroyed();
+	void iconUpdated(const QPixmap &);
+	void toolButtonToggled(bool);
+
+public:
+	class Private;
+private:
+	Private *d;
+	friend class Private;
+};
+
+class IconActionGroup : public IconAction
+{
+	Q_OBJECT
+public:
+	IconActionGroup(QObject *parent, const char *name = 0, bool exclusive = false);
+	~IconActionGroup();
+
+	void setExclusive( bool );
+	bool isExclusive() const;
+
+	void add( QAction * );
+	void addSeparator();
+
+	bool addTo( QWidget * );
+
+	void setUsesDropDown( bool );
+	bool usesDropDown() const;
+
+	void insertChild( QObject * );
+	void removeChild( QObject * );
+
+	void addingToolButton(IconToolButton *);
+
+	IconAction *copy() const;
+
+public:
+	class Private;
+private:
+	Private *d;
+	friend class Private;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconselect.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconselect.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconselect.cpp	(revision 38)
@@ -0,0 +1,413 @@
+/*
+ * iconselect.cpp - class that allows user to select an Icon from an Iconset
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "iconselect.h"
+
+#include <qapplication.h>
+#include <qdesktopwidget.h>
+#include <qobjectlist.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qlayout.h>
+#include <qbutton.h>
+#include <qlabel.h>
+#include <qtooltip.h>
+#include <qtextcodec.h>
+
+#include <math.h>
+
+#include "iconset.h"
+
+//----------------------------------------------------------------------------
+// IconSelectButton
+//----------------------------------------------------------------------------
+
+class IconSelectButton : public QButton
+{
+	Q_OBJECT
+
+private:
+	Icon *ic;
+	QString text;
+	QSize s;
+	bool animated;
+
+public:
+	IconSelectButton(QWidget *parent, const char *name = 0)
+	: QButton(parent, name, WRepaintNoErase)
+	{
+		//setFocusPolicy(StrongFocus);
+		ic = 0;
+		animated = false;
+		connect (this, SIGNAL(clicked()), SLOT(iconSelected()));
+	}
+
+	~IconSelectButton()
+	{
+		iconStop();
+
+		if ( ic ) {
+			delete ic;
+			ic = 0;
+		}
+	}
+
+	void setIcon(const Icon *i)
+	{
+		iconStop();
+
+		if ( ic ) {
+			delete ic;
+			ic = 0;
+		}
+
+		if ( i )
+			ic = new Icon(*((Icon *)i));
+		else
+			ic = 0;
+	}
+
+	const Icon *icon() const
+	{
+		return ic;
+	}
+
+	QSize sizeHint() const { return s; }
+	void setSizeHint(QSize sh) { s = sh; }
+
+signals:
+	void iconSelected(const Icon *);
+	void textSelected(QString);
+
+public slots:
+	void aboutToShow() { iconStart(); }
+	void aboutToHide() { iconStop();  }
+
+private:
+	void iconStart()
+	{
+		if ( ic ) {
+			connect(ic, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
+			if ( !animated ) {
+				ic->activated(false);
+				animated = true;
+			}
+
+			if ( !ic->text().isEmpty() ) {
+				// first, try to get the text by priorities
+				QStringList lang;
+				lang << QString(QTextCodec::locale()).left(2);	// most prioritent, is the local language
+				lang << "";			// and then the language without name goes (international?)
+				lang << "en";			// then real English
+
+				QString *str = 0;
+				QStringList::Iterator it = lang.begin();
+				for ( ; it != lang.end(); ++it) {
+					str = ic->text().find( *it );
+					if (str)
+						break;
+				}
+
+				// if all fails, just get the first text
+				if ( !str )
+				{
+					QDictIterator<QString> it ( ic->text() );
+					for ( ; it.current(); ++it) {
+						if ( it.current() && !it.current()->isEmpty() ) {
+							str = it.current();
+							break;
+						}
+					}
+				}
+
+				if ( str )
+					text = *str;
+
+				// and list of possible variants in the ToolTip
+				QString toolTip;
+				QDictIterator<QString> it2 ( ic->text() );
+				for ( ; it2.current(); ++it2) {
+					if ( !toolTip.isEmpty() )
+						toolTip += ", ";
+					toolTip += **it2;
+					break; // comment this to get list of iconsets
+				}
+				if ( toolTip.length() > 30 )
+					toolTip = toolTip.left(30) + "...";
+				QToolTip::add(this, toolTip);
+			}
+		}
+	}
+
+	void iconStop()
+	{
+		if ( ic ) {
+			disconnect(ic, 0, this, 0 );
+			if ( animated ) {
+				ic->stop();
+				animated = false;
+			}
+		}
+	}
+
+	void enterEvent(QEvent *) { setFocus(); } // focus follows mouse mode
+	void leaveEvent(QEvent *) { clearFocus(); }
+
+private slots:
+	void iconUpdated(const QPixmap &)
+	{
+		update();
+	}
+
+	void iconSelected()
+	{
+		clearFocus();
+		if ( ic ) {
+			emit iconSelected(ic);
+			emit textSelected(text);
+			parentWidget()->parentWidget()->close();
+		}
+	}
+
+private:
+	void drawButton (QPainter *p)
+	{
+		int flags = QStyle::Style_Enabled;
+
+		if ( hasFocus() /* && hasMouse()*/ /*|| isDown() || isOn()*/ )
+			flags |= QStyle::Style_Active;
+
+		QMenuItem dummy;
+		style().drawControl(QStyle::CE_PopupMenuItem, p, this, rect(), colorGroup(),
+				flags, QStyleOption(&dummy, 0, 0));
+
+		if ( ic ) {
+			QPixmap pix = ic->pixmap();
+			p->drawPixmap((width() - pix.width())/2, (height() - pix.height())/2, pix);
+		}
+	}
+};
+
+//----------------------------------------------------------------------------
+// IconSelect -- the widget that does all dirty work
+//----------------------------------------------------------------------------
+
+class IconSelect : public QWidget
+{
+	Q_OBJECT
+
+private:
+	Iconset is;
+	QGridLayout *grid;
+
+public:
+	IconSelect(QWidget *parent, const char *name = 0);
+	~IconSelect();
+
+	void setIconset(const Iconset &);
+	const Iconset &iconset() const;
+
+	//bool isFocusEnabled () const { return true; }
+	//void keyPressEvent(QKeyEvent *);
+
+	/*void paintEvent(QPaintEvent *e)
+	{
+		QWidget *pw = (QWidget *)parent();
+		if ( !pw || !pw->paletteBackgroundPixmap() )
+			return;
+
+		QPainter paint;
+		paint.begin(this);
+
+		QRect r = rect();
+		QPixmap pix(r.width(), r.height());
+		QPainter p;
+		p.begin(&pix);
+		p.drawTiledPixmap (r, *pw->paletteBackgroundPixmap(), mapToParent( r.topLeft() ));
+		//QLabel::drawContents ( &p );
+		p.end();
+
+		paint.drawPixmap (r.topLeft(), pix );
+
+		paint.end();
+	}*/
+
+	void noIcons();
+};
+
+IconSelect::IconSelect(QWidget *parent, const char *name)
+: QWidget(parent, name)
+{
+	grid = 0;
+	noIcons();
+	setFocus();
+}
+
+IconSelect::~IconSelect()
+{
+}
+
+void IconSelect::noIcons()
+{
+	grid = new QGridLayout(this);
+	grid->setAutoAdd(true);
+
+	QLabel *lbl = new QLabel(this);
+	lbl->setText( tr("No icons available") );
+}
+
+void IconSelect::setIconset(const Iconset &iconset)
+{
+	is = iconset;
+
+	// delete all children
+	if (grid) {
+		delete grid;
+
+		QObjectList *l = queryList();
+		l->setAutoDelete(true);
+		delete l;
+	}
+
+	if ( !is.count() ) {
+		noIcons();
+		return;
+	}
+
+	// first we need to find optimal size for elements and don't forget about
+	// taking too much screen space
+	float w = 0, h = 0;
+
+	double count; // the 'double' type is somewhat important for MSVC.NET here
+	QPtrListIterator<Icon> it = is.iterator();
+	for (count = 0; it.current(); ++it, count++) {
+		w += (*it)->pixmap().width();
+		h += (*it)->pixmap().height();
+	}
+	w /= count;
+	h /= count;
+
+	const int margin = 2;
+	int tileSize = (int)QMAX(w, h) + 2*margin;
+
+	QRect r = QApplication::desktop()->availableGeometry( this );
+	int maxSize = QMIN(r.width(), r.height())*3/4;
+
+	int size = (int)ceil( sqrt( count ) );
+
+	if ( size*tileSize > maxSize ) { // too many icons. find reasonable size.
+		int c = 0;
+		for (w = 0; w <= maxSize; w += tileSize)
+			c++;
+		size = c - 1;
+	}
+
+	// now, fill grid with elements
+	grid = new QGridLayout(this, size, size);
+	grid->setAutoAdd(true);
+
+	count = 0;
+
+	it = is.iterator();
+	for ( ; it.current(); ++it) {
+		if ( ++count > size*size )
+			break;
+
+		IconSelectButton *b = new IconSelectButton(this);
+		b->setIcon( *it );
+		b->setSizeHint( QSize(tileSize, tileSize) );
+		connect (b, SIGNAL(iconSelected(const Icon *)), parent(), SIGNAL(iconSelected(const Icon *)));
+		connect (b, SIGNAL(textSelected(QString)), parent(), SIGNAL(textSelected(QString)));
+
+		connect (parent(), SIGNAL(aboutToShow()), b, SLOT(aboutToShow()));
+		connect (parent(), SIGNAL(aboutToHide()), b, SLOT(aboutToHide()));
+	}
+}
+
+const Iconset &IconSelect::iconset() const
+{
+	return is;
+}
+
+/*void IconSelect::keyPressEvent(QKeyEvent *e)
+{
+	int it = 0;
+	switch ( e->key() ) {
+		case Key_Tab:
+			it = e->state() & ShiftButton ? -1 : 1;
+			break;
+
+		case Key_Up:
+		case Key_Left:
+			it = -1;
+			break;
+
+		case Key_Down:
+		case Key_Right:
+			it = 1;
+			break;
+
+		default:
+			e->ignore();
+	}
+
+	if ( it )
+		focusNextPrevChild ( it > 0 );
+}*/
+
+//----------------------------------------------------------------------------
+// IconSelectPopup
+//----------------------------------------------------------------------------
+
+class IconSelectPopup::Private : public QObject
+{
+public:
+	Private(QWidget *parent)
+	: QObject(parent)
+	{
+		icsel = new IconSelect(parent);
+	}
+
+	IconSelect *icsel;
+};
+
+IconSelectPopup::IconSelectPopup(QWidget *parent, const char *name)
+: QPopupMenu(parent, name)
+{
+	d = new Private(this);
+
+	insertItem (d->icsel);
+}
+
+IconSelectPopup::~IconSelectPopup()
+{
+}
+
+void IconSelectPopup::setIconset(const Iconset &i)
+{
+	d->icsel->setIconset(i);
+}
+
+const Iconset &IconSelectPopup::iconset() const
+{
+	return d->icsel->iconset();
+}
+
+#include "iconselect.moc"
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconselect.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconselect.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconselect.h	(revision 38)
@@ -0,0 +1,49 @@
+/*
+ * iconselect.h - class that allows user to select an Icon from an Iconset
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ICONSELECT_H
+#define ICONSELECT_H
+
+#include <qpopupmenu.h>
+
+class Icon;
+class Iconset;
+
+class IconSelectPopup : public QPopupMenu
+{
+	Q_OBJECT
+
+public:
+	IconSelectPopup(QWidget *parent = 0, const char *name = 0);
+	~IconSelectPopup();
+
+	void setIconset(const Iconset &);
+	const Iconset &iconset() const;
+
+signals:
+	void iconSelected(const Icon *);
+	void textSelected(QString);
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconwidget.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconwidget.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconwidget.cpp	(revision 38)
@@ -0,0 +1,805 @@
+/*
+ * iconwidget.cpp - misc. Iconset- and Icon-aware widgets
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "iconwidget.h"
+
+#include <qapplication.h>
+#include <qpainter.h>
+
+#ifndef WIDGET_PLUGIN
+#	include "iconset.h"
+#	include <qstyle.h>
+#	include <qbitmap.h>
+#	include <qmap.h>
+#else
+#	include <qimage.h>
+
+static const char *cancel_xpm[] = {
+"22 22 60 1",
+" 	c None",
+".	c #E84300",
+"+	c #E63F00",
+"@	c #D11E00",
+"#	c #D11B00",
+"$	c #F69D50",
+"%	c #F59A4D",
+"&	c #E23800",
+"*	c #EE5F1F",
+"=	c #ED5A1D",
+"-	c #CD1700",
+";	c #FECBA2",
+">	c #FEC69A",
+",	c #F39045",
+"'	c #DE3200",
+")	c #FE7B3C",
+"!	c #FE7234",
+"~	c #EC4C15",
+"{	c #CC1100",
+"]	c #FEC091",
+"^	c #FEBA89",
+"/	c #F2873D",
+"(	c #DA2C00",
+"_	c #FE692C",
+":	c #EB4712",
+"<	c #CA0F00",
+"[	c #FEB480",
+"}	c #FEAE78",
+"|	c #F07D35",
+"1	c #D62600",
+"2	c #FEA870",
+"3	c #FEA166",
+"4	c #EF722D",
+"5	c #D32100",
+"6	c #FE9B5F",
+"7	c #FE9356",
+"8	c #F16C2A",
+"9	c #F16525",
+"0	c #FE8B4D",
+"a	c #FE8445",
+"b	c #EE4B15",
+"c	c #FE6025",
+"d	c #EE4310",
+"e	c #C90E00",
+"f	c #FE561D",
+"g	c #FE4B16",
+"h	c #EA2F08",
+"i	c #C70900",
+"j	c #FE4010",
+"k	c #FE350B",
+"l	c #EA1D03",
+"m	c #C60700",
+"n	c #FE2906",
+"o	c #FE1A02",
+"p	c #E90900",
+"q	c #C50300",
+"r	c #FE0A00",
+"s	c #FE0000",
+"t	c #E90000",
+"u	c #C40000",
+"                      ",
+"                      ",
+"    .+          @#    ",
+"   .$%&        @*=-   ",
+"  .$;>,'      @*)!~{  ",
+"  +%>]^/(    @*)!_:<  ",
+"   &,^[}|1  @*)!_:<   ",
+"    '/}2345@*)!_:<    ",
+"     (|36789)!_:<     ",
+"      1470a)!_:<      ",
+"       58a)!_b<       ",
+"       @9)!_cde       ",
+"      @*)!_cfghi      ",
+"     @*)!_bdgjklm     ",
+"    @*)!_:<ehknopq    ",
+"   @*)!_:<  ilorstu   ",
+"  @*)!_:<    mpssstu  ",
+"  #=!_:<      qtsstu  ",
+"   -~:<        uttu   ",
+"    {<          uu    ",
+"                      ",
+"                      "};
+
+// dammit MSVC6
+class Iconset {
+public:
+	Iconset() { }
+};
+#endif
+
+//----------------------------------------------------------------------------
+// IconsetSelect
+//----------------------------------------------------------------------------
+
+class IconsetSelectItem : public IconWidgetItem
+{
+	Q_OBJECT
+private:
+	static const int margin;
+	static const int displayNumIcons;
+#ifndef WIDGET_PLUGIN
+	Iconset iss;
+	QMap<Icon*, QRect> iconRects;
+#endif
+	int w, h;
+	mutable int fullW, fullH;
+
+public:
+	IconsetSelectItem(QListBox *parent, IconsetSelectItem *after, const Iconset &_iconset)
+	: IconWidgetItem(parent, after)
+	{
+#ifndef WIDGET_PLUGIN
+		iss = _iconset;
+		setText( iss.name() );
+
+		w = margin;
+		h = 2*margin;
+
+		int count;
+
+		QPtrListIterator<Icon> it = iss.iterator();
+		for (count = 0; it.current(); ++it) {
+			if ( count++ >= displayNumIcons )
+				break; // display only first displayNumIcons icons
+
+			QPixmap pix = it.current()->pixmap();
+
+			iconRects[it.current()] = QRect( w, margin, pix.width(), pix.height() );
+
+			w += pix.width() + margin;
+			h = QMAX( h, pix.height() + 2*margin );
+
+			connect (it.current(), SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
+			it.current()->activated(false); // start animation
+		}
+
+		QMap<Icon*, QRect>::Iterator it2;
+		for (it2 = iconRects.begin(); it2 != iconRects.end(); it2++) {
+			QRect r = it2.data();
+			it2.data() = QRect( r.x(), (h - r.height())/2, r.width(), r.height() );
+		}
+#else
+		Q_UNUSED( _iconset );
+#endif
+	}
+
+	~IconsetSelectItem()
+	{
+#ifndef WIDGET_PLUGIN
+		QMap<Icon*, QRect>::Iterator it;
+		for (it = iconRects.begin(); it != iconRects.end(); it++)
+			it.key()->stop();
+#endif
+	}
+
+	const Iconset *iconset() const
+	{
+#ifndef WIDGET_PLUGIN
+		return &iss;
+#else
+		return 0;
+#endif
+	}
+
+	int height( const QListBox *lb ) const
+	{
+		fullH = lb->fontMetrics().lineSpacing() + 2 + h;
+		return QMAX( fullH, QApplication::globalStrut().height() );
+	}
+
+	int width( const QListBox *lb ) const
+	{
+		fullW = QMAX(lb->fontMetrics().width( text() ) + 6, w + 10);
+		return QMAX( fullW, QApplication::globalStrut().width() );
+	}
+
+	void paint(QPainter *painter)
+	{
+#ifndef WIDGET_PLUGIN
+		QFontMetrics fm = painter->fontMetrics();
+		painter->drawText( 3, fm.ascent() + (fm.leading()+1)/2 + 1, text() );
+
+		QMap<Icon*, QRect>::Iterator it;
+		for (it = iconRects.begin(); it != iconRects.end(); it++) {
+			Icon *icon = it.key();
+			QRect r = it.data();
+			painter->drawPixmap(QPoint(10 + r.left(), fm.lineSpacing() + 2 + r.top()), icon->pixmap());
+		}
+#else
+		Q_UNUSED(painter);
+#endif
+	}
+
+private slots:
+	void iconUpdated(const QPixmap &)
+	{
+#ifndef WIDGET_PLUGIN
+		IconsetSelect *issel = (IconsetSelect *)listBox();
+		issel->updateItem (this);
+#endif
+	}
+};
+const int IconsetSelectItem::margin = 3;
+const int IconsetSelectItem::displayNumIcons = 10;
+
+class IconsetSelect::Private
+{
+public:
+	Private()
+	{
+		lastItem = 0;
+	}
+
+	IconsetSelectItem *lastItem;
+};
+
+IconsetSelect::IconsetSelect(QWidget *parent, const char *name)
+: QListBox(parent, name)
+{
+	d = new Private;
+}
+
+IconsetSelect::~IconsetSelect()
+{
+	delete d;
+}
+
+void IconsetSelect::insert(const Iconset &iconset)
+{
+#ifndef WIDGET_PLUGIN
+	IconsetSelectItem *item = new IconsetSelectItem(this, d->lastItem, iconset);
+	d->lastItem = item;
+#else
+	Q_UNUSED(iconset);
+#endif
+}
+
+void IconsetSelect::moveItemUp()
+{
+	if ( currentItem() < 1 )
+		return;
+
+	IconsetSelectItem *i = (IconsetSelectItem *)item(currentItem());
+	if ( !i )
+		return;
+	QListBoxItem *prev = i->prev()->prev();
+	takeItem (i);
+	insertItem (i, prev);
+	setSelected (i, true);
+	setCurrentItem (i);
+}
+
+void IconsetSelect::moveItemDown()
+{
+	if ( currentItem() == -1 || currentItem() > (int)count() - 2 )
+		return;
+
+	IconsetSelectItem *i = (IconsetSelectItem *)item(currentItem());
+	if ( !i )
+		return;
+	QListBoxItem *next = i->next();
+	takeItem (i);
+	insertItem (i, next);
+	setCurrentItem (i);
+}
+
+const Iconset *IconsetSelect::iconset() const
+{
+	IconsetSelectItem *i = (IconsetSelectItem *)selectedItem();
+	if ( !i )
+		i = (IconsetSelectItem *)item(currentItem());
+	if ( i )
+		return i->iconset();
+	return 0;
+}
+
+void IconsetSelect::paintCell(QPainter *p, int row, int col)
+{
+	// we'll do some caching to avoid flicker
+	QListBoxItem *item = QListBox::item(row);
+
+	if ( !item ) {
+		QListBox::paintCell(p, row, col);
+		return;
+	}
+
+	int w = contentsWidth();
+	int h = item->height(this);
+	QPixmap pix(w, h);
+	QPainter p2;
+	p2.begin (&pix);
+	QListBox::paintCell(&p2, row, col);
+	p2.end ();
+
+	p->drawPixmap(QPoint(0, 0), pix);
+}
+
+
+//----------------------------------------------------------------------------
+// IconsetDisplay
+//----------------------------------------------------------------------------
+
+class IconsetDisplayItem : public IconWidgetItem
+{
+	Q_OBJECT
+private:
+	static const int margin;
+	Icon *icon;
+	int w, h;
+
+public:
+	IconsetDisplayItem(QListBox *parent, IconsetDisplayItem *after, Icon *i, int iconW)
+	: IconWidgetItem(parent, after)
+	{
+#ifndef WIDGET_PLUGIN
+		icon = i;
+		w = iconW;
+
+		connect (icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
+		icon->activated(false);
+
+		h = icon->pixmap().height();
+
+		QString str;
+		QDictIterator<QString> it ( icon->text() );
+		for ( ; it.current(); ++it) {
+			if ( !str.isEmpty() )
+				str += ", ";
+			str += **it;
+		}
+		if ( !str.isEmpty() )
+			setText(str);
+		else
+			setText(tr("Name: '%1'").arg(icon->name()));
+#else
+		Q_UNUSED( i );
+		Q_UNUSED( iconW );
+#endif
+	}
+
+	~IconsetDisplayItem()
+	{
+#ifndef WIDGET_PLUGIN
+		icon->stop();
+#endif
+	}
+
+	int height( const QListBox *lb ) const
+	{
+		int hh = QMAX(h + 2*margin, lb->fontMetrics().lineSpacing() + 2);
+		return QMAX( hh, QApplication::globalStrut().height() );
+	}
+
+	int width( const QListBox *lb ) const
+	{
+		int ww = lb->fontMetrics().width( text() ) + 6 + w + 2*margin;
+		return QMAX( ww, QApplication::globalStrut().width() );
+	}
+
+	void paint(QPainter *painter)
+	{
+#ifndef WIDGET_PLUGIN
+		painter->drawPixmap(QPoint((2*margin+w - icon->pixmap().width())/2, margin), icon->pixmap());
+		QFontMetrics fm = painter->fontMetrics();
+		//int hh = QMAX(h + 2*margin, fm.lineSpacing() + 2);
+		painter->drawText( w + 2*margin + 3, fm.ascent() + (fm.leading()+1)/2 + 1, text() );
+		//painter->drawText( w + 2*margin + 3, (hh - fm.lineSpacing())/2, text() );
+#else
+		Q_UNUSED(painter);
+#endif
+	}
+
+private slots:
+	void iconUpdated(const QPixmap &)
+	{
+		IconsetDisplay *issel = (IconsetDisplay *)listBox();
+		issel->updateItem (this);
+	}
+};
+const int IconsetDisplayItem::margin = 3;
+
+class IconsetDisplay::Private
+{
+public:
+	Private()
+	{
+		lastItem = 0;
+	}
+
+	IconsetDisplayItem *lastItem;
+};
+
+IconsetDisplay::IconsetDisplay(QWidget *parent, const char *name)
+: QListBox(parent, name, WStaticContents | WResizeNoErase | WRepaintNoErase)
+{
+	d = new Private;
+}
+
+IconsetDisplay::~IconsetDisplay()
+{
+	delete d;
+}
+
+void IconsetDisplay::setIconset(const Iconset &iconset)
+{
+#ifndef WIDGET_PLUGIN
+	int w = 0;
+	QPtrListIterator<Icon> it = iconset.iterator();
+	for ( ; it.current(); ++it) {
+		w = QMAX(w, it.current()->pixmap().width());
+	}
+
+	it = iconset.iterator();
+	for ( ; it.current(); ++it) {
+		IconsetDisplayItem *item = new IconsetDisplayItem(this, d->lastItem, it.current(), w);
+		d->lastItem = item;
+	}
+#else
+	Q_UNUSED(iconset);
+#endif
+}
+
+void IconsetDisplay::paintCell(QPainter *p, int row, int col)
+{
+	// we'll do some caching to avoid flicker
+	QListBoxItem *item = QListBox::item(row);
+
+	if ( !item ) {
+		QListBox::paintCell(p, row, col);
+		return;
+	}
+
+	int w = contentsWidth();
+	int h = item->height(this);
+	QPixmap pix(w, h);
+	QPainter p2;
+	p2.begin (&pix);
+	QListBox::paintCell(&p2, row, col);
+	p2.end ();
+
+	p->drawPixmap(QPoint(0, 0), pix);
+}
+
+
+//----------------------------------------------------------------------------
+// IconButton
+//----------------------------------------------------------------------------
+
+class IconButton::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Icon *icon;
+	IconButton *button;
+	bool textVisible;
+	bool activate, forced;
+#ifdef WIDGET_PLUGIN
+	QString iconName;
+#endif
+
+public:
+	Private(IconButton *b)
+	{
+		icon = 0;
+		button = b;
+		textVisible = true;
+		forced = false;
+	}
+
+	~Private()
+	{
+		iconStop();
+	}
+
+	void setIcon(Icon *i)
+	{
+#ifndef WIDGET_PLUGIN
+		iconStop();
+		if ( i )
+			icon = new Icon(*i);
+		iconStart();
+#else
+		Q_UNUSED(i);
+#endif
+	}
+
+	void iconStart()
+	{
+#ifndef WIDGET_PLUGIN
+		if ( icon ) {
+			connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
+			if ( activate )
+				icon->activated(true); // FIXME: should icon play sound when it's activated on button?
+		}
+
+		updateIcon();
+#endif
+	}
+
+	void iconStop()
+	{
+#ifndef WIDGET_PLUGIN
+		if ( icon ) {
+			disconnect(icon, 0, this, 0 );
+			if ( activate )
+				icon->stop();
+
+			delete icon;
+			icon = 0;
+		}
+#endif
+	}
+
+	void update()
+	{
+#ifndef WIDGET_PLUGIN
+		if ( icon )
+			iconUpdated( icon->pixmap() );
+#endif
+	}
+
+	void updateIcon()
+	{
+#ifndef WIDGET_PLUGIN
+		if ( icon )
+			iconUpdated( icon->pixmap() );
+		else
+			iconUpdated( QPixmap() );
+#endif
+	}
+
+public slots:
+	void iconUpdated(const QPixmap &pix)
+	{
+		button->setUpdatesEnabled(FALSE);
+		if ( textVisible || button->text().isEmpty() )
+			button->setIconSet(pix);
+		else
+			button->setPixmap(pix);
+		button->setUpdatesEnabled(TRUE);
+		button->update();
+	}
+};
+
+IconButton::IconButton(QWidget *parent, const char *name)
+: QPushButton(parent, name)
+{
+	setWFlags(getWFlags() | WRepaintNoErase); // no nasty flicker anymore :)
+	d = new Private(this);
+}
+
+IconButton::~IconButton()
+{
+	delete d;
+}
+
+void IconButton::setIcon(const QPixmap &p)
+{
+	QPushButton::setIcon(p);
+}
+
+void IconButton::forceSetIcon(const Icon *i, bool activate)
+{
+	d->activate = activate;
+	d->setIcon ((Icon *)i);
+	d->forced = true;
+}
+
+void IconButton::setIcon(const Icon *i, bool activate)
+{
+#ifndef Q_WS_X11
+	if ( !text().isEmpty() )
+		return;
+#endif
+
+	forceSetIcon(i, activate);
+	d->forced = false;
+}
+
+void IconButton::setIcon(const QString &name)
+{
+#ifndef WIDGET_PLUGIN
+	setIcon( IconsetFactory::iconPtr(name) );
+#else
+	d->iconName = name;
+
+	if ( !name.isEmpty() ) {
+		QPixmap pix((const char **)cancel_xpm);
+		d->iconUpdated(QPixmap( pix ));
+	}
+	else
+		d->iconUpdated(QPixmap());
+#endif
+}
+
+const QString &IconButton::iconName() const
+{
+#ifndef WIDGET_PLUGIN
+	if ( d->icon )
+		return d->icon->name();
+	return QString::null;
+#else
+	return d->iconName;
+#endif
+}
+
+void IconButton::setText(const QString &text)
+{
+#ifndef Q_WS_X11
+	if ( !d->forced )
+		setIcon(0);
+#endif
+
+	QPushButton::setText( text );
+	d->updateIcon();
+}
+
+bool IconButton::textVisible() const
+{
+	return d->textVisible;
+}
+
+void IconButton::setTextVisible(bool v)
+{
+	d->textVisible = v;
+	d->updateIcon();
+}
+
+void IconButton::drawButtonLabel(QPainter *p)
+{
+	QPushButton::drawButtonLabel(p);
+}
+
+//----------------------------------------------------------------------------
+// IconToolButton
+//----------------------------------------------------------------------------
+
+class IconToolButton::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Icon *icon;
+	IconToolButton *button;
+	bool activate;
+#ifdef WIDGET_PLUGIN
+	QString iconName;
+#endif
+
+public:
+	Private(IconToolButton *b)
+	{
+		icon = 0;
+		button = b;
+	}
+
+	~Private()
+	{
+		iconStop();
+	}
+
+	void setIcon(Icon *i)
+	{
+#ifndef WIDGET_PLUGIN
+		iconStop();
+		if ( i )
+			icon = new Icon(*i);
+		iconStart();
+#else
+		Q_UNUSED(i);
+#endif
+	}
+
+	void iconStart()
+	{
+#ifndef WIDGET_PLUGIN
+		if ( icon ) {
+			connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
+			if ( activate )
+				icon->activated(true); // FIXME: should icon play sound when it's activated on button?
+			iconUpdated( icon->pixmap() );
+		}
+		else
+			iconUpdated( QPixmap() );
+#endif
+	}
+
+	void iconStop()
+	{
+#ifndef WIDGET_PLUGIN
+		if ( icon ) {
+			disconnect(icon, 0, this, 0 );
+			if ( activate )
+				icon->stop();
+
+			delete icon;
+			icon = 0;
+		}
+#endif
+	}
+
+	void update()
+	{
+#ifndef WIDGET_PLUGIN
+		if ( icon )
+			iconUpdated( icon->pixmap() );
+#endif
+	}
+
+private slots:
+	void iconUpdated(const QPixmap &pix)
+	{
+		button->setUpdatesEnabled(FALSE);
+		//if ( textVisible )
+			button->setIconSet(pix);
+		//else
+		//	button->setPixmap(pix);
+		button->setUpdatesEnabled(TRUE);
+		button->update();
+	}
+};
+
+IconToolButton::IconToolButton(QWidget *parent, const char *name)
+: QToolButton(parent, name)
+{
+	setWFlags(getWFlags() | WRepaintNoErase);
+	d = new Private(this);
+}
+
+IconToolButton::~IconToolButton()
+{
+	delete d;
+}
+
+void IconToolButton::setIcon(const QPixmap &p)
+{
+	QToolButton::setIcon(p);
+}
+
+void IconToolButton::setIcon(const Icon *i, bool activate)
+{
+	d->activate = activate;
+	d->setIcon ((Icon *)i);
+}
+
+void IconToolButton::setIcon(const QString &name)
+{
+#ifndef WIDGET_PLUGIN
+	setIcon( IconsetFactory::iconPtr(name) );
+#else
+	d->iconName = name;
+#endif
+}
+
+const QString &IconToolButton::iconName() const
+{
+#ifndef WIDGET_PLUGIN
+	if ( d->icon )
+		return d->icon->name();
+	return QString::null;
+#else
+	return d->iconName;
+#endif
+}
+
+void IconToolButton::drawButtonLabel(QPainter *p)
+{
+	QToolButton::drawButtonLabel(p);
+}
+
+#include "iconwidget.moc"
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconwidget.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconwidget.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/iconwidget.h	(revision 38)
@@ -0,0 +1,149 @@
+/*
+ * iconwidget.h - misc. Iconset- and Icon-aware widgets
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ICONWIDGET_H
+#define ICONWIDGET_H
+
+#include <qlistbox.h>
+#include <qpushbutton.h>
+#include <qtoolbutton.h>
+
+class QPaintEvent;
+
+class Icon;
+class Iconset;
+
+class IconsetSelectItem;
+
+class IconsetSelect : public QListBox
+{
+	Q_OBJECT
+public:
+	IconsetSelect(QWidget *parent = 0, const char *name = 0);
+	~IconsetSelect();
+
+	void insert(const Iconset &); // iconsets must be inserted in following order: most prioritent first
+
+	const Iconset *iconset() const;
+
+public slots:
+	void moveItemUp();
+	void moveItemDown();
+
+private:
+	class Private;
+	Private *d;
+
+	void paintCell(QPainter *p, int row, int col);
+
+	friend class IconsetSelectItem;
+};
+
+class IconWidgetItem : public QObject, public QListBoxItem
+{
+	Q_OBJECT
+public:
+	IconWidgetItem(QListBox *parent = 0, QListBoxItem *after = 0)
+	: QListBoxItem(parent, after) {}
+
+	virtual const Iconset *iconset() const { return 0; }
+};
+
+class IconsetDisplayItem;
+
+class IconsetDisplay : public QListBox
+{
+	Q_OBJECT
+public:
+	IconsetDisplay(QWidget *parent = 0, const char *name = 0);
+	~IconsetDisplay();
+
+	void setIconset(const Iconset &);
+private:
+	class Private;
+	Private *d;
+
+	void paintCell(QPainter *p, int row, int col);
+
+	friend class IconsetDisplayItem;
+};
+
+class IconButton : public QPushButton
+{
+	Q_OBJECT
+	Q_PROPERTY( QString iconName READ iconName WRITE setIcon )
+	Q_PROPERTY( bool textVisible READ textVisible WRITE setTextVisible )
+
+	Q_OVERRIDE( QPixmap pixmap DESIGNABLE false SCRIPTABLE false )
+	Q_OVERRIDE( QIconSet iconSet DESIGNABLE false SCRIPTABLE false )
+
+public:
+	IconButton(QWidget *parent = 0, const char *name = 0);
+	~IconButton();
+
+	void setIcon(const QPixmap &);
+
+public slots:
+	void setIcon(const Icon *, bool activate = true);
+	void forceSetIcon(const Icon *, bool activate = true);
+	void setIcon(const QString &);
+	const QString &iconName() const;
+
+	void setText(const QString &);
+
+	bool textVisible() const;
+	void setTextVisible(bool);
+
+public:
+	class Private;
+private:
+	Private *d;
+
+	virtual void drawButtonLabel(QPainter *p);
+};
+
+class IconToolButton : public QToolButton
+{
+	Q_OBJECT
+	Q_PROPERTY( QString iconName READ iconName WRITE setIcon )
+
+	Q_OVERRIDE( QPixmap pixmap DESIGNABLE false SCRIPTABLE false )
+	Q_OVERRIDE( QIconSet iconSet DESIGNABLE false SCRIPTABLE false )
+
+public:
+	IconToolButton(QWidget *parent = 0, const char *name = 0);
+	~IconToolButton();
+
+	void setIcon(const QPixmap &);
+
+public slots:
+	void setIcon(const Icon *, bool activate = true);
+	void setIcon(const QString &);
+	const QString &iconName() const;
+
+public:
+	class Private;
+private:
+	Private *d;
+
+	void drawButtonLabel(QPainter *p);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabbar.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabbar.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabbar.cpp	(revision 38)
@@ -0,0 +1,426 @@
+/* Copyright (C) 2005 Kevin Smith
+		notice goes here
+	 */
+
+
+/* This file is heavily based on part of the KDE libraries
+    Copyright (C) 2003 Stephan Binner <binner@kde.org>
+    Copyright (C) 2003 Zack Rusin <zack@kde.org>
+*/
+
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qstyle.h>
+#include <qtimer.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+
+#include "psitabbar.h"
+#include "psitabwidget.h"
+
+KTabBar::KTabBar( QWidget *parent, const char *name )
+    : QTabBar( parent, name ), mReorderStartTab( -1 ), mReorderPreviousTab( -1 ),
+      mHoverCloseButtonTab( 0 ), mDragSwitchTab( 0 ), mHoverCloseButton( 0 ),
+      mHoverCloseButtonEnabled( false ), mHoverCloseButtonDelayed( true ),
+      mTabReorderingEnabled( false ), mTabCloseActivatePrevious( false )
+{
+    setAcceptDrops( true );
+    setMouseTracking( true );
+
+    mEnableCloseButtonTimer = new QTimer( this );
+    connect( mEnableCloseButtonTimer, SIGNAL( timeout() ), SLOT( enableCloseButton() ) );
+
+    mActivateDragSwitchTabTimer = new QTimer( this );
+    connect( mActivateDragSwitchTabTimer, SIGNAL( timeout() ), SLOT( activateDragSwitchTab() ) );
+
+    connect(this, SIGNAL(layoutChanged()), SLOT(onLayoutChange()));
+		dndEventDelay=5;
+}
+
+KTabBar::~KTabBar()
+{
+    //For the future
+    //delete d;
+}
+
+void KTabBar::setCloseIcon(const QIconSet& icon)
+{
+	closeIcon=icon;
+}
+
+void KTabBar::setTabEnabled( int id, bool enabled )
+{
+    QTab * t = tab( id );
+    if ( t ) {
+        if ( t->isEnabled() != enabled ) {
+            t->setEnabled( enabled );
+            QRect r( t->rect() );
+            if ( !enabled && id == currentTab() && count()>1 ) {
+                QPtrList<QTab> *tablist = tabList();
+                if ( mTabCloseActivatePrevious )
+                    t = tablist->at( count()-2 );
+                else {
+                int index = indexOf( id );
+                index += ( index+1 == count() ) ? -1 : 1;
+                t = tabAt( index );
+                }
+
+                if ( t->isEnabled() ) {
+                    r = r.unite( t->rect() );
+                    tablist->append( tablist->take( tablist->findRef( t ) ) );
+                    emit selected( t->identifier() );
+                }
+            }
+            repaint( r );
+        }
+    }
+}
+
+void KTabBar::mouseDoubleClickEvent( QMouseEvent *e )
+{
+    if( e->button() != LeftButton )
+        return;
+
+    QTab *tab = selectTab( e->pos() );
+    if( tab ) {
+        emit( mouseDoubleClick( indexOf( tab->identifier() ) ) );
+        return;
+    }
+    QTabBar::mouseDoubleClickEvent( e );
+}
+
+void KTabBar::mousePressEvent( QMouseEvent *e )
+{
+    if( e->button() == LeftButton ) {
+        mEnableCloseButtonTimer->stop();
+        mDragStart = e->pos();
+    }
+    else if( e->button() == RightButton ) {
+        QTab *tab = selectTab( e->pos() );
+        if( tab ) {
+            emit( contextMenu( indexOf( tab->identifier() ), mapToGlobal( e->pos() ) ) );
+            return;
+        }
+    }
+    QTabBar::mousePressEvent( e );
+}
+
+void KTabBar::mouseMoveEvent( QMouseEvent *e )
+{
+    if ( e->state() == LeftButton ) {
+        QTab *tab = selectTab( e->pos() );
+        if ( mDragSwitchTab && tab != mDragSwitchTab ) {
+          mActivateDragSwitchTabTimer->stop();
+          mDragSwitchTab = 0;
+        }
+
+        QPoint newPos = e->pos();
+        if( newPos.x() > mDragStart.x()+dndEventDelay || newPos.x() < mDragStart.x()-dndEventDelay ||
+            newPos.y() > mDragStart.y()+dndEventDelay || newPos.y() < mDragStart.y()-dndEventDelay )
+         {
+            if( tab ) {
+                emit( initiateDrag( indexOf( tab->identifier() ) ) );
+                return;
+           }
+       }
+    }
+    else if ( e->state() == MidButton ) {
+        if (mReorderStartTab==-1) {
+            QPoint newPos = e->pos();
+            if( newPos.x() > mDragStart.x()+dndEventDelay || newPos.x() < mDragStart.x()-dndEventDelay ||
+                newPos.y() > mDragStart.y()+dndEventDelay || newPos.y() < mDragStart.y()-dndEventDelay )
+            {
+                QTab *tab = selectTab( e->pos() );
+                if( tab && mTabReorderingEnabled ) {
+                    mReorderStartTab = indexOf( tab->identifier() );
+                    grabMouse( sizeAllCursor );
+                    return;
+                }
+            }
+        }
+        else {
+            QTab *tab = selectTab( e->pos() );
+            if( tab ) {
+                int reorderStopTab = indexOf( tab->identifier() );
+                if ( mReorderStartTab!=reorderStopTab && mReorderPreviousTab!=reorderStopTab ) {
+                    emit( moveTab( mReorderStartTab, reorderStopTab ) );
+                    mReorderPreviousTab=mReorderStartTab;
+                    mReorderStartTab=reorderStopTab;
+                    return;
+                }
+            }
+        }
+    }
+
+    if ( mHoverCloseButtonEnabled && mReorderStartTab==-1) {
+        QTab *t = selectTab( e->pos() );
+        if( t && t->iconSet() && t->isEnabled() ) {
+            QPixmap pixmap = t->iconSet()->pixmap( QIconSet::Small, QIconSet::Normal );
+#ifndef Q_WS_MAC
+            QRect rect( 0, 0, pixmap.width() + 4, pixmap.height() +4);
+#else
+			QRect rect( 0, 0, pixmap.width(), pixmap.height());
+#endif
+			int xoff = 0, yoff = 0;
+            // The additional offsets were found by try and error, TODO: find the rational behind them
+            if ( t == tab( currentTab() ) ) {
+                xoff = style().pixelMetric( QStyle::PM_TabBarTabShiftHorizontal, this ) + 3;
+                yoff = style().pixelMetric( QStyle::PM_TabBarTabShiftVertical, this ) - 4;
+            }
+            else {
+                xoff = 7;
+                yoff = 0;
+            }
+#ifdef Q_WS_MAC
+			yoff=0; //kev hacks for OSX
+			xoff=7; //also kev
+#endif
+            rect.moveLeft( t->rect().left() + 2 + xoff );
+            rect.moveTop( t->rect().center().y()-pixmap.height()/2 + yoff );
+            if ( rect.contains( e->pos() ) ) {
+                if ( mHoverCloseButton ) {
+                    if ( mHoverCloseButtonTab == t )
+                        return;
+                    mEnableCloseButtonTimer->stop();
+                    delete mHoverCloseButton;
+                }
+
+                //This is the code that shows the close button.
+				// The problems with MAC, 'fix'ed above appear here
+				mHoverCloseButton = new QPushButton( this );
+                mHoverCloseButton->setIconSet( closeIcon);
+                mHoverCloseButton->setGeometry( rect );
+                QToolTip::add(mHoverCloseButton,tr("Close this tab"));
+                mHoverCloseButton->setFlat(true);
+                mHoverCloseButton->show();
+                if ( mHoverCloseButtonDelayed ) {
+                  mHoverCloseButton->setEnabled(false);
+                  mEnableCloseButtonTimer->start( QApplication::doubleClickInterval(), true );
+                }
+                mHoverCloseButtonTab = t;
+                connect( mHoverCloseButton, SIGNAL( clicked() ), SLOT( closeButtonClicked() ) );
+                return;
+            }
+        }
+        if ( mHoverCloseButton ) {
+            mEnableCloseButtonTimer->stop();
+            delete mHoverCloseButton;
+            mHoverCloseButton = 0;
+        }
+    }
+
+    QTabBar::mouseMoveEvent( e );
+}
+
+void KTabBar::enableCloseButton()
+{
+    mHoverCloseButton->setEnabled(true);
+}
+
+void KTabBar::activateDragSwitchTab()
+{
+    QTab *tab = selectTab( mapFromGlobal( QCursor::pos() ) );
+    if ( tab && mDragSwitchTab == tab )
+    setCurrentTab( mDragSwitchTab );
+    mDragSwitchTab = 0;
+}
+
+void KTabBar::mouseReleaseEvent( QMouseEvent *e )
+{
+    if( e->button() == MidButton ) {
+        if ( mReorderStartTab==-1 ) {
+            QTab *tab = selectTab( e->pos() );
+            if( tab ) {
+                emit( mouseMiddleClick( indexOf( tab->identifier() ) ) );
+                return;
+            }
+        }
+        else {
+            releaseMouse();
+            setCursor( arrowCursor );
+            mReorderStartTab=-1;
+            mReorderPreviousTab=-1;
+        }
+    }
+    QTabBar::mouseReleaseEvent( e );
+}
+
+void KTabBar::dragMoveEvent( QDragMoveEvent *e )
+{
+    QTab *tab = selectTab( e->pos() );
+    if( tab ) {
+        bool accept = false;
+        // The receivers of the testCanDecode() signal has to adjust
+        // 'accept' accordingly.
+        emit testCanDecode( e, accept);
+        if ( accept && tab != QTabBar::tab( currentTab() ) ) {
+          mDragSwitchTab = tab;
+          mActivateDragSwitchTabTimer->start( QApplication::doubleClickInterval()*2, true );
+        }
+        e->accept( accept );
+        return;
+    }
+    e->accept( false );
+    QTabBar::dragMoveEvent( e );
+}
+
+void KTabBar::dropEvent( QDropEvent *e )
+{
+    QTab *tab = selectTab( e->pos() );
+    if( tab ) {
+        mActivateDragSwitchTabTimer->stop();
+        mDragSwitchTab = 0;
+        emit( receivedDropEvent( indexOf( tab->identifier() ) , e ) );
+        return;
+    }
+    QTabBar::dropEvent( e );
+}
+
+#ifndef QT_NO_WHEELEVENT
+void KTabBar::wheelEvent( QWheelEvent *e )
+{
+    if ( e->orientation() == Horizontal )
+        return;
+
+    emit( wheelDelta( e->delta() ) );
+}
+#endif
+
+void KTabBar::setTabColor( int id, const QColor& color )
+{
+    QTab *t = tab( id );
+    if ( t ) {
+        mTabColors.insert( id, color );
+        repaint( t->rect(), false );
+    }
+}
+
+const QColor &KTabBar::tabColor( int id  ) const
+{
+    if ( mTabColors.contains( id ) )
+        return mTabColors[id];
+
+    return colorGroup().foreground();
+}
+
+int KTabBar::insertTab( QTab *t, int index )
+{
+    int res = QTabBar::insertTab( t, index );
+
+    if ( mTabCloseActivatePrevious && count() > 2 ) {
+        QPtrList<QTab> *tablist = tabList();
+        tablist->insert( count()-2, tablist->take( tablist->findRef( t ) ) );
+    }
+
+    return res;
+}
+
+void KTabBar::removeTab( QTab *t )
+{
+    mTabColors.remove( t->identifier() );
+    QTabBar::removeTab( t );
+}
+
+void KTabBar::paintLabel( QPainter *p, const QRect& br,
+                          QTab *t, bool has_focus ) const
+{
+    QRect r = br;
+    bool selected = currentTab() == t->identifier();
+    if ( t->iconSet() ) {
+        // the tab has an iconset, draw it in the right mode
+        QIconSet::Mode mode = ( t->isEnabled() && isEnabled() )
+                                 ? QIconSet::Normal : QIconSet::Disabled;
+        if ( mode == QIconSet::Normal && has_focus )
+            mode = QIconSet::Active;
+        QPixmap pixmap = t->iconSet()->pixmap( QIconSet::Small, mode );
+        int pixw = pixmap.width();
+        int pixh = pixmap.height();
+        r.setLeft( r.left() + pixw + 4 );
+        r.setRight( r.right() + 2 );
+
+        int inactiveXShift = style().pixelMetric( QStyle::PM_TabBarTabShiftHorizontal, this );
+        int inactiveYShift = style().pixelMetric( QStyle::PM_TabBarTabShiftVertical, this );
+
+        int right = t->text().isEmpty() ? br.right() - pixw : br.left() + 2;
+
+        p->drawPixmap( right + (selected ? 0 : inactiveXShift),
+                       br.center().y() - pixh / 2 + (selected ? 0 : inactiveYShift),
+                       pixmap );
+    }
+
+    QStyle::SFlags flags = QStyle::Style_Default;
+
+    if ( isEnabled() && t->isEnabled() )
+        flags |= QStyle::Style_Enabled;
+    if ( has_focus )
+	{
+        flags |= QStyle::Style_HasFocus;
+	}
+
+    QColorGroup cg( colorGroup() );
+    if ( mTabColors.contains( t->identifier() ) )
+        cg.setColor( QColorGroup::Foreground, mTabColors[t->identifier()] );
+
+    style().drawControl( QStyle::CE_TabBarLabel, p, this, r,
+                             t->isEnabled() ? cg : palette().disabled(),
+                             flags, QStyleOption(t) );
+}
+
+bool KTabBar::isTabReorderingEnabled() const
+{
+    return mTabReorderingEnabled;
+}
+
+void KTabBar::setTabReorderingEnabled( bool on )
+{
+    mTabReorderingEnabled = on;
+}
+
+bool KTabBar::tabCloseActivatePrevious() const
+{
+    return mTabCloseActivatePrevious;
+}
+
+void KTabBar::setTabCloseActivatePrevious( bool on )
+{
+    mTabCloseActivatePrevious = on;
+}
+
+void KTabBar::closeButtonClicked()
+{
+    emit closeRequest( indexOf( mHoverCloseButtonTab->identifier() ) );
+}
+
+void KTabBar::setHoverCloseButton( bool button )
+{
+    mHoverCloseButtonEnabled = button;
+    if ( !button )
+        onLayoutChange();
+}
+
+bool KTabBar::hoverCloseButton() const
+{
+    return mHoverCloseButtonEnabled;
+}
+
+void KTabBar::setHoverCloseButtonDelayed( bool delayed )
+{
+    mHoverCloseButtonDelayed = delayed;
+}
+
+bool KTabBar::hoverCloseButtonDelayed() const
+{
+    return mHoverCloseButtonDelayed;
+}
+
+void KTabBar::onLayoutChange()
+{
+    mEnableCloseButtonTimer->stop();
+    delete mHoverCloseButton;
+    mHoverCloseButton = 0;
+    mHoverCloseButtonTab = 0;
+    mActivateDragSwitchTabTimer->stop();
+    mDragSwitchTab = 0;
+}
+
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabbar.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabbar.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabbar.h	(revision 38)
@@ -0,0 +1,113 @@
+/* This file is part of the KDE libraries
+    Copyright (C) 2003 Stephan Binner <binner@kde.org>
+    Copyright (C) 2003 Zack Rusin <zack@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+    Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KTABBAR_H
+#define KTABBAR_H
+
+#include <qtabbar.h>
+#include <qiconset.h>
+
+class QTimer;
+class QPushButton;
+class KTabBarPrivate;
+
+class KTabBar: public QTabBar
+{
+    Q_OBJECT
+
+public:
+    KTabBar( QWidget* parent=0, const char* name=0 );
+    virtual ~KTabBar();
+
+		void setCloseIcon(const QIconSet&);
+		
+    virtual void setTabEnabled( int, bool );
+
+    const QColor &tabColor( int ) const;
+    void setTabColor( int, const QColor& );
+
+    virtual int insertTab( QTab *, int index = -1 );
+    virtual void removeTab( QTab * );
+
+    void setTabReorderingEnabled( bool enable );
+    bool isTabReorderingEnabled() const;
+
+    void setHoverCloseButton( bool );
+    bool hoverCloseButton() const;
+
+    void setHoverCloseButtonDelayed( bool );
+    bool hoverCloseButtonDelayed() const;
+
+    void setTabCloseActivatePrevious( bool );
+    bool tabCloseActivatePrevious() const;
+
+signals:
+    void contextMenu( int, const QPoint & );
+    void mouseDoubleClick( int );
+    void mouseMiddleClick( int );
+    void initiateDrag( int );
+    void testCanDecode(const QDragMoveEvent *e, bool &accept /* result */);
+    void receivedDropEvent( int, QDropEvent * );
+    void moveTab( int, int );
+    void closeRequest( int );
+#ifndef QT_NO_WHEELEVENT
+    void wheelDelta( int );
+#endif
+
+protected:
+    virtual void mouseDoubleClickEvent( QMouseEvent *e );
+    virtual void mousePressEvent( QMouseEvent *e );
+    virtual void mouseMoveEvent( QMouseEvent *e );
+    virtual void mouseReleaseEvent( QMouseEvent *e );
+#ifndef QT_NO_WHEELEVENT
+    virtual void wheelEvent( QWheelEvent *e );
+#endif
+
+    virtual void dragMoveEvent( QDragMoveEvent *e );
+    virtual void dropEvent( QDropEvent *e );
+
+    virtual void paintLabel( QPainter*, const QRect&, QTab*, bool ) const;
+
+protected slots:
+    virtual void closeButtonClicked();
+    virtual void onLayoutChange();
+    virtual void enableCloseButton();
+    virtual void activateDragSwitchTab();
+
+private:
+    QPoint mDragStart;
+    int mReorderStartTab;
+    int mReorderPreviousTab;
+    QMap<int, QColor> mTabColors;
+    QTab *mHoverCloseButtonTab, *mDragSwitchTab;
+    QPushButton *mHoverCloseButton;
+    QTimer *mEnableCloseButtonTimer, *mActivateDragSwitchTabTimer;
+
+    bool mHoverCloseButtonEnabled;
+    bool mHoverCloseButtonDelayed;
+    bool mTabReorderingEnabled;
+    bool mTabCloseActivatePrevious;
+		
+		int dndEventDelay;
+		QIconSet closeIcon;
+    KTabBarPrivate * d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabwidget.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabwidget.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabwidget.cpp	(revision 38)
@@ -0,0 +1,513 @@
+/* Kevin Smith 2005
+	 *insert new header here
+
+	 */
+
+/* This file is heavily based upon part of the KDE libraries
+    Copyright (C) 2003 Stephan Binner <binner@kde.org>
+    Copyright (C) 2003 Zack Rusin <zack@kde.org>
+*/
+
+#include <qapplication.h>
+#include <qstyle.h>
+
+#include "psitabwidget.h"
+#include "psitabbar.h"
+
+class KTabWidgetPrivate {
+public:
+    bool m_automaticResizeTabs;
+    int m_maxLength;
+    int m_minLength;
+    unsigned int m_CurrentMaxLength;
+
+    //holds the full names of the tab, otherwise all we
+    //know about is the shortened name
+    QStringList m_tabNames;
+
+    KTabWidgetPrivate() {
+        m_automaticResizeTabs = false;
+        m_maxLength = 30;
+        m_minLength = 3;
+        m_CurrentMaxLength = m_minLength;
+    }
+};
+
+KTabWidget::KTabWidget( QWidget *parent, const char *name, WFlags f )
+    : QTabWidget( parent, name, f )
+{
+    d = new KTabWidgetPrivate;
+    setTabBar( new KTabBar(this, "tabbar") );
+    setAcceptDrops( true );
+
+    connect(tabBar(), SIGNAL(contextMenu( int, const QPoint & )), SLOT(contextMenu( int, const QPoint & )));
+    connect(tabBar(), SIGNAL(mouseDoubleClick( int )), SLOT(mouseDoubleClick( int )));
+    connect(tabBar(), SIGNAL(mouseMiddleClick( int )), SLOT(mouseMiddleClick( int )));
+    connect(tabBar(), SIGNAL(initiateDrag( int )), SLOT(initiateDrag( int )));
+    connect(tabBar(), SIGNAL(testCanDecode(const QDragMoveEvent *, bool & )), SIGNAL(testCanDecode(const QDragMoveEvent *, bool & )));
+    connect(tabBar(), SIGNAL(receivedDropEvent( int, QDropEvent * )), SLOT(receivedDropEvent( int, QDropEvent * )));
+    connect(tabBar(), SIGNAL(moveTab( int, int )), SLOT(moveTab( int, int )));
+    connect(tabBar(), SIGNAL(closeRequest( int )), SLOT(closeRequest( int )));
+#ifndef QT_NO_WHEELEVENT
+    connect(tabBar(), SIGNAL(wheelDelta( int )), SLOT(wheelDelta( int )));
+#endif
+}
+
+KTabWidget::~KTabWidget()
+{
+    delete d;
+}
+
+void KTabWidget::setCloseIcon(const QIconSet& icon)
+{
+	static_cast<KTabBar*>(tabBar())->setCloseIcon(icon);
+}
+
+void KTabWidget::insertTab( QWidget *child, const QString &label, int index )
+{
+    QTabWidget::insertTab( child, label, index );
+}
+
+void KTabWidget::insertTab( QWidget *child, const QIconSet& iconset, const QString &label, int index )
+{
+    QTabWidget::insertTab( child, iconset, label, index );
+}
+
+void KTabWidget::insertTab( QWidget *child, QTab *tab, int index )
+{
+    QTabWidget::insertTab( child, tab, index);
+    if ( d->m_automaticResizeTabs ) {
+        if ( index < 0 || index >= count() ) {
+            d->m_tabNames.append( tab->text() );
+            resizeTabs( d->m_tabNames.count()-1 );
+        }
+        else {
+            d->m_tabNames.insert( d->m_tabNames.at( index ), tab->text() );
+            resizeTabs( index );
+        }
+    }
+}
+
+void KTabWidget::setTabBarHidden( bool hide )
+{
+    QWidget *rightcorner = this->cornerWidget( TopRight );
+    QWidget *leftcorner = this->cornerWidget( TopLeft );
+
+    if ( hide ) {
+        if ( leftcorner ) leftcorner->hide();
+        if ( rightcorner ) rightcorner->hide();
+        tabBar()->hide();
+    } else {
+        tabBar()->show();
+        if ( leftcorner ) leftcorner->show();
+        if ( rightcorner ) rightcorner->show();
+    }
+}
+
+bool KTabWidget::isTabBarHidden() const
+{
+    return !( tabBar()->isVisible() );
+}
+
+void KTabWidget::setTabColor( QWidget *w, const QColor& color )
+{
+    QTab *t = tabBar()->tabAt( indexOf( w ) );
+    if (t) {
+        static_cast<KTabBar*>(tabBar())->setTabColor( t->identifier(), color );
+    }
+}
+
+QColor KTabWidget::tabColor( QWidget *w ) const
+{
+    QTab *t = tabBar()->tabAt( indexOf( w ) );
+    if (t) {
+        return static_cast<KTabBar*>(tabBar())->tabColor( t->identifier() );
+    } else {
+        return QColor();
+    }
+}
+
+void KTabWidget::setTabReorderingEnabled( bool on)
+{
+    static_cast<KTabBar*>(tabBar())->setTabReorderingEnabled( on );
+}
+
+bool KTabWidget::isTabReorderingEnabled() const
+{
+    return static_cast<KTabBar*>(tabBar())->isTabReorderingEnabled();
+}
+
+void KTabWidget::setTabCloseActivatePrevious( bool previous)
+{
+    static_cast<KTabBar*>(tabBar())->setTabCloseActivatePrevious( previous );
+}
+
+bool KTabWidget::tabCloseActivatePrevious() const
+{
+    return static_cast<KTabBar*>(tabBar())->tabCloseActivatePrevious();
+}
+
+unsigned int KTabWidget::tabBarWidthForMaxChars( uint maxLength )
+{
+	Q_UNUSED(maxLength);
+	int hframe, overlap;
+    hframe  = tabBar()->style().pixelMetric( QStyle::PM_TabBarTabHSpace, tabBar() );
+    overlap = tabBar()->style().pixelMetric( QStyle::PM_TabBarTabOverlap, tabBar() );
+
+    QFontMetrics fm = tabBar()->fontMetrics();
+    int x = 0;
+    for( int i=0; i < count(); ++i ) {
+        QString newTitle = d->m_tabNames[ i ];
+        //newTitle = KStringHandler::rsqueeze( newTitle, maxLength ).leftJustify( d->m_minLength, ' ' );
+
+        QTab* tab = tabBar()->tabAt( i );
+        int lw = fm.width( newTitle );
+        int iw = 0;
+        if ( tab->iconSet() )
+          iw = tab->iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4;
+        x += ( tabBar()->style().sizeFromContents( QStyle::CT_TabBarTab, this,
+                   QSize( QMAX( lw + hframe + iw, QApplication::globalStrut().width() ), 0 ),
+                   QStyleOption( tab ) ) ).width();
+    }
+    return x;
+}
+
+void KTabWidget::changeTab( QWidget *w, const QString &label )
+{
+    QTabWidget::changeTab( w, label );
+    if ( d->m_automaticResizeTabs ) {
+        int index = indexOf( w );
+        if ( index != -1 ) {
+            d->m_tabNames[ index ] = label;
+            resizeTabs( index );
+        }
+    }
+}
+
+void KTabWidget::changeTab( QWidget *w, const QIconSet &iconset, const QString &label )
+{
+    QTabWidget::changeTab( w, iconset, label );
+    if ( d->m_automaticResizeTabs ) {
+        int index = indexOf( w );
+        if ( index != -1 ) {
+            d->m_tabNames[ index ] = label;
+            resizeTabs( index );
+        }
+    }
+}
+
+QString KTabWidget::label( int index ) const
+{
+    if ( d->m_automaticResizeTabs ) {
+        if ( index >= 0 && index < count() )
+            return d->m_tabNames[ index ];
+        else
+            return QString::null;
+    }
+    else
+        return QTabWidget::label( index );
+}
+
+QString KTabWidget::tabLabel( QWidget * w ) const
+{
+    if ( d->m_automaticResizeTabs ) {
+        int index = indexOf( w );
+        if ( index == -1 )
+            return QString::null;
+        else
+            return d->m_tabNames[ index ];
+    }
+    else
+        return QTabWidget::tabLabel( w );
+}
+
+void KTabWidget::setTabLabel( QWidget *w, const QString &l )
+{
+    QTabWidget::setTabLabel( w, l );
+    if ( d->m_automaticResizeTabs ) {
+        int index = indexOf( w );
+        if ( index != -1 ) {
+            d->m_tabNames[ index ] = l;
+            resizeTabs( index );
+        }
+  }
+}
+
+void KTabWidget::resizeTabs( int changeTabIndex )
+{
+    uint newMaxLength;
+    if ( d->m_automaticResizeTabs ) {
+        // Calculate new max length
+        newMaxLength=d->m_maxLength;
+        uint lcw=0, rcw=0;
+
+        int tabBarHeight = tabBar()->sizeHint().height();
+        if ( cornerWidget( TopLeft ) && cornerWidget( TopLeft )->isVisible() )
+            lcw = QMAX( cornerWidget( TopLeft )->width(), tabBarHeight );
+        if ( cornerWidget( TopRight ) && cornerWidget( TopRight )->isVisible() )
+            rcw = QMAX( cornerWidget( TopRight )->width(), tabBarHeight );
+
+        uint maxTabBarWidth = width() - lcw - rcw;
+
+        for ( ; newMaxLength > (uint)d->m_minLength; newMaxLength-- ) {
+            if ( tabBarWidthForMaxChars( newMaxLength ) < maxTabBarWidth )
+                break;
+        }
+    }
+    else
+        newMaxLength = 4711;
+
+    // Update hinted or all tabs
+    if ( d->m_CurrentMaxLength != newMaxLength ) {
+        d->m_CurrentMaxLength = newMaxLength;
+        for( int i = 0; i < count(); ++i )
+            updateTab( i );
+    }
+    else if ( changeTabIndex != -1 )
+        updateTab( changeTabIndex );
+}
+
+void KTabWidget::updateTab( int index )
+{
+    QString title = d->m_automaticResizeTabs ? d->m_tabNames[ index ] : QTabWidget::label( index );
+    removeTabToolTip( page( index ) );
+    if ( title.length() > d->m_CurrentMaxLength )
+        setTabToolTip( page( index ), title );
+
+    //title = KStringHandler::rsqueeze( title, d->m_CurrentMaxLength ).leftJustify( d->m_minLength, ' ' );
+    title.replace( '&', "&&" );
+
+    if ( QTabWidget::label( index ) != title )
+        QTabWidget::setTabLabel( page( index ), title );
+}
+
+void KTabWidget::dragMoveEvent( QDragMoveEvent *e )
+{
+    if ( isEmptyTabbarSpace( e->pos() ) ) {
+        bool accept = false;
+        // The receivers of the testCanDecode() signal has to adjust
+        // 'accept' accordingly.
+        emit testCanDecode( e, accept);
+        e->accept( accept );
+        return;
+    }
+    e->accept( false );
+    QTabWidget::dragMoveEvent( e );
+}
+
+void KTabWidget::dropEvent( QDropEvent *e )
+{
+    if ( isEmptyTabbarSpace( e->pos() ) ) {
+        emit ( receivedDropEvent( e ) );
+        return;
+    }
+    QTabWidget::dropEvent( e );
+}
+
+#ifndef QT_NO_WHEELEVENT
+void KTabWidget::wheelEvent( QWheelEvent *e )
+{
+    if ( e->orientation() == Horizontal )
+        return;
+
+    if ( isEmptyTabbarSpace( e->pos() ) )
+        wheelDelta( e->delta() );
+    else
+        e->ignore();
+}
+
+void KTabWidget::wheelDelta( int delta )
+{
+    if ( count() < 2 )
+        return;
+
+    int page = currentPageIndex();
+    if ( delta < 0 )
+         page = (page + 1) % count();
+    else {
+        page--;
+        if ( page < 0 )
+            page = count() - 1;
+    }
+    setCurrentPage( page );
+}
+#endif
+
+void KTabWidget::mouseDoubleClickEvent( QMouseEvent *e )
+{
+    if( e->button() != LeftButton )
+        return;
+
+    if ( isEmptyTabbarSpace( e->pos() ) ) {
+        emit( mouseDoubleClick() );
+        return;
+    }
+    QTabWidget::mouseDoubleClickEvent( e );
+}
+
+void KTabWidget::mousePressEvent( QMouseEvent *e )
+{
+    if ( e->button() == RightButton ) {
+        if ( isEmptyTabbarSpace( e->pos() ) ) {
+            emit( contextMenu( mapToGlobal( e->pos() ) ) );
+            return;
+        }
+    } else if ( e->button() == MidButton ) {
+        if ( isEmptyTabbarSpace( e->pos() ) ) {
+            emit( mouseMiddleClick() );
+            return;
+        }
+    }
+    QTabWidget::mousePressEvent( e );
+}
+
+void KTabWidget::receivedDropEvent( int index, QDropEvent *e )
+{
+    emit( receivedDropEvent( page( index ), e ) );
+}
+
+void KTabWidget::initiateDrag( int index )
+{
+    emit( initiateDrag( page( index ) ) );
+}
+
+void KTabWidget::contextMenu( int index, const QPoint &p )
+{
+    emit( contextMenu( page( index ), p ) );
+}
+
+void KTabWidget::mouseDoubleClick( int index )
+{
+    emit( mouseDoubleClick( page( index ) ) );
+}
+
+void KTabWidget::mouseMiddleClick( int index )
+{
+    emit( mouseMiddleClick( page( index ) ) );
+}
+
+void KTabWidget::moveTab( int from, int to )
+{
+    QString tablabel = label( from );
+    QWidget *w = page( from );
+    QColor color = tabColor( w );
+    QIconSet tabiconset = tabIconSet( w );
+    QString tabtooltip = tabToolTip( w );
+    bool current = ( w == currentPage() );
+    bool enabled = isTabEnabled( w );
+    blockSignals(true);
+    removePage( w );
+
+    // Work-around kmdi brain damage which calls showPage() in insertTab()
+    QTab * t = new QTab();
+    t->setText(tablabel);
+    QTabWidget::insertTab( w, t, to );
+    if ( d->m_automaticResizeTabs ) {
+        if ( to < 0 || to >= count() )
+            d->m_tabNames.append( QString::null );
+        else
+            d->m_tabNames.insert( d->m_tabNames.at( to ), QString::null );
+    }
+
+    w = page( to );
+    changeTab( w, tabiconset, tablabel );
+    setTabToolTip( w, tabtooltip );
+    setTabColor( w, color );
+    if ( current )
+        showPage( w );
+    setTabEnabled( w, enabled );
+    blockSignals(false);
+
+    emit ( movedTab( from, to ) );
+}
+
+void KTabWidget::removePage( QWidget * w ) {
+    if ( d->m_automaticResizeTabs ) {
+        int index = indexOf( w );
+        if ( index != -1 )
+            d->m_tabNames.remove( d->m_tabNames.at( index ) );
+    }
+    QTabWidget::removePage( w );
+    if ( d->m_automaticResizeTabs )
+        resizeTabs();
+}
+
+
+bool KTabWidget::isEmptyTabbarSpace( const QPoint &p ) const
+{
+    QPoint point( p );
+    QSize size( tabBar()->sizeHint() );
+    if ( ( tabPosition()==Top && point.y()< size.height() ) || ( tabPosition()==Bottom && point.y()>(height()-size.height() ) ) ) {
+        QWidget *rightcorner = cornerWidget( TopRight );
+        if ( rightcorner ) {
+            if ( point.x()>=width()-rightcorner->width() )
+                return false;
+        }
+        QWidget *leftcorner = cornerWidget( TopLeft );
+        if ( leftcorner ) {
+            if ( point.x()<=leftcorner->width() )
+                return false;
+            point.setX( point.x()-size.height() );
+        }
+        if ( tabPosition()==Bottom )
+            point.setY( point.y()-( height()-size.height() ) );
+        QTab *tab = tabBar()->selectTab( point);
+        if( !tab )
+            return true;
+    }
+    return false;
+}
+
+void KTabWidget::setHoverCloseButton( bool button )
+{
+    static_cast<KTabBar*>(tabBar())->setHoverCloseButton( button );
+}
+
+bool KTabWidget::hoverCloseButton() const
+{
+    return static_cast<KTabBar*>(tabBar())->hoverCloseButton();
+}
+
+void KTabWidget::setHoverCloseButtonDelayed( bool delayed )
+{
+    static_cast<KTabBar*>(tabBar())->setHoverCloseButtonDelayed( delayed );
+}
+
+bool KTabWidget::hoverCloseButtonDelayed() const
+{
+    return static_cast<KTabBar*>(tabBar())->hoverCloseButtonDelayed();
+}
+
+void KTabWidget::setAutomaticResizeTabs( bool enabled )
+{
+    if ( d->m_automaticResizeTabs==enabled )
+        return;
+
+    d->m_automaticResizeTabs = enabled;
+    if ( enabled ) {
+        d->m_tabNames.clear();
+        for( int i = 0; i < count(); ++i )
+            d->m_tabNames.append( tabBar()->tabAt( i )->text() );
+    }
+    else
+        for( int i = 0; i < count(); ++i )
+            tabBar()->tabAt( i )->setText( d->m_tabNames[ i ] );
+    resizeTabs();
+}
+
+bool KTabWidget::automaticResizeTabs() const
+{
+    return d->m_automaticResizeTabs;
+}
+
+void KTabWidget::closeRequest( int index )
+{
+    emit( closeRequest( page( index ) ) );
+}
+
+void KTabWidget::resizeEvent( QResizeEvent *e )
+{
+    QTabWidget::resizeEvent( e );
+    resizeTabs();
+}
+
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabwidget.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabwidget.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitabwidget.h	(revision 38)
@@ -0,0 +1,281 @@
+//Kevin Smith 2005
+
+/* This file is heavily based upon part of the KDE libraries
+    Copyright (C) 2003 Stephan Binner <binner@kde.org>
+    Copyright (C) 2003 Zack Rusin <zack@kde.org>
+*/
+
+#ifndef PSITABWIDGET_H
+#define PSITABWIDGET_H
+
+#include <qtabwidget.h>
+#include <qstringlist.h>
+
+class KTabWidgetPrivate;
+class QIconSet;
+/**
+ * \brief A widget containing multiple tabs
+ *
+ * @since 0.10
+ */
+class KTabWidget : public QTabWidget
+{
+    Q_OBJECT
+    Q_PROPERTY( bool tabReorderingEnabled READ isTabReorderingEnabled WRITE setTabReorderingEnabled )
+    Q_PROPERTY( bool hoverCloseButton READ hoverCloseButton WRITE setHoverCloseButton )
+    Q_PROPERTY( bool hoverCloseButtonDelayed READ hoverCloseButtonDelayed WRITE setHoverCloseButtonDelayed )
+    Q_PROPERTY( bool tabCloseActivatePrevious READ tabCloseActivatePrevious WRITE setTabCloseActivatePrevious )
+    Q_PROPERTY( bool automaticResizeTabs READ automaticResizeTabs WRITE setAutomaticResizeTabs )
+
+public:
+    KTabWidget( QWidget *parent = 0, const char *name = 0, WFlags f = 0 );
+    /**
+     * Destructor.
+     */
+    virtual ~KTabWidget();
+    /*!
+      Set the tab of the given widget to \a color.
+    */
+    void setTabColor( QWidget *, const QColor& color );
+    QColor tabColor( QWidget * ) const;
+
+    /*!
+      Returns true if tab ordering with the middle mouse button
+      has been enabled.
+    */
+    bool isTabReorderingEnabled() const;
+
+    /*!
+      Returns true if the close button is shown on tabs
+      when mouse is hovering over them.
+    */
+    bool hoverCloseButton() const;
+
+    /*!
+      Returns true if the close button is shown on tabs
+      after a delay.
+    */
+    bool hoverCloseButtonDelayed() const;
+
+    /*!
+      Returns true if closing the current tab activates the previous
+      actice tab instead of the one to the right.
+      @since 3.3
+    */
+    bool tabCloseActivatePrevious() const;
+
+    /*!
+      Returns true if calling setTitle() will resize tabs
+      to the width of the tab bar.
+      @since 3.4
+    */
+    bool automaticResizeTabs() const;
+
+    /*!
+      If \a hide is true, the tabbar is hidden along with any corner
+      widgets.
+      @since 3.4
+    */
+    void setTabBarHidden( bool hide );
+
+    /*!
+      Return true if the tabbar is hidden.
+      @since 3.4
+    */
+    bool isTabBarHidden() const;
+
+		/*!
+			Sets the close icon used on the tabbar
+			@since 0.10
+		*/
+		void setCloseIcon(const QIconSet&);
+		
+    /*!
+      Reimplemented for internal reasons.
+     */
+    virtual void insertTab( QWidget *, const QString &, int index = -1 );
+
+    /*!
+      Reimplemented for internal reasons.
+     */
+    virtual void insertTab( QWidget *child, const QIconSet& iconset,
+                            const QString &label, int index = -1 );
+    /*!
+      Reimplemented for internal reasons.
+    */
+    virtual void insertTab( QWidget *, QTab *, int index = -1 );
+
+    /*!
+      Reimplemented for internal reasons.
+    */
+    void changeTab( QWidget *, const QString & );
+
+    /*!
+      Reimplemented for internal reasons.
+    */
+    void changeTab( QWidget *child, const QIconSet& iconset, const QString &label );
+
+    /*!
+      Reimplemented for internal reasons.
+    */
+    QString label( int ) const;
+
+    /*!
+      Reimplemented for internal reasons.
+    */
+    QString tabLabel( QWidget * ) const;
+
+    /*!
+      Reimplemented for internal reasons.
+    */
+    void setTabLabel( QWidget *, const QString & );
+
+public slots:
+    /*!
+      Move a widget's tab from first to second specified index and emit
+      signal movedTab( int, int ) afterwards.
+    */
+    virtual void moveTab( int, int );
+
+    /*!
+      Removes the widget, reimplemented for
+      internal reasons (keeping labels in sync).
+    */
+    virtual void removePage ( QWidget * w );
+
+    /*!
+      If \a enable is true, tab reordering with middle button will be enabled.
+
+      Note that once enabled you shouldn't rely on previously queried
+      currentPageIndex() or indexOf( QWidget * ) values anymore.
+
+      You can connect to signal movedTab(int, int) which will notify
+      you from which index to which index a tab has been moved.
+    */
+    void setTabReorderingEnabled( bool enable );
+
+    /*!
+      If \a enable is true, a close button will be shown on mouse hover
+      over tab icons which will emit signal closeRequest( QWidget * )
+      when pressed.
+    */
+    void setHoverCloseButton( bool enable );
+
+    /*!
+      If \a delayed is true, a close button will be shown on mouse hover
+      over tab icons after mouse double click delay else immediately.
+    */
+    void setHoverCloseButtonDelayed( bool delayed );
+
+    /*!
+      If \a delayed is true, closing the current tab activates the
+      previous active tab instead of the one to the right.
+      @since 3.3
+    */
+    void setTabCloseActivatePrevious( bool previous );
+
+    /*!
+      If \a enable is true, tabs will be resized to the width of the tab bar.
+
+      Does not work reliable with "QTabWidget* foo=new KTabWidget()" and if
+      you change tabs via the tabbar or by accessing tabs directly.
+      @since 3.4
+    */
+    void setAutomaticResizeTabs( bool enable );
+
+signals:
+    /*!
+      Connect to this and set accept to true if you can and want to decode the event.
+    */
+    void testCanDecode(const QDragMoveEvent *e, bool &accept /* result */);
+
+    /*!
+      Received an event in the empty space beside tabbar. Usually creates a new tab.
+      This signal is only possible after testCanDecode and positive accept result.
+    */
+    void receivedDropEvent( QDropEvent * );
+
+    /*!
+      Received an drop event on given widget's tab.
+      This signal is only possible after testCanDecode and positive accept result.
+    */
+    void receivedDropEvent( QWidget *, QDropEvent * );
+
+    /*!
+      Request to start a drag operation on the given tab.
+    */
+    void initiateDrag( QWidget * );
+
+    /*!
+      The right mouse button was pressed over empty space besides tabbar.
+    */
+    void contextMenu( const QPoint & );
+
+    /*!
+      The right mouse button was pressed over a widget.
+    */
+    void contextMenu( QWidget *, const QPoint & );
+
+    /*!
+      A tab was moved from first to second index. This signal is only
+      possible after you have called setTabReorderingEnabled( true ).
+    */
+    void movedTab( int, int );
+
+    /*!
+      A double left mouse button click was performed over empty space besides tabbar.
+      @since 3.3
+    */
+    void mouseDoubleClick();
+
+    /*!
+      A double left mouse button click was performed over the widget.
+    */
+    void mouseDoubleClick( QWidget * );
+
+    /*!
+      A middle mouse button click was performed over empty space besides tabbar.
+    */
+    void mouseMiddleClick();
+
+    /*!
+      A middle mouse button click was performed over the widget.
+    */
+    void mouseMiddleClick( QWidget * );
+
+    /*!
+      The close button of a widget's tab was clicked. This signal is
+      only possible after you have called setHoverCloseButton( true ).
+    */
+    void closeRequest( QWidget * );
+
+protected:
+    virtual void mouseDoubleClickEvent( QMouseEvent *e );
+    virtual void mousePressEvent( QMouseEvent * );
+    virtual void dragMoveEvent( QDragMoveEvent * );
+    virtual void dropEvent( QDropEvent * );
+    unsigned int tabBarWidthForMaxChars( uint maxLength );
+#ifndef QT_NO_WHEELEVENT
+    virtual void wheelEvent( QWheelEvent *e );
+#endif
+    virtual void resizeEvent( QResizeEvent * );
+
+protected slots:
+    virtual void receivedDropEvent( int, QDropEvent * );
+    virtual void initiateDrag( int );
+    virtual void contextMenu( int, const QPoint & );
+    virtual void mouseDoubleClick( int );
+    virtual void mouseMiddleClick( int );
+    virtual void closeRequest( int );
+#ifndef QT_NO_WHEELEVENT
+    virtual void wheelDelta( int );
+#endif
+
+private:
+    bool isEmptyTabbarSpace( const QPoint & )  const;
+    void resizeTabs( int changedTabIndex = -1 );
+    void updateTab( int index );
+    KTabWidgetPrivate *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitextview.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitextview.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitextview.cpp	(revision 38)
@@ -0,0 +1,483 @@
+/*
+ * psitextview.cpp - Icon-aware QTextView subclass widget
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "psitextview.h"
+
+#include <qpopupmenu.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qtooltip.h>
+
+#include <qstylesheet.h>
+#include <private/qrichtext_p.h>
+
+#ifndef WIDGET_PLUGIN
+#include "iconset.h"
+#else
+class Icon;
+class Iconset;
+#endif
+
+//----------------------------------------------------------------------------
+// URLObject - helper class for handling links
+//----------------------------------------------------------------------------
+
+class URLObject : public QObject
+{
+	Q_OBJECT
+
+public:
+	URLObject(QObject *parent = 0)
+	: QObject(qApp) { Q_UNUSED(parent); }
+
+	QPopupMenu *createPopupMenu(const QString &lnk);
+
+private:
+	QString link;
+
+	QString copyString(QString from)
+	{
+		QString l = from;
+
+		int colon = l.find(':');
+		if ( colon == -1 )
+			colon = 0;
+		QString service = l.left( colon );
+
+		if ( service == "mailto" || service == "jabber" || service == "jid" || service == "xmpp" ) {
+			if ( colon > -1 )
+				l = l.mid( colon + 1 );
+
+			while ( l[0] == '/' )
+				l = l.mid( 1 );
+		}
+
+		return l;
+	}
+
+signals:
+	void openURL(QString);
+
+public slots:
+	void popupAction(QString lnk) {
+		emit openURL(lnk);
+	}
+
+	void popupAction() {
+		popupAction(link);
+	}
+
+	void popupCopy(QString lnk) {
+		QApplication::clipboard()->setText( copyString(lnk), QClipboard::Clipboard );
+		if(QApplication::clipboard()->supportsSelection())
+			QApplication::clipboard()->setText( copyString(lnk), QClipboard::Selection );
+	}
+
+	void popupCopy() {
+		popupCopy(link);
+	}
+};
+
+static URLObject *urlObject = 0;
+
+QPopupMenu *URLObject::createPopupMenu(const QString &lnk)
+{
+	link = lnk;
+	if ( link.isEmpty() )
+		return 0;
+
+	int colon = link.find(':');
+	if ( colon == -1 )
+		colon = 0;
+	QString service = link.left( colon );
+
+	QString action = "ERROR";
+	QString iconName;
+
+	if ( service == "mailto" ) {
+		action = URLLabel::tr("Open mail composer");
+		iconName = "psi/email";
+	}
+	else if ( service == "jabber" || service == "jid" || service == "xmpp" ) {
+		// TODO: need more actions to jabber item. Ex: "add to roster", "send message"
+		action = URLLabel::tr("Add to Roster");
+		iconName = "psi/add";
+	}
+	else { //if ( service == "http" || service == "https" || service.isEmpty() ) {
+		action = URLLabel::tr("Open web browser");
+		iconName = "psi/www";
+	}
+
+#ifndef WIDGET_PLUGIN
+	const Icon *icon = 0;
+	if ( !iconName.isEmpty() )
+		icon = IconsetFactory::iconPtr( iconName );
+#endif
+
+	QPopupMenu *m = new QPopupMenu;
+
+#ifndef WIDGET_PLUGIN
+	if ( icon )
+		m->insertItem(/*QIconSet(*/ *icon /*)*/, action, this, SLOT(popupAction()));
+	else
+#endif
+		m->insertItem(action, this, SLOT(popupAction()));
+	m->insertItem(URLLabel::tr("Copy location"), this, SLOT(popupCopy()));
+
+	return m;
+}
+
+//----------------------------------------------------------------------------
+// TextIcon - helper class for PsiTextView
+//----------------------------------------------------------------------------
+
+class TextIcon : public QObject, public QTextCustomItem
+{
+	Q_OBJECT
+
+public:
+	TextIcon(QTextDocument *p, const QMap<QString, QString> &attr, const QString &context, QMimeSourceFactory &factory);
+	~TextIcon();
+
+	void adjustToPainter(QPainter *);
+	void draw(QPainter *p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup &cg, bool selected);
+	QString richText() const;
+
+	Placement placement() const { return place; }
+	int minimumWidth() const { return width; }
+
+private slots:
+	void iconUpdated(const QPixmap &);
+
+private:
+	Placement place;
+	int tmpwidth, tmpheight;
+	QMap<QString, QString> attributes;
+	QString imgId;
+	Icon *icon;
+};
+
+TextIcon::TextIcon(QTextDocument *p, const QMap<QString, QString> &attr, const QString &context, QMimeSourceFactory &factory)
+: QObject(0, 0), QTextCustomItem (p)
+{
+	Q_UNUSED(context);
+	Q_UNUSED(factory);
+
+	width = height = 0;
+
+	icon = 0;
+	QString iconName = attr["name"];
+
+	if ( iconName.isEmpty() )
+		iconName = attr["src"];
+
+	if ( iconName.isEmpty() )
+		iconName = attr["source"];
+
+#ifndef WIDGET_PLUGIN
+	if ( !iconName.isEmpty() ) {
+		icon = (Icon *)IconsetFactory::iconPtr( iconName );
+		if ( icon )
+			icon = new Icon(*icon);
+
+		if ( icon ) {
+			icon->activated();
+			connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
+
+			width = icon->pixmap().width();
+			height = icon->pixmap().height();
+		}
+	}
+#endif
+
+	if ( !icon && (width*height)==0 )
+		width = height = 50;
+
+	place = PlaceInline;
+	if ( attr["align"] == "left" )
+		place = PlaceLeft;
+	else if ( attr["align"] == "right" )
+		place = PlaceRight;
+
+	tmpwidth = width;
+	tmpheight = height;
+
+	attributes = attr;
+}
+
+TextIcon::~TextIcon()
+{
+#ifndef WIDGET_PLUGIN
+	if ( icon ) {
+		icon->stop();
+		delete icon;
+	}
+#endif
+}
+
+void TextIcon::adjustToPainter(QPainter *)
+{
+	// FIXME: This class can be incorrectly printed. Check this sometime later.
+}
+
+void TextIcon::draw(QPainter *p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup &cg, bool /*selected*/)
+{
+	if ( placement() != PlaceInline ) {
+		x = xpos;
+		y = ypos;
+	}
+
+	if ( !icon ) {
+		p->fillRect( x, y, width, height, cg.dark() );
+		return;
+	}
+
+	if ( placement() != PlaceInline && !QRect( xpos, ypos, width, height ).intersects( QRect( cx, cy, cw, ch ) ) )
+		return;
+
+#ifndef WIDGET_PLUGIN
+	if ( placement() == PlaceInline )
+		p->drawPixmap( x , y, icon->pixmap() );
+	else
+		p->drawPixmap( cx , cy, icon->pixmap(), cx - x, cy - y, cw, ch );
+#endif
+}
+
+void TextIcon::iconUpdated(const QPixmap &)
+{
+	// TODO: any ideas what it should do? ;-)
+}
+
+QString TextIcon::richText() const
+{
+	QString s;
+	s += "<icon ";
+	QMap<QString, QString>::ConstIterator it = attributes.begin();
+	for ( ; it != attributes.end(); ++it ) {
+		s += it.key() + "=";
+		if ( (*it).find( ' ' ) != -1 )
+			s += "\"" + *it + "\"" + " ";
+		else
+			s += *it + " ";
+	}
+	s += ">";
+	return s;
+}
+
+//----------------------------------------------------------------------------
+// PsiStyleSheet - helper class for PsiTextView
+//----------------------------------------------------------------------------
+
+class PsiStyleSheet : public QStyleSheet
+{
+	Q_OBJECT
+private:
+	PsiStyleSheet(QObject *parent = 0, const char *name = 0)
+	: QStyleSheet(parent, name)
+	{
+		new QStyleSheetItem(this, QString::fromLatin1("icon"));
+	}
+
+public:
+	QTextCustomItem *tag(const QString &name, const QMap<QString, QString> &attr,
+			const QString &context,
+			const QMimeSourceFactory &factory,
+			bool emptyTag, QTextDocument *doc ) const
+	{
+		const QStyleSheetItem *style = item( name );
+		if ( style && style->name() == "icon" )
+			return new TextIcon( doc, attr, context, (QMimeSourceFactory&)factory );
+		return QStyleSheet::tag(name, attr, context, factory, emptyTag, doc);
+	}
+
+	static PsiStyleSheet *self();
+
+private:
+	static PsiStyleSheet *psiStyleSheet;
+};
+
+PsiStyleSheet *PsiStyleSheet::psiStyleSheet = 0;
+
+PsiStyleSheet *PsiStyleSheet::self()
+{
+	if ( !psiStyleSheet ) {
+		psiStyleSheet = new PsiStyleSheet();
+	}
+
+	return psiStyleSheet;
+}
+
+//----------------------------------------------------------------------------
+// PsiTextView
+//----------------------------------------------------------------------------
+
+class PsiTextView::Private : public QObject
+{
+	Q_OBJECT
+
+public:
+	Private(QObject *parent)
+	: QObject(parent, "PsiTextView::Private") { }
+};
+
+PsiTextView::PsiTextView(QWidget *parent, const char *name)
+: QTextEdit(parent, name)
+{
+	d = new Private(this);
+
+	setReadOnly(true);
+	setTextFormat(RichText);
+
+	setStyleSheet( styleSheet() );
+}
+
+QStyleSheet *PsiTextView::styleSheet()
+{
+	return PsiStyleSheet::self();
+}
+
+QPopupMenu *PsiTextView::createPopupMenu(const QPoint &pos)
+{
+	QString link = anchorAt(pos);
+	if ( link.isEmpty() )
+		return QTextEdit::createPopupMenu(pos);
+
+	if ( !urlObject )
+		urlObject = new URLObject();
+	return urlObject->createPopupMenu( link );
+}
+
+void PsiTextView::emitHighlighted(const QString &)
+{
+}
+
+void PsiTextView::emitLinkClicked(const QString &s)
+{
+	if ( !urlObject )
+		urlObject = new URLObject();
+	urlObject->popupAction( s );
+}
+
+//----------------------------------------------------------------------------
+// URLLabel
+//----------------------------------------------------------------------------
+
+class URLLabel::Private
+{
+public:
+	QString url;
+	QString title;
+};
+
+URLLabel::URLLabel(QWidget *parent, const char *name)
+: QLabel(parent, name)
+{
+	d = new Private;
+	setCursor( pointingHandCursor );
+}
+
+URLLabel::~URLLabel()
+{
+	delete d;
+}
+
+const QString &URLLabel::url() const
+{
+	return d->url;
+}
+
+void URLLabel::setUrl(const QString &url)
+{
+	d->url = url;
+	updateText();
+}
+
+const QString &URLLabel::title() const
+{
+	return d->title;
+}
+
+void URLLabel::setTitle(const QString &t)
+{
+	d->title = t;
+	updateText();
+}
+
+void URLLabel::updateText()
+{
+	setText( QString("<a href=\"%1\">%2</a>").arg(d->url).arg(d->title) );
+
+	if ( d->url != d->title )
+		QToolTip::add(this, d->url);
+	else
+		QToolTip::remove(this);
+}
+
+void URLLabel::mouseReleaseEvent (QMouseEvent *e)
+{
+	QLabel::mouseReleaseEvent (e);
+
+	switch ( e->button() ) {
+	case LeftButton:
+		if ( !urlObject )
+			urlObject = new URLObject();
+		urlObject->popupAction( url() );
+		break;
+
+	case MidButton:
+		break;
+
+	case RightButton:
+		{
+			if ( !urlObject )
+				urlObject = new URLObject();
+			QPopupMenu *m = urlObject->createPopupMenu( d->url );
+
+			if ( m ) {
+				m->exec( e->globalPos() );
+				delete m;
+			}
+			break;
+		}
+
+	default:
+		; // to supress warnings
+	}
+}
+
+void URLLabel::enterEvent (QEvent *e)
+{
+	QLabel::enterEvent (e);
+}
+
+void URLLabel::leaveEvent (QEvent *e)
+{
+	QLabel::leaveEvent (e);
+}
+
+void URLLabel::connectOpenURL(QObject *receiver, const char *slot)
+{
+	if ( !urlObject )
+		urlObject = new URLObject();
+
+	connect(urlObject, SIGNAL(openURL(QString)), receiver, slot);
+}
+
+#include "psitextview.moc"
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitextview.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitextview.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psitextview.h	(revision 38)
@@ -0,0 +1,93 @@
+/*
+ * psitextview.h - Icon-aware QTextView subclass widget
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSITEXTVIEW
+#define PSITEXTVIEW
+
+#include <qtextedit.h>
+#include <qlabel.h>
+
+class QStyleSheet;
+
+class URLLabel : public QLabel
+{
+	Q_OBJECT
+
+	Q_PROPERTY( QString url READ url WRITE setUrl )
+	Q_PROPERTY( QString title READ title WRITE setTitle )
+
+	Q_OVERRIDE( QString text DESIGNABLE false SCRIPTABLE false )
+	//Q_OVERRIDE( TextFormat DESIGNABLE false SCRIPTABLE false )
+
+public:
+	URLLabel(QWidget *parent = 0, const char *name = 0);
+	~URLLabel();
+
+	const QString &url() const;
+	void setUrl(const QString &);
+
+	const QString &title() const;
+	void setTitle(const QString &);
+
+	static void connectOpenURL(QObject *receiver, const char *slot); // openURL(QString url);
+
+protected:
+	virtual void mouseReleaseEvent (QMouseEvent *);
+	virtual void enterEvent (QEvent *);
+	virtual void leaveEvent (QEvent *);
+
+	void updateText();
+
+public:
+	class Private;
+private:
+	Private *d;
+};
+
+class PsiTextView : public QTextEdit
+{
+	Q_OBJECT
+
+	Q_OVERRIDE( int undoDepth DESIGNABLE false SCRIPTABLE false )
+	Q_OVERRIDE( bool overwriteMode DESIGNABLE false SCRIPTABLE false )
+	Q_OVERRIDE( bool modified SCRIPTABLE false)
+	Q_OVERRIDE( bool readOnly DESIGNABLE false SCRIPTABLE false )
+	Q_OVERRIDE( bool undoRedoEnabled DESIGNABLE false SCRIPTABLE false )
+
+public:
+	PsiTextView(QWidget *parent = 0, const char *name = 0);
+
+	static QStyleSheet *styleSheet();
+
+	class Private;
+
+protected:
+	QPopupMenu *createPopupMenu(const QPoint &pos);
+	QPopupMenu *createPopupMenu() { return 0; } // FIXME
+
+private:
+	bool linksEnabled() const { return TRUE; }
+	void emitHighlighted(const QString &s);
+	void emitLinkClicked(const QString &s);
+
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.cpp
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.cpp	(revision 38)
@@ -0,0 +1,206 @@
+/*
+ * psiwidgets.cpp - plugin for loading Psi's custom widgets into Qt Designer
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "psiwidgets.h"
+
+#include "fancylabel.h"
+#include "busywidget.h"
+#include "iconwidget.h"
+#include "psitextview.h"
+
+static const char *psiwidget_data[] = {
+"16 16 5 1",
+". c None",
+"# c #000000",
+"c c #57acda",
+"b c #72bde6",
+"a c #cde9f8",
+".###..####..###.",
+"#aaa#.#aa#.#aaa#",
+"#abbb##ac##abcc#",
+".##ab##ac##ac##.",
+"..#abc#ac#abc#..",
+"..#abc#ac#abc#..",
+"..#abc#ac#abc#..",
+"..#abc#ac#abc#..",
+"..#abbbbbbbbc#..",
+"...#abbbbbcc#...",
+"....##cccc##....",
+"......#ac#......",
+"......#ac#......",
+"......#ac#......",
+"......#ac#......",
+"......####......"};
+
+#define iconlabel_data psiwidget_data
+#define fancylabel_data psiwidget_data
+#define busywidget_data psiwidget_data
+#define iconsetselect_data psiwidget_data
+#define iconsetdisplay_data psiwidget_data
+#define iconbutton_data psiwidget_data
+#define icontoolbutton_data psiwidget_data
+#define psitextview_data psiwidget_data
+#define urllabel_data psiwidget_data
+
+PsiWidgetsPlugin::PsiWidgetsPlugin()
+{
+}
+
+QStringList PsiWidgetsPlugin::keys() const
+{
+	QStringList list;
+	list << "IconLabel";
+	list << "FancyLabel";
+	list << "BusyWidget";
+	list << "IconsetSelect";
+	list << "IconsetDisplay";
+	list << "IconButton";
+	list << "IconToolButton";
+	list << "PsiTextView";
+	list << "URLLabel";
+	return list;
+}
+
+QWidget *PsiWidgetsPlugin::create(const QString &key, QWidget *parent, const char *name)
+{
+	if ( key == "IconLabel" )
+		return new IconLabel( parent, name );
+	if ( key == "FancyLabel" )
+		return new FancyLabel( parent, name );
+	if ( key == "BusyWidget" )
+		return new BusyWidget( parent, name );
+	if ( key == "IconsetSelect" )
+		return new IconsetSelect( parent, name );
+	if ( key == "IconsetDisplay" )
+		return new IconsetDisplay( parent, name );
+	if ( key == "IconButton" )
+		return new IconButton( parent, name );
+	if ( key == "IconToolButton" )
+		return new IconToolButton( parent, name );
+	if ( key == "PsiTextView" )
+		return new PsiTextView( parent, name );
+	if ( key == "URLLabel" )
+		return new URLLabel( parent, name );
+	return 0;
+}
+
+QString PsiWidgetsPlugin::includeFile(const QString &feature) const
+{
+	if ( feature == "IconLabel" || feature == "FancyLabel" )
+		return "fancylabel.h";
+	if ( feature == "BusyWidget" )
+		return "busywidget.h";
+	if ( feature == "IconsetSelect" || feature == "IconsetDisplay" || feature == "IconButton" || feature == "IconToolButton" )
+		return "iconwidget.h";
+	if ( feature == "PsiTextView" || feature == "URLLabel" )
+		return "psitextview.h";
+	return QString::null;
+}
+
+QString PsiWidgetsPlugin::group(const QString &feature) const
+{
+	if ( feature == "IconLabel" || feature == "FancyLabel" )
+		return "Display";
+	if ( feature == "BusyWidget" )
+		return "Display";
+	if ( feature == "IconsetSelect" || feature == "IconsetDisplay" )
+		return "Views";
+	if ( feature == "IconButton" || feature == "IconToolButton" )
+		return "Buttons";
+	if ( feature == "PsiTextView" || feature == "URLLabel" )
+		return "Display";
+	return QString::null;
+}
+
+QIconSet PsiWidgetsPlugin::iconSet(const QString &key) const
+{
+	if ( key == "IconLabel" )
+		return QIconSet( QPixmap( (const char **)iconlabel_data ) );
+	if ( key == "FancyLabel" )
+		return QIconSet( QPixmap( (const char **)fancylabel_data ) );
+	if ( key == "BusyWidget" )
+		return QIconSet( QPixmap( (const char **)busywidget_data ) );
+	if ( key == "IconsetSelect" )
+		return QIconSet( QPixmap( (const char **)iconsetselect_data ) );
+	if ( key == "IconsetDisplay" )
+		return QIconSet( QPixmap( (const char **)iconsetdisplay_data ) );
+	if ( key == "IconButton" )
+		return QIconSet( QPixmap( (const char **)iconbutton_data ) );
+	if ( key == "IconToolButton" )
+		return QIconSet( QPixmap( (const char **)icontoolbutton_data ) );
+	if ( key == "PsiTextView" )
+		return QIconSet( QPixmap( (const char **)psitextview_data ) );
+	if ( key == "URLLabel" )
+		return QIconSet( QPixmap( (const char **)psitextview_data ) );
+	return QIconSet();
+}
+
+QString PsiWidgetsPlugin::toolTip(const QString &feature) const
+{
+	if ( feature == "IconLabel" )
+		return "Icon Label";
+	if ( feature == "FancyLabel" )
+		return "Fancy Label";
+	if ( feature == "BusyWidget" )
+		return "Busy Widget";
+	if ( feature == "IconsetSelect" )
+		return "Iconset Select";
+	if ( feature == "IconsetDisplay" )
+		return "Iconset Display";
+	if ( feature == "IconButton" )
+		return "Icon Button";
+	if ( feature == "IconToolButton" )
+		return "Icon Tool Button";
+	if ( feature == "PsiTextView" )
+		return "Psi's Text View";
+	if ( feature == "URLLabel" )
+		return "URL Label";
+	return QString::null;
+}
+
+QString PsiWidgetsPlugin::whatsThis(const QString &feature) const
+{
+	if ( feature == "IconLabel" )
+		return "Label that can contain animated Icon.";
+	if ( feature == "FancyLabel" )
+		return "Just a Fancy Label. Use it for decoration of dialogs. ;-)";
+	if ( feature == "BusyWidget" )
+		return "Widget for indicating that program is doing something.";
+	if ( feature == "IconsetSelect" )
+		return "Widget for Iconset selection.";
+	if ( feature == "IconsetDisplay" )
+		return "Displays all icons in Iconset.";
+	if ( feature == "IconButton" )
+		return "PushButton that can contain animated Icon.";
+	if ( feature == "IconToolButton" )
+		return "ToolButton that can contain animated Icon.";
+	if ( feature == "PsiTextView" )
+		return "Widget for displaying rich-text data, with inline Icons.";
+	if ( feature == "URLLabel" )
+		return "Widget for displaying clickable URLs.";
+	return QString::null;
+}
+
+bool PsiWidgetsPlugin::isContainer(const QString &) const
+{
+	return FALSE;
+}
+
+Q_EXPORT_PLUGIN( PsiWidgetsPlugin )
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.h
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.h	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.h	(revision 38)
@@ -0,0 +1,41 @@
+/*
+ * psiwidgets.h - plugin for loading Psi's custom widgets into Qt Designer
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIWIDGETSPLUGIN_H
+#define PSIWIDGETSPLUGIN_H
+
+#include <qwidgetplugin.h>
+
+class QT_WIDGET_PLUGIN_EXPORT PsiWidgetsPlugin : public QWidgetPlugin
+{
+public:
+	PsiWidgetsPlugin();
+
+	QStringList keys() const;
+	QWidget *create(const QString &classname, QWidget *parent = 0, const char *name = 0 );
+	QString group(const QString &) const;
+	QIconSet iconSet(const QString &) const;
+	QString includeFile(const QString &) const;
+	QString toolTip(const QString &) const;
+	QString whatsThis(const QString &) const;
+	bool isContainer(const QString &) const;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.pri
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.pri	(revision 38)
@@ -0,0 +1,32 @@
+psiwidgets {
+	INCLUDEPATH += $$WIDGETS_CPP
+
+	SOURCES += \
+		$$WIDGETS_CPP/busywidget.cpp \
+		$$WIDGETS_CPP/fancylabel.cpp \
+		$$WIDGETS_CPP/iconselect.cpp \
+		$$WIDGETS_CPP/iconwidget.cpp \ 
+		$$WIDGETS_CPP/psitextview.cpp \
+		$$WIDGETS_CPP/iconaction.cpp \
+		$$WIDGETS_CPP/fancypopup.cpp \
+		$$WIDGETS_CPP/psitabwidget.cpp \
+		$$WIDGETS_CPP/psitabbar.cpp
+
+	HEADERS += \
+		$$WIDGETS_CPP/busywidget.h \
+		$$WIDGETS_CPP/fancylabel.h \
+		$$WIDGETS_CPP/iconselect.h \
+		$$WIDGETS_CPP/iconwidget.h \
+		$$WIDGETS_CPP/psitextview.h \
+		$$WIDGETS_CPP/iconaction.h \
+		$$WIDGETS_CPP/fancypopup.h \
+		$$WIDGETS_CPP/fancypopuplist.h \
+		$$WIDGETS_CPP/psitabwidget.h \
+		$$WIDGETS_CPP/psitabbar.h
+
+	# to remove dependency on iconset and stuff
+	#DEFINES += WIDGET_PLUGIN
+
+	# where to search for widgets plugin
+	QMAKE_UIC += -L $$WIDGETS_CPP
+}
Index: psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.pro
===================================================================
--- psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/libpsi/psiwidgets/psiwidgets.pro	(revision 38)
@@ -0,0 +1,42 @@
+TEMPLATE	= lib
+CONFIG		+= qt thread warn_on release plugin
+TARGET		= psiwidgets
+os2:TARGET	= psiwgts
+
+#target.path	= $(QTDIR)/plugins/designer
+#INSTALLS	+= target
+
+SOURCES += \
+	psiwidgets.cpp \
+	fancylabel.cpp \
+	busywidget.cpp \
+	iconwidget.cpp \
+	psitextview.cpp \
+	psitabwidget.cpp \
+	psitabbar.cpp
+
+HEADERS += \
+	psiwidgets.h \
+	fancylabel.h \
+	busywidget.h \
+	iconwidget.h \
+	psitextview.h \
+	psitabwidget.h \
+	psitabbar.h
+
+DISTFILES += \
+	README \
+	iconselect.cpp \
+	iconselect.h
+
+DEFINES += WIDGET_PLUGIN
+
+debug: {
+	MOC_DIR        = debug/.moc
+	OBJECTS_DIR    = debug/.obj
+	UI_DIR         = debug/.ui
+} else {
+	MOC_DIR        = .moc
+	OBJECTS_DIR    = .obj
+	UI_DIR         = .ui
+}
Index: psi/tags/0.10-os2-b1/mac/Info.plist
===================================================================
--- psi/tags/0.10-os2-b1/mac/Info.plist	(revision 38)
+++ psi/tags/0.10-os2-b1/mac/Info.plist	(revision 38)
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>psi</string>
+	<key>CFBundleGetInfoString</key>
+	<string>Psi 0.10-test4 by the Psi team. This program is licensed under the GNU GPL.</string>
+	<key>CFBundleIconFile</key>
+	<string>application.icns</string>
+	<key>CFBundleIdentifier</key>
+	<string>com.affinix.psi</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>Psi</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>Psi 0.10</string>
+	<key>CFBundleSignature</key>
+	<string>psi</string>
+	<key>CFBundleVersion</key>
+	<string>0.10</string>
+	<key>CSResourcesFileMapped</key>
+	<true/>
+</dict>
+</plist>
Index: psi/tags/0.10-os2-b1/mac/Makefile
===================================================================
--- psi/tags/0.10-os2-b1/mac/Makefile	(revision 38)
+++ psi/tags/0.10-os2-b1/mac/Makefile	(revision 38)
@@ -0,0 +1,142 @@
+#
+# Build file for packaging Psi/Mac.
+#
+# The DMG packager looks for a template.dmg.bz2 for using as its 
+# DMG template. If it doesn't find one, it generates a clean one.
+#
+# If you create a DMG template, you should make one containing all
+# the files listed in $(FILES) below, and arrange everything to suit
+# your style. The contents of the files themselves does not matter, so
+# they can be empty. 
+#
+# Parameters:
+#	* BUILD: Set this to 'no' if you do not want to build the source tree or
+#		check dependencies of the 'disk' dir when building the DMG.
+#
+# Remko Troncon <remko@psi-im.org>
+#
+
+# Change these according to your own setup
+QTDIR=/Developer/qt
+QCADIR=/Developer/qca
+GROWLDIR=/Library/Frameworks
+PSI_DIR=..
+
+NAME=Psi
+#VERSION=0.10_CVS_$(shell date +"%Y%m%d")
+VERSION=0.10
+FILES=Psi.app 
+DISK_DIR=disk
+ifneq ($(BUILD),no)
+DISK_FILES=$(foreach f,$(FILES), $(addprefix $(DISK_DIR)/,$(f)))
+endif
+MASTER_DMG=$(NAME)-$(VERSION).dmg
+
+.PHONY: buildall
+all: build-psi $(DISK_FILES)
+
+.PHONY: dmg
+dmg: build-psi $(DISK_FILES) $(MASTER_DMG)
+
+# Cleanup
+.PHONY: clean
+clean: clean-dmg
+	-rm -rf $(DISK_DIR)
+
+# Help
+.PHONY: help
+help:
+	@echo "Usage:"
+	@echo "    make : Build a distributable binary in $(DISK_DIR)/"
+	@echo "    make dmg : Build a DMG disk image"
+
+# Building the main source tree
+.PHONY: build-psi
+ifneq ($(BUILD),no)
+build-psi:
+	@echo
+	@echo --------------------- Building Source Tree --------------------
+	$(MAKE) -C $(PSI_DIR)
+	@echo
+else
+build-psi:
+endif
+
+
+################################################################################
+# Generation of the final files
+################################################################################
+
+# The binary
+$(DISK_DIR)/Psi.app: $(PSI_DIR)/src/psi.app
+	@echo
+	@echo --------------------- Packaging Binary --------------------
+	rm -rf "$@"
+	ditto -rsrc "$<" "$@"
+	mkdir -p "$@/Contents/Resources/crypto"
+	cp -f "$(QTDIR)/plugins/crypto/libqca-tls.dylib" "$@/Contents/Resources/crypto/"
+	mkdir -p "$@/Contents/Frameworks"
+	-ditto -rsrc "$(GROWLDIR)/Growl.framework" "$@/Contents/Frameworks/Growl.framework"
+	cp -f "$(QTDIR)/lib/libqt-mt.3.dylib" "$@/Contents/Frameworks/"
+	cp -f "$(QCADIR)/lib/libqca.1.dylib" "$@/Contents/Frameworks/"
+	strip "$@/Contents/MacOS/psi"
+	install_name_tool -id @executable_path/../Frameworks/libqt-mt.3.dylib "$@/Contents/Frameworks/libqt-mt.3.dylib"
+	install_name_tool -change libqt-mt.3.dylib "@executable_path/../Frameworks/libqt-mt.3.dylib" "$@/Contents/MacOS/psi"
+	install_name_tool -change libqt-mt.3.dylib "@executable_path/../Frameworks/libqt-mt.3.dylib" "$@/Contents/Frameworks/libqca.1.dylib"
+	install_name_tool -change libqt-mt.3.dylib "@executable_path/../Frameworks/libqt-mt.3.dylib" "$@/Contents/Resources/crypto/libqca-tls.dylib"
+	install_name_tool -id @executable_path/../Frameworks/libqca.1.dylib "$@/Contents/Frameworks/libqca.1.dylib"
+	install_name_tool -change libqca.1.dylib "@executable_path/../Frameworks/libqca.1.dylib" "$@/Contents/MacOS/psi"
+	@echo 
+
+# The rest
+$(DISK_DIR)/%: $(PSI_DIR)/%
+	ditto -rsrc "$<" $@
+
+
+################################################################################
+# Disk image creation
+################################################################################
+
+WC_DMG=wc.dmg
+WC_DIR=wc
+TEMPLATE_DMG=template.dmg
+
+$(TEMPLATE_DMG): $(TEMPLATE_DMG).bz2
+	bunzip2 -k $<
+
+$(TEMPLATE_DMG).bz2: 
+	@echo
+	@echo --------------------- Generating empty template --------------------
+	mkdir template
+	hdiutil create -size 40m "$(TEMPLATE_DMG)" -srcfolder template -format UDRW -volname "$(NAME)" -quiet
+	rmdir template
+	bzip2 "$(TEMPLATE_DMG)"
+	@echo
+
+$(WC_DMG): $(TEMPLATE_DMG)
+	cp $< $@
+
+$(MASTER_DMG): $(WC_DMG) $(DISK_FILES)
+	@echo
+	@echo --------------------- Creating Disk Image --------------------
+	mkdir -p $(WC_DIR)
+	hdiutil attach "$(WC_DMG)" -noautoopen -quiet -mountpoint "$(WC_DIR)"
+	for i in $(DISK_DIR)/*; do  \
+		file=`basename $$i`; \
+		rm -rf "$(WC_DIR)/$$file"; \
+		ditto -rsrc "$(DISK_DIR)/$$file" "$(WC_DIR)/$$file"; \
+	done
+	#rm -f "$@"
+	#hdiutil create -srcfolder "$(WC_DIR)" -format UDZO -imagekey zlib-level=9 "$@" -volname "$(NAME) $(VERSION)" -scrub -quiet
+	WC_DEV=`hdiutil info | grep "$(WC_DIR)" | grep "Apple_HFS" | awk '{print $$1}'` && \
+	hdiutil detach $$WC_DEV -quiet -force
+	rm -f "$(MASTER_DMG)"
+	hdiutil convert "$(WC_DMG)" -quiet -format UDZO -imagekey zlib-level=9 -o "$@"
+	rm -rf $(WC_DIR)
+	-hdiutil internet-enable -yes -quiet "$@"
+	@echo
+
+.PHONY: clean-dmg
+clean-dmg:
+	-rm -rf $(TEMPLATE_DMG) $(MASTER_DMG) $(WC_DMG) $(NAME)-*.dmg
+
Index: psi/tags/0.10-os2-b1/makewin.bat
===================================================================
--- psi/tags/0.10-os2-b1/makewin.bat	(revision 38)
+++ psi/tags/0.10-os2-b1/makewin.bat	(revision 38)
@@ -0,0 +1,34 @@
+@echo off
+
+echo Running qmake ...
+qmake psi.pro
+if errorlevel 1 goto ERROR1
+echo Compiling ...
+nmake
+if errorlevel 1 goto ERROR2
+
+mkdir psiwin
+copy src\psi.exe psiwin\Psi.exe
+copy %QTDIR%\bin\qt-mt*.dll psiwin
+copy ..\qca\qca*.dll psiwin
+copy src\tools\idle\win32\idleui.dll psiwin
+xcopy /e certs psiwin\certs\
+xcopy /e iconsets psiwin\iconsets\
+xcopy /e sound psiwin\sound\
+copy COPYING psiwin
+win32\tod README psiwin\Readme.txt
+win32\tod INSTALL psiwin\Install.txt
+
+echo 'psiwin' created successfully
+goto END
+
+:ERROR1
+echo Error running qmake..  Are you sure you have Qt set up?
+goto END
+
+:ERROR2
+echo Compile failed, stopping.
+goto END
+
+:END
+
Index: psi/tags/0.10-os2-b1/os2/WarpIN/Readme.txt
===================================================================
--- psi/tags/0.10-os2-b1/os2/WarpIN/Readme.txt	(revision 38)
+++ psi/tags/0.10-os2-b1/os2/WarpIN/Readme.txt	(revision 38)
@@ -0,0 +1,121 @@
+=====================================
+ Document-Version: v1.0 - 2006-07-28
+=====================================
+
+
+Introduction
+============
+
+Here is a simple description how this package allows you to create new WarpIN
+packages and ZIP archives for PSI.
+
+
+
+Directory structure
+===================
+
+  [...]
+    +-- WarpIN                 <-- The stuff to create packages (*.cmd)
+           +-- Build_Packages  <-- The created *.wpi, *.exe and *.zip go into                       
+           |                       this directory.
+           |                       e.g. PSI_0-10-0a1_os2_2006-07-25.wpi
+           |
+           +-- Scripts         <-- The *.wis (WarpIN) script for each Psi 
+           |                       version is here.
+           |                       e.g. psi_0-10-0a1.wis
+           --- Version         <-- The Psi package for each Psi version is
+                  |                stored here in a subdirectory 
+                  |                e.g. psi_0-10-0a1.
+                  + psi_0-10-0a1
+                         |
+                         + [files]
+
+
+
+Filenames and Version
+=====================
+
+For Psi Version 0.10 I extended the version number to 0.10.0 because older
+packages where named 0.9.1, 0.9.2 and so on and it's most likely that there
+may be a revision number again in the future.
+
+As you can see I didn't use '.' in the version number as eCS and OS/2 still
+have problems (WarpIN packages are not recognized as WarpIN archive,...) if
+there is more than one dot in the filename.
+
+As dots are evil they are replaced by a minus character: major-minor-revision
+Alpha versions get a 'a' and beta versions the 'b' and release-candidates
+get 'rc' after the revision number followed by an increasing (build) number.
+Final versions don't have the character.
+
+Other elements in the filename are separated by '_' instead of spaces.
+As additional element I added the operating system and the release-date
+of the ported Psi version in the sortable 'YYYY-MM-DD' format.
+
+So finally we get:
+<APPNAME>_<MAJOR>-<MINOR>-<REVISION>[<'a'|'b'|'rc'><number>]_os2_<YYYY>-<MM>-<YY>.<'wpi'|'exe'|'zip'>
+
+
+
+WarpIN installation script
+==========================
+
+The WarpIN script can be found in the scripts directory where
+you need to add a new script for every version.
+There are three things you need to look for, if a new package will be created:
+ - Version number of the WarpIN script/package: "PACKAGEID="
+   You have to update the version as described before:
+    ..."\PSI Core Files\<MAJOR>\<MINOR>\<REVISION>[<'a'|'b'|'rc'><number>]
+ - Version number and date information as text in the WarpIN *.wis file
+   Look for the "YYYY-MM-DD" format and you should find them.
+ - Changelog and Readme updates have to be updated in WarpIN "page" sections.
+
+
+
+Creating new packages
+=====================
+
+In the WarpIN directory you will find two *.cmd files.
+
+1.) If you don't have WarpIN in your "SET PATH" statement (which you normally
+    don't have) you need to call the "warpin_env.cmd" script which should be
+    able to get the WarpIN installation path from the OS/2 INI.
+    If not you get an error message.
+
+2.) You need to update the "create_psi_packages.cmd" with the WarpIN script
+    version, the new path to the directory where build programm is and how
+    the created package should be named.
+    
+3.) After you updated the "create_psi_packages.cmd" script you can call it.
+    The created WarpIN packages (*.exe and *.wpi) and the created ZIP archive
+    can be found in "Build_Packages".
+    If there has been an error or you need to rebuild the packages you don't
+    need to delete the files from the "Build_Packages" manually.
+    This is done automatically. Logfiles for *.wpi and *.exe are created
+    but you should still execute the packages manually.
+
+4.) Distribute the files (*.wpi, *.exe, *.zip)! ;)
+
+
+
+TODO
+====
+
+ - More REXX to automate creating WarpIN packages.
+ - Add gcc*.dll + libc*.dll as additional package for installation
+ - Automatic check if the packages and the ZIP archive is ok.
+
+
+Contact
+=======
+
+Juergen Ulbts
+E-Mail.: On request or see website
+Website: http://www.juergen-ulbts.de/
+ICQ....: 65814655 (Nick: magog96)
+Jabber.: magog96@12jabber.com
+
+
+
+Juergen Ulbts (2006-07-28)
+
Index: psi/tags/0.10-os2-b1/os2/WarpIN/Scripts/psi_0-10-0-2.wis
===================================================================
--- psi/tags/0.10-os2-b1/os2/WarpIN/Scripts/psi_0-10-0-2.wis	(revision 38)
+++ psi/tags/0.10-os2-b1/os2/WarpIN/Scripts/psi_0-10-0-2.wis	(revision 38)
@@ -0,0 +1,122 @@
+<WARPIN>
+VERSION=0.95
+OS=OS2_3x
+
+
+<HEAD>
+<TITLE>Psi Version 0.10 Beta 1 (2006-08-18)</TITLE>
+<PCK INDEX=1
+     PACKAGEID="Psi\Psi\Psi Core Files\0\10\0\2"
+     TARGET="$(WARPIN_DEFAULTAPPSPATH)\Psi" BASE
+     TITLE="Psi Version 0.10 Beta 1 (2006-08-18)"
+     CREATEOBJECT="REPLACE WPFolder|Psi Jabber Client|<WP_DESKTOP>|OBJECTID=<PSI_Folder>;"
+     CREATEOBJECT="REPLACE WPProgram|Psi|<PSI_Folder>|EXENAME=$(1)\Psi.exe;STARTUPDIR=$(1);ICONFILE=$(1)\Psi.ico;"
+     CREATEOBJECT="REPLACE WPShadow|Readme.txt|<PSI_Folder>|SHADOWID=$(1)\Readme.txt;"
+     CREATEOBJECT="REPLACE WPShadow|Install.txt|<PSI_Folder>|SHADOWID=$(1)\Install.txt;"
+     CREATEOBJECT="REPLACE WPShadow|Readme-OS2.txt|<PSI_Folder>|SHADOWID=$(1)\Readme-OS2.txt;"
+
+     CREATEOBJECT="REPLACE WPFolder|Additional Links|<PSI_Folder>|OBJECTID=<PSI_Links>;"
+     CREATEOBJECT="REPLACE WPUrl|Psi HomePage|<PSI_Links>|NOTDEFAULTICON=YES;DEFAULTVIEW=CONTENTS;URL=http://psi-im.org/;"
+     CREATEOBJECT="REPLACE WPUrl|Psi Wiki|<PSI_Links>|NOTDEFAULTICON=YES;DEFAULTVIEW=CONTENTS;URL=http://psi-im.org/wiki/Main_Page;"
+     CREATEOBJECT="REPLACE WPUrl|Psi for OS/2 HomePage|<PSI_Links>|NOTDEFAULTICON=YES;DEFAULTVIEW=CONTENTS;URL=http://svn.netlabs.org/qt3/ticket/25/;"
+     CREATEOBJECT="REPLACE WPUrl|Psi for OS/2 Downloads|<PSI_Links>|NOTDEFAULTICON=YES;DEFAULTVIEW=CONTENTS;URL=ftp://ftp.netlabs.org/pub/qt3/psi/;"
+     CREATEOBJECT="REPLACE WPUrl|Qt3 for OS/2 HomePage|<PSI_Links>|NOTDEFAULTICON=YES;DEFAULTVIEW=CONTENTS;URL=http://svn.netlabs.org/qt3/;"
+
+     SELECT NODESELECT
+
+     >Installation of Psi Version 0.10 Beta 1 (2006-08-18).</PCK>
+</HEAD>
+<BODY>
+
+
+<PAGE INDEX=1 TYPE=README>
+<NEXTBUTTON TARGET=2>~Next</NEXTBUTTON>
+<TEXT>Welcome to the Psi for OS/2 Installation Program!
+
+Please read through the following pages by pressing "Next" to continue or press "Cancel" to abort the installation.</TEXT>
+<README FORMAT=HTML>
+<b>Psi IM Jabber Client</b>
+<p>Psi is the premiere Instant Messaging application designed for Microsoft
+Windows, Apple Mac OS X, GNU/Linux, eComStation (eCS) and OS/2. Built upon an
+open protocol named Jabber, Psi is a fast and lightweight messaging client
+that utilises the best in open source technologies.</p>
+<p>The goal of the Psi project is to create a powerful, yet easy-to-use
+Jabber/XMPP client that tries to strictly adhere to the XMPP drafts and
+Jabber JEPs. This means that in most cases, Psi will not implement a feature
+unless there is an accepted standard for it in the Jabber community. Doing
+so ensures that Psi will be compatible, stable, and predictable, both from
+an end-user and developer standpoint.</p>
+<p>This does not mean that Psi will never have "cool new features". The
+dev team and community are power-users who look to Psi to keep up with the
+times. We always want to hear what the users of Psi would like, and if a
+feature that the dev team wishes to implement does not have an accepted
+standard, or would diverge from an existing one, we can work together
+to see the standards changed to accomodate everyone.</p>
+<p>Perhaps you have been considering Jabber, but don't want to abandon
+your friends? Jabber technologies are highly extensible, so Psi is able
+to interoperate with other, proprietory messaging systems such as AOL's
+ICQ and Microsoft's Messenger. You can keep in contact with your friends,
+regardless of whether they use AIM, ICQ, MSNM or YahooM. It's even
+possible to use Internet Relay Chat! (*)</p>
+<p>Unlike other IM clients, Psi is small, fast and responsive. It uses a
+graphical toolkit called Qt, which takes on the appearance of whichever
+operating system you run it on. For this reason, Psi doesn't use 'skins'.
+We'd rather it blend with your desktop like any other application.</p>
+<p>Psi has full support for Unicode. Unicode allows Psi to send and receive
+messages in other languages, even ones that aren't based upon the English
+letterset. In fact, you are able to use Psi in your choice of eight
+different languages, with more on the way!</p>
+<p>Cross-platform compatibility is important to the Psi development team;
+we believe that you should be able to use whichever operating system you
+wish. In keeping with this belief, we have a version for each of the
+all major OS's.</p>
+<p>(*) Requires connection to a Jabber server with suitable transports available.</p>
+<p><b>Authors</b></p>
+<ul>
+<li>Project Lead/Maintainer (2004-current):<br>
+Kevin Smith<br>
+kismith@psi-im.org<br>
+http://www.kismith.co.uk/<br>
+<li>Original Author (2001-2004):<br>
+Justin Karneges<br>
+justin@affinix.com<br>
+http://psi-im.org/<br>
+</ul>
+</README>
+</PAGE>
+
+
+<PAGE INDEX=2 TYPE=README>
+<NEXTBUTTON TARGET=3>~I agree</NEXTBUTTON>
+<TEXT>Please read the Psi License information. By pressing the "I agree" button, you agree to all terms and conditions to the below license agreement.</TEXT>
+<README EXTRACTFROMPCK=1 FORMAT=PLAIN>COPYING</README>
+</PAGE>
+
+
+<PAGE INDEX=3 TYPE=README>
+<NEXTBUTTON TARGET=4>~Next</NEXTBUTTON>
+<TEXT>Below is the contents of the Readme-OS2.txt file. Please read it before installing and press "Next" to continue.</TEXT>
+<README EXTRACTFROMPCK=1 FORMAT=PLAIN>Readme-OS2.txt</README>
+</PAGE>
+
+
+<PAGE INDEX=4 TYPE=CONTAINER>
+<NEXTBUTTON TARGET=5>~Next</NEXTBUTTON>
+<TEXT>Please select the packages which are to be installed. You may change the target paths for the packages.</TEXT>
+</PAGE>
+
+
+<PAGE INDEX=5 TYPE=CONFIGURE>
+<NEXTBUTTON TARGET=6>~Next</NEXTBUTTON>
+<TEXT>Please select additional configuration that WarpIN should perform after installing this archive.</TEXT>
+</PAGE>
+
+
+<PAGE INDEX=6 TYPE=TEXT>
+<NEXTBUTTON TARGET=0>I~nstall</NEXTBUTTON>
+<TEXT>Press "Install" to begin installing the Psi Jabber Client.</TEXT>
+</PAGE>
+
+
+</BODY>
+</WARPIN>
Index: psi/tags/0.10-os2-b1/os2/WarpIN/Scripts/psi_0-10-0a1.wis
===================================================================
--- psi/tags/0.10-os2-b1/os2/WarpIN/Scripts/psi_0-10-0a1.wis	(revision 38)
+++ psi/tags/0.10-os2-b1/os2/WarpIN/Scripts/psi_0-10-0a1.wis	(revision 38)
@@ -0,0 +1,184 @@
+<WARPIN>
+VERSION=0.95
+OS=OS2_3x
+<HEAD>
+<TITLE>Psi Version 0.10.0 ALPHA 1 (2006-07-25)</TITLE>
+<PCK INDEX=1
+     PACKAGEID="PSI\PSI\PSI Core Files\0\10\0a1"
+     TARGET="$(WARPIN_DEFAULTAPPSPATH)\Psi" BASE
+     TITLE="Psi Version 0.10.0 ALPHA 1 (2006-07-25)"
+     CREATEOBJECT="WPFolder|Psi Jabber Client|<WP_DESKTOP>|OBJECTID=<PSI_Folder>;"
+     CREATEOBJECT="WPProgram|Psi|<PSI_Folder>|EXENAME=$(1)\Psi.exe;STARTUPDIR=$(1);"
+     CREATEOBJECT="WPProgram|Readme-OS2.txt|<PSI_Folder>|EXENAME=e.exe;PARAMETERS=Readme-OS2.txt;STARTUPDIR=$(1);"
+     CREATEOBJECT="WPProgram|Readme.txt|<PSI_Folder>|EXENAME=e.exe;PARAMETERS=Readme.txt;STARTUPDIR=$(1);"
+     CREATEOBJECT="WPProgram|Install.txt|<PSI_Folder>|EXENAME=e.exe;PARAMETERS=Install.txt;STARTUPDIR=$(1);"
+
+     CREATEOBJECT="WPFolder|Additional Links|<PSI_Folder>|OBJECTID=<PSI_Links>;"
+     CREATEOBJECT="WPUrl|PSI OS2-Port HomePage (QT3)|<PSI_Links>|NOTDEFAULTICON=YES;DEFAULTVIEW=CONTENTS;URL=ftp://ftp.netlabs.org/pub/qt3/;"
+     CREATEOBJECT="WPUrl|PSI HomePage|<PSI_Links>|NOTDEFAULTICON=YES;DEFAULTVIEW=CONTENTS;URL=http://psi-im.org/;"
+     CREATEOBJECT="WPUrl|PSI Wiki|<PSI_Links>|NOTDEFAULTICON=YES;DEFAULTVIEW=CONTENTS;URL=http://psi-im.org/wiki/Main_Page;"
+
+     SELECT NODESELECT
+
+     >Installation of PSI Version 0.10.0 ALPHA 1 (2006-02-22).</PCK>
+</HEAD>
+<BODY>
+<PAGE INDEX=1 TYPE=TEXT>
+<NEXTBUTTON TARGET=2>~Next</NEXTBUTTON>
+<TEXT>
+Psi IM Jabber Client
+====================
+
+Psi is the premiere Instant Messaging application designed for Microsoft Windows, Apple Mac OS X, GNU/Linux, eComStation (eCS) and OS2. Built upon an open protocol named Jabber, Psi is a fast and lightweight messaging client that utilises the best in open source technologies. JMDB is a free project with the goal to deliver a home database for films, actors and other informations!
+
+The goal of the Psi project is to create a powerful, yet easy-to-use Jabber/XMPP client that tries to strictly adhere to the XMPP drafts and Jabber JEPs. This means that in most cases, Psi will not implement a feature unless there is an accepted standard for it in the Jabber community. Doing so ensures that Psi will be compatible, stable, and predictable, both from an end-user and developer standpoint. 
+
+This does not mean that Psi will never have "cool new features". The dev team and community are power-users who look to Psi to keep up with the times. We always want to hear what the users of Psi would like, and if a feature that the dev team wishes to implement does not have an accepted standard, or would diverge from an existing one, we can work together to see the standards changed to accomodate everyone.
+
+Perhaps you have been considering Jabber, but don't want to abandon your friends? Jabber technologies are highly extensible, so Psi is able to interoperate with other, proprietory messaging systems such as AOL's ICQ and Microsoft's Messenger. You can keep in contact with your friends, regardless of whether they use AIM, ICQ, MSNM or YahooM. It's even possible to use Internet Relay Chat! * 
+
+Unlike other IM clients, Psi is small, fast and responsive. It uses a graphical toolkit called Qt, which takes on the appearance of whichever operating system you run it on. For this reason, Psi doesn't use 'skins'. We'd rather it blend with your desktop like any other application. 
+
+Psi has full support for Unicode. Unicode allows Psi to send and receive messages in other languages, even ones that aren't based upon the English letterset. In fact, you are able to use Psi in your choice of eight different languages, with more on the way! 
+
+Cross-platform compatibility is important to the Psi development team; we believe that you should be able to use whichever operating system you wish. In keeping with this belief, we have a version for each of the three major OS's. The images on this page show Psi running on Microsoft Windows XP, Apple MacOS X and GNU/Linux/KDE3. 
+
+* Requires connection to a Jabber server with suitable transports available.
+
+</TEXT>
+</PAGE>
+<PAGE INDEX=2 TYPE=README>
+<NEXTBUTTON TARGET=3>~I agree</NEXTBUTTON>
+<TEXT>
+Psi Introduction, License, Copyright, Warranty. By pressing the "I agree" button, you agree to all terms and conditions to the below license agreement.
+</TEXT>
+<README>
+This readme contains information specific to the OS/2 version of Psi.
+For general information, please refer to Readme.txt, Install.txt and
+to the online documentation you can find at http://psi-im.org/.
+
+For the list of changes, see the bottom of this file. 
+
+
+Installation
+------------
+
+1.  Make sure Innotek (Bird) LIBC 0.6.1 library (libc061.dll) is
+    installed on your system. Download the library from
+    ftp://ftp.netlabs.org/pub/gcc/libc-0.6.1-csd1.zip and unzip
+    the archive to the directory you plan to install Psi to or to
+    one of the directories listed in your LIBPATH environment variable.
+
+2.  Unzip the Psi archive containing this file to the directory of your
+    choice and run the Psi executable (psi.exe). No additional
+    installation is required.
+
+
+Configuration data
+------------------
+
+Psi stores all configuration files in the subdirectory /PsiData of
+your home directory (pointed to by the HOME environment variable).
+
+The name of the last used Psi profile is stored in the registry
+under the key HKEY_CURRENT_USER\Software\Affinix\psi.
+
+
+Current Limitations and Known Bugs
+----------------------------------
+
+1.  Using the PM Default Browser/Mail choice for opening links doesn't
+    work yet. The Custom browser setting requires a command like
+    'cmd /c cd D:\Apps\Firefox & D:\Apps\Firefox\Firefox.exe'.
+    
+2.  SSL connection is completely untested and will most likely not work.
+
+3.  PGP encryption is completely untested and probably will not work.
+
+4.  The global hot keys are not available. 
+ 
+5.  Idle (i.e. Auto-Away) detection may not work properly.
+
+6.  The directory watcher service is not available (though I don't yet know
+    what is it good for in Psi). 
+
+7.  There is no "tray icon" (xCenter widget) for Psi. It will be available
+    later.
+    
+8.  Power management events such as "sleep" or "wakeup" are not detected by Psi.
+
+9.  Drag and Drop support in Qt/OS2 is not yet finished, so there are few
+    known issues (for example, text in entry fields may be corrupted when the
+    drag object is moved over them).
+
+
+Bug Reports
+-----------
+
+To submit a bug report, please visit http://svn.netlabs.org/qt3/ticket/25
+and add a comment describing the problem.
+
+Note that in order to add a comment, you'll need to login to the Trac system
+using the special name and the password presented on the start page
+(http://svn.netlabs.org/qt3/wiki).  
+
+
+License
+-------
+Open-Source software
+
+
+Warranty
+--------
+EXCEPT AS OTHERWISE RESTRICTED BY LAW, THIS WORK IS PROVIDED
+WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES OF ANY KIND, INCLUDING
+BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES OF FITNESS FOR A
+PARTICULAR PURPOSE, MERCHANTABILITY OR TITLE.  EXCEPT AS
+OTHERWISE PROVIDED BY LAW, NO AUTHOR, COPYRIGHT HOLDER OR
+LICENSOR SHALL BE LIABLE TO YOU FOR DAMAGES OF ANY KIND, EVEN IF
+THEY HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+</README>
+</PAGE>
+
+
+<PAGE INDEX=3 TYPE=README>
+<NEXTBUTTON TARGET=4>~Next</NEXTBUTTON>
+<TEXT>
+List of changes and bugfixes for Psi Version 0.10.0 ALPHA 1 (2006-07-25)
+</TEXT>
+<README>
+Psi History and ChangeLog
+=========================
+
+*   Version 0.10-os2-a1 (2005-07-25)
+    
+    The first public alpha version.
+</README>
+</PAGE>
+
+
+<PAGE INDEX=4 TYPE=CONTAINER>
+<NEXTBUTTON TARGET=5>~Next</NEXTBUTTON>
+<TEXT>
+Please select the packages which are to be installed. You may change the target paths for the packages.
+</TEXT>
+</PAGE>
+
+
+<PAGE INDEX=5 TYPE=CONFIGURE>
+<NEXTBUTTON TARGET=6>~Next</NEXTBUTTON>
+<TEXT>
+Please select additional configuration that WarpIN should perform after installing this archive.
+</TEXT>
+</PAGE>
+
+
+<PAGE INDEX=6 TYPE=TEXT>
+<NEXTBUTTON TARGET=0>I~nstall</NEXTBUTTON>
+<TEXT>
+Press "Install" to begin installing the Psi Jabber Client.</TEXT>
+</PAGE>
+
+</BODY>
+</WARPIN>
+
Index: psi/tags/0.10-os2-b1/os2/WarpIN/create_psi_packages.cmd
===================================================================
--- psi/tags/0.10-os2-b1/os2/WarpIN/create_psi_packages.cmd	(revision 38)
+++ psi/tags/0.10-os2-b1/os2/WarpIN/create_psi_packages.cmd	(revision 38)
@@ -0,0 +1,57 @@
+/*  
+    create_psi_packages.cmd:
+
+    This script will create the PSI packages for distribution.
+    *.wpi
+    *.exe
+    *.zip
+
+    (c) 2006 - Juergen Ulbts
+*/
+call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
+call SysLoadFuncs
+
+CURRENT_DIR = DIRECTORY()
+
+/* ========================================================= */
+/*  Application specific information - NEEDS MODIFICATION    */
+/* ========================================================= */
+ARCHIVE_NAME='psi_0-10-os2-b1'               /* Target archive name without extension */
+WARPIN_SCRIPT_NAME='psi_0-10-0-2.wis'        /* WarpIN *.wis script name  */
+BINARY_DIRECTORY='psi_0-10-os2-b1'           /* Subdirectory of version where the build application can be found */
+
+
+/* ========================================================= */
+/*  Set the package creation application and settings        */
+/* ========================================================= */
+WARPIN_CREATOR='wic.exe'                     /* WarpIN application the create WarpIN achives */
+ARCHIVE_CREATOR='zip.exe'                    /* Packer */
+ARCHIVE_CREATOR_PARAMETER='-9rS'             /* Packer parameters */       
+
+
+/* ========================================================= */
+/*  Search for wic.exe and call warpin_env.cmd if not found  */
+/* ========================================================= */
+if (SysSearchPath('PATH', WARPIN_CREATOR) == '') then do
+    call 'warpin_env.cmd'
+end
+ 
+/* ========================================================= */
+/*  Delete old package with the same name as 'ARCHIVE_NAME'  */
+/* ========================================================= */
+'del .\Build_Packages\'||ARCHIVE_NAME||'.* > NUL'
+
+/* ========================================================= */
+/*  Create WarpIN packages                                   */
+/* ========================================================= */
+''||WARPIN_CREATOR||' .\Build_Packages\'||ARCHIVE_NAME||'.wpi -a 1 -c.\Version\'||BINARY_DIRECTORY||' -r * -s ".\Scripts\'||WARPIN_SCRIPT_NAME||'" > .\Build_Packages\'||ARCHIVE_NAME||'.wpi_log 2>&1'
+''||WARPIN_CREATOR||' .\Build_Packages\'||ARCHIVE_NAME||'.exe -a 1 -c.\Version\'||BINARY_DIRECTORY||' -U -r * -s ".\Scripts\'||WARPIN_SCRIPT_NAME||'" > .\Build_Packages\'||ARCHIVE_NAME||'.exe_log 2>&1'
+
+/* ========================================================= */
+/*  Create ZIP archive                                       */
+/* ========================================================= */
+'cd Version'
+''||ARCHIVE_CREATOR||' '||ARCHIVE_CREATOR_PARAMETER||' ..\Build_Packages\'||ARCHIVE_NAME||'.zip .\'||BINARY_DIRECTORY
+CALL DIRECTORY CURRENT_DIR
+
+exit;
Index: psi/tags/0.10-os2-b1/os2/WarpIN/warpin_env.cmd
===================================================================
--- psi/tags/0.10-os2-b1/os2/WarpIN/warpin_env.cmd	(revision 38)
+++ psi/tags/0.10-os2-b1/os2/WarpIN/warpin_env.cmd	(revision 38)
@@ -0,0 +1,42 @@
+/*  
+    warpin_env.cmd:
+
+    This script tries to find WarpIN and call the supplied
+    envwic.cmd so you can call the necessary.
+
+    (c) 2006 - Juergen Ulbts
+*/
+
+call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
+call SysLoadFuncs
+
+/* get WarpIN path from OS2.INI */
+WARPIN_INSTALL_DIR = SysIni("USER", "WarpIN", "Path")
+
+if (WARPIN_INSTALL_DIR = "ERROR:") then do
+    SAY "ERROR: The WarpIN path could not be found in OS2.INI. WarpIN not installed?";
+    SAY "       Terminating script!";
+    exit;
+end;
+
+/* this thing is null-terminated, so get rid of the null byte */
+p = pos('0'x, WARPIN_INSTALL_DIR);
+if (p \= 0) then do
+    WARPIN_INSTALL_DIR = left(WARPIN_INSTALL_DIR, p - 1);
+end
+
+SAY "WarpIN found at: "||WARPIN_INSTALL_DIR
+
+if (stream(WARPIN_INSTALL_DIR||'\envwic.cmd', 'C' 'QUERY EXISTS') \== '') then do
+    /* Execute the WIC script that comes with WarpIN */
+    SAY "Calling envwic.cmd from WarpIN install directory..."
+    ''||WARPIN_INSTALL_DIR||'\envwic.cmd'
+end
+else do
+    /* Set up WarpIN environment manually */
+    SAY "Setting up WarpIN environment..."
+    '@set PATH=%PATH%;'WARPIN_INSTALL_DIR
+    '@set BEGINLIBPATH=%BEGINLIBPATH%;'WARPIN_INSTALL_DIR
+end
+
+exit;
Index: psi/tags/0.10-os2-b1/os2/psi_os2.rc
===================================================================
--- psi/tags/0.10-os2-b1/os2/psi_os2.rc	(revision 38)
+++ psi/tags/0.10-os2-b1/os2/psi_os2.rc	(revision 38)
@@ -0,0 +1,1 @@
+ICON 1 "..\\os2\\PSI_001.ICO"
Index: psi/tags/0.10-os2-b1/psi.desktop
===================================================================
--- psi/tags/0.10-os2-b1/psi.desktop	(revision 38)
+++ psi/tags/0.10-os2-b1/psi.desktop	(revision 38)
@@ -0,0 +1,28 @@
+[Desktop Entry]
+Type=Application
+Version=0.9.4
+Encoding=UTF-8
+Name=Psi
+GenericName=Jabber Client
+Comment=Communicate over the Jabber network
+Icon=psi
+Exec=psi
+Terminal=false
+Categories=Qt;Network;InstantMessaging;
+StartupNotify=true
+
+# Translations
+GenericName[nl]=Jabber Client
+Comment[nl]=Communiceer over het Jabber netwerk
+GenericName[sl]=Odjemalec za Jabber
+Comment[sl]=Komuniciranje prek omreÅŸja Jabber
+GenericName[mk]=ÐÐ»ÐžÐµÐœÑ Ð·Ð° Jabber
+Comment[mk]=ÐÐŸÐŒÑÐœÐžÑÐžÑÐ°ÑÑÐµ Ð¿ÑÐµÐºÑ Jabber ÐŒÑÐµÐ¶Ð°ÑÐ°
+GenericName[ca]=Client Jabber
+Comment[ca]=Comuniqui's a travÃ©s de la xarxa Jabber
+GenericName[eo]=ÄŽabbera Klientilo
+Comment[eo]=KomunikiÄu per la Äµabbera reto
+GenericName[pl]=Klient Jabber
+Comment[pl]=Komunikacja poprzez sieÄ Jabber
+GenericName[pt_BR]=Cliente Jabber
+Comment[pt_BR]=Comunique-se atravÃ©s da rede Jabber
Index: psi/tags/0.10-os2-b1/psi.pro
===================================================================
--- psi/tags/0.10-os2-b1/psi.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/psi.pro	(revision 38)
@@ -0,0 +1,11 @@
+TEMPLATE = subdirs
+
+LIBPSI_CPP = ../libpsi
+exists(libpsi) {
+	LIBPSI_CPP = libpsi
+}
+
+SUBDIRS += \
+	$$LIBPSI_CPP/psiwidgets \
+	src
+
Index: psi/tags/0.10-os2-b1/psi.qc
===================================================================
--- psi/tags/0.10-os2-b1/psi.qc	(revision 38)
+++ psi/tags/0.10-os2-b1/psi.qc	(revision 38)
@@ -0,0 +1,23 @@
+<qconf>
+  <name>Psi</name>
+  <profile>psi.pro</profile>
+  <sharename>psi</sharename>
+  <moddir>qcm</moddir>
+  <qt3/>
+  <dep type='qt31'>
+    <required/>
+  </dep>
+  <dep type='qca'>
+    <required/>
+  </dep>
+  <dep type='zlib'>
+    <required/>
+  </dep>
+  <dep type='growl'/>
+  <dep type='xss'/>
+  <dep type='dnotify'/>
+  <dep type='ghbnr'/>
+  <dep type='conf'>
+	<required/>
+  </dep>
+</qconf>
Index: psi/tags/0.10-os2-b1/psics.pri
===================================================================
--- psi/tags/0.10-os2-b1/psics.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/psics.pri	(revision 38)
@@ -0,0 +1,102 @@
+psics {
+	# iris
+	INCLUDEPATH += \
+		$$IRISCS_CPP/util \
+		$$IRISCS_CPP/network
+
+	HEADERS += \
+		$$IRISCS_CPP/util/base64.h \
+		$$IRISCS_CPP/util/bytestream.h \
+		$$IRISCS_CPP/util/safedelete.h \
+		$$IRISCS_CPP/network/ndns.h \
+		$$IRISCS_CPP/network/srvresolver.h \
+		$$IRISCS_CPP/network/bsocket.h \
+		$$IRISCS_CPP/network/httpconnect.h \
+		$$IRISCS_CPP/network/httppoll.h \
+		$$IRISCS_CPP/network/servsock.h \
+		$$IRISCS_CPP/network/socks.h
+
+	SOURCES += \
+		$$IRISCS_CPP/util/base64.cpp \
+		$$IRISCS_CPP/util/bytestream.cpp \
+		$$IRISCS_CPP/util/safedelete.cpp \
+		$$IRISCS_CPP/network/ndns.cpp \
+		$$IRISCS_CPP/network/srvresolver.cpp \
+		$$IRISCS_CPP/network/bsocket.cpp \
+		$$IRISCS_CPP/network/httpconnect.cpp \
+		$$IRISCS_CPP/network/httppoll.cpp \
+		$$IRISCS_CPP/network/servsock.cpp \
+		$$IRISCS_CPP/network/socks.cpp
+
+	# trayicon
+	CONFIG += trayicon
+	TRAYICON_CPP = $$PSICS_CPP/trayicon
+	INCLUDEPATH += $$TRAYICON_CPP
+	include($$TRAYICON_CPP/trayicon.pri)
+
+	# idle
+	CONFIG += idle
+	IDLE_CPP = $$PSICS_CPP/idle
+	INCLUDEPATH += $$IDLE_CPP
+	include($$IDLE_CPP/idle.pri)
+
+	# systemwatch
+	CONFIG += systemwatch
+	SYSTEMWATCH_CPP = $$PSICS_CPP/systemwatch
+	INCLUDEPATH += $$SYSTEMWATCH_CPP
+	include($$SYSTEMWATCH_CPP/systemwatch.pri)
+
+	# growlnotifier
+	mac {
+		contains(DEFINES, HAVE_GROWL) {
+			CONFIG += growlnotifier
+			GROWLNOTIFIER_CPP = $$PSICS_CPP/growlnotifier
+			INCLUDEPATH += $$GROWLNOTIFIER_CPP
+			include($$GROWLNOTIFIER_CPP/growlnotifier.pri)
+		}
+	}
+
+	# mac dock
+	CONFIG += mac_dock
+	MACDOCK_CPP = $$PSICS_CPP/mac_dock
+	INCLUDEPATH += $$MACDOCK_CPP
+	include($$MACDOCK_CPP/mac_dock.pri)	
+
+	# zip
+	CONFIG += zip
+	ZIP_CPP = $$PSICS_CPP/zip
+	INCLUDEPATH += $$ZIP_CPP
+	include($$ZIP_CPP/zip.pri)
+
+	# dirwatch
+	CONFIG += dirwatch
+	DIRWATCH_CPP = $$PSICS_CPP/dirwatch
+	INCLUDEPATH += $$DIRWATCH_CPP
+	include($$DIRWATCH_CPP/dirwatch.pri)
+
+	# openpgp
+	CONFIG += openpgp
+	OPENPGP_CPP = $$PSICS_CPP/openpgp
+	INCLUDEPATH += $$OPENPGP_CPP
+	include($$OPENPGP_CPP/openpgp.pri)
+
+	# globalaccel
+	CONFIG += globalaccel
+	GLOBALACCEL_CPP = $$PSICS_CPP/globalaccel
+	INCLUDEPATH += $$GLOBALACCEL_CPP
+	include($$GLOBALACCEL_CPP/globalaccel.pri)
+
+	# advanced widget
+	CONFIG += advwidget
+	ADVWIDGET_CPP = $$PSICS_CPP/advwidget
+	INCLUDEPATH += $$ADVWIDGET_CPP
+	include($$ADVWIDGET_CPP/advwidget.pri)
+
+	use_crash {
+		# crash
+		CONFIG += crash
+		DEFINES += USE_CRASH
+		CRASH_CPP = $$PSICS_CPP/crash
+		include($$CRASH_CPP/crash.pri)
+	}
+}
Index: psi/tags/0.10-os2-b1/qcm/conf.qcm
===================================================================
--- psi/tags/0.10-os2-b1/qcm/conf.qcm	(revision 38)
+++ psi/tags/0.10-os2-b1/qcm/conf.qcm	(revision 38)
@@ -0,0 +1,32 @@
+/*
+-----BEGIN QCMOD-----
+name: Psi Configuration
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_conf
+//----------------------------------------------------------------------------
+class qc_conf : public ConfObj
+{
+public:
+	qc_conf(Conf *c) : ConfObj(c) {}
+	QString name() const { return "Psi Configuration"; }
+	QString shortname() const { return "conf"; }
+	QString checkString() const { return "Writing configuration ..."; }
+	bool exec()
+	{
+		conf->addExtra(QString("PREFIX=%1").arg(conf->getenv("PREFIX")));
+		conf->addExtra(QString("DATADIR=%1/psi").arg(conf->getenv("DATADIR")));
+
+		QFile file("src/config.h");
+		if ( file.open( IO_WriteOnly ) ) {
+			QTextStream stream( &file );
+			stream << "#define PSI_DATADIR \"" << conf->getenv("DATADIR") << "/psi\"" << endl;
+		}
+		
+		conf->addDefine("HAVE_CONFIG");
+		
+		return true;
+	}
+};
Index: psi/tags/0.10-os2-b1/qcm/dnotify.qcm
===================================================================
--- psi/tags/0.10-os2-b1/qcm/dnotify.qcm	(revision 38)
+++ psi/tags/0.10-os2-b1/qcm/dnotify.qcm	(revision 38)
@@ -0,0 +1,89 @@
+/*
+-----BEGIN QCMOD-----
+name: Linux Directory Notification
+-----END QCMOD-----
+*/
+
+#include<unistd.h>
+#include<fcntl.h>
+#include<signal.h>
+#include<sys/utsname.h>
+
+//----------------------------------------------------------------------------
+// qc_dnotify
+//----------------------------------------------------------------------------
+class qc_dnotify : public ConfObj
+{
+public:
+	qc_dnotify(Conf *c) : ConfObj(c)
+	{
+	}
+
+	~qc_dnotify()
+	{
+		remove("ftest.c");
+		remove("ftest.o");
+	}
+
+	QString name() const { return "Linux Directory Notification"; }
+	QString shortname() const { return "dnotify"; }
+
+	bool do_write()
+	{
+		char *fdata =
+			"#define _GNU_SOURCE\n"
+			"#include<unistd.h>\n"
+			"#include<fcntl.h>\n"
+			"#include<signal.h>\n"
+			"#include<sys/utsname.h>\n"
+			"\n"
+			"int main()\n"
+			"{\n"
+			"    DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT|DN_MODIFY|DN_ATTRIB;\n"
+			"    return 0;\n"
+			"}\n";
+
+		FILE *f;
+		f = fopen("ftest.c", "w");
+		if(!f)
+			return false;
+		fwrite(fdata, strlen(fdata), 1, f);
+		fclose(f);
+
+		return true;
+	}
+
+	bool do_compile()
+	{
+		QString str = conf->qvar("QMAKE_CC") + " -c ftest.c -o ftest.o";
+		int r = conf->doCommand(str);
+		if(r == 0)
+			return true;
+		else
+			return false;
+	}
+
+	bool exec()
+	{
+		// taken from KDE
+		bool supports_dnotify = true; // not guilty until proven guilty
+		struct utsname uts;
+		int major, minor, patch;
+		if(uname(&uts) < 0)
+			supports_dnotify = false; // *shrug*
+		else if(sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
+			supports_dnotify = false; // *shrug*
+		else if( major * 1000000 + minor * 1000 + patch < 2004019 ) // <2.4.19
+			supports_dnotify = false;
+		if(!supports_dnotify)
+			return false;
+
+		if(!do_write())
+			return false;
+		if(!do_compile())
+			return false;
+
+		conf->addDefine("HAVE_DNOTIFY");
+		return true;
+	}
+};
Index: psi/tags/0.10-os2-b1/qcm/ghbnr.qcm
===================================================================
--- psi/tags/0.10-os2-b1/qcm/ghbnr.qcm	(revision 38)
+++ psi/tags/0.10-os2-b1/qcm/ghbnr.qcm	(revision 38)
@@ -0,0 +1,49 @@
+/*
+-----BEGIN QCMOD-----
+name: gethostbyname_r()
+-----END QCMOD-----
+*/
+class qc_ghbnr : public ConfObj
+{
+public:
+	qc_ghbnr(Conf *c) : ConfObj(c)
+	{
+	}
+
+	~qc_ghbnr()
+	{
+		remove("ftest.c");
+		remove("ftest");
+	}
+
+	QString name() const { return "gethostbyname_r()"; }
+	QString shortname() const { return "ghbnr"; }
+
+	bool exec()
+	{
+		char *fdata =
+			"#include<netdb.h>\n"
+			"\n"
+			"int main()\n"
+			"{\n"
+			"    gethostbyname_r(\"\", 0, 0, 0, 0, 0);\n"
+			"    return 0;\n"
+			"}\n";
+
+		FILE *f;
+		f = fopen("ftest.c", "w");	
+		if(!f)
+			return false;
+		fwrite(fdata, strlen(fdata), 1, f);
+		fclose(f);
+
+		QString str = conf->qvar("QMAKE_CC") + " ftest.c -o ftest";
+		int r = conf->doCommand(str);
+		if(r == 0) {
+			conf->addDefine("HAVE_GETHOSTBYNAME_R");
+			return true;
+		}
+		else
+			return false;
+	}
+};
Index: psi/tags/0.10-os2-b1/qcm/growl.qcm
===================================================================
--- psi/tags/0.10-os2-b1/qcm/growl.qcm	(revision 38)
+++ psi/tags/0.10-os2-b1/qcm/growl.qcm	(revision 38)
@@ -0,0 +1,60 @@
+/*
+-----BEGIN QCMOD-----
+name: Growl
+arg: with-growl=[path],Path to the Growl framework
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_growl
+//----------------------------------------------------------------------------
+class qc_growl : public ConfObj
+{
+public:
+	qc_growl(Conf *c) : ConfObj(c) {}
+	QString name() const { return "Growl"; }
+	QString shortname() const { return "growl"; }
+
+	// TODO: This should go into ConfObj
+    bool checkFramework(const QString &path, const QString &name)
+    {
+        QString str =
+            "int main()\n"
+            "{\n"
+            "    return 0;\n"
+            "}\n";
+
+        QString extra;
+        if(!path.isEmpty())
+            extra += QString("-F") + path + ' ';
+        extra += QString("-framework ") + name;
+        if(!conf->doCompileAndLink(str, extra))
+            return false;
+        return true;
+    }
+	
+	// TODO: This should go into ConfObj
+	void addFrameworkPath(const QString& str)
+	{
+		conf->addExtra("QMAKE_CXXFLAGS += -F" + str);
+		conf->addLib("-F" + str);
+	}
+
+	bool exec()
+	{
+#ifdef Q_WS_MAC
+		QString growl_path = conf->getenv("QC_WITH_GROWL");
+		if(!checkFramework(growl_path, "Growl"))
+			return false;
+
+		if(!growl_path.isEmpty()) 
+			addFrameworkPath(growl_path);
+		conf->addLib("-framework Growl");
+		conf->addDefine("HAVE_GROWL");
+
+		return true;
+#else
+		return false;
+#endif
+	}
+};
Index: psi/tags/0.10-os2-b1/qcm/kde.qcm
===================================================================
--- psi/tags/0.10-os2-b1/qcm/kde.qcm	(revision 38)
+++ psi/tags/0.10-os2-b1/qcm/kde.qcm	(revision 38)
@@ -0,0 +1,25 @@
+/*
+-----BEGIN QCMOD-----
+name: KDE
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_kde
+//----------------------------------------------------------------------------
+class qc_kde : public ConfObj
+{
+public:
+	qc_kde(Conf *c) : ConfObj(c) {}
+	QString name() const { return "KDE"; }
+	QString shortname() const { return "kde"; }
+	bool exec()
+	{
+		char *p = getenv("KDEDIR");
+		if(!p)
+			return false;
+
+		conf->addExtra(QString("KDE = %1").arg(p));
+		return true;
+	}
+};
Index: psi/tags/0.10-os2-b1/qcm/qca.qcm
===================================================================
--- psi/tags/0.10-os2-b1/qcm/qca.qcm	(revision 38)
+++ psi/tags/0.10-os2-b1/qcm/qca.qcm	(revision 38)
@@ -0,0 +1,55 @@
+/*
+-----BEGIN QCMOD-----
+name: QCA 1.0
+arg: with-qca-inc=[path],Path to QCA include files
+arg: with-qca-lib=[path],Path to QCA library files
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_qca
+//----------------------------------------------------------------------------
+class qc_qca : public ConfObj
+{
+public:
+	qc_qca(Conf *c) : ConfObj(c) {}
+	QString name() const { return "QCA 1.0"; }
+	QString shortname() const { return "qca"; }
+	bool exec()
+	{
+		QString inc, lib;
+		QString s;
+
+		s = conf->getenv("QC_WITH_QCA_INC");
+		if(!s.isEmpty()) {
+			if(!conf->checkHeader(s, "qca.h"))
+				return false;
+			inc = s;
+		}
+		else {
+			if(!conf->findHeader("qca.h", QStringList(), &s))
+				return false;
+			inc = s;
+		}
+
+		s = conf->getenv("QC_WITH_QCA_LIB");
+		if(!s.isEmpty()) {
+			if(!conf->checkLibrary(s, "qca"))
+				return false;
+			lib = s;
+		}
+		else {
+			if(!conf->findLibrary("qca", &s))
+				return false;
+			lib = s;
+		}
+
+		if(!inc.isEmpty())
+			conf->addIncludePath(inc);
+		if(!lib.isEmpty())
+			conf->addLib(QString("-L") + s);
+		conf->addLib("-lqca");
+
+		return true;
+	}
+};
Index: psi/tags/0.10-os2-b1/qcm/qt31.qcm
===================================================================
--- psi/tags/0.10-os2-b1/qcm/qt31.qcm	(revision 38)
+++ psi/tags/0.10-os2-b1/qcm/qt31.qcm	(revision 38)
@@ -0,0 +1,22 @@
+/*
+-----BEGIN QCMOD-----
+name: Qt >= 3.1
+-----END QCMOD-----
+*/
+class qc_qt31 : public ConfObj
+{
+public:
+	qc_qt31(Conf *c) : ConfObj(c) {}
+	QString name() const { return "Qt >= 3.1"; }
+	QString shortname() const { return "qt31"; }
+	bool exec()
+	{
+		if(QT_VERSION >= 0x030100) {
+			if(QT_VERSION < 0x030300)
+				conf->addExtra("QXML_STATIC = Y");
+			return true;
+		}
+		else
+			return false;
+	}
+};
Index: psi/tags/0.10-os2-b1/qcm/xss.qcm
===================================================================
--- psi/tags/0.10-os2-b1/qcm/xss.qcm	(revision 38)
+++ psi/tags/0.10-os2-b1/qcm/xss.qcm	(revision 38)
@@ -0,0 +1,116 @@
+/*
+-----BEGIN QCMOD-----
+name: the XScreenSaver extension
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_xss
+//----------------------------------------------------------------------------
+class qc_xss : public ConfObj
+{
+public:
+	enum { OK, NEEDLIB, FAIL };
+	qc_xss(Conf *c) : ConfObj(c)
+	{
+	}
+
+	~qc_xss()
+	{
+		remove("xssprobe_test.c");
+		remove("xssprobe_test.o");
+		remove("xssprobe_test");
+	}
+
+	QString name() const
+	{
+		return "the XScreenSaver extension";
+	}
+
+	QString shortname() const { return "xss"; }
+
+	int do_write()
+	{
+		char *xsstest =
+			"#include<X11/Xlib.h>\n"
+			"#include<X11/Xutil.h>\n"
+			"#include<X11/extensions/scrnsaver.h>\n"
+			"\n"
+			"int main()\n"
+			"{\n"
+			"    XScreenSaverQueryExtension(NULL, NULL, NULL);\n"
+			"    return 0;\n"
+			"}\n";
+
+		FILE *f;
+		f = fopen("xssprobe_test.c", "w");
+		if(!f)
+			return 0;
+		fwrite(xsstest, strlen(xsstest), 1, f);
+		fclose(f);
+
+		return 1;
+	}
+
+	int do_compile()
+	{
+		QString inc = conf->expandIncludes(conf->qvar("QMAKE_INCDIR_X11"));
+		QString str = conf->qvar("QMAKE_CC") + " -c " + inc + " xssprobe_test.c -o xssprobe_test.o";
+		int r = conf->doCommand(str);
+		if(r == 0)
+			return 1;
+		else
+			return 0;
+	}
+
+	int do_link()
+	{
+		QString lib = conf->expandLibs(conf->qvar("QMAKE_LIBDIR_X11"));
+		QString inc = conf->expandIncludes(conf->qvar("QMAKE_INCDIR_X11"));
+		QString str = conf->qvar("QMAKE_CC") + " xssprobe_test.o -o xssprobe_test " + lib + ' ' + conf->qvar("QMAKE_LIBS_X11");
+		int r = conf->doCommand(str);
+		if(r == 0)
+			return 1;
+		else
+			return 0;
+	}
+
+	int do_linkLib()
+	{
+		QString lib = conf->expandLibs(conf->qvar("QMAKE_LIBDIR_X11"));
+		QString inc = conf->expandIncludes(conf->qvar("QMAKE_INCDIR_X11"));
+		QString str = conf->qvar("QMAKE_CC") + " xssprobe_test.o -o xssprobe_test " + lib + ' ' + conf->qvar("QMAKE_LIBS_X11") + " -lXss";
+		int r = conf->doCommand(str);
+		if(r == 0)
+			return 1;
+		else
+			return 0;
+	}
+
+	int do_all()
+	{
+		if(!do_write())
+			return FAIL;
+		if(!do_compile())
+			return FAIL;
+		if(do_link())
+			return OK;
+		if(do_linkLib())
+			return NEEDLIB;
+		return FAIL;
+	}
+
+	bool exec()
+	{
+		int r = do_all();
+		if(r == OK) {
+			conf->addDefine("HAVE_XSS");
+			return true;
+		}
+		else if(r == NEEDLIB) {
+			conf->addDefine("HAVE_XSS");
+			conf->addLib("-lXss");
+			return true;
+		}
+	}
+};
Index: psi/tags/0.10-os2-b1/qcm/zlib.qcm
===================================================================
--- psi/tags/0.10-os2-b1/qcm/zlib.qcm	(revision 38)
+++ psi/tags/0.10-os2-b1/qcm/zlib.qcm	(revision 38)
@@ -0,0 +1,55 @@
+/*
+-----BEGIN QCMOD-----
+name: zlib
+arg: with-zlib-inc=[path],Path to zlib include files
+arg: with-zlib-lib=[path],Path to zlib library files
+-----END QCMOD-----
+*/
+
+//----------------------------------------------------------------------------
+// qc_zlib
+//----------------------------------------------------------------------------
+class qc_zlib : public ConfObj
+{
+public:
+	qc_zlib(Conf *c) : ConfObj(c) {}
+	QString name() const { return "zlib"; }
+	QString shortname() const { return "zlib"; }
+	bool exec()
+	{
+		QString inc, lib;
+		QString s;
+
+		s = conf->getenv("QC_WITH_ZLIB_INC");
+		if(!s.isEmpty()) {
+			if(!conf->checkHeader(s, "zlib.h"))
+				return false;
+			inc = s;
+		}
+		else {
+			if(!conf->findHeader("zlib.h", QStringList(), &s))
+				return false;
+			inc = s;
+		}
+
+		s = conf->getenv("QC_WITH_ZLIB_LIB");
+		if(!s.isEmpty()) {
+			if(!conf->checkLibrary(s, "z"))
+				return false;
+			lib = s;
+		}
+		else {
+			if(!conf->findLibrary("z", &s))
+				return false;
+			lib = s;
+		}
+
+		if(!inc.isEmpty())
+			conf->addIncludePath(inc);
+		if(!lib.isEmpty())
+			conf->addLib(QString("-L") + s);
+		conf->addLib("-lz");
+
+		return true;
+	}
+};
Index: psi/tags/0.10-os2-b1/src/accountdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/accountdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/accountdlg.cpp	(revision 38)
@@ -0,0 +1,1171 @@
+/*
+ * accountdlg.cpp - dialogs for manipulating PsiAccounts
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"accountdlg.h"
+
+#include<qpushbutton.h>
+#include<qlistview.h>
+#include<qlineedit.h>
+#include<qcheckbox.h>
+#include<qlabel.h>
+#include<qlayout.h>
+#include<qgroupbox.h>
+#include<qcombobox.h>
+#include<qradiobutton.h>
+#include<qbuttongroup.h>
+#include<qtabwidget.h>
+#include<qwhatsthis.h>
+#include<qurl.h>
+#include<qca.h>
+#include"psicon.h"
+#include"psiaccount.h"
+#include"common.h"
+#include"im.h"
+#include"xmpp_tasks.h"
+#include"proxy.h"
+#include"sslcertdlg.h"
+#include"busywidget.h"
+#include"iconwidget.h"
+#include"fancylabel.h"
+#include"base64.h"
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// MiniClient
+//----------------------------------------------------------------------------
+static QCA::Cert readCertXml(const QDomElement &e)
+{
+	QCA::Cert cert;
+	// there should be one child data tag
+	QDomElement data = e.elementsByTagName("data").item(0).toElement();
+	if(!data.isNull())
+		cert.fromDER(Base64::stringToArray(data.text()));
+	return cert;
+}
+
+static QPtrList<QCA::Cert> getRootCerts(const QStringList &stores)
+{
+	QPtrList<QCA::Cert> list;
+
+	for(QStringList::ConstIterator dit = stores.begin(); dit != stores.end(); ++dit) {
+		QDir dir(*dit);
+		dir.setNameFilter("*.xml");
+		QStringList entries = dir.entryList();
+		for(QStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it) {
+			QFile f(dir.filePath(*it));
+			if(!f.open(IO_ReadOnly))
+				continue;
+			QDomDocument doc;
+			bool ok = doc.setContent(&f);
+			f.close();
+			if(!ok)
+				continue;
+
+			QDomElement base = doc.documentElement();
+			if(base.tagName() != "store")
+				continue;
+			QDomNodeList cl = base.elementsByTagName("certificate");
+
+			int num = 0;
+			for(int n = 0; n < (int)cl.count(); ++n) {
+				QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
+				if(cert->isNull()) {
+					delete cert;
+					continue;
+				}
+
+				++num;
+				list.append(cert);
+			}
+		}
+	}
+
+	return list;
+}
+
+MiniClient::MiniClient(QObject *parent)
+:QObject(parent)
+{
+	_client = new Client;
+	conn = 0;
+	tls = 0;
+	tlsHandler = 0;
+	stream = 0;
+	certList.setAutoDelete(true);
+}
+
+MiniClient::~MiniClient()
+{
+	delete _client;
+	reset();
+}
+
+void MiniClient::reset()
+{
+	delete stream;
+	stream = 0;
+
+	delete tls;
+	tls = 0;
+	tlsHandler = 0;
+
+	delete conn;
+	conn = 0;
+
+	certList.clear();
+}
+
+void MiniClient::connectToServer(const Jid &jid, bool ssl, const QString &_host, int _port, ProxyManager *pm, int proxy, QString *_pass)
+{
+	j = jid;
+
+	QString host;
+	int port;
+	if(!_host.isEmpty()) {
+		host = _host;
+		port = _port;
+	}
+	else {
+		host = jid.host();
+		if(ssl)
+			port = 5223;
+		else
+			port = 5222;
+	}
+
+	AdvancedConnector::Proxy p;
+	if(proxy > 0) {
+		const ProxyItem &pi = pm->getItem(proxy-1);
+		if(pi.type == "http") // HTTP Connect
+			p.setHttpConnect(pi.settings.host, pi.settings.port);
+		else if(pi.type == "socks") // SOCKS
+			p.setSocks(pi.settings.host, pi.settings.port);
+		else if(pi.type == "poll") { // HTTP Poll
+			QUrl u = pi.settings.url;
+			if(u.query().isEmpty()) {
+				QString v = host + ':' + QString::number(port);
+				QUrl::encode(v);
+				u.setQuery(QString("server=") + v);
+			}
+			p.setHttpPoll(pi.settings.host, pi.settings.port, u.toString());
+			p.setPollInterval(2);
+		}
+
+		if(pi.settings.useAuth)
+			p.setUserPass(pi.settings.user, pi.settings.pass);
+	}
+
+	conn = new AdvancedConnector;
+	if(ssl) {
+		QStringList certDirs;
+		certDirs += g.pathBase + "/certs";
+		certList = getRootCerts(certDirs);
+
+		tls = new QCA::TLS;
+		tls->setCertificateStore(certList);
+		tlsHandler = new QCATLSHandler(tls);
+		connect(tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
+	}
+	conn->setProxy(p);
+	conn->setOptHostPort(host, port);
+	conn->setOptSSL(ssl);
+
+	stream = new ClientStream(conn, tlsHandler);
+	stream->setOldOnly(true);
+	connect(stream, SIGNAL(connected()), SLOT(cs_connected()));
+	connect(stream, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated(int)));
+	connect(stream, SIGNAL(needAuthParams(bool, bool, bool)), SLOT(cs_needAuthParams(bool, bool, bool)));
+	connect(stream, SIGNAL(authenticated()), SLOT(cs_authenticated()));
+	connect(stream, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
+	connect(stream, SIGNAL(delayedCloseFinished()), SLOT(cs_delayedCloseFinished()));
+	connect(stream, SIGNAL(warning(int)), SLOT(cs_warning(int)));
+	connect(stream, SIGNAL(error(int)), SLOT(cs_error(int)));
+
+	if(_pass) {
+		pass = *_pass;
+		_client->connectToServer(stream, j);
+	}
+	else
+		_client->connectToServer(stream, j, false);
+}
+
+void MiniClient::close()
+{
+	_client->close();
+	reset();
+}
+
+Client *MiniClient::client()
+{
+	return _client;
+}
+
+void MiniClient::tls_handshaken()
+{
+	QCA::Cert cert = tls->peerCertificate();
+	int r = tls->certificateValidityResult();
+	if(r != QCA::TLS::Valid) {
+		QString str = PsiAccount::resultToString(r);
+		while(1) {
+			int n = QMessageBox::warning(0,
+				tr("Server Authentication"),
+				tr("The %1 certificate failed the authenticity test.").arg(j.host()) + '\n' + tr("Reason: %1").arg(str),
+				tr("&Details..."),
+				tr("Co&ntinue"),
+				tr("&Cancel"), 0, 2);
+			if(n == 0) {
+				SSLCertDlg::showCert(cert, r);
+			}
+			else if(n == 1) {
+				tlsHandler->continueAfterHandshake();
+				break;
+			}
+			else if(n == 2) {
+				close();
+				error();
+				break;
+			}
+		}
+	}
+	else
+		tlsHandler->continueAfterHandshake();
+}
+
+void MiniClient::cs_connected()
+{
+}
+
+void MiniClient::cs_securityLayerActivated(int)
+{
+}
+
+void MiniClient::cs_needAuthParams(bool, bool, bool)
+{
+	stream->setPassword(pass);
+	stream->continueAfterParams();
+}
+
+void MiniClient::cs_authenticated()
+{
+	_client->start(j.host(), j.user(), "", "");
+	handshaken();
+}
+
+void MiniClient::cs_connectionClosed()
+{
+	cs_error(-1);
+}
+
+void MiniClient::cs_delayedCloseFinished()
+{
+}
+
+void MiniClient::cs_warning(int)
+{
+	stream->continueAfterWarning();
+}
+
+void MiniClient::cs_error(int err)
+{
+	QString str;
+	bool reconn;
+
+	PsiAccount::getErrorInfo(err, conn, stream, tlsHandler, &str, &reconn);
+	close();
+
+	QMessageBox::critical(0, tr("Server Error"), tr("There was an error communicating with the Jabber server.\nDetails: %1").arg(str));
+	error();
+}
+
+//----------------------------------------------------------------------------
+// AccountManageDlg
+//----------------------------------------------------------------------------
+class AccountManageItem : public QCheckListItem
+{
+public:
+	AccountManageItem(QListView *par, PsiAccount *_pa)
+	:QCheckListItem(par,"",CheckBox)
+	{
+		pa = _pa;
+		updateInfo();
+	}
+
+	void updateInfo()
+	{
+		const UserAccount &acc = pa->userAccount();
+		setText(0, pa->name());
+		//setPixmap(0, IconsetFactory::icon("psi/account"));
+		Jid j = acc.jid;
+		setText(1, acc.opt_host ? acc.host : j.host());
+		setText(2, pa->isActive() ? AccountManageDlg::tr("Active") : AccountManageDlg::tr("Not active"));
+		setOn(pa->enabled());
+	}
+
+	void stateChange( bool s)
+	{
+		if (pa->enabled()!=s)
+			pa->setEnabled(s);
+		updateInfo();
+	}
+
+	int rtti() const
+	{
+		return 8109;
+	}
+
+	PsiAccount *pa;
+};
+
+AccountManageDlg::AccountManageDlg(PsiCon *_psi, const char *name)
+:AccountManageUI(0, name, false, WDestructiveClose)
+{
+	psi = _psi;
+	psi->dialogRegister(this);
+
+	setCaption(CAP(caption()));
+
+	// setup signals
+	connect(pb_add, SIGNAL(clicked()), SLOT(add()));
+	connect(pb_modify, SIGNAL(clicked()), SLOT(modify()));
+	connect(pb_remove, SIGNAL(clicked()), SLOT(remove()));
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+
+	connect(lv_accs, SIGNAL(doubleClicked(QListViewItem *)), SLOT(modify(QListViewItem *)));
+	connect(lv_accs, SIGNAL(selectionChanged(QListViewItem *)), SLOT(qlv_selectionChanged(QListViewItem *)));
+	connect(psi, SIGNAL(accountAdded(PsiAccount *)), SLOT(accountAdded(PsiAccount *)));
+	connect(psi, SIGNAL(accountUpdated(PsiAccount *)), SLOT(accountUpdated(PsiAccount *)));
+	connect(psi, SIGNAL(accountRemoved(PsiAccount *)), SLOT(accountRemoved(PsiAccount *)));
+
+	lv_accs->setAllColumnsShowFocus(true);
+	lv_accs->setResizeMode(QListView::LastColumn);
+
+	PsiAccountListIt it(psi->accountList());
+	for(PsiAccount *pa; (pa = it.current()); ++it) {
+		new AccountManageItem(lv_accs, pa);
+	}
+
+	if(lv_accs->childCount() > 0)
+		lv_accs->setSelected(lv_accs->firstChild(), true);
+	else
+		qlv_selectionChanged(0);
+}
+
+AccountManageDlg::~AccountManageDlg()
+{
+	psi->dialogUnregister(this);
+}
+
+void AccountManageDlg::qlv_selectionChanged(QListViewItem *lvi)
+{
+	AccountManageItem *i = (AccountManageItem *)lvi;
+	bool ok = i ? true: false;
+
+	pb_modify->setEnabled(ok);
+	pb_remove->setEnabled(ok);
+}
+
+void AccountManageDlg::add()
+{
+	AccountAddDlg *w = new AccountAddDlg(psi, 0);
+	w->show();
+}
+
+void AccountManageDlg::modify()
+{
+	modify(lv_accs->currentItem());
+}
+
+void AccountManageDlg::modify(QListViewItem *lvi)
+{
+	AccountManageItem *i = (AccountManageItem *)lvi;
+	if(!i)
+		return;
+
+	psi->modifyAccount(i->pa);
+}
+
+void AccountManageDlg::remove()
+{
+	AccountManageItem *i = (AccountManageItem *)lv_accs->currentItem();
+	if(!i)
+		return;
+
+	if(i->pa->isActive()) {
+		QMessageBox::information(this, tr("Error"), tr("Unable to remove the account, as it is currently active."));
+		return;
+	}
+
+	AccountRemoveDlg *w = new AccountRemoveDlg(psi->proxy(), i->pa->userAccount());
+	int n = w->exec();
+	if(n != QDialog::Accepted) {
+		delete w;
+		return;
+	}
+	delete w;
+	psi->removeAccount(i->pa);
+}
+
+void AccountManageDlg::accountAdded(PsiAccount *pa)
+{
+	new AccountManageItem(lv_accs, pa);
+}
+
+void AccountManageDlg::accountUpdated(PsiAccount *pa)
+{
+	AccountManageItem *i;
+	QListViewItemIterator it(lv_accs);
+	for(; (i = (AccountManageItem *)it.current()) ; ++it) {
+		if(i->pa == pa)
+			break;
+	}
+	if(!i)
+		return;
+
+	i->updateInfo();
+}
+
+void AccountManageDlg::accountRemoved(PsiAccount *pa)
+{
+	AccountManageItem *i;
+	QListViewItemIterator it(lv_accs);
+	for(; (i = (AccountManageItem *)it.current()) ; ++it) {
+		if(i->pa == pa)
+			break;
+	}
+	if(!i)
+		return;
+
+	delete i;
+
+	qlv_selectionChanged(lv_accs->currentItem());
+}
+
+
+//----------------------------------------------------------------------------
+// AccountAddDlg
+//----------------------------------------------------------------------------
+AccountAddDlg::AccountAddDlg(PsiCon *_psi, QWidget *parent, const char *name)
+:AccountAddUI(parent, name, false, WDestructiveClose)
+{
+	psi = _psi;
+	psi->dialogRegister(this);
+
+	setCaption(CAP(caption()));
+
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	connect(pb_add, SIGNAL(clicked()), SLOT(add()));
+	connect(le_name, SIGNAL(textChanged(const QString &)), SLOT(setAddButton(const QString &)));
+
+	QWhatsThis::add(ck_reg,
+		tr("Check this option if you don't yet have a Jabber account "
+		"and you want to register one.  Note that this will only work "
+		"on servers that allow anonymous registration."));
+
+	QString def = tr("Default");
+	QString aname = def;
+	int n = 0;
+	while(1) {
+		bool taken = false;
+		PsiAccountListIt it(psi->accountList());
+		for(PsiAccount *pa; (pa = it.current()); ++it) {
+			if(aname == pa->name()) {
+				taken = true;
+				break;
+			}
+		}
+
+		if(!taken)
+			break;
+		aname = def + '_' + QString::number(++n);
+	}
+
+	le_name->setText(aname);
+	le_name->setFocus();
+}
+
+AccountAddDlg::~AccountAddDlg()
+{
+	psi->dialogUnregister(this);
+}
+
+void AccountAddDlg::add()
+{
+	QString def = le_name->text();
+	QString aname = def;
+	int n = 0;
+	while(1) {
+		bool taken = false;
+		PsiAccountListIt it(psi->accountList());
+		for(PsiAccount *pa; (pa = it.current()); ++it) {
+			if(aname == pa->name()) {
+				taken = true;
+				break;
+			}
+		}
+
+		if(!taken)
+			break;
+		aname = def + '_' + QString::number(++n);
+	}
+	le_name->setText( aname );
+
+	if(ck_reg->isChecked()) {
+		AccountRegDlg *w = new AccountRegDlg(psi->proxy(), this);
+		int n = w->exec();
+		if(n != QDialog::Accepted) {
+			delete w;
+			return;
+		}
+
+		Jid jid = w->jid;
+		QString pass = w->pass;
+		bool opt_host = w->opt_host;
+		QString host = w->sp_host;
+		int port = w->sp_port;
+		bool ssl = w->ssl;
+		int proxy = w->proxy;
+
+		delete w;
+
+		psi->createAccount(le_name->text(), jid, pass, opt_host, host, port, ssl, proxy);
+	}
+	else {
+		psi->createAccount(le_name->text());
+	}
+
+	close();
+}
+
+void AccountAddDlg::setAddButton(const QString &s)
+{
+	pb_add->setEnabled(!s.isEmpty());
+}
+
+
+//----------------------------------------------------------------------------
+// AccountModifyDlg
+//----------------------------------------------------------------------------
+AccountModifyDlg::AccountModifyDlg(PsiAccount *_pa, QWidget *parent, const char *name)
+:AccountModifyUI(parent, name, false, WDestructiveClose)
+{
+	pa = _pa;
+	connect(pa->psi(), SIGNAL(pgpToggled(bool)), SLOT(pgpToggled(bool)));
+	pa->dialogRegister(this);
+
+	setCaption(CAP(caption()));
+	setIcon(IconsetFactory::icon("psi/account"));
+
+	const UserAccount &acc = pa->userAccount();
+
+	connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
+	connect(ck_host, SIGNAL(toggled(bool)), SLOT(hostToggled(bool)));
+	connect(pb_key, SIGNAL(clicked()), SLOT(chooseKey()));
+	connect(pb_keyclear, SIGNAL(clicked()), SLOT(clearKey()));
+	connect(pb_save, SIGNAL(clicked()), SLOT(save()));
+
+	le_pass->setEnabled(true);
+	le_host->setEnabled(false);
+	le_port->setEnabled(false);
+
+	gb_pgp->setEnabled(false);
+
+	connect(pb_vcard, SIGNAL(clicked()), SLOT(detailsVCard()));
+	connect(pb_changepw, SIGNAL(clicked()), SLOT(detailsChangePW()));
+
+	le_name->setText(acc.name);
+	le_jid->setText(acc.jid);
+
+	ck_ssl->setChecked(acc.opt_ssl);
+	connect(ck_ssl, SIGNAL(toggled(bool)), SLOT(sslToggled(bool)));
+
+	if (acc.opt_pass)
+		le_pass->setText(acc.pass);
+
+	ck_host->setChecked(acc.opt_host);
+	le_host->setText(acc.host);
+	le_port->setText(QString::number(acc.port));
+
+	le_resource->setText(acc.resource);
+	le_priority->setText(QString::number(acc.priority));
+
+	ck_plain->setChecked(acc.opt_plain);
+	ck_auto->setChecked(acc.opt_auto);
+	ck_reconn->setChecked(acc.opt_reconn);
+	ck_log->setChecked(acc.opt_log);
+	ck_keepAlive->setChecked(acc.opt_keepAlive);
+	ck_ignoreSSLWarnings->setChecked(acc.opt_ignoreSSLWarnings);
+	le_dtProxy->setText(acc.dtProxy.full());
+
+	keyID = acc.pgpSecretKeyID;
+	updateUserID();
+	if(pa->psi()->pgp()) {
+		gb_pgp->setEnabled(true);
+	}
+
+	pc = pa->psi()->proxy()->createProxyChooser(gb_proxy);
+	replaceWidget(lb_proxychooser, pc);
+	pc->setCurrentItem(acc.proxy_index);
+
+	if(le_name->text().isEmpty())
+		le_name->setFocus();
+	else if(le_jid->text().isEmpty())
+		le_jid->setFocus();
+	else
+		pb_save->setFocus();
+
+	// QWhatsThis helpers
+	QWhatsThis::add(ck_plain,
+		tr("Normally, Psi logs in using the <i>digest</i> authentication "
+		"method.  Check this box to force a plain text login "
+		"to the Jabber server. Use this option only if you have "
+		"problems connecting with the normal login procedure, as it "
+		"makes your connection potentially vulnerable to "
+		"attacks."));
+	QWhatsThis::add(ck_auto,
+		tr("Automatically login to this account on Psi startup.  Useful if "
+		"you have Psi automatically launched when an Internet "
+		"connection is detected."));
+	QWhatsThis::add(ck_reconn,
+		tr("Makes Psi try to reconnect if the connection was broken.  "
+		"Useful, if you have an unstable connection and have to "
+		"reconnect often."));
+	QWhatsThis::add(ck_log,
+		tr("Keep a log of message history.  Disable this "
+		"option if you want to conserve disk space or if you need "
+		"maximum security."));
+	QWhatsThis::add(ck_keepAlive,
+		tr("Sends so called \"Keep-alive\" packets periodically.  "
+		"It is useful if your connection is set to be "
+		"automatically disconnected after a certain period of "
+		"inactivity (for example, by your ISP) and you want to keep it "
+		"up all the time."));
+	QWhatsThis::add(ck_ignoreSSLWarnings,
+		tr("Ignores all the SSL warnings, for example, about "
+		"incorrect certificates.  Useful if your server doesn't "
+		"use a validated SSL certificate and you are "
+		"annoyed with warnings."));
+	QWhatsThis::add(ck_ssl,
+		tr("Check this option to use an encrypted SSL connection to "
+		"the Jabber server.  You may use this option if your "
+		"server supports it and if you have the necessary QSSL "
+		"plugin installed.  For more information, check the "
+		"Psi homepage."));
+	QWhatsThis::add(ck_host,
+		tr("Use this option for manual configuration of your Jabber host "
+		"if it is not the same as the host you are connecting to.  This option is mostly useful "
+		"if you have some sort of proxy route on your "
+		"local machine (i.e. you connect to localhost), but your "
+		"account is registered on an external server."));
+	QWhatsThis::add(le_resource,
+		tr("You can have multiple clients connected to the Jabber server "
+		"with your single account.  Each login is distinguished by a \"resource\" "
+		"name, which you can specify in this field."));
+	QWhatsThis::add(le_priority,
+		tr("<p>You can have multiple clients connected to the Jabber "
+		"server with your single account.  In such a situation, "
+		"the client with the highest priority (that is specified in "
+		"this field) will be the one that will receive "
+		"all incoming events.</p>"
+		"<p>For example, if you have a permanent connection "
+		"to the Internet at your work location, and have a dial-up at home, "
+		"you can have your Jabber client permanently running at work "
+		"with a low priority, and you can still use the same account "
+		"from home, using a client with higher priority to "
+		"temporary \"disable\" the lower priority client at work.</p>"));
+
+	resize(minimumSize());
+}
+
+AccountModifyDlg::~AccountModifyDlg()
+{
+	pa->dialogUnregister(this);
+}
+
+void AccountModifyDlg::updateUserID()
+{
+	if(keyID.isEmpty()) {
+		setKeyID(false);
+	}
+	else {
+		QString userID = QString::null;
+		if(pa->psi()->pgp()) {
+			OpenPGP::KeyList list = pa->psi()->pgp()->secretKeys();
+			for(OpenPGP::KeyList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+				const OpenPGP::Key &k = *it;
+				if(k.keyID() == keyID) {
+					userID = k.userID();
+					break;
+				}
+			}
+		}
+		if(userID.isNull())
+			setKeyID(true, tr("Unknown Key: %1").arg(keyID.left(8)));
+		else
+			setKeyID(true, userID);
+	}
+}
+
+void AccountModifyDlg::setKeyID(bool b, const QString &s)
+{
+	if(b) {
+		lb_keyname->setText(s);
+		lb_keyname->setMinimumWidth(100);
+		lb_keyicon->setEnabled(true);
+		lb_keyname->setEnabled(true);
+		pb_keyclear->setEnabled(true);
+	}
+	else {
+		lb_keyname->setText(tr("No Key Selected"));
+		lb_keyicon->setEnabled(false);
+		lb_keyname->setEnabled(false);
+		pb_keyclear->setEnabled(false);
+	}
+}
+
+void AccountModifyDlg::pgpToggled(bool b)
+{
+	if(b) {
+		gb_pgp->setEnabled(true);
+	}
+	else {
+		gb_pgp->setEnabled(false);
+	}
+	updateUserID();
+}
+
+void AccountModifyDlg::setPassword(const QString &pw)
+{
+	if (!le_pass->text().isEmpty())
+		le_pass->setText(pw);
+}
+
+void AccountModifyDlg::sslToggled(bool on)
+{
+	if(on && !QCA::isSupported(QCA::CAP_TLS)) {
+		QMessageBox::information(this, tr("SSL error"), tr("Cannot enable SSL/TLS.  Plugin not found."));
+		ck_ssl->setChecked(false);
+		return;
+	}
+
+	le_port->setText(on ? "5223": "5222");
+}
+
+void AccountModifyDlg::hostToggled(bool on)
+{
+	le_host->setEnabled(on);
+	le_port->setEnabled(on);
+}
+
+void AccountModifyDlg::chooseKey()
+{
+	OpenPGP::KeyList list = pa->psi()->pgp()->secretKeys();
+	PGPKeyDlg *w = new PGPKeyDlg(list, keyID, this);
+	w->setCaption(tr("Secret Key"));
+	int r = w->exec();
+	QString key;
+	if(r == QDialog::Accepted)
+		key = w->keyID();
+	delete w;
+
+	if(!key.isEmpty()) {
+		keyID = key;
+		updateUserID();
+	}
+}
+
+void AccountModifyDlg::clearKey()
+{
+	setKeyID(false);
+	keyID = "";
+}
+
+void AccountModifyDlg::detailsVCard()
+{
+	pa->changeVCard();
+}
+
+void AccountModifyDlg::detailsChangePW()
+{
+	pa->changePW();
+}
+
+void AccountModifyDlg::save()
+{
+	UserAccount acc = pa->userAccount();
+
+	if(le_name->text().isEmpty()) {
+		QMessageBox::information(this, tr("Error"), tr("You must specify a name for the account before you may save it."));
+		return;
+	}
+
+	Jid newJid( le_jid->text() );
+	if ( newJid.user().isEmpty() || newJid.host().isEmpty() ) {
+		QMessageBox::information(this, tr("Error"), tr("<i>Jabber ID</i> must be specified in the format <i>user@host</i>."));
+		return;
+	}
+
+	// do not allow duplicate account names
+	QString def = le_name->text();
+	QString aname = def;
+	int n = 0;
+	{
+		PsiAccountListIt it(pa->psi()->accountList());
+		for(PsiAccount *pa; (pa = it.current()); ++it)
+			if(aname == pa->name())
+				n++;
+	}
+
+	if ( aname == acc.name )
+		n--;
+
+	if ( n )
+		aname = def + '_' + QString::number(++n);
+	le_name->setText( aname );
+
+	acc.name = le_name->text();
+	acc.jid = le_jid->text();
+	acc.pass = le_pass->text();
+	acc.opt_pass = !acc.pass.isEmpty();
+
+	acc.opt_host = ck_host->isChecked();
+	acc.host = le_host->text();
+	acc.port = le_port->text().toInt();
+
+	acc.resource = le_resource->text();
+	acc.priority = le_priority->text().toInt();
+
+	acc.opt_ssl = ck_ssl->isChecked();
+	acc.opt_plain = ck_plain->isChecked();
+	acc.opt_auto = ck_auto->isChecked();
+	acc.opt_reconn = ck_reconn->isChecked();
+	acc.opt_log = ck_log->isChecked();
+	acc.opt_keepAlive = ck_keepAlive->isChecked();
+	acc.opt_ignoreSSLWarnings = ck_ignoreSSLWarnings->isChecked();
+	acc.dtProxy = le_dtProxy->text();
+
+	acc.pgpSecretKeyID = keyID;
+
+	acc.proxy_index = pc->currentItem();
+
+	if(pa->isActive()) {
+		QMessageBox::information(this, tr("Warning"), tr("This account is currently active, so certain changes may not take effect until the next login."));
+	}
+
+	pa->setUserAccount(acc);
+
+	accept();
+}
+
+
+//----------------------------------------------------------------------------
+// AccountRegDlg
+//----------------------------------------------------------------------------
+AccountRegDlg::AccountRegDlg(ProxyManager *_proxyman, QWidget *parent, const char *name)
+:AccountRegUI(parent, name, false)
+{
+	setCaption(CAP(caption()));
+
+	le_host->setEnabled(false);
+	le_port->setEnabled(false);
+
+	connect(pb_reg, SIGNAL(clicked()), SLOT(reg()));
+	connect(ck_ssl, SIGNAL(toggled(bool)), SLOT(sslToggled(bool)));
+	connect(ck_host, SIGNAL(toggled(bool)), SLOT(hostToggled(bool)));
+
+	proxyman = _proxyman;
+	pc = proxyman->createProxyChooser(gb_proxy);
+	replaceWidget(lb_proxychooser, pc);
+	pc->fixTabbing(le_confirm, ck_ssl);
+	pc->setCurrentItem(0);
+
+	le_port->setText("5222");
+	le_host->setFocus();
+
+	client = new MiniClient;
+	connect(client, SIGNAL(handshaken()), SLOT(client_handshaken()));
+	connect(client, SIGNAL(error()), SLOT(client_error()));
+}
+
+AccountRegDlg::~AccountRegDlg()
+{
+	delete client;
+}
+
+/*void AccountRegDlg::closeEvent(QCloseEvent *e)
+{
+	e->ignore();
+	reject();
+}*/
+
+void AccountRegDlg::done(int r)
+{
+	if(busy->isActive()) {
+		int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the registration?"), tr("&Yes"), tr("&No"));
+		if(n != 0)
+			return;
+	}
+	QDialog::done(r);
+}
+
+void AccountRegDlg::sslToggled(bool on)
+{
+	if(on && !QCA::isSupported(QCA::CAP_TLS)) {
+		QMessageBox::information(this, tr("SSL error"), tr("Cannot enable SSL/TLS.  Plugin not found."));
+		ck_ssl->setChecked(false);
+		return;
+	}
+
+	le_port->setText(on ? "5223": "5222");
+}
+
+void AccountRegDlg::hostToggled(bool on)
+{
+	le_host->setEnabled(on);
+	le_port->setEnabled(on);
+}
+
+void AccountRegDlg::reg()
+{
+	// sanity check
+	Jid j(le_jid->text());
+	if ( j.user().isEmpty() || j.host().isEmpty() ) {
+		QMessageBox::information(this, tr("Error"), tr("<i>Jabber ID</i> must be specified in the format <i>user@host</i>."));
+		return;
+	}
+
+	if(le_pass->text().isEmpty()) {
+		QMessageBox::information(this, tr("Error"), tr("You must fill out the fields properly before you can register."));
+		return;
+	}
+
+	if(le_pass->text() != le_confirm->text()) {
+		QMessageBox::information(this, tr("Error"), tr("Password and confirmation do not match.  Please enter them again."));
+		le_pass->setText("");
+		le_confirm->setText("");
+		le_pass->setFocus();
+		return;
+	}
+
+	busy->start();
+	block();
+
+	jid = j;
+	ssl = ck_ssl->isChecked();
+	pass = le_pass->text();
+	opt_host = ck_host->isChecked();
+	sp_host = le_host->text();
+	sp_port = le_port->text().toInt();
+
+	client->connectToServer(jid, ssl, opt_host ? sp_host : QString(), sp_port, proxyman, pc->currentItem(), 0);
+}
+
+void AccountRegDlg::client_handshaken()
+{
+	// try to register an account
+	JT_Register *reg = new JT_Register(client->client()->rootTask());
+	connect(reg, SIGNAL(finished()), SLOT(reg_finished()));
+	reg->reg(jid.user(), pass);
+	reg->go(true);
+}
+
+void AccountRegDlg::client_error()
+{
+	busy->stop();
+	unblock();
+}
+
+void AccountRegDlg::reg_finished()
+{
+	JT_Register *reg = (JT_Register *)sender();
+
+	client->close();
+	busy->stop();
+
+	if(reg->success()) {
+		QMessageBox::information(this, tr("Success"), tr("The account was registered successfully."));
+		proxy = pc->currentItem();
+		accept();
+		return;
+	}
+	else if(reg->statusCode() != Task::ErrDisc) {
+		unblock();
+		QMessageBox::critical(this, tr("Error"), QString(tr("There was an error registering the account.\nReason: %1")).arg(reg->statusString()));
+	}
+}
+
+void AccountRegDlg::block()
+{
+	gb_account->setEnabled(false);
+	gb_proxy->setEnabled(false);
+	gb_advanced->setEnabled(false);
+	pb_reg->setEnabled(false);
+}
+
+void AccountRegDlg::unblock()
+{
+	gb_account->setEnabled(true);
+	gb_proxy->setEnabled(true);
+	gb_advanced->setEnabled(true);
+	pb_reg->setEnabled(true);
+	le_jid->setFocus();
+}
+
+
+//----------------------------------------------------------------------------
+// AccountRemoveDlg
+//----------------------------------------------------------------------------
+class AccountRemoveDlg::Private
+{
+public:
+	Private() {}
+
+	UserAccount acc;
+	QButtonGroup *bg;
+	ProxyManager *proxyman;
+};
+
+AccountRemoveDlg::AccountRemoveDlg(ProxyManager *proxyman, const UserAccount &acc, QWidget *parent, const char *name)
+:AccountRemoveUI(parent, name, false)
+{
+	d = new Private;
+	d->acc = acc;
+	d->proxyman = proxyman;
+
+	setCaption(CAP(caption()));
+
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	connect(pb_remove, SIGNAL(clicked()), SLOT(remove()));
+
+	d->bg = new QButtonGroup(0);
+	d->bg->insert(rb_remove, 0);
+	d->bg->insert(rb_removeAndUnreg, 1);
+	connect(d->bg, SIGNAL(clicked(int)), SLOT(bg_clicked(int)));
+	d->bg->setButton(0);
+	bg_clicked(0);
+
+	pb_close->setFocus();
+
+	client = new MiniClient;
+	connect(client, SIGNAL(handshaken()), SLOT(client_handshaken()));
+	connect(client, SIGNAL(error()), SLOT(client_error()));
+}
+
+AccountRemoveDlg::~AccountRemoveDlg()
+{
+	delete client;
+
+	delete d->bg;
+	delete d;
+}
+
+/*void AccountRemoveDlg::closeEvent(QCloseEvent *e)
+{
+	e->ignore();
+	reject();
+}*/
+
+void AccountRemoveDlg::done(int r)
+{
+	if(busy->isActive()) {
+		int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the unregistration?"), tr("&Yes"), tr("&No"));
+		if(n != 0)
+			return;
+	}
+	QDialog::done(r);
+}
+
+void AccountRemoveDlg::bg_clicked(int x)
+{
+	if(x == 0) {
+		lb_pass->setEnabled(false);
+		le_pass->setEnabled(false);
+	}
+	else if(x == 1) {
+		lb_pass->setEnabled(true);
+		le_pass->setEnabled(true);
+		le_pass->setFocus();
+	}
+}
+
+void AccountRemoveDlg::remove()
+{
+	bool unreg = rb_removeAndUnreg->isChecked();
+
+	if(unreg) {
+		if(le_pass->text() != d->acc.pass) {
+			QMessageBox::information(this, tr("Error"), tr("Password does not match account.  Please try again."));
+			le_pass->setFocus();
+			return;
+		}
+	}
+
+	int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to remove <b>%1</b> ?").arg(d->acc.name), tr("&Yes"), tr("&No"));
+	if(n != 0)
+		return;
+
+	if(!unreg) {
+		accept();
+		return;
+	}
+
+	busy->start();
+	gb_account->setEnabled(false);
+	pb_remove->setEnabled(false);
+
+	Jid j = d->acc.jid;
+	j.setResource(d->acc.resource);
+	client->connectToServer(j, d->acc.opt_ssl, d->acc.opt_host ? d->acc.host : QString(), d->acc.port, d->proxyman, d->acc.proxy_index, &d->acc.pass);
+}
+
+void AccountRemoveDlg::client_handshaken()
+{
+	// try to unregister an account
+	JT_Register *reg = new JT_Register(client->client()->rootTask());
+	connect(reg, SIGNAL(finished()), SLOT(unreg_finished()));
+	reg->unreg();
+	reg->go(true);
+}
+
+void AccountRemoveDlg::client_error()
+{
+	busy->stop();
+	gb_account->setEnabled(true);
+	pb_remove->setEnabled(true);
+}
+
+void AccountRemoveDlg::unreg_finished()
+{
+	JT_Register *reg = (JT_Register *)sender();
+
+	client->close();
+	busy->stop();
+
+	if(reg->success()) {
+		QMessageBox::information(this, tr("Success"), tr("The account was unregistered successfully."));
+		accept();
+		return;
+	}
+	else if(reg->statusCode() != Task::ErrDisc) {
+		gb_account->setEnabled(true);
+		pb_remove->setEnabled(true);
+		QMessageBox::critical(this, tr("Error"), QString(tr("There was an error unregistering the account.\nReason: %1")).arg(reg->statusString()));
+	}
+}
Index: psi/tags/0.10-os2-b1/src/accountdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/accountdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/accountdlg.h	(revision 38)
@@ -0,0 +1,227 @@
+/*
+ * accountdlg.h - dialogs for manipulating PsiAccounts
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ACCOUNTDLG_H
+#define ACCOUNTDLG_H
+
+#include<qca.h>
+#include"ui_accountmanage.h"
+#include"ui_accountadd.h"
+#include"ui_accountreg.h"
+#include"ui_accountremove.h"
+#include"ui_accountmodify.h"
+#include"ui_pgpkey.h"
+#include"xmpp.h"
+#include"openpgp.h"
+
+namespace XMPP
+{
+	class Jid;
+	class Client;
+}
+
+using namespace XMPP;
+
+class PsiCon;
+class PsiAccount;
+class QListViewItem;
+class UserAccount;
+class ProxyManager;
+class ProxyChooser;
+
+namespace QCA
+{
+	class Cert;
+}
+
+class MiniClient : public QObject
+{
+	Q_OBJECT
+public:
+	MiniClient(QObject *parent=0);
+	~MiniClient();
+
+	void reset();
+	void connectToServer(const Jid &j, bool ssl, const QString &host, int port, ProxyManager *pm, int proxy, QString *pass);
+	void close();
+	Client *client();
+
+signals:
+	void handshaken();
+	void error();
+
+private slots:
+	void tls_handshaken();
+	void cs_connected();
+	void cs_securityLayerActivated(int);
+	void cs_needAuthParams(bool, bool, bool);
+	void cs_authenticated();
+	void cs_connectionClosed();
+	void cs_delayedCloseFinished();
+	void cs_warning(int);
+	void cs_error(int);
+
+private:
+	AdvancedConnector *conn;
+	ClientStream *stream;
+	QCA::TLS *tls;
+	QCATLSHandler *tlsHandler;
+	QPtrList<QCA::Cert> certList;
+	Client *_client;
+	Jid j;
+	QString pass;
+};
+
+class AccountManageDlg : public AccountManageUI
+{
+	Q_OBJECT
+public:
+	AccountManageDlg(PsiCon *, const char *name=0);
+	~AccountManageDlg();
+
+private slots:
+	void qlv_selectionChanged(QListViewItem *);
+	void add();
+	void modify();
+	void modify(QListViewItem *);
+	void remove();
+	void accountAdded(PsiAccount *);
+	void accountUpdated(PsiAccount *);
+	void accountRemoved(PsiAccount *);
+
+private:
+	PsiCon *psi;
+};
+
+class AccountAddDlg : public AccountAddUI
+{
+	Q_OBJECT
+public:
+	AccountAddDlg(PsiCon *, QWidget *parent=0, const char *name=0);
+	~AccountAddDlg();
+
+private slots:
+	void add();
+	void setAddButton(const QString &);
+
+private:
+	PsiCon *psi;
+};
+
+class AccountModifyDlg : public AccountModifyUI
+{
+	Q_OBJECT
+public:
+	AccountModifyDlg(PsiAccount *, QWidget *parent=0, const char *name=0);
+	~AccountModifyDlg();
+
+	void setPassword(const QString &);
+
+private slots:
+	void sslToggled(bool);
+	void hostToggled(bool);
+
+	void detailsVCard();
+	void detailsChangePW();
+
+	void save();
+
+	void pgpToggled(bool);
+	void chooseKey();
+	void clearKey();
+
+private:
+	PsiAccount *pa;
+	ProxyChooser *pc;
+	QString keyID;
+
+	void updateUserID();
+	void setKeyID(bool b, const QString &s="");
+};
+
+class AccountRegDlg : public AccountRegUI
+{
+	Q_OBJECT
+public:
+	AccountRegDlg(ProxyManager *, QWidget *parent=0, const char *name=0);
+	~AccountRegDlg();
+
+	Jid jid;
+	QString sp_host, pass;
+	int sp_port;
+	bool ssl, opt_host;
+	int proxy;
+
+protected:
+	// reimplemented
+	//void closeEvent(QCloseEvent *);
+
+public slots:
+	void done(int);
+
+private slots:
+	void sslToggled(bool);
+	void hostToggled(bool);
+	void reg();
+
+	void client_handshaken();
+	void client_error();
+
+	void reg_finished();
+
+	void block();
+	void unblock();
+
+private:
+	ProxyManager *proxyman;
+	ProxyChooser *pc;
+	MiniClient *client;
+};
+
+class AccountRemoveDlg : public AccountRemoveUI
+{
+	Q_OBJECT
+public:
+	AccountRemoveDlg(ProxyManager *, const UserAccount &, QWidget *parent=0, const char *name=0);
+	~AccountRemoveDlg();
+
+protected:
+	// reimplemented
+	//void closeEvent(QCloseEvent *);
+
+public slots:
+	void done(int);
+
+private slots:
+	void remove();
+	void bg_clicked(int);
+
+	void client_handshaken();
+	void client_error();
+	void unreg_finished();
+
+private:
+	class Private;
+	Private *d;
+
+	MiniClient *client;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/actionlist.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/actionlist.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/actionlist.cpp	(revision 38)
@@ -0,0 +1,220 @@
+/*
+ * actionlist.cpp - the customizeable action list
+ * Copyright (C) 2004  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "actionlist.h"
+
+#include <qobject.h>
+#include <qdict.h>
+
+#include "iconaction.h"
+
+//----------------------------------------------------------------------------
+// ActionList
+//----------------------------------------------------------------------------
+
+class ActionList::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private() { }
+	Private( const Private & );
+
+	QString name;
+	int id;
+	QStringList sortedActions;
+	QDict<IconAction> actions;
+
+public slots:
+	void actionDestroyed(QObject *);
+};
+
+ActionList::ActionList( QString name, int id, bool autoDelete )
+{
+	d = new Private();
+	d->actions.setAutoDelete( autoDelete );
+
+	d->name = name;
+	d->id   = id;
+}
+
+ActionList::ActionList( const ActionList &from )
+{
+	d = new Private( *from.d );
+}
+
+ActionList::~ActionList()
+{
+	delete d;
+}
+
+QString ActionList::name() const
+{
+	return d->name;
+}
+
+int ActionList::id() const
+{
+	return d->id;
+}
+
+IconAction *ActionList::action( QString name ) const
+{
+	return d->actions[name];
+}
+
+QStringList ActionList::actions() const
+{
+	return d->sortedActions;
+}
+
+void ActionList::addAction( QString name, IconAction *action )
+{
+	d->sortedActions << name;
+
+	if ( action ) {
+		action->setName( name.latin1() );
+		d->actions.insert( name, action );
+		d->connect( action, SIGNAL( destroyed(QObject *) ), d, SLOT( actionDestroyed(QObject *) ) );
+	}
+}
+
+void ActionList::clear()
+{
+	d->actions.clear();
+}
+
+ActionList::Private::Private( const Private &from )
+	: QObject()
+{
+	name = from.name;
+	id   = from.id;
+	
+	actions = from.actions;
+	actions.setAutoDelete( from.actions.autoDelete() );
+	
+	sortedActions = from.sortedActions;
+}
+
+void ActionList::Private::actionDestroyed(QObject *obj)
+{
+	bool autoDelete = actions.autoDelete();
+	actions.setAutoDelete( false );
+
+	actions.remove( obj->name() );
+
+	actions.setAutoDelete( autoDelete );
+}
+
+//----------------------------------------------------------------------------
+// MetaActionList
+//----------------------------------------------------------------------------
+
+class MetaActionList::Private
+{
+public:
+	Private() { }
+
+	QPtrList<ActionList> lists;
+};
+
+MetaActionList::MetaActionList()
+{
+	d = new Private();
+	d->lists.setAutoDelete( true );
+}
+
+MetaActionList::~MetaActionList()
+{
+	delete d;
+}
+
+ActionList *MetaActionList::actionList( QString name ) const
+{
+	QPtrListIterator<ActionList> it(d->lists);
+	for ( ; it.current() != 0; ++it ) {
+		if ( it.current()->name() == name )
+			return it.current();
+	}
+
+	return 0;
+}
+
+QPtrList<ActionList> MetaActionList::actionLists( int id ) const
+{
+	QPtrList<ActionList> list;
+
+	for ( int i = 0; i < 32; i++ ) {
+		if ( !(id & ( 1 << i )) )
+			continue;
+
+		QPtrListIterator<ActionList> it(d->lists);
+		for ( ; it.current() != 0; ++it ) {
+			ActionList *l = it.current();
+
+			if ( l->id() & ( 1 << i ) )
+				list.append( l );
+		}
+	}
+
+	return list;
+}
+
+ActionList MetaActionList::suitableActions( int id ) const
+{
+	QPtrList<ActionList> lists = actionLists( id );
+	ActionList actions("", 0, false), *list;
+
+	QPtrListIterator<ActionList> it( lists );
+	while ( (list = it.current()) ) {
+		++it;
+		QStringList actionList = list->actions();
+		QStringList::Iterator it2 = actionList.begin();
+		for ( ; it2 != actionList.end(); ++it2 )
+			actions.addAction( *it2, list->action( *it2 ) );
+	}
+
+	return actions;
+}
+
+QStringList MetaActionList::actionLists() const
+{
+	QStringList names;
+
+	QPtrListIterator<ActionList> it(d->lists);
+	for ( ; it.current() != 0; ++it )
+		names << it.current()->name();
+
+	return names;
+}
+
+void MetaActionList::addList( ActionList *list )
+{
+	if ( list )
+		d->lists.append( list );
+}
+
+void MetaActionList::clear()
+{
+	QPtrListIterator<ActionList> it(d->lists);
+	for ( ; it.current() != 0; ++it )
+		it.current()->clear();
+}
+
+#include "actionlist.moc"
Index: psi/tags/0.10-os2-b1/src/actionlist.h
===================================================================
--- psi/tags/0.10-os2-b1/src/actionlist.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/actionlist.h	(revision 38)
@@ -0,0 +1,73 @@
+/*
+ * actionlist.h - the customizeable action list
+ * Copyright (C) 2004  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ACTIONLIST_H
+#define ACTIONLIST_H
+
+#include <qstringlist.h>
+#include <qptrlist.h>
+
+class IconAction;
+
+class ActionList
+{
+public:
+	ActionList(QString name, int id, bool autoDelete = true);
+	ActionList(const ActionList &);
+	~ActionList();
+
+	QString name() const;
+	int id() const;
+
+	IconAction *action( QString name ) const;
+	QStringList actions() const;
+
+	void addAction( QString name, IconAction *action );
+
+	void clear();
+
+public:
+	class Private;
+private:
+	Private *d;
+};
+
+class MetaActionList
+{
+public:
+	MetaActionList();
+	~MetaActionList();
+
+	ActionList *actionList( QString name ) const;
+	QPtrList<ActionList> actionLists( int id ) const;
+	QStringList actionLists() const;
+
+	ActionList suitableActions( int id ) const;
+
+	void addList( ActionList * );
+
+	void clear();
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/adduserdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/adduserdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/adduserdlg.cpp	(revision 38)
@@ -0,0 +1,280 @@
+/*
+ * adduserdlg.cpp - dialog for adding contacts
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"adduserdlg.h"
+
+#include<qlabel.h>
+#include<qlayout.h>
+#include<qpushbutton.h>
+#include<qmessagebox.h>
+#include<qcombobox.h>
+#include<qgroupbox.h>
+#include<qstringlist.h>
+#include<qlineedit.h>
+#include<qcheckbox.h>
+#include"xmpp_tasks.h"
+#include"psiaccount.h"
+#include"contactview.h"
+#include"busywidget.h"
+#include"common.h"
+#include"iconwidget.h"
+#include"tasklist.h"
+#include"xmpp_vcard.h"
+#include"vcardfactory.h"
+#include"infodlg.h"
+
+class AddUserDlg::Private
+{
+public:
+	Private() {}
+
+	PsiAccount *pa;
+	BusyWidget *busy;
+	QStringList services;
+	JT_Gateway *jt;
+	TaskList *tasks;
+};
+
+AddUserDlg::AddUserDlg(const QStringList &services, const QStringList &names, const QStringList &groups, PsiAccount *pa)
+:AddUserUI(0, 0, false, WDestructiveClose)
+{
+	d = new Private;
+	d->pa = pa;
+	d->pa->dialogRegister(this);
+	connect(d->pa, SIGNAL(updatedActivity()), SLOT(pa_updatedActivity()));
+	d->services = services;
+	d->jt = 0;
+	d->tasks = new TaskList;
+	connect(d->tasks, SIGNAL(started()),  busy, SLOT(start()));
+	connect(d->tasks, SIGNAL(finished()), busy, SLOT(stop()));
+
+	setCaption(CAP(caption()));
+	setIcon(IconsetFactory::icon("psi/addContact"));
+
+	d->busy = busy;
+
+	QStringList::ConstIterator it1 = services.begin();
+	QStringList::ConstIterator it2 = names.begin();
+	for(; it1 != services.end(); ++it1, ++it2)
+		cb_service->insertItem(is->status(*it1, STATUS_ONLINE), *it2);
+	connect(cb_service, SIGNAL(activated(int)), SLOT(serviceActivated(int)));
+
+	connect(le_transPrompt, SIGNAL(textChanged(const QString &)), SLOT(le_transPromptChanged(const QString &)));
+	pb_transGet->setEnabled(false);
+
+	QString str = tr("<None>");
+	cb_group->insertItem(str);
+	cb_group->insertStringList(groups);
+	str = ContactView::tr("Hidden");
+	if(!groups.contains(str))
+		cb_group->insertItem(str);
+	cb_group->setAutoCompletion(true);
+
+	pb_add->setDefault(true);
+	connect(pb_add, SIGNAL(clicked()), SLOT(ok()));
+	connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
+	connect(pb_transGet, SIGNAL(clicked()), SLOT(getTransID()));
+
+	connect(tb_vCard, SIGNAL(clicked()), SLOT(getVCardActivated()));
+	connect(tb_resolveNick, SIGNAL(clicked()), SLOT(resolveNickActivated()));
+
+	ck_authreq->setChecked(true);
+	ck_close->setChecked(true);
+
+	le_jid->setFocus();
+}
+
+AddUserDlg::~AddUserDlg()
+{
+	delete d->tasks;
+	d->pa->dialogUnregister(this);
+	delete d;
+}
+
+void AddUserDlg::pa_updatedActivity()
+{
+	if(!d->pa->loggedIn())
+		close();
+}
+
+void AddUserDlg::ok()
+{
+	if(le_jid->text().isEmpty()) {
+		QMessageBox::information(this, tr("Add User: Error"), tr("Please fill in the Jabber ID of the person you wish to add."));
+		return;
+	}
+	Jid j(le_jid->text().stripWhiteSpace());
+	if(!j.isValid()) {
+		QMessageBox::information(this, tr("Add User: Error"), tr("The Jabber ID you entered is not valid!\nMake sure you enter a fully qualified Jabber ID."));
+		return;
+	}
+
+	QString gname = cb_group->currentText();
+	QStringList list;
+	if(gname != tr("<None>"))
+		list += gname;
+
+	add(j, le_nick->text(), list, ck_authreq->isChecked());
+
+	QMessageBox::information(this, tr("Add User: Success"), tr("Added %1 to your roster.").arg(j.full()));
+
+	if(ck_close->isChecked())
+		accept();
+	else {
+		le_jid->setText("");
+		le_nick->setText("");
+		le_jid->setFocus();
+	}
+}
+
+void AddUserDlg::serviceActivated(int x)
+{
+	if(d->jt) {
+		delete d->jt;
+		d->jt = 0;
+		d->busy->stop();
+	}
+	gb_trans->setEnabled(false);
+	le_transPrompt->setText("");
+
+	// Jabber entry
+	if(x == 0)
+		return;
+	--x;
+
+	if(x >= 0 && x < (int)d->services.count()) {
+		d->jt = new JT_Gateway(d->pa->client()->rootTask());
+		connect(d->jt, SIGNAL(finished()), SLOT(jt_getFinished()));
+		d->jt->get(Jid(d->services[x]));
+		d->jt->go(true);
+		d->tasks->append( d->jt );
+	}
+}
+
+void AddUserDlg::le_transPromptChanged(const QString &str)
+{
+	pb_transGet->setEnabled(!str.isEmpty());
+}
+
+void AddUserDlg::getTransID()
+{
+	cb_service->setEnabled(false);
+	le_transPrompt->setEnabled(false);
+	pb_transGet->setEnabled(false);
+
+	d->jt = new JT_Gateway(d->pa->client()->rootTask());
+	connect(d->jt, SIGNAL(finished()), SLOT(jt_setFinished()));
+	d->jt->set(Jid(d->services[cb_service->currentItem()-1]), le_transPrompt->text());
+	d->jt->go(true);
+	d->tasks->append( d->jt );
+}
+
+void AddUserDlg::jt_getFinished()
+{
+	JT_Gateway *jt = d->jt;
+	d->jt = 0;
+
+	if(jt->success()) {
+		gb_trans->setEnabled(true);
+		lb_transDesc->setText(jt->desc());
+	}
+	else {
+		errorGateway(cb_service->currentText(), jt->statusString());
+	}
+}
+
+void AddUserDlg::jt_setFinished()
+{
+	cb_service->setEnabled(true);
+	le_transPrompt->setEnabled(true);
+	pb_transGet->setEnabled(true);
+
+	JT_Gateway *jt = d->jt;
+	d->jt = 0;
+
+	if(jt->success()) {
+		le_jid->setText(jt->prompt());
+		le_nick->setText(le_transPrompt->text());
+		le_transPrompt->setText("");
+		le_jid->setCursorPosition(0);
+		le_nick->setCursorPosition(0);
+
+		le_nick->setFocus();
+		le_nick->selectAll();
+	}
+	else {
+		errorGateway(cb_service->currentText(), jt->statusString());
+		le_transPrompt->setFocus();
+	}
+}
+
+void AddUserDlg::errorGateway(const QString &str, const QString &err)
+{
+	QMessageBox::information(this, CAP(tr("Error")), tr("<qt>\n"
+		"There was an error getting the Service ID translation information from \"%1\".<br>"
+		"Reason: %2<br>"
+		"<br>"
+		"The service may not support this feature.  In this case you "
+		"will need to enter the Jabber ID manually for the contact you wish "
+		"to add.  Examples:<br>"
+		"<br>"
+		"&nbsp;&nbsp;jabberuser@somehost.com<br>"
+		"&nbsp;&nbsp;aoluser@[Jabber ID of AIM Transport]<br>"
+		"&nbsp;&nbsp;1234567@[Jabber ID of ICQ Transport]<br>"
+		"&nbsp;&nbsp;joe%hotmail.com@[Jabber ID of MSN Transport]<br>"
+		"&nbsp;&nbsp;yahooUser@[Jabber ID of Yahoo Transport]<br>"
+		"</qt>"
+		).arg(str).arg(err));
+}
+
+void AddUserDlg::getVCardActivated()
+{
+	Jid j(le_jid->text().stripWhiteSpace());
+
+	const VCard *vcard = VCardFactory::vcard(j);
+
+	VCard tmp;
+	if ( vcard )
+		tmp = *vcard;
+
+	InfoDlg *w = new InfoDlg(InfoDlg::Contact, j, tmp, d->pa, 0, 0, false);
+	w->show();
+
+	// automatically retrieve info if it doesn't exist
+	if(!vcard)
+		w->doRefresh();
+}
+
+void AddUserDlg::resolveNickActivated()
+{
+	Jid j(le_jid->text().stripWhiteSpace());
+	JT_VCard *jt = VCardFactory::getVCard(j, d->pa->client()->rootTask(), this, SLOT(resolveNickFinished()), false);
+	d->tasks->append( jt );
+}
+
+void AddUserDlg::resolveNickFinished()
+{
+	JT_VCard *jt = (JT_VCard *)sender();
+
+	if(jt->success()) {
+		if ( !jt->vcard().nickName().isEmpty() )
+			le_nick->setText( jt->vcard().nickName() );
+	}
+}
Index: psi/tags/0.10-os2-b1/src/adduserdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/adduserdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/adduserdlg.h	(revision 38)
@@ -0,0 +1,63 @@
+/*
+ * adduserdlg.h - dialog for adding contacts
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ADDUSERDLG_H
+#define ADDUSERDLG_H
+
+#include"ui_adduser.h"
+#include"xmpp.h"
+
+using namespace XMPP;
+
+class QString;
+class QStringList;
+class PsiAccount;
+
+class AddUserDlg : public AddUserUI
+{
+	Q_OBJECT
+public:
+	AddUserDlg(const QStringList &services, const QStringList &names, const QStringList &groups, PsiAccount *);
+	~AddUserDlg();
+
+signals:
+	void add(const Jid &, const QString &, const QStringList &, bool authReq);
+
+private slots:
+	void ok();
+	void serviceActivated(int);
+	void getTransID();
+	void pa_updatedActivity();
+	void jt_getFinished();
+	void jt_setFinished();
+	void le_transPromptChanged(const QString &);
+
+	void getVCardActivated();
+	void resolveNickActivated();
+	void resolveNickFinished();
+
+private:
+	class Private;
+	Private *d;
+
+	void errorGateway(const QString &str, const QString &err);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/alerticon.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/alerticon.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/alerticon.cpp	(revision 38)
@@ -0,0 +1,293 @@
+/*
+ * alerticon.cpp - class for handling animating alert icons
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "alerticon.h"
+#include "common.h"
+#include <qtimer.h>
+#include <qapplication.h>
+
+//----------------------------------------------------------------------------
+// MetaAlertIcon
+//----------------------------------------------------------------------------
+
+class MetaAlertIcon : public QObject
+{
+	Q_OBJECT
+public:
+	MetaAlertIcon();
+	~MetaAlertIcon();
+
+	Impix blank16() const;
+	int framenumber() const;
+
+signals:
+	void updateFrame(int frame);
+	void update();
+
+public slots:
+	void updateAlertStyle();
+
+private slots:
+	void animTimeout();
+
+private:
+	QTimer *animTimer;
+	int frame;
+	Impix _blank16;
+};
+
+static MetaAlertIcon *metaAlertIcon = 0;
+
+MetaAlertIcon::MetaAlertIcon()
+: QObject(qApp)
+{
+	animTimer = new QTimer(this);
+	connect(animTimer, SIGNAL(timeout()), SLOT(animTimeout()));
+	animTimer->start(120 * 5);
+	frame = 0;
+
+	// blank icon
+	QImage blankImg(16, 16, 32);
+	blankImg.fill(0);
+	blankImg.setAlphaBuffer(true);
+	_blank16.setImage(blankImg);
+}
+
+MetaAlertIcon::~MetaAlertIcon()
+{
+}
+
+Impix MetaAlertIcon::blank16() const
+{
+	return _blank16;
+}
+
+void MetaAlertIcon::animTimeout()
+{
+	frame = !frame;
+	emit updateFrame(frame);
+}
+
+void MetaAlertIcon::updateAlertStyle()
+{
+	emit update();
+}
+
+int MetaAlertIcon::framenumber() const
+{
+	return frame;
+}
+
+//----------------------------------------------------------------------------
+// AlertIcon::Private
+//----------------------------------------------------------------------------
+
+class AlertIcon::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(AlertIcon *_ai);
+	~Private();
+
+	void init();
+
+public slots:
+	void update();
+	void activated(bool playSound);
+	void stop();
+
+	void updateFrame(int frame);
+	void pixmapChanged(const QPixmap &);
+
+public:
+	AlertIcon *ai;
+	Icon *real;
+	bool isActivated;
+	Impix impix;
+};
+
+AlertIcon::Private::Private(AlertIcon *_ai)
+{
+	if ( !metaAlertIcon )
+		metaAlertIcon = new MetaAlertIcon();
+
+	ai = _ai;
+	real = 0;
+	isActivated = false;
+}
+
+AlertIcon::Private::~Private()
+{
+	if ( isActivated )
+		stop();
+
+	if ( real )
+		delete real;
+}
+
+void AlertIcon::Private::init()
+{
+	connect(metaAlertIcon, SIGNAL(update()), SLOT(update()));
+	connect(real, SIGNAL(iconModified(const QPixmap &)), SLOT(pixmapChanged(const QPixmap &)));
+
+	if ( option.alertStyle == 2 && real->isAnimated() )
+		impix = real->frameImpix();
+	else
+		impix = real->impix();
+}
+
+void AlertIcon::Private::update()
+{
+	stop();
+	activated(false);
+}
+
+void AlertIcon::Private::activated(bool playSound)
+{
+	if ( option.alertStyle == 2 && real->isAnimated() ) {
+		if ( !isActivated ) {
+			connect(real, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(pixmapChanged(const QPixmap &)));
+			real->activated(playSound);
+			isActivated = true;
+		}
+	}
+	else if ( option.alertStyle == 1 || (option.alertStyle == 2 && !real->isAnimated()) ) {
+		connect(metaAlertIcon, SIGNAL(updateFrame(int)), SLOT(updateFrame(int)));
+	}
+	else {
+		impix = real->impix();
+		emit ai->pixmapChanged(impix.pixmap());
+	}
+}
+
+void AlertIcon::Private::stop()
+{
+	disconnect(metaAlertIcon, SIGNAL(updateFrame(int)), this, SLOT(updateFrame(int)));
+
+	if ( isActivated ) {
+		disconnect(real, SIGNAL(pixmapChanged(const QPixmap &)), this, SLOT(pixmapChanged(const QPixmap &)));
+		real->stop();
+		isActivated = false;
+	}
+}
+
+void AlertIcon::Private::updateFrame(int frame)
+{
+	if ( !metaAlertIcon ) // just in case
+		metaAlertIcon = new MetaAlertIcon();
+
+	if ( frame )
+		impix = real->impix();
+	else
+		impix = metaAlertIcon->blank16();
+
+	emit ai->pixmapChanged(impix.pixmap());
+}
+
+void AlertIcon::Private::pixmapChanged(const QPixmap &p)
+{
+	impix = real->frameImpix();
+
+	emit ai->pixmapChanged(p);
+}
+
+//----------------------------------------------------------------------------
+// AlertIcon
+//----------------------------------------------------------------------------
+
+AlertIcon::AlertIcon(const Icon *icon)
+{
+	d = new Private(this);
+	if ( icon )
+		d->real = new Icon(*icon);
+	else
+		d->real = new Icon();
+
+	d->init();
+}
+
+AlertIcon::~AlertIcon()
+{
+	delete d;
+}
+
+bool AlertIcon::isAnimated() const
+{
+	return d->real->isAnimated();
+}
+
+const QPixmap &AlertIcon::pixmap() const
+{
+	return d->impix.pixmap();
+}
+
+const QImage &AlertIcon::image() const
+{
+	return d->impix.image();
+}
+
+void AlertIcon::activated(bool playSound)
+{
+	d->activated(playSound);
+}
+
+void AlertIcon::stop()
+{
+	d->stop();
+}
+
+const QIconSet &AlertIcon::iconSet() const
+{
+	return d->real->iconSet();
+}
+
+const Impix &AlertIcon::impix() const
+{
+	return d->impix;
+}
+
+int AlertIcon::framenumber() const
+{
+	if ( option.alertStyle == 2 && d->real->isAnimated() ) {
+		return d->real->framenumber();
+	}
+	else if ( option.alertStyle == 1 || (option.alertStyle == 2 && !d->real->isAnimated()) ) {
+		return metaAlertIcon->framenumber();
+	}
+
+	return 0;
+}
+
+const QString &AlertIcon::name() const
+{
+	return d->real->name();
+}
+
+//----------------------------------------------------------------------------
+
+void alertIconUpdateAlertStyle()
+{
+	if ( !metaAlertIcon )
+		metaAlertIcon = new MetaAlertIcon();
+
+	metaAlertIcon->updateAlertStyle();
+}
+
+#include "alerticon.moc"
Index: psi/tags/0.10-os2-b1/src/alerticon.h
===================================================================
--- psi/tags/0.10-os2-b1/src/alerticon.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/alerticon.h	(revision 38)
@@ -0,0 +1,54 @@
+/*
+ * alerticon.h - class for handling animating alert icons
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ALERTICON_H
+#define ALERTICON_H
+
+#include "iconset.h"
+
+class AlertIcon : public Icon
+{
+	Q_OBJECT
+public:
+	AlertIcon(const Icon *icon);
+	~AlertIcon();
+
+	bool isAnimated() const;
+	const QPixmap &pixmap() const;
+	const QImage &image() const;
+	const QIconSet & iconSet() const;
+	const Impix &impix() const;
+	int framenumber() const;
+	const QString &name() const;
+
+public slots:
+	void activated(bool playSound = true);
+	void stop();
+
+public:
+	class Private;
+private:
+	Private *d;
+	friend class Private;
+};
+
+void alertIconUpdateAlertStyle();
+
+#endif
Index: psi/tags/0.10-os2-b1/src/avatars.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/avatars.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/avatars.cpp	(revision 38)
@@ -0,0 +1,405 @@
+#include <qpixmap.h>
+#include <qbuffer.h>
+#include <qcstring.h>
+#include <qdatetime.h>
+#include <qsize.h>
+#include <qfile.h>
+
+#include "xmpp.h"
+#include "avatars.h"
+#include "common.h"
+#include "iconset.h"
+#include "psiaccount.h"
+#include "profiles.h"
+#include "vcardfactory.h"
+
+#define MAX_AVATAR_SIZE 64
+
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//  Avatar: Base class for avatars.
+//------------------------------------------------------------------------------
+
+Avatar::Avatar()
+{
+}
+
+
+Avatar::~Avatar()
+{
+}
+
+int Avatar::maxSize()
+{
+	return (option.avatarsSize > MAX_AVATAR_SIZE ? MAX_AVATAR_SIZE : option.avatarsSize);
+}
+
+void Avatar::setImage(const QImage& i)
+{
+
+	if (i.width() > maxSize() || i.height() > option.avatarsSize)
+		pixmap_.convertFromImage(i.smoothScale(maxSize(),maxSize(),QImage::ScaleMin));
+	else
+		pixmap_.convertFromImage(i);
+}
+
+void Avatar::setImage(const QByteArray& ba)
+{
+	setImage(QImage(ba));
+}
+
+void Avatar::setImage(const QPixmap& p)
+{
+	if (p.width() > maxSize() || p.height() > maxSize())
+		setImage(p.convertToImage());
+	else
+		pixmap_ = p;
+}
+
+void Avatar::resetImage()
+{
+	pixmap_ = QPixmap();
+}
+
+
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// VCardAvatar: Avatars coming from VCards of contacts.
+//------------------------------------------------------------------------------
+
+class VCardAvatar : public QObject, public Avatar
+{
+	Q_OBJECT
+
+public:
+	VCardAvatar(AvatarFactory* factory, const Jid& jid);
+	QPixmap getPixmap();
+
+signals:
+	void avatarChanged(const Jid&);
+
+private:
+	Jid jid_;
+	AvatarFactory* factory_;
+	const VCard* lastVCard_; // Never dereference this, might point to dead object
+};
+
+
+VCardAvatar::VCardAvatar(AvatarFactory* factory, const Jid& jid) :
+	jid_(jid), factory_(factory)
+{
+	const VCard* vcard = VCardFactory::vcard(jid_);
+	if (vcard && vcard->photo()) {
+		setImage(vcard->photo());
+	}
+	lastVCard_ = vcard;
+}
+
+
+QPixmap VCardAvatar::getPixmap()
+{
+	// Try to find an avatar if the current one is empty
+	// FIXME: The pixmap shouldn't be updated by active polling, but by
+	// some signal from the VCard factory when the vcard is updated.
+	const VCard* vcard = VCardFactory::vcard(jid_);
+	if  (lastVCard_ != vcard) {
+		if (vcard && vcard->photo()) {
+			setImage(vcard->photo());
+		}
+		else
+			resetImage();
+		lastVCard_ = vcard;
+		emit avatarChanged(jid_);
+	}
+
+	return pixmap();
+}
+
+
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// FileAvatar: Avatars coming from local files.
+//------------------------------------------------------------------------------
+
+class FileAvatar : public Avatar
+{
+public:
+	FileAvatar(const Jid& jid);
+	void import(const QString& file);
+	void removeFromDisk();
+	bool exists();
+	QPixmap getPixmap();
+	const Jid& getJid() const
+		{ return jid_; }
+
+protected:
+	bool isDirty() const;
+	QString getFileName() const;
+	void refresh();
+	QDateTime lastModified() const
+		{ return lastModified_; }
+
+private:
+	QDateTime lastModified_;
+	Jid jid_;
+};
+
+
+FileAvatar::FileAvatar(const Jid& jid) : jid_(jid)
+{
+}
+
+void FileAvatar::import(const QString& file)
+{
+	if (QFileInfo(file).exists()) {
+		// Check if the manual dir exists
+		if (!QDir(AvatarFactory::getAvatarsDir()).exists())
+			QDir("").mkdir(AvatarFactory::getAvatarsDir());
+		if (!QDir(AvatarFactory::getManualDir()).exists())
+			QDir("").mkdir(AvatarFactory::getManualDir());
+
+		QFile source_file(file);
+		QFile target_file(getFileName());
+		if (source_file.open(IO_ReadOnly) && target_file.open(IO_WriteOnly)) {
+			// Copy all the data
+			char data[4096];
+			while (!source_file.atEnd()) {
+				Q_LONG read = source_file.readBlock(data,4096);
+				target_file.writeBlock(data,read);
+			}
+		}
+	}
+}
+
+void FileAvatar::removeFromDisk()
+{
+	QFile f(getFileName());
+	f.remove();
+}
+
+bool FileAvatar::exists()
+{
+	return QFileInfo(getFileName()).exists();
+}
+
+QPixmap FileAvatar::getPixmap()
+{
+	refresh();
+	return pixmap();
+}
+
+void FileAvatar::refresh()
+{
+	if (isDirty()) {
+		if (QFileInfo(getFileName()).exists()) {
+			QImage img(getFileName());
+			setImage(QImage(getFileName()));
+		}
+		else
+			resetImage();
+	}
+}
+
+
+QString FileAvatar::getFileName() const
+{
+	QString f = getJid().bare();
+	f.replace('@',"_at_");
+	return QDir(AvatarFactory::getManualDir()).filePath(f);
+}
+
+
+bool FileAvatar::isDirty() const
+{
+	return (pixmap().isNull()
+			|| !QFileInfo(getFileName()).exists()
+			|| QFileInfo(getFileName()).lastModified() > lastModified());
+}
+
+
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// ClientAvatar: Avatar representing the logo of a specific client.
+//------------------------------------------------------------------------------
+
+class ClientAvatar : public Avatar
+{
+public:
+	ClientAvatar(const QString& name);
+
+protected:
+	static ClientAvatar getClientAvatar(const QString& client);
+	static QMap<QString,ClientAvatar*> client_avatars;
+};
+
+
+ClientAvatar::ClientAvatar(const QString& name)
+{
+	//printf("%s\n",name.latin1());
+	QPixmap p;
+	if (name == "Psi")
+		p = IconsetFactory::icon("psi/psiMac").pixmap();
+	else if (name == "gaim")
+		p = IconsetFactory::icon("client/gaim").pixmap();
+	else if (name == "Gabber")
+		p = IconsetFactory::icon("client/gabber").pixmap();
+	else if (name == "Gossip")
+		p = IconsetFactory::icon("client/gossip").pixmap();
+	else if (name == "Exodus")
+		p = IconsetFactory::icon("client/exodus").pixmap();
+	else if (name == "Pandion")
+		p = IconsetFactory::icon("client/pandion").pixmap();
+	else if (name == "Nitro")
+		p = IconsetFactory::icon("client/nitro").pixmap();
+	else if (name == "Kopete")
+		p = IconsetFactory::icon("client/kopete").pixmap();
+	else if (name == "Tkabber")
+		p = IconsetFactory::icon("client/tkabber").pixmap();
+	else if (name == "MSN Transport")
+		p = IconsetFactory::icon("client/msn").pixmap();
+	else if (name == "AIM Transport")
+		p = IconsetFactory::icon("client/aim").pixmap();
+
+	// Clients with an ugly version string come last
+	else if (name.contains("Gadu-Gadu"))
+		p = IconsetFactory::icon("client/gadu").pixmap();
+	else if (name.contains("Miranda"))
+		p = IconsetFactory::icon("client/miranda").pixmap();
+	else if (name.contains("JAJC"))
+		p = IconsetFactory::icon("client/jajc").pixmap();
+	else if (name.contains("Trillian"))
+		p = IconsetFactory::icon("client/trillian").pixmap();
+	else if (name.contains("Yahoo"))
+		p = IconsetFactory::icon("client/yahoo").pixmap();
+	else if (name.contains("JIT"))
+		p = IconsetFactory::icon("client/icq").pixmap();
+	//else
+	//	p = IconsetFactory::icon("client/unknown").pixmap();
+
+	setImage(p);
+}
+
+
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// Avatar factory
+//------------------------------------------------------------------------------
+
+AvatarFactory::AvatarFactory(PsiAccount* pa) : pa_(pa)
+{
+	VCardFactory::registerVCardChanged(this,SLOT(updateAvatar(const Jid&)));
+}
+
+
+QPixmap AvatarFactory::getAvatar(const Jid& jid, const QString& resource)
+{
+	// Compute the avatar of the user
+	Avatar* av = retrieveAvatar(jid,resource);
+
+	// If the avatar changed since the previous request, notify everybody of this
+	if (av != active_avatars_[jid.full()]) {
+		active_avatars_[jid.full()] = av;
+		active_avatars_[jid.bare()] = av;
+		emit avatarChanged(jid);
+	}
+
+	return (av ? av->getPixmap() : QPixmap());
+}
+
+Avatar* AvatarFactory::retrieveAvatar(const Jid& jid, const QString& client)
+{
+	// printf("Retrieving avatar of %s\n", jid.full().latin1());
+
+	// Try finding a file avatar.
+	//printf("File avatar\n");
+	if (!file_avatars_.contains(jid.bare())) {
+		//printf("File avatar not yet loaded\n");
+		file_avatars_[jid.bare()] = new FileAvatar(jid);
+	}
+	//printf("Trying file avatar\n");
+	if (!file_avatars_[jid.bare()]->isEmpty())
+		return file_avatars_[jid.bare()];
+
+	// Try finding an avatar in the VCard
+	//printf("VCard avatar\n");
+	if (!vcard_avatars_.contains(jid.bare())) {
+		//printf("VCard avatar not yet loaded\n");
+		vcard_avatars_[jid.bare()] = new VCardAvatar(this, jid);
+		connect(vcard_avatars_[jid.bare()],SIGNAL(avatarChanged(const Jid&)),this,SLOT(updateAvatar(const Jid&)));
+	}
+	//printf("Trying VCard avatar\n");
+	if (!vcard_avatars_[jid.bare()]->isEmpty()) {
+		return vcard_avatars_[jid.bare()];
+	}
+
+	// Return the client avatar
+	if (client.isEmpty()) {
+		return 0;
+	}
+	else {
+		if (!client_avatars_.contains(client)) {
+			// printf("Client avatar not yet loaded\n");
+			client_avatars_[client] = new ClientAvatar(client);
+		}
+		return client_avatars_[client];
+	}
+}
+
+QPixmap AvatarFactory::getSelfAvatar()
+{
+	return getAvatar(account()->jid(),"");
+}
+
+void AvatarFactory::updateAvatar(const Jid& j)
+{
+	// FIXME: Maybe we should really look up the client version
+	getAvatar(j,"");
+
+	// FIXME: This signal might be emitted twice (first time from getAvatar()).
+	emit avatarChanged(j);
+}
+
+void AvatarFactory::importManualAvatar(const Jid& j, const QString& fileName)
+{
+	FileAvatar(j).import(fileName);
+	emit avatarChanged(j);
+}
+
+void AvatarFactory::removeManualAvatar(const Jid& j)
+{
+	FileAvatar(j).removeFromDisk();
+	// TODO: Remove from caches. Maybe create a clearManualAvatar() which
+	// removes the file but doesn't remove the avatar from caches (since it'll
+	// be created again whenever the FileAvatar is requested)
+	emit avatarChanged(j);
+}
+
+bool AvatarFactory::hasManualAvatar(const Jid& j)
+{
+	return FileAvatar(j).exists();
+}
+
+QString AvatarFactory::getAvatarsDir()
+{
+	QDir avatars(pathToProfile(activeProfile) + "/avatars");
+	if (!avatars.exists()) {
+		QDir profile(pathToProfile(activeProfile));
+		profile.mkdir("avatars");
+	}
+
+	return avatars.path();
+}
+
+QString AvatarFactory::getManualDir()
+{
+	return getAvatarsDir() + "/manual";
+}
+
+//------------------------------------------------------------------------------
+
+#include "avatars.moc"
Index: psi/tags/0.10-os2-b1/src/avatars.h
===================================================================
--- psi/tags/0.10-os2-b1/src/avatars.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/avatars.h	(revision 38)
@@ -0,0 +1,84 @@
+#ifndef AVATARS_H
+#define AVATARS_H
+
+//#define AVATARS
+
+#include <qpixmap.h>
+#include <qstring.h>
+
+#include "xmpp.h"
+
+using namespace XMPP;
+
+class PsiAccount;
+class Avatar;
+class VCardAvatar;
+class FileAvatar;
+class ClientAvatar;
+
+//------------------------------------------------------------------------------
+
+class AvatarFactory : public QObject
+{
+	Q_OBJECT
+
+public:
+	AvatarFactory(PsiAccount* pa);
+
+	QPixmap getAvatar(const Jid& jid, const QString& client);
+	PsiAccount* account() const
+		{ return pa_; }
+	QPixmap getSelfAvatar();
+
+	void importManualAvatar(const Jid& j, const QString& fileName);
+	void removeManualAvatar(const Jid& j);
+	bool hasManualAvatar(const Jid& j);
+
+	static QString getAvatarsDir();
+	static QString getManualDir();
+
+signals:
+	void avatarChanged(const Jid&);
+
+public slots:
+	void updateAvatar(const Jid&);
+
+protected:
+	Avatar* retrieveAvatar(const Jid& jid, const QString& resource);
+
+private:
+	QMap<QString,Avatar*> active_avatars_;
+	QMap<QString,FileAvatar*> file_avatars_;
+	QMap<QString,ClientAvatar*> client_avatars_;
+	QMap<QString,VCardAvatar*> vcard_avatars_;
+	PsiAccount* pa_;
+};
+
+//------------------------------------------------------------------------------
+
+class Avatar
+{
+public:
+	Avatar();
+	virtual ~Avatar();
+	virtual QPixmap getPixmap()
+		{ return pixmap(); }
+	virtual bool isEmpty()
+		{ return getPixmap().isNull(); }
+	static int maxSize();
+
+protected:
+	virtual const QPixmap& pixmap() const 
+		{ return pixmap_; }
+
+	virtual void setImage(const QImage&);
+	virtual void setImage(const QByteArray&);
+	virtual void setImage(const QPixmap&);
+	virtual void resetImage();
+
+private:
+	QPixmap pixmap_;
+};
+
+
+#endif
Index: psi/tags/0.10-os2-b1/src/browserdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/browserdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/browserdlg.cpp	(revision 38)
@@ -0,0 +1,909 @@
+/*
+ * browserdlg.cpp - main dialog for iq:browse protocol
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "browserdlg.h"
+
+#include <qlistview.h>
+#include <qpushbutton.h>
+#include <qcombobox.h>
+#include <qcheckbox.h>
+#include <qstringlist.h>
+#include <qguardedptr.h>
+#include <qptrlist.h>
+#include <qlistview.h>
+#include <qgroupbox.h>
+#include <qpopupmenu.h>
+#include <qlayout.h>
+#include <qobjectlist.h>
+#include <qaction.h>
+#include <qtoolbar.h>
+#include <qstatusbar.h>
+
+#include "ui_search.h"
+#include "im.h"
+#include "xmpp_tasks.h"
+
+#include "psicon.h"
+#include "psiaccount.h"
+#include "common.h"
+#include "busywidget.h"
+#include "tasklist.h"
+#include "iconaction.h"
+
+//----------------------------------------------------------------------------
+// BrowserData -- a shared data struct
+//----------------------------------------------------------------------------
+
+struct BrowserData {
+	PsiAccount *pa;
+	TaskList *tasks;
+};
+
+//----------------------------------------------------------------------------
+// BrowserItem
+//----------------------------------------------------------------------------
+
+class BrowserItem : public QObject, public QListViewItem
+{
+	Q_OBJECT
+public:
+	BrowserItem (const AgentItem &item, BrowserData *p, QListView *parent);
+	BrowserItem (const AgentItem &item, BrowserData *p, QListViewItem *parent);
+
+	QString text (int c) const;
+	const AgentItem & item() const;
+	void setOpen (bool o);
+
+public slots:
+	void update(bool preBrowse = true, bool parentPreBrowsing = false);
+
+private slots:
+	void updateFinished();
+
+private:
+	class Private;
+	Private *d;
+	friend class Private;
+
+	void init (const AgentItem &item, BrowserData *p);
+
+public:
+	BrowserDlg *dlg();
+	bool preBrowsingEnabled();
+};
+
+class BrowserItem::Private : public QObject
+{
+private:
+	BrowserItem *bItem;
+
+public:
+	Private (BrowserItem *parent)
+	: QObject (parent)
+	{
+		bItem = parent;
+		parentPreBrowsing = false;
+		alreadyBrowsed = false;
+	}
+
+	void copyItem (const AgentItem &a)
+	{
+		if ( !a.name().isEmpty() )
+			item.setName( a.name() );
+		if ( !a.jid().full().isEmpty() )
+			item.setJid( a.jid() );
+
+		if ( !a.category().isEmpty() )
+			item.setCategory( a.category() );
+		if ( !a.type().isEmpty() ) {
+			// FIXME: workaround for buggy AIM transport (it is also used for an ICQ transport)
+			if ( !((item.type() == "aim" || item.type() == "icq") && a.type() == "jabber") ) {
+				item.setType( a.type() );
+			}
+		}
+
+		if ( !a.category().isEmpty() )
+			bItem->setPixmap (0, category2icon(item.category(), item.type()));
+		else
+			bItem->setPixmap (0, is->status(item.jid(), STATUS_ONLINE));
+
+		if ( !a.features().list().isEmpty() )
+			item.setFeatures(a.features());
+	}
+
+	void preBrowseChilds()
+	{
+		if ( !bItem->preBrowsingEnabled() )
+			return;
+
+		// pre-browse all child items
+		BrowserItem *child = (BrowserItem *)bItem->firstChild();
+		while ( child ) {
+			child->update(false, true); // checkout update(bool, bool) function, if you change this
+
+			child = (BrowserItem *)child->nextSibling();
+		}
+	}
+
+	AgentItem item;
+	BrowserData *p;
+	bool isRoot;
+	BrowserItem *root;
+	bool preBrowsing, parentPreBrowsing;
+	bool alreadyBrowsed;
+};
+
+BrowserItem::BrowserItem (const AgentItem &item, BrowserData *p, QListView *parent)
+: QListViewItem (parent)
+{
+	init (item, p);
+	d->isRoot = true;
+}
+
+BrowserItem::BrowserItem (const AgentItem &item, BrowserData *p, QListViewItem *parent)
+: QListViewItem (parent)
+{
+	init (item, p);
+	d->isRoot = false;
+}
+
+void BrowserItem::init (const AgentItem &item, BrowserData *p)
+{
+	d = new Private (this);
+	d->item = (AgentItem)item;
+	d->p = p;
+
+	// find 'root' BrowserItem
+	d->root = this;
+	while ( d->root->QListViewItem::parent() )
+		d->root = (BrowserItem *)d->root->QListViewItem::parent();
+
+	d->copyItem(item); // hmm... guess this is too much, concerning that we do 'd->item = item' already
+
+	if ( !preBrowsingEnabled() )
+		setExpandable (true);
+}
+
+QString BrowserItem::text (int c) const
+{
+	if ( c == 0 )
+		return d->item.name();
+	else if ( c == 1 )
+		return d->item.jid().full();
+	return "Oops :)";
+}
+
+const AgentItem & BrowserItem::item() const
+{
+	return d->item;
+}
+
+BrowserDlg *BrowserItem::dlg()
+{
+	return (BrowserDlg *)listView()->parent()->parent();
+}
+
+bool BrowserItem::preBrowsingEnabled()
+{
+	return dlg()->cb_preBrowse->isChecked();
+}
+
+void BrowserItem::setOpen (bool o)
+{
+	if ( o ) {
+		if ( !childCount() )
+			update();
+		else
+			d->preBrowseChilds();
+	}
+
+	QListViewItem::setOpen (o);
+}
+
+void BrowserItem::update(bool preBrowse, bool parentPreBrowsing)
+{
+	if ( preBrowse == false && parentPreBrowsing == true ) {
+		// to save some traffic
+		if ( d->alreadyBrowsed )
+			return;
+
+		// FIXME: currently, JUD doesn't seem to answer to browsing requests
+		if ( d->item.category() == "service" && d->item.type() == "jud" )
+			return;
+	}
+
+	if ( d->root == this )
+		preBrowse = true;
+
+	if ( !preBrowsingEnabled() )
+		preBrowse = false;
+
+	d->preBrowsing = preBrowse;
+	d->parentPreBrowsing = parentPreBrowsing;
+
+	JT_Browse *jt = new JT_Browse(d->p->pa->client()->rootTask());
+	connect(jt, SIGNAL(finished()), SLOT(updateFinished()));
+	jt->get(d->item.jid());
+	jt->go(true);
+	d->p->tasks->append(jt);
+}
+
+void BrowserItem::updateFinished()
+{
+	QObject *senderObj = (QObject *)sender();
+	JT_GetServices *jtAgents = 0;
+	JT_Browse *jtBrowse = (senderObj->inherits("XMPP::JT_Browse") ? (JT_Browse *)senderObj : 0);
+	if ( !jtBrowse )
+		jtAgents = (senderObj->inherits("XMPP::JT_GetServices") ? (JT_GetServices *)senderObj : 0);
+
+	if( (jtBrowse && jtBrowse->success()) || (jtAgents && jtAgents->success()) ) {
+		if ( jtBrowse )
+			d->copyItem ( jtBrowse->root() );
+		else {
+			// iq:agents doesn't have as much useful info as iq:browse has,
+			// so we're gonna to provide it by ourselves :-)
+			AgentItem ai;
+
+			ai.setName     ( "Jabber Service" );
+			ai.setCategory ( "services" );
+			ai.setType     ( "jabber" );
+
+			d->copyItem ( ai );
+		}
+
+		if ( !d->parentPreBrowsing )
+			QListViewItem::setOpen (true);
+
+		AgentList agents;
+		if ( jtBrowse )
+			agents = jtBrowse->agents();
+		else
+			agents = jtAgents->agents();
+
+		// add/update items
+		for(AgentList::ConstIterator it = agents.begin(); it != agents.end(); ++it) {
+			const AgentItem a = *it;
+			bool found = false;
+
+			BrowserItem *child = (BrowserItem *)firstChild();
+			while ( child ) {
+				if ( child->item().jid().full() == a.jid().full() ) {
+					child->d->copyItem ( a );
+					child->repaint();
+					found = true;
+					break;
+				}
+
+				child = (BrowserItem *)child->nextSibling();
+			}
+
+			if ( !found )
+				new BrowserItem (a, d->p, this);
+		}
+
+		// check for removed items
+		QPtrList<BrowserItem> removeList;
+		removeList.setAutoDelete (true);
+		BrowserItem *child = (BrowserItem *)firstChild();
+		while ( child ) {
+			bool found = false;
+
+			for(AgentList::ConstIterator it = agents.begin(); it != agents.end(); ++it) {
+				const AgentItem a = *it;
+
+				if ( child->item().jid().full() == a.jid().full() ) {
+					found = true;
+					break;
+				}
+			}
+
+			if ( !found )
+				removeList.append ( child ); // items will be deleted later
+
+			child = (BrowserItem *)child->nextSibling();
+		}
+
+		if ( jtBrowse && !d->parentPreBrowsing ) // comment this to auto-browse entire server tree
+			d->preBrowseChilds();
+
+		// don't forget to remove '+' (or '-') sign in case, that the child list is empty
+		setExpandable ( !agents.isEmpty() );
+
+		// root item is initially hidden
+		if ( d->isRoot )
+			setVisible (true);
+	}
+	else {
+		setExpandable ( false );
+
+		if ( jtBrowse && d->isRoot ) {
+			// there are chances, that server just doesn't support browsing (this is
+			// true for the Jabber Inc. commercial server). Go to jabber.com for
+			// example
+
+			JT_GetServices *jt = new JT_GetServices(d->p->pa->client()->rootTask());
+			connect(jt, SIGNAL(finished()), SLOT(updateFinished()));
+			jt->get(d->item.jid());
+			jt->go(true);
+			d->p->tasks->append(jt);
+		}
+		else {
+			if ( !d->parentPreBrowsing ) {
+				QString error;
+				if ( jtBrowse )
+					error = jtBrowse->statusString();
+				else
+					error = jtAgents->statusString();
+				QMessageBox::critical(dlg(), tr("Error"), tr("There was an error browsing the <b>%1</b>.\nReason: %2").arg(item().jid().full()).arg(error));
+			}
+		}
+	}
+
+	dlg()->itemSelected (dlg()->lv_browse->selectedItem());
+
+	d->alreadyBrowsed = true;
+	d->preBrowsing = false;
+	d->parentPreBrowsing = false;
+	repaint();
+}
+
+//----------------------------------------------------------------------------
+// BrowserDlg
+//----------------------------------------------------------------------------
+
+class BrowserDlg::Private : public QObject
+{
+	Q_OBJECT
+
+public:
+	Private (BrowserDlg *parent, PsiAccount *pa)
+	: QObject(parent)
+	{
+		dlg = parent;
+		data.pa = pa;
+
+		busy = parent->busy;
+		connect(busy, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *)));
+
+		data.tasks = new TaskList;
+		connect(data.tasks, SIGNAL(started()), SLOT(itemUpdateStarted()));
+		connect(data.tasks, SIGNAL(finished()), SLOT(itemUpdateFinished()));
+
+		// create actions
+		actBrowse = new IconAction (tr("Browse"), "psi/jabber", tr("&Browse"), 0, dlg);
+		connect (actBrowse, SIGNAL(activated()), SLOT(actionBrowse()));
+		actRefresh = new IconAction (tr("Refresh"), "psi/reload", tr("&Refresh"), 0, dlg);
+		connect (actRefresh, SIGNAL(activated()), SLOT(actionRefresh()));
+		actStop = new IconAction (tr("Stop"), "psi/stop", tr("Sto&p"), 0, dlg);
+		connect (actStop, SIGNAL(activated()), SLOT(actionStop()));
+
+		actRegister = new IconAction (tr("Register"), "psi/register", tr("&Register"), 0, dlg);
+		connect (actRegister, SIGNAL(activated()), SLOT(actionRegister()));
+		actSearch = new IconAction (tr("Search"), "psi/search", tr("&Search"), 0, dlg);
+		connect (actSearch, SIGNAL(activated()), SLOT(actionSearch()));
+		actJoin = new IconAction (tr("Join"), "psi/groupChat", tr("&Join"), 0, dlg);
+		connect (actJoin, SIGNAL(activated()), SLOT(actionJoin()));
+
+		actInfo = new IconAction (tr("Info"), "psi/vCard", tr("&Info"), 0, dlg);
+		connect (actInfo, SIGNAL(activated()), SLOT(actionInfo()));
+
+		/*actTime = new QAction (tr("Get Time"), QIconSet(is.getTime), tr("Get &Time"), 0, dlg);
+		connect (actTime, SIGNAL(activated()), SLOT(actionGetTime()));
+		actLast = new QAction (tr("Last Time"), QIconSet(is.getLastTime), tr("&Last Time"), 0, dlg);
+		connect (actLast, SIGNAL(activated()), SLOT(actionGetLast()));
+		actVersion = new QAction (tr("Get Version"), QIconSet(is.getVersion), tr("Get &Version"), 0, dlg);
+		connect (actVersion, SIGNAL(activated()), SLOT(actionVersion()));
+		actMessage = new QAction (tr("Send Message"), QIconSet(is.message.base()), tr("Send &Message"), 0, dlg);
+		connect (actMessage, SIGNAL(activated()), SLOT(actionMessage()));
+		actChat = new QAction (tr("Open Chat"), QIconSet(is.chat.base()), tr("Open &Chat"), 0, dlg);
+		connect (actChat, SIGNAL(activated()), SLOT(actionChat()));
+		actAdd = new QAction (tr("Add to roster"), QIconSet(is.add), tr("Add to &roster"), 0, dlg);
+		connect (actAdd, SIGNAL(activated()), SLOT(actionAdd()));*/
+
+		actBack = new IconAction (tr("Back"), "psi/arrowLeft", tr("&Back"), 0, dlg);
+		connect (actBack, SIGNAL(activated()), SLOT(actionBack()));
+		actForward = new IconAction (tr("Forward"), "psi/arrowRight", tr("&Forward"), 0, dlg);
+		connect (actForward, SIGNAL(activated()), SLOT(actionForward()));
+
+		toolBar = new QToolBar(dlg);
+
+		actBack->addTo(toolBar);
+		actBrowse->addTo(toolBar);
+		actForward->addTo(toolBar);
+
+		toolBar->addSeparator();
+		actRefresh->addTo(toolBar);
+		actStop->addTo(toolBar);
+
+		toolBar->addSeparator();
+		actRegister->addTo(toolBar);
+		actSearch->addTo(toolBar);
+		actJoin->addTo(toolBar);
+
+		toolBar->addSeparator();
+		actInfo->addTo(toolBar);
+		/*actVersion->addTo(toolBar);
+		actTime->addTo(toolBar);
+		actLast->addTo(toolBar);
+
+		toolBar->addSeparator();
+		actMessage->addTo(toolBar);
+		actChat->addTo(toolBar);
+
+		toolBar->addSeparator();
+		actAdd->addTo(toolBar);*/
+
+		toolBar->setStretchableWidget(new StretchWidget(toolBar));
+		pa->accountLabel(toolBar, true);
+
+		backHistory.setAutoDelete(true);
+		forwardHistory.setAutoDelete(true);
+
+		disableButtons();
+		actStop->setEnabled(false); // stop action is not handled by disableButtons()
+		updateBackForward(); // same applies to back & forward
+	}
+
+	~Private()
+	{
+		delete data.tasks;
+	}
+
+public slots:
+	void objectDestroyed(QObject *obj)
+	{
+		if ( obj == busy )
+			busy = 0;
+	}
+
+	void actionBrowse()
+	{
+		BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
+		if ( !it )
+			return;
+
+		doBrowse( it->item().jid().full() );
+	}
+
+	void actionRefresh()
+	{
+		BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
+		if ( !it )
+			return;
+
+		it->update();
+	}
+
+	void actionStop()
+	{
+		actStop->setEnabled(false);
+		if ( busy )
+			     busy->stop();
+		data.tasks->clear();
+	}
+
+	void actionRegister()
+	{
+		BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
+		if ( !it )
+			return;
+
+		emit dlg->signalRegister( it->item().jid() );
+	}
+
+	void actionSearch()
+	{
+		BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
+		if ( !it )
+			return;
+
+		emit dlg->signalSearch( it->item().jid() );
+	}
+
+	void actionJoin()
+	{
+		BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
+		if ( !it )
+			return;
+
+		emit dlg->signalJoin( it->item().jid() );
+	}
+
+	void actionInfo()
+	{
+		BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
+		if ( !it )
+			return;
+
+		data.pa->actionInfo( it->item().jid() );
+	}
+
+	/*void actionGetTime()
+	{
+		BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
+		if ( !it )
+			return;
+
+		// emit ...
+	}
+
+	void actionGetLast()
+	{
+		BrowserItem *it = (BrowserItem *)dlg->lv_browse->selectedItem();
+		if ( !it )
+			return;
+
+		// emit ...
+	}*/
+
+
+	void actionBack()
+	{
+		// add current selection to forward history
+		QListViewItem *item = dlg->lv_browse->firstChild();
+		if ( item )
+			dlg->lv_browse->takeItem( item );
+
+		forwardHistory.append( new History(jid, item) );
+
+		// now, take info from back history...
+		History *h = backHistory.last();
+		Jid j            = h->jid();
+		QListViewItem *i = h->takeItem();
+		backHistory.removeLast();
+
+		// and restore view
+		doBrowse(j.full(), true, i);
+	}
+
+	void actionForward()
+	{
+		// add current selection to back history
+		QListViewItem *item = dlg->lv_browse->firstChild();
+		if ( item )
+			dlg->lv_browse->takeItem( item );
+
+		backHistory.append( new History(jid, item) );
+
+		// now, take info from forward history...
+		History *h = forwardHistory.last();
+		Jid j            = h->jid();
+		QListViewItem *i = h->takeItem();
+		forwardHistory.removeLast();
+
+		// and restore view
+		doBrowse(j.full(), true, i);
+	}
+
+
+	void itemUpdateStarted()
+	{
+		actStop->setEnabled(true);
+		if ( busy )
+			busy->start();
+	}
+
+	void itemUpdateFinished()
+	{
+		actStop->setEnabled(false);
+		if ( busy )
+			busy->stop();
+	}
+
+	void doBrowse(const QString &host = QString::null, bool backForwardClicked = false, QListViewItem *rootItem = 0)
+	{
+		PsiAccount *pa = data.pa;
+		if(!pa->checkConnected(dlg))
+			return;
+
+		Jid j;
+		QString h = host;
+
+		if ( h.isEmpty() )
+			h = dlg->cb_browse->currentText();
+
+		j = h.stripWhiteSpace();
+
+		if ( !j.isValid() )
+			return;
+
+		if ( !backForwardClicked && jid.full() != j.full() ) {
+			QListViewItem *item = dlg->lv_browse->firstChild();
+			if ( item )
+				dlg->lv_browse->takeItem( item );
+
+			backHistory.append( new History(jid, item) );
+			forwardHistory.clear();
+		}
+
+		updateBackForward();
+
+		jid = j;
+		pa->psi()->recentBrowseAdd( jid.full() );
+		dlg->cb_browse->clear();
+		dlg->cb_browse->insertStringList(pa->psi()->recentBrowseList());
+
+		data.tasks->clear(); // also will call all all necessary functions
+		disableButtons();
+
+		if ( !rootItem ) {
+			dlg->lv_browse->clear();
+
+			AgentItem ai;
+			ai.setJid( j );
+			BrowserItem *root = new BrowserItem (ai, &data, dlg->lv_browse);
+			root->setVisible (false); // don't confuse users
+
+			root->setOpen(true); // begin browsing
+		}
+		else {
+			dlg->lv_browse->insertItem( rootItem );
+
+			// fixes multiple selection bug
+			QListViewItemIterator it( dlg->lv_browse );
+			while ( it.current() ) {
+				QListViewItem *item = it.current();
+				++it;
+
+				if ( item->isSelected() )
+					for (int i = 0; i <= 1; i++) // its boring to write same line twice :)
+						dlg->lv_browse->setSelected(item, (bool)i);
+			}
+		}
+	}
+
+public:
+	BrowserDlg *dlg;
+	Jid jid;
+	BrowserData data;
+	BusyWidget *busy;
+
+	QToolBar *toolBar;
+	QAction *actBrowse, *actRegister, *actSearch,
+		*actJoin, *actInfo, /* *actTime, *actVersion,
+		*actLast,*/ *actRefresh, *actStop,
+		*actMessage, *actChat, *actAdd;
+
+	QAction *actBack, *actForward;
+
+	// helper class for use in toolbar
+	class StretchWidget : public QWidget
+	{
+	public:
+		StretchWidget(QWidget *parent)
+		: QWidget(parent)
+		{
+			setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+		}
+	};
+
+	// helper class to store browser history
+	class History {
+	private:
+		QListViewItem *item;
+		Jid j;
+	public:
+		History(Jid jj, QListViewItem *it) {
+			item = it;
+			j = jj;
+		}
+
+		~History() {
+			if ( item )
+				delete item;
+		}
+
+		QListViewItem *takeItem() {
+			QListViewItem *i = item;
+			item = 0;
+			return i;
+		}
+
+		Jid jid() {
+			return j;
+		}
+	};
+
+	typedef QPtrList<History> HistoryList;
+	HistoryList backHistory, forwardHistory;
+
+	void disableButtons()
+	{
+		actRefresh->setEnabled(false);
+		//actStop->setEnabled(false);
+
+		actRegister->setEnabled(false);
+		actSearch->setEnabled(false);
+		actJoin->setEnabled(false);
+
+		actBrowse->setEnabled(false);
+		actInfo->setEnabled(false);
+
+		/*actTime->setEnabled(false);
+		actLast->setEnabled(false);
+		actVersion->setEnabled(false);
+		actMessage->setEnabled(false);
+		actChat->setEnabled(false);
+		actAdd->setEnabled(false);*/
+	}
+
+	void enableButtons(const AgentItem &agent)
+	{
+		const Features &f = agent.features();
+
+		actBrowse->setEnabled(true);
+		actRefresh->setEnabled(true);
+		//actStop->setEnabled(false);
+		actRegister->setEnabled( f.canRegister() );
+		actSearch->setEnabled( f.canSearch() );
+		actJoin->setEnabled( f.canGroupchat() );
+		actInfo->setEnabled(true);
+		/*actTime->setEnabled(false);
+		actLast->setEnabled(false);*/
+	}
+
+	bool eventFilter (QObject *object, QEvent *event)
+	{
+		if ( object == dlg->lv_browse ) {
+			if ( event->type() == QEvent::ContextMenu ) {
+				QContextMenuEvent *e = (QContextMenuEvent *)event;
+
+				BrowserItem *item = (BrowserItem *)dlg->lv_browse->selectedItem();
+				if ( !item )
+					return true;
+				enableButtons ( item->item() );
+
+				QPopupMenu p;
+
+				actBrowse->addTo (&p);
+				actRefresh->addTo (&p);
+				actStop->addTo (&p);
+
+				p.insertSeparator();
+				actRegister->addTo (&p);
+				actSearch->addTo (&p);
+				actJoin->addTo (&p);
+
+				p.insertSeparator();
+				actInfo->addTo (&p);
+				/*actVersion->addTo (&p);
+				actTime->addTo (&p);
+				actLast->addTo (&p);
+
+				p.insertSeparator();
+				actMessage->addTo (&p);
+				actChat->addTo (&p);
+
+				p.insertSeparator();
+				actAdd->addTo (&p);*/
+
+				e->accept();
+				p.exec ( e->globalPos() );
+
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	void updateBackForward()
+	{
+		actBack->setEnabled( !backHistory.isEmpty() );
+		actForward->setEnabled( !forwardHistory.isEmpty() );
+	}
+};
+
+BrowserDlg::BrowserDlg (const Jid &j, PsiAccount *pa)
+: BrowserUI (0, 0, WDestructiveClose)
+{
+	d = new Private (this, pa);
+	pa->dialogRegister(this);
+	d->jid = j;
+
+	setCaption(CAP(caption()));
+	setIcon(is->transportStatus("transport", STATUS_ONLINE));
+
+	cb_browse->setInsertionPolicy(QComboBox::NoInsertion);
+	cb_browse->insertStringList(pa->psi()->recentBrowseList());
+	cb_browse->setFocus();
+	connect(cb_browse, SIGNAL(activated(const QString &)), d, SLOT(doBrowse()));
+
+	pb_browse->setDefault(true);
+	cb_browse->setCurrentText(d->jid.full());
+
+	connect (pb_browse, SIGNAL(clicked()), d, SLOT(doBrowse()));
+
+	lv_browse->installEventFilter (d);
+
+	statusBar()->hide();
+
+	X11WM_CLASS("browser");
+
+	if(pa->loggedIn())
+		doBrowse();
+}
+
+BrowserDlg::~BrowserDlg()
+{
+	d->data.pa->dialogUnregister(this);
+}
+
+void BrowserDlg::doBrowse(const QString &host)
+{
+	d->doBrowse(host, true);
+}
+
+void BrowserDlg::itemSelected (QListViewItem *item)
+{
+	BrowserItem *it = (BrowserItem *)item;
+	if ( !it ) {
+		d->disableButtons();
+		return;
+	}
+
+	const AgentItem agent = it->item();
+	d->enableButtons ( agent );
+}
+
+void BrowserDlg::itemDoubleclicked (QListViewItem *item)
+{
+	BrowserItem *it = (BrowserItem *)item;
+	if ( !it )
+		return;
+
+	const AgentItem a = it->item();
+	const Features &f = a.features();
+
+	// set the prior state of item
+	// FIXME: causes minor flickering
+	if ( f.canGroupchat() || f.canRegister() || f.canSearch() ) {
+		if ( !it->isOpen() ) {
+			if ( it->isExpandable() || it->childCount() )
+				it->setOpen( true );
+		}
+		else {
+			it->setOpen( false );
+		}
+	}
+
+	// trigger default action
+	if ( f.canGroupchat() ) {
+		d->actionJoin();
+	}
+	else {
+		bool searchFirst = a.category() == "service" && a.type() == "jud";
+
+		if ( !searchFirst ) {
+			if ( f.canRegister() )
+				d->actionRegister();
+			else if ( f.canSearch() )
+				d->actionSearch();
+		}
+		else {
+			if ( f.canSearch() )
+				d->actionSearch();
+			else if ( f.canRegister() )
+				d->actionRegister();
+		}
+	}
+}
+
+#include "browserdlg.moc"
Index: psi/tags/0.10-os2-b1/src/browserdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/browserdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/browserdlg.h	(revision 38)
@@ -0,0 +1,65 @@
+/*
+ * browserdlg.h - main dialog for iq:browse protocol
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef BROWSERDLG_H
+#define BROWSERDLG_H
+
+#include "ui_browser.h"
+
+#include"xmpp.h"
+using namespace XMPP;
+
+class QAction;
+class QToolBar;
+
+class PsiAccount;
+class BusyWidget;
+
+class BrowserItem;
+
+class BrowserDlg : public BrowserUI
+{
+	Q_OBJECT
+
+public:
+	BrowserDlg(const Jid &, PsiAccount *);
+	~BrowserDlg();
+
+public slots:
+	void doBrowse(const QString &host = "");
+
+signals:
+	void signalRegister(const Jid &);
+	void signalSearch(const Jid &);
+	void signalJoin(const Jid &);
+
+private slots:
+	void itemSelected (QListViewItem *);
+	void itemDoubleclicked (QListViewItem *);
+
+public:
+	class Private;
+private:
+	friend class Private;
+	friend class BrowserItem;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/changepwdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/changepwdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/changepwdlg.cpp	(revision 38)
@@ -0,0 +1,160 @@
+/*
+ * changepwdlg.cpp - dialog for changing password
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"changepwdlg.h"
+
+#include<qlayout.h>
+#include<qlabel.h>
+#include<qpushbutton.h>
+#include<qlineedit.h>
+#include<qmessagebox.h>
+#include"profiles.h"
+#include"psiaccount.h"
+#include"busywidget.h"
+#include"im.h"
+#include"xmpp_tasks.h"
+#include"accountdlg.h"
+#include"iconwidget.h"
+
+using namespace XMPP;
+
+ChangePasswordDlg::ChangePasswordDlg(PsiAccount *_pa, QWidget *parent, const char *name)
+:ChangePasswordUI(parent, name, FALSE, WDestructiveClose)
+{
+	pa = _pa;
+	pa->dialogRegister(this);
+
+	connect(pa, SIGNAL(disconnected()), SLOT(disc()));
+
+	setCaption(CAP(caption()));
+
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	connect(pb_apply, SIGNAL(clicked()), SLOT(apply()));
+}
+
+ChangePasswordDlg::~ChangePasswordDlg()
+{
+	pa->dialogUnregister(this);
+}
+
+/*void ChangePasswordDlg::closeEvent(QCloseEvent *e)
+{
+	e->ignore();
+	reject();
+}*/
+
+void ChangePasswordDlg::done(int r)
+{
+	if(busy->isActive())
+		return;
+	QDialog::done(r);
+}
+
+void ChangePasswordDlg::apply()
+{
+	// sanity check
+	if(le_pwcur->text().isEmpty() || le_pwnew->text().isEmpty() || le_pwver->text().isEmpty()) {
+		QMessageBox::information(this, tr("Error"), tr("You must fill out the fields properly before you can proceed."));
+		return;
+	}
+
+	if(le_pwcur->text() != pa->userAccount().pass) {
+		QMessageBox::information(this, tr("Error"), tr("You entered your old password incorrectly.  Try again."));
+		le_pwcur->setText("");
+		le_pwcur->setFocus();
+		return;
+	}
+
+	if(le_pwnew->text() != le_pwver->text()) {
+		QMessageBox::information(this, tr("Error"), tr("New password and confirmation do not match.  Please enter them again."));
+		le_pwnew->setText("");
+		le_pwver->setText("");
+		le_pwnew->setFocus();
+		return;
+	}
+
+	busy->start();
+	blockWidgets();
+
+	JT_Register *reg = new JT_Register(pa->client()->rootTask());
+	connect(reg, SIGNAL(finished()), SLOT(finished()));
+	Jid j = pa->userAccount().jid;
+	reg->reg(j.user(), le_pwnew->text());
+	reg->go(true);
+}
+
+void ChangePasswordDlg::finished()
+{
+	busy->stop();
+
+	JT_Register *reg = (JT_Register *)sender();
+	QString err = reg->statusString();
+	int code = reg->statusCode();
+	bool ok = reg->success();
+	if(ok) {
+		UserAccount acc = pa->userAccount();
+		acc.pass = le_pwnew->text();
+		pa->setUserAccount(acc);
+		AccountModifyDlg *amd = (AccountModifyDlg *)pa->dialogFind("AccountModifyDlg");
+		if(amd)
+			amd->setPassword(acc.pass);
+
+		QMessageBox::information(this, tr("Success"), tr("Successfully changed password."));
+		close();
+	}
+	else {
+		// ignore task disconnect error
+		if(code == Task::ErrDisc)
+			return;
+
+		QMessageBox::critical(this, tr("Error"), QString(tr("There was an error when trying to set the password.\nReason: %1")).arg(err) );
+		restoreWidgets();
+	}
+}
+
+void ChangePasswordDlg::disc()
+{
+	busy->stop();
+	close();
+}
+
+void ChangePasswordDlg::blockWidgets()
+{
+	lb_pwcur->setEnabled(false);
+	lb_pwnew->setEnabled(false);
+	lb_pwver->setEnabled(false);
+	le_pwcur->setEnabled(false);
+	le_pwnew->setEnabled(false);
+	le_pwver->setEnabled(false);
+	pb_close->setEnabled(false);
+	pb_apply->setEnabled(false);
+}
+
+void ChangePasswordDlg::restoreWidgets()
+{
+	lb_pwcur->setEnabled(true);
+	lb_pwnew->setEnabled(true);
+	lb_pwver->setEnabled(true);
+	le_pwcur->setEnabled(true);
+	le_pwnew->setEnabled(true);
+	le_pwver->setEnabled(true);
+	pb_close->setEnabled(true);
+	pb_apply->setEnabled(true);
+}
Index: psi/tags/0.10-os2-b1/src/changepwdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/changepwdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/changepwdlg.h	(revision 38)
@@ -0,0 +1,53 @@
+/*
+ * changepwdlg.h - dialog for changing password
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CHANGEPWDLG_H
+#define CHANGEPWDLG_H
+
+#include"ui_changepw.h"
+
+class PsiAccount;
+
+class ChangePasswordDlg : public ChangePasswordUI
+{
+	Q_OBJECT
+public:
+	ChangePasswordDlg(PsiAccount *, QWidget *parent=0, const char *name=0);
+	~ChangePasswordDlg();
+
+protected:
+	//void closeEvent(QCloseEvent *e);
+
+public slots:
+	void done(int);
+
+private slots:
+	void apply();
+	void finished();
+	void disc();
+
+private:
+	void blockWidgets();
+	void restoreWidgets();
+
+	PsiAccount *pa;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/chatdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/chatdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/chatdlg.cpp	(revision 38)
@@ -0,0 +1,1142 @@
+/*
+ * chatdlg.cpp - dialog for handling chats
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"chatdlg.h"
+
+#include<qlabel.h>
+#include<qcursor.h>
+#include<qdragobject.h>
+#include<qlineedit.h>
+#include<qtoolbutton.h>
+#include<qlayout.h>
+#include<qsplitter.h>
+#include<qtooltip.h>
+#include<qtimer.h>
+#include<qdatetime.h>
+#include<qlcdnumber.h>
+#include"profiles.h"
+#include"psiaccount.h"
+#include"common.h"
+#include"userlist.h"
+#include"iconwidget.h"
+#include"fancylabel.h"
+#include"msgmle.h"
+#include"iconselect.h"
+#include"psicon.h"
+#include"psitoolbar.h"
+#include"iconaction.h"
+#include"avatars.h"
+#include"tabdlg.h"
+
+#ifdef Q_WS_WIN
+#include<windows.h>
+#endif
+
+//----------------------------------------------------------------------------
+// ChatDlg
+//----------------------------------------------------------------------------
+class ChatDlg::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(ChatDlg *d) {
+		dlg = d;
+	}
+
+	ChatDlg *dlg;
+	Jid jid;
+	PsiAccount *pa;
+	QString dispNick;
+	int status;
+	QString statusString;
+
+	ChatView *log;
+	ChatEdit *mle;
+
+#ifdef AVATARS
+	QLabel *avatar;
+#endif 
+
+	QLabel *lb_ident;
+	IconLabel *lb_status;
+	QLineEdit *le_jid;
+	QLCDNumber *lcd_count;
+	QPopupMenu *pm_settings;
+
+	QLabel *lb_composing;
+
+	PsiToolBar *toolbar;
+	IconAction *act_send, *act_clear, *act_history, *act_info, *act_pgp, *act_icon, *act_file, *act_compact;
+
+	int pending;
+	bool keepOpen, warnSend;
+
+	QTimer *selfDestruct;
+
+	QString key;
+	int transid;
+	Message m;
+	bool lastWasEncrypted;
+	bool smallChat;
+
+	// Message Events
+	QTimer *composingTimer;
+	bool isComposing;
+	bool contactIsComposing;
+	bool sendComposingEvents;
+	QString eventId;
+
+signals:
+	// Signals if user (re)started/stopped composing
+	void composing(bool);
+
+public slots:
+	void addEmoticon(const Icon *icon) {
+		if ( !dlg->isActiveWindow() )
+		     return;
+		
+		QString text;
+
+		QDict<QString> itext = icon->text();
+		QDictIterator<QString> it ( itext );
+		for ( ; it.current(); ++it) {
+			if ( it.current() && !it.current()->isEmpty() ) {
+				text = *(it.current()) + " ";
+				break;
+			}
+		}
+
+		if ( !text.isEmpty() )
+			mle->insert( text );
+	}
+
+	void addEmoticon(QString text) {
+		if ( !pa->psi()->isChatActiveWindow(dlg) )
+		     return;
+
+		mle->insert( text + " " );
+	}
+
+	void updateCounter() {
+		lcd_count->display((int)mle->text().length());
+	}
+
+	// Records that the user is composing
+	void setComposing() {
+		if (!composingTimer) {
+			/* User (re)starts composing */
+			composingTimer = new QTimer(this);
+			connect(composingTimer, SIGNAL(timeout()), SLOT(checkComposing()));
+			composingTimer->start(2000); // FIXME: magic number
+			emit composing(true);
+		}
+		isComposing = true;
+	}
+
+	// Checks if the user is still composing
+	void checkComposing() {
+		if (!isComposing) {
+			// User stopped composing
+			delete composingTimer;
+			composingTimer = 0;
+			emit composing(false);
+		}
+		isComposing = false; // Reset composing
+	}
+};
+
+ChatDlg::ChatDlg(const Jid &jid, PsiAccount *pa)
+{
+	d = new Private(this);
+	d->jid = jid;
+	d->pa = pa;
+
+	d->pending = 0;
+	d->keepOpen = false;
+	d->warnSend = false;
+	d->selfDestruct = 0;
+	d->transid = -1;
+	d->key = "";
+	d->lastWasEncrypted = false;
+
+	setAcceptDrops(TRUE);
+
+	QVBoxLayout *vb1 = new QVBoxLayout(this, 4);
+
+	QWidget *sp;
+	if ( !option.chatLineEdit ) {
+		sp = new QSplitter(Vertical, this);
+		vb1->addWidget(sp);
+	}
+	else
+		sp = this;
+
+	QWidget *sp_top = new QWidget(sp);
+	sp_top->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
+	if ( option.chatLineEdit )
+		vb1->addWidget( sp_top );
+	// first row
+	QVBoxLayout *vb2 = new QVBoxLayout(sp_top, 0, 4);
+	d->lb_ident = d->pa->accountLabel(sp_top, true);
+	d->lb_ident->setSizePolicy(QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ));
+
+	// second row
+	QHBoxLayout *hb2 = new QHBoxLayout(vb2);
+	d->lb_status = new IconLabel(sp_top);
+	d->lb_status->setIcon(IconsetFactory::iconPtr("status/noauth"));
+	hb2->addWidget(d->lb_status);
+	d->le_jid = new QLineEdit(sp_top);
+	d->le_jid->setReadOnly(true);
+	d->lcd_count = new QLCDNumber(sp_top);
+	d->le_jid->setFocusPolicy(QWidget::NoFocus);
+	QToolTip::add(d->lcd_count, tr("Message length"));
+	d->lcd_count->setFixedWidth(50);
+	hb2->addWidget(d->le_jid);
+	hb2->addWidget(d->lcd_count);
+	hb2->addWidget(d->lb_ident);
+
+	// mid area
+	d->log = new ChatView(sp_top);
+	vb2->addWidget(d->log);
+#ifdef Q_WS_MAC
+	connect(d->log,SIGNAL(selectionChanged()),SLOT(logSelectionChanged()));
+#endif
+
+	QWidget *sp_bottom = new QWidget(sp);
+	sp_bottom->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum );
+	// tool area
+	QVBoxLayout *vb3 = new QVBoxLayout(sp_bottom);
+	QHBoxLayout *hb3 = new QHBoxLayout(vb3);
+
+	d->toolbar = new PsiToolBar(tr("Chat toolbar"), 0, sp_bottom);
+	d->toolbar->setCustomizeable( false ); // it isn't ready now, and we don't want segfaults
+	d->toolbar->setFrameShape( QFrame::NoFrame );
+	hb3->addWidget( d->toolbar );
+
+	d->act_clear = new IconAction (tr("Clear chat window"), "psi/clearChat", tr("Clear chat window"), 0, this);
+	connect( d->act_clear, SIGNAL( activated() ), SLOT( doClearButton() ) );
+
+	connect(pa->psi()->iconSelectPopup(), SIGNAL(textSelected(QString)), d, SLOT(addEmoticon(QString)));
+	d->act_icon = new IconAction( tr( "Select icon" ), "psi/smile", tr( "Select icon" ), 0, this );
+	d->act_icon->setPopup( pa->psi()->iconSelectPopup() );
+
+	d->act_file = new IconAction( tr( "Send file" ), "psi/upload", tr( "Send file" ), 0, this );
+	connect( d->act_file, SIGNAL( activated() ), SLOT( doFile() ) );
+
+	d->act_pgp = new IconAction( tr( "Toggle encryption" ), "psi/cryptoNo", tr( "Toggle encryption" ), 0, this, 0, true );
+
+	d->act_info = new IconAction( tr( "User info" ), "psi/vCard", tr( "User info" ), CTRL+Key_I, this );
+	connect( d->act_info, SIGNAL( activated() ), SLOT( doInfo() ) );
+
+	d->act_history = new IconAction( tr( "Message history" ), "psi/history", tr( "Message history" ), CTRL+Key_H, this );
+	connect( d->act_history, SIGNAL( activated() ), SLOT( doHistory() ) );
+	
+	d->act_compact = new IconAction( tr( "Toggle Compact/Full size" ), "psi/compact", tr( "Toggle Compact/Full size" ), 0, this );
+	connect( d->act_compact, SIGNAL( activated() ), SLOT( toggleSmallChat() ) );
+
+	d->act_clear->addTo( d->toolbar );
+	d->toolbar->setStretchableWidget(new StretchWidget(d->toolbar));
+
+	d->act_icon->addTo( d->toolbar );
+	d->act_file->addTo( d->toolbar );
+	d->act_pgp->addTo( d->toolbar );
+	d->act_info->addTo( d->toolbar );
+	d->act_history->addTo( d->toolbar );
+
+	// Bottom but one row (composing notification)
+	QHBoxLayout *hb_comp = new QHBoxLayout(vb3);
+	d->lb_composing = new QLabel(sp_bottom,"composing");
+	d->lb_composing->setPaletteBackgroundColor(Qt::yellow.light(190));
+	hb_comp->addWidget(d->lb_composing);
+	d->lb_composing->hide();
+
+	// Bottom row
+	QHBoxLayout *hb4 = new QHBoxLayout(vb3);
+
+	// Text input
+	if ( !option.chatLineEdit ) {
+		d->mle = new ChatEdit(sp_bottom);
+		hb4->addWidget(d->mle);
+	}
+	else {
+		vb1->addWidget( sp_bottom );
+		QHBoxLayout *hb5 = new QHBoxLayout( vb1 );
+		d->mle = new LineEdit(sp);
+#ifdef Q_WS_MAC
+		hb5->addSpacing( 16 );
+#endif
+		hb5->addWidget( d->mle );
+#ifdef Q_WS_MAC
+		hb5->addSpacing( 16 );
+#endif
+	}
+
+	connect(d->mle, SIGNAL(textChanged()), d, SLOT(updateCounter()));
+
+	// Avatars
+#ifdef AVATARS
+	d->avatar = new QLabel(sp_bottom);
+	hb4->addWidget(d->avatar);
+	updateAvatar();
+	// FIXME: Maybe the psiaccount should do this, to avoid a signal to each
+	// open window when an avatar changes.
+	connect(d->pa->avatarFactory(),SIGNAL(avatarChanged(const Jid&)), this, SLOT(updateAvatar(const Jid&)));
+	connect(d->pa->psi(),SIGNAL(emitOptionsUpdate()), this, SLOT(updateAvatar()));
+#endif
+
+	d->pm_settings = new QPopupMenu(this);
+	connect(d->pm_settings, SIGNAL(aboutToShow()), SLOT(buildMenu()));
+
+	QValueList<int> list;
+	list << 324;
+	list << 96;
+	if ( !option.chatLineEdit )
+		((QSplitter *)sp)->setSizes(list);
+
+	d->status = -1;
+	d->pa->dialogRegister(this, d->jid);
+
+	// Message events
+	d->contactIsComposing = false;
+	d->sendComposingEvents = false;
+	d->isComposing = false;
+	d->composingTimer = 0;
+	connect(d->mle, SIGNAL(textChanged()), d, SLOT(setComposing()));
+	connect(d, SIGNAL(composing(bool)), SLOT(updateIsComposing(bool)));
+
+	updateContact(d->jid, true);
+
+	d->smallChat = option.smallChats;
+	X11WM_CLASS("chat");
+	setLooks();
+
+	updatePGP();
+	connect(d->pa, SIGNAL(pgpKeyChanged()), SLOT(updatePGP()));
+	connect(d->pa, SIGNAL(encryptedMessageSent(int, bool)), SLOT(encryptedMessageSent(int, bool)));
+#ifdef Q_WS_X11
+	d->le_jid->setFocusPolicy(QWidget::NoFocus);
+	d->log->setFocusPolicy(QWidget::NoFocus);
+#elif defined(Q_WS_MAC)
+	d->log->setFocusPolicy(QWidget::NoFocus);
+#endif
+	d->mle->setFocus();
+	resize(option.sizeChatDlg);
+
+	UserListItem *u = d->pa->findFirstRelavent(d->jid);
+	if(u && u->isSecure(d->jid.resource()))
+		d->act_pgp->setOn(true);
+}
+
+ChatDlg::~ChatDlg()
+{
+	d->pa->dialogUnregister(this);
+
+	delete d;
+}
+
+void ChatDlg::contextMenuEvent(QContextMenuEvent *)
+{
+	d->pm_settings->exec(QCursor::pos());
+}
+
+void ChatDlg::keyPressEvent(QKeyEvent *e)
+{
+	if(e->key() == Key_Escape && !option.useTabs)
+		close();
+//#ifdef Q_WS_MAC //this is a standard on other platforms too
+	else if(e->key() == Key_W && e->state() & ControlButton && !option.useTabs)
+		close();
+//#endif
+	else if(e->key() == Key_Return || e->key() == Key_Enter || (e->key() == Key_S && (e->state() & AltButton)))
+		doSend();
+	else if(e->key() == Key_PageUp && (e->state() & ShiftButton))
+		d->log->setContentsPos(d->log->contentsX(), d->log->contentsY() - d->log->visibleHeight()/2);
+	else if(e->key() == Key_PageDown && (e->state() & ShiftButton))
+		d->log->setContentsPos(d->log->contentsX(), d->log->contentsY() + d->log->visibleHeight()/2);
+	else
+		e->ignore();
+}
+
+void ChatDlg::resizeEvent(QResizeEvent *e)
+{
+	if(option.keepSizes)
+		option.sizeChatDlg = e->size();
+}
+
+void ChatDlg::closeEvent(QCloseEvent *e)
+{
+	// really lame way of checking if we are encrypting
+	if(!d->mle->isEnabled())
+		return;
+
+	if(d->keepOpen) {
+		int n = QMessageBox::information(this, tr("Warning"), tr("A new chat message was just received.\nDo you still want to close the window?"), tr("&Yes"), tr("&No"));
+		if(n != 0)
+			return;
+	}
+
+	// destroy the dialog if delChats is dcClose
+	if(option.delChats == dcClose)
+		setWFlags(getWFlags() | WDestructiveClose);
+	else {
+		if(option.delChats == dcHour)
+			setSelfDestruct(60);
+		else if(option.delChats == dcDay)
+			setSelfDestruct(60 * 24);
+	}
+
+	// Reset 'contact is composing' & cancel own composing event
+	updateContactIsComposing(false);
+	if (d->composingTimer) {
+		delete d->composingTimer;
+		d->composingTimer = 0;
+		d->isComposing = false;
+		updateIsComposing(false);
+	}
+
+
+	if(d->pending > 0) {
+		d->pending = 0;
+		messagesRead(d->jid);
+		updateCaption();
+	}
+	doFlash(false);
+
+	d->mle->setFocus();
+	e->accept();
+}
+
+void ChatDlg::showEvent(QShowEvent *)
+{
+	setSelfDestruct(0);
+}
+
+void ChatDlg::windowActivationChange(bool oldstate)
+{
+	QWidget::windowActivationChange(oldstate);
+
+	// if we're bringing it to the front, get rid of the '*' if necessary
+	if( isActiveWindow() && !isHidden() ) { //this is a tab hack
+		activated();
+	}
+}
+
+void ChatDlg::logSelectionChanged()
+{
+#ifdef Q_WS_MAC
+	// A hack to only give the message log focus when text is selected
+	if (d->log->hasSelectedText()) 
+		d->log->setFocus();
+	else 
+		d->mle->setFocus();
+#endif
+}
+
+void ChatDlg::activated()
+{
+	if(d->pending > 0) {
+		d->pending = 0;
+		messagesRead(d->jid);
+		updateCaption();
+	}
+	doFlash(false);
+
+	if(option.showCounter && !d->smallChat)
+		d->lcd_count->show();
+	else
+		d->lcd_count->hide();
+
+	d->mle->setFocus();
+}
+
+
+void ChatDlg::dropEvent(QDropEvent* event)
+{
+	QStringList l;
+	if (d->pa->loggedIn() && QUriDrag::decodeLocalFiles(event,l) && !l.isEmpty())
+		d->pa->actionSendFiles(d->jid,l);
+}
+
+void ChatDlg::dragEnterEvent(QDragEnterEvent* event)
+{
+	QStringList l;
+	event->accept(d->pa->loggedIn() && QUriDrag::canDecode(event) && QUriDrag::decodeLocalFiles(event,l) && !l.isEmpty());
+}
+
+
+const Jid & ChatDlg::jid() const
+{
+	return d->jid;
+}
+
+void ChatDlg::setJid(const Jid &jid)
+{
+	if(!jid.compare(d->jid)) {
+		d->pa->dialogUnregister(this);
+		d->jid = jid;
+		d->pa->dialogRegister(this, d->jid);
+		updateContact(d->jid, false);
+	}
+}
+
+const QString& ChatDlg::getDisplayNick()
+{
+	return d->dispNick;
+}
+
+QSize ChatDlg::defaultSize()
+{
+	return QSize(320, 280);
+}
+
+void ChatDlg::updateContact(const Jid &jid, bool fromPresence)
+{
+	// if groupchat, only update if the resource matches
+	if(d->pa->findGCContact(jid) && !d->jid.compare(jid))
+		return;
+
+	if(d->jid.compare(jid, false)) {
+		QString rname = d->jid.resource();
+		QPtrList<UserListItem> ul = d->pa->findRelavent(jid);
+		UserListItem *u = 0;
+		int status = -1;
+		QString statusString, key;
+		if(!ul.isEmpty()) {
+			u = ul.first();
+			if(rname.isEmpty()) {
+				// use priority
+				if(!u->isAvailable())
+					status = STATUS_OFFLINE;
+				else {
+					const UserResource &r = *u->userResourceList().priority();
+					status = makeSTATUS(r.status());
+					statusString = r.status().status();
+					key = r.publicKeyID();
+				}
+			}
+			else {
+				// use specific
+				UserResourceList::ConstIterator rit = u->userResourceList().find(rname);
+				if(rit != u->userResourceList().end()) {
+					status = makeSTATUS((*rit).status());
+					statusString = (*rit).status().status();
+					key = (*rit).publicKeyID();
+				}
+				else {
+					status = STATUS_OFFLINE;
+					statusString = u->lastUnavailableStatus().status();
+					key = "";
+				}
+			}
+		}
+
+		bool statusChanged = false;
+		if(d->status != status || d->statusString != statusString) {
+			statusChanged = true;
+			d->status = status;
+			d->statusString = statusString;
+		}
+
+		if(statusChanged) {
+			if(status == -1 || !u)
+				d->lb_status->setIcon(IconsetFactory::iconPtr("status/noauth"));
+			else
+				d->lb_status->setIcon(is->statusPtr(jid, status));
+		}
+
+		if(u)
+			QToolTip::add(d->lb_status, u->makeTip(true, false));
+		else
+			QToolTip::remove(d->lb_status);
+
+		if(u) {
+			QString name;
+			QString j;
+			if(rname.isEmpty())
+				j = u->jid().full();
+			else
+				j = u->jid().userHost() + '/' + rname;
+
+			if(!u->name().isEmpty())
+				name = u->name() + QString(" <%1>").arg(j);
+			else
+				name = j;
+
+			d->le_jid->setText(name);
+			d->le_jid->setCursorPosition(0);
+			QToolTip::add(d->le_jid, name);
+
+			d->dispNick = jidnick(u->jid().full(), u->name());
+			updateCaption();
+
+			d->key = key;
+			updatePGP();
+
+			if(fromPresence && statusChanged) {
+				QString msg = tr("%1 is %2").arg(expandEntities(d->dispNick)).arg(status2txt(d->status));
+				if(!statusString.isEmpty()) {
+					QString ss = linkify(plain2rich(statusString));
+					if(option.useEmoticons)
+						ss = emoticonify(ss);
+
+					msg += QString(" [%1]").arg(ss);
+				}
+				appendSysMsg(msg);
+			}
+		}
+
+		// Reset 'is composing' event if the status changed
+		if (statusChanged) {
+			updateContactIsComposing(false);
+		}
+	}
+}
+
+
+void ChatDlg::updateAvatar(const Jid& j)
+{
+	if (j.compare(d->jid,false))
+		updateAvatar();
+}
+
+void ChatDlg::updateAvatar()
+{
+#ifdef AVATARS
+	QString res;
+	QString client;
+
+	if (option.chatLineEdit || !option.avatarsEnabled || !option.avatarsChatdlgEnabled) {
+		d->avatar->hide();
+		return;
+	}
+
+	UserListItem *ul = d->pa->findFirstRelavent(d->jid);
+	if (ul && !ul->userResourceList().isEmpty()) {
+		UserResourceList::Iterator it = ul->userResourceList().find(d->jid.resource());
+		if(it == ul->userResourceList().end())
+			it = ul->userResourceList().priority();
+
+		res = (*it).name();
+		client = (*it).clientName();
+	}
+	QPixmap p = d->pa->avatarFactory()->getAvatar(d->jid.withResource(res),client);
+	if (p.isNull()) {
+		d->avatar->hide();
+	}
+	else {
+		d->avatar->setPixmap(p);
+		d->avatar->show();
+	}
+#endif
+}
+
+
+void ChatDlg::setLooks()
+{
+	// update the font
+	QFont f;
+	f.fromString(option.font[fChat]);
+	d->log->setFont(f);
+	d->mle->setFont(f);
+
+	if (d->smallChat) {
+		d->lb_ident->hide();
+		d->lb_status->hide();
+		d->le_jid->hide();
+		d->toolbar->hide();
+	}
+	else {
+		d->lb_ident->show();
+		d->lb_status->show();
+		d->le_jid->show();
+		d->toolbar->show();
+	}
+
+	if ( option.showCounter && !d->smallChat )
+		d->lcd_count->show();
+	else
+		d->lcd_count->hide();
+
+	// update contact info
+	d->status = -2; // sick way of making it redraw the status
+	updateContact(d->jid, false);
+
+	// toolbuttons
+	QIconSet i;
+	i.setPixmap(IconsetFactory::icon("psi/cryptoNo"),  QIconSet::Automatic, QIconSet::Normal, QIconSet::Off);
+	i.setPixmap(IconsetFactory::icon("psi/cryptoYes"), QIconSet::Automatic, QIconSet::Normal, QIconSet::On);
+	d->act_pgp->setIcon( 0 );
+	d->act_pgp->setIconSet( i );
+
+	// update the widget icon
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/start-chat"));
+#endif
+
+	/*QBrush brush;
+	brush.setPixmap( QPixmap( option.chatBgImage ) );
+	d->log->setPaper(brush);
+	d->log->setStaticBackground(true);*/
+
+#if QT_VERSION >= 0x030300
+	setWindowOpacity(double(option.chatOpacity)/100);
+#endif
+}
+
+void ChatDlg::optionsUpdate()
+{
+	if (option.oldSmallChats!=option.smallChats)
+	{
+		d->smallChat=option.smallChats;
+	}
+
+	setLooks();
+
+	if(isHidden()) {
+		if(option.delChats == dcClose) {
+			deleteLater();
+			return;
+		}
+		else {
+			if(option.delChats == dcHour)
+				setSelfDestruct(60);
+			else if(option.delChats == dcDay)
+				setSelfDestruct(60 * 24);
+			else
+				setSelfDestruct(0);
+		}
+	}
+}
+
+void ChatDlg::updatePGP()
+{
+	if(!d->pa->pgpKey().isEmpty()) {
+		d->act_pgp->setEnabled(true);
+	}
+	else {
+		d->act_pgp->setOn(false);
+		d->act_pgp->setEnabled(false);
+	}
+}
+
+void ChatDlg::doInfo()
+{
+	aInfo(d->jid);
+}
+
+void ChatDlg::doHistory()
+{
+	aHistory(d->jid);
+}
+
+void ChatDlg::doFile()
+{
+	aFile(d->jid);
+}
+
+void ChatDlg::doClearButton()
+{
+	int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to clear the chat window?\n(note: does not affect saved history)"), tr("&Yes"), tr("&No"));
+	if(n == 0)
+		doClear();
+}
+
+void ChatDlg::doClear()
+{
+	d->log->setText("");
+}
+
+void ChatDlg::setKeepOpenFalse()
+{
+	d->keepOpen = false;
+}
+
+void ChatDlg::setWarnSendFalse()
+{
+	d->warnSend = false;
+}
+
+void ChatDlg::setSelfDestruct(int minutes)
+{
+	if(minutes <= 0) {
+		if(d->selfDestruct) {
+			delete d->selfDestruct;
+			d->selfDestruct = 0;
+		}
+		return;
+	}
+
+	if(!d->selfDestruct) {
+		d->selfDestruct = new QTimer(this);
+		connect(d->selfDestruct, SIGNAL(timeout()), SLOT(deleteLater()));
+	}
+
+	d->selfDestruct->start(minutes * 60000);
+}
+
+void ChatDlg::updateCaption()
+{
+	QString cap = "";
+
+	if(d->pending > 0) {
+		cap += "* ";
+		if(d->pending > 1)
+			cap += QString("[%1] ").arg(d->pending);
+	}
+	cap += d->dispNick;
+
+	if (d->contactIsComposing)
+		cap = tr("%1 (Composing ...)").arg(cap);
+
+	setCaption(cap);
+
+	emit captionChanged(this);
+	emit unreadMessageUpdate(this, d->pending);
+}
+
+void ChatDlg::doSend()
+{
+	if(!d->mle->isEnabled())
+		return;
+
+	if(d->mle->text().isEmpty())
+		return;
+
+	if(d->mle->text() == "/clear") {
+		d->mle->setText("");
+		doClear();
+		return;
+	}
+
+	if(!d->pa->loggedIn())
+		return;
+
+	if(d->warnSend) {
+		d->warnSend = false;
+		int n = QMessageBox::information(this, tr("Warning"), tr(
+			"<p>Encryption was recently disabled by the remote contact.  "
+			"Are you sure you want to send this message without encryption?</p>"
+			), tr("&Yes"), tr("&No"));
+		if(n != 0)
+			return;
+	}
+
+	Message m(d->jid);
+	m.setType("chat");
+	m.setBody(d->mle->text());
+	m.setTimeStamp(QDateTime::currentDateTime());
+	if(d->act_pgp->isOn())
+		m.setWasEncrypted(true);
+	d->m = m;
+
+ 	// Request events
+ 	if (option.messageEvents) {
+ 		//m.addEvent(OfflineEvent);
+ 		//m.addEvent(DeliveredEvent);
+ 		//m.addEvent(DisplayedEvent);
+ 		m.addEvent(ComposingEvent);
+ 	}
+
+	if(d->act_pgp->isOn()) {
+		d->mle->setEnabled(false);
+		d->transid = d->pa->sendMessageEncrypted(m);
+		if(d->transid == -1) {
+			d->mle->setEnabled(true);
+			d->mle->setFocus();
+			return;
+		}
+	}
+	else {
+		aSend(m);
+		doneSend();
+	}
+}
+
+void ChatDlg::doneSend()
+{
+	appendMessage(d->m, true);
+	disconnect(d->mle, SIGNAL(textChanged()), d, SLOT(setComposing()));
+	d->mle->setText("");
+
+	// Reset composing timer
+	connect(d->mle, SIGNAL(textChanged()), d, SLOT(setComposing()));
+	if (d->composingTimer) {
+		delete d->composingTimer;
+		d->composingTimer = 0;
+		d->isComposing = false;
+	}
+}
+
+void ChatDlg::encryptedMessageSent(int x, bool b)
+{
+	if(d->transid == -1)
+		return;
+	if(d->transid != x)
+		return;
+	d->transid = -1;
+	if(b)
+		doneSend();
+	else
+		QMessageBox::information(this, tr("Error"), tr("There was an error trying to send the message encrypted.\nCheck your OpenPGP application/settings."));
+	d->mle->setEnabled(true);
+	d->mle->setFocus();
+}
+
+void ChatDlg::incomingMessage(const Message &m)
+{
+	if (m.body().isEmpty()) {
+		/* Event message */
+		if (m.containsEvent(CancelEvent))
+			updateContactIsComposing(false);
+		if (m.containsEvent(ComposingEvent))
+			updateContactIsComposing(true);
+	}
+	else {
+		// Normal message
+		// Check if user requests event messages
+		d->sendComposingEvents = m.containsEvent(ComposingEvent);
+		if (!m.eventId().isEmpty())
+			d->eventId = m.eventId();
+		updateContactIsComposing(false);
+		appendMessage(m);
+	}
+}
+
+void ChatDlg::appendSysMsg(const QString &str)
+{
+	QString timestr;
+	QDateTime time = QDateTime::currentDateTime();
+	//timestr.sprintf("%02d:%02d:%02d", time.time().hour(), time.time().minute(), time.time().second());
+	timestr = time.time().toString(LocalDate);
+
+	int y = d->log->contentsHeight() - d->log->visibleHeight();
+	if(y < 0)
+		y = 0;
+	bool atBottom = (d->log->contentsY() < y - 32) ? false: true;
+
+	d->log->append(QString("<font color=\"#00A000\">[%1]").arg(timestr) + QString(" *** %1</font>").arg(str));
+	if(atBottom)
+		deferredScroll();
+}
+
+void ChatDlg::appendMessage(const Message &m, bool local)
+{
+	QString who, color;
+
+	if(local) {
+		who = d->pa->nick();
+		color = "#FF0000";
+	}
+	else {
+		who = d->dispNick;
+		color = "#0000FF";
+	}
+	if(m.spooled())
+		color = "#008000";
+
+	// figure out the encryption state
+	bool encChanged = false;
+	bool encEnabled = false;
+	if(d->lastWasEncrypted != m.wasEncrypted())
+		encChanged = true;
+	d->lastWasEncrypted = m.wasEncrypted();
+	encEnabled = d->lastWasEncrypted;
+
+	if(encChanged) {
+		if(encEnabled) {
+			appendSysMsg(QString("<icon name=\"psi/cryptoYes\"> ") + tr("Encryption Enabled"));
+			if(!local)
+				d->act_pgp->setOn(true);
+		}
+		else {
+			appendSysMsg(QString("<icon name=\"psi/cryptoNo\"> ") + tr("Encryption Disabled"));
+			if(!local) {
+				d->act_pgp->setOn(false);
+
+				// enable warning
+				d->warnSend = true;
+				QTimer::singleShot(3000, this, SLOT(setWarnSendFalse()));
+			}
+		}
+	}
+
+	QString timestr;
+	QDateTime time = m.timeStamp();
+	timestr = time.time().toString(LocalDate);
+
+	int y = d->log->contentsHeight() - d->log->visibleHeight();
+	if(y < 0)
+		y = 0;
+	bool atBottom = (d->log->contentsY() < y - 32) ? false: true;
+
+	bool emote = false;
+	if(m.body().left(4) == "/me ")
+		emote = true;
+
+	QString txt;
+	if(emote)
+		txt = plain2rich(m.body().mid(4));
+	else
+		txt = plain2rich(m.body());
+
+	txt = linkify(txt);
+
+	if(option.useEmoticons)
+		txt = emoticonify(txt);
+
+	who = expandEntities(who);
+
+	if(emote) {
+		d->log->append(QString("<font color=\"%1\">").arg(color) + QString("[%1]").arg(timestr) + QString(" *%1 ").arg(who) + txt + "</font>");
+	}
+	else {
+		if(option.chatSays)
+			d->log->append(QString("<font color=\"%1\">").arg(color) + QString("[%1] ").arg(timestr) + tr("%1 says:").arg(who) + "</font><br>" + txt);
+		else
+			d->log->append(QString("<font color=\"%1\">").arg(color) + QString("[%1] &lt;").arg(timestr) + who + QString("&gt;</font> ") + txt);
+	}
+	if(!m.subject().isEmpty()) {
+		d->log->append(QString("<b>") + tr("Subject:") + "</b> " + QString("%1").arg(expandEntities(m.subject())));
+	}
+	if(!m.urlList().isEmpty()) {
+		UrlList urls = m.urlList();
+		d->log->append(QString("<i>") + tr("-- Attached URL(s) --") + "</i>");
+		for(QValueList<Url>::ConstIterator it = urls.begin(); it != urls.end(); ++it) {
+			const Url &u = *it;
+			d->log->append(QString("<b>") + tr("URL:") + "</b> " + QString("%1").arg( linkify(expandEntities(u.url())) ));
+			d->log->append(QString("<b>") + tr("Desc:") + "</b> " + QString("%1").arg(u.desc()));
+		}
+	}
+
+	if(local || atBottom)
+		deferredScroll();
+
+	// if we're not active, notify the user by changing the title
+	if(!isActiveWindow() || isHidden()) { //isHidden==tab hack
+		if (!isActiveWindow() || !option.raiseChatWindow) {
+			++d->pending;
+			updateCaption();
+			doFlash(true);
+		}
+		TabDlg *dlg = d->pa->psi()->getManagingTabs(this);
+		if(option.raiseChatWindow) {
+			if (!dlg) {
+				bringToFront(this, false);
+			} else {
+				dlg->selectTab(this);
+				bringToFront(dlg, false);
+			}
+		}
+	}
+	//else {
+	//	messagesRead(d->jid);
+	//}
+
+	if(!local) {
+		d->keepOpen = true;
+		QTimer::singleShot(1000, this, SLOT(setKeepOpenFalse()));
+	}
+}
+
+void ChatDlg::deferredScroll()
+{
+	QTimer::singleShot(250, this, SLOT(slotScroll()));
+}
+
+void ChatDlg::slotScroll()
+{
+	d->log->scrollToBottom();
+}
+
+void ChatDlg::updateIsComposing(bool c)
+{
+	if (option.messageEvents && d->sendComposingEvents) {
+		// Don't send to offline resource
+		QPtrList<UserListItem> ul = d->pa->findRelavent(d->jid);
+		if(ul.isEmpty()) {
+			d->sendComposingEvents = false;
+			return;
+		}
+
+		QString rname = d->jid.resource();
+		UserListItem *u = ul.first();
+		if(rname.isEmpty() && !u->isAvailable() || u->userResourceList().find(rname) == u->userResourceList().end()) {
+			d->sendComposingEvents = false;
+			return;
+		}
+
+		// Send event message
+		Message m(d->jid);
+		m.setEventId(d->eventId);
+		if (c) {
+			m.addEvent(ComposingEvent);
+		}
+		else {
+			m.addEvent(CancelEvent);
+		}
+		d->pa->dj_sendMessage(m, false);
+	}
+}
+
+void ChatDlg::updateContactIsComposing(bool c)
+{
+	d->contactIsComposing = c;
+	emit contactIsComposing(this, c);
+	updateCaption();
+	/*if (c)
+	{
+		d->lb_composing->setText(QString(d->dispNick+" "+tr("is composing a reply.")));
+		d->lb_composing->show();
+	}
+	else
+	{
+		d->lb_composing->hide();
+	}*/
+}
+
+void ChatDlg::toggleSmallChat()
+{
+	d->smallChat = !d->smallChat;
+	setLooks();
+}
+
+void ChatDlg::toggleEncryption()
+{
+	d->act_pgp->setOn( !d->act_pgp->isOn() );
+}
+
+void ChatDlg::buildMenu()
+{
+	// Dialog menu
+	d->pm_settings->clear();
+	d->act_compact->addTo( d->pm_settings );
+	d->act_clear->addTo( d->pm_settings );
+	d->pm_settings->insertSeparator();
+
+	d->act_icon->addTo( d->pm_settings );
+	d->act_file->addTo( d->pm_settings );
+	d->act_pgp->addTo( d->pm_settings );
+	d->pm_settings->insertSeparator();
+
+	d->act_info->addTo( d->pm_settings );
+	d->act_history->addTo( d->pm_settings );
+}
+
+#include "chatdlg.moc"
Index: psi/tags/0.10-os2-b1/src/chatdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/chatdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/chatdlg.h	(revision 38)
@@ -0,0 +1,111 @@
+/*
+ * chatdlg.h - dialog for handling chats
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CHATDLG_H
+#define CHATDLG_H
+
+#include "advwidget.h"
+#include "im.h"
+
+using namespace XMPP;
+
+class PsiAccount;
+class UserListItem;
+class QDropEvent;
+class QDragEnterEvent;
+
+class ChatDlg : public AdvancedWidget<QWidget>
+{
+	Q_OBJECT
+public:
+	ChatDlg(const Jid &, PsiAccount *);
+	~ChatDlg();
+
+	const Jid & jid() const;
+	void setJid(const Jid &);
+	const QString & getDisplayNick();
+
+	static QSize defaultSize();
+
+signals:
+	void aInfo(const Jid &);
+	void aHistory(const Jid &);
+	void messagesRead(const Jid &);
+	void aSend(const Message &);
+	void aFile(const Jid &);
+	void captionChanged(ChatDlg*);
+	void contactIsComposing(ChatDlg*, bool);
+	void unreadMessageUpdate(ChatDlg*, int);
+
+protected:
+	// reimplemented
+	void keyPressEvent(QKeyEvent *);
+	void closeEvent(QCloseEvent *);
+	void resizeEvent(QResizeEvent *);
+	void showEvent(QShowEvent *);
+	void windowActivationChange(bool);
+	void dropEvent(QDropEvent* event);
+	void dragEnterEvent(QDragEnterEvent* event);
+
+public slots:
+	void optionsUpdate();
+	void updateContact(const Jid &, bool);
+	void incomingMessage(const Message &);
+	void activated();
+	void updateAvatar();
+	void updateAvatar(const Jid&);
+
+private slots:
+	void doInfo();
+	void doHistory();
+	void doClear();
+	void doClearButton();
+	void doSend();
+	void doFile();
+	void setKeepOpenFalse();
+	void setWarnSendFalse();
+	void updatePGP();
+	void encryptedMessageSent(int, bool);
+	void slotScroll();
+	void updateIsComposing(bool);
+	void updateContactIsComposing(bool);
+	void toggleSmallChat();
+	void toggleEncryption();
+	void buildMenu();
+	void logSelectionChanged();
+
+public:
+	class Private;
+private:
+	Private *d;
+
+	void contextMenuEvent(QContextMenuEvent *);
+
+	void doneSend();
+	void setLooks();
+	void setSelfDestruct(int);
+	void updateCaption();
+	void deferredScroll();
+
+	void appendMessage(const Message &, bool local=false);
+	void appendSysMsg(const QString &);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/common.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/common.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/common.cpp	(revision 38)
@@ -0,0 +1,1795 @@
+/*
+ * common.cpp - contains all the common variables and functions for Psi
+ * Copyright (C) 2001-2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"common.h"
+
+#include"profiles.h"
+#include"rtparse.h"
+
+#include"psievent.h"
+
+QString PROG_NAME = "Psi";
+#ifdef Q_WS_PM
+QString PROG_VERSION = "0.10-os2-b1 (" __DATE__ ")"; // OS/2 versions are betas yet
+#else
+//QString PROG_VERSION = "0.11-dev" " (" __DATE__ ")"; //CVS Builds are dated
+QString PROG_VERSION = "0.10";
+#endif
+
+#ifdef HAVE_CONFIG
+#include "config.h"
+#endif
+
+#ifndef PSI_DATADIR
+#define PSI_DATADIR "/usr/local/share/psi"
+#endif
+
+#include<qregexp.h>
+#include<qdir.h>
+#include<qfile.h>
+#include<qapplication.h>
+#include<qsound.h>
+#include<qobjectlist.h>
+#include<qlibrary.h>
+#include<qstylesheet.h>
+
+
+#include<stdio.h>
+
+#ifdef Q_WS_X11
+#include<sys/types.h>
+#include<sys/stat.h>
+#include<sys/wait.h>
+#endif
+
+#ifdef Q_WS_WIN
+#include<windows.h>
+#endif
+
+#ifdef Q_WS_PM
+#include<sys/types.h>
+#include<sys/stat.h>
+#endif
+
+#ifdef Q_WS_MAC
+#include<sys/types.h>
+#include<sys/stat.h>
+#include <Carbon/Carbon.h> // for HIToolbox/InternetConfig
+#include <CoreServices/CoreServices.h>
+#endif
+
+QString activeProfile;
+
+//QStringList dtcp_hostList;
+//int dtcp_port;
+//QString dtcp_proxy;
+//bool link_test = false;
+bool use_gpg = true;
+bool no_gpg_agent = false;
+uint psi_dialog_flags = (Qt::WStyle_SysMenu | Qt::WStyle_MinMax);
+//uint psi_dialog_flags = (Qt::WStyle_Customize | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu | Qt::WStyle_MinMax);
+
+char *pixdat_ft_back, *pixdat_ft_file, *pixdat_ft_folder;
+int pixlen_ft_back, pixlen_ft_file, pixlen_ft_folder;
+
+
+XMPP::Client *debug_jabber = 0;
+Options option;
+PsiGlobal g;
+PsiIconset *is;
+bool useSound;
+
+
+QString qstrlower(QString str)
+{
+	for(unsigned int n = 0; n < str.length(); ++n) {
+		str.at(n) = str.at(n).lower();
+	}
+
+	return str;
+}
+
+int qstrcasecmp(const QString &str1, const QString &str2)
+{
+        if(str1.length() != str2.length())
+                return 1;
+
+        for(unsigned int n = 0; n < str1.length(); ++n) {
+                if(str1.at(n).lower() != str2.at(n).lower())
+                        return 1;
+        }
+
+        return 0;
+}
+
+int qstringlistmatch(QStringList &list, const QString &str)
+{
+	int n = 0;
+
+        for(QStringList::Iterator i = list.begin(); i != list.end(); ++i, ++n) {
+                if(*i == str)
+                        return n;
+        }
+
+        return -1;
+}
+
+QString qstringlistlookup(QStringList &list, int x)
+{
+	int n = 0;
+	QStringList::Iterator i = list.begin();
+	for(;i != list.end() && n < x; ++i, ++n);
+	if(n != x)
+		return "";
+
+	return *i;
+}
+
+QString CAP(const QString &str)
+{
+	return QString("%1: %2").arg(PROG_NAME).arg(str);
+}
+
+QString eatptag(QString txt)
+{
+	if(txt.left(3) == "<p>")
+                txt = txt.mid(3, txt.length() - 7);  // strlen("<p>") + strlen("</p>") = 7
+
+	return txt;
+}
+
+QString qstrquote(const QString &toquote, int width, bool quoteEmpty)
+{
+	int ql = 0, col = 0, atstart = 1, ls=0;
+
+	QString quoted = "> "+toquote;  // quote first line
+	QString rxs  = quoteEmpty ? "\n" : "\n(?!\\s*\n)";
+	QRegExp rx(rxs);                // quote following lines
+	quoted.replace(rx, "\n> ");
+	rx.setPattern("> +>");          // compress > > > > quotes to >>>>
+	quoted.replace(rx, ">>");
+	quoted.replace(rx, ">>");
+	quoted.replace(QRegExp(" +\n"), "\n");  // remove trailing spaces
+
+	if (!quoteEmpty)
+	{
+		quoted.replace(QRegExp("^>+\n"), "\n\n");  // unquote empty lines
+		quoted.replace(QRegExp("\n>+\n"), "\n\n");
+	}
+
+
+	for (int i=0;i<(int) quoted.length();i++)
+	{
+		col++;
+		if (atstart && quoted[i] == '>') ql++; else atstart=0;
+
+		switch(quoted[i].latin1())
+		{
+			case '\n': ql = col = 0; atstart = 1; break;
+			case ' ':
+			case '\t': ls = i; break;
+
+		}
+		if (quoted[i]=='\n') { ql=0; atstart = 1; }
+
+		if (col > width)
+		{
+			if ((ls+width) < i)
+			{
+				ls = i; i = quoted.length();
+				while ((ls<i) && !quoted[ls].isSpace()) ls++;
+				i = ls;
+			}
+			if ((i<(int)quoted.length()) && (quoted[ls] != '\n'))
+			{
+				quoted.insert(ls, '\n');
+				++ls;
+				quoted.insert(ls, QString().fill('>', ql));
+				i += ql+1;
+				col = 0;
+			}
+		}
+	}
+	quoted += "\n\n";// add two empty lines to quoted text - the cursor
+			// will be positioned at the end of those.
+	return quoted;
+}
+
+QString plain2rich(const QString &plain)
+{
+	QString rich;
+	int col = 0;
+
+	for(int i = 0; i < (int)plain.length(); ++i) {
+		if(plain[i] == '\n') {
+			rich += "<br>";
+			col = 0;
+		}
+		else if(plain[i] == '\t') {
+			rich += QChar::nbsp;
+			while(col % 4) {
+				rich += QChar::nbsp;
+				++col;
+			}
+		}
+		else if(plain[i].isSpace()) {
+			if(i > 0 && plain[i-1] == ' ')
+				rich += QChar::nbsp;
+			else
+				rich += ' ';
+		}
+		else if(plain[i] == '<')
+			rich += "&lt;";
+		else if(plain[i] == '>')
+			rich += "&gt;";
+		else if(plain[i] == '\"')
+			rich += "&quot;";
+		else if(plain[i] == '\'')
+			rich += "&apos;";
+		else if(plain[i] == '&')
+			rich += "&amp;";
+		else
+			rich += plain[i];
+		++col;
+	}
+
+	return rich;
+}
+
+QString rich2plain(const QString &in)
+{
+	QString out;
+
+	for(int i = 0; i < (int)in.length(); ++i) {
+		// tag?
+		if(in[i] == '<') {
+			// find end of tag
+			++i;
+			int n = in.find('>', i);
+			if(n == -1)
+				break;
+			QString str = in.mid(i, (n-i));
+			i = n;
+
+			QString tagName;
+			n = str.find(' ');
+			if(n != -1)
+				tagName = str.mid(0, n);
+			else
+				tagName = str;
+
+			if(tagName == "br")
+				out += '\n';
+		}
+		// entity?
+		else if(in[i] == '&') {
+			// find a semicolon
+			++i;
+			int n = in.find(';', i);
+			if(n == -1)
+				break;
+			QString type = in.mid(i, (n-i));
+			i = n; // should be n+1, but we'll let the loop increment do it
+
+			if(type == "amp")
+				out += '&';
+			else if(type == "lt")
+				out += '<';
+			else if(type == "gt")
+				out += '>';
+			else if(type == "quot")
+				out += '\"';
+			else if(type == "apos")
+				out += '\'';
+		}
+		else if(in[i].isSpace()) {
+			if(in[i] == QChar::nbsp)
+				out += ' ';
+			else if(in[i] != '\n') {
+				if(i == 0)
+					out += ' ';
+				else {
+					QChar last = out.at(out.length()-1);
+					bool ok = TRUE;
+					if(last.isSpace() && last != '\n')
+						ok = FALSE;
+					if(ok)
+						out += ' ';
+				}
+			}
+		}
+		else {
+			out += in[i];
+		}
+	}
+
+	return out;
+}
+
+
+// clips plain text
+QString clipStatus(const QString &str, int width, int height)
+{
+	QString out = "";
+	int at = 0;
+	int len = str.length();
+	if(len == 0)
+		return out;
+
+	// only take the first "height" lines
+	for(int n2 = 0; n2 < height; ++n2) {
+		// only take the first "width" chars
+		QString line;
+		bool hasNewline = false;
+		for(int n = 0; at < len; ++n, ++at) {
+			if(str.at(at) == '\n') {
+				hasNewline = true;
+				break;
+			}
+			line += str.at(at);
+		}
+		++at;
+		if((int)line.length() > width) {
+			line.truncate(width-3);
+			line += "...";
+		}
+		out += line;
+		if(hasNewline)
+			out += '\n';
+
+		if(at >= len)
+			break;
+	}
+
+	return out;
+}
+
+QString expandEntities(const QString &in)
+{
+	return QStyleSheet::escape(in);
+}
+
+QString resolveEntities(const QString &in)
+{
+	QString out;
+
+	for(int i = 0; i < (int)in.length(); ++i) {
+		if(in[i] == '&') {
+			// find a semicolon
+			++i;
+			int n = in.find(';', i);
+			if(n == -1)
+				break;
+			QString type = in.mid(i, (n-i));
+			i = n; // should be n+1, but we'll let the loop increment do it
+
+			if(type == "amp")
+				out += '&';
+			else if(type == "lt")
+				out += '<';
+			else if(type == "gt")
+				out += '>';
+			else if(type == "quot")
+				out += '\"';
+			else if(type == "apos")
+				out += '\'';
+		}
+		else {
+			out += in[i];
+		}
+	}
+
+	return out;
+}
+
+
+static bool linkify_pmatch(const QString &str1, int at, const QString &str2)
+{
+	if(str2.length() > (str1.length()-at))
+		return FALSE;
+
+	for(int n = 0; n < (int)str2.length(); ++n) {
+		if(str1.at(n+at).lower() != str2.at(n).lower())
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static bool linkify_isOneOf(const QChar &c, const QString &charlist)
+{
+	for(int i = 0; i < (int)charlist.length(); ++i) {
+		if(c == charlist.at(i))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+// encodes a few dangerous html characters
+static QString linkify_htmlsafe(const QString &in)
+{
+	QString out;
+
+	for(unsigned int n = 0; n < in.length(); ++n) {
+		if(linkify_isOneOf(in.at(n), "\"\'`<>")) {
+			// hex encode
+			QString hex;
+			hex.sprintf("%%%02X", in.at(n).latin1());
+			out.append(hex);
+		}
+		else {
+			out.append(in.at(n));
+		}
+	}
+
+	return out;
+}
+
+static bool linkify_okUrl(const QString &url)
+{
+	if(url.at(url.length()-1) == '.')
+		return FALSE;
+
+	return TRUE;
+}
+
+static bool linkify_okEmail(const QString &addy)
+{
+	// this makes sure that there is an '@' and a '.' after it, and that there is
+	// at least one char for each of the three sections
+	int n = addy.find('@');
+	if(n == -1 || n == 0)
+		return FALSE;
+	int d = addy.find('.', n+1);
+	if(d == -1 || d == 0)
+		return FALSE;
+	if((addy.length()-1) - d <= 0)
+		return FALSE;
+	if(addy.find("..") != -1)
+		return false;
+
+	return TRUE;
+}
+
+QString linkify(const QString &in)
+{
+	QString out = in;
+	int x1, x2;
+	bool isUrl, isEmail;
+	QString linked, link, href;
+
+	for(int n = 0; n < (int)out.length(); ++n) {
+		isUrl = FALSE;
+		isEmail = FALSE;
+		x1 = n;
+
+		if(linkify_pmatch(out, n, "http://")) {
+			n += 7;
+			isUrl = TRUE;
+			href = "";
+		}
+		else if(linkify_pmatch(out, n, "https://")) {
+			n += 8;
+			isUrl = TRUE;
+			href = "";
+		}
+		else if(linkify_pmatch(out, n, "ftp://")) {
+			n += 6;
+			isUrl = TRUE;
+			href = "";
+		}
+		else if(linkify_pmatch(out, n, "news://")) {
+			n += 7;
+			isUrl = TRUE;
+			href = "";
+		}
+		else if (linkify_pmatch(out, n, "ed2k://")) {
+			n += 7;
+			isUrl = TRUE;
+			href = "";
+		}
+		else if(linkify_pmatch(out, n, "www.")) {
+			isUrl = TRUE;
+			href = "http://";
+		}
+		else if(linkify_pmatch(out, n, "ftp.")) {
+			isUrl = TRUE;
+			href = "ftp://";
+		}
+		else if(linkify_pmatch(out, n, "@")) {
+			isEmail = TRUE;
+			href = "mailto:";
+		}
+
+		if(isUrl) {
+			// make sure the previous char is not alphanumeric
+			if(x1 > 0 && out.at(x1-1).isLetterOrNumber())
+				continue;
+
+			// find whitespace (or end)
+			for(x2 = n; x2 < (int)out.length(); ++x2) {
+				if(out.at(x2).isSpace() || out.at(x2) == '<')
+					break;
+			}
+			int len = x2-x1;
+			QString pre = resolveEntities(out.mid(x1, x2-x1));
+
+			// go backward hacking off unwanted punctuation
+			int cutoff;
+			for(cutoff = pre.length()-1; cutoff >= 0; --cutoff) {
+				if(!linkify_isOneOf(pre.at(cutoff), "!?,.()[]{}<>\""))
+					break;
+			}
+			++cutoff;
+			//++x2;
+
+			link = pre.mid(0, cutoff);
+			if(!linkify_okUrl(link)) {
+				n = x1 + link.length();
+				continue;
+			}
+			href += link;
+			href = linkify_htmlsafe(href);
+			//printf("link: [%s], href=[%s]\n", link.latin1(), href.latin1());
+			linked = QString("<a href=\"%1\">").arg(href) + expandEntities(link) + "</a>" + expandEntities(pre.mid(cutoff));
+			out.replace(x1, len, linked);
+			n = x1 + linked.length() - 1;
+		}
+		else if(isEmail) {
+			// go backward till we find the beginning
+			if(x1 == 0)
+				continue;
+			--x1;
+			for(; x1 >= 0; --x1) {
+				if(!linkify_isOneOf(out.at(x1), "_.-") && !out.at(x1).isLetterOrNumber())
+					break;
+			}
+			++x1;
+
+			// go forward till we find the end
+			x2 = n + 1;
+			for(; x2 < (int)out.length(); ++x2) {
+				if(!linkify_isOneOf(out.at(x2), "_.-") && !out.at(x2).isLetterOrNumber())
+					break;
+			}
+
+			int len = x2-x1;
+			link = out.mid(x1, len);
+			//link = resolveEntities(link);
+
+			if(!linkify_okEmail(link)) {
+				n = x1 + link.length();
+				continue;
+			}
+
+			href += link;
+			//printf("link: [%s], href=[%s]\n", link.latin1(), href.latin1());
+			linked = QString("<a href=\"%1\">").arg(href) + link + "</a>";
+			out.replace(x1, len, linked);
+			n = x1 + linked.length() - 1;
+		}
+	}
+
+	return out;
+}
+
+// sickening
+QString emoticonify(const QString &in)
+{
+	RTParse p(in);
+	while ( !p.atEnd() ) {
+		// returns us the first chunk as a plaintext string
+		QString str = p.next();
+
+		int i = 0;
+		while ( i >= 0 ) {
+			// find closest emoticon
+			int ePos = -1;
+			Icon *closest = 0;
+
+			int foundPos = -1, foundLen = -1;
+
+			QPtrListIterator<Iconset> iconsets(is->emoticons);
+			Iconset *iconset;
+    			while ( (iconset = iconsets.current()) != 0 ) {
+				QPtrListIterator<Icon> it = iconset->iterator();
+				for ( ; it.current(); ++it) {
+					Icon *icon = it.current();
+					if ( icon->regExp().isEmpty() )
+						continue;
+
+					// some hackery
+					int iii = i;
+					bool searchAgain;
+
+					do {
+						searchAgain = false;
+
+						// find the closest match
+						const QRegExp &rx = icon->regExp();
+						int n = rx.search(str, iii);
+						if ( n == -1 )
+							continue;
+
+						if(ePos == -1 || n < ePos || (rx.matchedLength() > foundLen && n < ePos + foundLen)) {
+							// there must be whitespace at least on one side of the emoticon
+							if ( ( n == 0 ) ||
+							     ( n+rx.matchedLength() == (int)str.length() ) ||
+							     ( n > 0 && str[n-1].isSpace() ) ||
+							     ( n+rx.matchedLength() < (int)str.length() && str[n+rx.matchedLength()].isSpace() ) )
+							{
+								ePos = n;
+								closest = icon;
+
+								foundPos = n;
+								foundLen = rx.matchedLength();
+								break;
+							}
+
+							searchAgain = true;
+						}
+
+						iii = n + rx.matchedLength();
+					} while ( searchAgain );
+				}
+
+				++iconsets;
+			}
+
+			QString s;
+			if(ePos == -1)
+				s = str.mid(i);
+			else
+				s = str.mid(i, ePos-i);
+			p.putPlain(s);
+
+			if ( !closest )
+				break;
+
+			p.putRich( QString("<icon name=\"%1\" text=\"%2\">").arg(closest->name()).arg(str.mid(foundPos, foundLen)) );
+			i = foundPos + foundLen;
+		}
+	}
+
+	QString out = p.output();
+
+	//enable *bold* stuff
+	// //old code
+	//out=out.replace(QRegExp("(^[^<>\\s]*|\\s[^<>\\s]*)\\*(\\S+)\\*([^<>\\s]*\\s|[^<>\\s]*$)"),"\\1<b>*\\2*</b>\\3");
+	//out=out.replace(QRegExp("(^[^<>\\s\\/]*|\\s[^<>\\s\\/]*)\\/([^\\/\\s]+)\\/([^<>\\s\\/]*\\s|[^<>\\s\\/]*$)"),"\\1<i>/\\2/</i>\\3");
+	//out=out.replace(QRegExp("(^[^<>\\s]*|\\s[^<>\\s]*)_(\\S+)_([^<>\\s]*\\s|[^<>\\s]*$)"),"\\1<u>_\\2_</u>\\3");
+
+	out=out.replace(QRegExp("(^_|\\s_)(\\S+)(_\\s|_$)"),"\\1<u>\\2</u>\\3");
+	out=out.replace(QRegExp("(^\\*|\\s\\*)(\\S+)(\\*\\s|\\*$)"),"\\1<b>\\2</b>\\3");
+	out=out.replace(QRegExp("(^\\/|\\s\\/)(\\S+)(\\/\\s|\\/$)"),"\\1<i>\\2</i>\\3");
+
+	return out;
+}
+
+QString encodePassword(const QString &pass, const QString &key)
+{
+	QString result;
+	unsigned int n1, n2;
+
+	if(key.length() == 0)
+		return pass;
+
+	for(n1 = 0, n2 = 0; n1 < pass.length(); ++n1) {
+		ushort x = pass.at(n1).unicode() ^ key.at(n2++).unicode();
+		QString hex;
+		hex.sprintf("%04x", x);
+		result += hex;
+		if(n2 >= key.length())
+			n2 = 0;
+	}
+	return result;
+}
+
+QString decodePassword(const QString &pass, const QString &key)
+{
+	QString result;
+	unsigned int n1, n2;
+
+	if(key.length() == 0)
+		return pass;
+
+	for(n1 = 0, n2 = 0; n1 < pass.length(); n1 += 4) {
+		ushort x = 0;
+		if(n1 + 4 > pass.length())
+			break;
+		x += hexChar2int(pass.at(n1))*4096;
+		x += hexChar2int(pass.at(n1+1))*256;
+		x += hexChar2int(pass.at(n1+2))*16;
+		x += hexChar2int(pass.at(n1+3));
+		QChar c(x ^ key.at(n2++).unicode());
+		result += c;
+		if(n2 >= key.length())
+			n2 = 0;
+	}
+	return result;
+}
+
+QString status2txt(int status)
+{
+	switch(status) {
+		case STATUS_OFFLINE:    return QObject::tr("Offline");
+		case STATUS_AWAY:       return QObject::tr("Away");
+		case STATUS_XA:         return QObject::tr("Not Available");
+		case STATUS_DND:        return QObject::tr("Do not Disturb");
+		case STATUS_CHAT:       return QObject::tr("Free for Chat");
+		case STATUS_INVISIBLE:  return QObject::tr("Invisible");
+
+		case STATUS_ONLINE:
+		default:                return QObject::tr("Online");
+	}
+}
+
+Icon category2icon(const QString &category, const QString &type)
+{
+	// TODO: update this to http://www.jabber.org/registrar/disco-categories.html#gateway
+
+	// still have to add more options...
+	if ( category == "service" || category == "gateway" ) {
+		QString trans;
+
+		if (type == "aim")
+			trans = "aim";
+		else if (type == "icq")
+			trans = "icq";
+		else if (type == "msn")
+			trans = "msn";
+		else if (type == "yahoo")
+			trans = "yahoo";
+		else if (type == "gadu-gadu" || type == "x-gadugadu")
+			trans = "gadugadu";
+		else if (type == "sms")
+			trans = "sms";
+		else
+			trans = "transport";
+
+		return is->transportStatus(trans, STATUS_ONLINE);
+
+		// irc
+		// jud
+		// pager
+		// jabber
+		// serverlist
+		// smtp
+	}
+	else if ( category == "conference" ) {
+		if (type == "public" || type == "private" || type == "text" || type == "irc")
+			return IconsetFactory::icon("psi/groupChat");
+		else if (type == "url")
+			return IconsetFactory::icon("psi/www");
+		// irc
+		// list
+		// topic
+	}
+	else if ( category == "validate" ) {
+		if (type == "xml")
+			return IconsetFactory::icon("psi/xml");
+		// grammar
+		// spell
+	}
+	else if ( category == "user" || category == "client" ) {
+		// client
+		// forward
+		// inbox
+		// portable
+		// voice
+		return is->status(STATUS_ONLINE);
+	}
+	// application
+	   // bot
+	   // calendar
+	   // editor
+	   // fileserver
+	   // game
+	   // whiteboard
+	// headline
+	   // logger
+	   // notice
+	   // rss
+	   // stock
+	// keyword
+	   // dictionary
+	   // dns
+	   // software
+	   // thesaurus
+	   // web
+	   // whois
+	// render
+	   // en2ru
+	   // ??2??
+	   // tts
+	return Icon();
+}
+
+int hexChar2int(char c)
+{
+	if(c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+	else if(c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	else if(c >= '0' && c <= '9')
+		return c - '0';
+
+	return 0;
+}
+
+char int2hexChar(int x)
+{
+	if(x < 10)
+		return (char)x + '0';
+	else
+		return (char)x - 10 + 'a';
+}
+
+QString jidEncode(const QString &jid)
+{
+	QString jid2;
+
+	for(unsigned int n = 0; n < jid.length(); ++n) {
+		if(jid.at(n) == '@') {
+			jid2.append("_at_");
+		}
+		else if(jid.at(n) == '.') {
+			jid2.append('.');
+		}
+		else if(!jid.at(n).isLetterOrNumber()) {
+			// hex encode
+			QString hex;
+			hex.sprintf("%%%02X", jid.at(n).latin1());
+			jid2.append(hex);
+		}
+		else {
+			jid2.append(jid.at(n));
+		}
+	}
+
+	return jid2;
+}
+
+QString jidDecode(const QString &jid)
+{
+	QString jid2;
+	int n;
+
+	for(n = 0; n < (int)jid.length(); ++n) {
+		if(jid.at(n) == '%' && (jid.length() - n - 1) >= 2) {
+			QString str = jid.mid(n+1,2);
+			bool ok;
+			char c = str.toInt(&ok, 16);
+			if(!ok)
+				continue;
+
+			QChar a(c);
+			jid2.append(a);
+			n += 2;
+		}
+		else {
+			jid2.append(jid.at(n));
+		}
+	}
+
+	// search for the _at_ backwards, just in case
+	for(n = (int)jid2.length(); n >= 3; --n) {
+		if(jid2.mid(n, 4) == "_at_") {
+			jid2.replace(n, 4, "@");
+			break;
+		}
+	}
+
+	return jid2;
+}
+
+QString jidnick(const QString &jid, const QString &nick)
+{
+	if(nick.isEmpty())
+		return jid;
+	else
+		return nick;
+}
+
+QString logencode(QString str)
+{
+        str.replace(QRegExp("\\\\"), "\\\\");   // backslash to double-backslash
+        str.replace(QRegExp("\\|"), "\\p");     // pipe to \p
+        str.replace(QRegExp("\n"), "\\n");      // newline to \n
+        return str;
+}
+
+QString logdecode(const QString &str)
+{
+        QString ret;
+
+        for(unsigned int n = 0; n < str.length(); ++n) {
+                if(str.at(n) == '\\') {
+                        ++n;
+                        if(n >= str.length())
+                                break;
+
+                        if(str.at(n) == 'n')
+                                ret.append('\n');
+                        if(str.at(n) == 'p')
+                                ret.append('|');
+                        if(str.at(n) == '\\')
+                                ret.append('\\');
+                }
+                else {
+                        ret.append(str.at(n));
+                }
+        }
+
+        return ret;
+}
+
+void qstringlistisort(QStringList &c)
+{
+	if ( c.count() <= 1 )
+		return;
+
+	QStringList::Iterator it;
+	uint size = c.count();
+
+	// first, make array that is easy (and quick) to manipulate
+	QString *heap = new QString[ size ];
+
+	int i = 0;
+	for (it = c.begin(); it != c.end(); ++it)
+		heap[i++] = *it;
+
+	// Insertion sort
+	for (uint tmp = 0; tmp < c.count(); tmp++) {
+		heap[tmp] = c[tmp];
+		size = tmp + 1;
+
+		for (uint j = 1; j < size; j++) {
+			QString k = heap[j].lower();
+			QString r = heap[j];
+
+			for (i = j - 1; i >= 0; i--) {
+				if ( QString::compare(k, heap[i].lower()) > 0 )
+					break;
+
+				heap[i+1] = heap[i];
+			}
+
+			heap[i+1] = r;
+		}
+	}
+
+	// now, copy sorted data back to QStringList
+	it = c.begin();
+	for (i = 0; i < (int)size; i++)
+		*it++ = heap[i];
+
+	delete[] heap;
+}
+
+void openURL(const QString &url)
+{
+	//fprintf(stderr, "openURL: [%s]\n", url.latin1());
+	bool useCustom = TRUE;
+
+	int colon = url.find(':');
+	if ( colon == -1 )
+		colon = 0;
+	QString service = url.left( colon );
+	if ( service == "jabber" || service == "jid" ) {
+		// TODO
+		return;
+	}
+
+#ifdef Q_WS_WIN
+	if(option.browser == 0)
+		useCustom = FALSE;
+#endif
+#ifdef Q_WS_PM
+        /// @todo (dmik) use HINI_USER_PROFILE\WPURLDEFAULTSETTINGS\DefaultBrowserExe
+#endif
+#ifdef Q_WS_X11
+	if(option.browser == 0 || option.browser == 2)
+		useCustom = FALSE;
+#endif
+#ifdef Q_WS_MAC
+	useCustom = FALSE;
+#endif
+
+	if(useCustom) {
+		bool isMail = FALSE;
+		QString s = url;
+		if(url.left(7) == "mailto:") {
+			s.remove(0, 7);
+			isMail = TRUE;
+		}
+
+		QStringList args;
+
+		if(isMail) {
+			if(option.customMailer.isEmpty()) {
+				QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. You have not selected a mailer (see Options)."));
+				return;
+			}
+			args += QStringList::split(' ', option.customMailer);
+		}
+		else {
+			if(option.customBrowser.isEmpty()) {
+				QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. You have not selected a browser (see Options)."));
+				return;
+			}
+			args += QStringList::split(' ', option.customBrowser);
+		}
+
+		args += s;
+		QProcess cmd(args);
+		if(!cmd.start()) {
+			QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that your custom browser/mailer exists (see Options)."));
+		}
+	}
+	else {
+#ifdef Q_WS_WIN
+		QCString cs = url.local8Bit();
+		if ((unsigned int)::ShellExecuteA(NULL,NULL,cs.data(),NULL,NULL,SW_SHOW) <= 32) {
+			QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have a web browser installed."));
+		}
+#endif
+#ifdef Q_WS_PM
+        /// @todo (dmik) use HINI_USER_PROFILE\WPURLDEFAULTSETTINGS\DefaultBrowserExe
+#endif
+#ifdef Q_WS_X11
+		// KDE
+		if(option.browser == 0) {
+			QStringList args;
+			args += "kfmclient";
+			args += "exec";
+			args += url;
+			QProcess cmd(args);
+			if(!cmd.start()) {
+				QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have KDE installed."));
+			}
+		}
+		// GNOME 2
+		else if(option.browser == 2) {
+			QStringList args;
+			args += "gnome-open";
+			args += url;
+			QProcess cmd(args);
+			if(!cmd.start()) {
+				QMessageBox::critical(0, CAP(QObject::tr("URL error")), QObject::tr("Unable to open the URL. Ensure that you have GNOME 2 installed."));
+			}
+		}
+#endif
+#ifdef Q_WS_MAC
+    // Use Internet Config to hand the URL to the appropriate application, as
+    // set by the user in the Internet Preferences pane.
+    // NOTE: ICStart could be called once at Psi startup, saving the
+    //       ICInstance in a global variable, as a minor optimization.
+    //       ICStop should then be called at Psi shutdown if ICStart succeeded.
+    ICInstance icInstance;
+    OSType psiSignature = 'psi ';
+    OSStatus error = ::ICStart( &icInstance, psiSignature );
+    if ( error == noErr ) {
+      ConstStr255Param hint( 0x0 );
+      QCString cs = url.local8Bit();
+      const char* data = cs.data();
+      long length = cs.length();
+      long start( 0 );
+      long end( length );
+      // Don't bother testing return value (error); launched application will report problems.
+      ::ICLaunchURL( icInstance, hint, data, length, &start, &end );
+      ICStop( icInstance );
+    }
+#endif
+	}
+}
+
+static bool sysinfo_done = FALSE;
+static int timezone_offset = 0;
+static QString timezone_str = "N/A";
+static QString os_str = "Unknown";
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC)
+#include<time.h>
+#include<stdlib.h>
+#include<string.h>
+#include<sys/utsname.h>
+#endif
+
+static void getSysInfo()
+{
+#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_PM)
+        /// @todo (dmik) test it
+	time_t x;
+	time(&x);
+	char str[256];
+	char fmt[32];
+	strcpy(fmt, "%z");
+	strftime(str, 256, fmt, localtime(&x));
+	if(strcmp(fmt, str)) {
+		QString s = str;
+		if(s.at(0) == '+')
+			s.remove(0,1);
+		s.truncate(s.length()-2);
+		timezone_offset = s.toInt();
+	}
+	strcpy(fmt, "%Z");
+	strftime(str, 256, fmt, localtime(&x));
+	if(strcmp(fmt, str))
+		timezone_str = str;
+#endif
+#if defined(Q_WS_X11)
+	struct utsname u;
+	uname(&u);
+	os_str.sprintf("%s", u.sysname);
+
+	// get description about os
+	enum LinuxName {
+		LinuxNone = 0,
+
+		LinuxMandrake,
+		LinuxDebian,
+		LinuxRedHat,
+		LinuxGentoo,
+		LinuxSlackware,
+		LinuxSuSE,
+		LinuxConectiva,
+		LinuxCaldera,
+		LinuxLFS,
+
+		LinuxASP, // Russian Linux distros
+		LinuxALT,
+
+		LinuxPLD, // Polish Linux distros
+		LinuxAurox,
+		LinuxArch
+	};
+
+	enum OsFlags {
+		OsUseName = 0,
+		OsUseFile,
+		OsAppendFile
+	};
+
+	struct OsInfo {
+		LinuxName id;
+		OsFlags flags;
+		QString file;
+		QString name;
+	} osInfo[] = {
+		{ LinuxMandrake,	OsUseFile,	"/etc/mandrake-release",	"Mandrake Linux"	},
+		{ LinuxDebian,		OsAppendFile,	"/etc/debian_version",		"Debian GNU/Linux"	},
+		{ LinuxGentoo,		OsUseFile,	"/etc/gentoo-release",		"Gentoo Linux"		},
+		{ LinuxSlackware,	OsAppendFile,	"/etc/slackware-version",	"Slackware Linux"	},
+		{ LinuxPLD,		OsUseFile,	"/etc/pld-release",		"PLD Linux"		},
+		{ LinuxAurox,		OsUseName,	"/etc/aurox-release",		"Aurox Linux"		},
+		{ LinuxArch,		OsUseFile,	"/etc/arch-release",		"Arch Linux"		},
+		{ LinuxLFS,		OsAppendFile,	"/etc/lfs-release",		"LFS Linux"		},
+
+		// untested
+		{ LinuxSuSE,		OsUseFile,	"/etc/SuSE-release",		"SuSE Linux"		},
+		{ LinuxConectiva,	OsUseFile,	"/etc/conectiva-release",	"Conectiva Linux"	},
+		{ LinuxCaldera,		OsUseFile,	"/etc/.installed",		"Caldera Linux"		},
+
+		// many distros use the /etc/redhat-release for compatibility, so RedHat will be the last :)
+		{ LinuxRedHat,		OsUseFile,	"/etc/redhat-release",		"RedHat Linux"		},
+
+		{ LinuxNone,		OsUseName,	"",				""			}
+	};
+
+	for (int i = 0; osInfo[i].id != LinuxNone; i++) {
+		QFileInfo fi( osInfo[i].file );
+		if ( fi.exists() ) {
+			QString desc;
+
+			QFile f( osInfo[i].file );
+			f.open( IO_ReadOnly );
+			f.readLine( desc, 128 );
+
+			desc = desc.stripWhiteSpace ();
+
+			switch ( osInfo[i].flags ) {
+				case OsUseFile:
+					os_str = desc;
+					break;
+				case OsUseName:
+					os_str = osInfo[i].name;
+					break;
+				case OsAppendFile:
+					os_str = osInfo[i].name + " (" + desc + ")";
+					break;
+			}
+
+			break;
+		}
+	}
+#elif defined(Q_WS_PM)
+	os_str = "OS/2";
+#elif defined(Q_WS_MAC)
+	os_str = "Mac OS X";
+#endif
+
+#if defined(Q_WS_WIN)
+	TIME_ZONE_INFORMATION i;
+	//GetTimeZoneInformation(&i);
+	//timezone_offset = (-i.Bias) / 60;
+	memset(&i, 0, sizeof(i));
+	bool inDST = (GetTimeZoneInformation(&i) == TIME_ZONE_ID_DAYLIGHT);
+	int bias = i.Bias;
+	if(inDST)
+		bias += i.DaylightBias;
+	timezone_offset = (-bias) / 60;
+	timezone_str = "";
+	for(int n = 0; n < 32; ++n) {
+		uint w = inDST ? i.DaylightName[n] : i.StandardName[n];
+		if(w == 0)
+			break;
+		timezone_str += QChar(w);
+	}
+
+	Qt::WindowsVersion v = QApplication::winVersion();
+	if(v == Qt::WV_95)
+		os_str = "Windows 95";
+	else if(v == Qt::WV_98)
+		os_str = "Windows 98";
+	else if(v == Qt::WV_Me)
+		os_str = "Windows Me";
+	else if(v == Qt::WV_DOS_based)
+		os_str = "Windows 9x/Me";
+	else if(v == Qt::WV_NT)
+		os_str = "Windows NT 4.x";
+	else if(v == Qt::WV_2000)
+		os_str = "Windows 2000";
+	else if(v == Qt::WV_XP)
+		os_str = "Windows XP";
+#if QT_VERSION >= 0x030300
+	else if(v == Qt::WV_2003)
+		os_str = "Windows Server 2003";
+#endif
+	else if(v == Qt::WV_NT_based)
+		os_str = "Windows NT";
+#endif
+	sysinfo_done = TRUE;
+}
+
+QString getOSName()
+{
+	if(!sysinfo_done)
+		getSysInfo();
+
+	return os_str;
+}
+
+int getTZOffset()
+{
+	if(!sysinfo_done)
+		getSysInfo();
+
+	return timezone_offset;
+}
+
+QString getTZString()
+{
+	if(!sysinfo_done)
+		getSysInfo();
+
+	return timezone_str;
+}
+
+
+#ifdef Q_WS_X11
+QString getResourcesDir()
+{
+	return PSI_DATADIR;
+}
+
+QString getHomeDir()
+{
+	QDir proghome(QDir::homeDirPath() + "/.psi");
+	if(!proghome.exists()) {
+		QDir home = QDir::home();
+		home.mkdir(".psi");
+		chmod(QFile::encodeName(proghome.path()), 0700);
+	}
+
+	return proghome.path();
+}
+#endif
+
+#ifdef Q_WS_WIN
+QString getResourcesDir()
+{
+#if QT_VERSION >= 0x030200
+	return qApp->applicationDirPath();
+#else
+	char baseName[MAX_PATH];
+	GetModuleFileNameA(GetModuleHandle(NULL), baseName, MAX_PATH);
+	QString base(baseName);
+	int idx = base.findRev('\\');
+
+	if (-1 == idx)
+		return ".";
+
+	base.truncate(idx);
+	QDir baseDir(base);
+
+	return baseDir.absPath();
+#endif
+}
+
+QString getHomeDir()
+{
+	QString base;
+
+	// Windows 9x
+	if(QDir::homeDirPath() == QDir::rootDirPath())
+		base = ".";
+	// Windows NT/2K/XP variant
+	else
+		base = QDir::homeDirPath();
+
+	// no trailing slash
+	if(base.at(base.length()-1) == '/')
+		base.truncate(base.length()-1);
+
+	QDir proghome(base + "/PsiData");
+	if(!proghome.exists()) {
+		QDir home(base);
+		home.mkdir("PsiData");
+	}
+
+	return proghome.path();
+}
+#endif
+
+#ifdef Q_WS_PM
+QString getResourcesDir()
+{
+#if QT_VERSION >= 0x030200
+	return qApp->applicationDirPath();
+#else
+#  error Not implemented!
+#endif
+}
+
+QString getHomeDir()
+{
+	QString base = QDir::homeDirPath();
+
+	// no trailing slash
+	if(base.at(base.length()-1) == '/')
+		base.truncate(base.length()-1);
+
+	QDir proghome(base + "/PsiData");
+	if(!proghome.exists()) {
+		QDir home(base);
+		home.mkdir("PsiData");
+	}
+
+	return proghome.path();
+}
+#endif
+
+#ifdef Q_WS_MAC
+/******************************************************************************/
+/* Get path to Resources directory as a string.                               */
+/* Return an empty string if can't find it.                                   */
+/******************************************************************************/
+QString getResourcesDir()
+{
+  // System routine locates resource files. We "know" that Psi.icns is
+  // in the Resources directory.
+  QString resourcePath;
+  CFBundleRef mainBundle = CFBundleGetMainBundle();
+  CFStringRef resourceCFStringRef
+      = CFStringCreateWithCString( NULL, "application.icns",
+                                   kCFStringEncodingASCII );
+  CFURLRef resourceURLRef = CFBundleCopyResourceURL( mainBundle,
+                                                     resourceCFStringRef,
+                                                     NULL,
+                                                     NULL );
+  if ( resourceURLRef ) {
+    CFStringRef resourcePathStringRef =
+    CFURLCopyFileSystemPath( resourceURLRef, kCFURLPOSIXPathStyle );
+    const char* resourcePathCString =
+      CFStringGetCStringPtr( resourcePathStringRef, kCFStringEncodingASCII );
+    if ( resourcePathCString ) {
+      resourcePath.setLatin1( resourcePathCString );
+    } else { // CFStringGetCStringPtr failed; use fallback conversion
+      CFIndex bufferLength = CFStringGetLength( resourcePathStringRef ) + 1;
+      char* resourcePathCString = new char[ bufferLength ];
+      Boolean conversionSuccess =
+        CFStringGetCString( resourcePathStringRef,
+                            resourcePathCString, bufferLength,
+                            kCFStringEncodingASCII );
+      if ( conversionSuccess ) {
+        resourcePath = resourcePathCString;
+      }
+      delete [] resourcePathCString;  // I own this
+    }
+    CFRelease( resourcePathStringRef ); // I own this
+  }
+  // Remove the tail component of the path
+  if ( ! resourcePath.isNull() ) {
+    QFileInfo fileInfo( resourcePath );
+    resourcePath = fileInfo.dirPath( true );
+  }
+  return resourcePath;
+}
+
+QString getHomeDir()
+{
+	QDir proghome(QDir::homeDirPath() + "/.psi");
+	if(!proghome.exists()) {
+		QDir home = QDir::home();
+		home.mkdir(".psi");
+		chmod(QFile::encodeName(proghome.path()), 0700);
+	}
+
+	return proghome.path();
+}
+#endif
+
+
+QString getHistoryDir()
+{
+	QDir history(pathToProfile(activeProfile) + "/history");
+	if (!history.exists()) {
+		QDir profile(pathToProfile(activeProfile));
+		profile.mkdir("history");
+	}
+
+	return history.path();
+}
+
+QString getVCardDir()
+{
+	QDir vcard(pathToProfile(activeProfile) + "/vcard");
+	if (!vcard.exists()) {
+		QDir profile(pathToProfile(activeProfile));
+		profile.mkdir("vcard");
+	}
+
+	return vcard.path();
+}
+
+bool fileCopy(const QString &src, const QString &dest)
+{
+	QFile in(src);
+	QFile out(dest);
+
+	if(!in.open(IO_ReadOnly))
+		return FALSE;
+	if(!out.open(IO_WriteOnly))
+		return FALSE;
+
+	char *dat = new char[16384];
+	int n = 0;
+	while(!in.atEnd()) {
+		n = in.readBlock(dat, 16384);
+		if(n == -1) {
+			delete dat;
+			return FALSE;
+		}
+		out.writeBlock(dat, n);
+	}
+	delete dat;
+
+	out.close();
+	in.close();
+
+	return TRUE;
+}
+
+
+void soundPlay(const QString &str)
+{
+	if(str == "!beep") {
+		QApplication::beep();
+		return;
+	}
+
+	if(!QFile::exists(str))
+		return;
+
+#if defined(Q_WS_WIN) || defined(Q_WS_PM) || defined(Q_WS_MAC)
+	QSound::play(str);
+#else
+	if(!option.player.isEmpty()) {
+		QStringList args;
+		args = QStringList::split(' ', option.player);
+		args += str;
+		QProcess cmd(args);
+		if(!cmd.start())
+			wait3(NULL,WNOHANG,NULL);
+	}
+#endif
+}
+
+XMPP::Status makeStatus(int x, const QString &str)
+{
+	XMPP::Status s;
+	s.setStatus(str);
+	if(x == STATUS_OFFLINE)
+		s.setIsAvailable(false);
+	else if(x == STATUS_INVISIBLE)
+		s.setIsInvisible(true);
+	else {
+		if(x == STATUS_AWAY)
+			s.setShow("away");
+		else if(x == STATUS_XA)
+			s.setShow("xa");
+		else if(x == STATUS_DND)
+			s.setShow("dnd");
+		else if(x == STATUS_CHAT)
+			s.setShow("chat");
+	}
+
+	return s;
+}
+
+int makeSTATUS(const XMPP::Status &s)
+{
+	int type = STATUS_ONLINE;
+	if(!s.isAvailable())
+		type = STATUS_OFFLINE;
+	else if(s.isInvisible())
+		type= STATUS_INVISIBLE;
+	else {
+		if(s.show() == "away")
+			type = STATUS_AWAY;
+		else if(s.show() == "xa")
+			type = STATUS_XA;
+		else if(s.show() == "dnd")
+			type = STATUS_DND;
+		else if(s.show() == "chat")
+			type = STATUS_CHAT;
+	}
+	return type;
+}
+
+#include<qlayout.h>
+QLayout *rw_recurseFindLayout(QLayout *lo, QWidget *w)
+{
+	//printf("scanning layout: %p\n", lo);
+	QLayoutIterator it = lo->iterator();
+	for(QLayoutItem *i; (i = it.current()); ++it) {
+		//printf("found: %p,%p\n", i->layout(), i->widget());
+		QLayout *slo = i->layout();
+		if(slo) {
+			QLayout *tlo = rw_recurseFindLayout(slo, w);
+			if(tlo)
+				return tlo;
+		}
+		else if(i->widget() == w)
+			return lo;
+	}
+	return 0;
+}
+
+QLayout *rw_findLayoutOf(QWidget *w)
+{
+	return rw_recurseFindLayout(w->parentWidget()->layout(), w);
+}
+
+void replaceWidget(QWidget *a, QWidget *b)
+{
+	if(!a)
+		return;
+
+	QLayout *lo = rw_findLayoutOf(a);
+	if(!lo)
+		return;
+	//printf("decided on this: %p\n", lo);
+
+	if(lo->inherits("QBoxLayout")) {
+		QBoxLayout *bo = (QBoxLayout *)lo;
+		int n = bo->findWidget(a);
+		bo->insertWidget(n+1, b);
+		delete a;
+	}
+}
+
+void closeDialogs(QWidget *w)
+{
+	// close qmessagebox?
+	const QObjectList *list = w->children();
+	if(list) {
+		QObjectListIt it(*list);
+		QPtrList<QDialog> dialogs;
+		for(QObject *o; (o = it.current()); ++it) {
+			if(o->inherits("QDialog"))
+				dialogs.append((QDialog *)o);
+		}
+		QPtrListIterator<QDialog> dit(dialogs);
+		for(QDialog *w; (w = dit.current()); ++dit)
+			w->close();
+	}
+}
+
+QString enc822jid(const QString &s)
+{
+	QString out;
+	for(int n = 0; n < (int)s.length(); ++n) {
+		if(s[n] == '\\' || s[n] == '<' || s[n] == '>') {
+			QString hex;
+			hex.sprintf("\\x%02X", (unsigned char )s[n]);
+			out.append(hex);
+		}
+		else
+			out += s[n];
+	}
+	return out;
+}
+
+QString dec822jid(const QString &s)
+{
+	QString out;
+	for(int n = 0; n < (int)s.length(); ++n) {
+		if(s[n] == '\\' && n + 3 < (int)s.length()) {
+			int x = n + 1;
+			n += 3;
+			if(s[x] != 'x')
+				continue;
+			ushort val = 0;
+			val += hexChar2int(s[x+1])*16;
+			val += hexChar2int(s[x+2]);
+			QChar c(val);
+			out += c;
+		}
+		else
+			out += s[n];
+	}
+	return out;
+}
+
+#ifdef Q_WS_X11
+#include<X11/Xlib.h>
+#include<X11/Xutil.h> // needed for WM_CLASS hinting
+
+void x11wmClass(Display *dsp, WId wid, QString resName)
+{
+	char app_name[] = "psi";
+
+	//Display *dsp = x11Display();			// get the display
+	//WId win = winId();				// get the window
+	XClassHint classhint;				// class hints
+	classhint.res_name = (char *)resName.latin1();	// res_name
+	classhint.res_class = app_name;			// res_class
+	XSetClassHint(dsp, wid, &classhint); 		// set the class hints
+}
+
+//>>>-- Nathaniel Gray -- Caltech Computer Science ------>
+//>>>-- Mojave Project -- http://mojave.cs.caltech.edu -->
+// Copied from http://www.nedit.org/archives/discuss/2002-Aug/0386.html
+
+// Helper function
+bool getCardinal32Prop(Display *display, Window win, char *propName, long *value)
+{
+	Atom nameAtom, typeAtom, actual_type_return;
+	int actual_format_return, result;
+	unsigned long nitems_return, bytes_after_return;
+	long *result_array=NULL;
+
+	nameAtom = XInternAtom(display, propName, False);
+	typeAtom = XInternAtom(display, "CARDINAL", False);
+	if (nameAtom == None || typeAtom == None) {
+		//qDebug("Atoms not interned!");
+		return false;
+	}
+
+
+	// Try to get the property
+	result = XGetWindowProperty(display, win, nameAtom, 0, 1, False,
+		typeAtom, &actual_type_return, &actual_format_return,
+		&nitems_return, &bytes_after_return,
+		(unsigned char **)&result_array);
+
+	if( result != Success ) {
+		//qDebug("not Success");
+		return false;
+	}
+	if( actual_type_return == None || actual_format_return == 0 ) {
+		//qDebug("Prop not found");
+		return false;
+	}
+	if( actual_type_return != typeAtom ) {
+		//qDebug("Wrong type atom");
+	}
+	*value = result_array[0];
+	XFree(result_array);
+	return true;
+}
+
+
+// Get the desktop number that a window is on
+bool desktopOfWindow(Window *window, long *desktop)
+{
+	Display *display = qt_xdisplay();
+	bool result = getCardinal32Prop(display, *window, (char *)"_NET_WM_DESKTOP", desktop);
+	//if( result )
+	//	qDebug("Desktop: " + QString::number(*desktop));
+	return result;
+}
+
+
+// Get the current desktop the WM is displaying
+bool currentDesktop(long *desktop)
+{
+	Window rootWin;
+	Display *display = qt_xdisplay();;
+	bool result;
+
+	rootWin = RootWindow(qt_xdisplay(), XDefaultScreen(qt_xdisplay()));
+	result = getCardinal32Prop(display, rootWin, (char *)"_NET_CURRENT_DESKTOP", desktop);
+	//if( result )
+	//	qDebug("Current Desktop: " + QString::number(*desktop));
+	return result;
+}
+#endif
+
+void bringToFront(QWidget *w, bool)
+{
+#ifdef Q_WS_X11
+	// If we're not on the current desktop, do the hide/show trick
+	long dsk, curr_dsk;
+	Window win = w->winId();
+	if(desktopOfWindow(&win, &dsk) && currentDesktop(&curr_dsk)) {
+		if(dsk != curr_dsk) {
+			w->hide();
+			//qApp->processEvents();
+		}
+	}
+
+	// FIXME: multi-desktop hacks for Win and Mac required
+#endif
+
+	w->show();
+	if(w->isMinimized()) {
+		//w->hide();
+		if(w->isMaximized())
+			w->showMaximized();
+		else
+			w->showNormal();
+	}
+	//if(grabFocus)
+	//	w->setActiveWindow();
+	w->raise();
+	w->setActiveWindow();
+}
+
+bool operator!=(const QMap<QString, QString> &m1, const QMap<QString, QString> &m2)
+{
+	if ( m1.size() != m2.size() )
+		return true;
+
+	QMap<QString, QString>::ConstIterator it = m1.begin(), it2;
+	for ( ; it != m1.end(); ++it) {
+		it2 = m2.find( it.key() );
+		if ( it2 == m2.end() )
+			return true;
+		if ( it.data() != it2.data() )
+			return true;
+	}
+
+	return false;
+}
+
Index: psi/tags/0.10-os2-b1/src/common.h
===================================================================
--- psi/tags/0.10-os2-b1/src/common.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/common.h	(revision 38)
@@ -0,0 +1,334 @@
+/*
+ * common.h - contains all the common variables and functions for Psi
+ * Copyright (C) 2001-2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include<qstring.h>
+#include<qpixmap.h>
+#include<qimage.h>
+#include<qstringlist.h>
+#include<qwidget.h>
+#include<qmessagebox.h>
+#include<qprocess.h>
+#include<qkeysequence.h>
+#include"im.h"
+//#include"xmpp_message.h"
+//#include"xmpp_client.h"
+//#include"xmpp_types.h"
+#include"varlist.h"
+#include"psiiconset.h"
+
+#define PROXY_NONE       0
+#define PROXY_HTTPS      1
+#define PROXY_SOCKS4     2
+#define PROXY_SOCKS5     3
+
+#define STATUS_OFFLINE   0
+#define STATUS_ONLINE    1
+#define STATUS_AWAY      2
+#define STATUS_XA        3
+#define STATUS_DND       4
+#define STATUS_INVISIBLE 5
+#define STATUS_CHAT      6
+
+#define STATUS_ASK	 100
+#define STATUS_NOAUTH	 101
+#define STATUS_ERROR	 102
+
+// global
+struct PsiGlobal
+{
+	QString pathBase, pathHome, pathProfiles;
+};
+
+extern QString activeProfile;
+
+//extern QStringList dtcp_hostList;
+//extern int dtcp_port;
+//extern QString dtcp_proxy;
+//extern bool link_test;
+extern bool use_gpg, no_gpg_agent;
+extern uint psi_dialog_flags;
+
+// options stuff
+enum { cOnline, cOffline, cAway, cDND, cProfileFore, cProfileBack, cGroupFore, cGroupBack, cListBack, cAnimFront, cAnimBack };
+enum { fRoster, fMessage, fChat, fPopup };
+enum { eMessage, eChat1, eChat2, eHeadline, eSystem, eOnline, eOffline, eSend, eIncomingFT, eFTComplete };
+enum { dcClose, dcHour, dcDay, dcNever };
+
+struct Options
+{
+	QColor color[11];
+	QString font[4];
+	int smallFontSize; // do not modify or save/load this value! it is calculated at run time!
+	int alertStyle;
+
+	QString systemIconset;
+	QStringList emoticons;
+	QString defaultRosterIconset;
+	QMap<QString, QString> serviceRosterIconset;
+	QMap<QString, QString> customRosterIconset;
+	int chatOpacity, rosterOpacity;
+
+	bool useleft, singleclick, hideMenubar, askOnline, askOffline, popupMsgs, popupChats, popupHeadlines, popupFiles, raise;
+	bool alwaysOnTop, noAwaySound, noAwayPopup, noUnlistedPopup, rosterAnim, autoVersion, autoVCardOnLogin, xmlConsoleOnLogin;
+	bool useDock, dockDCstyle, dockHideMW, dockToolMW, isWMDock;
+	bool smallChats, chatLineEdit, showJoins, useTabs, putTabsAtBottom, usePerTabCloseButton, autoRosterSize, autoRosterSizeGrowTop, autoResolveNicksOnAdd;
+
+	bool autoCopy; // although this setting is ignored under linux,
+	               // it is preserved in case user uses the same config file on different platforms
+
+	bool oldSmallChats; //Filthy hack, see chat code.
+	int delChats, browser;
+
+	// Avatars
+	bool avatarsEnabled, avatarsChatdlgEnabled;
+	int avatarsSize;
+
+	int defaultAction;
+	int incomingAs;
+	VarList sp; //status message presets
+	QStringList recentStatus; //recent status messages
+
+	int asAway, asXa, asOffline;
+	bool use_asAway, use_asXa, use_asOffline;
+	QString asMessage;
+	QString onevent[10];
+
+	// Added by Kiko 020621: points to the directory where the trusted
+	// certificates used in validating the server's certificate are kept
+	QString trustCertStoreDir;
+
+	QString player;
+	QString customBrowser, customMailer;
+
+	bool ignoreHeadline, ignoreNonRoster, excludeGroupChatsFromIgnore, scrollTo, keepSizes, useEmoticons, alertOpenChats;
+	bool raiseChatWindow, showSubjects, showCounter, chatSays, chatSoftReturn, showGroupCounts;
+
+	QSize sizeEventDlg, sizeChatDlg, sizeTabDlg;
+	bool jidComplete, grabUrls, noGCSound;
+
+	QString pgp;
+
+	struct ToolbarPrefs {
+		bool dirty;
+
+		QString name;
+		bool on;
+		bool locked;
+		bool stretchable;
+		QStringList keys;
+
+		Qt::Dock dock;
+		int index;
+		bool nl;
+		int extraOffset;
+	};
+	QMap< QString, QValueList<ToolbarPrefs> > toolbars;
+
+	// groupchat highlighting/nick colouring
+	bool gcHighlighting, gcNickColoring;
+	QStringList gcHighlights, gcNickColors;
+
+	bool clNewHeadings;
+	bool outlineHeadings;
+
+	// passive popups
+	bool ppIsOn;
+	bool ppMessage, ppHeadline, ppChat, ppOnline, ppStatus, ppOffline, ppFile;
+	int  ppJidClip, ppStatusClip, ppTextClip, ppHideTime;
+	QColor ppBorderColor;
+
+	// Bouncing of the dock (Mac OS X)
+	typedef enum { NoBounce, BounceOnce, BounceForever } BounceDockSetting;
+	BounceDockSetting bounceDock;
+
+	struct {
+		bool roster, options, profiles, services, accounts;
+	} lockdown;
+
+	bool useTransportIconsForContacts;
+
+	// roster sorting styles
+	typedef enum Roster_ContactSortStyle {
+		ContactSortStyle_Status = 0,
+		ContactSortStyle_Alpha
+	};
+	Roster_ContactSortStyle rosterContactSortStyle;
+
+	typedef enum Roster_GroupSortStyle {
+		GroupSortStyle_Alpha = 0,
+		GroupSortStyle_Rank
+	};
+	Roster_GroupSortStyle rosterGroupSortStyle;
+
+	typedef enum Roster_AccountSortStyle {
+		AccountSortStyle_Alpha = 0,
+		AccountSortStyle_Rank
+	};
+	Roster_AccountSortStyle rosterAccountSortStyle;
+
+	bool showTips;
+	int tipNum;
+
+	bool discoItems, discoInfo;
+
+	bool autoAuth, notifyAuth;
+
+	// event priority
+	enum { EventPriorityDontCare = -1 };
+	int eventPriorityHeadline;
+	int eventPriorityChat;
+	int eventPriorityMessage;
+	int eventPriorityAuth;
+	int eventPriorityFile;
+
+	// Message events
+	bool messageEvents;
+
+	int dtPort;
+	QString dtExternal;
+
+	// Last used path remembering
+	QString lastPath;
+	QString lastSavePath;
+
+	//background images
+	QString chatBgImage, rosterBgImage;
+
+	// Global accelerators
+	QValueList<QKeySequence> globalAccels;
+};
+
+// functions
+
+QString getResourcesDir();
+QString getHomeDir();
+
+QString getHistoryDir();
+QString getVCardDir();
+
+QString qstrlower(QString);
+int qstrcasecmp(const QString &str1, const QString &str2);
+int qstringlistmatch(QStringList &list, const QString &str);
+QString qstringlistlookup(QStringList &list, int x);
+QString CAP(const QString &str);
+
+QString status2txt(int status);
+
+QString eatptag(QString txt);
+QString qstrquote(const QString &, int width=60, bool quoteEmpty=FALSE);
+QString plain2rich(const QString &);
+QString rich2plain(const QString &);
+QString clipStatus(const QString &str, int width, int height);
+QString expandEntities(const QString &in);
+QString resolveEntities(const QString &);
+QString linkify(const QString &);
+QString emoticonify(const QString &in);
+
+QString encodePassword(const QString &, const QString &);
+QString decodePassword(const QString &, const QString &);
+
+Icon category2icon(const QString &category, const QString &type);
+
+void bringToFront(QWidget *w, bool grabFocus = true);
+QString jidEncode(const QString &jid);
+QString jidDecode(const QString &jid);
+
+QString jidnick(const QString &jid, const QString &nick);
+
+int hexChar2int(char c);
+char int2hexChar(int x);
+
+QString logencode(QString);
+QString logdecode(const QString &);
+
+bool operator!=(const QMap<QString, QString> &, const QMap<QString, QString> &);
+void qstringlistisort(QStringList &c);
+
+void openURL(const QString &);
+
+QString getOSName();
+int getTZOffset();
+QString getTZString();
+
+bool fileCopy(const QString &src, const QString &dest);
+
+#ifdef Q_WS_MAC
+void stopDockTileBounce();
+void bounceDockTile();
+#endif
+
+#ifdef Q_WS_X11
+void x11wmClass(Display *dsp, WId wid, QString resName);
+#define X11WM_CLASS(x)	x11wmClass(x11Display(), winId(), (x));
+#else
+#define X11WM_CLASS(x)	/* dummy */
+#endif
+
+void soundPlay(const QString &);
+
+XMPP::Status makeStatus(int, const QString &);
+int makeSTATUS(const XMPP::Status &);
+
+void replaceWidget(QWidget *, QWidget *);
+void closeDialogs(QWidget *);
+
+QString enc822jid(const QString &);
+QString dec822jid(const QString &);
+
+extern
+QString PROG_NAME, PROG_VERSION;
+
+extern char *pixdat_ft_back, *pixdat_ft_file, *pixdat_ft_folder;
+extern int pixlen_ft_back, pixlen_ft_file, pixlen_ft_folder;
+
+extern
+XMPP::Client *debug_jabber;
+
+extern
+Options option;
+
+extern
+PsiGlobal g;
+
+extern
+PsiIconset *is;
+
+extern
+bool useSound;
+
+// helper class for toolbars
+class StretchWidget: public QWidget
+{
+public:
+	StretchWidget(QWidget *parent)
+	: QWidget( parent )
+	{
+		setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+	}
+};
+
+typedef QValueList<XMPP::Jid> JidList;
+typedef QValueListIterator<XMPP::Jid> JidListIt;
+
+
+#endif
Index: psi/tags/0.10-os2-b1/src/contactview.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/contactview.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/contactview.cpp	(revision 38)
@@ -0,0 +1,3226 @@
+/*
+ * contactview.cpp - contact list widget
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"contactview.h"
+
+#include<qapplication.h>
+#include<qptrlist.h>
+#include<qheader.h>
+#include<qtimer.h>
+#include<qpainter.h>
+#include<qpopupmenu.h>
+#include<qmessagebox.h>
+#include<qinputdialog.h>
+#include<qiconset.h>
+#include<qdragobject.h>
+#include<qfiledialog.h>
+#include<qlayout.h>
+#include<stdlib.h>
+#include"im.h"
+#include"common.h"
+#include"userlist.h"
+#include"psiaccount.h"
+#include"psicon.h"
+#include"iconaction.h"
+#include"alerticon.h"
+#include"avatars.h"
+
+//----------------------------------------------------------------------------
+// ContactProfile
+//----------------------------------------------------------------------------
+class ContactProfile::Entry
+{
+public:
+	Entry()
+	{
+		alerting = false;
+		cvi.setAutoDelete(true);
+	}
+	~Entry()
+	{
+	}
+
+	UserListItem u;
+	QPtrList<ContactViewItem> cvi;
+	bool alerting;
+	Icon anim;
+};
+
+class ContactProfile::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private() {}
+
+	QString name;
+	ContactView *cv;
+	ContactViewItem *cvi;
+	ContactViewItem *self;
+	UserListItem su;
+	QPtrList<Entry> roster;
+	QPtrList<ContactViewItem> groups;
+	int oldstate;
+	QTimer *t;
+	PsiAccount *pa;
+	bool v_enabled;
+
+public slots:
+	/*
+	 * \brief This slot is toggled when number of active accounts is changed
+	 *
+	 * At the moment, it tries to recalculate the roster size.
+	 */
+	void numAccountsChanged()
+	{
+		cv->recalculateSize();
+	}
+};
+
+ContactProfile::ContactProfile(PsiAccount *pa, const QString &name, ContactView *cv, bool unique)
+{
+	d = new Private;
+	d->pa = pa;
+	d->v_enabled = d->pa->enabled();
+	d->name = name;
+	d->cv = cv;
+	d->cv->link(this);
+	d->t = new QTimer;
+	connect(d->t, SIGNAL(timeout()), SLOT(updateGroups()));
+	connect(pa->psi(), SIGNAL(accountCountChanged()), d, SLOT(numAccountsChanged()));
+
+	d->roster.setAutoDelete(true);
+
+	d->self = 0;
+
+	if(!unique)
+		d->cvi = new ContactViewItem(name, this, d->cv);
+	else
+		d->cvi = 0;
+
+	d->oldstate = -2;
+
+	deferredUpdateGroups();
+}
+
+ContactProfile::~ContactProfile()
+{
+	// delete the roster
+	clear();
+
+	// clean up
+	delete d->self;
+	delete d->cvi;
+
+	delete d->t;
+	d->cv->unlink(this);
+
+	delete d;
+}
+
+void ContactProfile::setEnabled(bool e)
+{
+	d->v_enabled = e;
+	if(d->v_enabled){
+		if(!d->cvi)
+			d->cvi = new ContactViewItem(d->name, this, d->cv);
+		addAllNeededContactItems();
+	}
+	else{
+		if(d->self)
+			removeSelf();
+
+		removeAllUnneededContactItems();
+		if(d->cvi)
+			delete d->cvi;
+		d->cvi = 0;
+		d->self = 0;
+	}
+}
+
+ContactView *ContactProfile::contactView() const
+{
+	return d->cv;
+}
+
+ContactViewItem *ContactProfile::self() const
+{
+	return d->self;
+}
+
+PsiAccount *ContactProfile::psiAccount() const
+{
+	return d->pa;
+}
+
+const QString & ContactProfile::name() const
+{
+	return d->name;
+}
+
+void ContactProfile::setName(const QString &name)
+{
+	d->name = name;
+	if(d->cvi)
+		d->cvi->setProfileName(name);
+}
+
+void ContactProfile::setName(const char *s)
+{
+	QObject::setName(s);
+}
+
+void ContactProfile::setState(int state)
+{
+	if(state == d->oldstate)
+		return;
+	d->oldstate = state;
+
+	if(d->cvi) {
+		d->cv->resetAnim();
+		d->cvi->setProfileState(state);
+	}
+}
+
+void ContactProfile::setUsingSSL(bool on)
+{
+	if(d->cvi)
+		d->cvi->setProfileSSL(on);
+}
+
+ContactViewItem *ContactProfile::addGroup(int type)
+{
+	ContactViewItem *item;
+
+	QString gname;
+	if(type == ContactViewItem::gGeneral)
+		gname = tr("General");
+	else if(type == ContactViewItem::gNotInList)
+		gname = tr("Not in list");
+	else if(type == ContactViewItem::gAgents)
+		gname = tr("Agents/Transports");
+	else if(type == ContactViewItem::gPrivate)
+		gname = tr("Private Messages");
+
+	if(d->cvi)
+		item = new ContactViewItem(gname, type, this, d->cvi);
+	else
+		item = new ContactViewItem(gname, type, this, d->cv);
+
+	if(type == ContactViewItem::gAgents && !d->cv->isShowAgents())
+		item->setVisible(false);
+
+	d->groups.append(item);
+
+	return item;
+}
+
+ContactViewItem *ContactProfile::addGroup(const QString &name)
+{
+	ContactViewItem *item;
+	if(d->cvi)
+		item = new ContactViewItem(name, ContactViewItem::gUser, this, d->cvi);
+	else
+		item = new ContactViewItem(name, ContactViewItem::gUser, this, d->cv);
+
+	d->groups.append(item);
+
+	return item;
+}
+
+// check for special group
+ContactViewItem *ContactProfile::checkGroup(int type)
+{
+	ContactViewItem *item;
+	if(d->cvi)
+		item = (ContactViewItem *)d->cvi->firstChild();
+	else
+		item = (ContactViewItem *)d->cv->firstChild();
+
+	for(; item; item = (ContactViewItem *)item->nextSibling()) {
+		if(item->type() == ContactViewItem::Group && item->groupType() == type)
+			return item;
+	}
+
+	return 0;
+}
+
+// make a tooltip with account information
+QString ContactProfile::makeTip(bool trim, bool doLinkify) const
+{
+	if (d->cvi)
+		return "<qt> <center> <b>" + d->cvi->text(0) + " " + d->cvi->groupInfo() + "</b> </center>" + d->su.makeBareTip(trim,doLinkify) + "</qt>";
+	else
+		return d->su.makeTip(trim,doLinkify);
+}
+
+// check for user group
+ContactViewItem *ContactProfile::checkGroup(const QString &name)
+{
+	ContactViewItem *item;
+	if(d->cvi)
+		item = (ContactViewItem *)d->cvi->firstChild();
+	else
+		item = (ContactViewItem *)d->cv->firstChild();
+
+	for(; item; item = (ContactViewItem *)item->nextSibling()) {
+		if(item->type() == ContactViewItem::Group && item->groupType() == ContactViewItem::gUser && item->groupName() == name)
+				return item;
+	}
+
+	return 0;
+}
+
+ContactViewItem *ContactProfile::ensureGroup(int type)
+{
+	ContactViewItem *group_item = checkGroup(type);
+	if(!group_item)
+		group_item = addGroup(type);
+
+	return group_item;
+}
+
+ContactViewItem *ContactProfile::ensureGroup(const QString &name)
+{
+	ContactViewItem *group_item = checkGroup(name);
+	if(!group_item)
+		group_item = addGroup(name);
+
+	return group_item;
+}
+
+void ContactProfile::checkDestroyGroup(const QString &group)
+{
+	ContactViewItem *group_item = checkGroup(group);
+	if(group_item)
+		checkDestroyGroup(group_item);
+}
+
+void ContactProfile::checkDestroyGroup(ContactViewItem *group)
+{
+	if(group->childCount() == 0) {
+		d->groups.remove(group);
+		delete group;
+	}
+}
+
+void ContactProfile::updateEntry(const UserListItem &u)
+{
+	if (u.isSelf()) {
+		// Update the self item
+		d->su = u;
+
+		// Show and/or update item if necessary
+		if (d->cv->isShowSelf() || d->su.userResourceList().count() > 1) {
+			if (d->self) {
+				updateSelf();
+			}
+			else {
+				addSelf();
+			}
+		}
+		else {
+			removeSelf();
+		}
+	}
+	else {
+		Entry *e = findEntry(u.jid());
+		if(!e) {
+			e = new Entry;
+			d->roster.append(e);
+			e->u = u;
+		}
+		else {
+			e->u = u;
+			removeUnneededContactItems(e);
+
+			// update remaining items
+			QPtrListIterator<ContactViewItem> it(e->cvi);
+			for(ContactViewItem *i; (i = it.current()); ++it) {
+				i->setContact(&e->u);
+				if(!u.isAvailable())
+					i->stopAnimateNick();
+			}
+		}
+
+		deferredUpdateGroups();
+		addNeededContactItems(e);
+	}
+}
+
+void ContactProfile::updateSelf()
+{
+	if (d->self) {
+		d->self->setContact(&d->su);
+		if(!d->su.isAvailable())
+			d->self->stopAnimateNick();
+	}
+}
+
+void ContactProfile::addSelf()
+{
+	if(!d->self) {
+		if(!d->cvi)
+			return;
+		d->self = new ContactViewItem(&d->su, this, d->cvi);
+	}
+}
+
+void ContactProfile::removeSelf()
+{
+	if (d->self) {
+		delete d->self;
+		d->self = 0;
+	}
+}
+
+ContactViewItem *ContactProfile::addContactItem(Entry *e, ContactViewItem *group_item)
+{
+	ContactViewItem *i = new ContactViewItem(&e->u, this, group_item);
+	e->cvi.append(i);
+	if(e->alerting)
+		i->setAlert(&e->anim);
+	deferredUpdateGroups();
+	//printf("ContactProfile: adding [%s] to group [%s]\n", e->u.jid().full().latin1(), group_item->groupName().latin1());
+	return i;
+}
+
+/*
+ * \brief Ensures that specified Entry is present in contactlist
+ *
+ * \param e - Entry with the necessary data about item
+ * \param group_item - ContactViewItem that will be the group for this item
+ */
+ContactViewItem *ContactProfile::ensureContactItem(Entry *e, ContactViewItem *group_item)
+{
+	d->cv->recalculateSize();
+
+	QPtrListIterator<ContactViewItem> it(e->cvi);
+	for(ContactViewItem *i; (i = it.current()); ++it) {
+		ContactViewItem *g = (ContactViewItem *)static_cast<QListViewItem *>(i)->parent();
+		if(g == group_item)
+			return i;
+	}
+	return addContactItem(e, group_item);
+}
+
+/*
+ * \brief Removes specified item from ContactView
+ *
+ * \param e - Entry with item's data
+ * \param i - ContactViewItem corresponding to the e
+ */
+void ContactProfile::removeContactItem(Entry *e, ContactViewItem *i)
+{
+	d->cv->recalculateSize();
+
+	ContactViewItem *group_item = (ContactViewItem *)static_cast<QListViewItem *>(i)->parent();
+	//printf("ContactProfile: removing [%s] from group [%s]\n", e->u.jid().full().latin1(), group_item->groupName().latin1());
+	e->cvi.removeRef(i);
+	deferredUpdateGroups();
+	checkDestroyGroup(group_item);
+}
+
+void ContactProfile::addNeededContactItems(Entry *e)
+{
+	if(!d->v_enabled)
+		return;
+
+	const UserListItem &u = e->u;
+
+	if(u.inList()) {
+		// don't add if we're not supposed to see it
+		if(u.isTransport()) {
+			if(!d->cv->isShowAgents())
+				return;
+		}
+		else {
+			if(!e->alerting) {
+				if((!d->cv->isShowOffline() && !u.isAvailable()) || (!d->cv->isShowAway() && u.isAway()) || (!d->cv->isShowHidden() && u.isHidden()))
+					return;
+			}
+		}
+	}
+
+	if(u.isPrivate())
+		ensureContactItem(e, ensureGroup(ContactViewItem::gPrivate));
+	else if(!u.inList())
+		ensureContactItem(e, ensureGroup(ContactViewItem::gNotInList));
+	else if(u.isTransport())
+		ensureContactItem(e, ensureGroup(ContactViewItem::gAgents));
+	else if(u.groups().isEmpty())
+		ensureContactItem(e, ensureGroup(ContactViewItem::gGeneral));
+	else {
+		const QStringList &groups = u.groups();
+		for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git)
+			ensureContactItem(e, ensureGroup(*git));
+	}
+}
+
+void ContactProfile::removeUnneededContactItems(Entry *e)
+{
+	const UserListItem &u = e->u;
+
+	if(u.inList()) {
+		bool delAll = !d->v_enabled;
+		if(u.isTransport()) {
+			if(!d->cv->isShowAgents())
+				delAll = true;
+		}
+		else {
+			if(!e->alerting) {
+				if((!d->cv->isShowOffline() && !u.isAvailable()) || (!d->cv->isShowAway() && u.isAway()) || (!d->cv->isShowHidden() && u.isHidden()))
+					delAll = true;
+			}
+		}
+		if(delAll) {
+			clearContactItems(e);
+			return;
+		}
+	}
+
+	QPtrListIterator<ContactViewItem> it(e->cvi);
+	for(ContactViewItem *i; (i = it.current());) {
+		bool del = false;
+		ContactViewItem *g = (ContactViewItem *)static_cast<QListViewItem *>(i)->parent();
+
+		if(g->groupType() == ContactViewItem::gNotInList && u.inList())
+			del = true;
+		else if(g->groupType() != ContactViewItem::gNotInList && g->groupType() != ContactViewItem::gPrivate && !u.inList())
+			del = true;
+		else if(g->groupType() == ContactViewItem::gAgents && !u.isTransport())
+			del = true;
+		else if(g->groupType() != ContactViewItem::gAgents && u.isTransport())
+			del = true;
+		else if(g->groupType() == ContactViewItem::gGeneral && !u.groups().isEmpty())
+			del = true;
+		else if(g->groupType() != ContactViewItem::gPrivate && g->groupType() != ContactViewItem::gGeneral && u.groups().isEmpty() && !u.isTransport() && u.inList())
+			del = true;
+		else if(g->groupType() == ContactViewItem::gUser) {
+			const QStringList &groups = u.groups();
+			if(!groups.isEmpty()) {
+				bool found = false;
+				for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) {
+					if(g->groupName() == *git) {
+						found = true;
+						break;
+					}
+				}
+				if(!found)
+					del = true;
+			}
+		}
+
+		if(del)
+			removeContactItem(e, i);
+		else
+			++it;
+	}
+}
+
+void ContactProfile::clearContactItems(Entry *e)
+{
+	QPtrListIterator<ContactViewItem> it(e->cvi);
+	for(ContactViewItem *i; (i = it.current());)
+		removeContactItem(e, i);
+}
+
+void ContactProfile::addAllNeededContactItems()
+{
+	QPtrListIterator<Entry> it(d->roster);
+	for(Entry *e; (e = it.current()); ++it)
+		addNeededContactItems(e);
+}
+
+void ContactProfile::removeAllUnneededContactItems()
+{
+	QPtrListIterator<Entry> it(d->roster);
+	for(Entry *e; (e = it.current()); ++it)
+		removeUnneededContactItems(e);
+}
+
+void ContactProfile::removeEntry(const Jid &j)
+{
+	Entry *e = findEntry(j);
+	if(e)
+		removeEntry(e);
+}
+
+void ContactProfile::removeEntry(Entry *e)
+{
+	e->alerting = false;
+	clearContactItems(e);
+	d->roster.remove(e);
+}
+
+void ContactProfile::setAlert(const Jid &j, const Icon *anim)
+{
+	if(d->su.jid().compare(j)) {
+		if(d->self)
+			d->self->setAlert(anim);
+	}
+	else {
+		Entry *e = findEntry(j);
+		if(!e)
+			return;
+
+		if(!d->cv->isShowAgents() && e->u.isTransport() && e->u.inList())
+			d->cv->setShowAgents(true);
+
+		e->alerting = true;
+		e->anim = *anim;
+		addNeededContactItems(e);
+		QPtrListIterator<ContactViewItem> it(e->cvi);
+		for(ContactViewItem *i; (i = it.current()); ++it)
+			i->setAlert(anim);
+
+		if(option.scrollTo)
+			ensureVisible(e);
+	}
+}
+
+void ContactProfile::clearAlert(const Jid &j)
+{
+	if(d->su.jid().compare(j)) {
+		if(d->self)
+			d->self->clearAlert();
+	}
+	else {
+		Entry *e = findEntry(j);
+		if(!e)
+			return;
+
+		e->alerting = false;
+		QPtrListIterator<ContactViewItem> it(e->cvi);
+		for(ContactViewItem *i; (i = it.current()); ++it)
+			i->clearAlert();
+		if(e->u.inList())
+			removeUnneededContactItems(e);
+	}
+}
+
+void ContactProfile::clear()
+{
+	QPtrListIterator<Entry> it(d->roster);
+	for(Entry *e; (e = it.current());)
+		removeEntry(e);
+}
+
+ContactProfile::Entry *ContactProfile::findEntry(const Jid &jid) const
+{
+	QPtrListIterator<Entry> it(d->roster);
+	for(Entry *e; (e = it.current()); ++it) {
+		if(e->u.jid().compare(jid))
+			return e;
+	}
+	return 0;
+}
+
+ContactProfile::Entry *ContactProfile::findEntry(ContactViewItem *i) const
+{
+	QPtrListIterator<Entry> it(d->roster);
+	for(Entry *e; (e = it.current()); ++it) {
+		QPtrListIterator<ContactViewItem> ci(e->cvi);
+		for(ContactViewItem *cvi; (cvi = ci.current()); ++ci) {
+			if(cvi == i)
+				return e;
+		}
+	}
+	return 0;
+}
+
+// return a list of contacts from a CVI group
+JidList ContactProfile::contactListFromCVGroup(ContactViewItem *group) const
+{
+	JidList list;
+
+	for(ContactViewItem *item = (ContactViewItem *)group->firstChild(); item ; item = (ContactViewItem *)item->nextSibling()) {
+		if(item->type() != ContactViewItem::Contact)
+			continue;
+
+		list.append(item->u()->jid());
+	}
+
+	return list;
+}
+
+// return the number of contacts from a CVI group
+int ContactProfile::contactSizeFromCVGroup(ContactViewItem *group) const
+{
+	int total = 0;
+
+	for(ContactViewItem *item = (ContactViewItem *)group->firstChild(); item ; item = (ContactViewItem *)item->nextSibling()) {
+		if(item->type() != ContactViewItem::Contact)
+			continue;
+
+		++total;
+	}
+
+	return total;
+}
+
+// return the number of contacts from a CVI group
+int ContactProfile::contactsOnlineFromCVGroup(ContactViewItem *group) const
+{
+	int total = 0;
+
+	for(ContactViewItem *item = (ContactViewItem *)group->firstChild(); item ; item = (ContactViewItem *)item->nextSibling()) {
+		if(item->type() == ContactViewItem::Contact && item->u()->isAvailable())
+			++total;
+	}
+
+	return total;
+}
+
+// return a list of contacts associated with "groupName"
+JidList ContactProfile::contactListFromGroup(const QString &groupName) const
+{
+	JidList list;
+
+	QPtrListIterator<Entry> it(d->roster);
+	for(Entry *e; (e = it.current()); ++it) {
+		const UserListItem &u = e->u;
+		if(u.isTransport())
+			continue;
+		const QStringList &g = u.groups();
+		if(g.isEmpty()) {
+			if(groupName.isEmpty())
+				list.append(u.jid());
+		}
+		else {
+			for(QStringList::ConstIterator git = g.begin(); git != g.end(); ++git) {
+				if(*git == groupName) {
+					list.append(u.jid());
+					break;
+				}
+			}
+		}
+	}
+
+	return list;
+}
+
+// return the number of contacts associated with "groupName"
+int ContactProfile::contactSizeFromGroup(const QString &groupName) const
+{
+	int total = 0;
+
+	QPtrListIterator<Entry> it(d->roster);
+	for(Entry *e; (e = it.current()); ++it) {
+		const UserListItem &u = e->u;
+		if(u.isTransport())
+			continue;
+		const QStringList &g = u.groups();
+		if(g.isEmpty()) {
+			if(groupName.isEmpty())
+				++total;
+		}
+		else {
+			for(QStringList::ConstIterator git = g.begin(); git != g.end(); ++git) {
+				if(*git == groupName) {
+					++total;
+					break;
+				}
+			}
+		}
+	}
+
+	return total;
+}
+
+void ContactProfile::updateGroupInfo(ContactViewItem *group)
+{
+	int type = group->groupType();
+	if(type == ContactViewItem::gGeneral || type == ContactViewItem::gAgents || type == ContactViewItem::gPrivate || type == ContactViewItem::gUser) {
+		int online = contactsOnlineFromCVGroup(group);
+		int total;
+		if(type == ContactViewItem::gGeneral || type == ContactViewItem::gUser) {
+			QString gname;
+			if(type == ContactViewItem::gUser)
+				gname = group->groupName();
+			else
+				gname = "";
+			total = contactSizeFromGroup(gname);
+		}
+		else {
+			total = group->childCount();
+		}
+		if (option.showGroupCounts)
+			group->setGroupInfo(QString("(%1/%2)").arg(online).arg(total));
+	}
+	else if (option.showGroupCounts) {
+		int inGroup = contactSizeFromCVGroup(group);
+		group->setGroupInfo(QString("(%1)").arg(inGroup));
+	}
+}
+
+QStringList ContactProfile::groupList() const
+{
+	QStringList groupList;
+
+	QPtrListIterator<Entry> it(d->roster);
+	for(Entry *e; (e = it.current()); ++it) {
+		const QStringList &groups = e->u.groups();
+		for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) {
+			if(qstringlistmatch(groupList, *git) == -1)
+				groupList.append(*git);
+		}
+	}
+
+	groupList.sort();
+	return groupList;
+}
+
+void ContactProfile::animateNick(const Jid &j)
+{
+	if(d->su.jid().compare(j)) {
+		if(d->self)
+			d->self->setAnimateNick();
+	}
+
+	Entry *e = findEntry(j);
+	if(!e)
+		return;
+	QPtrListIterator<ContactViewItem> it(e->cvi);
+	for(ContactViewItem *i; (i = it.current()); ++it)
+		i->setAnimateNick();
+}
+
+void ContactProfile::deferredUpdateGroups()
+{
+	d->t->start(250, true);
+}
+
+void ContactProfile::updateGroups()
+{
+	int totalOnline = 0;
+	{
+		QPtrListIterator<Entry> it(d->roster);
+		for(Entry *e; (e = it.current()); ++it) {
+			if(e->u.isAvailable())
+				++totalOnline;
+		}
+		if(d->cvi && option.showGroupCounts)
+			d->cvi->setGroupInfo(QString("(%1/%2)").arg(totalOnline).arg(d->roster.count()));
+	}
+
+	{
+		QPtrListIterator<ContactViewItem> it(d->groups);
+		for(ContactViewItem *g; (g = it.current()); ++it)
+			updateGroupInfo(g);
+	}
+}
+
+void ContactProfile::ensureVisible(const Jid &j)
+{
+	Entry *e = findEntry(j);
+	if(!e)
+		return;
+	ensureVisible(e);
+}
+
+void ContactProfile::ensureVisible(Entry *e)
+{
+	if(!e->alerting) {
+		if(!d->cv->isShowAgents() && e->u.isTransport())
+			d->cv->setShowAgents(true);
+		if(!d->cv->isShowOffline() && !e->u.isAvailable())
+			d->cv->setShowOffline(true);
+		if(!d->cv->isShowAway() && e->u.isAway())
+			d->cv->setShowAway(true);
+		if(!d->cv->isShowHidden() && e->u.isHidden())
+			d->cv->setShowHidden(true);
+	}
+
+	ContactViewItem *i = e->cvi.first();
+	if(!i)
+		return;
+	d->cv->ensureItemVisible(i);
+}
+
+void ContactProfile::doContextMenu(ContactViewItem *i, const QPoint &pos)
+{
+	bool online = d->pa->loggedIn();
+
+	if(i->type() == ContactViewItem::Profile) {
+		QPopupMenu pm;
+
+		QPopupMenu *am = new QPopupMenu(&pm);
+		am->insertItem(IconsetFactory::iconPixmap("psi/disco"), tr("Online Users"), 5);
+		am->insertItem(IconsetFactory::iconPixmap("psi/sendMessage"), tr("Send server message"), 1);
+		am->insertSeparator();
+		am->insertItem(/*IconsetFactory::iconPixmap("psi/edit"),*/ tr("Set MOTD"), 2);
+		am->insertItem(/*IconsetFactory::iconPixmap("psi/edit/clear"),*/ tr("Update MOTD"), 3);
+		am->insertItem(IconsetFactory::iconPixmap("psi/remove"), tr("Delete MOTD"), 4);
+
+		const int status_start = 16;
+		QPopupMenu *sm = new QPopupMenu(&pm);
+		sm->insertItem(is->status(STATUS_ONLINE),	status2txt(STATUS_ONLINE),	STATUS_ONLINE		+ status_start);
+		sm->insertItem(is->status(STATUS_CHAT),		status2txt(STATUS_CHAT),	STATUS_CHAT		+ status_start);
+		sm->insertSeparator();
+		sm->insertItem(is->status(STATUS_AWAY),		status2txt(STATUS_AWAY),	STATUS_AWAY		+ status_start);
+		sm->insertItem(is->status(STATUS_XA),		status2txt(STATUS_XA),		STATUS_XA		+ status_start);
+		sm->insertItem(is->status(STATUS_DND),		status2txt(STATUS_DND),		STATUS_DND		+ status_start);
+		sm->insertSeparator();
+		sm->insertItem(is->status(STATUS_INVISIBLE),	status2txt(STATUS_INVISIBLE),	STATUS_INVISIBLE	+ status_start);
+		sm->insertSeparator();
+		sm->insertItem(is->status(STATUS_OFFLINE),	status2txt(STATUS_OFFLINE),	STATUS_OFFLINE		+ status_start);
+		pm.insertItem(tr("&Status"), sm);
+
+		pm.insertSeparator();
+		pm.insertItem(IconsetFactory::iconPixmap("psi/addContact"), tr("&Add a contact"), 7);
+		pm.insertItem(IconsetFactory::iconPixmap("psi/disco"), tr("Service &Discovery"), 9);
+		pm.insertItem(IconsetFactory::iconPixmap("psi/sendMessage"), tr("New &blank message"), 6);
+		pm.insertSeparator();
+		pm.insertItem(IconsetFactory::iconPixmap("psi/xml"), tr("&XML Console"), 10);
+		pm.insertSeparator();
+		pm.insertItem(IconsetFactory::iconPixmap("psi/account"), tr("&Modify Account..."), 0);
+		pm.insertSeparator();
+		pm.insertItem(tr("&Admin"), am);
+
+		int x = pm.exec(pos);
+
+		if(x == -1)
+			return;
+
+		if(x == 0)
+			d->pa->modify();
+		else if(x == 1) {
+			Jid j = d->pa->jid().host() + '/' + "announce/online";
+			actionSendMessage(j);
+		}
+		else if(x == 2) {
+			Jid j = d->pa->jid().host() + '/' + "announce/motd";
+			actionSendMessage(j);
+		}
+		else if(x == 3) {
+			Jid j = d->pa->jid().host() + '/' + "announce/motd/update";
+			actionSendMessage(j);
+		}
+		else if(x == 4) {
+			Jid j = d->pa->jid().host() + '/' + "announce/motd/delete";
+			Message m;
+			m.setTo(j);
+			d->pa->dj_sendMessage(m, false);
+		}
+		else if(x == 5) {
+			// FIXME: will it still work on XMPP servers?
+			Jid j = d->pa->jid().host() + '/' + "admin";
+			actionDisco(j, "");
+		}
+		else if(x == 6) {
+			actionSendMessage("");
+		}
+		else if(x == 7) {
+			d->pa->openAddUserDlg();
+		}
+		else if(x == 9) {
+			Jid j = d->pa->jid().host();
+			actionDisco(j, "");
+		}
+		else if(x == 10) {
+			d->pa->showXmlConsole();
+		}
+		else if(x >= status_start) {
+			int status = x - status_start;
+			d->pa->changeStatus(status);
+		}
+	}
+	else if(i->type() == ContactViewItem::Group) {
+		QString gname = i->groupName();
+		QPopupMenu pm;
+
+		pm.insertItem(IconsetFactory::iconPixmap("psi/sendMessage"), tr("Send message to group"), 0);
+		if(!option.lockdown.roster) {
+			// disable if it's not a user group
+			if(!online || i->groupType() != ContactViewItem::gUser || gname == ContactView::tr("Hidden")) {
+				d->cv->qa_ren->setEnabled(false);
+				pm.setItemEnabled(2, false);
+				pm.setItemEnabled(3, false);
+			}
+			else
+				d->cv->qa_ren->setEnabled(true);
+
+			d->cv->qa_ren->addTo(&pm);
+			pm.insertSeparator();
+			pm.insertItem(IconsetFactory::iconPixmap("psi/remove"), tr("Remove group"), 2);
+			pm.insertItem(IconsetFactory::iconPixmap("psi/remove"), tr("Remove group and contacts"), 3);
+		}
+
+		if(i->groupType() == ContactViewItem::gAgents) {
+			pm.insertSeparator();
+			pm.insertItem(tr("Hide"), 4);
+		}
+
+		int x = pm.exec(pos);
+
+		// restore actions
+		if(option.lockdown.roster)
+			d->cv->qa_ren->setEnabled(false);
+		else
+			d->cv->qa_ren->setEnabled(true);
+
+		if(x == -1)
+			return;
+
+		if(x == 0) {
+			JidList list = contactListFromCVGroup(i);
+
+			// send multi
+			actionSendMessage(list);
+		}
+		else if(x == 2 && online) {
+			int n = QMessageBox::information(d->cv, tr("Remove Group"),tr(
+			"This will cause all contacts in this group to be disassociated with it.\n"
+			"\n"
+			"Proceed?"), tr("&Yes"), tr("&No"));
+
+			if(n == 0) {
+				JidList list = contactListFromGroup(i->groupName());
+				for(QValueList<Jid>::Iterator it = list.begin(); it != list.end(); ++it)
+					actionGroupRemove(*it, gname);
+			}
+		}
+		else if(x == 3 && online) {
+			int n = QMessageBox::information(d->cv, tr("Remove Group and Contacts"),tr(
+			"WARNING!  This will remove all contacts associated with this group!\n"
+			"\n"
+			"Proceed?"), tr("&Yes"), tr("&No"));
+
+			if(n == 0) {
+				JidList list = contactListFromGroup(i->groupName());
+				for(QValueList<Jid>::Iterator it = list.begin(); it != list.end(); ++it) {
+					removeEntry(*it);
+					actionRemove(*it);
+				}
+			}
+		}
+		else if(x == 4) {
+			if(i->groupType() == ContactViewItem::gAgents)
+				d->cv->setShowAgents(false);
+		}
+	}
+	else if(i->type() == ContactViewItem::Contact) {
+		bool self = false;
+		UserListItem *u;
+		Entry *e = 0;
+		if(i == d->self) {
+			self = true;
+			u = &d->su;
+		}
+		else {
+			e = findEntry(i);
+			if(!e)
+				return;
+			u = &e->u;
+		}
+
+		QStringList gl = groupList();
+		qstringlistisort(gl); // caseless sort
+
+		bool inList = e ? e->u.inList() : false;
+		bool isPrivate = e ? e->u.isPrivate(): false;
+		bool isAgent = e ? e->u.isTransport() : false;
+		bool avail = e ? e->u.isAvailable() : false;
+		QString groupNameCache = ((ContactViewItem *)static_cast<QListViewItem *>(i)->parent())->groupName();
+
+		QPopupMenu pm;
+
+		if(!self && !inList && !isPrivate && !option.lockdown.roster) {
+			pm.insertItem(IconsetFactory::iconPixmap("psi/addContact"), tr("Add/Authorize to contact list"), 10);
+			if(!online)
+				pm.setItemEnabled(10, false);
+			pm.insertSeparator();
+		}
+
+		if ( (self  && i->isAlerting()) ||
+		     (!self && e->alerting) ) {
+			d->cv->qa_recv->addTo(&pm);
+			pm.insertSeparator();
+		}
+
+		d->cv->qa_send->addTo(&pm);
+
+		//pm.insertItem(QIconSet(is->url), tr("Send &URL"), 2);
+
+		const UserResourceList &rl = u->userResourceList();
+
+		int base_sendto = 32;
+		int at_sendto = 0;
+		QPopupMenu *s2m = new QPopupMenu(&pm);
+		QPopupMenu *c2m = new QPopupMenu(&pm);
+
+		/*if ( rl.isEmpty() ) {
+			d->cv->qa_send->addTo(&pm);
+			d->cv->qa_chat->addTo(&pm);
+		}
+		else {
+			d->cv->qa_send->addTo(s2m);
+			s2m->insertSeparator();
+
+			d->cv->qa_chat->addTo(c2m);
+			c2m->insertSeparator();
+
+			pm.insertItem(is->send.pixmap(), tr("Send &message"), s2m, 17);
+			pm.insertItem(is->chat.base().pixmap(), tr("Open &chat window"), c2m, 18);
+		}*/
+
+		//s2m->insertItem(tr("Recipient Default"), base_sendto+at_sendto++);
+		//c2m->insertItem(tr("Recipient Default"), base_sendto+at_sendto++);
+		if(!rl.isEmpty()) {
+			//s2m->insertSeparator();
+			//c2m->insertSeparator();
+			for(UserResourceList::ConstIterator it = rl.begin(); it != rl.end(); ++it) {
+				const UserResource &r = *it;
+				QString rname;
+				if(r.name().isEmpty())
+					rname = tr("[blank]");
+				else
+					rname = r.name();
+				s2m->insertItem(is->status(r.status()), rname, base_sendto+at_sendto++);
+				c2m->insertItem(is->status(r.status()), rname, base_sendto+at_sendto++);
+			}
+		}
+
+		if(!isPrivate)
+			pm.insertItem(tr("Send message to"), s2m, 17);
+
+		d->cv->qa_chat->setIconSet(IconsetFactory::iconPixmap("psi/start-chat"));
+		d->cv->qa_chat->addTo(&pm);
+
+		if(!isPrivate)
+			pm.insertItem(tr("Open chat to"), c2m, 18);
+
+		if(!isPrivate) {
+			if(rl.isEmpty()) {
+				pm.setItemEnabled(17, false);
+				pm.setItemEnabled(18, false);
+			}
+		}
+
+		int base_hidden = base_sendto + at_sendto;
+		int at_hidden = 0;
+		QStringList hc;
+		if(!isPrivate) {
+			hc = d->pa->hiddenChats(u->jid());
+			QPopupMenu *cm = new QPopupMenu(&pm);
+			for(QStringList::ConstIterator it = hc.begin(); it != hc.end(); ++it) {
+				QString rname;
+				if((*it).isEmpty())
+					rname = tr("[blank]");
+				else
+					rname = *it;
+
+				// determine status
+				int status;
+				const UserResourceList &rl = u->userResourceList();
+				UserResourceList::ConstIterator uit = rl.find(*it);
+				if(uit != rl.end() || (uit = rl.priority()) != rl.end())
+					status = makeSTATUS((*uit).status());
+				else
+					status = STATUS_OFFLINE;
+				cm->insertItem(is->status(status), rname, base_hidden+at_hidden++);
+			}
+			pm.insertItem(tr("Active chats"), cm, 7);
+			if(hc.isEmpty())
+				pm.setItemEnabled(7, false);
+		}
+
+		if(!isAgent) {
+			pm.insertSeparator();
+			pm.insertItem(IconsetFactory::iconPixmap("psi/upload"), tr("Send &file"), 23);
+			if(!online)
+				pm.setItemEnabled(23, false);
+		}
+
+		// invites
+		int base_gc = base_hidden + at_hidden;
+		int at_gc = 0;
+		QStringList groupchats;
+		if(!isPrivate && !isAgent) {
+			QPopupMenu *gm = new QPopupMenu(&pm);
+			groupchats = d->pa->groupchats();
+			for(QStringList::ConstIterator it = groupchats.begin(); it != groupchats.end(); ++it) {
+				int id = gm->insertItem(*it, base_gc+at_gc++);
+				if(!online)
+					gm->setItemEnabled(id, false);
+			}
+			pm.insertItem(IconsetFactory::iconPixmap("psi/groupChat"), tr("Invite to"), gm, 14);
+			if(groupchats.isEmpty())
+				pm.setItemEnabled(14, false);
+		}
+
+		// weird?
+		if(inList || !isAgent)
+			pm.insertSeparator();
+
+		int base_group = base_gc + at_gc;
+
+		if(!self) {
+			if(inList) {
+				if(!option.lockdown.roster) {
+					d->cv->qa_ren->setEnabled(online);
+					d->cv->qa_ren->addTo(&pm);
+				}
+			}
+
+			if(!isAgent) {
+				if(inList && !option.lockdown.roster) {
+					QPopupMenu *gm = new QPopupMenu(&pm);
+
+					gm->setCheckable(true);
+					gm->insertItem(tr("&None"), 8);
+					gm->insertSeparator();
+
+					QString g;
+					if(e->u.groups().isEmpty())
+						gm->setItemChecked(8, true);
+					else
+						g = groupNameCache;
+
+					int n = 0;
+					gl.remove(ContactView::tr("Hidden"));
+					for(QStringList::ConstIterator it = gl.begin(); it != gl.end(); ++it) {
+						QString str;
+						if(n < 9)
+							str = "&";
+						str += QString("%1. %2").arg(n+1).arg(*it);
+						gm->insertItem(str, n+base_group);
+
+						if(*it == g)
+							gm->setItemChecked(n+base_group, true);
+						++n;
+					}
+					if(n > 0)
+						gm->insertSeparator();
+
+					gm->insertItem(ContactView::tr("Hidden"),n+base_group);
+					if(g == ContactView::tr("Hidden"))
+						gm->setItemChecked(n+base_group, true);
+					gm->insertSeparator();
+					gm->insertItem(/*IconsetFactory::iconPixmap("psi/edit/clear"),*/ tr("&Create new..."), 9);
+					pm.insertItem(tr("&Group"), gm, 5);
+
+					if(!online)
+						pm.setItemEnabled(5, false);
+				}
+			}
+			else {
+				pm.insertSeparator();
+
+				d->cv->qa_logon->setEnabled( !avail && online );
+
+				d->cv->qa_logon->setIconSet(is->status(e->u.jid(), STATUS_ONLINE));
+				d->cv->qa_logon->addTo(&pm);
+
+				pm.insertItem(is->status(e->u.jid(), STATUS_OFFLINE), tr("Log off"), 16);
+				if(!avail || !online)
+					pm.setItemEnabled(16, false);
+				pm.insertSeparator();
+			}
+		}
+
+		if(inList && !option.lockdown.roster) {
+			QPopupMenu *authm = new QPopupMenu (&pm);
+
+			authm->insertItem(tr("Resend authorization to"), 6);
+			authm->insertItem(tr("Rerequest authorization from"), 11);
+			authm->insertItem(/*IconsetFactory::iconPixmap("psi/edit/delete"),*/ tr("Remove authorization from"), 15);
+
+			pm.insertItem (IconsetFactory::iconPixmap("psi/register"), tr("Authorization"), authm, 20);
+			if(!online)
+				pm.setItemEnabled(20, false);
+		}
+
+		if(!self) {
+			if(!option.lockdown.roster) {
+				if(online || !inList)
+					d->cv->qa_rem->setEnabled(true);
+				else
+					d->cv->qa_rem->setEnabled(false);
+
+				d->cv->qa_rem->addTo(&pm);
+			}
+			pm.insertSeparator();
+		}
+
+		// Avatars
+#ifdef AVATARS
+		QPopupMenu *avpm = new QPopupMenu(&pm);
+		d->cv->qa_assignAvatar->addTo(avpm);
+		d->cv->qa_clearAvatar->setEnabled(d->pa->avatarFactory()->hasManualAvatar(u->jid()));
+		d->cv->qa_clearAvatar->addTo(avpm);
+		pm.insertItem(tr("&Avatar"), avpm);
+		avpm->setEnabled(option.avatarsEnabled);
+#endif
+
+		if(d->pa->psi()->pgp()) {
+			if(u->publicKeyID().isEmpty())
+				pm.insertItem(IconsetFactory::iconPixmap("psi/gpg-yes"), tr("Assign Open&PGP key"), 21);
+			else
+				pm.insertItem(IconsetFactory::iconPixmap("psi/gpg-no"), tr("Unassign Open&PGP key"), 22);
+		}
+
+		d->cv->qa_vcard->addTo( &pm );
+
+		if(!isPrivate) {
+			d->cv->qa_hist->addTo(&pm);
+		}
+
+		//if(link_test) {
+		//	pm.insertSeparator();
+		//	pm.insertItem(tr("Link Test"), 13);
+		//}
+
+		QString name = u->jid().full();
+		QString show = jidnick(u->jid().full(), u->name());
+		if(name != show)
+			name += QString(" (%1)").arg(u->name());
+
+		int x = pm.exec(pos);
+
+		// restore actions
+		if(option.lockdown.roster) {
+			d->cv->qa_ren->setEnabled(false);
+			d->cv->qa_rem->setEnabled(false);
+		}
+		else {
+			d->cv->qa_ren->setEnabled(true);
+			d->cv->qa_rem->setEnabled(true);
+		}
+
+		if(x == -1)
+			return;
+
+		if(x == 0) {
+			actionRecvEvent(u->jid());
+		}
+		else if(x == 1) {
+			actionSendMessage(u->jid());
+		}
+		else if(x == 2) {
+			actionSendUrl(u->jid());
+		}
+		else if(x == 6) {
+			if(online) {
+				actionAuth(u->jid());
+				QMessageBox::information(d->cv, tr("Authorize"),
+				tr("Sent authorization to <b>%1</b>.").arg(name));
+			}
+		}
+		else if(x == 8) {
+			if(online) {
+				// if we have groups, but we are setting to 'none', then remove that particular group
+				if(!u->groups().isEmpty()) {
+					QString gname = groupNameCache;
+					actionGroupRemove(u->jid(), gname);
+				}
+			}
+		}
+		else if(x == 9) {
+			if(online) {
+				while(1) {
+					bool ok = false;
+					QString newgroup = QInputDialog::getText(tr("Create New Group"), tr("Enter the new Group name:"), QLineEdit::Normal, QString::null, &ok, d->cv);
+					if(!ok)
+						break;
+					if(newgroup.isEmpty())
+						continue;
+
+					// make sure we don't have it already
+					bool found = false;
+					const QStringList &groups = u->groups();
+					for(QStringList::ConstIterator it = groups.begin(); it != groups.end(); ++it) {
+						if(*it == newgroup) {
+							found = true;
+							break;
+						}
+					}
+					if(!found) {
+						QString gname = groupNameCache;
+						actionGroupRemove(u->jid(), gname);
+						actionGroupAdd(u->jid(), newgroup);
+						break;
+					}
+				}
+			}
+		}
+		else if(x == 10) {
+			if(online) {
+				actionAdd(u->jid());
+				actionAuth(u->jid());
+
+				QMessageBox::information(d->cv, tr("Add"),
+				tr("Added/Authorized <b>%1</b> to the contact list.").arg(name));
+			}
+		}
+		else if(x == 11) {
+			if(online) {
+				actionAuthRequest(u->jid());
+				QMessageBox::information(d->cv, tr("Authorize"),
+				tr("Rerequested authorization from <b>%1</b>.").arg(name));
+			}
+		}
+		else if(x == 13) {
+			actionTest(u->jid());
+		}
+		else if(x == 15) {
+			if(online) {
+				int n = QMessageBox::information(d->cv, tr("Remove"),
+				tr("Are you sure you want to remove authorization from <b>%1</b>?").arg(name),
+				tr("&Yes"), tr("&No"));
+
+				if(n == 0)
+					actionAuthRemove(u->jid());
+			}
+		}
+		else if(x == 16) {
+			if(online) {
+				Status s=makeStatus(STATUS_OFFLINE,"");
+				actionAgentSetStatus(u->jid(), s);
+			}
+		}
+		else if(x == 21) {
+			actionAssignKey(u->jid());
+		}
+		else if(x == 22) {
+			actionUnassignKey(u->jid());
+		}
+		else if(x == 23) {
+			if(online)
+				actionSendFile(u->jid());
+		}
+		else if(x >= base_sendto && x < base_hidden) {
+			int n = x - base_sendto;
+			int res = n / 2;
+			int type = n % 2;
+			QString rname = "";
+			//if(res > 0) {
+				const UserResource &r = rl[res];
+				rname = r.name();
+			//}
+			QString s = u->jid().userHost();
+			if(!rname.isEmpty()) {
+				s += '/';
+				s += rname;
+			}
+			Jid j(s);
+
+			if(type == 0)
+				actionSendMessage(j);
+			else if(type == 1)
+				actionOpenChatSpecific(j);
+		}
+		else if(x >= base_hidden && x < base_gc) {
+			int n = 0;
+			int n2 = x - base_hidden;
+
+			QString rname;
+			for(QStringList::ConstIterator it = hc.begin(); it != hc.end(); ++it) {
+				if(n == n2) {
+					rname = *it;
+					break;
+				}
+				++n;
+			}
+
+			QString s = u->jid().userHost();
+			if(!rname.isEmpty()) {
+				s += '/';
+				s += rname;
+			}
+			Jid j(s);
+
+			actionOpenChatSpecific(j);
+		}
+		else if(x >= base_gc && x < base_group) {
+			if(online) {
+				QString gc = groupchats[x - base_gc];
+				actionInvite(u->jid(), gc);
+
+				QMessageBox::information(d->cv, tr("Invitation"),
+				tr("Sent groupchat invitation to <b>%1</b>.").arg(name));
+			}
+		}
+		else if(x >= base_group) {
+			if(online) {
+				int n = 0;
+				int n2 = x - base_group;
+
+				QString newgroup;
+				for(QStringList::Iterator it = gl.begin(); it != gl.end(); ++it) {
+					if(n == n2) {
+						newgroup = *it;
+						break;
+					}
+					++n;
+				}
+
+				if(newgroup.isEmpty())
+					newgroup = ContactView::tr("Hidden");
+
+				if(n == n2) {
+					// remove the group of this cvi if there is one
+					if(!u->groups().isEmpty()) {
+						//QString gname = ((ContactViewItem *)static_cast<QListViewItem *>(i)->parent())->groupName();
+						QString gname = groupNameCache;
+						actionGroupRemove(u->jid(), gname);
+					}
+					// add the group
+					actionGroupAdd(u->jid(), newgroup);
+				}
+			}
+		}
+	}
+}
+
+void ContactProfile::scActionDefault(ContactViewItem *i)
+{
+	if(i->type() == ContactViewItem::Contact)
+		actionDefault(i->u()->jid());
+}
+
+void ContactProfile::scRecvEvent(ContactViewItem *i)
+{
+	if(i->type() == ContactViewItem::Contact)
+		actionRecvEvent(i->u()->jid());
+}
+
+void ContactProfile::scSendMessage(ContactViewItem *i)
+{
+	if(i->type() == ContactViewItem::Contact)
+		actionSendMessage(i->u()->jid());
+}
+
+void ContactProfile::scRename(ContactViewItem *i)
+{
+	if(!d->pa->loggedIn())
+		return;
+
+	if((i->type() == ContactViewItem::Contact && i->u()->inList()) ||
+		(i->type() == ContactViewItem::Group && i->groupType() == ContactViewItem::gUser && i->groupName() != ContactView::tr("Hidden"))) {
+		i->setRenameEnabled(0, true);
+		i->startRename(0);
+		i->setRenameEnabled(0, false);
+	}
+}
+
+void ContactProfile::scVCard(ContactViewItem *i)
+{
+	if(i->type() == ContactViewItem::Contact)
+		actionInfo(i->u()->jid());
+}
+
+void ContactProfile::scHistory(ContactViewItem *i)
+{
+	if(i->type() == ContactViewItem::Contact)
+		actionHistory(i->u()->jid());
+}
+
+void ContactProfile::scOpenChat(ContactViewItem *i)
+{
+	if(i->type() == ContactViewItem::Contact)
+		actionOpenChat(i->u()->jid());
+}
+
+void ContactProfile::scAgentSetStatus(ContactViewItem *i, Status &s)
+{
+	if(i->type() != ContactViewItem::Contact)
+		return;
+	if(!i->isAgent())
+		return;
+
+	if(i->u()->isAvailable() || !d->pa->loggedIn())
+		return;
+
+	actionAgentSetStatus(i->u()->jid(), s);
+}
+
+void ContactProfile::scRemove(ContactViewItem *i)
+{
+	if(i->type() != ContactViewItem::Contact)
+		return;
+
+	Entry *e = findEntry(i);
+	if(!e)
+		return;
+
+	bool ok = true;
+	if(!d->pa->loggedIn())
+		ok = false;
+	if(!i->u()->inList())
+		ok = true;
+
+	if(ok) {
+		QString name = e->u.jid().full();
+		QString show = jidnick(e->u.jid().full(), e->u.name());
+		if(name != show)
+			name += QString(" (%1)").arg(e->u.name());
+
+		int n = 0;
+		int gt = i->parentGroupType();
+		if(gt != ContactViewItem::gNotInList && gt != ContactViewItem::gPrivate) {
+			n = QMessageBox::information(d->cv, tr("Remove"),
+			tr("Are you sure you want to remove <b>%1</b> from your contact list?").arg(name),
+			tr("&Yes"), tr("&No"));
+		}
+		else
+			n = 0;
+
+		if(n == 0) {
+			Jid j = e->u.jid();
+			removeEntry(e);
+			actionRemove(j);
+		}
+	}
+}
+
+void ContactProfile::doItemRenamed(ContactViewItem *i, const QString &text)
+{
+	if(i->type() == ContactViewItem::Contact) {
+		Entry *e = findEntry(i);
+		if(!e)
+			return;
+
+		// no change?
+		//if(text == i->text(0))
+		//	return;
+		if(text.isEmpty()) {
+			i->resetName();
+			QMessageBox::information(d->cv, tr("Error"), tr("You can't set a blank name."));
+			return;
+		}
+
+		//e->u.setName(text);
+		//i->setContact(&e->u);
+		actionRename(e->u.jid(), text);
+	}
+	else {
+		// no change?
+		if(text == i->groupName()) {
+			i->resetGroupName();
+			return;
+		}
+		if(text.isEmpty()) {
+			i->resetGroupName();
+			QMessageBox::information(d->cv, tr("Error"), tr("You can't set a blank group name."));
+			return;
+		}
+
+		// make sure we don't have it already
+		QStringList g = groupList();
+		bool found = false;
+		for(QStringList::ConstIterator it = g.begin(); it != g.end(); ++it) {
+			if(*it == text) {
+				found = true;
+				break;
+			}
+		}
+		if(found) {
+			i->resetGroupName();
+			QMessageBox::information(d->cv, tr("Error"), tr("You already have a group with that name."));
+			return;
+		}
+
+		QString oldName = i->groupName();
+
+		// set group name
+		i->setGroupName(text);
+
+		// send signal
+		actionGroupRename(oldName, text);
+	}
+}
+
+void ContactProfile::dragDrop(const QString &text, ContactViewItem *i)
+{
+	if(!d->pa->loggedIn())
+		return;
+
+	// get group
+	ContactViewItem *gr;
+	if(i->type() == ContactViewItem::Group)
+		gr = i;
+	else
+		gr = (ContactViewItem *)static_cast<QListViewItem *>(i)->parent();
+
+	Jid j(text);
+	if(!j.isValid())
+		return;
+	Entry *e = findEntry(j);
+	if(!e)
+		return;
+	const UserListItem &u = e->u;
+	QStringList gl = u.groups();
+
+	// already in the general group
+	if(gr->groupType() == ContactViewItem::gGeneral && gl.isEmpty())
+		return;
+	// already in this user group
+	if(gr->groupType() == ContactViewItem::gUser && u.inGroup(gr->groupName()))
+		return;
+
+	//printf("putting [%s] into group [%s]\n", u.jid().full().latin1(), gr->groupName().latin1());
+
+	// remove all other groups from this contact
+	for(QStringList::ConstIterator it = gl.begin(); it != gl.end(); ++it) {
+		actionGroupRemove(u.jid(), *it);
+	}
+	if(gr->groupType() == ContactViewItem::gUser) {
+		// add the new group
+		actionGroupAdd(u.jid(), gr->groupName());
+	}
+}
+
+void ContactProfile::dragDropFiles(const QStringList &files, ContactViewItem *i)
+{
+	if(files.isEmpty() || !d->pa->loggedIn() || i->type() != ContactViewItem::Contact)
+		return;
+
+	Entry *e = findEntry(i);
+	if(!e)
+		return;
+
+	actionSendFiles(e->u.jid(),files);
+}
+
+//----------------------------------------------------------------------------
+// ContactView
+//----------------------------------------------------------------------------
+class ContactView::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(ContactView *_cv)
+		: QObject(_cv)
+	{
+		cv = _cv;
+		autoRosterResizeInProgress = false;
+
+		// type ahead
+		typeAheadTimer = new QTimer(cv);
+		connect(typeAheadTimer, SIGNAL(timeout()), SLOT(resetTypeAhead()));
+	}
+
+	ContactView *cv;
+	QTimer *animTimer, *recalculateSizeTimer;
+	QPtrList<ContactProfile> profiles;
+	QString typeAhead;
+	QTimer *typeAheadTimer;
+	QSize lastSize;
+	bool autoRosterResizeInProgress;
+
+	bool doTypeAhead(QKeyEvent *e)
+	{
+		typeAheadTimer->start(3000, TRUE); // reset type ahead in 3 seconds of inactivity
+
+		QString text = e->text().lower();
+		if ( text.isEmpty() ) {
+			// Shift key could be used for entering '@', '#' and other extended chars
+			// and other modifier keys could be used for layout changing
+
+			//if ( e->key() != Qt::Key_Shift )
+			//	typeAhead = QString::null;
+			return false;
+		}
+
+		bool searchFromStart = true;
+		if ( typeAhead.isEmpty() )
+			searchFromStart = false;
+
+		bool found = false;
+		typeAhead += text;
+
+		QListViewItemIterator it(cv);
+		ContactViewItem *item;
+
+		if ( !searchFromStart && cv->currentItem() ) {
+			for ( ; (item = (ContactViewItem *)it.current()); ++it) {
+				if ( item == cv->currentItem() ) {
+					++it; // we want the search to start from next contact
+					break;
+				}
+			}
+		}
+
+		for ( ; (item = (ContactViewItem *)it.current()); ++it) {
+			//qWarning("->%s | %s | %s", text.latin1(), item->text(0).lower().latin1(), typeAhead.latin1());
+			if ( item->text(0).lower().startsWith(typeAhead) ) {
+				found = true;
+				cv->setSelected(item, true);
+				cv->ensureItemVisible(item);
+				item->optionsUpdate();
+				item->repaint();
+				break;
+			}
+		}
+
+		//qWarning("---------------\n");
+		if ( !found ) {
+			typeAhead = text;
+			return false;
+		}
+
+		return true;
+	}
+
+public slots:
+	/*
+	 * \brief Recalculates the size of ContactView and resizes it accordingly
+	 */
+	void recalculateSize()
+	{
+		// save some CPU
+		if ( !cv->allowResize() )
+			return;
+
+		if ( !cv->isUpdatesEnabled() )
+			return;
+
+		QSize oldSize = cv->size();
+		QSize newSize = cv->sizeHint();
+
+		if ( newSize.height() != oldSize.height() ) {
+			lastSize = newSize;
+			QWidget *topParent = cv->topLevelWidget();
+
+			if ( cv->allowResize() ) {
+				topParent->layout()->setEnabled( false ); // try to reduce some flicker
+
+				int dh = newSize.height() - oldSize.height();
+				topParent->resize( topParent->width(),
+						   topParent->height() + dh );
+
+				autoRosterResizeInProgress = true;
+
+				QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
+				if ( option.autoRosterSizeGrowTop ) {
+					int newy = topParent->y() - dh;
+
+					// check, if we need to move roster lower
+					if ( dh > 0 && ( topParent->frameGeometry().top() <= desktop.top() ) ) {
+						newy = desktop.top();
+					}
+
+					topParent->move( topParent->x(), newy );
+				}
+
+				// check, if we need to move roster upper
+				if ( dh > 0 && ( topParent->frameGeometry().bottom() >= desktop.bottom() ) ) {
+					int newy = desktop.bottom() - topParent->frameGeometry().height();
+					topParent->move( topParent->x(), newy );
+				}
+
+				QTimer::singleShot( 0, this, SLOT( resetAutoRosterResize() ) );
+
+				topParent->layout()->setEnabled( true );
+			}
+
+			// issue a layout update
+			topParent->layout()->activate();
+		}
+	}
+
+	/*
+	 * \brief Determine in which direction to grow Psi roster window when autoRosterSize is enabled
+	 */
+	void determineAutoRosterSizeGrowSide()
+	{
+		if ( autoRosterResizeInProgress )
+			return;
+
+		QWidget *topParent = cv->topLevelWidget();
+		QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
+
+		int top_offs    = abs( desktop.top()    - topParent->frameGeometry().top() );
+		int bottom_offs = abs( desktop.bottom() - topParent->frameGeometry().bottom() );
+
+		option.autoRosterSizeGrowTop = bottom_offs < top_offs;
+		//qWarning("growTop = %d", option.autoRosterSizeGrowTop);
+	}
+
+private slots:
+	void resetAutoRosterResize()
+	{
+		//qWarning("resetAutoRosterResize");
+		autoRosterResizeInProgress = false;
+	}
+
+	void resetTypeAhead()
+	{
+		typeAhead = QString::null;
+	}
+};
+
+ContactView::ContactView(QWidget *parent, const char *name)
+:QListView(parent, name, QListView::WRepaintNoErase | QListView::WResizeNoErase), QToolTip(viewport())
+{
+	d = new Private(this);
+
+	// setup the QListView
+	setAllColumnsShowFocus(true);
+	setShowToolTips(false);
+	setHScrollBarMode(AlwaysOff);
+	setMinimumSize(96,32);
+	setTreeStepSize(4);
+	setSorting(0,true);
+
+	topLevelWidget()->installEventFilter( this );
+
+	// create the column and hide the header
+	addColumn("");
+	header()->hide();
+
+	setResizeMode(QListView::LastColumn);
+	setDefaultRenameAction(QListView::Accept);
+
+	// catch signals
+	lcto_active = false;
+	connect(this, SIGNAL(itemRenamed(QListViewItem *, int, const QString &)), SLOT(qlv_itemRenamed(QListViewItem *, int, const QString &)));
+	connect(this, SIGNAL(mouseButtonPressed(int, QListViewItem *, const QPoint &, int)),SLOT(qlv_singleclick(int, QListViewItem *, const QPoint &, int)) );
+	connect(this, SIGNAL(doubleClicked(QListViewItem *)),SLOT(qlv_doubleclick(QListViewItem *)) );
+	connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), SLOT(qlv_contextMenuRequested(QListViewItem *, const QPoint &, int)));
+
+	v_showOffline = true;
+	v_showAway = true;
+	v_showHidden = true;
+	v_showAgents = true;
+	v_showSelf = true;
+
+	d->lastSize = QSize( 0, 0 );
+
+	// animation timer
+	d->animTimer = new QTimer(this);
+	d->animTimer->start(120 * 5);
+
+	d->recalculateSizeTimer = new QTimer;
+	connect(d->recalculateSizeTimer, SIGNAL(timeout()), d, SLOT(recalculateSize()));
+
+	// actions
+	qa_send = new IconAction("", "psi/sendMessage", tr("Send &message"), QListView::CTRL+QListView::Key_M, this);
+	connect(qa_send, SIGNAL(activated()), SLOT(doSendMessage()));
+	qa_ren = new IconAction("", /*"psi/edit/clear",*/ tr("Re&name"), QListView::Key_F2, this);
+	connect(qa_ren, SIGNAL(activated()), SLOT(doRename()));
+#ifdef AVATARS
+	qa_assignAvatar = new IconAction("", tr("&Assign Custom Avatar"),QKeySequence(), this);
+	connect(qa_assignAvatar, SIGNAL(activated()), SLOT(doAssignAvatar()));
+	qa_clearAvatar = new IconAction("", tr("&Clear Custom Avatar"), QKeySequence(), this);
+	connect(qa_clearAvatar, SIGNAL(activated()), SLOT(doClearAvatar()));
+#endif
+	qa_chat = new IconAction("", "psi/start-chat", tr("Open &chat window"), QListView::CTRL+QListView::Key_C, this);
+	connect(qa_chat, SIGNAL(activated()), SLOT(doOpenChat()));
+	qa_hist = new IconAction("", "psi/history", tr("&History"), QListView::CTRL+QListView::Key_H, this);
+	connect(qa_hist, SIGNAL(activated()), SLOT(doHistory()));
+	qa_logon = new IconAction("", tr("&Log on"), QListView::CTRL+QListView::Key_L, this);
+	connect(qa_logon, SIGNAL(activated()), SLOT(doLogon()));
+	qa_recv = new IconAction("", tr("&Receive incoming event"), QListView::CTRL+QListView::Key_R, this);
+	connect(qa_recv, SIGNAL(activated()), SLOT(doRecvEvent()));
+	qa_rem = new IconAction("", "psi/remove", tr("Rem&ove"), QListView::Key_Delete, this);
+	connect(qa_rem, SIGNAL(activated()), SLOT(doRemove()));
+	qa_vcard = new IconAction("", "psi/vCard", tr("User &Info"), QListView::CTRL+QListView::Key_I, this);
+	connect(qa_vcard, SIGNAL(activated()), SLOT(doVCard()));
+
+	if(option.lockdown.roster) {
+		qa_ren->setEnabled(false);
+		qa_rem->setEnabled(false);
+	}
+
+	optionsUpdate();
+	setAcceptDrops(true);
+	viewport()->setAcceptDrops(true);
+}
+
+ContactView::~ContactView()
+{
+	clear();
+	delete d;
+}
+
+QTimer *ContactView::animTimer() const
+{
+	return d->animTimer;
+}
+
+void ContactView::clear()
+{
+	d->profiles.setAutoDelete(true);
+	d->profiles.clear();
+	d->profiles.setAutoDelete(false);
+}
+
+void ContactView::link(ContactProfile *cp)
+{
+	d->profiles.append(cp);
+}
+
+void ContactView::unlink(ContactProfile *cp)
+{
+	d->profiles.removeRef(cp);
+}
+
+void ContactView::maybeTip(const QPoint &pos)
+{
+	ContactViewItem *i = (ContactViewItem *)itemAt(pos);
+	if(!i)
+		return;
+	QRect r(itemRect(i));
+	if(i->type() == ContactViewItem::Contact)
+		tip(r, i->u()->makeTip(true, false));
+	else if(i->type() == ContactViewItem::Profile) {
+		tip(r, i->contactProfile()->makeTip(true, false));
+	}
+	else
+		tip(r, i->text(0) + " " + i->groupInfo());
+}
+
+void ContactView::keyPressEvent(QKeyEvent *e)
+{
+	int key = e->key();
+	if(key == QListView::Key_Enter || key == QListView::Key_Return)
+		doEnter();
+	else if(key == QListView::Key_Space && d->typeAhead.isEmpty())
+		doContext();
+	else if (key == QListView::Key_Home   || key == QListView::Key_End      ||
+		 key == QListView::Key_PageUp || key == QListView::Key_PageDown ||
+		 key == QListView::Key_Up     || key == QListView::Key_Down     ||
+		 key == QListView::Key_Left   || key == QListView::Key_Right) {
+
+		d->typeAhead = QString::null;
+		QListView::keyPressEvent(e);
+	}
+	else {
+		if (!d->doTypeAhead(e))
+			QListView::keyPressEvent(e);
+	}
+}
+
+void ContactView::setShowOffline(bool x)
+{
+	bool oldstate = v_showOffline;
+	v_showOffline = x;
+
+	if(v_showOffline != oldstate) {
+		showOffline(v_showOffline);
+
+		QPtrListIterator<ContactProfile> it(d->profiles);
+		for(ContactProfile *cp; (cp = it.current()); ++it) {
+			if(!v_showOffline)
+				cp->removeAllUnneededContactItems();
+			else
+				cp->addAllNeededContactItems();
+		}
+	}
+}
+
+void ContactView::setShowAway(bool x)
+{
+	bool oldstate = v_showAway;
+	v_showAway = x;
+
+	if(v_showAway != oldstate) {
+		showAway(v_showAway);
+
+		QPtrListIterator<ContactProfile> it(d->profiles);
+		for(ContactProfile *cp; (cp = it.current()); ++it) {
+			if(!v_showAway)
+				cp->removeAllUnneededContactItems();
+			else
+				cp->addAllNeededContactItems();
+		}
+	}
+}
+
+void ContactView::setShowHidden(bool x)
+{
+	bool oldstate = v_showHidden;
+	v_showHidden = x;
+
+	if(v_showHidden != oldstate) {
+		showHidden(v_showHidden);
+
+		QPtrListIterator<ContactProfile> it(d->profiles);
+		for(ContactProfile *cp; (cp = it.current()); ++it) {
+			if(!v_showHidden)
+				cp->removeAllUnneededContactItems();
+			else
+				cp->addAllNeededContactItems();
+		}
+	}
+}
+
+/*
+ * \brief Shows/hides the self contact in roster
+ *
+ * \param x - boolean variable specifies whether to show self-contact or not
+ */
+void ContactView::setShowSelf(bool x)
+{
+	if (v_showSelf != x) {
+		v_showSelf = x;
+		showSelf(v_showSelf);
+
+		QPtrListIterator<ContactProfile> it(d->profiles);
+		for(ContactProfile *cp; (cp = it.current()); ++it) {
+			if (v_showSelf && ! cp->self()) {
+				cp->addSelf();
+			}
+			else if (!v_showSelf && cp->self() && cp->self()->u()->userResourceList().count() <= 1) {
+				cp->removeSelf();
+			}
+		}
+
+		recalculateSize();
+	}
+}
+
+/*
+ * \brief Event filter. Nuff said.
+ */
+bool ContactView::eventFilter( QObject *obj, QEvent *event )
+{
+	if ( event->type() == QEvent::Move )
+		d->determineAutoRosterSizeGrowSide();
+
+	return QListView::eventFilter( obj, event );
+}
+
+
+void ContactView::setShowAgents(bool x)
+{
+	bool oldstate = v_showAgents;
+	v_showAgents = x;
+
+	if(v_showAgents != oldstate) {
+		showAgents(v_showAgents);
+
+		QPtrListIterator<ContactProfile> it(d->profiles);
+		for(ContactProfile *cp; (cp = it.current()); ++it) {
+			if(!v_showAgents)
+				cp->removeAllUnneededContactItems();
+			else
+				cp->addAllNeededContactItems();
+		}
+	}
+}
+
+// right-click context menu
+void ContactView::qlv_contextMenuRequested(QListViewItem *lvi, const QPoint &pos, int c)
+{
+	if(option.useleft)
+		return;
+
+	qlv_contextPopup(lvi, pos, c);
+}
+
+void ContactView::qlv_contextPopup(QListViewItem *lvi, const QPoint &pos, int)
+{
+	ContactViewItem *i = (ContactViewItem *)lvi;
+	if(!i)
+		return;
+
+	i->contactProfile()->doContextMenu(i, pos);
+}
+
+void ContactView::qlv_singleclick(int button, QListViewItem *i, const QPoint &pos, int c)
+{
+	lcto_active = false;
+	QToolTip::hide();
+
+	if(!i)
+		return;
+
+	ContactViewItem *item = (ContactViewItem *)i;
+	setSelected(item, true);
+
+	if(button == QListView::MidButton) {
+		if(item->type() == ContactViewItem::Contact)
+			item->contactProfile()->scActionDefault(item);
+	}
+	else {
+		const QPixmap * pix = item->pixmap(0);			
+		if (button == QListView::LeftButton && item->type() == ContactViewItem::Group && pix && viewport()->mapFromGlobal(pos).x() <= pix->width() + treeStepSize()) {
+			setOpen(item, !item->isOpen());
+		}
+		else if(option.useleft) {
+			if(button == QListView::LeftButton) {
+				if(option.singleclick) {
+					qlv_contextPopup(i, pos, c);
+				}
+				else {
+					lcto_active = true;
+					lcto_pos = pos;
+					lcto_item = i;
+					QTimer::singleShot(QApplication::doubleClickInterval()/2, this, SLOT(leftClickTimeOut()));
+				}
+			}
+			else if(option.singleclick && button == QListView::RightButton) {
+				if(item->type() == ContactViewItem::Contact)
+					item->contactProfile()->scActionDefault(item);
+			}
+		}
+		else {
+			//if(button == QListView::RightButton) {
+			//	qlv_contextPopup(i, pos, c);
+			//}
+			/*else*/if(button == QListView::LeftButton && option.singleclick) {
+				if(item->type() == ContactViewItem::Contact)
+					item->contactProfile()->scActionDefault(item);
+			}
+		}
+	}
+
+	d->typeAhead = "";
+}
+
+void ContactView::qlv_doubleclick(QListViewItem *i)
+{
+	lcto_active = false;
+
+	if(!i)
+		return;
+
+	if(option.singleclick)
+		return;
+
+	ContactViewItem *item = (ContactViewItem *)i;
+	item->contactProfile()->scActionDefault(item);
+
+	d->typeAhead = "";
+}
+
+void ContactView::qlv_itemRenamed(QListViewItem *lvi, int, const QString &text)
+{
+	ContactViewItem *i = (ContactViewItem *)lvi;
+	i->contactProfile()->doItemRenamed(i, text);
+}
+
+void ContactView::leftClickTimeOut()
+{
+	if(lcto_active) {
+		QToolTip::hide();
+		qlv_contextPopup(lcto_item, lcto_pos, 0);
+		lcto_active = false;
+	}
+}
+
+void ContactView::optionsUpdate()
+{
+	// set the font
+	QFont f;
+	f.fromString(option.font[fRoster]);
+	QListView::setFont(f);
+
+	// set the text and background colors
+	QPalette mypal = QListView::palette();
+	mypal.setColor(QColorGroup::Text, option.color[cOnline]);
+	mypal.setColor(QColorGroup::Base, option.color[cListBack]);
+	QListView::setPalette(mypal);
+
+	// reload the icons
+	QListViewItemIterator it(this);
+	ContactViewItem *item;
+	for(; it.current() ; ++it) {
+		item = (ContactViewItem *)it.current();
+		item->optionsUpdate();
+	}
+
+	// resize if necessary
+	if (option.autoRosterSize)
+		recalculateSize();
+
+	update();
+}
+
+void ContactView::resetAnim()
+{
+	for(QListViewItemIterator it(this); it.current() ; ++it) {
+		ContactViewItem *item = (ContactViewItem *)it.current();
+		if(item->isAlerting())
+			item->resetAnim();
+	}
+}
+
+void ContactView::doRecvEvent()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	i->contactProfile()->scRecvEvent(i);
+}
+
+void ContactView::doRename()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	i->contactProfile()->scRename(i);
+}
+
+void ContactView::doAssignAvatar()
+{
+	// FIXME: Should check the supported filetypes dynamically
+	QString file = QFileDialog::getOpenFileName("", tr("All files (*.png *.jpg *.gif)"), this, 0, tr("Choose an image"));
+	if (!file.isNull()) {
+		ContactViewItem *i = (ContactViewItem *)selectedItem();
+		i->contactProfile()->psiAccount()->avatarFactory()->importManualAvatar(i->u()->jid(),file);
+	}
+}
+
+void ContactView::doClearAvatar()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	i->contactProfile()->psiAccount()->avatarFactory()->removeManualAvatar(i->u()->jid());
+}
+
+void ContactView::doEnter()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	i->contactProfile()->scActionDefault(i);
+}
+
+void ContactView::doContext()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	ensureItemVisible(i);
+
+	if(i->type() == ContactViewItem::Group)
+		setOpen(i, !i->isOpen());
+	else
+		qlv_contextPopup(i, viewport()->mapToGlobal(QPoint(32, itemPos(i))), 0);
+}
+
+void ContactView::doSendMessage()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	i->contactProfile()->scSendMessage(i);
+}
+
+void ContactView::doOpenChat()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	i->contactProfile()->scOpenChat(i);
+}
+
+void ContactView::doHistory()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	i->contactProfile()->scHistory(i);
+}
+
+void ContactView::doVCard()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	i->contactProfile()->scVCard(i);
+}
+
+void ContactView::doLogon()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	Status s=i->contactProfile()->psiAccount()->status();
+	i->contactProfile()->scAgentSetStatus(i, s);
+}
+
+void ContactView::doRemove()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return;
+	i->contactProfile()->scRemove(i);
+}
+
+QDragObject *ContactView::dragObject()
+{
+	ContactViewItem *i = (ContactViewItem *)selectedItem();
+	if(!i)
+		return 0;
+	if(i->type() != ContactViewItem::Contact)
+		return 0;
+
+	QDragObject *d = new QTextDrag(i->u()->jid().full(), this);
+	d->setPixmap(IconsetFactory::iconPixmap("status/online"), QPoint(8,8));
+	return d;
+}
+
+bool ContactView::allowResize() const
+{
+	if ( !option.autoRosterSize )
+		return false;
+
+	if ( topLevelWidget()->isMaximized() )
+		return false;
+
+	return true;
+}
+
+QSize ContactView::minimumSizeHint() const
+{
+	return QSize( minimumWidth(), minimumHeight() );
+}
+
+QSize ContactView::sizeHint() const
+{
+	// save some CPU
+	if ( !allowResize() )
+		return minimumSizeHint();
+
+	QSize s( QListView::sizeHint().width(), 0 );
+	int border = 5;
+	int h = border;
+
+	QListView *listView = (QListView *)this;
+	QListViewItemIterator it( listView );
+	while ( it.current() ) {
+		if ( it.current()->isVisible() ) {
+			// also we need to check whether the group is open or closed
+			bool show = true;
+			QListViewItem *item = it.current()->parent();
+			while ( item ) {
+				if ( !item->isOpen() ) {
+					show = false;
+					break;
+				}
+				item = item->parent();
+			}
+
+			if ( show )
+				h += it.current()->height();
+		}
+
+		++it;
+	}
+
+	QWidget *topParent = topLevelWidget();
+	QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
+	int dh = h - d->lastSize.height();
+
+	// check that our dialog's height doesn't exceed the desktop's
+	if ( allowResize() && dh > 0 && (topParent->frameGeometry().height() + dh) >= desktop.height() ) {
+		h = desktop.height() - ( topParent->frameGeometry().height() - d->lastSize.height() );
+	}
+
+	int minH = minimumSizeHint().height();
+	if ( h < minH )
+		h = minH + border;
+	s.setHeight( h );
+	return s;
+}
+
+/*
+ * \brief Adds the request to recalculate the ContactView size to the event queue
+ */
+void ContactView::recalculateSize()
+{
+	d->recalculateSizeTimer->start( 0, true );
+}
+
+//----------------------------------------------------------------------------
+// ContactViewItem
+//----------------------------------------------------------------------------
+class ContactViewItem::Private
+{
+private:
+	ContactViewItem *cvi;
+
+public:
+	Private(ContactViewItem *parent, ContactProfile *_cp) {
+		cvi = parent;
+		cp = _cp;
+		u = 0;
+
+		icon = lastIcon = 0;
+	}
+
+	~Private() {
+	}
+
+	void initGroupState() {
+		UserAccount::GroupData gd = groupData();
+		cvi->setOpen(gd.open);
+	}
+
+	QString getGroupName() {
+		QString group;
+		if ( type == Profile )
+			group = "/\\/" + profileName + "\\/\\";
+		else
+			group = groupName;
+
+		return group;
+	}
+
+	QMap<QString, UserAccount::GroupData> *groupState() {
+		return (QMap<QString, UserAccount::GroupData> *)&cp->psiAccount()->userAccount().groupState;
+	}
+
+	UserAccount::GroupData groupData() {
+		QMap<QString, UserAccount::GroupData> groupState = (QMap<QString, UserAccount::GroupData>)cp->psiAccount()->userAccount().groupState;
+		QMap<QString, UserAccount::GroupData>::Iterator it = groupState.find(getGroupName());
+
+		UserAccount::GroupData gd;
+		gd.open = true;
+		gd.rank = 0;
+
+		if ( it != groupState.end() )
+			gd = it.data();
+
+		return gd;
+	}
+
+	int type;
+	ContactProfile *cp;
+	int status;
+
+	// profiles
+	QString profileName;
+	bool ssl;
+
+	// groups
+	int groupType;
+	QString groupName;
+	QString groupInfo;
+
+	// contact
+	UserListItem *u;
+	bool isAgent;
+	bool alerting;
+	bool animatingNick;
+
+	Icon *icon, *lastIcon;
+	int animateNickX, animateNickColor; // nick animation
+};
+
+ContactViewItem::ContactViewItem(const QString &profileName, ContactProfile *cp, ContactView *parent)
+:QListViewItem(parent)
+{
+	d = new Private(this, cp);
+	d->type = Profile;
+	d->profileName = profileName;
+	d->alerting = false;
+	d->ssl = false;
+
+	setProfileState(STATUS_OFFLINE);
+	setText(0, profileName);
+
+	d->initGroupState();
+}
+
+ContactViewItem::ContactViewItem(const QString &groupName, int groupType, ContactProfile *cp, ContactView *parent)
+:QListViewItem(parent)
+{
+	d = new Private(this, cp);
+	d->type = Group;
+	d->groupName = groupName;
+	d->groupType = groupType;
+	d->alerting = false;
+
+	drawGroupIcon();
+	resetGroupName();
+	setDropEnabled(true);
+
+	d->initGroupState();
+}
+
+ContactViewItem::ContactViewItem(const QString &groupName, int groupType, ContactProfile *cp, ContactViewItem *parent)
+:QListViewItem(parent)
+{
+	d = new Private(this, cp);
+	d->type = Group;
+	d->groupName = groupName;
+	d->groupType = groupType;
+	d->alerting = false;
+
+	drawGroupIcon();
+	resetGroupName();
+	setDropEnabled(true);
+
+	if(!parent->isVisible())
+		setVisible(false);
+
+	d->initGroupState();
+}
+
+ContactViewItem::ContactViewItem(UserListItem *u, ContactProfile *cp, ContactViewItem *parent)
+:QListViewItem(parent)
+{
+	d = new Private(this, cp);
+	d->cp = cp;
+	d->type = Contact;
+	d->u = u;
+	d->alerting = false;
+	d->animatingNick = false;
+
+	cacheValues();
+
+	resetStatus();
+	resetName();
+
+	setDragEnabled(true);
+	setDropEnabled(true);
+
+	if(!parent->isVisible())
+		setVisible(false);
+}
+
+ContactViewItem::~ContactViewItem()
+{
+	setIcon( 0 );
+	delete d;
+}
+
+void ContactViewItem::cacheValues()
+{
+	if ( d->u ) {
+		if( !d->u->isAvailable() )
+			d->status = STATUS_OFFLINE;
+		else
+			d->status = makeSTATUS((*d->u->priority()).status());
+		d->isAgent = d->u->isTransport();
+	}
+}
+
+ContactProfile *ContactViewItem::contactProfile() const
+{
+	return d->cp;
+}
+
+int ContactViewItem::type() const
+{
+	return d->type;
+}
+
+const QString & ContactViewItem::groupName() const
+{
+	return d->groupName;
+}
+
+const QString & ContactViewItem::groupInfo() const
+{
+	return d->groupInfo;
+}
+
+int ContactViewItem::groupType() const
+{
+	return d->groupType;
+}
+
+UserListItem *ContactViewItem::u() const
+{
+	return d->u;
+}
+
+int ContactViewItem::status() const
+{
+	return d->status;
+}
+
+bool ContactViewItem::isAgent() const
+{
+	return d->isAgent;
+}
+
+bool ContactViewItem::isAlerting() const
+{
+	return d->alerting;
+}
+
+bool ContactViewItem::isAnimatingNick() const
+{
+	return d->animatingNick;
+}
+
+int ContactViewItem::parentGroupType() const
+{
+	ContactViewItem *item = (ContactViewItem *)QListViewItem::parent();
+	return item->groupType();
+}
+
+void ContactViewItem::drawGroupIcon()
+{
+	if ( d->type == Group ) {
+		if ( childCount() == 0 )
+			setIcon(IconsetFactory::iconPtr("psi/groupEmpty"));
+		else if ( isOpen() )
+			setIcon(IconsetFactory::iconPtr("psi/groupOpen"));
+		else
+			setIcon(IconsetFactory::iconPtr("psi/groupClosed"));
+	}
+	else if ( d->type == Profile ) {
+		if ( !d->alerting )
+			setProfileState(d->status);
+	}
+}
+
+void ContactViewItem::paintFocus(QPainter *, const QColorGroup &, const QRect &)
+{
+	// re-implimented to do nothing.  selection is enough of a focus
+}
+
+void ContactViewItem::paintBranches(QPainter *p, const QColorGroup &cg, int w, int, int h)
+{
+	// paint a square of nothing
+	p->fillRect(0, 0, w, h, cg.base());
+}
+
+void ContactViewItem::paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int alignment)
+{
+	if ( d->type == Contact ) {
+		QColorGroup xcg = cg;
+
+		if(d->status == STATUS_AWAY || d->status == STATUS_XA)
+			xcg.setColor(QColorGroup::Text, option.color[cAway]);
+		else if(d->status == STATUS_DND)
+			xcg.setColor(QColorGroup::Text, option.color[cDND]);
+		else if(d->status == STATUS_OFFLINE)
+			xcg.setColor(QColorGroup::Text, option.color[cOffline]);
+
+		if(d->animatingNick) {
+			xcg.setColor(QColorGroup::Text, d->animateNickColor ? option.color[cAnimFront] : option.color[cAnimBack]);
+			xcg.setColor(QColorGroup::HighlightedText, d->animateNickColor ? option.color[cAnimFront] : option.color[cAnimBack]);
+		}
+
+		QListViewItem::paintCell(p, xcg, column, width, alignment);
+
+		QFontMetrics fm(p->font());
+		const QPixmap *pix = pixmap(column);
+		int x = fm.width(text(column)) + (pix ? pix->width() : 0) + 8;
+
+		if ( d->u ) {
+			UserResourceList::ConstIterator it = d->u->priority();
+			if(it != d->u->userResourceList().end()) {
+				if(d->u->isSecure((*it).name())) {
+					const QPixmap &pix = IconsetFactory::iconPixmap("psi/cryptoYes");
+					int y = (height() - pix.height()) / 2;
+					p->drawPixmap(x, y, pix);
+					x += 24;
+				}
+			}
+		}
+	}
+	else if ( d->type == Group || d->type == Profile ) {
+		QColorGroup xcg = cg;
+
+		if(d->type == Profile) {
+			xcg.setColor(QColorGroup::Text, option.color[cProfileFore]);
+			xcg.setColor(QColorGroup::Base, option.color[cProfileBack]);
+		}
+		else if(d->type == Group) {
+			QFont f = p->font();
+			f.setPointSize(option.smallFontSize);
+			p->setFont(f);
+			xcg.setColor(QColorGroup::Text, option.color[cGroupFore]);
+			if (!option.clNewHeadings) {
+				xcg.setColor(QColorGroup::Base, option.color[cGroupBack]);
+			}
+		} 
+
+		QListViewItem::paintCell(p, xcg, column, width, alignment);
+
+		QFontMetrics fm(p->font());
+		const QPixmap *pix = pixmap(column);
+		int x = fm.width(text(column)) + (pix ? pix->width() : 0) + 8;
+
+		if(d->type == Profile) {
+			const QPixmap &pix = d->ssl ? IconsetFactory::iconPixmap("psi/cryptoYes") : IconsetFactory::iconPixmap("psi/cryptoNo");
+			int y = (height() - pix.height()) / 2;
+			p->drawPixmap(x, y, pix);
+			x += 24;
+		}
+
+		if(isSelected())
+			p->setPen(xcg.highlightedText());
+		else
+			p->setPen(xcg.text());
+
+		QFont f_info = p->font();
+		f_info.setPointSize(option.smallFontSize);
+		p->setFont(f_info);
+		QFontMetrics fm_info(p->font());
+		//int info_x = width - fm_info.width(d->groupInfo) - 8;
+		int info_x = x;
+		int info_y = ((height() - fm_info.height()) / 2) + fm_info.ascent();
+		p->drawText((info_x > x ? info_x : x), info_y, d->groupInfo);
+
+		if(d->type == Group && option.clNewHeadings && !isSelected()) {
+			x += fm.width(d->groupInfo) + 8;
+			if(x < width - 8) {
+				int h = (height() / 2) - 1;
+				p->setPen(QPen(option.color[cGroupBack]));
+				p->drawLine(x, h, width - 8, h);
+				h++;
+				p->setPen(QPen(option.color[cGroupFore]));
+				/*int h = height() / 2;
+
+				p->setPen(QPen(option.color[cGroupBack], 2));*/
+				p->drawLine(x, h, width - 8, h);
+			}
+		} 
+		else {
+			if (option.outlineHeadings) {
+				p->setPen(QPen(option.color[cGroupFore]));
+				p->drawRect(0, 0, width, height());
+			}
+		}
+	}
+}
+
+/*
+ * \brief "Opens" or "closes the ContactViewItem
+ *
+ * When the item is in "open" state, all it's children items are visible.
+ *
+ * \param o - if true, the item will be "open"
+ */
+void ContactViewItem::setOpen(bool o)
+{
+	((ContactView *)listView())->recalculateSize();
+
+	QListViewItem::setOpen(o);
+	drawGroupIcon();
+
+	// save state
+	UserAccount::GroupData gd = d->groupData();
+	gd.open = o;
+	d->groupState()->insert(d->getGroupName(), gd);
+}
+
+void ContactViewItem::insertItem(QListViewItem *i)
+{
+	QListViewItem::insertItem(i);
+	drawGroupIcon();
+}
+
+void ContactViewItem::takeItem(QListViewItem *i)
+{
+	QListViewItem::takeItem(i);
+	drawGroupIcon();
+}
+
+int ContactViewItem::rankGroup(int groupType) const
+{
+	static int rankgroups[5] = {
+		gGeneral,
+		gUser,
+		gPrivate,
+		gAgents,
+		gNotInList,
+	};
+
+	int n;
+	for(n = 0; n < (int)sizeof(rankgroups); ++n) {
+		if(rankgroups[n] == groupType)
+			break;
+	}
+	if(n == sizeof(rankgroups))
+		return sizeof(rankgroups)-1;
+
+	return n;
+}
+
+int ContactViewItem::rankStatus(int status) const
+{
+	static int rankstatuses[7] = {
+		STATUS_CHAT,
+		STATUS_ONLINE,
+		STATUS_AWAY,
+		STATUS_XA,
+		STATUS_DND,
+		STATUS_INVISIBLE,
+		STATUS_OFFLINE,
+	};
+
+	int n;
+	for(n = 0; n < (int)sizeof(rankstatuses); ++n) {
+		if(rankstatuses[n] == status)
+			break;
+	}
+	if(n == sizeof(rankstatuses))
+		return sizeof(rankstatuses)-1;
+
+	return n;
+}
+
+int ContactViewItem::compare(QListViewItem *lvi, int, bool) const
+{
+	ContactViewItem *i = (ContactViewItem *)lvi;
+	int ret = 0;
+
+	if(d->type == Group || d->type == Profile) {
+		// contacts always go before groups
+		if(i->type() == Contact)
+			ret = 1;
+		else if(i->type() == Group) {
+			if ( option.rosterGroupSortStyle == Options::GroupSortStyle_Rank ) {
+				int ourRank   = d->groupData().rank;
+				int theirRank = i->d->groupData().rank;
+
+				ret = ourRank - theirRank;
+			}
+			else { // GroupSortStyle_Alpha
+				ret = rankGroup(d->groupType) - rankGroup(i->groupType());
+				if(ret == 0)
+					ret = text(0).lower().localeAwareCompare(i->text(0).lower());
+			}
+		}
+		else if(i->type() == Profile) {
+			if ( option.rosterAccountSortStyle == Options::AccountSortStyle_Rank ) {
+				int ourRank = d->groupData().rank;
+				int theirRank = i->d->groupData().rank;
+
+				ret = ourRank - theirRank;
+			}
+			else // AccountSortStyle_Alpha
+				ret = text(0).lower().localeAwareCompare(i->text(0).lower());
+		}
+	}
+	else if(d->type == Contact) {
+		// contacts always go before groups
+		if(i->type() == Group)
+			ret = -1;
+		else {
+			if ( option.rosterContactSortStyle == Options::ContactSortStyle_Status ) {
+				ret = rankStatus(d->status) - rankStatus(i->status());
+				if(ret == 0)
+					ret = text(0).lower().localeAwareCompare(i->text(0).lower());
+			}
+			else { // ContactSortStyle_Alpha
+				ret = text(0).lower().localeAwareCompare(i->text(0).lower());
+			}
+		}
+	}
+
+	return ret;
+}
+
+void ContactViewItem::setProfileName(const QString &name)
+{
+	d->profileName = name;
+	setText(0, d->profileName);
+}
+
+void ContactViewItem::setProfileState(int status)
+{
+	if ( status == -1 ) {
+		setAlert( IconsetFactory::iconPtr("psi/connect") );
+	}
+	else {
+		d->status = status;
+
+		clearAlert();
+		setIcon(is->statusPtr(status));
+	}
+}
+
+void ContactViewItem::setProfileSSL(bool on)
+{
+	d->ssl = on;
+	repaint();
+}
+
+void ContactViewItem::setGroupName(const QString &name)
+{
+	d->groupName = name;
+	resetGroupName();
+
+	updatePosition();
+}
+
+void ContactViewItem::setGroupInfo(const QString &info)
+{
+	d->groupInfo = info;
+	repaint();
+}
+
+void ContactViewItem::resetStatus()
+{
+	if ( !d->alerting && d->u ) {
+		setIcon(is->statusPtr(d->u));
+	}
+}
+
+void ContactViewItem::resetName()
+{
+	resetStatus();
+	if ( d->u ) {
+		QString s = jidnick(d->u->jid().full(), d->u->name());
+		if ( s != text(0) )
+			setText(0, s);
+	}
+}
+
+void ContactViewItem::resetGroupName()
+{
+	if ( d->groupName != text(0) )
+		setText(0, d->groupName);
+}
+
+void ContactViewItem::resetAnim()
+{
+	if ( d->alerting ) {
+		// TODO: think of how to reset animation frame
+	}
+}
+
+void ContactViewItem::setAlert(const Icon *icon)
+{
+	bool reset = false;
+
+	if ( !d->alerting ) {
+		d->alerting = true;
+		reset = true;
+	}
+	else {
+		if ( d->lastIcon != icon )
+			reset = true;
+	}
+
+	if ( reset )
+		setIcon(icon, true);
+}
+
+void ContactViewItem::clearAlert()
+{
+	if ( d->alerting ) {
+		d->alerting = false;
+		//disconnect(static_cast<ContactView*>(QListViewItem::listView())->animTimer(), SIGNAL(timeout()), this, SLOT(animate()));
+		resetStatus();
+	}
+}
+
+void ContactViewItem::setIcon(const Icon *icon, bool alert)
+{
+	if ( d->lastIcon == icon ) {
+		return; // cause less flicker. but still have to run calltree valgring skin on psi while online (mblsha).
+	}
+	else
+		d->lastIcon = (Icon *)icon;
+
+	if ( d->icon ) {
+		disconnect(d->icon, 0, this, 0 );
+		d->icon->stop();
+
+		delete d->icon;
+		d->icon = 0;
+	}
+
+	QPixmap pix;
+	if ( icon ) {
+		if ( !alert )
+			d->icon = new Icon(*icon);
+		else
+			d->icon = new AlertIcon(icon);
+
+		connect(d->icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
+		d->icon->activated();
+
+		pix = d->icon->pixmap();
+	}
+
+	setPixmap(0, pix);
+}
+
+void ContactViewItem::iconUpdated(const QPixmap &pix)
+{
+	setPixmap(0, pix);
+}
+
+void ContactViewItem::animateNick()
+{
+	d->animateNickColor = !d->animateNickColor;
+	repaint();
+
+	if(++d->animateNickX >= 16)
+		stopAnimateNick();
+}
+
+void ContactViewItem::stopAnimateNick()
+{
+	if ( !d->animatingNick )
+		return;
+
+	disconnect(static_cast<ContactView*>(QListViewItem::listView())->animTimer(), SIGNAL(timeout()), this, SLOT(animateNick()));
+
+	d->animatingNick = false;
+	repaint();
+}
+
+void ContactViewItem::setAnimateNick()
+{
+	stopAnimateNick();
+
+	connect(static_cast<ContactView*>(QListViewItem::listView())->animTimer(), SIGNAL(timeout()), SLOT(animateNick()));
+
+	d->animatingNick = true;
+	d->animateNickX = 0;
+	animateNick();
+}
+
+void ContactViewItem::updatePosition()
+{
+	ContactViewItem *par = (ContactViewItem *)QListViewItem::parent();
+	if(!par)
+		return;
+
+	ContactViewItem *after = 0;
+	for(QListViewItem *i = par->firstChild(); i; i = i->nextSibling()) {
+		ContactViewItem *item = (ContactViewItem *)i;
+		// skip self
+		if(item == this)
+			continue;
+		int x = compare(item, 0, true);
+		if(x == 0)
+			continue;
+		if(x < 0)
+			break;
+		after = item;
+	}
+
+	if(after)
+		moveItem(after);
+	else {
+		QListViewItem *i = par->firstChild();
+		moveItem(i);
+		i->moveItem(this);
+	}
+}
+
+void ContactViewItem::optionsUpdate()
+{
+	if(d->type == Group || d->type == Profile) {
+		drawGroupIcon();
+	}
+	else if(d->type == Contact) {
+		if(!d->alerting)
+			resetStatus();
+		else
+			resetAnim();
+	}
+}
+
+void ContactViewItem::setContact(UserListItem *u)
+{
+	//int oldStatus = d->status;
+	QString oldName = text(0);
+	//bool wasAgent = d->isAgent;
+
+	QString newName = jidnick(u->jid().full(), u->name());
+
+	d->u = u;
+	cacheValues();
+
+	bool needUpdate = false;
+	//if(d->status != oldStatus || d->isAgent != wasAgent || !u->presenceError().isEmpty()) {
+		resetStatus();
+		needUpdate = true;
+	//}
+	if(newName != oldName) {
+		resetName();
+		needUpdate = true;
+	}
+
+	if(needUpdate)
+		updatePosition();
+
+	repaint();
+}
+
+bool ContactViewItem::acceptDrop(const QMimeSource *m) const
+{
+	if ( d->type == Profile )
+		return false;
+	else if ( d->type == Group ) {
+		if(d->groupType != gGeneral && d->groupType != gUser)
+			return false;
+	}
+	else if ( d->type == Contact ) {
+		if ( d->u && d->u->isSelf() )
+			return false;
+		ContactViewItem *par = (ContactViewItem *)QListViewItem::parent();
+		if(par->groupType() != gGeneral && par->groupType() != gUser)
+			return false;
+	}
+
+	// Files. Note that the QTextDrag test has to come after QUriDrag.
+	if (d->type == Contact && QUriDrag::canDecode(m)) {
+		QStringList l;
+		if (QUriDrag::decodeLocalFiles(m,l) && !l.isEmpty())
+			return true;
+	}
+
+	if(!QTextDrag::canDecode(m))
+		return false;
+
+	QString str;
+	if(!QTextDrag::decode(m, str))
+		return false;
+
+	return true;
+}
+
+void ContactViewItem::dragEntered()
+{
+	//printf("entered\n");
+}
+
+void ContactViewItem::dragLeft()
+{
+	//printf("left\n");
+}
+
+void ContactViewItem::dropped(QDropEvent *i)
+{
+	if(!acceptDrop(i))
+		return;
+
+	// Files
+	if (QUriDrag::canDecode(i)) {
+		QStringList l;
+		if (QUriDrag::decodeLocalFiles(i,l) && !l.isEmpty()) {
+			d->cp->dragDropFiles(l, this);
+			return;
+		}
+	}
+
+	// Text
+	if(QTextDrag::canDecode(i)) {
+		QString text;
+		if(QTextDrag::decode(i, text))
+			d->cp->dragDrop(text, this);
+	}
+}
+
+int ContactViewItem::rtti() const
+{
+	return 5103;
+}
+
+#include "contactview.moc"
Index: psi/tags/0.10-os2-b1/src/contactview.h
===================================================================
--- psi/tags/0.10-os2-b1/src/contactview.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/contactview.h	(revision 38)
@@ -0,0 +1,336 @@
+/*
+ * contactview.h - contact list widget
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CONTACTVIEW_H
+#define CONTACTVIEW_H
+
+#include<qobject.h>
+#include<qlistview.h>
+#include<qtooltip.h>
+#include<qvaluelist.h>
+#include"xmpp.h"
+#include"common.h"
+
+class IconAction;
+class UserListItem;
+class ContactView;
+class ContactViewItem;
+class PsiAccount;
+class Icon;
+class QTimer;
+class QPixmap;
+
+using namespace XMPP;
+
+class ContactProfile;
+
+// ContactProfile: this holds/manipulates a roster profile for an account
+class ContactProfile : public QObject
+{
+	Q_OBJECT
+public:
+	ContactProfile(PsiAccount *, const QString &name, ContactView *, bool unique=false);
+	~ContactProfile();
+
+	void setEnabled(bool e=TRUE);
+
+	const QString & name() const;
+	void setName(const QString &name);
+	void setState(int);
+	void setUsingSSL(bool);
+	ContactView *contactView() const;
+	ContactViewItem *self() const;
+
+	PsiAccount *psiAccount() const;
+
+	void updateEntry(const UserListItem &);
+	void removeEntry(const Jid &);
+	void updateSelf();
+	void addSelf();
+	void removeSelf();
+	void setAlert(const Jid &, const Icon *);
+	void clearAlert(const Jid &);
+	void animateNick(const Jid &);
+
+	void addAllNeededContactItems();
+	void removeAllUnneededContactItems();
+
+	void ensureVisible(const Jid &);
+	void clear();
+
+	QString makeTip(bool trim, bool doLinkify) const;
+
+	ContactViewItem *checkGroup(int type);
+	ContactViewItem *checkGroup(const QString &name);
+
+	void setName(const char *);
+
+
+signals:
+	void actionDefault(const Jid &);
+	void actionRecvEvent(const Jid &);
+	void actionSendMessage(const Jid &);
+	void actionSendMessage(const JidList &);
+	void actionSendUrl(const Jid &);
+	void actionRemove(const Jid &);
+	void actionRename(const Jid &, const QString &);
+	void actionGroupRename(const QString &, const QString &);
+	void actionHistory(const Jid &);
+	void actionOpenChat(const Jid &);
+	void actionOpenChatSpecific(const Jid &);
+	void actionAgentSetStatus(const Jid &, Status &);
+	void actionInfo(const Jid &);
+	void actionAuth(const Jid &);
+	void actionAuthRequest(const Jid &);
+	void actionAuthRemove(const Jid &);
+	void actionAdd(const Jid &);
+	void actionGroupAdd(const Jid &, const QString &);
+	void actionGroupRemove(const Jid &, const QString &);
+	void actionTest(const Jid &);
+	void actionSendFile(const Jid &);
+	void actionSendFiles(const Jid &, const QStringList &);
+	void actionDisco(const Jid &, const QString &);
+	void actionInvite(const Jid &, const QString &);
+	void actionAssignKey(const Jid &);
+	void actionUnassignKey(const Jid &);
+
+private slots:
+	void updateGroups();
+
+public:
+	class Entry;
+	class Private;
+private:
+	Private *d;
+
+	ContactViewItem *addGroup(int type);
+	ContactViewItem *addGroup(const QString &name);
+	ContactViewItem *ensureGroup(int type);
+	ContactViewItem *ensureGroup(const QString &name);
+	void checkDestroyGroup(ContactViewItem *group);
+	void checkDestroyGroup(const QString &group);
+	ContactViewItem *addContactItem(Entry *e, ContactViewItem *group_item);
+	ContactViewItem *ensureContactItem(Entry *e, ContactViewItem *group_item);
+	void removeContactItem(Entry *e, ContactViewItem *i);
+	void addNeededContactItems(Entry *e);
+	void removeUnneededContactItems(Entry *e);
+	void clearContactItems(Entry *e);
+
+	void removeEntry(Entry *);
+	Entry *findEntry(const Jid &) const;
+	Entry *findEntry(ContactViewItem *) const;
+
+	void ensureVisible(Entry *);
+
+	// useful functions to grab groups of users
+	JidList contactListFromCVGroup(ContactViewItem *) const;
+	int contactSizeFromCVGroup(ContactViewItem *) const;
+	int contactsOnlineFromCVGroup(ContactViewItem *) const;
+	JidList contactListFromGroup(const QString &groupName) const;
+	int contactSizeFromGroup(const QString &groupName) const;
+
+	void updateGroupInfo(ContactViewItem *group);
+	QStringList groupList() const;
+
+	void deferredUpdateGroups();
+
+	friend class ContactView;
+	void scActionDefault(ContactViewItem *);
+	void scRecvEvent(ContactViewItem *);
+	void scSendMessage(ContactViewItem *);
+	void scRename(ContactViewItem *);
+	void scVCard(ContactViewItem *);
+	void scHistory(ContactViewItem *);
+	void scOpenChat(ContactViewItem *);
+	void scAgentSetStatus(ContactViewItem *, Status &);
+	void scRemove(ContactViewItem *);
+	void doItemRenamed(ContactViewItem *, const QString &);
+	void doContextMenu(ContactViewItem *, const QPoint &);
+
+	friend class ContactViewItem;
+	void dragDrop(const QString &, ContactViewItem *);
+	void dragDropFiles(const QStringList &, ContactViewItem *);
+};
+
+// ContactView: the actual widget
+class ContactView : public QListView, public QToolTip
+{
+	Q_OBJECT
+public:
+	ContactView(QWidget *parent=0, const char *name=0);
+	~ContactView();
+
+	bool isShowOffline() const { return v_showOffline; }
+	bool isShowAgents() const { return v_showAgents; }
+	bool isShowAway() const { return v_showAway; }
+	bool isShowHidden() const { return v_showHidden; }
+	bool isShowSelf() const { return v_showSelf; }
+
+	void clear();
+	void resetAnim();
+	QTimer *animTimer() const;
+
+	IconAction *qa_send, *qa_chat, *qa_ren, *qa_hist, *qa_logon, *qa_recv, *qa_rem, *qa_vcard;
+	IconAction *qa_assignAvatar, *qa_clearAvatar;
+
+	QSize minimumSizeHint() const;
+	QSize sizeHint() const;
+
+protected:
+	// reimplemented
+	void maybeTip(const QPoint &);
+	void keyPressEvent(QKeyEvent *);
+	bool eventFilter( QObject *, QEvent * );
+	QDragObject *dragObject();
+
+signals:
+	void showOffline(bool);
+	void showAway(bool);
+	void showHidden(bool);
+	void showAgents(bool);
+	void showSelf(bool);
+
+public slots:
+	void setShowOffline(bool);
+	void setShowAgents(bool);
+	void setShowAway(bool);
+	void setShowHidden(bool);
+	void setShowSelf(bool);
+	void optionsUpdate();
+	void recalculateSize();
+
+private slots:
+	void qlv_singleclick(int, QListViewItem *, const QPoint &, int);
+	void qlv_doubleclick(QListViewItem *);
+	void qlv_contextPopup(QListViewItem *, const QPoint &, int);
+	void qlv_contextMenuRequested(QListViewItem *, const QPoint &, int);
+	void qlv_itemRenamed(QListViewItem *, int, const QString &);
+	void leftClickTimeOut();
+
+	void doRecvEvent();
+	void doRename();
+	void doEnter();
+	void doContext();
+	void doSendMessage();
+	void doOpenChat();
+	void doHistory();
+	void doVCard();
+	void doLogon();
+	void doRemove();
+
+	void doAssignAvatar();
+	void doClearAvatar();
+
+public:
+	class Private;
+	friend class Private;
+private:
+	Private *d;
+
+	QPoint mousePressPos; // store pressed position, idea taken from Licq
+	bool v_showOffline, v_showAgents, v_showAway, v_showHidden, v_showSelf;
+	bool lcto_active; // double click active?
+	QPoint lcto_pos;
+	QListViewItem *lcto_item;
+	QSize lastSize;
+
+	friend class ContactProfile;
+	void link(ContactProfile *);
+	void unlink(ContactProfile *);
+	bool allowResize() const;
+};
+
+// ContactViewItem: an entry in the ContactView (profile, group, or contact)
+class ContactViewItem : public QObject, public QListViewItem
+{
+	Q_OBJECT
+public:
+	enum { Profile, Group, Contact };
+	enum { gGeneral, gNotInList, gAgents, gPrivate, gUser };
+
+	ContactViewItem(const QString &profileName, ContactProfile *, ContactView *parent);
+	ContactViewItem(const QString &groupName, int groupType, ContactProfile *, ContactView *parent);
+	ContactViewItem(const QString &groupName, int groupType, ContactProfile *, ContactViewItem *parent);
+	ContactViewItem(UserListItem *, ContactProfile *, ContactViewItem *parent);
+	~ContactViewItem();
+
+	ContactProfile *contactProfile() const;
+	int type() const;
+	int groupType() const;
+	const QString & groupName() const;
+	const QString & groupInfo() const;
+	UserListItem *u() const;
+	int status() const;
+	bool isAgent() const;
+	bool isAlerting() const;
+	bool isAnimatingNick() const;
+	int parentGroupType() const; // use with contacts: returns grouptype of parent group
+
+	void setContact(UserListItem *);
+	void setProfileName(const QString &);
+	void setProfileState(int);
+	void setProfileSSL(bool);
+	void setGroupName(const QString &);
+	void setGroupInfo(const QString &);
+	void setAnimateNick();
+	void setAlert(const Icon *);
+	void clearAlert();
+	void setIcon(const Icon *, bool alert = false);
+
+	void resetStatus();
+	void resetName(); // use this to cancel a rename
+	void resetGroupName();
+
+	void updatePosition();
+	void optionsUpdate();
+
+	// reimplemented functions
+	int rtti() const;
+	void paintFocus(QPainter *, const QColorGroup &, const QRect &);
+	void paintBranches(QPainter *, const QColorGroup &, int, int, int);
+	void paintCell(QPainter *, const QColorGroup & cg, int column, int width, int alignment);
+	void setOpen(bool o);
+	void insertItem(QListViewItem * newChild);
+	void takeItem(QListViewItem * item);
+	int compare(QListViewItem *, int, bool) const;
+	bool acceptDrop(const QMimeSource *) const;
+
+public slots:
+	void resetAnim();
+	void iconUpdated(const QPixmap &);
+	void animateNick();
+	void stopAnimateNick();
+
+protected:
+	void dragEntered();
+	void dragLeft();
+	void dropped(QDropEvent *);
+
+private:
+	class Private;
+	Private *d;
+
+	void cacheValues();
+	int rankGroup(int groupType) const;
+	int rankStatus(int status) const;
+	void drawGroupIcon();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/discodlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/discodlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/discodlg.cpp	(revision 38)
@@ -0,0 +1,1363 @@
+/*
+ * discodlg.cpp - main dialog for the Service Discovery protocol
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "discodlg.h"
+
+#include <qlistview.h>
+#include <qcombobox.h>
+#include <qcheckbox.h>
+#include <qmessagebox.h>
+#include <qaction.h>
+#include <qstatusbar.h>
+#include <qpopupmenu.h>
+#include <qsignalmapper.h>
+#include <qpushbutton.h>
+#include <qdragobject.h>
+#include <qtooltip.h>
+#include <qtoolbutton.h>
+
+#include "im.h"
+#include "xmpp_tasks.h"
+
+#include "psitoolbar.h"
+#include "tasklist.h"
+#include "psiaccount.h"
+#include "psicon.h"
+#include "busywidget.h"
+#include "common.h"
+#include "iconaction.h"
+
+//----------------------------------------------------------------------------
+// DiscoData -- a shared data struct
+//----------------------------------------------------------------------------
+
+class DiscoListItem;
+class DiscoConnector : public QObject
+{
+	Q_OBJECT
+public:
+	DiscoConnector(QObject *parent)
+	: QObject(parent) {}
+
+signals:
+	void itemUpdated(QListViewItem *);
+
+private:
+	friend class DiscoListItem;
+};
+
+struct DiscoData {
+	PsiAccount *pa;
+	TaskList *tasks;
+	DiscoConnector *d;
+
+	enum Protocol {
+		Auto,
+
+		Disco,
+		Browse,
+		Agents
+	};
+	Protocol protocol;
+};
+
+//----------------------------------------------------------------------------
+// ProtocolAction
+//----------------------------------------------------------------------------
+
+class ProtocolAction : public QAction
+{
+	Q_OBJECT
+public:
+	ProtocolAction(QString text, QString toolTip, QObject *parent, QSignalMapper *sm, int parm);
+
+	bool addTo(QWidget *w);
+
+public slots:
+	void setOn(bool);
+private:
+	QToolButton *btn;
+};
+
+ProtocolAction::ProtocolAction(QString text, QString toolTip, QObject *parent, QSignalMapper *sm, int parm)
+: QAction(text, text, 0, parent)
+{
+	setText( text );
+	setToggleAction(true);
+	setToolTip(toolTip);
+	connect(this, SIGNAL(activated()), sm, SLOT(map()));
+	sm->setMapping(this, parm);
+	btn = 0;
+}
+
+bool ProtocolAction::addTo(QWidget *w)
+{
+	if ( w->inherits("QToolBar") ) {
+		if ( btn )
+			delete btn;
+		QCString bname = name() + QCString("_action_button");
+		btn = new QToolButton ( w, bname );
+
+		btn->setToggleButton ( isToggleAction() );
+		btn->setOn( isOn() );
+		btn->setTextLabel ( text() );
+		btn->setEnabled ( isEnabled() );
+		btn->setUsesTextLabel( true );
+		QToolTip::add(btn, toolTip());
+
+		connect(btn, SIGNAL(toggled(bool)), this, SLOT(setOn(bool)));
+
+		return true;
+	}
+
+	return false;
+}
+
+void ProtocolAction::setOn(bool b)
+{
+	if ( btn ) {
+		if ( b )
+			emit activated();
+		btn->setOn(b);
+	}
+
+	QAction::setOn(b);
+}
+
+//----------------------------------------------------------------------------
+// DiscoListItem
+//----------------------------------------------------------------------------
+
+class DiscoListItem : public QObject, public QListViewItem
+{
+	Q_OBJECT
+public:
+	DiscoListItem(DiscoItem it, DiscoData *d, QListView *parent);
+	DiscoListItem(DiscoItem it, DiscoData *d, QListViewItem *parent);
+	~DiscoListItem();
+
+	QString text(int columns) const;
+	void setOpen(bool open);
+	const DiscoItem &item() const;
+
+	void itemSelected();
+
+public slots: // the two are used internally by class, and also called by DiscoDlg::Private::refresh()
+	void updateInfo();
+	void updateItems(bool parentAutoItems = false);
+
+private slots:
+	void discoItemsFinished();
+	void discoInfoFinished();
+
+	void doBrowse(bool parentAutoItems = false);
+	void doAgents(bool parentAutoItems = false);
+	void browseFinished();
+	void agentsFinished();
+
+private:
+	DiscoItem di;
+	DiscoData *d;
+	bool isRoot;
+	bool alreadyItems, alreadyInfo;
+	bool autoItems; // used in updateItemsFinished
+	bool autoInfo;
+
+	void copyItem(const DiscoItem &);
+	void updateInfo(const DiscoItem &);
+	void updateItemsFinished(const DiscoList &);
+	void autoItemsChildren() const; // automatically call disco#items for children :-)
+	QString hash() { return computeHash( item().jid().full(), item().node() ); }
+	QString computeHash( QString jid, QString node );
+
+	// helper functions
+	void init(DiscoItem it, DiscoData *dd);
+
+	bool autoItemsEnabled() const;
+	bool autoInfoEnabled() const;
+	DiscoDlg *dlg() const;
+};
+
+DiscoListItem::DiscoListItem(DiscoItem it, DiscoData *_d, QListView *parent)
+: QListViewItem (parent)
+{
+	isRoot = true;
+
+	init(it, _d);
+}
+
+DiscoListItem::DiscoListItem(DiscoItem it, DiscoData *_d, QListViewItem *parent)
+: QListViewItem (parent)
+{
+	isRoot = false;
+
+	init(it, _d);
+}
+
+DiscoListItem::~DiscoListItem()
+{
+}
+
+void DiscoListItem::init(DiscoItem _item, DiscoData *_d)
+{
+	d = _d;
+	di = _item;
+	copyItem(_item);
+
+	alreadyItems = alreadyInfo = false;
+
+	if ( !autoItemsEnabled() )
+		setExpandable (true);
+
+	autoInfo = false;
+	if ( autoInfoEnabled() || isRoot ) {
+		updateInfo();
+		if ( !isRoot )
+			autoInfo = true;
+	}
+
+	//setDragEnabled(true); // EXPERIMENTAL
+}
+
+void DiscoListItem::copyItem(const DiscoItem &it)
+{
+	if ( !(!di.jid().full().isEmpty() && it.jid().full().isEmpty()) )
+		di.setJid ( it.jid() );
+	if ( !(!di.node().isEmpty() && it.node().isEmpty()) )
+		di.setNode ( it.node() );
+	if ( !(!di.name().isEmpty() && it.name().isEmpty()) )
+		di.setName ( it.name() );
+
+	di.setAction ( it.action() );
+
+	if ( !(!di.features().list().isEmpty() && it.features().list().isEmpty()) )
+		di.setFeatures ( it.features() );
+	if ( !(!di.identities().isEmpty() && it.identities().isEmpty()) )
+		di.setIdentities ( it.identities() );
+
+	if ( di.jid().userHost().left(4) == "jud." || di.jid().userHost().left(6) == "users." ) {
+		// nasty hack for the nasty (and outdated) JUD service :-/
+		if ( !di.features().canSearch() ) {
+			QStringList features = di.features().list();
+			features << "jabber:iq:search";
+			di.setFeatures( features );
+		}
+
+		bool found = false;
+		DiscoItem::Identities::ConstIterator it = di.identities().begin();
+		for ( ; it != di.identities().end(); ++it) {
+			if ( (*it).category == "service" && (*it).type == "jud" ) {
+				found = true;
+				break;
+			}
+		}
+		if ( !found ) {
+			DiscoItem::Identity id;
+			id.category = "service";
+			id.type     = "jud";
+			DiscoItem::Identities ids;
+			ids << id;
+			di.setIdentities( ids );
+		}
+	}
+
+	bool pixmapOk = false;
+	if ( !di.identities().isEmpty() ) {
+		DiscoItem::Identity id = di.identities().first();
+
+		if ( !id.category.isEmpty() ) {
+			Icon ic = category2icon(id.category, id.type);
+
+			if ( !ic.impix().isNull() ) {
+				setPixmap (0, ic.impix().pixmap());
+				pixmapOk = true;
+			}
+		}
+	}
+
+	if ( !pixmapOk )
+		setPixmap (0, is->status(di.jid(), STATUS_ONLINE));
+
+	repaint();
+
+	if ( isSelected() ) // update actions
+		emit d->d->itemUpdated( this );
+}
+
+QString DiscoListItem::text (int c) const
+{
+	if (c == 0)
+		return di.name();
+	else if (c == 1)
+		return di.jid().full();
+	else if (c == 2)
+		return di.node();
+	return "";
+}
+
+const DiscoItem &DiscoListItem::item() const
+{
+	return di;
+}
+
+DiscoDlg *DiscoListItem::dlg() const
+{
+	return (DiscoDlg *)listView()->parent()->parent();
+}
+
+bool DiscoListItem::autoItemsEnabled() const
+{
+	return dlg()->ck_autoItems->isChecked();
+}
+
+bool DiscoListItem::autoInfoEnabled() const
+{
+	return dlg()->ck_autoInfo->isChecked();
+}
+
+void DiscoListItem::setOpen (bool o)
+{
+	if ( o ) {
+		if ( !alreadyItems )
+			updateItems();
+		else
+			autoItemsChildren();
+	}
+
+	QListViewItem::setOpen(o);
+}
+
+void DiscoListItem::itemSelected()
+{
+	if ( !alreadyInfo )
+		updateInfo();
+}
+
+void DiscoListItem::updateItems(bool parentAutoItems)
+{
+	if ( parentAutoItems ) {
+		// save traffic
+		if ( alreadyItems )
+			return;
+
+		// FIXME: currently, JUD doesn't seem to answer to browsing requests
+		if ( item().identities().size() ) {
+			DiscoItem::Identity id = item().identities().first();
+			if ( id.category == "service" && id.type == "jud" )
+				return;
+		}
+		QString j = item().jid().host(); // just another method to discover if we're gonna to browse JUD
+		if ( item().jid().user().isEmpty() && (j.left(4) == "jud." || j.left(6) == "users.") )
+			return;
+	}
+
+	autoItems = !parentAutoItems;
+
+	if ( !autoItemsEnabled() )
+		autoItems = false;
+
+	if ( d->protocol == DiscoData::Auto || d->protocol == DiscoData::Disco ) {
+		JT_DiscoItems *jt = new JT_DiscoItems(d->pa->client()->rootTask());
+		connect(jt, SIGNAL(finished()), SLOT(discoItemsFinished()));
+		jt->get(di.jid(), di.node());
+		jt->go(true);
+		d->tasks->append(jt);
+	}
+	else if ( d->protocol == DiscoData::Browse )
+		doBrowse(parentAutoItems);
+	else if ( d->protocol == DiscoData::Agents )
+		doAgents(parentAutoItems);
+}
+
+void DiscoListItem::discoItemsFinished()
+{
+	JT_DiscoItems *jt = (JT_DiscoItems *)sender();
+
+	if ( jt->success() ) {
+		updateItemsFinished(jt->items());
+	}
+	else if ( d->protocol == DiscoData::Auto ) {
+		doBrowse();
+		return;
+	}
+	else if ( !autoItems ) {
+		QString error = jt->statusString();
+		QMessageBox::critical(dlg(), tr("Error"), tr("There was an error getting items for <b>%1</b>.\nReason: %2").arg(di.jid().full()).arg(error));
+	}
+
+	alreadyItems = true;
+}
+
+void DiscoListItem::doBrowse(bool parentAutoItems)
+{
+	if ( parentAutoItems ) {
+		// save traffic
+		if ( alreadyItems )
+			return;
+
+		if ( item().identities().size() ) {
+			DiscoItem::Identity id = item().identities().first();
+			if ( id.category == "service" && id.type == "jud" )
+				return;
+		}
+	}
+
+	autoItems = !parentAutoItems;
+	if ( !autoItemsEnabled() )
+		autoItems = false;
+
+	JT_Browse *jt = new JT_Browse(d->pa->client()->rootTask());
+	connect(jt, SIGNAL(finished()), SLOT(browseFinished()));
+	jt->get(di.jid());
+	jt->go(true);
+	d->tasks->append(jt);
+}
+
+void DiscoListItem::browseFinished()
+{
+	JT_Browse *jt = (JT_Browse *)sender();
+
+	if ( jt->success() ) {
+		// update info
+		DiscoItem root;
+		root.fromAgentItem( jt->root() );
+		updateInfo(root);
+		alreadyInfo = true;
+		autoInfo = false;
+
+		// update items
+		AgentList from = jt->agents();
+		DiscoList to;
+		AgentList::Iterator it = from.begin();
+		for ( ; it != from.end(); ++it) {
+			DiscoItem item;
+			item.fromAgentItem( *it );
+
+			to.append( item );
+		}
+
+		updateItemsFinished(to);
+	}
+	else if ( d->protocol == DiscoData::Auto ) {
+		doAgents();
+		return;
+	}
+	else if ( !autoItems ) {
+		QString error = jt->statusString();
+		QMessageBox::critical(dlg(), tr("Error"), tr("There was an error browsing items for <b>%1</b>.\nReason: %2").arg(di.jid().full()).arg(error));
+	}
+
+	alreadyItems = true;
+}
+
+void DiscoListItem::doAgents(bool parentAutoItems)
+{
+	if ( parentAutoItems ) {
+		// save traffic
+		if ( alreadyItems )
+			return;
+
+		if ( item().identities().size() ) {
+			DiscoItem::Identity id = item().identities().first();
+			if ( id.category == "service" && id.type == "jud" )
+				return;
+		}
+	}
+
+	autoItems = !parentAutoItems;
+	if ( !autoItemsEnabled() )
+		autoItems = false;
+
+	JT_GetServices *jt = new JT_GetServices(d->pa->client()->rootTask());
+	connect(jt, SIGNAL(finished()), SLOT(agentsFinished()));
+	jt->get(di.jid());
+	jt->go(true);
+	d->tasks->append(jt);
+}
+
+void DiscoListItem::agentsFinished()
+{
+	JT_GetServices *jt = (JT_GetServices *)sender();
+
+	if ( jt->success() ) {
+		// update info
+		DiscoItem root;
+		DiscoItem::Identity id;
+		id.name     = tr("Jabber Service");
+		id.category = "service";
+		id.type     = "jabber";
+		DiscoItem::Identities ids;
+		ids.append(id);
+		root.setIdentities(ids);
+		updateInfo(root);
+		alreadyInfo = true;
+		autoInfo = false;
+
+		// update items
+		AgentList from = jt->agents();
+		DiscoList to;
+		AgentList::Iterator it = from.begin();
+		for ( ; it != from.end(); ++it) {
+			DiscoItem item;
+			item.fromAgentItem( *it );
+
+			to.append( item );
+		}
+
+		updateItemsFinished(to);
+	}
+	else if ( !autoItems ) {
+		QString error = jt->statusString();
+		QMessageBox::critical(dlg(), tr("Error"), tr("There was an error getting agents for <b>%1</b>.\nReason: %2").arg(di.jid().full()).arg(error));
+	}
+
+	alreadyItems = true;
+}
+
+QString DiscoListItem::computeHash( QString jid, QString node )
+{
+	QString ret = jid.replace( '@', "\\@" );
+	ret += "@";
+	ret += node.replace( '@', "\\@" );
+	return ret;
+}
+
+void DiscoListItem::updateItemsFinished(const DiscoList &list)
+{
+	QDict<DiscoListItem> children;
+	DiscoListItem *child = (DiscoListItem *)firstChild();
+	while ( child ) {
+		children.insert( child->hash(), child );
+
+		child = (DiscoListItem *)child->nextSibling();
+	}
+
+	// add/update items
+	for(DiscoList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+		const DiscoItem a = *it;
+
+		QString key = computeHash(a.jid().full(), a.node());
+		DiscoListItem *child = children[ key ];
+
+		if ( child ) {
+			child->copyItem ( a );
+			children.remove( key );
+		}
+		else {
+			new DiscoListItem (a, d, this);
+		}
+	}
+
+	// remove all items that are not on new DiscoList
+	children.setAutoDelete( true );
+	children.clear();
+
+	if ( autoItems && isOpen() )
+		autoItemsChildren();
+
+	// don't forget to remove '+' (or '-') sign in case, that the child list is empty
+	setExpandable ( !list.isEmpty() );
+
+	repaint();
+
+	// root item is initially hidden
+	if ( isRoot && !isVisible() )
+		setVisible (true);
+}
+
+void DiscoListItem::autoItemsChildren() const
+{
+	if ( !autoItemsEnabled() )
+		return;
+
+	DiscoListItem *child = (DiscoListItem *)firstChild();
+	while ( child ) {
+		child->updateItems(true);
+
+		child = (DiscoListItem *)child->nextSibling();
+	}
+}
+
+void DiscoListItem::updateInfo()
+{
+	if ( d->protocol != DiscoData::Auto && d->protocol != DiscoData::Disco )
+		return;
+
+	JT_DiscoInfo *jt = new JT_DiscoInfo(d->pa->client()->rootTask());
+	connect(jt, SIGNAL(finished()), SLOT(discoInfoFinished()));
+	jt->get(di.jid(), di.node());
+	jt->go(true);
+	d->tasks->append(jt);
+}
+
+void DiscoListItem::discoInfoFinished()
+{
+	JT_DiscoInfo *jt = (JT_DiscoInfo *)sender();
+
+	if ( jt->success() ) {
+		updateInfo( jt->item() );
+	}
+	else if ( !autoInfo && d->protocol != DiscoData::Auto ) {
+		QString error = jt->statusString();
+		QMessageBox::critical(dlg(), tr("Error"), tr("There was an error getting item's info for <b>%1</b>.\nReason: %2").arg(di.jid().full()).arg(error));
+	}
+
+	alreadyInfo = true;
+	autoInfo = false;
+}
+
+void DiscoListItem::updateInfo(const DiscoItem &item)
+{
+	copyItem( item );
+
+	if ( isRoot && !isVisible() )
+		setVisible (true);
+}
+
+//----------------------------------------------------------------------------
+// DiscoList
+//----------------------------------------------------------------------------
+
+class DiscoListView : public QListView, public QToolTip
+{
+	Q_OBJECT
+public:
+	DiscoListView(QWidget *parent);
+
+protected:
+	void maybeTip(const QPoint &);
+	QDragObject *dragObject();
+};
+
+DiscoListView::DiscoListView(QWidget *parent)
+: QListView(parent), QToolTip(viewport())
+{
+	addColumn( tr( "Name" ) );
+	addColumn( tr( "JID" ) );
+	addColumn( tr( "Node" ) );
+}
+
+void DiscoListView::maybeTip(const QPoint &pos)
+{
+	DiscoListItem *i = (DiscoListItem *)itemAt(pos);
+	if(!i)
+		return;
+
+	// NAME <JID> (Node "NODE")
+	//
+	// Identities:
+	// (icon) NAME (Category "CATEGORY"; Type "TYPE")
+	// (icon) NAME (Category "CATEGORY"; Type "TYPE")
+	//
+	// Features:
+	// NAME (http://jabber.org/feature)
+	// NAME (http://jabber.org/feature)
+
+	// top row
+	QString text = "<qt><nobr>";
+	DiscoItem item = i->item();
+
+	if ( !item.name().isEmpty() )
+		text += item.name() + " ";
+
+	text += "&lt;" + item.jid().full() + "&gt;";
+
+	if ( !item.node().isEmpty() )
+		text += " (" + tr("Node") + " \"" + item.node() + "\")";
+
+	text += "<nobr>";
+
+	if ( !item.identities().isEmpty() || !item.features().list().isEmpty() )
+		text += "<br>\n";
+
+	// identities
+	if ( !item.identities().isEmpty() ) {
+		text += "<br>\n<b>" + tr("Identities:") + "</b>\n";
+
+		DiscoItem::Identities::ConstIterator it = item.identities().begin();
+		for ( ; it != item.identities().end(); ++it) {
+			text += "<br>";
+			Icon icon( category2icon((*it).category, (*it).type) );
+			if ( !icon.name().isEmpty() )
+				text += "<icon name=\"" + icon.name() + "\"> ";
+			text += (*it).name;
+			text += " (" + tr("Category") + " \"" + (*it).category + "\"; " + tr("Type") + " \"" + (*it).type + "\")\n";
+		}
+
+		if ( !item.features().list().isEmpty() )
+			text += "<br>\n";
+	}
+
+	// features
+	if ( !item.features().list().isEmpty() ) {
+		text += "<br>\n<b>" + tr("Features:") + "</b>\n";
+
+		QStringList features = item.features().list();
+		QStringList::ConstIterator it = features.begin();
+		for ( ; it != features.end(); ++it) {
+			Features f( *it );
+			text += "\n<br>";
+			if ( f.id() > Features::FID_None )
+				text += f.name() + " (";
+			text += *it;
+			if ( f.id() > Features::FID_None )
+				text += ")";
+		}
+	}
+
+	text += "</qt>";
+	QRect r( itemRect(i) );
+	tip(r, text);
+}
+
+QDragObject *DiscoListView::dragObject()
+{
+	DiscoListItem *i = (DiscoListItem *)selectedItem();
+	if(!i)
+		return 0;
+
+	QDragObject *d = new QTextDrag(i->item().jid().full(), this);
+	d->setPixmap(IconsetFactory::icon("status/online"), QPoint(8,8));
+	return d;
+}
+
+//----------------------------------------------------------------------------
+// DiscoDlg::Private
+//----------------------------------------------------------------------------
+
+class DiscoDlg::Private : public QObject
+{
+	Q_OBJECT
+
+private:
+	// helper class for use in toolbar
+	class StretchWidget : public QWidget
+	{
+	public:
+		StretchWidget(QWidget *parent)
+		: QWidget(parent)
+		{
+			setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+		}
+	};
+
+	// helper class to store browser history
+	class History {
+	private:
+		QListViewItem *item;
+	public:
+		History(QListViewItem *it) {
+			item = it;
+		}
+
+		~History() {
+			if ( item )
+				delete item;
+		}
+
+		QListViewItem *takeItem() {
+			QListViewItem *i = item;
+			item = 0;
+			return i;
+		}
+	};
+
+public: // data
+	DiscoDlg *dlg;
+	Jid jid;
+	QString node;
+
+	DiscoData data;
+
+	PsiToolBar *toolBar;
+	IconAction *actBrowse, *actBack, *actForward, *actRefresh, *actStop;
+
+	// custom actions, that will be added to toolbar and context menu
+	IconAction *actRegister, *actSearch, *actJoin, *actVCard, *actAdd;
+
+	typedef QPtrList<History> HistoryList;
+	HistoryList backHistory, forwardHistory;
+
+	BusyWidget *busy;
+
+public: // functions
+	Private(DiscoDlg *parent, PsiAccount *pa);
+	~Private();
+
+public slots:
+	void doDisco(QString host = QString::null, QString node = QString::null);
+
+	void actionStop();
+	void actionRefresh();
+	void actionBrowse();
+
+	void actionBack();
+	void actionForward();
+	void updateBackForward();
+	void backForwardHelper(QListViewItem *);
+
+	void updateComboBoxes(Jid j, QString node);
+
+	void itemUpdateStarted();
+	void itemUpdateFinished();
+
+	void disableButtons();
+	void enableButtons(const DiscoItem &);
+
+	void itemSelected (QListViewItem *);
+	void itemDoubleclicked (QListViewItem *);
+	bool eventFilter (QObject *, QEvent *);
+
+	void setProtocol(int);
+
+	// features...
+	void actionActivated(int);
+
+	void objectDestroyed(QObject *);
+
+private:
+	friend class DiscoListItem;
+};
+
+DiscoDlg::Private::Private(DiscoDlg *parent, PsiAccount *pa)
+{
+	dlg = parent;
+	data.pa = pa;
+	data.tasks = new TaskList;
+	connect(data.tasks, SIGNAL(started()),  SLOT(itemUpdateStarted()));
+	connect(data.tasks, SIGNAL(finished()), SLOT(itemUpdateFinished()));
+	data.d = new DiscoConnector(this);
+	connect(data.d, SIGNAL(itemUpdated(QListViewItem *)), SLOT(itemSelected (QListViewItem *)));
+	data.protocol = DiscoData::Auto;
+
+	backHistory.setAutoDelete(true);
+	forwardHistory.setAutoDelete(true);
+
+	// mess with widgets
+	busy = parent->busy;
+	connect(busy, SIGNAL(destroyed(QObject *)), SLOT(objectDestroyed(QObject *)));
+
+	QListView *lv_discoOld = dlg->lv_disco;
+	dlg->lv_disco = new DiscoListView(dlg->centralWidget());
+	replaceWidget(lv_discoOld, dlg->lv_disco);
+
+	dlg->lv_disco->installEventFilter (this);
+	connect(dlg->lv_disco, SIGNAL(selectionChanged (QListViewItem *)), SLOT(itemSelected (QListViewItem *)));;
+	connect(dlg->lv_disco, SIGNAL(doubleClicked (QListViewItem *)),    SLOT(itemDoubleclicked (QListViewItem *)));;
+
+	// protocol actions
+	QSignalMapper *pm = new QSignalMapper(this);
+	connect(pm, SIGNAL(mapped(int)), SLOT(setProtocol(int)));
+	QActionGroup *protocolActions = new QActionGroup (this);
+	protocolActions->setExclusive(true);
+
+	ProtocolAction *autoProtocol = new ProtocolAction (tr("Auto"), tr("Automatically determine protocol"), protocolActions, pm, DiscoData::Auto);
+	ProtocolAction *discoProtocol = new ProtocolAction ("D", tr("Service Discovery"), protocolActions, pm, DiscoData::Disco);
+	ProtocolAction *browseProtocol = new ProtocolAction ("B", tr("Browse Services"), protocolActions, pm, DiscoData::Browse);
+	ProtocolAction *agentsProtocol = new ProtocolAction ("A", tr("Browse Agents"), protocolActions, pm, DiscoData::Agents);
+	autoProtocol->setOn(true);
+
+	// create actions
+	actBrowse = new IconAction (tr("Browse"), "psi/jabber", tr("&Browse"), 0, dlg);
+	connect (actBrowse, SIGNAL(activated()), SLOT(actionBrowse()));
+	actRefresh = new IconAction (tr("Refresh Item"), "psi/reload", tr("&Refresh Item"), 0, dlg);
+	connect (actRefresh, SIGNAL(activated()), SLOT(actionRefresh()));
+	actStop = new IconAction (tr("Stop"), "psi/stop", tr("Sto&p"), 0, dlg);
+	connect (actStop, SIGNAL(activated()), SLOT(actionStop()));
+	actBack = new IconAction (tr("Back"), "psi/arrowLeft", tr("&Back"), 0, dlg);
+	connect (actBack, SIGNAL(activated()), SLOT(actionBack()));
+	actForward = new IconAction (tr("Forward"), "psi/arrowRight", tr("&Forward"), 0, dlg);
+	connect (actForward, SIGNAL(activated()), SLOT(actionForward()));
+
+	// custom actions
+	QSignalMapper *sm = new QSignalMapper(this);
+	connect(sm, SIGNAL(mapped(int)), SLOT(actionActivated(int)));
+	actRegister = new IconAction (tr("Register"), "psi/register", tr("&Register"), 0, dlg);
+	connect (actRegister, SIGNAL(activated()), sm, SLOT(map()));
+	sm->setMapping(actRegister, Features::FID_Register);
+	actSearch = new IconAction (tr("Search"), "psi/search", tr("&Search"), 0, dlg);
+	connect (actSearch, SIGNAL(activated()), sm, SLOT(map()));
+	sm->setMapping(actSearch, Features::FID_Search);
+	actJoin = new IconAction (tr("Join"), "psi/groupChat", tr("&Join"), 0, dlg);
+	connect (actJoin, SIGNAL(activated()), sm, SLOT(map()));
+	sm->setMapping(actJoin, Features::FID_Groupchat);
+	actVCard = new IconAction (tr("vCard"), "psi/vCard", tr("&vCard"), 0, dlg);
+	connect (actVCard, SIGNAL(activated()), sm, SLOT(map()));
+	sm->setMapping(actVCard, Features::FID_VCard);
+	actAdd = new IconAction (tr("Add to roster"), "psi/addContact", tr("&Add to roster"), 0, dlg);
+	connect (actAdd, SIGNAL(activated()), sm, SLOT(map()));
+	sm->setMapping(actAdd, Features::FID_Add);
+
+	// create toolbar
+	toolBar = new PsiToolBar(dlg);
+	toolBar->setCustomizeable( false );
+
+	actBack->addTo(toolBar);
+	actBrowse->addTo(toolBar);
+	actForward->addTo(toolBar);
+
+	toolBar->addSeparator();
+	actRefresh->addTo(toolBar);
+	actStop->addTo(toolBar);
+
+	// custom actions
+	toolBar->addSeparator();
+	actRegister->addTo(toolBar);
+	actSearch->addTo(toolBar);
+	actJoin->addTo(toolBar);
+
+	toolBar->addSeparator();
+	actAdd->addTo(toolBar);
+	actVCard->addTo(toolBar);
+
+	// select protocol
+	toolBar->addSeparator();
+	autoProtocol->addTo(toolBar);
+	discoProtocol->addTo(toolBar);
+	browseProtocol->addTo(toolBar);
+	agentsProtocol->addTo(toolBar);
+
+	toolBar->setStretchableWidget(new StretchWidget(toolBar));
+	pa->accountLabel(toolBar, true);
+
+	// misc stuff
+	disableButtons();
+	actStop->setEnabled(false);	// stop action is not handled by disableButtons()
+	updateBackForward();		// same applies to back & forward
+}
+
+DiscoDlg::Private::~Private()
+{
+	delete data.tasks;
+}
+
+void DiscoDlg::Private::doDisco(QString _host, QString _node)
+{
+	PsiAccount *pa = data.pa;
+	if ( !pa->checkConnected(dlg) )
+		return;
+
+	// Strip whitespace
+	Jid j;
+	QString host = _host;
+	if ( host.isEmpty() )
+		host = dlg->cb_address->currentText();
+	j = host.stripWhiteSpace();
+	if ( !j.isValid() )
+		return;
+
+	QString n = _node.stripWhiteSpace();
+	if ( n.isEmpty() )
+		n = dlg->cb_node->currentText().stripWhiteSpace();
+
+	// check, whether we need to update history
+	if ( (jid.full() != j.full()) || (node != n) ) {
+		QListViewItem *item = dlg->lv_disco->firstChild(); // get the root item
+
+		if ( item ) {
+			dlg->lv_disco->takeItem( item );
+
+			backHistory.append( new History(item) );
+			forwardHistory.clear();
+		}
+	}
+
+	jid  = j;
+	node = n;
+
+	updateComboBoxes(jid, node);
+
+	data.tasks->clear(); // also will call all all necessary functions
+	disableButtons();
+	updateBackForward();
+
+	dlg->lv_disco->clear();
+
+	// create new root item
+	DiscoItem di;
+	di.setJid( jid );
+	di.setNode( node );
+
+	DiscoListItem *root = new DiscoListItem (di, &data, dlg->lv_disco);
+	root->setVisible (false); // don't confuse users with empty root
+
+	root->setOpen(true); // begin browsing
+}
+
+void DiscoDlg::Private::updateComboBoxes(Jid j, QString n)
+{
+	data.pa->psi()->recentBrowseAdd( j.full() );
+	dlg->cb_address->clear();
+	dlg->cb_address->insertStringList(data.pa->psi()->recentBrowseList());
+
+	data.pa->psi()->recentNodeAdd( n );
+	dlg->cb_node->clear();
+	dlg->cb_node->insertStringList(data.pa->psi()->recentNodeList());
+}
+
+void DiscoDlg::Private::actionStop()
+{
+	data.tasks->clear();
+}
+
+void DiscoDlg::Private::actionRefresh()
+{
+	DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
+	if ( !it )
+		return;
+
+	it->updateItems();
+	it->updateInfo();
+}
+
+void DiscoDlg::Private::actionBrowse()
+{
+	DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
+	if ( !it )
+		return;
+
+	doDisco(it->item().jid().full(), it->item().node());
+}
+
+void DiscoDlg::Private::actionBack()
+{
+	// add current selection to forward history
+	QListViewItem *item = dlg->lv_disco->firstChild();
+	if ( item ) {
+		dlg->lv_disco->takeItem( item );
+
+		forwardHistory.append( new History(item) );
+	}
+
+	// now, take info from back history...
+	QListViewItem *i = backHistory.last()->takeItem();
+	backHistory.removeLast();
+
+	// and restore view
+	backForwardHelper(i);
+}
+
+void DiscoDlg::Private::actionForward()
+{
+	// add current selection to back history
+	QListViewItem *item = dlg->lv_disco->firstChild();
+	if ( item ) {
+		dlg->lv_disco->takeItem( item );
+
+		backHistory.append( new History(item) );
+	}
+
+	// now, take info from forward history...
+	QListViewItem *i = forwardHistory.last()->takeItem();
+	forwardHistory.removeLast();
+
+	// and restore view
+	backForwardHelper(i);
+}
+
+void DiscoDlg::Private::backForwardHelper(QListViewItem *root)
+{
+	DiscoListItem *i = (DiscoListItem *)root;
+
+	jid  = i->item().jid();
+	node = i->item().node();
+
+	updateComboBoxes(jid, node);
+
+	data.tasks->clear(); // also will call all all necessary functions
+	disableButtons();
+	updateBackForward();
+
+	dlg->lv_disco->insertItem( root );
+
+	// fixes multiple selection bug
+	QListViewItemIterator it( dlg->lv_disco );
+	while ( it.current() ) {
+		QListViewItem *item = it.current();
+		++it;
+
+		if ( item->isSelected() )
+			for (int i = 0; i <= 1; i++) // it's boring to write same line twice :-)
+				dlg->lv_disco->setSelected(item, (bool)i);
+	}
+}
+
+void DiscoDlg::Private::updateBackForward()
+{
+	actBack->setEnabled ( !backHistory.isEmpty() );
+	actForward->setEnabled ( !forwardHistory.isEmpty() );
+}
+
+void DiscoDlg::Private::itemUpdateStarted()
+{
+	actStop->setEnabled(true);
+	if ( busy )
+		busy->start();
+}
+
+void DiscoDlg::Private::itemUpdateFinished()
+{
+	actStop->setEnabled(false);
+	if ( busy )
+		busy->stop();
+}
+
+void DiscoDlg::Private::disableButtons()
+{
+	DiscoItem di;
+	enableButtons ( di );
+}
+
+void DiscoDlg::Private::enableButtons(const DiscoItem &it)
+{
+	bool itemSelected = !it.jid().full().isEmpty();
+	actRefresh->setEnabled( itemSelected );
+	actBrowse->setEnabled( itemSelected );
+
+	// custom actions
+	Features f = it.features();
+	actRegister->setEnabled( f.canRegister() );
+	actSearch->setEnabled( f.canSearch() );
+	actJoin->setEnabled( f.canGroupchat() );
+	actAdd->setEnabled( itemSelected );
+	actVCard->setEnabled( f.haveVCard() );
+}
+
+void DiscoDlg::Private::itemSelected (QListViewItem *item)
+{
+	DiscoListItem *it = (DiscoListItem *)item;
+	if ( !it ) {
+		disableButtons();
+		return;
+	}
+
+	it->itemSelected();
+
+	const DiscoItem di = it->item();
+	enableButtons ( di );
+}
+
+void DiscoDlg::Private::itemDoubleclicked (QListViewItem *item)
+{
+	DiscoListItem *it = (DiscoListItem *)item;
+	if ( !it )
+		return;
+
+	const DiscoItem d = it->item();
+	const Features &f = d.features();
+
+	// set the prior state of item
+	// FIXME: causes minor flickering
+	if ( f.canGroupchat() || f.canRegister() || f.canSearch() ) {
+		if ( !it->isOpen() ) {
+			if ( it->isExpandable() || it->childCount() )
+				it->setOpen( true );
+		}
+		else {
+			it->setOpen( false );
+		}
+	}
+
+	long id = 0;
+
+	// trigger default action
+	if ( f.canGroupchat() ) {
+		id = Features::FID_Groupchat;
+	}
+	else {
+		// FIXME: check the category and type for JUD!
+		DiscoItem::Identity ident = d.identities().first();
+		bool searchFirst = ident.category == "service" && ident.type == "jud";
+
+		if ( searchFirst && f.canSearch() ) {
+			id = Features::FID_Search;
+		}
+		else {
+			if ( f.canRegister() )
+				id = Features::FID_Register;
+		}
+	}
+
+	if ( id > 0 )
+		emit dlg->featureActivated( Features::feature(id), d.jid(), d.node() );
+}
+
+bool DiscoDlg::Private::eventFilter (QObject *object, QEvent *event)
+{
+	if ( object == dlg->lv_disco ) {
+		if ( event->type() == QEvent::ContextMenu ) {
+			QContextMenuEvent *e = (QContextMenuEvent *)event;
+
+			DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
+			if ( !it )
+				return true;
+
+			// prepare features list
+			QValueList<long> idFeatures;
+			QStringList features = it->item().features().list();
+			{	// convert all features to their IDs
+				QStringList::Iterator it = features.begin();
+				for ( ; it != features.end(); ++it) {
+					Features f( *it );
+					if ( f.id() > Features::FID_None )
+						idFeatures.append( Features::id(*it) );
+				}
+				//qHeapSort(idFeatures);
+			}
+
+			QValueList<long> ids;
+			{	// ensure, that there's in no duplicated IDs inside. FIXME: optimize this, anyone?
+				long id = 0, count = 0;
+				QValueList<long>::Iterator it;
+				while ( count < (long)idFeatures.count() ) {
+					bool found = false;
+
+					for (it = idFeatures.begin(); it != idFeatures.end(); ++it) {
+						if ( id == *it ) {
+							if ( !found ) {
+								found = true;
+								ids.append( id );
+							}
+							count++;
+						}
+					}
+					id++;
+				}
+			}
+
+			// prepare popup menu
+			QPopupMenu p;
+
+			actBrowse->addTo (&p);
+			actRefresh->addTo (&p);
+			actStop->addTo (&p);
+
+			// custom actions
+			p.insertSeparator();
+			actRegister->addTo(&p);
+			actSearch->addTo(&p);
+			actJoin->addTo(&p);
+
+			p.insertSeparator();
+			actAdd->addTo(&p);
+			actVCard->addTo(&p);
+
+			// popup with all available features
+			QPopupMenu *fm = new QPopupMenu(&p);
+			{
+				QValueList<long>::Iterator it = ids.begin();
+				for ( ; it != ids.end(); ++it)
+					fm->insertItem(Features::name(*it), *it + 10000); // TODO: add pixmap
+			}
+
+			//p.insertSeparator();
+			//int menuId = p.insertItem(tr("Activate &Feature"), fm);
+			//p.setItemEnabled(menuId, !ids.isEmpty());
+
+			// display popup
+			e->accept();
+			int r = p.exec ( e->globalPos() );
+
+			if ( r > 10000 )
+				actionActivated(r-10000);
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+void DiscoDlg::Private::actionActivated(int id)
+{
+	DiscoListItem *it = (DiscoListItem *)dlg->lv_disco->selectedItem();
+	if ( !it )
+		return;
+
+	emit dlg->featureActivated(Features::feature(id), it->item().jid(), it->item().node());
+}
+
+void DiscoDlg::Private::objectDestroyed(QObject *obj)
+{
+	if ( obj == busy )
+		busy = 0;
+}
+
+void DiscoDlg::Private::setProtocol(int p)
+{
+	data.protocol = (DiscoData::Protocol)p;
+}
+
+//----------------------------------------------------------------------------
+// DiscoDlg
+//----------------------------------------------------------------------------
+
+DiscoDlg::DiscoDlg(PsiAccount *pa, const Jid &jid, const QString &node)
+: DiscoUI (0, 0, WDestructiveClose)
+{
+	// restore options
+	ck_autoItems->setChecked(option.discoItems);
+	ck_autoInfo->setChecked(option.discoInfo);
+
+	// initialize
+	d = new Private(this, pa);
+	d->jid  = jid;
+	d->node = node;
+
+	setCaption(CAP(caption()));
+	setIcon(is->transportStatus("transport", STATUS_ONLINE));
+	X11WM_CLASS("disco");
+
+	statusBar()->hide();
+
+	pb_browse->setDefault(true);
+	connect (pb_browse, SIGNAL(clicked()), d, SLOT(doDisco()));
+
+
+	cb_address->setInsertionPolicy(QComboBox::NoInsertion);
+	cb_address->insertStringList(pa->psi()->recentBrowseList()); // FIXME
+	cb_address->setFocus();
+	connect(cb_address, SIGNAL(activated(const QString &)), d, SLOT(doDisco()));
+	cb_address->setCurrentText(d->jid.full());
+
+	cb_node->setInsertionPolicy(QComboBox::NoInsertion);
+	cb_node->insertStringList(pa->psi()->recentNodeList());
+	connect(cb_node, SIGNAL(activated(const QString &)), d, SLOT(doDisco()));
+	cb_node->setCurrentText(node);
+
+	if ( pa->loggedIn() )
+		doDisco();
+}
+
+DiscoDlg::~DiscoDlg()
+{
+	delete d;
+
+	// save options
+	option.discoItems = ck_autoItems->isChecked();
+	option.discoInfo  = ck_autoInfo->isChecked();
+}
+
+void DiscoDlg::doDisco(QString host, QString node)
+{
+	d->doDisco(host, node);
+}
+
+#include "discodlg.moc"
Index: psi/tags/0.10-os2-b1/src/discodlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/discodlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/discodlg.h	(revision 38)
@@ -0,0 +1,52 @@
+/*
+ * discodlg.h - main dialog for the Service Discovery protocol
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef DISCODLG_H
+#define DISCODLG_H
+
+#include "ui_disco.h"
+
+class PsiAccount;
+
+#include "xmpp.h"
+using namespace XMPP;
+
+class DiscoDlg : public DiscoUI
+{
+	Q_OBJECT
+public:
+	DiscoDlg(PsiAccount *, const Jid &, const QString &node = QString::null);
+	~DiscoDlg();
+
+	void doDisco(QString host = QString::null, QString node = QString::null);
+
+signals:
+	void featureActivated(QString feature, Jid jid, QString node);
+
+public:
+	class Private;
+	friend class Private;
+private:
+	Private *d;
+};
+
+
+#endif
+
Index: psi/tags/0.10-os2-b1/src/eventdb.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/eventdb.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/eventdb.cpp	(revision 38)
@@ -0,0 +1,862 @@
+/*
+ * eventdb.cpp - asynchronous I/O event database
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"eventdb.h"
+
+#define FAKEDELAY 0
+
+#include<qfile.h>
+#include<qptrlist.h>
+#include<qtimer.h>
+#include<qtextstream.h>
+#include<qvaluevector.h>
+#include"common.h"
+#include"profiles.h"
+
+
+//----------------------------------------------------------------------------
+// EDBItem
+//----------------------------------------------------------------------------
+EDBItem::EDBItem(PsiEvent *event, const QString &id, const QString &prevId, const QString &nextId)
+{
+	e = event;
+	v_id = id;
+	v_prevId = prevId;
+	v_nextId = nextId;
+}
+
+EDBItem::~EDBItem()
+{
+	delete e;
+}
+
+PsiEvent *EDBItem::event() const
+{
+	return e;
+}
+
+const QString & EDBItem::id() const
+{
+	return v_id;
+}
+
+const QString & EDBItem::nextId() const
+{
+	return v_nextId;
+}
+
+const QString & EDBItem::prevId() const
+{
+	return v_prevId;
+}
+
+
+//----------------------------------------------------------------------------
+// EDBHandle
+//----------------------------------------------------------------------------
+class EDBHandle::Private
+{
+public:
+	Private() {}
+
+	EDB *edb;
+	EDBResult *r;
+	bool busy;
+	bool writeSuccess;
+	int listeningFor;
+	int lastRequestType;
+};
+
+EDBHandle::EDBHandle(EDB *edb)
+:QObject(0)
+{
+	d = new Private;
+	d->edb = edb;
+	d->r = 0;
+	d->busy = false;
+	d->writeSuccess = false;
+	d->listeningFor = -1;
+	d->lastRequestType = Read;
+
+	d->edb->reg(this);
+}
+
+EDBHandle::~EDBHandle()
+{
+	d->edb->unreg(this);
+
+	delete d->r;
+	delete d;
+}
+
+void EDBHandle::getLatest(const Jid &j, int len)
+{
+	d->busy = true;
+	d->lastRequestType = Read;
+	d->listeningFor = d->edb->op_getLatest(j, len);
+}
+
+void EDBHandle::getOldest(const Jid &j, int len)
+{
+	d->busy = true;
+	d->lastRequestType = Read;
+	d->listeningFor = d->edb->op_getOldest(j, len);
+}
+
+void EDBHandle::get(const Jid &j, const QString &id, int direction, int len)
+{
+	d->busy = true;
+	d->lastRequestType = Read;
+	d->listeningFor = d->edb->op_get(j, id, direction, len);
+}
+
+void EDBHandle::find(const QString &str, const Jid &j, const QString &id, int direction)
+{
+	d->busy = true;
+	d->lastRequestType = Read;
+	d->listeningFor = d->edb->op_find(str, j, id, direction);
+}
+
+void EDBHandle::append(const Jid &j, PsiEvent *e)
+{
+	d->busy = true;
+	d->lastRequestType = Write;
+	d->listeningFor = d->edb->op_append(j, e);
+}
+
+bool EDBHandle::busy() const
+{
+	return d->busy;
+}
+
+const EDBResult *EDBHandle::result() const
+{
+	return d->r;
+}
+
+bool EDBHandle::writeSuccess() const
+{
+	return d->writeSuccess;
+}
+
+void EDBHandle::edb_resultReady(EDBResult *r)
+{
+	d->busy = false;
+	if(r == 0) {
+		delete d->r;
+		d->r = 0;
+	}
+	else {
+		d->r = r;
+	}
+	d->listeningFor = -1;
+	finished();
+}
+
+void EDBHandle::edb_writeFinished(bool b)
+{
+	d->busy = false;
+	d->writeSuccess = b;
+	d->listeningFor = -1;
+	finished();
+}
+
+int EDBHandle::listeningFor() const
+{
+	return d->listeningFor;
+}
+
+int EDBHandle::lastRequestType() const
+{
+	return d->lastRequestType;
+}
+
+
+//----------------------------------------------------------------------------
+// EDB
+//----------------------------------------------------------------------------
+class EDB::Private
+{
+public:
+	Private() {}
+
+	QPtrList<EDBHandle> list;
+	int reqid_base;
+};
+
+EDB::EDB()
+{
+	d = new Private;
+	d->reqid_base = 0;
+}
+
+EDB::~EDB()
+{
+	d->list.setAutoDelete(true);
+	d->list.clear();
+	delete d;
+}
+
+int EDB::genUniqueId() const
+{
+	return d->reqid_base++;
+}
+
+void EDB::reg(EDBHandle *h)
+{
+	d->list.append(h);
+}
+
+void EDB::unreg(EDBHandle *h)
+{
+	d->list.removeRef(h);
+}
+
+int EDB::op_getLatest(const Jid &j, int len)
+{
+	return getLatest(j, len);
+}
+
+int EDB::op_getOldest(const Jid &j, int len)
+{
+	return getOldest(j, len);
+}
+
+int EDB::op_get(const Jid &jid, const QString &id, int direction, int len)
+{
+	return get(jid, id, direction, len);
+}
+
+int EDB::op_find(const QString &str, const Jid &j, const QString &id, int direction)
+{
+	return find(str, j, id, direction);
+}
+
+int EDB::op_append(const Jid &j, PsiEvent *e)
+{
+	return append(j, e);
+}
+
+void EDB::resultReady(int req, EDBResult *r)
+{
+	// deliver
+	QPtrListIterator<EDBHandle> it(d->list);
+	for(EDBHandle *h; (h = it.current()); ++it) {
+		if(h->listeningFor() == req) {
+			h->edb_resultReady(r);
+			return;
+		}
+	}
+	delete r;
+}
+
+void EDB::writeFinished(int req, bool b)
+{
+	// deliver
+	QPtrListIterator<EDBHandle> it(d->list);
+	for(EDBHandle *h; (h = it.current()); ++it) {
+		if(h->listeningFor() == req) {
+			h->edb_writeFinished(b);
+			return;
+		}
+	}
+}
+
+
+//----------------------------------------------------------------------------
+// EDBFlatFile
+//----------------------------------------------------------------------------
+struct item_file_req
+{
+	Jid j;
+	int type; // 0 = latest, 1 = oldest, 2 = random, 3 = write
+	int len;
+	int dir;
+	int id;
+	int eventId;
+	QString findStr;
+	PsiEvent *event;
+
+	typedef enum Type {
+		Type_getLatest = 0,
+		Type_getOldest,
+		Type_get,
+		Type_append,
+		Type_find
+	};
+};
+
+class EDBFlatFile::Private
+{
+public:
+	Private() {}
+
+	QPtrList<File> flist;
+	QPtrList<item_file_req> rlist;
+};
+
+EDBFlatFile::EDBFlatFile()
+:EDB()
+{
+	d = new Private;
+}
+
+EDBFlatFile::~EDBFlatFile()
+{
+	d->rlist.setAutoDelete(true);
+	d->flist.setAutoDelete(true);
+	d->flist.clear();
+
+	delete d;
+}
+
+int EDBFlatFile::getLatest(const Jid &j, int len)
+{
+	item_file_req *r = new item_file_req;
+	r->j = j;
+	r->type = item_file_req::Type_getLatest;
+	r->len = len < 1 ? 1: len;
+	r->id = genUniqueId();
+	d->rlist.append(r);
+
+	QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
+	return r->id;
+}
+
+int EDBFlatFile::getOldest(const Jid &j, int len)
+{
+	item_file_req *r = new item_file_req;
+	r->j = j;
+	r->type = item_file_req::Type_getOldest;
+	r->len = len < 1 ? 1: len;
+	r->id = genUniqueId();
+	d->rlist.append(r);
+
+	QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
+	return r->id;
+}
+
+int EDBFlatFile::get(const Jid &j, const QString &id, int direction, int len)
+{
+	item_file_req *r = new item_file_req;
+	r->j = j;
+	r->type = item_file_req::Type_get;
+	r->len = len < 1 ? 1: len;
+	r->dir = direction;
+	r->eventId = id.toInt();
+	r->id = genUniqueId();
+	d->rlist.append(r);
+
+	QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
+	return r->id;
+}
+
+int EDBFlatFile::find(const QString &str, const Jid &j, const QString &id, int direction)
+{
+	item_file_req *r = new item_file_req;
+	r->j = j;
+	r->type = item_file_req::Type_find;
+	r->len = 1;
+	r->dir = direction;
+	r->findStr = str;
+	r->eventId = id.toInt();
+	r->id = genUniqueId();
+	d->rlist.append(r);
+
+	QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
+	return r->id;
+}
+
+int EDBFlatFile::append(const Jid &j, PsiEvent *e)
+{
+	item_file_req *r = new item_file_req;
+	r->j = j;
+	r->type = item_file_req::Type_append;
+	r->event = e->copy();
+	if ( !r->event ) {
+		qWarning("EDBFlatFile::append(): Attempted to append incompatible type.");
+		delete r;
+		return 0;
+	}
+	r->id = genUniqueId();
+	d->rlist.append(r);
+
+	QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
+	return r->id;
+}
+
+EDBFlatFile::File *EDBFlatFile::findFile(const Jid &j) const
+{
+	QPtrListIterator<File> it(d->flist);
+	for(File *i; (i = it.current()); ++it) {
+		if(i->j.compare(j, false))
+			return i;
+	}
+	return 0;
+}
+
+EDBFlatFile::File *EDBFlatFile::ensureFile(const Jid &j)
+{
+	File *i = findFile(j);
+	if(!i) {
+		i = new File(Jid(j.userHost()));
+		connect(i, SIGNAL(timeout()), SLOT(file_timeout()));
+		d->flist.append(i);
+	}
+	return i;
+}
+
+void EDBFlatFile::performRequests()
+{
+	if(d->rlist.isEmpty())
+		return;
+
+	item_file_req *r = d->rlist.first();
+	d->rlist.removeRef(r);
+
+	File *f = ensureFile(r->j);
+	int type = r->type;
+	if(type >= item_file_req::Type_getLatest && type <= item_file_req::Type_get) {
+		int id, direction;
+
+		if(type == item_file_req::Type_getLatest) {
+			direction = Backward;
+			id = f->total()-1;
+		}
+		else if(type == item_file_req::Type_getOldest) {
+			direction = Forward;
+			id = 0;
+		}
+		else if(type == item_file_req::Type_get) {
+			direction = r->dir;
+			id = r->eventId;
+		}
+		else {
+			qWarning("EDBFlatFile::performRequests(): Invalid type.");
+			return;
+		}
+
+		int len;
+		if(direction == Forward) {
+			if(id + r->len > f->total())
+				len = f->total() - id;
+			else
+				len = r->len;
+		}
+		else {
+			if((id+1) - r->len < 0)
+				len = id+1;
+			else
+				len = r->len;
+		}
+
+		EDBResult *result = new EDBResult;
+		result->setAutoDelete(true);
+		for(int n = 0; n < len; ++n) {
+			PsiEvent *e = f->get(id);
+			if(e) {
+				QString prevId, nextId;
+				if(id > 0)
+					prevId = QString::number(id-1);
+				if(id < f->total()-1)
+					nextId = QString::number(id+1);
+				EDBItem *ei = new EDBItem(e, QString::number(id), prevId, nextId);
+				result->append(ei);
+			}
+
+			if(direction == Forward)
+				++id;
+			else
+				--id;
+		}
+		resultReady(r->id, result);
+	}
+	else if(type == item_file_req::Type_append) {
+		writeFinished(r->id, f->append(r->event));
+		delete r->event;
+	}
+	else if(type == item_file_req::Type_find) {
+		int id = r->eventId;
+		EDBResult *result = new EDBResult;
+		result->setAutoDelete(true);
+		while(1) {
+			PsiEvent *e = f->get(id);
+			if(!e)
+				break;
+
+			QString prevId, nextId;
+			if(id > 0)
+				prevId = QString::number(id-1);
+			if(id < f->total()-1)
+				nextId = QString::number(id+1);
+
+			if(e->type() == PsiEvent::Message) {
+				MessageEvent *me = (MessageEvent *)e;
+				const Message &m = me->message();
+				if(m.body().find(r->findStr, 0, false) != -1) {
+					EDBItem *ei = new EDBItem(e, QString::number(id), prevId, nextId);
+					result->append(ei);
+					break;
+				}
+			}
+
+			if(r->dir == Forward)
+				++id;
+			else
+				--id;
+		}
+		resultReady(r->id, result);
+	}
+
+	delete r;
+}
+
+void EDBFlatFile::file_timeout()
+{
+	File *i = (File *)sender();
+	d->flist.remove(i);
+	delete i;
+}
+
+
+//----------------------------------------------------------------------------
+// EDBFlatFile::File
+//----------------------------------------------------------------------------
+class EDBFlatFile::File::Private
+{
+public:
+	Private() {}
+
+	QValueVector<int> index;
+	bool indexed;
+};
+
+EDBFlatFile::File::File(const Jid &_j)
+{
+	d = new Private;
+	d->indexed = false;
+
+	j = _j;
+	valid = false;
+	t = new QTimer(this);
+	connect(t, SIGNAL(timeout()), SLOT(timer_timeout()));
+
+	//printf("[EDB opening -- %s]\n", j.full().latin1());
+	QString s = j.userHost();
+	fname = getHistoryDir() + "/" + qstrlower(jidEncode(s)) + ".history";
+	f.setName(fname);
+	valid = f.open(IO_ReadWrite);
+
+	touch();
+}
+
+EDBFlatFile::File::~File()
+{
+	if(valid)
+		f.close();
+	//printf("[EDB closing -- %s]\n", j.full().latin1());
+
+	delete d;
+}
+
+void EDBFlatFile::File::ensureIndex()
+{
+	if ( valid && !d->indexed ) {
+		f.reset(); // go to beginning
+		d->index.clear();
+
+		//printf(" file: %s\n", fname.latin1());
+		// build index
+		while(1) {
+			int at = f.at();
+
+			// locate a newline
+			bool found = false;
+			do {
+				int c = f.getch();
+				if(c == '\n') {
+					found = true;
+					break;
+				}
+			} while(!f.atEnd());
+
+			if(!found)
+				break;
+
+			int oldsize = d->index.size();
+			d->index.resize(oldsize+1);
+			d->index[oldsize] = at;
+		}
+
+		d->indexed = true;
+	}
+	else {
+		//printf(" file: can't open\n");
+	}
+
+	//printf(" messages: %d\n\n", d->index.size());
+}
+
+int EDBFlatFile::File::total() const
+{
+	((EDBFlatFile::File *)this)->ensureIndex();
+	return d->index.size();
+}
+
+void EDBFlatFile::File::touch()
+{
+	t->start(30000);
+}
+
+void EDBFlatFile::File::timer_timeout()
+{
+	timeout();
+}
+
+PsiEvent *EDBFlatFile::File::get(int id)
+{
+	touch();
+
+	if(!valid)
+		return 0;
+
+	ensureIndex();
+	if(id < 0 || id > (int)d->index.size())
+		return 0;
+
+	f.at(d->index[id]);
+
+	QTextStream t;
+	t.setDevice(&f);
+	t.setEncoding(QTextStream::UnicodeUTF8);
+	QString line = t.readLine();
+	t.unsetDevice();
+
+	return lineToEvent(line);
+}
+
+bool EDBFlatFile::File::append(PsiEvent *e)
+{
+	touch();
+
+	if(!valid)
+		return false;
+
+	QString line = eventToLine(e);
+	if(line.isEmpty())
+		return false;
+
+	f.at(f.size());
+	int at = f.at();
+
+	QTextStream t;
+	t.setDevice(&f);
+	t.setEncoding(QTextStream::UnicodeUTF8);
+	t << line << endl;
+	t.unsetDevice();
+	f.flush();
+
+	if ( d->indexed ) {
+		int oldsize = d->index.size();
+		d->index.resize(oldsize+1);
+		d->index[oldsize] = at;
+	}
+
+	return true;
+}
+
+PsiEvent *EDBFlatFile::File::lineToEvent(const QString &line)
+{
+	// -- read the line --
+	QString sTime, sType, sOrigin, sFlags, sText, sSubj, sUrl, sUrlDesc;
+	int x1, x2;
+	x1 = line.find('|') + 1;
+
+	x2 = line.find('|', x1);
+	sTime = line.mid(x1, x2-x1);
+	x1 = x2 + 1;
+
+	x2 = line.find('|', x1);
+	sType = line.mid(x1, x2-x1);
+	x1 = x2 + 1;
+
+	x2 = line.find('|', x1);
+	sOrigin = line.mid(x1, x2-x1);
+	x1 = x2 + 1;
+
+	x2 = line.find('|', x1);
+	sFlags = line.mid(x1, x2-x1);
+	x1 = x2 + 1;
+
+	// check for extra fields
+	if(sFlags[1] != '-') {
+		int subflags = hexChar2int(sFlags[1].latin1());
+
+		// have subject?
+		if(subflags & 1) {
+			x2 = line.find('|', x1);
+			sSubj = line.mid(x1, x2-x1);
+			x1 = x2 + 1;
+		}
+		// have url?
+		if(subflags & 2) {
+			x2 = line.find('|', x1);
+			sUrl = line.mid(x1, x2-x1);
+			x1 = x2 + 1;
+			x2 = line.find('|', x1);
+			sUrlDesc = line.mid(x1, x2-x1);
+			x1 = x2 + 1;
+		}
+	}
+
+	// body text is last
+	sText = line.mid(x1);
+
+	// -- read end --
+
+	int type = sType.toInt();
+	if(type == 0 || type == 1 || type == 4 || type == 5) {
+		Message m;
+		m.setTimeStamp(QDateTime::fromString(sTime, Qt::ISODate));
+		if(type == 1)
+			m.setType("chat");
+		else if(type == 4)
+			m.setType("error");
+		else if(type == 5)
+			m.setType("headline");
+		else
+			m.setType("");
+
+		bool originLocal = (sOrigin == "to") ? true: false;
+		m.setFrom(j);
+		if(sFlags[0] == 'N')
+			m.setBody(logdecode(sText));
+		else
+			m.setBody(logdecode(QString::fromUtf8(sText)));
+		m.setSubject(logdecode(sSubj));
+
+		QString url = logdecode(sUrl);
+		if(!url.isEmpty())
+			m.urlAdd(Url(url, logdecode(sUrlDesc)));
+		m.setSpooled(true);
+
+		MessageEvent *me = new MessageEvent(m, 0);
+		me->setOriginLocal(originLocal);
+
+		return me;
+	}
+	else if(type == 2 || type == 3 || type == 6 || type == 7 || type == 8) {
+		QString subType = "subscribe";
+		if(type == 2) {
+			// stupid "system message" from Psi <= 0.8.6
+			// try to figure out what kind it REALLY is based on the text
+			if(sText == tr("<big>[System Message]</big><br>You are now authorized."))
+				subType = "subscribed";
+			else if(sText == tr("<big>[System Message]</big><br>Your authorization has been removed!"))
+				subType = "unsubscribed";
+		}
+		else if(type == 3)
+			subType = "subscribe";
+		else if(type == 6)
+			subType = "subscribed";
+		else if(type == 7)
+			subType = "unsubscribe";
+		else if(type == 8)
+			subType = "unsubscribed";
+
+		AuthEvent *ae = new AuthEvent(j, subType, 0);
+		ae->setTimeStamp(QDateTime::fromString(sTime, Qt::ISODate));
+		return ae;
+	}
+	else
+		return 0;
+}
+
+QString EDBFlatFile::File::eventToLine(PsiEvent *e)
+{
+	int subflags = 0;
+	QString sTime, sType, sOrigin, sFlags;
+
+	if(e->type() == PsiEvent::Message) {
+		MessageEvent *me = (MessageEvent *)e;
+		const Message &m = me->message();
+		const UrlList urls = m.urlList();
+
+		if(!m.subject().isEmpty())
+			subflags |= 1;
+		if(!urls.isEmpty())
+			subflags |= 2;
+
+		sTime = m.timeStamp().toString(Qt::ISODate);
+		int n = 0;
+		if(m.type() == "chat")
+			n = 1;
+		else if(m.type() == "error")
+			n = 4;
+		else if(m.type() == "headline")
+			n = 5;
+		sType.setNum(n);
+		sOrigin = e->originLocal() ? "to": "from";
+		sFlags = "N---";
+
+		if(subflags != 0)
+			sFlags[1] = int2hexChar(subflags);
+
+		//  | date | type | To/from | flags | text
+		QString line = "|" + sTime + "|" + sType + "|" + sOrigin + "|" + sFlags + "|";
+
+		if(subflags & 1) {
+			line += logencode(m.subject()) + "|";
+		}
+		if(subflags & 2) {
+			const Url &url = urls.first();
+			line += logencode(url.url()) + "|";
+			line += logencode(url.desc()) + "|";
+		}
+		line += logencode(m.body());
+
+		return line;
+	}
+	else if(e->type() == PsiEvent::Auth) {
+		AuthEvent *ae = (AuthEvent *)e;
+		sTime = ae->timeStamp().toString(Qt::ISODate);
+		QString subType = ae->authType();
+		int n = 0;
+		if(subType == "subscribe")
+			n = 3;
+		else if(subType == "subscribed")
+			n = 6;
+		else if(subType == "unsubscribe")
+			n = 7;
+		else if(subType == "unsubscribed")
+			n = 8;
+		sType.setNum(n);
+		sOrigin = e->originLocal() ? "to": "from";
+		sFlags = "N---";
+
+		//  | date | type | To/from | flags | text
+		QString line = "|" + sTime + "|" + sType + "|" + sOrigin + "|" + sFlags + "|";
+		line += logencode(subType);
+
+		return line;
+	}
+	else
+		return "";
+}
Index: psi/tags/0.10-os2-b1/src/eventdb.h
===================================================================
--- psi/tags/0.10-os2-b1/src/eventdb.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/eventdb.h	(revision 38)
@@ -0,0 +1,181 @@
+/*
+ * eventdb.h - asynchronous I/O event database
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef EVENTDB_H
+#define EVENTDB_H
+
+#include<qobject.h>
+#include<qtimer.h>
+#include<qfile.h>
+#include<qptrlist.h>
+#include"psievent.h"
+
+
+class EDBItem
+{
+public:
+	EDBItem(PsiEvent *, const QString &id, const QString &nextId, const QString &prevId);
+	~EDBItem();
+
+	PsiEvent *event() const;
+	const QString & id() const;
+	const QString & nextId() const;
+	const QString & prevId() const;
+
+private:
+	QString v_id, v_prevId, v_nextId;
+	PsiEvent *e;
+};
+
+class EDBResult : public QPtrList<EDBItem>
+{
+public:
+	EDBResult() {}
+};
+
+class EDB;
+class EDBHandle : public QObject
+{
+	Q_OBJECT
+public:
+	enum { Read, Write };
+	EDBHandle(EDB *);
+	~EDBHandle();
+
+	// operations
+	void getLatest(const Jid &, int len);
+	void getOldest(const Jid &, int len);
+	void get(const Jid &jid, const QString &id, int direction, int len);
+	void find(const QString &, const Jid &, const QString &id, int direction);
+	void append(const Jid &, PsiEvent *);
+
+	bool busy() const;
+	const EDBResult *result() const;
+	bool writeSuccess() const;
+	int lastRequestType() const;
+
+signals:
+	void finished();
+
+private:
+	class Private;
+	Private *d;
+
+	friend class EDB;
+	void edb_resultReady(EDBResult *);
+	void edb_writeFinished(bool);
+	int listeningFor() const;
+};
+
+class EDB : public QObject
+{
+	Q_OBJECT
+public:
+	enum { Forward, Backward };
+	EDB();
+	virtual ~EDB()=0;
+
+protected:
+	int genUniqueId() const;
+	virtual int getLatest(const Jid &, int len)=0;
+	virtual int getOldest(const Jid &, int len)=0;
+	virtual int get(const Jid &jid, const QString &id, int direction, int len)=0;
+	virtual int append(const Jid &, PsiEvent *)=0;
+	virtual int find(const QString &, const Jid &, const QString &id, int direction)=0;
+	void resultReady(int, EDBResult *);
+	void writeFinished(int, bool);
+
+private:
+	class Private;
+	Private *d;
+
+	friend class EDBHandle;
+	void reg(EDBHandle *);
+	void unreg(EDBHandle *);
+
+	int op_getLatest(const Jid &, int len);
+	int op_getOldest(const Jid &, int len);
+	int op_get(const Jid &, const QString &id, int direction, int len);
+	int op_find(const QString &, const Jid &, const QString &id, int direction);
+	int op_append(const Jid &, PsiEvent *);
+};
+
+class EDBFlatFile : public EDB
+{
+	Q_OBJECT
+public:
+	EDBFlatFile();
+	~EDBFlatFile();
+
+	int getLatest(const Jid &, int len);
+	int getOldest(const Jid &, int len);
+	int get(const Jid &jid, const QString &id, int direction, int len);
+	int find(const QString &, const Jid &, const QString &id, int direction);
+	int append(const Jid &, PsiEvent *);
+
+	class File;
+
+private slots:
+	void performRequests();
+	void file_timeout();
+
+private:
+	class Private;
+	Private *d;
+
+	File *findFile(const Jid &) const;
+	File *ensureFile(const Jid &);
+};
+
+class EDBFlatFile::File : public QObject
+{
+	Q_OBJECT
+public:
+	File(const Jid &_j);
+	~File();
+
+	int total() const;
+	void touch();
+	PsiEvent *get(int);
+	bool append(PsiEvent *);
+
+signals:
+	void timeout();
+
+private slots:
+	void timer_timeout();
+
+public:
+	Jid j;
+	QString fname;
+	QFile f;
+	bool valid;
+	QTimer *t;
+
+	class Private;
+	Private *d;
+
+private:
+	PsiEvent *lineToEvent(const QString &);
+	QString eventToLine(PsiEvent *);
+	void ensureIndex();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/eventdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/eventdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/eventdlg.cpp	(revision 38)
@@ -0,0 +1,1697 @@
+/*
+ * eventdlg.cpp - dialog for sending / receiving messages and events
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"eventdlg.h"
+
+#include<qlabel.h>
+#include<qcombobox.h>
+#include<qlayout.h>
+#include<qpushbutton.h>
+#include<qdragobject.h>
+#include<qdropsite.h>
+#include<qmessagebox.h>
+#include<qstringlist.h>
+#include<qtimer.h>
+#include<qcursor.h>
+#include<qpopupmenu.h>
+#include<qiconset.h>
+#include<qdatetime.h>
+#include<qtooltip.h>
+#include<qheader.h>
+#include<qapplication.h>
+#include<qclipboard.h>
+#include<qlcdnumber.h>
+#include"psievent.h"
+#include"psicon.h"
+#include"psiaccount.h"
+#include"msgmle.h"
+#include"common.h"
+#include"userlist.h"
+#include"iconwidget.h"
+#include"fancylabel.h"
+#include"iconselect.h"
+#include"iconwidget.h"
+
+static QString findJid(const QString &s, int x, int *p1, int *p2)
+{
+	// scan backward for the beginning of a Jid
+	int n1 = x;
+	if(n1 >= (int)s.length())
+		n1 = s.length()-1;
+	for(; n1 >= 0; --n1) {
+		if(s.at(n1) == ',') {
+			++n1;
+			break;
+		}
+	}
+	if(n1 < 0)
+		n1 = 0;
+	// go forward, skipping whitespace
+	for(; n1 < (int)s.length(); ++n1) {
+		if(!s.at(n1).isSpace())
+			break;
+	}
+
+	// now find the end of the Jid
+	int n2 = n1;
+	for(; n2 < (int)s.length(); ++n2) {
+		if(s.at(n2) == ',')
+			break;
+	}
+	--n2;
+	// scan backwards from the end, skipping whitespace
+	for(; n2 > n1; --n2) {
+		if(!s.at(n2).isSpace())
+			break;
+	}
+	++n2;
+
+	*p1 = n1;
+	*p2 = n2;
+
+	return s.mid(n1, n2-n1);
+}
+
+//----------------------------------------------------------------------------
+// ELineEdit - a line edit that handles advanced Jid entry
+//----------------------------------------------------------------------------
+// hack hack hack hack
+struct QLineEditPrivate
+{
+	// qt 3.3.1
+   /*QLineEdit *q;
+   QString text;
+   int cursor;
+   int cursorTimer;
+   QPoint tripleClick;
+   int tripleClickTimer;
+   uint frame : 1;
+   uint cursorVisible : 1;
+   uint separator : 1;
+   uint readOnly : 1;
+   uint modified : 1;
+   uint direction : 5;
+   uint dragEnabled : 1;
+   uint alignment : 3;
+   uint echoMode : 2;
+   uint textDirty : 1;
+   uint selDirty : 1;
+   uint validInput : 1;
+   int ascent;
+   int maxLength;
+   int menuId;
+   int hscroll;*/
+
+	char pad[sizeof(QLineEdit *) + sizeof(QString) + (sizeof(int)*2) + sizeof(QPoint) + sizeof(int) + 3 + (sizeof(int)*3)];
+	int hscroll;
+};
+
+ELineEdit::ELineEdit(EventDlg *parent, const char *name)
+:QLineEdit(parent, name)
+{
+	setAcceptDrops(TRUE);
+}
+
+void ELineEdit::dragEnterEvent(QDragEnterEvent *e)
+{
+	e->accept(QTextDrag::canDecode(e));
+}
+
+void ELineEdit::dropEvent(QDropEvent *e)
+{
+	QString str;
+
+	if(QTextDrag::decode(e, str)) {
+		Jid jid(str);
+		if(!jid.isValid())
+			setText(str);
+		else {
+			EventDlg *e = (EventDlg *)parent();
+			QString name = e->jidToString(jid);
+
+			bool hasComma = false, hasText = false;
+			int len = text().length();
+			while ( --len >= 0 ) {
+				QChar c = text().at( len );
+				if ( c == ',' ) {
+					hasComma = true;
+					break;
+				}
+				else if ( !c.isSpace() ) {
+					hasText = true;
+					break;
+				}
+			}
+
+			if ( hasComma || !hasText )
+				setText(text() + ' ' + name);
+			else
+				setText(text() + ", " + name);
+		}
+		setCursorPosition(text().length());
+
+		repaint();
+	}
+}
+
+void ELineEdit::keyPressEvent(QKeyEvent *e)
+{
+	QLineEdit::keyPressEvent(e);
+	if(e->ascii() >= 32 && e->ascii() < 127)
+		tryComplete();
+}
+
+QPopupMenu *ELineEdit::createPopupMenu()
+{
+	EventDlg *e = (EventDlg *)parent();
+	int xoff = mapFromGlobal(QCursor::pos()).x();
+	int x = characterAt(d->hscroll + xoff, 0);
+	QString str = text();
+	int p1, p2;
+	QString j = findJid(str, x, &p1, &p2);
+	if(j.isEmpty())
+		return QLineEdit::createPopupMenu();
+
+	UserResourceList list = e->getResources(j);
+
+	setCursorPosition(p1);
+	setSelection(p1, p2-p1);
+
+	url = list;
+	url.sort();
+
+	int n = 100;
+	QPopupMenu *rm = new QPopupMenu(this); //= new QPopupMenu(pm);
+	connect(rm, SIGNAL(activated(int)), SLOT(resourceMenuActivated(int)));
+
+	rm->insertItem(tr("Recipient Default"), n++);
+
+	if(!list.isEmpty()) {
+		rm->insertSeparator();
+
+		for(UserResourceList::ConstIterator it = url.begin(); it != url.end(); ++it) {
+			const UserResource &r = *it;
+			QString name;
+			if(r.name().isEmpty())
+				name = QObject::tr("[blank]");
+			else
+				name = r.name();
+
+			rm->insertItem(is->status(r.status()), name, n++);
+		}
+	}
+
+	//pm->insertItem("Change Resource", rm, -1, 0);
+	//pm->insertSeparator(1);
+
+	return rm;
+}
+
+void ELineEdit::resourceMenuActivated(int x)
+{
+	if(x < 100)
+		return;
+
+	QString name;
+	if(x == 100)
+		name = "";
+	else {
+		int n = 101;
+		for(UserResourceList::ConstIterator it = url.begin(); it != url.end(); ++it) {
+			if(n == x) {
+				name = (*it).name();
+				break;
+			}
+			++n;
+		}
+	}
+	url.clear();
+
+	changeResource(name);
+}
+
+
+//----------------------------------------------------------------------------
+// AttachView
+//----------------------------------------------------------------------------
+class AttachViewItem : public QListViewItem
+{
+public:
+	AttachViewItem(const QString &_url, const QString &_desc, AttachView *par)
+	:QListViewItem(par)
+	{
+		type = 0;
+		url = _url;
+		desc = _desc;
+
+		setPixmap(0, IconsetFactory::icon("psi/www"));
+		setText(0, url + " (" + desc + ')');
+		setMultiLinesEnabled(true);
+	}
+
+	AttachViewItem(const QString &_gc, AttachView *par)
+	:QListViewItem(par)
+	{
+		type = 1;
+		gc = _gc;
+
+		setPixmap(0, IconsetFactory::icon("psi/groupChat"));
+		setText(0, gc);
+		setMultiLinesEnabled(true);
+	}
+
+	int rtti() const
+	{
+		return 9100;
+	}
+
+	QString url, desc;
+	QString gc;
+	int type;
+};
+
+AttachView::AttachView(QWidget *parent, const char *name)
+:QListView(parent, name)
+{
+	v_readOnly = false;
+	addColumn(tr("Attachments"));
+	setResizeMode(QListView::AllColumns);
+
+	connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), SLOT(qlv_context(QListViewItem *, const QPoint &, int)));
+	connect(this, SIGNAL(doubleClicked(QListViewItem *)), SLOT(qlv_doubleClicked(QListViewItem *)));
+};
+
+AttachView::~AttachView()
+{
+}
+
+void AttachView::setReadOnly(bool b)
+{
+	v_readOnly = b;
+}
+
+void AttachView::urlAdd(const QString &url, const QString &desc)
+{
+	new AttachViewItem(url, desc, this);
+	childCountChanged();
+}
+
+void AttachView::gcAdd(const QString &gc)
+{
+	new AttachViewItem(gc, this);
+	childCountChanged();
+}
+
+void AttachView::qlv_context(QListViewItem *lvi, const QPoint &pos, int)
+{
+	AttachViewItem *i = (AttachViewItem *)lvi;
+	if(!i)
+		return;
+
+	QPopupMenu pm(this);
+	if(i->type == 0) {
+		pm.insertItem(tr("Go to &URL..."), 0);
+		pm.insertItem(tr("Copy location"), 1);
+	}
+	else
+		pm.insertItem(tr("Join &Groupchat..."), 0);
+	pm.insertSeparator();
+	pm.insertItem(tr("Remove"), 2);
+
+	if(v_readOnly)
+		pm.setItemEnabled(2, false);
+
+	int n = pm.exec(pos);
+	if(n == -1)
+		return;
+
+	if(n == 0) {
+		if(i->type == 0)
+			goURL(i->url);
+		else
+			actionGCJoin(i->gc);
+	}
+	else if(n == 1) {
+		QApplication::clipboard()->setText(i->url, QClipboard::Clipboard);
+		if(QApplication::clipboard()->supportsSelection())
+			QApplication::clipboard()->setText(i->url, QClipboard::Selection);
+	}
+	else if(n == 2) {
+		delete i;
+		childCountChanged();
+	}
+}
+
+void AttachView::qlv_doubleClicked(QListViewItem *lvi)
+{
+	AttachViewItem *i = (AttachViewItem *)lvi;
+	if(!i)
+		return;
+
+	if(i->type == 0)
+		goURL(i->url);
+	else
+		actionGCJoin(i->gc);
+}
+
+void AttachView::goURL(const QString &_url)
+{
+	if(_url.isEmpty())
+		return;
+
+	QString url = _url;
+	if(url.find("://") == -1)
+		url.insert(0, "http://");
+
+	openURL(url);
+}
+
+UrlList AttachView::urlList() const
+{
+	UrlList list;
+
+	for(AttachViewItem *i = (AttachViewItem *)firstChild(); i; i = (AttachViewItem *)i->nextSibling())
+		list += Url(i->url, i->desc);
+
+	return list;
+}
+
+void AttachView::addUrlList(const UrlList &list)
+{
+	for(QValueList<Url>::ConstIterator it = list.begin(); it != list.end(); ++it) {
+		const Url &u = *it;
+		urlAdd(u.url(), u.desc());
+	}
+}
+
+
+//----------------------------------------------------------------------------
+// AddUrlDlg
+//----------------------------------------------------------------------------
+AddUrlDlg::AddUrlDlg(QWidget *parent, const char *name)
+:AddUrlUI(parent, name, true)
+{
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/www"));
+#endif
+
+	connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
+	connect(pb_ok, SIGNAL(clicked()), SLOT(accept()));
+}
+
+AddUrlDlg::~AddUrlDlg()
+{
+}
+
+
+//----------------------------------------------------------------------------
+// EventDlg - a window to read and write events
+//----------------------------------------------------------------------------
+class EventDlg::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(EventDlg *d) {
+		dlg = d;
+	}
+
+	EventDlg *dlg;
+	PsiCon *psi;
+	PsiAccount *pa;
+
+	bool composing;
+
+	AccountsComboBox *cb_ident;
+	QComboBox *cb_type;
+	QLabel *lb_ident, *lb_time;
+	IconLabel *lb_status;
+	ELineEdit *le_to;
+	QLineEdit *le_from, *le_subj;
+	QLCDNumber *lcd_count;
+	IconToolButton *tb_url, *tb_info, *tb_history, *tb_pgp, *tb_icon;
+	IconLabel *lb_pgp;
+	bool enc;
+	int transid;
+	IconButton *pb_next;
+	IconButton *pb_close, *pb_quote, *pb_deny, *pb_send, *pb_reply, *pb_chat, *pb_auth;
+	ChatView *mle;
+	AttachView *attachView;
+	QTimer *whois;
+	QString lastWhois;
+	Jid jid, realJid;
+	QString thread;
+	QStringList completionList;
+	Icon *anim, *nextAnim;
+	int nextAmount;
+
+	bool urlOnShow;
+
+	Message m;
+	QStringList sendLeft;
+
+public slots:
+	void addEmoticon(const Icon *icon) {
+		if ( !dlg->isActiveWindow() )
+		     return;
+
+		QString text;
+
+		QDict<QString> itext = icon->text();
+		QDictIterator<QString> it ( itext );
+		for ( ; it.current(); ++it) {
+			if ( it.current() && !it.current()->isEmpty() ) {
+				text = *(it.current()) + " ";
+				break;
+			}
+		}
+
+		if ( !text.isEmpty() )
+			mle->insert( text );
+	}
+
+	void addEmoticon(QString text) {
+		if ( !dlg->isActiveWindow() )
+		     return;
+
+		mle->insert( text + " " );
+	}
+
+	void updateCounter() {
+		lcd_count->display((int)mle->text().length());
+	}
+};
+
+EventDlg::EventDlg(const QString &to, PsiCon *psi, PsiAccount *pa)
+:AdvancedWidget<QWidget>(0, 0, WDestructiveClose | WGroupLeader)
+{
+	d = new Private(this);
+	d->composing = true;
+	d->psi = psi;
+	d->pa = 0;
+	d->psi->dialogRegister(this);
+
+	d->anim = 0;
+	d->nextAnim = 0;
+	d->nextAmount = 0;
+	d->urlOnShow = false;
+
+	setAccount(pa);
+
+	d->whois = new QTimer;
+	connect(d->whois, SIGNAL(timeout()), SLOT(doWhois()));
+
+	init();
+
+	d->cb_ident->setAccount(pa);
+
+	d->pb_send->show();
+	d->le_to->setText(expandAddresses(to, false));
+	d->le_to->setCursorPosition(0);
+
+	if(option.grabUrls) {
+		// url in clipboard?
+		QClipboard *cb = QApplication::clipboard();
+		QCString subtype("plain");
+		bool oldmode = cb->selectionModeEnabled();
+		cb->setSelectionMode(false);
+		QString text = cb->text(subtype);
+		if(!text && cb->supportsSelection()) {
+			cb->setSelectionMode(true);
+			text = cb->text(subtype);
+		}
+		if(text) {
+			if(text.left(7) == "http://") {
+				d->attachView->urlAdd(text, "");
+				cb->setSelectionMode(false);
+				cb->clear();
+				cb->setSelectionMode(true);
+				cb->clear();
+			}
+		}
+		cb->setSelectionMode(oldmode);
+	}
+
+	updateIdentity(pa);
+
+	X11WM_CLASS("event");
+
+	if(d->le_to->text().isEmpty())
+		d->le_to->setFocus();
+	else
+		d->mle->setFocus();
+
+	if(d->tb_pgp) {
+		UserListItem *u = d->pa->findFirstRelavent(d->jid);
+		if(u && u->isSecure(d->jid.resource()))
+			d->tb_pgp->setOn(true);
+	}
+}
+
+EventDlg::EventDlg(const Jid &j, PsiAccount *pa, bool unique)
+:AdvancedWidget<QWidget>(0, 0, WDestructiveClose | WGroupLeader)
+{
+	d = new Private(this);
+	d->composing = false;
+	d->psi = pa->psi();
+	d->pa = pa;
+	d->jid = j;
+	d->realJid = j;
+
+	if(unique)
+		d->pa->dialogRegister(this, j);
+	else
+		d->pa->dialogRegister(this);
+
+	d->anim = 0;
+	d->nextAnim = 0;
+	d->nextAmount = 0;
+	d->urlOnShow = false;
+
+	init();
+
+	d->le_from->setText(expandAddresses(d->jid.full(), false));
+	d->le_from->setCursorPosition(0);
+
+	doWhois();
+
+	d->pb_next->show();
+
+	d->pb_close->setFocus();
+
+	X11WM_CLASS("event");
+
+	setTime(QDateTime::currentDateTime(), true);
+}
+
+EventDlg::~EventDlg()
+{
+	if(d->composing) {
+		delete d->whois;
+		d->psi->dialogUnregister(this);
+	}
+	else {
+		d->pa->dialogUnregister(this);
+	}
+	delete d;
+}
+
+void EventDlg::init()
+{
+	QVBoxLayout *vb1 = new QVBoxLayout(this, 4);
+	QLabel *l;
+
+	// first row
+	QHBoxLayout *hb1 = new QHBoxLayout(vb1);
+	l = new QLabel(tr("Identity:"), this);
+	hb1->addWidget(l);
+
+	d->enc = false;
+	d->transid = -1;
+
+	if(d->composing) {
+		d->cb_ident = d->psi->accountsComboBox(this);
+		connect(d->cb_ident, SIGNAL(activated(PsiAccount *)), SLOT(updateIdentity(PsiAccount *)));
+		hb1->addWidget(d->cb_ident);
+	}
+	else {
+		d->lb_ident = d->pa->accountLabel(this);
+		d->lb_ident->setSizePolicy(QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ));
+		hb1->addWidget(d->lb_ident);
+	}
+
+	if(d->composing) {
+		l = new QLabel(tr("Type:"), this);
+		hb1->addWidget(l);
+		d->cb_type = new QComboBox(this);
+		d->cb_type->insertItem(tr("Normal"));
+		d->cb_type->insertItem(tr("Chat"));
+		hb1->addWidget(d->cb_type);
+	}
+	else {
+		l = new QLabel(tr("Time:"), this);
+		hb1->addWidget(l);
+		d->lb_time = new QLabel(this);
+		d->lb_time->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+		hb1->addWidget(d->lb_time);
+	}
+
+	// second row
+	QHBoxLayout *hb2 = new QHBoxLayout(vb1);
+
+	d->lb_status = new IconLabel(this);
+	d->lb_status->setIcon(IconsetFactory::iconPtr("status/noauth"));
+
+	if(d->composing) {
+		l = new QLabel(tr("To:"), this);
+		hb2->addWidget(l);
+		hb2->addWidget(d->lb_status);
+		d->le_to = new ELineEdit(this);
+		connect(d->le_to, SIGNAL(textChanged(const QString &)), SLOT(to_textChanged(const QString &)));
+		connect(d->le_to, SIGNAL(changeResource(const QString &)), SLOT(to_changeResource(const QString &)));
+		connect(d->le_to, SIGNAL(tryComplete()), SLOT(to_tryComplete()));
+		hb2->addWidget(d->le_to);
+	}
+	else {
+		l = new QLabel(tr("From:"), this);
+		hb2->addWidget(l);
+		hb2->addWidget(d->lb_status);
+		d->le_from = new QLineEdit(this);
+		d->le_from->setReadOnly(true);
+		hb2->addWidget(d->le_from);
+	}
+
+	// icon select
+	//connect(d->psi->iconSelectPopup(), SIGNAL(iconSelected(const Icon *)), d, SLOT(addEmoticon(const Icon *)));
+	connect(d->psi->iconSelectPopup(), SIGNAL(textSelected(QString)), d, SLOT(addEmoticon(QString)));
+
+	d->tb_icon = new IconToolButton(this);
+	d->tb_icon->setIcon(IconsetFactory::iconPtr("psi/smile"));
+	d->tb_icon->setPopup(d->psi->iconSelectPopup());
+	d->tb_icon->setPopupDelay (1);
+	QToolTip::add(d->tb_icon, tr("Select icon"));
+	if ( !d->composing )
+		d->tb_icon->setEnabled(false);
+
+	// message length counter
+	d->le_subj = new QLineEdit(this);
+	d->lcd_count = new QLCDNumber(this);
+	QToolTip::add(d->lcd_count, tr("Message length"));
+	d->lcd_count->setFixedWidth(50);
+
+	if(d->composing) {
+		d->tb_pgp = new IconToolButton(this);
+		d->tb_pgp->setToggleButton(true);
+		QToolTip::add(d->tb_pgp, tr("Toggle encryption"));
+		d->lb_pgp = 0;
+	}
+	else {
+		d->lb_pgp = new IconLabel(this);
+		d->lb_pgp->setIcon(IconsetFactory::iconPtr("psi/cryptoNo"));
+		d->tb_pgp = 0;
+	}
+
+	d->tb_url = new IconToolButton(this);
+	connect(d->tb_url, SIGNAL(clicked()), SLOT(addUrl()));
+	QToolTip::add(d->tb_url, tr("Add URL"));
+	d->tb_info = new IconToolButton(this);
+	connect(d->tb_info, SIGNAL(clicked()), SLOT(doInfo()));
+	QToolTip::add(d->tb_info, tr("User info"));
+	d->tb_history = new IconToolButton(this);
+	connect(d->tb_history, SIGNAL(clicked()), SLOT(doHistory()));
+	QToolTip::add(d->tb_history, tr("Message history"));
+
+	QHBoxLayout *hb3 = new QHBoxLayout(vb1);
+
+//	if(d->composing /* && config->showsubject */) {
+	if(option.showSubjects) {
+		// third row
+		l = new QLabel(tr("Subject:"), this);
+		hb3->addWidget(l);
+		hb3->addWidget(d->le_subj);
+		hb3->addWidget(d->lcd_count);
+		hb3->addWidget(d->tb_icon);
+		hb3->addWidget(d->tb_url);
+		hb3->addWidget(d->tb_info);
+		hb3->addWidget(d->tb_history);
+
+		if(!d->composing) {
+			d->le_subj->setReadOnly(true);
+			d->tb_url->setEnabled(false);
+			hb3->addWidget(d->lb_pgp);
+		} else
+			hb3->addWidget(d->tb_pgp);
+
+	} else {
+		d->le_subj->hide();
+		hb2->addWidget(d->lcd_count);
+		hb2->addWidget(d->tb_icon);
+		hb2->addWidget(d->tb_url);
+		hb2->addWidget(d->tb_info);
+		hb2->addWidget(d->tb_history);
+		if(d->composing)
+			hb2->addWidget(d->tb_pgp);
+		else
+			hb2->addWidget(d->lb_pgp);
+	}
+
+	// text area
+	d->mle = new ChatView(this);
+	d->mle->setReadOnly(false);
+	d->mle->setUndoRedoEnabled(true);
+	d->mle->setTextFormat(PlainText);
+	d->mle->setMinimumHeight(50);
+	vb1->addWidget(d->mle);
+
+	connect(d->mle, SIGNAL(textChanged()), d, SLOT(updateCounter()));
+
+	d->mle->setTextFormat(QTextEdit::PlainText);
+	if(!d->composing) {
+		d->mle->setReadOnly(true);
+		d->mle->setUndoRedoEnabled(false);
+	}
+
+	// attachment view
+	d->attachView = new AttachView(this);
+	d->attachView->setFixedHeight(80);
+	d->attachView->hide();
+	connect(d->attachView, SIGNAL(childCountChanged()), SLOT(showHideAttachView()));
+	connect(d->attachView, SIGNAL(actionGCJoin(const QString &)), SLOT(actionGCJoin(const QString &)));
+	vb1->addWidget(d->attachView);
+
+	if(!d->composing)
+		d->attachView->setReadOnly(true);
+
+	// bottom row
+	QHBoxLayout *hb4 = new QHBoxLayout(vb1);
+	d->pb_close = new IconButton(this);
+	d->pb_close->setText(tr("&Close"));
+	connect(d->pb_close, SIGNAL(clicked()), SLOT(close()));
+	d->pb_close->setMinimumWidth(80);
+	hb4->addWidget(d->pb_close);
+	hb4->addStretch(1);
+	d->pb_next = new IconButton(this);
+	connect(d->pb_next, SIGNAL(clicked()), SLOT(doReadNext()));
+	d->pb_next->setText(tr("&Next"));
+	d->pb_next->hide();
+	d->pb_next->setMinimumWidth(96);
+	d->pb_next->setEnabled(false);
+	hb4->addWidget(d->pb_next);
+	d->pb_quote = new IconButton(this);
+	d->pb_quote->setText(tr("&Quote"));
+	connect(d->pb_quote, SIGNAL(clicked()), SLOT(doQuote()));
+	d->pb_quote->hide();
+	d->pb_quote->setMinimumWidth(96);
+	hb4->addWidget(d->pb_quote);
+	d->pb_deny = new IconButton(this);
+	d->pb_deny->setText(tr("&Deny"));
+	connect(d->pb_deny, SIGNAL(clicked()), SLOT(doDeny()));
+	d->pb_deny->hide();
+	d->pb_deny->setMinimumWidth(96);
+	hb4->addWidget(d->pb_deny);
+	d->pb_auth = new IconButton(this);
+	d->pb_auth->setText(tr("&Add/Auth"));
+	connect(d->pb_auth, SIGNAL(clicked()), SLOT(doAuth()));
+	d->pb_auth->setIcon(IconsetFactory::iconPtr("psi/addContact"));
+	d->pb_auth->hide();
+	d->pb_auth->setMinimumWidth(96);
+	hb4->addWidget(d->pb_auth);
+	d->pb_send = new IconButton(this);
+	d->pb_send->setText(tr("&Send"));
+	connect(d->pb_send, SIGNAL(clicked()), SLOT(doSend()));
+	d->pb_send->hide();
+	d->pb_send->setMinimumWidth(96);
+	hb4->addWidget(d->pb_send);
+	d->pb_chat = new IconButton(this);
+	d->pb_chat->setText(tr("&Chat"));
+	connect(d->pb_chat, SIGNAL(clicked()), SLOT(doChat()));
+	d->pb_chat->hide();
+	d->pb_chat->setMinimumWidth(96);
+	hb4->addWidget(d->pb_chat);
+	d->pb_reply = new IconButton(this);
+	d->pb_reply->setText(tr("&Reply"));
+	connect(d->pb_reply, SIGNAL(clicked()), SLOT(doReply()));
+	d->pb_reply->hide();
+	d->pb_reply->setMinimumWidth(96);
+	hb4->addWidget(d->pb_reply);
+
+	setTabOrder(d->mle, d->pb_send);
+
+	updatePGP();
+	connect(d->pa, SIGNAL(pgpKeyChanged()), SLOT(updatePGP()));
+	connect(d->pa, SIGNAL(encryptedMessageSent(int, bool)), SLOT(encryptedMessageSent(int, bool)));
+
+	resize(option.sizeEventDlg);
+	optionsUpdate();
+}
+
+void EventDlg::setAccount(PsiAccount *pa)
+{
+	if(d->pa)
+		disconnect(d->pa, SIGNAL(updatedActivity()), this, SLOT(accountUpdatedActivity()));
+
+	d->pa = pa;
+	connect(d->pa, SIGNAL(updatedActivity()), this, SLOT(accountUpdatedActivity()));
+}
+
+void EventDlg::updateIdentity(PsiAccount *pa)
+{
+	if(!pa) {
+		close();
+		return;
+	}
+
+	setAccount(pa);
+
+	buildCompletionList();
+	doWhois(true);
+}
+
+void EventDlg::accountUpdatedActivity()
+{
+	// TODO: act on account activity change
+}
+
+QString EventDlg::text() const
+{
+	return d->mle->text();
+}
+
+void EventDlg::setText(const QString &s)
+{
+	d->mle->setText(s);
+	d->mle->moveCursor(QTextEdit::MoveEnd, false);
+}
+
+void EventDlg::setSubject(const QString &s)
+{
+	d->le_subj->setText(s);
+}
+
+void EventDlg::setThread(const QString &t)
+{
+	d->thread = t;
+}
+
+void EventDlg::setUrlOnShow()
+{
+	d->urlOnShow = true;
+}
+
+PsiAccount *EventDlg::psiAccount()
+{
+	return d->pa;
+}
+
+QStringList EventDlg::stringToList(const QString &s, bool enc) const
+{
+	QStringList list;
+
+	int x1, x2;
+	x1 = 0;
+	x2 = 0;
+	while(1) {
+		// scan along for a comma
+		bool found = false;
+		for(int n = x1; n < (int)s.length(); ++n) {
+			if(s.at(n) == ',') {
+				found = true;
+				x2 = n;
+				break;
+			}
+		}
+		if(!found)
+			x2 = s.length();
+
+		QString c = s.mid(x1, (x2-x1));
+		QString j;
+		if(enc)
+			j = findJidInString(c);
+		else
+			j = c;
+		if(j.isEmpty())
+			j = c;
+
+		j = j.stripWhiteSpace();
+		//printf("j=[%s]\n", j.latin1());
+		if(!j.isEmpty())
+			list += j;
+
+		if(!found)
+			break;
+		x1 = x2+1;
+	}
+
+	return list;
+}
+
+QString EventDlg::findJidInString(const QString &s) const
+{
+	int a = s.find('<');
+	if(a != -1) {
+		++a;
+		int b = s.find('>', a);
+		if(b != -1)
+			return dec822jid(s.mid(a, b-a));
+	}
+	return "";
+}
+
+QString EventDlg::expandAddresses(const QString &in, bool enc) const
+{
+	//printf("in: [%s]\n", in.latin1());
+	QString str;
+	QStringList list = stringToList(in, enc);
+	bool first = true;
+	for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+		if(!first)
+			str += ", ";
+		first = false;
+
+		Jid j(*it);
+		QPtrList<UserListItem> ul = d->pa->findRelavent(j);
+		if(ul.isEmpty()) {
+			str += j.full();
+			continue;
+		}
+		UserListItem *u = ul.first();
+
+		Jid jid;
+		if(j.resource().isEmpty())
+			jid = u->jid().full();
+		else
+			jid = u->jid().userHost() + '/' + j.resource();
+
+		QString name;
+		if(!u->name().isEmpty())
+			name += u->name() + QString(" <%1>").arg(enc822jid(jid.full()));
+		else
+			name = enc822jid(jid.full());
+		str += name;
+	}
+
+	//printf("expanding: [%s]\n", str.latin1());
+	return str;
+}
+
+void EventDlg::to_textChanged(const QString &)
+{
+	d->whois->start(250);
+}
+
+void EventDlg::to_changeResource(const QString &r)
+{
+	QString str = d->le_to->text();
+	int start, len;
+	if(!d->le_to->getSelection(&start, &len)) {
+		//printf("bad selection\n");
+		return;
+	}
+	//printf("selection: [%d,%d]\n", start, len);
+
+	int p1, p2;
+	QString s = findJid(str, start, &p1, &p2);
+	QString j = findJidInString(s);
+	if(j.isEmpty())
+		j = s;
+	Jid jid(j);
+	if(!jid.isValid()) {
+		//printf("invalid jid\n");
+		return;
+	}
+	//printf("s=[%s], j=[%s], p: [%d,%d]\n", s.latin1(), j.latin1(), p1, p2);
+
+	QString js = jidToString(jid, r);
+	//printf("js=[%s]\n", js.latin1());
+	/*str.remove(start, len);
+	str.insert(start, js);
+	d->le_to->deselect();
+	d->le_to->setCursorPosition(0);
+	d->le_to->setText(str);
+	//d->le_to->setCursorPosition(start + js.length());*/
+	d->le_to->insert(js);
+}
+
+void EventDlg::to_tryComplete()
+{
+	if(!option.jidComplete)
+		return;
+
+	QString str = d->le_to->text();
+	int x = d->le_to->cursorPosition();
+
+	int p1, p2;
+	QString s = findJid(str, x, &p1, &p2);
+	if(s.length() < 1 || x != p2)
+		return;
+
+	for(QStringList::ConstIterator it = d->completionList.begin(); it != d->completionList.end(); ++it) {
+		QString name = *it;
+		if(s.length() > name.length())
+			continue;
+
+		bool ok = true;
+		int n;
+		for(n = 0; n < (int)s.length(); ++n) {
+			if(s.at(n).lower() != name.at(n).lower()) {
+				ok = false;
+				break;
+			}
+		}
+		name = name.mid(n);
+		if(ok) {
+			d->le_to->insert(name);
+			d->le_to->setCursorPosition(x);
+			d->le_to->setSelection(x, name.length());
+			break;
+		}
+	}
+}
+
+void EventDlg::buildCompletionList()
+{
+	d->completionList.clear();
+
+	d->completionList += d->pa->jid().full();
+
+	UserListIt it(*d->pa->userList());
+	for(UserListItem *u; (u = it.current()); ++it) {
+		QString j = u->jid().full();
+		if(!u->name().isEmpty())
+			d->completionList += u->name() + " <"+j+'>';
+		d->completionList += j;
+	}
+}
+
+QString EventDlg::jidToString(const Jid &jid, const QString &r) const
+{
+	QString name;
+
+	QPtrList<UserListItem> ul = d->pa->findRelavent(jid);
+	if(!ul.isEmpty()) {
+		UserListItem *u = ul.first();
+
+		QString j;
+		if(r.isEmpty())
+			j = u->jid().full();
+		else
+			j = u->jid().userHost() + '/' + r;
+
+		if(!u->name().isEmpty())
+			name = u->name() + QString(" <%1>").arg(enc822jid(j));
+		else
+			name = enc822jid(j);
+	}
+	else
+		name = enc822jid(jid.full());
+
+	return name;
+}
+
+void EventDlg::doWhois(bool force)
+{
+	QString str;
+	if(d->composing) {
+		str = d->le_to->text();
+		if(str == d->lastWhois && !force)
+			return;
+	}
+	else {
+		str = d->le_from->text();
+	}
+
+	//printf("whois: [%s]\n", str.latin1());
+	d->lastWhois = str;
+	QStringList list = stringToList(str);
+	bool found = false;
+	if(list.count() == 1) {
+		Jid j(list[0]);
+		d->jid = j;
+		QPtrList<UserListItem> ul = d->pa->findRelavent(j);
+		if(!ul.isEmpty()) {
+			d->tb_info->setEnabled(true);
+			d->tb_history->setEnabled(true);
+			found = true;
+		}
+		updateContact(d->jid);
+	}
+	if(!found) {
+		d->jid = "";
+
+		d->lb_status->setIcon(IconsetFactory::iconPtr("status/noauth"));
+		d->tb_info->setEnabled(false);
+		d->tb_history->setEnabled(false);
+		setCaption(tr("Send Message"));
+		QToolTip::remove(d->lb_status);
+	}
+}
+
+UserResourceList EventDlg::getResources(const QString &s) const
+{
+	UserResourceList list;
+
+	QString j = findJidInString(s);
+	if(j.isEmpty())
+		j = s;
+	Jid jid(j);
+	if(!jid.isValid())
+		return list;
+
+	QPtrList<UserListItem> ul = d->pa->findRelavent(jid);
+	if(!ul.isEmpty()) {
+		UserListItem *u = ul.first();
+		if(u->isAvailable())
+			return u->userResourceList();
+	}
+
+	return list;
+}
+
+void EventDlg::optionsUpdate()
+{
+	// update the font
+	QFont f;
+	f.fromString(option.font[fMessage]);
+	d->mle->setFont(f);
+
+	// update status icon
+	doWhois(true);
+
+	if ( option.showCounter && d->composing )
+		d->lcd_count->show();
+	else
+		d->lcd_count->hide();
+
+	if ( option.useEmoticons )
+		d->tb_icon->show();
+	else
+		d->tb_icon->hide();
+
+	// tool buttons: not required
+	d->tb_url->setIcon(IconsetFactory::iconPtr("psi/www"));
+	d->tb_info->setIcon(IconsetFactory::iconPtr("psi/vCard"));
+	d->tb_history->setIcon(IconsetFactory::iconPtr("psi/history"));
+	if(d->tb_pgp) {
+		QIconSet i;
+		i.setPixmap(IconsetFactory::icon("psi/cryptoNo"),  QIconSet::Automatic, QIconSet::Normal, QIconSet::Off);
+		i.setPixmap(IconsetFactory::icon("psi/cryptoYes"), QIconSet::Automatic, QIconSet::Normal, QIconSet::On);
+		d->tb_pgp->setIconSet(i);
+	}
+	if(d->lb_pgp)
+		d->lb_pgp->setIcon(IconsetFactory::iconPtr(d->enc ? "psi/cryptoYes" : "psi/cryptoNo"));
+
+	// update the readnext icon
+	if(d->nextAmount > 0)
+		d->pb_next->forceSetIcon(d->nextAnim);
+
+	// update the widget icon
+#ifndef Q_WS_MAC
+	if(d->composing) {
+		setIcon(IconsetFactory::icon("psi/sendMessage"));
+	}
+	else {
+		if(d->anim)
+			setIcon(d->anim->impix());
+	}
+#endif
+}
+
+QSize EventDlg::defaultSize()
+{
+	return QSize(420, 280);
+}
+
+void EventDlg::showEvent(QShowEvent *e)
+{
+	QWidget::showEvent(e);
+
+	if(d->urlOnShow) {
+		d->urlOnShow = false;
+		QTimer::singleShot(1, this, SLOT(addUrl()));
+	}
+}
+
+void EventDlg::resizeEvent(QResizeEvent *e)
+{
+	if(option.keepSizes)
+		option.sizeEventDlg = e->size();
+}
+
+void EventDlg::keyPressEvent(QKeyEvent *e)
+{
+	if(e->key() == Key_Escape)
+		close();
+#ifdef Q_WS_MAC
+	else if(e->key() == Key_W && e->state() & ControlButton)
+		close();
+#endif
+	else if(d->composing && e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) )
+		doSend();
+	else if(e->key() == Key_H && (e->state() & ControlButton))
+		doHistory();
+	else if(e->key() == Key_I && (e->state() & ControlButton))
+		doInfo();
+	else
+		e->ignore();
+}
+
+void EventDlg::closeEvent(QCloseEvent *e)
+{
+	// really lame way of checking if we are encrypting
+	if(!d->mle->isEnabled())
+		return;
+
+	e->accept();
+}
+
+void EventDlg::doSend()
+{
+	if(!d->composing)
+		return;
+
+	if(!d->pb_send->isEnabled())
+		return;
+
+	if(!d->pa->checkConnected(this))
+		return;
+
+	if(d->mle->text().isEmpty() && d->attachView->childCount() == 0) {
+		QMessageBox::information(this, tr("Warning"), tr("Please type in a message first."));
+		return;
+	}
+
+	QStringList list = stringToList(d->le_to->text());
+	if(list.isEmpty()) {
+		QMessageBox::warning(this, tr("Warning"), tr("No recipients have been specified!"));
+		return;
+	}
+
+	Message m;
+	if(d->cb_type->currentItem() == 0)
+		m.setType("");
+	else
+		m.setType("chat");
+
+	m.setBody(d->mle->text());
+	m.setSubject(d->le_subj->text());
+	m.setUrlList(d->attachView->urlList());
+	m.setTimeStamp(QDateTime::currentDateTime());
+	m.setThread(d->thread);
+	if(d->tb_pgp->isOn())
+		m.setWasEncrypted(true);
+	d->m = m;
+
+	d->enc = false;
+	if(d->tb_pgp->isOn()) {
+		d->le_to->setEnabled(false);
+		d->mle->setEnabled(false);
+		d->enc = true;
+		d->sendLeft = list;
+
+		trySendEncryptedNext();
+	}
+	else {
+		for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+			m.setTo(Jid(*it));
+			d->pa->dj_sendMessage(m);
+		}
+		doneSend();
+	}
+}
+
+void EventDlg::doneSend()
+{
+	close();
+}
+
+void EventDlg::doReadNext()
+{
+	aReadNext(d->realJid);
+}
+
+void EventDlg::doChat()
+{
+	QStringList list = stringToList(d->le_from->text());
+	if(list.isEmpty())
+		return;
+
+	// FIXME: Maybe it's better without bare(), but then PsiAccount::actionChat
+	// doesn't find the UserListItem to chat with :-(
+	Jid jf(list[0]);
+	Jid j(jf.bare());
+	aChat(j);
+}
+
+void EventDlg::doReply()
+{
+	QStringList list = stringToList(d->le_from->text());
+	if(list.isEmpty())
+		return;
+	Jid j(list[0]);
+	aReply(j, "", d->le_subj->text(), d->thread);
+}
+
+void EventDlg::doQuote()
+{
+	QStringList list = stringToList(d->le_from->text());
+	if(list.isEmpty())
+		return;
+	Jid j(list[0]);
+
+	QString body;
+	if(d->mle->hasSelectedText())
+		body = rich2plain(d->mle->selectedText());
+	else
+		body = rich2plain(d->mle->text());
+
+	aReply(j, body, d->le_subj->text(), d->thread);
+}
+
+void EventDlg::doDeny()
+{
+	if(!d->pa->checkConnected(this))
+		return;
+
+	QStringList list = stringToList(d->le_from->text());
+	if(list.isEmpty())
+		return;
+	Jid j(list[0]);
+	aDeny(j);
+	close();
+}
+
+void EventDlg::doAuth()
+{
+	if(!d->pa->checkConnected(this))
+		return;
+
+	QStringList list = stringToList(d->le_from->text());
+	if(list.isEmpty())
+		return;
+	Jid j(list[0]);
+	aAuth(j);
+	d->pb_auth->setEnabled(false);
+	closeAfterReply();
+}
+
+void EventDlg::doHistory()
+{
+	d->pa->actionHistory(d->jid.withResource(""));
+}
+
+void EventDlg::doInfo()
+{
+	d->pa->actionInfo(d->jid);
+}
+
+void EventDlg::closeAfterReply()
+{
+	if(d->nextAmount == 0)
+		close();
+}
+
+void EventDlg::addUrl()
+{
+	AddUrlDlg *w = new AddUrlDlg(this);
+	int n = w->exec();
+	if(n != QDialog::Accepted) {
+		delete w;
+		return;
+	}
+
+	QString url = w->le_url->text();
+	QString desc = w->le_desc->text();
+	delete w;
+
+	d->attachView->urlAdd(url, desc);
+}
+
+void EventDlg::showHideAttachView()
+{
+	if(d->attachView->childCount() > 0) {
+		if(d->attachView->isHidden())
+			d->attachView->show();
+	}
+	else {
+		if(!d->attachView->isHidden())
+			d->attachView->hide();
+	}
+}
+
+void EventDlg::updateContact(const Jid &jid)
+{
+	if(d->jid.compare(jid, false)) {
+		QString rname = d->jid.resource();
+		QPtrList<UserListItem> ul = d->pa->findRelavent(d->jid);
+		UserListItem *u = 0;
+		int status = -1;
+		if(!ul.isEmpty()) {
+			u = ul.first();
+			if(rname.isEmpty()) {
+				// use priority
+				if(!u->isAvailable())
+					status = STATUS_OFFLINE;
+				else
+					status = makeSTATUS((*u->userResourceList().priority()).status());
+			}
+			else {
+				// use specific
+				UserResourceList::ConstIterator rit = u->userResourceList().find(rname);
+				if(rit != u->userResourceList().end())
+					status = makeSTATUS((*rit).status());
+				else
+					status = STATUS_OFFLINE;
+			}
+		}
+
+		if(status == -1 || !u)
+			d->lb_status->setIcon(IconsetFactory::iconPtr("status/noauth"));
+		else
+			d->lb_status->setIcon(is->statusPtr(jid, status));
+
+		if(u)
+			QToolTip::add(d->lb_status, u->makeTip(true, false));
+		else
+			QToolTip::remove(d->lb_status);
+
+		if(u)
+			setCaption(jidnick(u->jid().full(), u->name()));
+	}
+}
+
+void EventDlg::setTime(const QDateTime &t, bool late)
+{
+	QString str;
+	//str.sprintf("<nobr>%02d/%02d %02d:%02d:%02d</nobr>", t.date().month(), t.date().day(), t.time().hour(), t.time().minute(), t.time().second());
+	str = QString("<nobr>") + t.toString(LocalDate) + "</nobr>";
+	if(late)
+		str = QString("<font color=\"red\">") + str + "</font>";
+
+	d->lb_time->setText(str);
+}
+
+void EventDlg::updateEvent(PsiEvent *e)
+{
+	Icon *oldanim = d->anim;
+	d->anim = is->event2icon(e);
+
+	if(d->anim != oldanim)
+		setIcon(d->anim->impix());
+
+	d->le_from->setText(expandAddresses(e->from().full(), false));
+	d->le_from->setCursorPosition(0);
+	QToolTip::add(d->le_from, e->from().full());
+	setTime(e->timeStamp(), e->late());
+
+	d->enc = false;
+
+	if(e->type() == PsiEvent::Message) {
+		MessageEvent *me = (MessageEvent *)e;
+		const Message &m = me->message();
+
+		d->enc = m.wasEncrypted();
+
+		d->pb_auth->hide();
+		d->pb_deny->hide();
+
+		d->pb_chat->show();
+		d->pb_reply->show();
+		d->pb_quote->show();
+
+		QString txt = plain2rich(m.body());
+
+		// show subject line if the incoming message has one
+		if(m.subject() != "" && !option.showSubjects)
+                	txt = "<p><font color=\"red\"><b>" + tr("Subject:") + " " + m.subject() + "</b></font></p>" + txt;
+
+		d->mle->moveCursor(QTextEdit::MoveHome, false);
+		d->mle->setTextFormat(RichText);
+
+		if(option.useEmoticons)
+			txt = emoticonify(txt);
+
+		d->mle->setText("<qt>" + linkify(txt) + "</qt>");
+
+		d->le_subj->setText(m.subject());
+		d->le_subj->setCursorPosition(0);
+
+		d->thread = m.thread();
+
+		d->attachView->clear();
+		d->attachView->addUrlList(m.urlList());
+
+		if(!m.invite().isEmpty())
+			d->attachView->gcAdd(m.invite());
+		showHideAttachView();
+	}
+	else if(e->type() == PsiEvent::Auth) {
+		AuthEvent *ae = (AuthEvent *)e;
+		QString type = ae->authType();
+
+		d->le_subj->setText("");
+		if(type == "subscribe") {
+			QString body(tr("<big>[System Message]</big><br>This user wants to subscribe to your presence.  Click the button labelled \"Add/Auth\" to authorize the subscription.  This will also add the person to your contact list if it is not already there."));
+			d->mle->moveCursor(QTextEdit::MoveHome, false);
+			d->mle->setTextFormat(RichText);
+			d->mle->setText("<qt>" + body + "</qt>");
+
+			d->pb_chat->show();
+			d->pb_reply->hide();
+			d->pb_quote->hide();
+
+			d->pb_auth->setEnabled(true);
+			d->pb_auth->show();
+			d->pb_deny->show();
+		}
+		else if(type == "subscribed") {
+			QString body(tr("<big>[System Message]</big><br>You are now authorized."));
+			d->mle->moveCursor(QTextEdit::MoveHome, false);
+			d->mle->setTextFormat(RichText);
+			d->mle->setText("<qt>" + body + "</qt>");
+
+			d->pb_auth->hide();
+			d->pb_deny->hide();
+			d->pb_chat->show();
+			d->pb_reply->show();
+			d->pb_quote->show();
+		}
+		else if(type == "unsubscribed") {
+			QString body(tr("<big>[System Message]</big><br>Your authorization has been removed!"));
+			d->mle->moveCursor(QTextEdit::MoveHome, false);
+			d->mle->setTextFormat(RichText);
+			d->mle->setText("<qt>" + body + "</qt>");
+
+			d->pb_auth->hide();
+			d->pb_deny->hide();
+			d->pb_chat->show();
+			d->pb_reply->show();
+			d->pb_quote->show();
+		}
+	}
+
+	if(d->lb_pgp)
+		d->lb_pgp->setIcon( IconsetFactory::iconPtr(d->enc ? "psi/cryptoYes" : "psi/cryptoNo") );
+
+	if(!d->le_subj->text().isEmpty())
+		QToolTip::add(d->le_subj, d->le_subj->text());
+	else
+		QToolTip::remove(d->le_subj);
+
+	doWhois();
+}
+
+void EventDlg::updateReadNext(Icon *nextAnim, int nextAmount)
+{
+	int oldAmount = d->nextAmount;
+
+	d->nextAnim = nextAnim;
+	d->nextAmount = nextAmount;
+
+	if(nextAmount == 0) {
+		d->nextAnim = 0;
+		d->pb_next->forceSetIcon(0);
+		d->pb_next->setEnabled(false);
+		d->pb_next->setText(tr("&Next"));
+
+		if(d->pb_reply->isEnabled())
+			d->pb_reply->setFocus();
+		else if(d->pb_auth->isVisible())
+			d->pb_auth->setFocus();
+	}
+	else {
+		d->pb_next->setEnabled(true);
+		QString str(tr("&Next"));
+		str += QString(" - %1").arg(nextAmount);
+		d->pb_next->setText(str);
+
+		d->pb_next->forceSetIcon(d->nextAnim);
+
+		if(d->nextAmount > oldAmount)
+			d->pb_next->setFocus();
+	}
+}
+
+void EventDlg::actionGCJoin(const QString &gc)
+{
+	Jid j(gc);
+	d->pa->actionJoin(j.withResource(""));
+}
+
+void EventDlg::updatePGP()
+{
+	if(d->tb_pgp) {
+		if(!d->pa->pgpKey().isEmpty()) {
+			d->tb_pgp->setEnabled(true);
+		}
+		else {
+			d->tb_pgp->setOn(false);
+			d->tb_pgp->setEnabled(false);
+		}
+	}
+}
+
+void EventDlg::trySendEncryptedNext()
+{
+	if(d->sendLeft.isEmpty())
+		return;
+	Message m = d->m;
+	m.setTo(Jid(d->sendLeft.first()));
+	d->transid = d->pa->sendMessageEncrypted(m);
+	if(d->transid == -1) {
+		d->le_to->setEnabled(true);
+		d->mle->setEnabled(true);
+		d->mle->setFocus();
+		return;
+	}
+}
+
+void EventDlg::encryptedMessageSent(int x, bool b)
+{
+	if(d->transid == -1)
+		return;
+	if(d->transid != x)
+		return;
+	d->transid = -1;
+	if(b) {
+		// remove the item
+		QStringList::Iterator it = d->sendLeft.begin();
+		Jid j(*it);
+		d->sendLeft.remove(it);
+
+		//d->pa->toggleSecurity(j, d->enc);
+
+		if(d->sendLeft.isEmpty()) {
+			d->le_to->setEnabled(true);
+			d->mle->setEnabled(true);
+			doneSend();
+		}
+		else {
+			trySendEncryptedNext();
+			return;
+		}
+	}
+	else
+		QMessageBox::information(this, tr("Error"), tr("There was an error trying to send the message encrypted.\nCheck your OpenPGP application/settings."));
+
+	d->le_to->setEnabled(true);
+	d->mle->setEnabled(true);
+	d->mle->setFocus();
+}
+
+#include "eventdlg.moc"
Index: psi/tags/0.10-os2-b1/src/eventdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/eventdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/eventdlg.h	(revision 38)
@@ -0,0 +1,189 @@
+/*
+ * eventdlg.h - dialog for sending / receiving messages and events
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef EVENTDLG_H
+#define EVENTDLG_H
+
+#include"advwidget.h"
+#define private protected
+#include<qlineedit.h>
+#undef private
+#include<qlistview.h>
+
+#include"im.h"
+#include"userlist.h"
+#include"ui_addurl.h"
+
+using namespace XMPP;
+
+class QDateTime;
+class QStringList;
+class PsiEvent;
+class PsiCon;
+class PsiAccount;
+class Icon;
+
+class EventDlg;
+class ELineEdit : public QLineEdit
+{
+	Q_OBJECT
+public:
+	ELineEdit(EventDlg *parent, const char *name=0);
+
+signals:
+	void changeResource(const QString &);
+	void tryComplete();
+
+protected:
+	// reimplemented
+	void dragEnterEvent(QDragEnterEvent *);
+	void dropEvent(QDropEvent *);
+	void keyPressEvent(QKeyEvent *);
+	QPopupMenu *createPopupMenu();
+
+private slots:
+	void resourceMenuActivated(int);
+
+private:
+	UserResourceList url;
+};
+
+class AttachView : public QListView
+{
+	Q_OBJECT
+public:
+	AttachView(QWidget *parent=0, const char *name=0);
+	~AttachView();
+
+	void setReadOnly(bool);
+	void urlAdd(const QString &, const QString &);
+	void gcAdd(const QString &);
+
+	UrlList urlList() const;
+	void addUrlList(const UrlList &);
+
+signals:
+	void childCountChanged();
+	void actionGCJoin(const QString &);
+
+private slots:
+	void qlv_context(QListViewItem *, const QPoint &, int);
+	void qlv_doubleClicked(QListViewItem *);
+
+private:
+	bool v_readOnly;
+
+	void goURL(const QString &);
+};
+
+class AddUrlDlg : public AddUrlUI
+{
+	Q_OBJECT
+public:
+	AddUrlDlg(QWidget *parent=0, const char *name=0);
+	~AddUrlDlg();
+};
+
+class EventDlg : public AdvancedWidget<QWidget>
+{
+	Q_OBJECT
+public:
+	// compose
+	EventDlg(const QString &, PsiCon *, PsiAccount *);
+	// read
+	EventDlg(const Jid &, PsiAccount *, bool unique);
+	~EventDlg();
+
+	QString text() const;
+	void setText(const QString &);
+	void setSubject(const QString &);
+	void setThread(const QString &);
+	void setUrlOnShow();
+
+	PsiAccount *psiAccount();
+
+	static QSize defaultSize();
+
+signals:
+	void aChat(const Jid& jid);
+	void aReply(const Jid &jid, const QString &body, const QString &subject, const QString &thread);
+	void aReadNext(const Jid &);
+	void aDeny(const Jid &);
+	void aAuth(const Jid &);
+
+protected:
+	// reimplemented
+	void showEvent(QShowEvent *);
+	void resizeEvent(QResizeEvent *);
+	void keyPressEvent(QKeyEvent *);
+	void closeEvent(QCloseEvent *);
+
+public slots:
+	void optionsUpdate();
+	void closeAfterReply();
+	void updateContact(const Jid &);
+	void updateEvent(PsiEvent *);
+	void updateReadNext(Icon *, int);
+	void actionGCJoin(const QString &);
+
+private slots:
+	void to_textChanged(const QString &);
+	void to_changeResource(const QString &);
+	void to_tryComplete();
+	void updateIdentity(PsiAccount *);
+	void accountUpdatedActivity();
+	void doWhois(bool force=false);
+	void doSend();
+	void doReadNext();
+	void doChat();
+	void doReply();
+	void doQuote();
+	void doDeny();
+	void doAuth();
+	void doInfo();
+	void doHistory();
+	void showHideAttachView();
+	void addUrl();
+
+	void updatePGP();
+	void encryptedMessageSent(int, bool);
+	void trySendEncryptedNext();
+
+public:
+	class Private;
+private:
+	Private *d;
+
+	void doneSend();
+
+	void init();
+	QStringList stringToList(const QString &, bool enc=true) const;
+	QString findJidInString(const QString &) const;
+	QString expandAddresses(const QString &, bool enc=true) const;
+	void buildCompletionList();
+	void setAccount(PsiAccount *);
+	void setTime(const QDateTime &, bool late=false);
+
+	friend class ELineEdit;
+	UserResourceList getResources(const QString &) const;
+	QString jidToString(const Jid &, const QString &r="") const;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/filetransdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/filetransdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/filetransdlg.cpp	(revision 38)
@@ -0,0 +1,1730 @@
+#include"filetransdlg.h"
+
+#include<qlabel.h>
+#include<qlineedit.h>
+#include<qpushbutton.h>
+#include<qtimer.h>
+#include<qfiledialog.h>
+#include<qfile.h>
+#include<qprogressbar.h>
+#include<qdir.h>
+#include<qlistview.h>
+#include<qlayout.h>
+#include<qhbox.h>
+#include<qdatetime.h>
+#include<qpainter.h>
+#include<qheader.h>
+#include<qtooltip.h>
+#include<qpopupmenu.h>
+#include<qtextedit.h>
+#include"psicon.h"
+#include"psiaccount.h"
+#include"userlist.h"
+#include"common.h"
+#include"iconwidget.h"
+#include"busywidget.h"
+#include"filetransfer.h"
+#include"profiles.h"
+#include"psiiconset.h"
+#include"msgmle.h"
+
+#if QT_VERSION >= 0x030200
+typedef Q_UINT64 LARGE_TYPE;
+#else
+typedef Q_UINT32 LARGE_TYPE;
+#endif
+
+#define CSMAX (sizeof(LARGE_TYPE)*8)
+#define CSMIN 16
+static int calcShift(Q_LLONG big)
+{
+	LARGE_TYPE val = 1;
+	val <<= CSMAX - 1;
+	for(int n = CSMAX - CSMIN; n > 0; --n) {
+		if(big & val)
+			return n;
+		val >>= 1;
+	}
+	return 0;
+}
+
+static int calcComplement(Q_LLONG big, int shift)
+{
+	int block = 1 << shift;
+	Q_LLONG rem = big % block;
+	if(rem == 0)
+		return 0;
+	else
+		return (block - (int)rem);
+}
+
+static int calcTotalSteps(Q_LLONG big, int shift)
+{
+	if(big < 1)
+		return 0;
+	return ((big - 1) >> shift) + 1;
+}
+
+static int calcProgressStep(Q_LLONG big, int complement, int shift)
+{
+	return ((big + complement) >> shift);
+}
+
+static QStringList *activeFiles = 0;
+
+static void active_file_add(const QString &s)
+{
+	if(!activeFiles)
+		activeFiles = new QStringList;
+	activeFiles->append(s);
+	//printf("added: [%s]\n", s.latin1());
+}
+
+static void active_file_remove(const QString &s)
+{
+	if(!activeFiles)
+		return;
+	activeFiles->remove(s);
+	//printf("removed: [%s]\n", s.latin1());
+}
+
+static bool active_file_check(const QString &s)
+{
+	if(!activeFiles)
+		return false;
+	return activeFiles->contains(s);
+}
+
+static QString clean_filename(const QString &s)
+{
+//#ifdef Q_OS_WIN
+	QString badchars = "\\/|?*:\"<>";
+	QString str;
+	for(uint n = 0; n < s.length(); ++n) {
+		bool found = false;
+		for(uint b = 0; b < badchars.length(); ++b) {
+			if(s.at(n) == badchars.at(b)) {
+				found = true;
+				break;
+			}
+		}
+		if(!found)
+			str += s;
+	}
+	if(str.isEmpty())
+		str = "unnamed";
+	return str;
+//#else
+//	return s;
+//#endif
+}
+
+//----------------------------------------------------------------------------
+// FileTransferHandler
+//----------------------------------------------------------------------------
+class FileTransferHandler::Private
+{
+public:
+	PsiAccount *pa;
+	FileTransfer *ft;
+	S5BConnection *c;
+	Jid peer;
+	QString fileName, saveName;
+	Q_LLONG fileSize, sent, offset, length;
+	QString desc;
+	bool sending;
+	QFile f;
+	int shift;
+	int complement;
+	QString activeFile;
+};
+
+FileTransferHandler::FileTransferHandler(PsiAccount *pa, FileTransfer *ft)
+{
+	d = new Private;
+	d->pa = pa;
+	d->c = 0;
+
+	if(ft) {
+		d->sending = false;
+		d->peer = ft->peer();
+		d->fileName = clean_filename(ft->fileName());
+		d->fileSize = ft->fileSize();
+		d->desc = ft->description();
+		d->shift = calcShift(d->fileSize);
+		d->complement = calcComplement(d->fileSize, d->shift);
+		d->ft = ft;
+		Jid proxy = d->pa->userAccount().dtProxy;
+		if(proxy.isValid())
+			d->ft->setProxy(proxy);
+		mapSignals();
+	}
+	else {
+		d->sending = true;
+		d->ft = 0;
+	}
+}
+
+FileTransferHandler::~FileTransferHandler()
+{
+	if(!d->activeFile.isEmpty())
+		active_file_remove(d->activeFile);
+
+	if(d->ft) {
+		d->ft->close();
+		delete d->ft;
+	}
+	delete d;
+}
+
+void FileTransferHandler::send(const XMPP::Jid &to, const QString &fname, const QString &desc)
+{
+	if(!d->sending)
+		return;
+
+	d->peer = to;
+	QFileInfo fi(fname);
+	d->fileName = fi.fileName();
+	d->fileSize = fi.size(); // TODO: large file support
+	d->desc = desc;
+	d->shift = calcShift(d->fileSize);
+	d->complement = calcComplement(d->fileSize, d->shift);
+
+	d->ft = d->pa->client()->fileTransferManager()->createTransfer();
+	Jid proxy = d->pa->userAccount().dtProxy;
+	if(proxy.isValid())
+		d->ft->setProxy(proxy);
+	mapSignals();
+
+	d->f.setName(fname);
+	d->ft->sendFile(d->peer, d->fileName, d->fileSize, desc);
+}
+
+PsiAccount *FileTransferHandler::account() const
+{
+	return d->pa;
+}
+
+int FileTransferHandler::mode() const
+{
+	return (d->sending ? Sending : Receiving);
+}
+
+Jid FileTransferHandler::peer() const
+{
+	return d->peer;
+}
+
+QString FileTransferHandler::fileName() const
+{
+	return d->fileName;
+}
+
+Q_LLONG FileTransferHandler::fileSize() const
+{
+	return d->fileSize;
+}
+
+QString FileTransferHandler::description() const
+{
+	return d->desc;
+}
+
+Q_LLONG FileTransferHandler::offset() const
+{
+	return d->offset;
+}
+
+int FileTransferHandler::totalSteps() const
+{
+	return calcTotalSteps(d->fileSize, d->shift);
+}
+
+bool FileTransferHandler::resumeSupported() const
+{
+	if(d->ft)
+		return d->ft->rangeSupported();
+	else
+		return false;
+}
+
+QString FileTransferHandler::saveName() const
+{
+	return d->saveName;
+}
+
+void FileTransferHandler::accept(const QString &saveName, const QString &fileName, Q_LLONG offset)
+{
+	if(d->sending)
+		return;
+	d->fileName = fileName;
+	d->saveName = saveName;
+	d->offset = offset;
+	d->length = d->fileSize;
+	d->f.setName(saveName);
+	d->ft->accept(offset);
+}
+
+void FileTransferHandler::s5b_proxyQuery()
+{
+	statusMessage(tr("Quering proxy..."));
+}
+
+void FileTransferHandler::s5b_proxyResult(bool b)
+{
+	if(b)
+		statusMessage(tr("Proxy query successful."));
+	else
+		statusMessage(tr("Proxy query failed!"));
+}
+
+void FileTransferHandler::s5b_requesting()
+{
+	statusMessage(tr("Requesting data transfer channel..."));
+}
+
+void FileTransferHandler::s5b_accepted()
+{
+	statusMessage(tr("Peer accepted request."));
+}
+
+void FileTransferHandler::s5b_tryingHosts(const StreamHostList &)
+{
+	statusMessage(tr("Connecting to peer..."));
+}
+
+void FileTransferHandler::s5b_proxyConnect()
+{
+	statusMessage(tr("Connecting to proxy..."));
+}
+
+void FileTransferHandler::s5b_waitingForActivation()
+{
+	statusMessage(tr("Waiting for peer activation..."));
+}
+
+void FileTransferHandler::ft_accepted()
+{
+	d->offset = d->ft->offset();
+	d->length = d->ft->length();
+
+	d->c = d->ft->s5bConnection();
+	connect(d->c, SIGNAL(proxyQuery()), SLOT(s5b_proxyQuery()));
+	connect(d->c, SIGNAL(proxyResult(bool)), SLOT(s5b_proxyResult(bool)));
+	connect(d->c, SIGNAL(requesting()), SLOT(s5b_requesting()));
+	connect(d->c, SIGNAL(accepted()), SLOT(s5b_accepted()));
+	connect(d->c, SIGNAL(tryingHosts(const StreamHostList &)), SLOT(s5b_tryingHosts(const StreamHostList &)));
+	connect(d->c, SIGNAL(proxyConnect()), SLOT(s5b_proxyConnect()));
+	connect(d->c, SIGNAL(waitingForActivation()), SLOT(s5b_waitingForActivation()));
+
+	if(d->sending)
+		accepted();
+}
+
+void FileTransferHandler::ft_connected()
+{
+	d->sent = d->offset;
+
+	if(d->sending) {
+		// open the file, and set the correct offset
+		bool ok = false;
+		if(d->f.open(IO_ReadOnly)) {
+			if(d->offset == 0) {
+				ok = true;
+			}
+			else {
+				if(d->f.at(d->offset))
+					ok = true;
+			}
+		}
+		if(!ok) {
+			delete d->ft;
+			d->ft = 0;
+			error(ErrFile, 0, "");
+			return;
+		}
+
+		if(d->sent == d->fileSize)
+			QTimer::singleShot(0, this, SLOT(doFinish()));
+		else
+			QTimer::singleShot(0, this, SLOT(trySend()));
+	}
+	else {
+		// open the file, truncating if offset is zero, otherwise set the correct offset
+		int m = IO_ReadWrite;
+		if(d->offset == 0)
+			m |= IO_Truncate;
+		bool ok = false;
+		if(d->f.open(m)) {
+			if(d->offset == 0) {
+				ok = true;
+			}
+			else {
+				if(d->f.at(d->offset))
+					ok = true;
+			}
+		}
+		if(!ok) {
+			delete d->ft;
+			d->ft = 0;
+			error(ErrFile, 0, "");
+			return;
+		}
+
+		d->activeFile = d->f.name();
+		active_file_add(d->activeFile);
+
+		// done already?  this means a file size of zero
+		if(d->sent == d->fileSize)
+			QTimer::singleShot(0, this, SLOT(doFinish()));
+	}
+
+	connected();
+}
+
+void FileTransferHandler::ft_readyRead(const QByteArray &a)
+{
+	if(!d->sending) {
+		//printf("%d bytes read\n", a.size());
+		int r = d->f.writeBlock(a.data(), a.size());
+		if(r < 0) {
+			d->f.close();
+			delete d->ft;
+			d->ft = 0;
+			error(ErrFile, 0, "");
+			return;
+		}
+		d->sent += a.size();
+		doFinish();
+	}
+}
+
+void FileTransferHandler::ft_bytesWritten(int x)
+{
+	if(d->sending) {
+		//printf("%d bytes written\n", x);
+		d->sent += x;
+		if(d->sent == d->fileSize) {
+			d->f.close();
+			delete d->ft;
+			d->ft = 0;
+		}
+		else
+			QTimer::singleShot(0, this, SLOT(trySend()));
+		progress(calcProgressStep(d->sent, d->complement, d->shift), d->sent);
+	}
+}
+
+void FileTransferHandler::ft_error(int x)
+{
+	if(d->f.isOpen())
+		d->f.close();
+	delete d->ft;
+	d->ft = 0;
+
+	if(x == FileTransfer::ErrReject)
+		error(ErrReject, x, "");
+	else if(x == FileTransfer::ErrNeg)
+		error(ErrTransfer, x, tr("Unable to negotiate transfer."));
+	else if(x == FileTransfer::ErrConnect)
+		error(ErrTransfer, x, tr("Unable to connect to peer for data transfer."));
+	else if(x == FileTransfer::ErrProxy)
+		error(ErrTransfer, x, tr("Unable to connect to proxy for data transfer."));
+	else if(x == FileTransfer::ErrStream)
+		error(ErrTransfer, x, tr("Lost connection / Cancelled."));
+}
+
+void FileTransferHandler::trySend()
+{
+	int blockSize = d->ft->dataSizeNeeded();
+	QByteArray a(blockSize);
+	int r = d->f.readBlock(a.data(), a.size());
+	if(r < 0) {
+		d->f.close();
+		delete d->ft;
+		d->ft = 0;
+		error(ErrFile, 0, "");
+		return;
+	}
+	if(r < (int)a.size())
+		a.resize(r);
+	d->ft->writeFileData(a);
+}
+
+void FileTransferHandler::doFinish()
+{
+	if(d->sent == d->fileSize) {
+		d->f.close();
+		delete d->ft;
+		d->ft = 0;
+	}
+	progress(calcProgressStep(d->sent, d->complement, d->shift), d->sent);
+}
+
+void FileTransferHandler::mapSignals()
+{
+	connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
+	connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
+	connect(d->ft, SIGNAL(readyRead(const QByteArray &)), SLOT(ft_readyRead(const QByteArray &)));
+	connect(d->ft, SIGNAL(bytesWritten(int)), SLOT(ft_bytesWritten(int)));
+	connect(d->ft, SIGNAL(error(int)), SLOT(ft_error(int)));
+}
+
+//----------------------------------------------------------------------------
+// FileRequestDlg
+//----------------------------------------------------------------------------
+
+class FileRequestDlg::Private
+{
+public:
+	PsiCon *psi;
+	PsiAccount *pa;
+	AccountsComboBox *cb_ident;
+	QLabel *lb_ident, *lb_time;
+	ChatView *te;
+	Jid jid;
+	FileTransferHandler *ft;
+	QString fileName;
+	Q_LLONG fileSize;
+	bool sending;
+	QTimer t;
+};
+
+
+FileRequestDlg::FileRequestDlg(const Jid &j, PsiCon *psi, PsiAccount *pa) 
+:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
+{
+	QStringList l;
+	FileRequestDlg(j, psi, pa, l);
+}
+
+
+FileRequestDlg::FileRequestDlg(const Jid &jid, PsiCon *psi, PsiAccount *pa, const QStringList& files)
+:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
+{
+	d = new Private;
+	d->psi = psi;
+	d->pa = 0;
+	d->jid = jid;
+	d->ft = 0;
+	d->sending = true;
+	updateIdentity(pa);
+
+	QHBox *hb = new QHBox(this);
+	new QLabel(tr("Identity: "), hb);
+	d->cb_ident = d->psi->accountsComboBox(hb);
+	connect(d->cb_ident, SIGNAL(activated(PsiAccount *)), SLOT(updateIdentity(PsiAccount *)));
+	d->cb_ident->setAccount(pa);
+	replaceWidget(lb_accountlabel, hb);
+	setTabOrder(d->cb_ident, le_to);
+
+	d->te = new ChatView(this);
+	d->te->setReadOnly(false);
+	d->te->setTextFormat(PlainText);
+	replaceWidget(te_desc, d->te);
+	setTabOrder(le_fname, d->te);
+	setTabOrder(d->te, pb_stop);
+
+	setCaption(tr("Send File"));
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/upload"));
+#endif
+
+	le_to->setText(d->jid.full());
+	le_to->setReadOnly(false);
+	pb_start->setText(tr("&Send"));
+	pb_stop->setText(tr("&Close"));
+
+	connect(tb_browse, SIGNAL(clicked()), SLOT(chooseFile()));
+	connect(pb_start, SIGNAL(clicked()), SLOT(doStart()));
+	connect(pb_stop, SIGNAL(clicked()), SLOT(close()));
+
+	lb_status->setText(tr("Ready"));
+
+	d->te->setFocus();
+	d->psi->dialogRegister(this);
+
+	if (files.isEmpty()) {
+		QTimer::singleShot(0, this, SLOT(chooseFile()));
+	}
+	else {
+		// TODO: Once sending of multiple files is supported, change this
+		QFileInfo fi(files.first());
+
+		// Check if the file is legal
+		if(!fi.exists()) {
+			QMessageBox::critical(this, tr("Error"), QString("The file '%1' does not exist.").arg(files.first()));
+			QTimer::singleShot(0, this, SLOT(reject()));
+			return;
+		}
+		if(fi.isDir()) {
+			QMessageBox::critical(this, tr("Error"), tr("Sending folders is not supported."));
+			QTimer::singleShot(0, this, SLOT(reject()));
+			return;
+		}
+		
+		option.lastPath = fi.dirPath();
+		le_fname->setText(QDir::convertSeparators(fi.filePath()));
+		lb_size->setText(tr("%1 byte(s)").arg(fi.size())); // TODO: large file support
+	}
+}
+
+FileRequestDlg::FileRequestDlg(const QDateTime &ts, FileTransfer *ft, PsiAccount *pa)
+:FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose)
+{
+	d = new Private;
+	d->psi = 0;
+	d->pa = 0;
+	d->jid = ft->peer();
+	d->ft = new FileTransferHandler(pa, ft);
+	d->sending = false;
+	updateIdentity(pa);
+
+	d->fileName = ft->fileName();
+	d->fileSize = ft->fileSize();
+
+	d->cb_ident = 0;
+	QHBox *hb = new QHBox(this);
+	new QLabel(tr("Identity: "), hb);
+	d->lb_ident = d->pa->accountLabel(hb);
+	d->lb_ident->setSizePolicy(QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ));
+	new QLabel(tr("Time:"), hb);
+	d->lb_time = new QLabel(ts.time().toString(LocalDate), hb);
+	d->lb_time->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+
+	replaceWidget(lb_accountlabel, hb);
+
+	d->te = new ChatView(this);
+	d->te->setTextFormat(PlainText);
+	replaceWidget(te_desc, d->te);
+	setTabOrder(le_fname, d->te);
+	setTabOrder(d->te, pb_stop);
+
+	lb_to->setText(tr("From:"));
+	setCaption(tr("Receive File"));
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/download"));
+#endif
+
+	le_to->setText(d->jid.full());
+	le_fname->setText(d->fileName);
+	lb_size->setText(tr("%1 byte(s)").arg(d->fileSize));
+	d->te->setReadOnly(true);
+	d->te->setText(ft->description());
+	pb_start->setText(tr("&Accept"));
+	pb_stop->setText(tr("&Reject"));
+
+	tb_browse->hide();
+
+	connect(pb_start, SIGNAL(clicked()), SLOT(doStart()));
+	connect(pb_stop, SIGNAL(clicked()), SLOT(close()));
+
+	connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+
+	lb_status->setText(tr("Ready"));
+
+	pb_start->setFocus();
+	d->pa->dialogRegister(this);
+}
+
+FileRequestDlg::~FileRequestDlg()
+{
+	delete d->ft;
+	if(d->psi)
+		d->psi->dialogUnregister(this);
+	else
+		d->pa->dialogUnregister(this);
+	delete d;
+}
+
+void FileRequestDlg::done(int r)
+{
+	if(busy->isActive()) {
+		int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the transfer?"), tr("&Yes"), tr("&No"));
+		if(n != 0)
+			return;
+
+		// close/reject FT if there is one
+		if(d->ft) {
+			delete d->ft;
+			d->ft = 0;
+		}
+	}
+
+	QDialog::done(r);
+}
+
+void FileRequestDlg::keyPressEvent(QKeyEvent *e)
+{
+	if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) ) {
+		if(pb_start->isEnabled())
+			doStart();
+	}
+	else
+		QDialog::keyPressEvent(e);
+}
+
+void FileRequestDlg::updateIdentity(PsiAccount *pa)
+{
+	if(d->pa)
+		disconnect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
+
+	if(!pa) {
+		close();
+		return;
+	}
+
+	d->pa = pa;
+	connect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
+}
+
+void FileRequestDlg::pa_disconnected()
+{
+	//if(busy->isActive()) {
+	//	busy->stop();
+	//	close();
+	//}
+}
+
+void FileRequestDlg::blockWidgets()
+{
+	if(d->cb_ident)
+		d->cb_ident->setEnabled(false);
+	le_to->setEnabled(false);
+	le_fname->setEnabled(false);
+	tb_browse->setEnabled(false);
+	d->te->setEnabled(false);
+	pb_start->setEnabled(false);
+}
+
+void FileRequestDlg::unblockWidgets()
+{
+	if(d->cb_ident)
+		d->cb_ident->setEnabled(true);
+	le_to->setEnabled(true);
+	le_fname->setEnabled(true);
+	tb_browse->setEnabled(true);
+	d->te->setEnabled(true);
+	pb_start->setEnabled(true);
+}
+
+void FileRequestDlg::chooseFile()
+{
+	while(1) {
+		if(option.lastPath.isEmpty())
+			option.lastPath = QDir::homeDirPath();
+		QString str = QFileDialog::getOpenFileName(option.lastPath, tr("All files (*)"), this, 0, tr("Choose a file"));
+		if(!str.isEmpty()) {
+			QFileInfo fi(str);
+			if(!fi.exists()) {
+				QMessageBox::information(this, tr("Error"), tr("The file specified does not exist."));
+				continue;
+			}
+			option.lastPath = fi.dirPath();
+			le_fname->setText(QDir::convertSeparators(fi.filePath()));
+			lb_size->setText(tr("%1 byte(s)").arg(fi.size())); // TODO: large file support
+		}
+		break;
+	}
+}
+
+void FileRequestDlg::doStart()
+{
+	if(!d->pa->checkConnected(this))
+		return;
+
+	if(d->sending) {
+		Jid to = le_to->text();
+		if(!to.isValid()) {
+			QMessageBox::information(this, tr("Error"), tr("The Jabber ID specified is not valid.  Correct this and try again."));
+			return;
+		}
+
+		QFileInfo fi(le_fname->text());
+		if(!fi.exists()) {
+			QMessageBox::information(this, tr("Error"), tr("The file specified does not exist.  Choose a correct file name before sending."));
+			return;
+		}
+
+		blockWidgets();
+
+		pb_stop->setText(tr("&Cancel"));
+		pb_stop->setFocus();
+		busy->start();
+		lb_status->setText(tr("Requesting..."));
+
+		d->fileName = fi.fileName();
+		d->fileSize = fi.size(); // TODO: large file support
+
+		d->ft = new FileTransferHandler(d->pa);
+		connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
+		connect(d->ft, SIGNAL(statusMessage(const QString &)), SLOT(ft_statusMessage(const QString &)));
+		connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
+		connect(d->ft, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
+		d->ft->send(le_to->text(), le_fname->text(), d->te->text());
+	}
+	else {
+		QString fname, savename;
+		bool overwrite = false;
+		while(1) {
+			if(option.lastSavePath.isEmpty())
+				option.lastSavePath = QDir::homeDirPath();
+			fname = QFileDialog::getSaveFileName(QDir(option.lastSavePath).filePath(d->fileName), tr("All files (*)"), this, 0, tr("Save As"));
+			if(!fname.isEmpty()) {
+				QFileInfo fi(fname);
+				if(fi.exists()) {
+					int x = QMessageBox::information(this, tr("Error"), tr("File already exists.  Overwrite?"), tr("&Yes"), tr("&No"));
+					if(x != 0)
+						continue;
+					overwrite = true;
+				}
+				option.lastSavePath = fi.dirPath();
+				savename = fname + ".part";
+				fname = fi.fileName();
+			}
+			else
+				return;
+
+			break;
+		}
+
+		if(active_file_check(savename)) {
+			QMessageBox::information(this, tr("Error"), tr("This file is being transferred already!"));
+			return;
+		}
+
+		Q_LLONG resume_offset = 0;
+		if(!overwrite) {
+			// supports resume?  check for a .part
+			if(d->ft->resumeSupported()) {
+				QFileInfo fi(savename);
+				if(fi.exists())
+					resume_offset = fi.size();
+			}
+		}
+
+		pb_start->setEnabled(false);
+
+		le_fname->setText(fname);
+		pb_stop->setText(tr("&Cancel"));
+		pb_stop->setFocus();
+		busy->start();
+		lb_status->setText(tr("Accepting..."));
+
+		d->t.start(30000, true);
+
+		connect(d->ft, SIGNAL(accepted()), SLOT(ft_accepted()));
+		connect(d->ft, SIGNAL(statusMessage(const QString &)), SLOT(ft_statusMessage(const QString &)));
+		connect(d->ft, SIGNAL(connected()), SLOT(ft_connected()));
+		connect(d->ft, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
+		d->ft->accept(savename, fname, resume_offset);
+	}
+}
+
+void FileRequestDlg::ft_accepted()
+{
+	lb_status->setText(tr("Accepted!"));
+}
+
+void FileRequestDlg::ft_statusMessage(const QString &s)
+{
+	lb_status->setText(s);
+
+	// stop the timer at first activity
+	if(d->t.isActive())
+		d->t.stop();
+}
+
+void FileRequestDlg::ft_connected()
+{
+	d->t.stop();
+	busy->stop();
+	FileTransDlg *w = d->pa->psi()->ftdlg();
+	FileTransferHandler *h = d->ft;
+	d->ft = 0;
+	closeDialogs(this);
+	close();
+	bringToFront(w);
+
+	w->takeTransfer(h, 0, 0);
+}
+
+void FileRequestDlg::ft_error(int x, int fx, const QString &)
+{
+	d->t.stop();
+	busy->stop();
+
+	delete d->ft;
+	d->ft = 0;
+
+	closeDialogs(this);
+
+	if(d->sending) {
+		unblockWidgets();
+		pb_stop->setText(tr("&Close"));
+		lb_status->setText(tr("Ready"));
+	}
+
+	QString str;
+	if(x == FileTransferHandler::ErrReject)
+		str = tr("File was rejected by remote user.");
+	else if(x == FileTransferHandler::ErrTransfer) {
+		if(fx == FileTransfer::ErrNeg)
+			str = tr(
+				"Unable to negotiate transfer.\n\n"
+				"This can happen if the contact did not understand our request, or if the\n"
+				"contact is offline."
+				);
+		else if(fx == FileTransfer::ErrConnect)
+			str = tr(
+				"Unable to connect to peer for data transfer.\n\n"
+				"Ensure that your Data Transfer settings are proper.  If you are behind\n"
+				"a NAT router or firewall then you'll need to open the proper TCP port\n"
+				"or specify a Data Transfer Proxy in your account settings."
+				);
+		else if(fx == FileTransfer::ErrProxy)
+			str = tr(
+				"Failure to either connect to, or activate, the Data Transfer Proxy.\n\n"
+				"This means that the Proxy service is either not functioning or it is\n"
+				"unreachable.  If you are behind a firewall, then you'll need to ensure\n"
+				"that outgoing TCP connections are allowed."
+				);
+	}
+	else
+		str = tr("File I/O error");
+	QMessageBox::information(this, tr("Error"), str);
+
+	if(!d->sending || x == FileTransferHandler::ErrReject)
+		close();
+}
+
+void FileRequestDlg::t_timeout()
+{
+	delete d->ft;
+	d->ft = 0;
+
+	busy->stop();
+	closeDialogs(this);
+
+	QString str = tr("Unable to accept the file.  Perhaps the sender has cancelled the request.");
+	QMessageBox::information(this, tr("Error"), str);
+	close();
+}
+
+//----------------------------------------------------------------------------
+// FileTransDlg
+//----------------------------------------------------------------------------
+class FileTransItem : public QListViewItem
+{
+public:
+	QPixmap icon;
+	bool sending;
+	QString name;
+	Q_LLONG size;
+	QString peer;
+	QString rate;
+	int progress;
+	Q_LLONG sent;
+	int bps;
+	int timeRemaining;
+	int id;
+	int dist;
+	bool done;
+	QString error;
+
+	FileTransItem(QListView *parent, const QString &_name, Q_LLONG _size, const QString &_peer, bool _sending)
+	:QListViewItem(parent)
+	{
+		done = false;
+		sending = _sending;
+		name = _name;
+		size = _size;
+		peer = _peer;
+		rate = FileTransDlg::tr("N/A");
+		sent = 0;
+		progress = 0;
+		dist = -1;
+	}
+
+	void niceUnit(Q_LLONG n, Q_LLONG *div, QString *unit)
+	{
+		Q_LLONG gb = 1024 * 1024 * 1024;
+		Q_LLONG mb = 1024 * 1024;
+		Q_LLONG kb = 1024;
+		if(n >= gb) {
+			*div = gb;
+			*unit = QString("GB");
+		}
+		else if(n >= mb) {
+			*div = mb;
+			*unit = QString("MB");
+		}
+		else if(n >= kb) {
+			*div = kb;
+			*unit = QString("KB");
+		}
+		else {
+			*div = 1;
+			*unit = QString("B");
+		}
+	}
+
+	QString roundedNumber(Q_LLONG n, Q_LLONG div)
+	{
+		bool decimal = false;
+		if(div >= 1024) {
+			div /= 10;
+			decimal = true;
+		}
+		Q_LLONG x_long = n / div;
+		int x = (int)x_long;
+		if(decimal) {
+			double f = (double)x;
+			f /= 10;
+			return QString::number(f, 'f', 1);
+		}
+		else
+			return QString::number(x);
+	}
+
+	bool setProgress(int _progress, Q_LLONG _sent, int _bps)
+	{
+		progress = _progress;
+		sent = _sent;
+		bps = _bps;
+
+		if(bps > 0) {
+			Q_LLONG rest_long = size - sent;
+			rest_long /= bps;
+			int maxtime = (23 * 60 * 60) + (59 * 60) + (59); // 23h59m59s
+			if(rest_long > maxtime)
+				rest_long = maxtime;
+			timeRemaining = (int)rest_long;
+		}
+
+		int lastDist = dist;
+		dist = progressBarDist(progressBarWidth());
+		if(dist != lastDist)
+			return true;
+		else
+			return false;
+	}
+
+	void updateRate()
+	{
+		QString s;
+		{
+			Q_LLONG div;
+			QString unit;
+			niceUnit(size, &div, &unit);
+
+			s = roundedNumber(sent, div) + '/' + roundedNumber(size, div) + unit;
+
+			if(done) {
+				if(error.isEmpty())
+					s += QString(" ") + FileTransDlg::tr("[Done]");
+				else
+					s += QString(" ") + FileTransDlg::tr("[Error: %1]").arg(error);
+			}
+			else if(bps == -1)
+				s += "";
+			else if(bps == 0)
+				s += QString(" ") + FileTransDlg::tr("[Stalled]");
+			else {
+				niceUnit(bps, &div, &unit);
+				s += QString(" @ ") + FileTransDlg::tr("%1%2/s").arg(roundedNumber(bps, div)).arg(unit);
+
+				s += ", ";
+				QTime t = QTime().addSecs(timeRemaining);
+				s += FileTransDlg::tr("%1h%2m%3s remaining").arg(t.hour()).arg(t.minute()).arg(t.second());
+			}
+		}
+		rate = s;
+	}
+
+	int progressBarWidth() const
+	{
+		int m = 4;
+		int w = listView()->columnWidth(0);
+		//int pw = (w - (3 * m)) / 2;
+		int pw = (w - (3 * m)) * 2 / 3;
+		return pw;
+	}
+
+	int progressBarDist(int width) const
+	{
+		int xsize = width - 2;
+		return (progress * xsize / 8192);
+	}
+
+	void drawProgressBar(QPainter *p, const QColorGroup &cg, int x, int y, int width, int height) const
+	{
+		p->save();
+		if(isSelected())
+			p->setPen(cg.highlightedText());
+		else
+			p->setPen(cg.text());
+		p->drawRect(x, y, width, height);
+		int xoff = x + 1;
+		int yoff = y + 1;
+		int xsize = width - 2;
+		int ysize = height - 2;
+
+		int dist = progressBarDist(width);
+		p->fillRect(xoff, yoff, dist, ysize, cg.brush(QColorGroup::Highlight));
+		p->fillRect(xoff + dist, yoff, width - 2 - dist, ysize, cg.brush(QColorGroup::Base));
+
+		int percent = progress * 100 / 8192;
+		QString s = QString::number(percent) + '%';
+
+		QFontMetrics fm(p->font());
+		int ty = ((height - fm.height()) / 2) + fm.ascent() + y;
+		int textwidth = fm.width(s);
+		int center = xoff + (xsize / 2);
+
+		p->save();
+		p->setPen(cg.highlightedText());
+		p->setClipRect(xoff, yoff, dist, ysize, QPainter::CoordPainter);
+		p->drawText(center - (textwidth / 2), ty, s);
+		p->restore();
+
+		p->save();
+		p->setPen(cg.text());
+		p->setClipRect(xoff + dist, yoff, width - 2 - dist, ysize, QPainter::CoordPainter);
+		p->drawText(center - (textwidth / 2), ty, s);
+		p->restore();
+		p->restore();
+	}
+
+	void setup()
+	{
+		widthChanged();
+		QListView *lv = listView();
+
+		QFontMetrics fm = lv->fontMetrics();
+		int m = 4;
+		int pm = 2;
+		int ph = fm.height() + 2 + (pm * 2);
+		int h = (ph * 2) + (m * 3);
+
+		h += lv->itemMargin() * 2;
+
+		// ensure an even number
+		if(h & 1)
+			++h;
+
+		setHeight(h);
+	}
+
+	QString chopString(const QString &s, const QFontMetrics &fm, int len) const
+	{
+		if(fm.width(s) <= len)
+			return s;
+
+		QString str;
+		uint n = s.length();
+		do {
+			str = s.mid(0, --n) + "...";
+		} while(n > 0 && fm.width(str) > len);
+		return str;
+	}
+
+	void paintCell(QPainter *mp, const QColorGroup &_cg, int, int width, int)
+	{
+		QColorGroup cg = _cg;
+		int w = width;
+		int h = height();
+
+		// tint the background
+		/*//QColor base = Qt::black; //cg.base();
+		QColor base = Qt::white;
+		int red = base.red();
+		int green = base.green();
+		int blue = base.blue();
+		bool light = false;//true;
+		if(sending) {
+			if(light) {
+				green = green * 15 / 16;
+				blue = blue * 15 / 16;
+			}
+			else {
+				red = 255 - red;
+				red = red * 15 / 16;
+				red = 255 - red;
+			}
+		}
+		else {
+			if(light) {
+				red = red * 15 / 16;
+				blue = blue * 15 / 16;
+			}
+			else {
+				green = 255 - green;
+				green = green * 15 / 16;
+				green = 255 - green;
+			}
+		}
+		base.setRgb(red, green, blue);
+		cg.setColor(QColorGroup::Base, base);*/
+
+		QPixmap pix(w, h);
+		QPainter *p = new QPainter(&pix);
+		QFont font = mp->font();
+		QFont boldFont = font;
+		boldFont.setBold(true);
+		QFontMetrics fm(font);
+		QFontMetrics fmbold(boldFont);
+		QBrush br;
+
+		// m = margin, pm = progress margin, ph = progress height, yoff = text y offset,
+		// tt = text top, tb = text bottom, pw = progress width, px = progress x coord
+		int m = 4;
+		int pm = 2;
+		int ph = fm.height() + 2 + (pm * 2);
+		int yoff = 1 + pm;
+		int tt = m + yoff + fm.ascent();
+		int tb = (m * 2) + ph + yoff + fm.ascent();
+		//int pw = (w - (3 * m)) / 2;
+		int pw = (w - (3 * m)) * 2 / 3;
+		int tw = (w - (3 * m)) - pw;
+		int px = (m * 2) + tw;
+
+		// clear out the background
+		if(isSelected())
+			br = cg.brush(QColorGroup::Highlight);
+		else
+			br = cg.brush(QColorGroup::Base);
+		p->fillRect(0, 0, width, h, br);
+
+		// icon
+		p->drawPixmap(m, m + yoff, icon);
+		int tm = m + icon.width() + 4;
+		tw = tw - (icon.width() + 4);
+
+		// filename / peer
+		if(isSelected())
+			p->setPen(cg.highlightedText());
+		else
+			p->setPen(cg.text());
+		p->setFont(boldFont);
+		QString s1 = FileTransDlg::tr("File") + ": ";
+		QString s2 = FileTransDlg::tr("To") + ": ";
+		QString s3 = FileTransDlg::tr("From") + ": ";
+
+		int lw = QMAX(QMAX(fmbold.width(s1), fmbold.width(s2)), fmbold.width(s3));
+		int left = tw - lw;
+		p->drawText(tm, tt, s1);
+		p->drawText(tm, tb, sending ? s2 : s3);
+		p->setFont(font);
+		p->drawText(tm + lw, tt, chopString(name, fm, left));
+		p->drawText(tm + lw, tb, chopString(peer, fm, left));
+
+		// rate
+		p->setFont(boldFont);
+		s1 = FileTransDlg::tr("Status") + ": ";
+		lw = fmbold.width(s1);
+		left = pw - lw;
+		p->drawText(px, tb, s1);
+		p->setFont(font);
+		p->drawText(px + lw, tb, chopString(rate, fm, left));
+
+		// progress bar
+		drawProgressBar(p, cg, px, m, pw, ph);
+
+		delete p;
+		mp->drawPixmap(0, 0, pix);
+	}
+
+	QString makeTip() const
+	{
+		QTime t = QTime().addSecs(timeRemaining);
+		QString rem = FileTransDlg::tr("%1h%2m%3s").arg(t.hour()).arg(t.minute()).arg(t.second());
+
+		QString s;
+		s += FileTransDlg::tr("Filename") + QString(": %1").arg(name);
+		s += QString("\n") + FileTransDlg::tr("Type") + QString(": %1").arg(sending ? FileTransDlg::tr("Upload") : FileTransDlg::tr("Download"));
+		s += QString("\n") + FileTransDlg::tr("Peer") + QString(": %1").arg(peer);
+		s += QString("\n") + FileTransDlg::tr("Size") + QString(": %1").arg(size);
+		if(done) {
+			s += QString("\n") + FileTransDlg::tr("[Done]");
+		}
+		else {
+			s += QString("\n") + FileTransDlg::tr("Transferred") + QString(": %1").arg(sent);
+			if(bps > 0)
+				s += QString("\n") + FileTransDlg::tr("Time remaining") + QString(": %1").arg(rem);
+		}
+
+		return s;
+	}
+};
+
+class FileTransView : public QListView, public QToolTip
+{
+	Q_OBJECT
+public:
+	FileTransView(QWidget *parent=0, const char *name=0)
+	:QListView(parent, name), QToolTip(viewport())
+	{
+		connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), this, SLOT(qlv_contextMenuRequested(QListViewItem *, const QPoint &, int)));
+	}
+
+	void maybeTip(const QPoint &pos)
+	{
+		FileTransItem *i = static_cast<FileTransItem*>(itemAt(pos));
+		if(!i)
+			return;
+		QRect r(itemRect(i));
+		tip(r, i->makeTip());
+	}
+
+	void resizeEvent(QResizeEvent *e)
+	{
+		QListView::resizeEvent(e);
+
+		if(e->oldSize().width() != e->size().width())
+			doResize();
+	}
+
+signals:
+	void itemCancel(int id);
+	void itemOpenDest(int id);
+	void itemClear(int id);
+
+private slots:
+	void qlv_contextMenuRequested(QListViewItem *item, const QPoint &pos, int)
+	{
+		if(!item)
+			return;
+
+		FileTransItem *i = static_cast<FileTransItem*>(item);
+
+		QPopupMenu p;
+		p.insertItem(tr("&Cancel"), 0);
+		p.insertSeparator();
+		//p.insertItem(tr("&Open Destination Folder"), 1);
+		p.insertItem(tr("Cl&ear"), 2);
+
+		if(i->done) {
+			p.setItemEnabled(0, false);
+		}
+		else {
+			//p.setItemEnabled(1, false);
+			p.setItemEnabled(2, false);
+		}
+
+		int x = p.exec(pos);
+
+		// TODO: what if item is deleted during exec?
+
+		if(x == 0) {
+			if(!i->done)
+				itemCancel(i->id);
+		}
+		else if(x == 1)
+			itemOpenDest(i->id);
+		else if(x == 2)
+			itemClear(i->id);
+	}
+
+private:
+	void doResize()
+	{
+		QListViewItemIterator it(this);
+		for(QListViewItem *i; (i = it.current()); ++it)
+			i->setup();
+	}
+};
+
+class TransferMapping
+{
+public:
+	FileTransferHandler *h;
+	int id;
+	int p;
+	Q_LLONG sent;
+
+	int at;
+	Q_LLONG last[10];
+
+	TransferMapping()
+	{
+		h = 0;
+		at = 0;
+	}
+
+	~TransferMapping()
+	{
+		delete h;
+	}
+
+	void logSent()
+	{
+		// if we're at the end, shift down to make room
+		if(at == 10) {
+			for(int n = 0; n < at - 1; ++n)
+				last[n] = last[n + 1];
+			--at;
+		}
+		last[at++] = sent;
+	}
+};
+
+class FileTransDlg::Private
+{
+public:
+	FileTransDlg *parent;
+	PsiCon *psi;
+	FileTransView *lv;
+	QPtrList<TransferMapping> transferList;
+	QTimer t;
+
+	Private(FileTransDlg *_parent)
+	{
+		parent = _parent;
+		transferList.setAutoDelete(true);
+	}
+
+	int findFreeId()
+	{
+		int n = 0;
+		while(1) {
+			bool found = false;
+			QListViewItemIterator it(lv);
+			for(QListViewItem *i; (i = it.current()); ++it) {
+				FileTransItem *fi = static_cast<FileTransItem*>(i);
+				if(fi->id == n) {
+					found = true;
+					break;
+				}
+			}
+			if(!found)
+				break;
+			++n;
+		}
+		return n;
+	}
+
+	FileTransItem *findItem(int id)
+	{
+		QListViewItemIterator it(lv);
+		for(QListViewItem *i; (i = it.current()); ++it) {
+			FileTransItem *fi = static_cast<FileTransItem*>(i);
+			if(fi->id == id)
+				return fi;
+		}
+		return 0;
+	}
+
+	QPtrList<FileTransItem> getFinished()
+	{
+		QPtrList<FileTransItem> list;
+		QListViewItemIterator it(lv);
+		for(QListViewItem *i; (i = it.current()); ++it) {
+			FileTransItem *fi = static_cast<FileTransItem*>(i);
+			if(fi->done)
+				list.append(fi);
+		}
+		return list;
+	}
+
+	TransferMapping *findMapping(FileTransferHandler *h)
+	{
+		QPtrListIterator<TransferMapping> it(transferList);
+		for(TransferMapping *i; (i = it.current()); ++it) {
+			if(i->h == h)
+				return i;
+		}
+		return 0;
+	}
+
+	TransferMapping *findMapping(int id)
+	{
+		QPtrListIterator<TransferMapping> it(transferList);
+		for(TransferMapping *i; (i = it.current()); ++it) {
+			if(i->id == id)
+				return i;
+		}
+		return 0;
+	}
+
+	void updateProgress(TransferMapping *i, bool updateAll=true)
+	{
+		bool done = (i->p == i->h->totalSteps());
+
+		// calculate bps
+		int bps = -1;
+		if(!done && i->at >= 2) {
+			int seconds = i->at - 1;
+			Q_LLONG average = i->last[i->at-1] - i->last[0];
+			bps = ((int)average) / seconds;
+		}
+
+		if(done) {
+			FileTransItem *fi = findItem(i->id);
+			fi->done = true;
+		}
+
+		parent->setProgress(i->id, i->p, i->h->totalSteps(), i->sent, bps, updateAll);
+
+		if(done) {
+			bool recv = (i->h->mode() == FileTransferHandler::Receiving);
+			QString fname, savename;
+			if(recv) {
+				fname = i->h->fileName();
+				savename = i->h->saveName();
+			}
+
+			PsiAccount *pa = i->h->account();
+			transferList.removeRef(i);
+
+			if(recv) {
+				//printf("fname: [%s], savename: [%s]\n", fname.latin1(), savename.latin1());
+
+				// rename .part to original filename
+				QFileInfo fi(savename);
+				QDir dir = fi.dir();
+				if(dir.exists(fname))
+					dir.remove(fname);
+				if(!dir.rename(fi.fileName(), fname)) {
+					// TODO: display some error about renaming
+				}
+			}
+
+			pa->playSound(option.onevent[eFTComplete]);
+		}
+	}
+};
+
+FileTransDlg::FileTransDlg(PsiCon *psi)
+:QDialog(0, 0, false, psi_dialog_flags)
+{
+	d = new Private(this);
+	d->psi = psi;
+	//d->psi->dialogRegister(this);
+
+	connect(&d->t, SIGNAL(timeout()), SLOT(updateItems()));
+
+	setCaption(tr("Transfer Manager"));
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/filemanager"));
+#endif
+
+	QVBoxLayout *vb = new QVBoxLayout(this, 11, 6);
+	d->lv = new FileTransView(this);
+	connect(d->lv, SIGNAL(itemCancel(int)), SLOT(itemCancel(int)));
+	connect(d->lv, SIGNAL(itemOpenDest(int)), SLOT(itemOpenDest(int)));
+	connect(d->lv, SIGNAL(itemClear(int)), SLOT(itemClear(int)));
+	vb->addWidget(d->lv);
+	QHBoxLayout *hb = new QHBoxLayout(vb);
+	hb->addStretch(1);
+	QPushButton *pb_clear = new QPushButton(tr("Clear &Finished"), this);
+	connect(pb_clear, SIGNAL(clicked()), SLOT(clearFinished()));
+	hb->addWidget(pb_clear);
+	QPushButton *pb_close = new QPushButton(tr("&Hide"), this);
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	hb->addWidget(pb_close);
+
+	pb_close->setDefault(true);
+	pb_close->setFocus();
+
+	d->lv->addColumn("");
+	d->lv->header()->hide();
+
+	d->lv->setResizeMode(QListView::LastColumn);
+	d->lv->setAllColumnsShowFocus(true);
+	d->lv->setSorting(-1);
+
+	resize(560, 240);
+}
+
+FileTransDlg::~FileTransDlg()
+{
+	//d->psi->dialogUnregister(this);
+	delete d;
+}
+
+int FileTransDlg::addItem(const QString &filename, Q_LLONG size, const QString &peer, bool sending)
+{
+	int id = d->findFreeId();
+	FileTransItem *i = new FileTransItem(d->lv, filename, size, peer, sending);
+	if(sending)
+		i->icon = IconsetFactory::icon("psi/upload").impix().pixmap();
+	else
+		i->icon = IconsetFactory::icon("psi/download").impix().pixmap();
+	i->id = id;
+	d->t.start(1000);
+	return id;
+}
+
+void FileTransDlg::setProgress(int id, int step, int total, Q_LLONG sent, int bytesPerSecond, bool updateAll)
+{
+	FileTransItem *i = d->findItem(id);
+	if(i) {
+		// convert steps/total into a word
+		int progress;
+		if(total > 0)
+			progress = step * 8192 / total;
+		else
+			progress = 8192;
+
+		bool do_repaint = i->setProgress(progress, sent, bytesPerSecond);
+		if(updateAll) {
+			i->updateRate();
+			do_repaint = true;
+		}
+		if(do_repaint)
+			i->repaint();
+	}
+}
+
+void FileTransDlg::removeItem(int id)
+{
+	FileTransItem *i = d->findItem(id);
+	if(i)
+		delete i;
+	if(d->lv->childCount() == 0)
+		d->t.stop();
+}
+
+void FileTransDlg::setError(int id, const QString &reason)
+{
+	FileTransItem *i = d->findItem(id);
+	if(i) {
+		i->done = true;
+		i->error = reason;
+		i->updateRate();
+		i->repaint();
+		show();
+		d->lv->ensureItemVisible(i);
+		QMessageBox::information(this, tr("Transfer Error"), tr("Transfer of %1 with %2 failed.\nReason: %3").arg(i->name).arg(i->peer).arg(reason));
+	}
+}
+
+void FileTransDlg::takeTransfer(FileTransferHandler *h, int p, Q_LLONG sent)
+{
+	QString peer;
+	UserListItem *u = h->account()->findFirstRelavent(h->peer());
+	if(u)
+		peer = jidnick(u->jid().full(), u->name());
+	else
+		peer = h->peer().full();
+
+	TransferMapping *i = new TransferMapping;
+	i->h = h;
+	i->id = addItem(h->fileName(), h->fileSize(), peer, (h->mode() == FileTransferHandler::Sending));
+	i->p = p;
+	i->sent = sent;
+	d->transferList.append(i);
+
+	FileTransItem *fi = d->findItem(i->id);
+	d->lv->ensureItemVisible(fi);
+
+	if(p == i->h->totalSteps()) {
+		d->updateProgress(i, true);
+	}
+	else {
+		connect(h, SIGNAL(progress(int, Q_LLONG)), SLOT(ft_progress(int, Q_LLONG)));
+		connect(h, SIGNAL(error(int, int, const QString &)), SLOT(ft_error(int, int, const QString &)));
+	}
+}
+
+void FileTransDlg::clearFinished()
+{
+	QPtrList<FileTransItem> list = d->getFinished();
+	{
+		// remove related transfer mappings
+		QPtrListIterator<FileTransItem> it(list);
+		for(FileTransItem *fi; (fi = it.current()); ++it) {
+			TransferMapping *i = d->findMapping(fi->id);
+			d->transferList.removeRef(i);
+		}
+	}
+	list.setAutoDelete(true);
+	list.clear();
+}
+
+void FileTransDlg::ft_progress(int p, Q_LLONG sent)
+{
+	TransferMapping *i = d->findMapping((FileTransferHandler *)sender());
+	i->p = p;
+	i->sent = sent;
+	if(p == i->h->totalSteps())
+		d->updateProgress(i, true);
+	else
+		d->updateProgress(i, false);
+}
+
+void FileTransDlg::ft_error(int x, int, const QString &s)
+{
+	TransferMapping *i = d->findMapping((FileTransferHandler *)sender());
+	int id = i->id;
+	d->transferList.removeRef(i);
+
+	QString str;
+	//if(x == FileTransferHandler::ErrReject)
+	//	str = tr("File was rejected by remote user.");
+	if(x == FileTransferHandler::ErrTransfer)
+		str = s;
+	else
+		str = tr("File I/O error");
+	setError(id, str);
+}
+
+void FileTransDlg::updateItems()
+{
+	// operate on a copy so that we can delete items in updateProgress
+	QPtrList<TransferMapping> list = d->transferList;
+	QPtrListIterator<TransferMapping> it(list);
+	for(TransferMapping *i; (i = it.current()); ++it) {
+		if(i->h) {
+			i->logSent();
+			d->updateProgress(i);
+		}
+	}
+}
+
+void FileTransDlg::itemCancel(int id)
+{
+	FileTransItem *fi = d->findItem(id);
+	TransferMapping *i = d->findMapping(id);
+	d->transferList.removeRef(i);
+	delete fi;
+}
+
+void FileTransDlg::itemOpenDest(int id)
+{
+	TransferMapping *i = d->findMapping(id);
+
+	QString path;
+	bool recv = (i->h->mode() == FileTransferHandler::Receiving);
+	if(recv)
+		path = QFileInfo(i->h->saveName()).dirPath();
+	else
+		path = QFileInfo(i->h->fileName()).dirPath();
+
+	//printf("item open dest: [%s]\n", path.latin1());
+}
+
+void FileTransDlg::itemClear(int id)
+{
+	FileTransItem *fi = d->findItem(id);
+	TransferMapping *i = d->findMapping(id);
+	d->transferList.removeRef(i);
+	delete fi;
+}
+
+void FileTransDlg::killTransfers(PsiAccount *pa)
+{
+	QPtrList<TransferMapping> list = d->transferList;
+	QPtrListIterator<TransferMapping> it(list);
+	for(TransferMapping *i; (i = it.current()); ++it) {
+		// this account?
+		if(i->h->account() == pa) {
+			FileTransItem *fi = d->findItem(i->id);
+			d->transferList.removeRef(i);
+			delete fi;
+		}
+	}
+}
+
+#include"filetransdlg.moc"
Index: psi/tags/0.10-os2-b1/src/filetransdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/filetransdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/filetransdlg.h	(revision 38)
@@ -0,0 +1,140 @@
+#ifndef FILETRANSDLG_H
+#define FILETRANSDLG_H
+
+#include"ui_filetrans.h"
+#include"im.h"
+#include"s5b.h"
+
+#if QT_VERSION < 0x030200
+typedef long int Q_LLONG;
+#endif
+
+class PsiCon;
+class PsiAccount;
+namespace XMPP
+{
+	class FileTransfer;
+}
+using namespace XMPP;
+
+class FileTransferHandler : public QObject
+{
+	Q_OBJECT
+public:
+	enum { ErrReject, ErrTransfer, ErrFile };
+	enum { Sending, Receiving };
+	FileTransferHandler(PsiAccount *pa, FileTransfer *ft=0);
+	~FileTransferHandler();
+
+	PsiAccount *account() const;
+	int mode() const;
+	Jid peer() const;
+	QString fileName() const;
+	Q_LLONG fileSize() const;
+	QString description() const;
+	Q_LLONG offset() const;
+	int totalSteps() const;
+	bool resumeSupported() const;
+	QString saveName() const;
+
+	void send(const Jid &to, const QString &fname, const QString &desc);
+	void accept(const QString &saveName, const QString &fileName, Q_LLONG offset=0);
+
+signals:
+	void accepted();
+	void statusMessage(const QString &s);
+	void connected();
+	void progress(int p, Q_LLONG sent);
+	void error(int, int, const QString &s);
+
+private slots:
+	// s5b status
+	void s5b_proxyQuery();
+	void s5b_proxyResult(bool b);
+	void s5b_requesting();
+	void s5b_accepted();
+	void s5b_tryingHosts(const StreamHostList &hosts);
+	void s5b_proxyConnect();
+	void s5b_waitingForActivation();
+
+	// ft
+	void ft_accepted();
+	void ft_connected();
+	void ft_readyRead(const QByteArray &);
+	void ft_bytesWritten(int);
+	void ft_error(int);
+	void trySend();
+	void doFinish();
+
+private:
+	class Private;
+	Private *d;
+
+	void mapSignals();
+};
+
+class FileRequestDlg : public FileTransUI
+{
+	Q_OBJECT
+public:
+	FileRequestDlg(const Jid &j, PsiCon *psi, PsiAccount *pa);
+	FileRequestDlg(const Jid &j, PsiCon *psi, PsiAccount *pa, const QStringList& files);
+	FileRequestDlg(const QDateTime &ts, FileTransfer *ft, PsiAccount *pa);
+	~FileRequestDlg();
+
+protected:
+	void keyPressEvent(QKeyEvent *);
+
+public slots:
+	void done(int r);
+
+private slots:
+	void updateIdentity(PsiAccount *);
+	void pa_disconnected();
+	void chooseFile();
+	void doStart();
+	void ft_accepted();
+	void ft_statusMessage(const QString &s);
+	void ft_connected();
+	void ft_error(int, int, const QString &);
+	void t_timeout();
+
+private:
+	class Private;
+	Private *d;
+
+	void blockWidgets();
+	void unblockWidgets();
+};
+
+class FileTransDlg : public QDialog
+{
+	Q_OBJECT
+public:
+	FileTransDlg(PsiCon *);
+	~FileTransDlg();
+
+	int addItem(const QString &filename, Q_LLONG size, const QString &peer, bool sending);
+	void setProgress(int id, int step, int total, Q_LLONG sent, int bytesPerSecond, bool updateAll=false);
+	void setError(int id, const QString &reason);
+	void removeItem(int id);
+
+	void takeTransfer(FileTransferHandler *h, int p, Q_LLONG sent);
+	void killTransfers(PsiAccount *pa);
+
+private slots:
+	void clearFinished();
+	void ft_progress(int p, Q_LLONG sent);
+	void ft_error(int, int, const QString &s);
+	void updateItems();
+
+	void itemCancel(int);
+	void itemOpenDest(int);
+	void itemClear(int);
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/groupchatdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/groupchatdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/groupchatdlg.cpp	(revision 38)
@@ -0,0 +1,1507 @@
+/*
+ * groupchatdlg.cpp - dialogs for handling groupchat
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"groupchatdlg.h"
+
+#include<qlabel.h>
+#include<qlayout.h>
+#include<qpushbutton.h>
+#include<qgroupbox.h>
+#include<qmessagebox.h>
+#include<qtextedit.h>
+#include<qsplitter.h>
+#include<qtimer.h>
+#include<qheader.h>
+#include<qtoolbutton.h>
+#include<qinputdialog.h>
+#include<qguardedptr.h>
+#include<qaction.h>
+#include<qobjectlist.h>
+#include<qpopupmenu.h>
+#include<qcursor.h>
+#include"psicon.h"
+#include"psiaccount.h"
+#include"userlist.h"
+#include"statusdlg.h"
+#include"busywidget.h"
+#include"common.h"
+#include"msgmle.h"
+#include"iconwidget.h"
+#include"iconselect.h"
+#include"psitoolbar.h"
+#include"iconaction.h"
+
+#ifdef Q_WS_WIN
+#include<windows.h>
+#endif
+
+
+//----------------------------------------------------------------------------
+// GCJoinDlg
+//----------------------------------------------------------------------------
+class GCJoinDlg::Private
+{
+public:
+	Private() {}
+
+	PsiCon *psi;
+	PsiAccount *pa;
+	AccountsComboBox *cb_ident;
+	BusyWidget *busy;
+	QStringList rl;
+	Jid jid;
+};
+
+GCJoinDlg::GCJoinDlg(PsiCon *psi, PsiAccount *pa)
+:GCJoinUI(0, 0, false, WDestructiveClose)
+{
+	d = new Private;
+	d->psi = psi;
+	d->pa = 0;
+	d->psi->dialogRegister(this);
+	d->busy = busy;
+
+	updateIdentity(pa);
+
+	d->cb_ident = d->psi->accountsComboBox(this,true);
+	connect(d->cb_ident, SIGNAL(activated(PsiAccount *)), SLOT(updateIdentity(PsiAccount *)));
+	d->cb_ident->setAccount(pa);
+	replaceWidget(lb_ident, d->cb_ident);
+
+	d->rl = d->psi->recentGCList();
+	for(QStringList::ConstIterator it = d->rl.begin(); it != d->rl.end(); ++it) {
+		Jid j(*it);
+		QString s = tr("%1 on %2").arg(j.resource()).arg(j.userHost());
+		cb_recent->insertItem(s);
+	}
+
+	setCaption(CAP(caption()));
+	pb_join->setDefault(true);
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	connect(pb_join, SIGNAL(clicked()), SLOT(doJoin()));
+	connect(cb_recent, SIGNAL(activated(int)), SLOT(recent_activated(int)));
+	if(d->rl.isEmpty()) {
+		cb_recent->setEnabled(false);
+		le_host->setFocus();
+	}
+	else
+		recent_activated(0);
+}
+
+GCJoinDlg::~GCJoinDlg()
+{
+	if(d->psi)
+		d->psi->dialogUnregister(this);
+	if(d->pa)
+		d->pa->dialogUnregister(this);
+	delete d;
+}
+
+/*void GCJoinDlg::closeEvent(QCloseEvent *e)
+{
+	e->ignore();
+	reject();
+}*/
+
+void GCJoinDlg::done(int r)
+{
+	if(d->busy->isActive()) {
+		int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel joining groupchat?"), tr("&Yes"), tr("&No"));
+		if(n != 0)
+			return;
+		d->pa->groupChatLeave(d->jid.host(), d->jid.user());
+	}
+	QDialog::done(r);
+}
+
+void GCJoinDlg::updateIdentity(PsiAccount *pa)
+{
+	if(d->pa)
+		disconnect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
+
+	d->pa = pa;
+	pb_join->setEnabled(d->pa);
+
+	if(!d->pa) {
+		d->busy->stop();
+		return;
+	}
+
+	connect(d->pa, SIGNAL(disconnected()), this, SLOT(pa_disconnected()));
+}
+
+void GCJoinDlg::pa_disconnected()
+{
+	if(d->busy->isActive()) {
+		d->busy->stop();
+	}
+}
+
+void GCJoinDlg::recent_activated(int x)
+{
+	int n = 0;
+	bool found = false;
+	QString str;
+	for(QStringList::ConstIterator it = d->rl.begin(); it != d->rl.end(); ++it) {
+		if(n == x) {
+			found = true;
+			str = *it;
+			break;
+		}
+		++n;
+	}
+	if(!found)
+		return;
+
+	Jid j(str);
+	le_host->setText(j.host());
+	le_room->setText(j.user());
+	le_nick->setText(j.resource());
+}
+
+void GCJoinDlg::doJoin()
+{
+	if(!d->pa->checkConnected(this))
+		return;
+
+	QString host = le_host->text();
+	QString room = le_room->text();
+	QString nick = le_nick->text();
+
+	if(host.isEmpty() || room.isEmpty() || nick.isEmpty()) {
+		QMessageBox::information(this, tr("Error"), tr("You must fill out the fields in order to join."));
+		return;
+	}
+
+	Jid j = room + '@' + host + '/' + nick;
+	if(!j.isValid()) {
+		QMessageBox::information(this, tr("Error"), tr("You entered an invalid room name."));
+		return;
+	}
+
+	if(!d->pa->groupChatJoin(host, room, nick)) {
+		QMessageBox::information(this, tr("Error"), tr("You are in or joining this room already!"));
+		return;
+	}
+
+	d->psi->dialogUnregister(this);
+	d->jid = room + '@' + host + '/' + nick;
+	d->pa->dialogRegister(this, d->jid);
+
+	disableWidgets();
+	d->busy->start();
+}
+
+void GCJoinDlg::disableWidgets()
+{
+	d->cb_ident->setEnabled(false);
+	cb_recent->setEnabled(false);
+	gb_info->setEnabled(false);
+	pb_join->setEnabled(false);
+}
+
+void GCJoinDlg::enableWidgets()
+{
+	d->cb_ident->setEnabled(true);
+	if(!d->rl.isEmpty())
+		cb_recent->setEnabled(true);
+	gb_info->setEnabled(true);
+	pb_join->setEnabled(true);
+}
+
+void GCJoinDlg::joined()
+{
+	d->psi->recentGCAdd(d->jid.full());
+	d->busy->stop();
+
+	closeDialogs(this);
+	deleteLater();
+}
+
+void GCJoinDlg::error(int, const QString &str)
+{
+	d->busy->stop();
+	enableWidgets();
+
+	pb_join->setFocus();
+
+	d->pa->dialogUnregister(this);
+	d->psi->dialogRegister(this);
+
+	QMessageBox::information(this, tr("Error"), tr("Unable to join groupchat.\nReason: %1").arg(str));
+}
+
+
+//----------------------------------------------------------------------------
+// GCUserView
+//----------------------------------------------------------------------------
+GCUserViewItem::GCUserViewItem(QListView *par)
+:QListViewItem(par)
+{
+}
+
+GCUserView::GCUserView(QWidget *parent, const char *name)
+:QListView(parent, name), QToolTip(viewport())
+{
+	setResizeMode(QListView::AllColumns);
+	setSorting(0);
+	header()->hide();
+	addColumn("");
+
+	connect(this, SIGNAL(doubleClicked(QListViewItem *)), SLOT(qlv_doubleClicked(QListViewItem *)));
+	connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), SLOT(qlv_contextMenuRequested(QListViewItem *, const QPoint &, int)));
+}
+
+GCUserView::~GCUserView()
+{
+}
+
+void GCUserView::updateAll()
+{
+	for(GCUserViewItem *i = (GCUserViewItem *)firstChild(); i; i = (GCUserViewItem *)i->nextSibling())
+		i->setPixmap(0, is->status(i->s));
+}
+
+QStringList GCUserView::nickList() const
+{
+	QStringList list;
+
+	for(QListViewItem *lvi = firstChild(); lvi; lvi = lvi->nextSibling())
+		list << lvi->text(0);
+
+	qstringlistisort(list); // caseless sorting
+	return list;
+}
+
+QListViewItem *GCUserView::findEntry(const QString &nick)
+{
+	for(QListViewItem *lvi = firstChild(); lvi; lvi = lvi->nextSibling()) {
+		if(lvi->text(0) == nick)
+			return lvi;
+	}
+	return 0;
+}
+
+void GCUserView::updateEntry(const QString &nick, const Status &s)
+{
+	GCUserViewItem *lvi = (GCUserViewItem *)findEntry(nick);
+	if(!lvi) {
+		lvi = new GCUserViewItem(this);
+		lvi->setText(0, nick);
+	}
+
+	lvi->s = s;
+	lvi->setPixmap(0, is->status(lvi->s));
+}
+
+void GCUserView::removeEntry(const QString &nick)
+{
+	QListViewItem *lvi = findEntry(nick);
+	if(lvi)
+		delete lvi;
+}
+
+void GCUserView::maybeTip(const QPoint &pos)
+{
+	GCUserViewItem *lvi = (GCUserViewItem *)itemAt(pos);
+	if(!lvi)
+		return;
+
+	QRect r(itemRect(lvi));
+
+	const QString &nick = lvi->text(0);
+	const Status &s = lvi->s;
+	UserListItem u;
+	// SICK SICK SICK SICK
+	u.setJid(((GCMainDlg *)topLevelWidget())->jid().withResource(nick));
+	u.setName(nick);
+
+	// make a resource so the contact appears online
+	UserResource ur;
+	ur.setName(nick);
+	ur.setStatus(s);
+	u.userResourceList().append(ur);
+
+	tip(r, u.makeTip());
+}
+
+void GCUserView::qlv_doubleClicked(QListViewItem *i)
+{
+	GCUserViewItem *lvi = (GCUserViewItem *)i;
+	if(!lvi)
+		return;
+
+	if(option.defaultAction == 0)
+		action(lvi->text(0), lvi->s, 0);
+	else
+		action(lvi->text(0), lvi->s, 1);
+}
+
+void GCUserView::qlv_contextMenuRequested(QListViewItem *i, const QPoint &pos, int)
+{
+	GCUserViewItem *lvi = (GCUserViewItem *)i;
+	if(!lvi)
+		return;
+
+	QPopupMenu *pm = new QPopupMenu;
+	pm->insertItem(IconsetFactory::icon("psi/sendMessage"), tr("Send &message"), 0);
+	pm->insertItem(IconsetFactory::icon("psi/start-chat"), tr("Open &chat window"), 1);
+	pm->insertSeparator();
+	//pm->insertItem(tr("Send &file"), 4);
+	//pm->insertSeparator();
+	pm->insertItem(tr("Check &Status"), 2);
+	pm->insertItem(IconsetFactory::icon("psi/vCard"), tr("User &Info"), 3);
+	int x = pm->exec(pos);
+	delete pm;
+
+	if(x == -1)
+		return;
+	action(lvi->text(0), lvi->s, x);
+}
+
+
+//----------------------------------------------------------------------------
+// GCMainDlg
+//----------------------------------------------------------------------------
+class GCMainDlg::Private : public QObject
+{
+	Q_OBJECT
+public:
+	enum { Connecting, Connected, Idle };
+	Private(GCMainDlg *d) {
+		dlg = d;
+		nickSeparator = ":";
+		typingStatus = Typing_Normal;
+	}
+
+	GCMainDlg *dlg;
+	int state;
+	PsiAccount *pa;
+	Jid jid;
+	QString self;
+	ChatView *te_log;
+	ChatEdit *mle;
+	QLineEdit *le_topic;
+	GCUserView *lv_users;
+	QPushButton *pb_topic;
+	PsiToolBar *toolbar;
+	IconAction *act_find, *act_clear, *act_icon;
+	QPopupMenu *pm_settings;
+	bool smallChat;
+	int pending;
+
+	bool trackBar;
+	int  trackBarParagraph;
+
+	QTimer *flashTimer;
+	int flashCount;
+
+	QStringList hist;
+	int histAt;
+
+	QGuardedPtr<GCFindDlg> findDlg;
+	QString lastSearch;
+
+public slots:
+	void addEmoticon(const Icon *icon) {
+		if ( !dlg->isActiveWindow() )
+		     return;
+
+		QString text;
+
+		QDict<QString> itext = icon->text();
+		QDictIterator<QString> it ( itext );
+		for ( ; it.current(); ++it) {
+			if ( it.current() && !it.current()->isEmpty() ) {
+				text = *(it.current()) + " ";
+				break;
+			}
+		}
+
+		if ( !text.isEmpty() )
+			mle->insert( text );
+	}
+
+	void addEmoticon(QString text) {
+		if ( !dlg->isActiveWindow() )
+		     return;
+
+		mle->insert( text + " " );
+	}
+
+	void deferredScroll() {
+		//QTimer::singleShot(250, this, SLOT(slotScroll()));
+		te_log->scrollToBottom();
+	}
+
+protected slots:
+	void slotScroll() {
+		te_log->scrollToBottom();
+	}
+
+public:
+	// Nick auto-completion code follows...
+	enum TypingStatus {
+		Typing_Normal = 0,
+		Typing_TabPressed,
+		Typing_TabbingNicks,
+		Typing_MultipleSuggestions
+	};
+	TypingStatus typingStatus;
+	QString lastReferrer;  // contains nick of last person, who have said "yourNick: ..."
+	QString nickSeparator; // in case of "nick: ...", it equals ":"
+	QStringList suggestedNicks;
+	int  suggestedIndex;
+	bool suggestedFromStart;
+
+	QString beforeNickText(QString text) {
+		int i;
+		for (i = text.length() - 1; i >= 0; --i)
+			if ( text[i].isSpace() )
+				break;
+
+		QString beforeNick = text.left(i+1);
+		return beforeNick;
+	}
+
+	QStringList suggestNicks(QString text, bool fromStart) {
+		QString nickText = text;
+		QString beforeNick;
+		if ( !fromStart ) {
+			beforeNick = beforeNickText(text);
+			nickText   = text.mid(beforeNick.length());
+		}
+
+		QStringList nicks = lv_users->nickList();
+		QStringList::Iterator it = nicks.begin();
+		QStringList suggestedNicks;
+		for ( ; it != nicks.end(); ++it) {
+			if ( (*it).left(nickText.length()).lower() == nickText.lower() ) {
+				if ( fromStart )
+					suggestedNicks << *it;
+				else
+					suggestedNicks << beforeNick + *it;
+			}
+		}
+
+		return suggestedNicks;
+	}
+
+	QString longestSuggestedString(QStringList suggestedNicks) {
+		QString testString = suggestedNicks.first();
+		while ( testString.length() > 0 ) {
+			bool found = true;
+			QStringList::Iterator it = suggestedNicks.begin();
+			for ( ; it != suggestedNicks.end(); ++it) {
+				if ( (*it).left(testString.length()).lower() != testString.lower() ) {
+					found = false;
+					break;
+				}
+			}
+
+			if ( found )
+				break;
+
+			testString = testString.left( testString.length() - 1 );
+		}
+
+		return testString;
+	}
+
+	QString insertNick(bool fromStart, QString beforeNick = "") {
+		typingStatus = Typing_MultipleSuggestions;
+		suggestedFromStart = fromStart;
+		suggestedNicks = lv_users->nickList();
+		QStringList::Iterator it = suggestedNicks.begin();
+		for ( ; it != suggestedNicks.end(); ++it)
+			*it = beforeNick + *it;
+
+		QString newText;
+		if ( !lastReferrer.isEmpty() ) {
+			newText = beforeNick + lastReferrer;
+			suggestedIndex = -1;
+		}
+		else {
+			newText = suggestedNicks.first();
+			suggestedIndex = 0;
+		}
+
+		if ( fromStart ) {
+			newText += nickSeparator;
+			newText += " ";
+		}
+
+		return newText;
+	}
+
+	QString suggestNick(bool fromStart, QString origText, bool *replaced) {
+		suggestedFromStart = fromStart;
+		suggestedNicks = suggestNicks(origText, fromStart);
+		suggestedIndex = -1;
+
+		QString newText;
+		if ( suggestedNicks.count() ) {
+			if ( suggestedNicks.count() == 1 ) {
+				newText = suggestedNicks.first();
+				if ( fromStart ) {
+					newText += nickSeparator;
+					newText += " ";
+				}
+			}
+			else {
+				newText = longestSuggestedString(suggestedNicks);
+				if ( !newText.length() )
+					return origText;
+
+				typingStatus = Typing_MultipleSuggestions;
+				// TODO: display a tooltip that will contain all suggestedNicks
+			}
+
+			*replaced = true;
+		}
+
+		return newText;
+	}
+
+	void doAutoNickInsertion() {
+		int para, index;
+		mle->getCursorPosition(&para, &index);
+		QString paraText = mle->text(para);
+		QString origText = paraText.left(index);
+		QString newText;
+
+		bool replaced = false;
+
+		if ( typingStatus == Typing_MultipleSuggestions ) {
+			suggestedIndex++;
+			if ( suggestedIndex >= (int)suggestedNicks.count() )
+				suggestedIndex = 0;
+
+			newText = suggestedNicks[suggestedIndex];
+			if ( suggestedFromStart ) {
+				newText += nickSeparator;
+				newText += " ";
+			}
+
+			replaced = true;
+		}
+
+		if ( !para && !replaced ) {
+			if ( !index && typingStatus == Typing_TabbingNicks ) {
+				newText = insertNick(true, "");
+				replaced = true;
+			}
+			else {
+				newText = suggestNick(true, origText, &replaced);
+			}
+		}
+
+		if ( !replaced ) {
+			if ( (!index || origText[index-1].isSpace()) && typingStatus == Typing_TabbingNicks ) {
+				newText = insertNick(false, beforeNickText(origText));
+				replaced = true;
+			}
+			else {
+				newText = suggestNick(false, origText, &replaced);
+			}
+		}
+
+		if ( replaced ) {
+			mle->setUpdatesEnabled( false );
+			QString newParaText = newText + paraText.mid(index, paraText.length() - index - 1);
+			mle->insertParagraph(newParaText, para);
+			mle->removeParagraph(para+1);
+			mle->setCursorPosition(para, newText.length());
+			mle->setUpdatesEnabled( true );
+			mle->viewport()->update();
+		}
+	}
+
+	bool eventFilter( QObject *obj, QEvent *ev ) {
+		if ( obj == mle && ev->type() == QEvent::KeyPress ) {
+			QKeyEvent *e = (QKeyEvent *)ev;
+
+			if ( e->key() == Key_Tab ) {
+				switch ( typingStatus ) {
+				case Typing_Normal:
+					typingStatus = Typing_TabPressed;
+					break;
+				case Typing_TabPressed:
+					typingStatus = Typing_TabbingNicks;
+					break;
+				default:
+					break;
+				}
+
+				doAutoNickInsertion();
+				return TRUE;
+			}
+
+			typingStatus = Typing_Normal;
+
+			return FALSE;
+		}
+
+		return QObject::eventFilter( obj, ev );
+	}
+};
+
+GCMainDlg::GCMainDlg(PsiAccount *pa, const Jid &j)
+:AdvancedWidget<QWidget>(0, 0, WDestructiveClose)
+{
+	nicknumber=0;
+	d = new Private(this);
+	d->pa = pa;
+	d->jid = j.userHost();
+	d->self = j.resource();
+	d->pa->dialogRegister(this, d->jid);
+	connect(d->pa, SIGNAL(updatedActivity()), SLOT(pa_updatedActivity()));
+
+	d->pending = 0;
+	d->flashTimer = 0;
+
+	d->histAt = 0;
+	d->findDlg = 0;
+
+	d->trackBar = false;
+	d->trackBarParagraph = 0;
+
+	d->state = Private::Connected;
+
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/groupChat"));
+#endif
+
+	QVBoxLayout *dlg_layout = new QVBoxLayout(this, 4);
+
+	QWidget *vsplit;
+	if ( !option.chatLineEdit ) {
+		vsplit = new QSplitter(this);
+		((QSplitter *)vsplit)->setOrientation( QSplitter::Vertical );
+		dlg_layout->addWidget(vsplit);
+	}
+	else
+		vsplit = this;
+
+	// --- top part ---
+	QWidget *sp_top = new QWidget(vsplit);
+	sp_top->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
+	if ( option.chatLineEdit )
+		dlg_layout->addWidget( sp_top );
+	QVBoxLayout *vb_top = new QVBoxLayout(sp_top, 0, 4);
+
+	// top row
+	QWidget *sp_top_top = new QWidget( sp_top );
+	vb_top->addWidget( sp_top_top );
+	QHBoxLayout *hb_top = new QHBoxLayout( sp_top_top, 0, 4 );
+
+	d->pb_topic = new QPushButton(tr("Topic:"), sp_top_top);
+	connect(d->pb_topic, SIGNAL(clicked()), SLOT(doTopic()));
+	hb_top->addWidget(d->pb_topic);
+
+	d->le_topic = new QLineEdit(sp_top_top);
+	d->le_topic->setReadOnly(true);
+	hb_top->addWidget(d->le_topic);
+
+	d->act_find = new IconAction(tr("Find"), "psi/search", tr("&Find"), CTRL+Key_F, this);
+	connect(d->act_find, SIGNAL(activated()), SLOT(openFind()));
+	d->act_find->addTo( sp_top_top );
+
+	QLabel *lb_ident = d->pa->accountLabel(sp_top_top, true);
+	lb_ident->setSizePolicy(QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ));
+	hb_top->addWidget(lb_ident);
+
+	// bottom row
+	QSplitter *hsp = new QSplitter(sp_top);
+	hsp->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
+	vb_top->addWidget(hsp);
+	hsp->setOrientation(QSplitter::Horizontal);
+
+	d->te_log = new ChatView(hsp);
+	d->te_log->setTextFormat( RichText );
+#ifdef Q_WS_MAC
+	connect(d->te_log,SIGNAL(selectionChanged()),SLOT(logSelectionChanged()));
+	d->te_log->setFocusPolicy(QWidget::NoFocus);
+#endif
+
+	d->lv_users = new GCUserView(hsp);
+	d->lv_users->setMinimumWidth(20);
+	connect(d->lv_users, SIGNAL(action(const QString &, const Status &, int)), SLOT(lv_action(const QString &, const Status &, int)));
+
+	// --- bottom part ---
+	QWidget *sp_bottom = new QWidget(vsplit);
+	sp_bottom->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum );
+	if ( option.chatLineEdit )
+		dlg_layout->addWidget( sp_bottom );
+	QVBoxLayout *vb_bottom = new QVBoxLayout(sp_bottom);
+
+	// toolbar
+	d->act_clear = new IconAction (tr("Clear chat window"), "psi/clearChat", tr("Clear chat window"), 0, this);
+	connect( d->act_clear, SIGNAL( activated() ), SLOT( doClearButton() ) );
+
+	connect(pa->psi()->iconSelectPopup(), SIGNAL(textSelected(QString)), d, SLOT(addEmoticon(QString)));
+	d->act_icon = new IconAction( tr( "Select icon" ), "psi/smile", tr( "Select icon" ), 0, this );
+	d->act_icon->setPopup( pa->psi()->iconSelectPopup() );
+
+	d->toolbar = new PsiToolBar( tr("Groupchat toolbar"), 0, sp_bottom );
+	d->toolbar->setCustomizeable( false ); // it isn't ready now, and we don't want segfaults
+	d->toolbar->setFrameShape( QFrame::NoFrame );
+	vb_bottom->addWidget( d->toolbar );
+
+	d->act_clear->addTo( d->toolbar );
+	d->toolbar->setStretchableWidget(new StretchWidget(d->toolbar));
+	d->act_icon->addTo( d->toolbar );
+
+	// chat edit
+	if ( !option.chatLineEdit ) {
+		d->mle = new ChatEdit(sp_bottom);
+		vb_bottom->addWidget(d->mle);
+	}
+	else {
+		QHBoxLayout *hb5 = new QHBoxLayout( dlg_layout );
+		d->mle = new LineEdit( vsplit );
+#ifdef Q_WS_MAC
+		hb5->addSpacing( 16 );
+#endif
+		hb5->addWidget( d->mle );
+#ifdef Q_WS_MAC
+		hb5->addSpacing( 16 );
+#endif
+	}
+
+	d->mle->installEventFilter( d );
+
+	d->pm_settings = new QPopupMenu(this);
+	connect(d->pm_settings, SIGNAL(aboutToShow()), SLOT(buildMenu()));
+
+	// resize the horizontal splitter
+	QValueList<int> list;
+	list << 500;
+	list << 80;
+	hsp->setSizes(list);
+
+	list.clear();
+	list << 324;
+	list << 10;
+	if ( !option.chatLineEdit )
+		(( QSplitter *)vsplit)->setSizes(list);
+
+	resize(580,420);
+
+	d->smallChat = option.smallChats;
+	X11WM_CLASS("groupchat");
+
+	d->mle->setFocus();
+
+	setLooks();
+	updateCaption();
+}
+
+GCMainDlg::~GCMainDlg()
+{
+	if(d->state != Private::Idle)
+		d->pa->groupChatLeave(d->jid.host(), d->jid.user());
+
+	//QMimeSourceFactory *m = d->te_log->mimeSourceFactory();
+	//d->te_log->setMimeSourceFactory(0);
+	//delete m;
+
+	d->pa->dialogUnregister(this);
+	delete d;
+}
+
+void GCMainDlg::keyPressEvent(QKeyEvent *e)
+{
+	if(e->key() == Key_Return || e->key() == Key_Enter || (e->key() == Key_S && (e->state() & AltButton)))
+		mle_returnPressed();
+	else if(e->key() == Key_PageUp && (e->state() & ShiftButton))
+		d->te_log->setContentsPos(d->te_log->contentsX(), d->te_log->contentsY() - d->te_log->visibleHeight()/2);
+	else if(e->key() == Key_PageDown && (e->state() & ShiftButton))
+		d->te_log->setContentsPos(d->te_log->contentsX(), d->te_log->contentsY() + d->te_log->visibleHeight()/2);
+	else
+		e->ignore();
+}
+
+void GCMainDlg::closeEvent(QCloseEvent *e)
+{
+	e->accept();
+}
+
+void GCMainDlg::windowActivationChange(bool oldstate)
+{
+	QWidget::windowActivationChange(oldstate);
+
+	// if we're bringing it to the front, get rid of the '*' if necessary
+	if(isActiveWindow()) {
+		if(d->pending > 0) {
+			d->pending = 0;
+			updateCaption();
+		}
+		doFlash(false);
+
+		d->mle->setFocus();
+		d->trackBar = false;
+	} else {
+		d->trackBar = true;
+	}
+}
+
+
+void GCMainDlg::logSelectionChanged()
+{
+#ifdef Q_WS_MAC
+	// A hack to only give the message log focus when text is selected
+	if (d->te_log->hasSelectedText()) 
+		d->te_log->setFocus();
+	else 
+		d->mle->setFocus();
+#endif
+}
+
+void GCMainDlg::mle_returnPressed()
+{
+	if(d->mle->text().isEmpty())
+		return;
+
+	QString str = d->mle->text();
+	if(str == "/clear") {
+		doClear();
+
+		d->histAt = 0;
+		d->hist.prepend(str);
+		d->mle->setText("");
+		return;
+	}
+
+	if(str.lower().startsWith("/nick ")) {
+		QString nick = str.mid(6).stripWhiteSpace();
+		if ( !nick.isEmpty() ) {
+			d->self = nick;
+			d->pa->groupChatChangeNick(d->jid.host(), d->jid.user(), d->self, d->pa->status());
+		}
+		d->mle->setText("");
+		return;
+	}
+
+	if(d->state != Private::Connected)
+		return;
+
+	Message m(d->jid);
+	m.setType("groupchat");
+	m.setBody(str);
+	m.setTimeStamp(QDateTime::currentDateTime());
+
+	aSend(m);
+
+	d->histAt = 0;
+	d->hist.prepend(str);
+	d->mle->setText("");
+}
+
+/*void GCMainDlg::le_upPressed()
+{
+	if(d->histAt < (int)d->hist.count()) {
+		++d->histAt;
+		d->le_input->setText(d->hist[d->histAt-1]);
+	}
+}
+
+void GCMainDlg::le_downPressed()
+{
+	if(d->histAt > 0) {
+		--d->histAt;
+		if(d->histAt == 0)
+			d->le_input->setText("");
+		else
+			d->le_input->setText(d->hist[d->histAt-1]);
+	}
+}*/
+
+void GCMainDlg::doTopic()
+{
+	bool ok = false;
+	QString str = QInputDialog::getText(
+		tr("Set Groupchat Topic"),
+		tr("Enter a topic:"),
+		QLineEdit::Normal, d->le_topic->text(), &ok, this);
+
+	if(ok) {
+		Message m(d->jid);
+		m.setType("groupchat");
+		m.setSubject(str);
+		m.setBody(QString("/me ") + tr("has set the topic to: %1").arg(str));
+		m.setTimeStamp(QDateTime::currentDateTime());
+		aSend(m);
+	}
+}
+
+void GCMainDlg::doClear()
+{
+	d->te_log->setText("");
+}
+
+void GCMainDlg::doClearButton()
+{
+	int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to clear the chat window?\n(note: does not affect saved history)"), tr("&Yes"), tr("&No"));
+	if(n == 0)
+		doClear();
+}
+
+void GCMainDlg::openFind()
+{
+	if(d->findDlg)
+		bringToFront(d->findDlg);
+	else {
+		d->findDlg = new GCFindDlg(0, 0, d->lastSearch, this);
+		connect(d->findDlg, SIGNAL(find(int, int, const QString &)), SLOT(doFind(int, int, const QString &)));
+		d->findDlg->show();
+	}
+}
+
+void GCMainDlg::doFind(int para, int index, const QString &str)
+{
+	d->lastSearch = str;
+
+	if(!d->te_log->find(str, false, false, true, &para, &index))
+		d->findDlg->error(str);
+	else {
+		// pass one character over
+		d->findDlg->found(para, index+1);
+	}
+}
+
+void GCMainDlg::goDisc()
+{
+	if(d->state != Private::Idle) {
+		d->state = Private::Idle;
+		d->pb_topic->setEnabled(false);
+		appendSysMsg(tr("Disconnected."), true);
+	}
+}
+
+void GCMainDlg::goConn()
+{
+	if(d->state == Private::Idle) {
+		d->state = Private::Connecting;
+		appendSysMsg(tr("Reconnecting..."), true);
+
+		QString host = d->jid.host();
+		QString room = d->jid.user();
+		QString nick = d->self;
+
+		if(!d->pa->groupChatJoin(host, room, nick)) {
+			appendSysMsg(tr("Error: You are in or joining this room already!"), true);
+			d->state = Private::Idle;
+		}
+	}
+}
+
+void GCMainDlg::pa_updatedActivity()
+{
+	if(!d->pa->loggedIn())
+		goDisc();
+	else {
+		if(d->state == Private::Idle)
+			goConn();
+		else if(d->state == Private::Connected)
+			d->pa->groupChatSetStatus(d->jid.host(), d->jid.user(), d->pa->status());
+	}
+}
+
+Jid GCMainDlg::jid() const
+{
+	return d->jid;
+}
+
+void GCMainDlg::error(int, const QString &str)
+{
+	d->pb_topic->setEnabled(false);
+
+	if(d->state == Private::Connecting)
+		appendSysMsg(tr("Unable to join groupchat.  Reason: %1").arg(str), true);
+	else
+		appendSysMsg(tr("Unexpected groupchat error: %1").arg(str), true);
+
+	d->state = Private::Idle;
+}
+
+void GCMainDlg::presence(const QString &nick, const Status &s)
+{
+	if(s.isAvailable()) {
+		/*if ((option.showJoins)&&(d->lv_users->findEntry(nick)==0)) {
+			//contact joining
+			QString m=nick+tr(" has joined the channel");
+			appendSysMsg(m, false, QDateTime::currentDateTime());
+		}*/
+		d->lv_users->updateEntry(nick, s);
+	} else {
+		/*if (option.showJoins) {
+			//contact leaving
+			QString m=nick+tr(" has left the channel");
+			appendSysMsg(m, false, QDateTime::currentDateTime());
+		}*/
+		d->lv_users->removeEntry(nick);
+	}
+}
+
+void GCMainDlg::message(const Message &m)
+{
+	QString from = m.from().resource();
+	bool alert = false;
+
+	if(!m.subject().isEmpty()) {
+		d->le_topic->setText(m.subject());
+		d->le_topic->setCursorPosition(0);
+		QToolTip::add(d->le_topic, QString("<qt><p>%1</p></qt>").arg(m.subject()));
+	}
+
+	// code to determine if the speaker was addressing this client in chat
+	if(m.body().contains(d->self) > 0)
+		alert = true;
+
+	if (m.body().left(d->self.length()) == d->self)
+		d->lastReferrer = m.from().resource();
+
+	if(option.gcHighlighting) {
+		for(QStringList::Iterator it=option.gcHighlights.begin();it!=option.gcHighlights.end();it++) {
+			if(m.body().contains((*it),false) > 0) {
+				alert = true;
+			}
+		}
+	}
+
+	// play sound?
+	if(from == d->self) {
+		if(!m.spooled())
+			d->pa->playSound(option.onevent[eSend]);
+	}
+	else {
+		if(alert || (!option.noGCSound && !m.spooled() && !from.isEmpty()) )
+			d->pa->playSound(option.onevent[eChat2]);
+	}
+
+	if(from.isEmpty())
+		appendSysMsg(m.body(), alert, m.timeStamp());
+	else
+		appendMessage(m, alert);
+}
+
+void GCMainDlg::joined()
+{
+	if(d->state == Private::Connecting) {
+		d->lv_users->QListView::clear();
+		d->state = Private::Connected;
+		d->pb_topic->setEnabled(true);
+		appendSysMsg(tr("Connected."), true);
+	}
+}
+
+void GCMainDlg::appendSysMsg(const QString &str, bool alert, const QDateTime &ts)
+{
+	bool atBottom = d->te_log->contentsY() >= d->te_log->contentsHeight() - d->te_log->visibleHeight();
+
+	QString hr ="";
+	if (d->trackBar) {
+		hr = QString("<hr>");
+		d->trackBar = false;
+
+		if ( d->trackBarParagraph ) {
+			d->te_log->setUpdatesEnabled( false );
+			d->te_log->setSelection(d->trackBarParagraph, 0, d->trackBarParagraph, 1, 1);
+			d->te_log->removeSelectedText(1);
+			d->te_log->setUpdatesEnabled( true );
+		}
+		d->trackBarParagraph = d->te_log->paragraphs();
+	}
+
+	QString timestr;
+	if (!option.gcHighlighting)
+		alert=false;
+
+	QDateTime time;
+	if(!ts.isNull())
+		time = ts;
+	else
+		time = QDateTime::currentDateTime();
+
+	//timestr.sprintf("%02d:%02d:%02d", time.time().hour(), time.time().minute(), time.time().second());
+	timestr = time.time().toString(LocalDate);
+
+	/*int y = d->te_log->contentsHeight() - d->te_log->visibleHeight();
+	if(y < 0)
+		y = 0;
+	bool atBottom = (d->te_log->contentsY() < y - 32) ? false: true;*/
+
+	d->te_log->append(hr + QString("<font color=\"#00A000\">[%1]").arg(timestr) + QString(" *** %1</font>").arg(expandEntities(str)));
+
+	if(atBottom)
+		d->deferredScroll();
+
+	if(alert)
+		doAlert();
+}
+
+QString GCMainDlg::getNickColor(QString nick)
+{
+	int sender;
+	if(nick == d->self||nick.isEmpty())
+		sender = -1;
+	else {
+		if (!nicks.contains(nick)) {
+			//not found in map
+			nicks.insert(nick,nicknumber);
+			nicknumber++;
+		}
+		sender=nicks[nick];
+	}
+
+	if(!option.gcNickColoring || option.gcNickColors.empty()) {
+		return "#000000";
+	}
+	else if(sender == -1 || option.gcNickColors.size() == 1) {
+		return option.gcNickColors[option.gcNickColors.size()-1];
+	}
+	else {
+		int n = sender % (option.gcNickColors.size()-1);
+		return option.gcNickColors[n];
+	}
+}
+
+void GCMainDlg::appendMessage(const Message &m, bool alert)
+{
+	//QString who, color;
+	if (!option.gcHighlighting)
+		alert=false;
+	QString who, textcolor, nickcolor,alerttagso,alerttagsc;
+
+	bool atBottom = d->te_log->contentsY() >= d->te_log->contentsHeight() - d->te_log->visibleHeight();
+
+	who = m.from().resource();
+	QString hr = "";
+	if (d->trackBar&&m.from().resource() != d->self&&!m.spooled()) {
+		hr = QString("<hr>");
+		d->trackBar = false;
+
+		if ( d->trackBarParagraph ) {
+			d->te_log->setUpdatesEnabled( false );
+			d->te_log->setSelection(d->trackBarParagraph, 0, d->trackBarParagraph, 1, 1);
+			d->te_log->removeSelectedText(1);
+			d->te_log->setUpdatesEnabled( true );
+		}
+		d->trackBarParagraph = d->te_log->paragraphs();
+	}
+	/*if(local) {
+		color = "#FF0000";
+	}
+	else {
+		color = "#0000FF";
+	}*/
+	nickcolor = getNickColor(who);
+	textcolor = d->te_log->palette().active().text().name();
+	if(alert) {
+		textcolor = "#FF0000";
+		alerttagso = "<b>";
+		alerttagsc = "</b>";
+	}
+	if(m.spooled())
+		nickcolor = "#008000"; //color = "#008000";
+
+	QString timestr;
+	QDateTime time = m.timeStamp();
+	//timestr.sprintf("%02d:%02d:%02d", time.time().hour(), time.time().minute(), time.time().second());
+	timestr = time.time().toString(LocalDate);
+
+	/*int y = d->te_log->contentsHeight() - d->te_log->visibleHeight();
+	if(y < 0)
+		y = 0;
+	bool atBottom = (d->te_log->contentsY() < y - 32) ? false: true;*/
+
+	bool emote = false;
+	if(m.body().left(4) == "/me ")
+		emote = true;
+
+	QString txt;
+	if(emote)
+		txt = plain2rich(m.body().mid(4));
+	else
+		txt = plain2rich(m.body());
+
+	txt = linkify(txt);
+
+	if(option.useEmoticons)
+		txt = emoticonify(txt);
+
+	if(emote) {
+		//d->te_log->append(QString("<font color=\"%1\">").arg(color) + QString("[%1]").arg(timestr) + QString(" *%1 ").arg(expandEntities(who)) + txt + "</font>");
+		d->te_log->append(hr + QString("<font color=\"%1\">").arg(nickcolor) + QString("[%1]").arg(timestr) + QString(" *%1 ").arg(expandEntities(who)) + alerttagso + txt + alerttagsc + "</font>");
+	}
+	else {
+		if(option.chatSays) {
+			//d->te_log->append(QString("<font color=\"%1\">").arg(color) + QString("[%1] ").arg(timestr) + QString("%1 says:").arg(expandEntities(who)) + "</font><br>" + txt);
+			d->te_log->append(hr + QString("<font color=\"%1\">").arg(nickcolor) + QString("[%1] ").arg(timestr) + QString("%1 says:").arg(expandEntities(who)) + "</font><br>" + QString("<font color=\"%1\">").arg(textcolor) + alerttagso + txt + alerttagsc + "</font>");
+		}
+		else {
+			//d->te_log->append(QString("<font color=\"%1\">").arg(color) + QString("[%1] &lt;").arg(timestr) + expandEntities(who) + QString("&gt;</font> ") + txt);
+			d->te_log->append(hr + QString("<font color=\"%1\">").arg(nickcolor) + QString("[%1] &lt;").arg(timestr) + expandEntities(who) + QString("&gt;</font> ") + QString("<font color=\"%1\">").arg(textcolor) + alerttagso + txt + alerttagsc +"</font>");
+		}
+	}
+
+	//if(local || atBottom)
+	if(m.from().resource() == d->self || atBottom)
+		d->deferredScroll();
+
+	// if we're not active, notify the user by changing the title
+	if(!isActiveWindow()) {
+		++d->pending;
+		updateCaption();
+	}
+
+	//if someone directed their comments to us, notify the user
+	if(alert)
+		doAlert();
+
+	//if the message spoke to us, alert the user before closing this window
+	//except that keepopen doesn't seem to be implemented for this class yet.
+	/*if(alert) {
+		d->keepOpen = true;
+		QTimer::singleShot(1000, this, SLOT(setKeepOpenFalse()));
+        }*/
+}
+
+void GCMainDlg::doAlert()
+{
+	if(!isActiveWindow())
+		doFlash(true);
+}
+
+void GCMainDlg::updateCaption()
+{
+	QString cap = "";
+
+	if(d->pending > 0) {
+		cap += "* ";
+		if(d->pending > 1)
+			cap += QString("[%1] ").arg(d->pending);
+	}
+	cap += d->jid.full();
+
+	// if taskbar flash, then we need to erase first and redo
+#ifdef Q_WS_WIN
+	bool on = false;
+	if(d->flashTimer)
+		on = d->flashCount & 1;
+	if(on)
+		FlashWindow(winId(), true);
+#endif
+	setCaption(cap);
+#ifdef Q_WS_WIN
+	if(on)
+		FlashWindow(winId(), true);
+#endif
+}
+
+#ifdef Q_WS_WIN
+void GCMainDlg::doFlash(bool yes)
+{
+	if(yes) {
+		if(d->flashTimer)
+			return;
+		d->flashTimer = new QTimer(this);
+		connect(d->flashTimer, SIGNAL(timeout()), SLOT(flashAnimate()));
+		d->flashCount = 0;
+		flashAnimate(); // kick the first one immediately
+		d->flashTimer->start(500);
+	}
+	else {
+		if(d->flashTimer) {
+			delete d->flashTimer;
+			d->flashTimer = 0;
+			FlashWindow(winId(), false);
+		}
+	}
+}
+#else
+void GCMainDlg::doFlash(bool)
+{
+}
+#endif
+
+void GCMainDlg::flashAnimate()
+{
+#ifdef Q_WS_WIN
+	FlashWindow(winId(), true);
+	++d->flashCount;
+	if(d->flashCount == 5)
+		d->flashTimer->stop();
+#endif
+}
+
+void GCMainDlg::setLooks()
+{
+	// update the fonts
+	QFont f;
+	f.fromString(option.font[fChat]);
+	d->te_log->setFont(f);
+	d->mle->setFont(f);
+
+	f.fromString(option.font[fRoster]);
+	d->lv_users->QListView::setFont(f);
+
+	if ( d->smallChat ) {
+		d->toolbar->hide();
+	}
+	else {
+		d->toolbar->show();
+	}
+
+	// update the widget icon
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/groupChat"));
+#endif
+}
+
+void GCMainDlg::optionsUpdate()
+{
+	/*QMimeSourceFactory *m = d->te_log->mimeSourceFactory();
+	d->te_log->setMimeSourceFactory(is->emoticons.generateFactory());
+	delete m;*/
+
+	setLooks();
+
+	// update status icons
+	d->lv_users->updateAll();
+}
+
+void GCMainDlg::lv_action(const QString &nick, const Status &s, int x)
+{
+	if(x == 0) {
+		d->pa->invokeGCMessage(d->jid.withResource(nick));
+	}
+	else if(x == 1) {
+		d->pa->invokeGCChat(d->jid.withResource(nick));
+	}
+	else if(x == 2) {
+		UserListItem u;
+		u.setJid(d->jid.withResource(nick));
+		u.setName(nick);
+
+		// make a resource so the contact appears online
+		UserResource ur;
+		ur.setName(nick);
+		ur.setStatus(s);
+		u.userResourceList().append(ur);
+
+		StatusShowDlg *w = new StatusShowDlg(u);
+		w->show();
+	}
+	else if(x == 3) {
+		d->pa->invokeGCInfo(d->jid.withResource(nick));
+	}
+	else if(x == 4) {
+		d->pa->invokeGCFile(d->jid.withResource(nick));
+	}
+}
+
+void GCMainDlg::contextMenuEvent(QContextMenuEvent *)
+{
+	d->pm_settings->exec(QCursor::pos());
+}
+
+void GCMainDlg::buildMenu()
+{
+	// Dialog menu
+	d->pm_settings->clear();
+	d->pm_settings->insertItem(tr("Toggle Compact/Full Size"), this, SLOT(toggleSmallChat()));
+
+	d->act_clear->addTo( d->pm_settings );
+	d->pm_settings->insertSeparator();
+
+	d->act_icon->addTo( d->pm_settings );
+}
+
+void GCMainDlg::toggleSmallChat()
+{
+	d->smallChat = !d->smallChat;
+	setLooks();
+}
+
+//----------------------------------------------------------------------------
+// GCFindDlg
+//----------------------------------------------------------------------------
+GCFindDlg::GCFindDlg(int startPara, int startIndex, const QString &str, QWidget *parent, const char *name)
+:QDialog(parent, name, false, WDestructiveClose)
+{
+	para = startPara;
+	index = startIndex;
+
+	setCaption(tr("Find"));
+	QVBoxLayout *vb = new QVBoxLayout(this, 4);
+	QHBoxLayout *hb = new QHBoxLayout(vb);
+	QLabel *l = new QLabel(tr("Find:"), this);
+	hb->addWidget(l);
+	le_input = new QLineEdit(this);
+	hb->addWidget(le_input);
+	vb->addStretch(1);
+
+	QFrame *Line1 = new QFrame(this);
+	Line1->setFrameShape( QFrame::HLine );
+	Line1->setFrameShadow( QFrame::Sunken );
+	Line1->setFrameShape( QFrame::HLine );
+	vb->addWidget(Line1);
+
+	hb = new QHBoxLayout(vb);
+	hb->addStretch(1);
+	QPushButton *pb_close = new QPushButton(tr("&Close"), this);
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	hb->addWidget(pb_close);
+	QPushButton *pb_find = new QPushButton(tr("&Find"), this);
+	pb_find->setDefault(true);
+	connect(pb_find, SIGNAL(clicked()), SLOT(doFind()));
+	hb->addWidget(pb_find);
+	pb_find->setAutoDefault(true);
+
+	resize(200, minimumSize().height());
+
+	le_input->setText(str);
+	le_input->setFocus();
+}
+
+GCFindDlg::~GCFindDlg()
+{
+}
+
+void GCFindDlg::found(int _para, int _index)
+{
+	para = _para;
+	index = _index;
+}
+
+void GCFindDlg::error(const QString &str)
+{
+	QMessageBox::warning(this, tr("Find"), tr("Search string '%1' not found.").arg(str));
+	le_input->setFocus();
+}
+
+void GCFindDlg::doFind()
+{
+	emit find(para, index, le_input->text());
+}
+
+#include "groupchatdlg.moc"
Index: psi/tags/0.10-os2-b1/src/groupchatdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/groupchatdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/groupchatdlg.h	(revision 38)
@@ -0,0 +1,189 @@
+/*
+ * groupchatdlg.h - dialogs for handling groupchat
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include<qlistview.h>
+#include<qlineedit.h>
+#include<qtooltip.h>
+#include"advwidget.h"
+#include"im.h"
+#include"ui_gcjoin.h"
+
+using namespace XMPP;
+
+class PsiCon;
+class PsiAccount;
+
+class GCJoinDlg : public GCJoinUI
+{
+	Q_OBJECT
+public:
+	GCJoinDlg(PsiCon *, PsiAccount *);
+	~GCJoinDlg();
+
+	void joined();
+	void error(int, const QString &);
+
+protected:
+	//void closeEvent(QCloseEvent *);
+
+public slots:
+	void done(int);
+
+private slots:
+	void doJoin();
+	void updateIdentity(PsiAccount *);
+	void pa_disconnected();
+	void recent_activated(int);
+
+private:
+	class Private;
+	Private *d;
+
+	void disableWidgets();
+	void enableWidgets();
+};
+
+class GCUserViewItem : public QListViewItem
+{
+public:
+	GCUserViewItem(QListView *);
+
+	Status s;
+};
+
+class GCUserView : public QListView, public QToolTip
+{
+	Q_OBJECT
+public:
+	GCUserView(QWidget *parent=0, const char *name=0);
+	~GCUserView();
+
+	void updateAll();
+	QListViewItem *findEntry(const QString &);
+	void updateEntry(const QString &, const Status &);
+	void removeEntry(const QString &);
+	QStringList nickList() const;
+
+protected:
+	// reimplemented
+	void maybeTip(const QPoint &);
+
+signals:
+	void action(const QString &, const Status &, int);
+
+private slots:
+	void qlv_doubleClicked(QListViewItem *);
+	void qlv_contextMenuRequested(QListViewItem *, const QPoint &, int);
+};
+
+/*class GCLineEdit : public QLineEdit
+{
+	Q_OBJECT
+public:
+	GCLineEdit(QWidget *parent=0, const char *name=0);
+
+signals:
+	void upPressed();
+	void downPressed();
+
+protected:
+	void keyPressEvent(QKeyEvent *);
+};*/
+
+class GCMainDlg : public AdvancedWidget<QWidget>
+{
+	Q_OBJECT
+public:
+	GCMainDlg(PsiAccount *, const Jid &);
+	~GCMainDlg();
+
+	Jid jid() const;
+
+	void error(int, const QString &);
+	void presence(const QString &, const Status &);
+	void message(const Message &);
+	void joined();
+
+protected:
+	void keyPressEvent(QKeyEvent *);
+	void closeEvent(QCloseEvent *);
+	void windowActivationChange(bool);
+
+signals:
+	void aSend(const Message &);
+
+public slots:
+	void optionsUpdate();
+
+private slots:
+	void mle_returnPressed();
+	void doTopic();
+	void openFind();
+	void doFind(int, int, const QString &);
+	void flashAnimate();
+	void pa_updatedActivity();
+	void goDisc();
+	void goConn();
+	void lv_action(const QString &, const Status &, int);
+	void doClear();
+	void doClearButton();
+	void toggleSmallChat();
+	void buildMenu();
+	void logSelectionChanged();
+
+public:
+	class Private;
+private:
+	Private *d;
+
+	void doFlash(bool);
+	void doAlert();
+	void updateCaption();
+	void appendSysMsg(const QString &, bool, const QDateTime &ts=QDateTime());
+	void appendMessage(const Message &, bool);
+	void setLooks();
+
+	void contextMenuEvent(QContextMenuEvent *);
+
+	QString getNickColor(QString);
+	QMap<QString,int> nicks;
+	int nicknumber;
+};
+
+class GCFindDlg : public QDialog
+{
+	Q_OBJECT
+public:
+	GCFindDlg(int, int, const QString &, QWidget *parent=0, const char *name=0);
+	~GCFindDlg();
+
+	void found(int, int);
+	void error(const QString &);
+
+signals:
+	void find(int, int, const QString &);
+
+private slots:
+	void doFind();
+
+private:
+	int para, index;
+	QLineEdit *le_input;
+};
Index: psi/tags/0.10-os2-b1/src/historydlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/historydlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/historydlg.cpp	(revision 38)
@@ -0,0 +1,776 @@
+/*
+ * historydlg.cpp - a dialog to show event history
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"historydlg.h"
+
+#include<qpopupmenu.h>
+#include<qheader.h>
+#include<qlayout.h>
+#include<qlabel.h>
+#include<qlineedit.h>
+#include<qpushbutton.h>
+#include<qfiledialog.h>
+#include<qmessagebox.h>
+#include<qframe.h>
+#include<qapplication.h>
+#include<qclipboard.h>
+#include"psiaccount.h"
+#include"psievent.h"
+#include"common.h"
+#include"busywidget.h"
+#include"eventdb.h"
+#include"userlist.h"
+
+static QString getNext(QString *str)
+{
+	// are we in space?
+	int n = 0;
+	if(str->at(n).isSpace()) {
+		// get out of it
+		while(n < (int)str->length() && str->at(n).isSpace())
+			++n;
+		if(n == (int)str->length())
+			return QString::null;
+	}
+	// find end or next space
+	while(n < (int)str->length() && !str->at(n).isSpace())
+		++n;
+	QString result = str->mid(0, n);
+	*str = str->mid(n);
+	return result;
+}
+
+// wraps a string against a fixed width
+static QStringList wrapString(const QString &str, int wid)
+{
+	QStringList lines;
+	QString cur;
+	QString tmp = str;
+	//printf("parsing: [%s]\n", tmp.latin1());
+	while(1) {
+		QString word = getNext(&tmp);
+		if(word == QString::null) {
+			lines += cur;
+			break;
+		}
+		//printf("word:[%s]\n", word.latin1());
+		if(!cur.isEmpty()) {
+			if((int)cur.length() + (int)word.length() > wid) {
+				lines += cur;
+				cur = "";
+			}
+		}
+		if(cur.isEmpty()) {
+			// trim the whitespace in front
+			for(int n = 0; n < (int)word.length(); ++n) {
+				if(!word.at(n).isSpace()) {
+					if(n > 0)
+						word = word.mid(n);
+					break;
+				}
+			}
+		}
+		cur += word;
+	}
+	return lines;
+}
+
+//----------------------------------------------------------------------------
+// HistoryView
+//----------------------------------------------------------------------------
+class HistoryDlg::Private
+{
+public:
+	Private() {}
+
+	Jid jid;
+	PsiAccount *pa;
+	HistoryView *lv;
+	//BusyWidget *busy;
+	QPushButton *pb_prev, *pb_next, *pb_refresh, *pb_find;
+	QLineEdit *le_find;
+
+	QString id_prev, id_begin, id_end, id_next;
+	int reqtype;
+	QString findStr;
+
+	EDBHandle *h, *exp;
+};
+
+HistoryDlg::HistoryDlg(const Jid &jid, PsiAccount *pa)
+:QWidget(0, 0, WDestructiveClose)
+{
+	d = new Private;
+	d->pa = pa;
+	d->jid = jid;
+	d->pa->dialogRegister(this, d->jid);
+	d->exp = 0;
+
+	setCaption(d->jid.full());
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/history"));
+#endif
+
+	d->h = new EDBHandle(d->pa->edb());
+	connect(d->h, SIGNAL(finished()), SLOT(edb_finished()));
+
+	QVBoxLayout *vb1 = new QVBoxLayout(this, 8);
+	d->lv = new HistoryView(this);
+	d->lv->setVScrollBarMode(QScrollView::AlwaysOn);
+	connect(d->lv, SIGNAL(aOpenEvent(PsiEvent *)), SLOT(actionOpenEvent(PsiEvent *)));
+	QSizePolicy sp = d->lv->sizePolicy();
+	sp.setVerStretch(1);
+	d->lv->setSizePolicy(sp);
+	vb1->addWidget(d->lv);
+
+	QHBoxLayout *hb1 = new QHBoxLayout(vb1);
+
+	QVBoxLayout *vb2 = new QVBoxLayout(hb1);
+	QHBoxLayout *hb2 = new QHBoxLayout(vb2);
+
+	//d->busy = new BusyWidget(this);
+	//hb1->addWidget(d->busy);
+
+	d->pb_refresh = new QPushButton(tr("&Latest"), this);
+	d->pb_refresh->setMinimumWidth(80);
+	connect(d->pb_refresh, SIGNAL(clicked()), SLOT(doLatest()));
+	hb2->addWidget(d->pb_refresh);
+
+	d->pb_prev = new QPushButton(tr("&Previous"), this);
+	d->pb_prev->setMinimumWidth(80);
+	connect(d->pb_prev, SIGNAL(clicked()), SLOT(doPrev()));
+	hb2->addWidget(d->pb_prev);
+
+	d->pb_next = new QPushButton(tr("&Next"), this);
+	d->pb_next->setMinimumWidth(80);
+	connect(d->pb_next, SIGNAL(clicked()), SLOT(doNext()));
+	hb2->addWidget(d->pb_next);
+
+	QHBoxLayout *hb3 = new QHBoxLayout(vb2);
+
+	d->le_find = new QLineEdit(this);
+	connect(d->le_find, SIGNAL(textChanged(const QString &)), SLOT(le_textChanged(const QString &)));
+	connect(d->le_find, SIGNAL(returnPressed()), SLOT(doFind()));
+	hb3->addWidget(d->le_find);
+	d->pb_find = new QPushButton(tr("Find"), this);
+	connect(d->pb_find, SIGNAL(clicked()), SLOT(doFind()));
+	d->pb_find->setEnabled(false);
+	hb3->addWidget(d->pb_find);
+
+	QFrame *sep;
+	sep = new QFrame(this);
+	sep->setFrameShape(QFrame::VLine);
+	hb1->addWidget(sep);
+
+	QVBoxLayout *vb3 = new QVBoxLayout(hb1);
+	QPushButton *pb_save = new QPushButton(tr("&Export..."), this);
+	connect(pb_save, SIGNAL(clicked()), SLOT(doSave()));
+	vb3->addWidget(pb_save);
+	QPushButton *pb_erase = new QPushButton(tr("Er&ase All"), this);
+	connect(pb_erase, SIGNAL(clicked()), SLOT(doErase()));
+	vb3->addWidget(pb_erase);
+
+	sep = new QFrame(this);
+	sep->setFrameShape(QFrame::VLine);
+	hb1->addWidget(sep);
+
+	hb1->addStretch(1);
+
+	QVBoxLayout *vb4 = new QVBoxLayout(hb1);
+	vb4->addStretch(1);
+
+	QPushButton *pb_close = new QPushButton(tr("&Close"), this);
+	pb_close->setMinimumWidth(80);
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	vb4->addWidget(pb_close);
+
+	resize(520,320);
+
+	X11WM_CLASS("history");
+
+	d->le_find->setFocus();
+
+	setButtons();
+	doLatest();
+}
+
+HistoryDlg::~HistoryDlg()
+{
+	delete d->exp;
+	d->pa->dialogUnregister(this);
+	delete d;
+}
+
+void HistoryDlg::keyPressEvent(QKeyEvent *e)
+{
+	if(e->key() == Key_Escape)
+		close();
+	else {
+		e->ignore();
+	}
+}
+
+void HistoryDlg::closeEvent(QCloseEvent *e)
+{
+	if(d->exp)
+		return;
+
+	e->accept();
+}
+
+void HistoryDlg::show()
+{
+	QWidget::show();
+	d->lv->doResize();
+}
+
+void HistoryDlg::le_textChanged(const QString &s)
+{
+	if(s.isEmpty() && d->pb_find->isEnabled())
+		d->pb_find->setEnabled(false);
+	else if(!s.isEmpty() && !d->pb_find->isEnabled())
+		d->pb_find->setEnabled(true);
+}
+
+void HistoryDlg::setButtons()
+{
+	d->pb_prev->setEnabled(!d->id_prev.isEmpty());
+	d->pb_next->setEnabled(!d->id_next.isEmpty());
+}
+
+void HistoryDlg::doLatest()
+{
+	loadPage(0);
+}
+
+void HistoryDlg::doPrev()
+{
+	loadPage(1);
+}
+
+void HistoryDlg::doNext()
+{
+	loadPage(2);
+}
+
+void HistoryDlg::doSave()
+{
+	if(option.lastSavePath.isEmpty())
+		option.lastSavePath = QDir::homeDirPath();
+
+	UserListItem *u = d->pa->findFirstRelavent(d->jid);
+	QString them = jidnick(u->jid().full(), u->name());
+	QString s = qstrlower(jidEncode(them));
+
+	QString str = option.lastSavePath + "/" + s + ".txt";
+	while(1) {
+		str = QFileDialog::getSaveFileName(str, tr("Text files (*.txt);;All files (*.*)"), this, 0, tr("Export message history"));
+		if(str.isEmpty())
+			break;
+
+		QFileInfo fi(str);
+		if(fi.exists()) {
+			int x = QMessageBox::information(this, tr("Confirm overwrite"), tr("File already exists, overwrite?"), tr("&Yes"), tr("&No"));
+			if(x != 0)
+				continue;
+		}
+
+		option.lastSavePath = fi.dirPath();
+		exportHistory(str);
+		break;
+	}
+}
+
+void HistoryDlg::doErase()
+{
+	int x = QMessageBox::information(this, tr("Confirm erase all"), tr("This will erase all message history for this contact!\nAre you sure you want to do this?"), tr("&Yes"), tr("&No"), QString::null, 1);
+	if(x == 0) {
+		QString fname = getHistoryDir() + "/" + qstrlower(jidEncode(d->jid.userHost())) + ".history";
+		QFileInfo fi(fname);
+		if(fi.exists()) {
+			QDir dir = fi.dir();
+			dir.remove(fi.fileName());
+		}
+		d->lv->clear();
+	}
+}
+
+void HistoryDlg::loadPage(int type)
+{
+	d->reqtype = type;
+	if(type == 0) {
+		d->pb_refresh->setEnabled(false);
+		d->h->getLatest(d->jid, 50);
+		//printf("EDB: requesting latest 50 events\n");
+	}
+	else if(type == 1) {
+		d->pb_prev->setEnabled(false);
+		d->h->get(d->jid, d->id_prev, EDB::Backward, 50);
+		//printf("EDB: requesting 50 events backward, starting at %s\n", d->id_prev.latin1());
+	}
+	else if(type == 2) {
+		d->pb_next->setEnabled(false);
+		d->h->get(d->jid, d->id_next, EDB::Forward, 50);
+		//printf("EDB: requesting 50 events forward, starting at %s\n", d->id_next.latin1());
+	}
+
+	//d->busy->start();
+}
+
+void HistoryDlg::displayResult(const EDBResult *r, int direction, int max)
+{
+	//d->lv->setUpdatesEnabled(false);
+	d->lv->clear();
+	QPtrListIterator<EDBItem> it(*r);
+	if(direction == EDB::Forward)
+		it.toLast();
+	int at = 0;
+	for(EDBItem *i; (i = it.current()) && (max == -1 ? true : at < max);) {
+		PsiEvent *e = i->event();
+/*printf(" id=%s", i->id().latin1());
+if(i->prevId())
+	printf(", prevId=%s", i->prevId().latin1());
+if(i->nextId())
+	printf(", nextId=%s", i->nextId().latin1());
+printf("\n");
+if(e->type() == PsiEvent::Message) {
+	MessageEvent *me = (MessageEvent *)e;
+	printf(" body: [%s]\n", me->message().body().latin1());
+}
+else if(e->type() == PsiEvent::Auth) {
+	AuthEvent *ae = (AuthEvent *)e;
+	printf(" authType: [%s]\n", ae->authType().latin1());
+}
+else {
+	printf(" unknown event type\n");
+}
+printf("\n");*/
+
+		d->lv->addEvent(e, i->prevId());
+		++at;
+		if(direction == EDB::Backward)
+			++it;
+		else
+			--it;
+	}
+	//d->lv->setUpdatesEnabled(true);
+	//d->lv->repaint(true);
+}
+
+void HistoryDlg::edb_finished()
+{
+	const EDBResult *r = d->h->result();
+	if(r) {
+		//printf("EDB: retrieved %d events:\n", r->count());
+		if(r->count() > 0) {
+			QPtrListIterator<EDBItem> it(*r);
+			if(d->reqtype == 0 || d->reqtype == 1) {
+				// events are in backward order
+				// first entry is the end event
+				it.toFirst();
+				d->id_end = it.current()->id();
+				d->id_next = it.current()->nextId();
+				// last entry is the begin event
+				it.toLast();
+				d->id_begin = it.current()->id();
+				d->id_prev = it.current()->prevId();
+				displayResult(r, EDB::Backward);
+				//printf("[%s],[%s],[%s],[%s]\n", d->id_prev.latin1(), d->id_begin.latin1(), d->id_end.latin1(), d->id_next.latin1());
+			}
+			else if(d->reqtype == 2) {
+				// events are in forward order
+				// last entry is the end event
+				it.toLast();
+				d->id_end = it.current()->id();
+				d->id_next = it.current()->nextId();
+				// first entry is the begin event
+				it.toFirst();
+				d->id_begin = it.current()->id();
+				d->id_prev = it.current()->prevId();
+				displayResult(r, EDB::Forward);
+			}
+			else if(d->reqtype == 3) {
+				// should only be one entry
+				EDBItem *ei = it.current();
+				d->reqtype = 1;
+				d->h->get(d->jid, ei->id(), EDB::Backward, 50);
+				//printf("EDB: requesting 50 events backward, starting at %s\n", d->id_prev.latin1());
+				return;
+			}
+		}
+		else {
+			if(d->reqtype == 3) {
+				QMessageBox::information(this, tr("Find"), tr("Search string '%1' not found.").arg(d->findStr));
+				return;
+			}
+		}
+	}
+	else {
+		//printf("EDB: error\n");
+	}
+
+	if(d->lv->firstChild())
+		d->lv->setSelected(d->lv->firstChild(), true);
+
+	//d->busy->stop();
+	d->pb_refresh->setEnabled(true);
+	setButtons();
+}
+
+void HistoryDlg::actionOpenEvent(PsiEvent *e)
+{
+	openEvent(e);
+}
+
+void HistoryDlg::doFind()
+{
+	QString str = d->le_find->text();
+	if(str.isEmpty())
+		return;
+
+	if(d->lv->childCount() < 1)
+		return;
+
+	HistoryViewItem *i = (HistoryViewItem *)d->lv->selectedItem();
+	if(!i)
+		i = (HistoryViewItem *)d->lv->firstChild();
+	QString id = i->eventId;
+	if(id.isEmpty()) {
+		QMessageBox::information(this, tr("Find"), tr("Already at beginning of message history."));
+		return;
+	}
+
+	//printf("searching for: [%s], starting at id=[%s]\n", str.latin1(), id.latin1());
+	d->reqtype = 3;
+	d->findStr = str;
+	d->h->find(str, d->jid, id, EDB::Backward);
+}
+
+void HistoryDlg::exportHistory(const QString &fname)
+{
+	QFile f(fname);
+	if(!f.open(IO_WriteOnly | IO_Truncate | IO_Translate)) {
+		QMessageBox::information(this, tr("Error"), tr("Error writing to file."));
+		return;
+	}
+	QTextStream stream(&f);
+
+	QString us = d->pa->nick();
+	UserListItem *u = d->pa->findFirstRelavent(d->jid);
+	QString them = jidnick(u->jid().full(), u->name());
+
+	d->exp = new EDBHandle(d->pa->edb());
+	QString id;
+	while(1) {
+		if(id.isEmpty())
+			d->exp->getOldest(d->jid, 1000);
+		else
+			d->exp->get(d->jid, id, EDB::Forward, 1000);
+		while(d->exp->busy())
+			qApp->processEvents();
+
+		const EDBResult *r = d->exp->result();
+		if(!r)
+			break;
+		if(r->count() <= 0)
+			break;
+
+		// events are in forward order
+		QPtrListIterator<EDBItem> it(*r);
+		for(EDBItem *i; (i = it.current()); ++it) {
+			id = it.current()->nextId();
+			PsiEvent *e = i->event();
+			QString txt;
+
+			QDateTime dt = e->timeStamp();
+			QString ts;
+			//ts.sprintf("%04d/%02d/%02d %02d:%02d:%02d", dt.date().year(), dt.date().month(), dt.date().day(), dt.time().hour(), dt.time().minute(), dt.time().second());
+			ts = dt.toString(LocalDate);
+
+			QString nick;
+			if(e->originLocal())
+				nick = us;
+			else
+				nick = them;
+
+			QString heading = QString("(%1) ").arg(ts) + nick + ": ";
+			if(e->type() == PsiEvent::Message) {
+				MessageEvent *me = (MessageEvent *)e;
+				stream << heading << endl;
+
+				QStringList lines = QStringList::split('\n', me->message().body(), true);
+				for(QStringList::ConstIterator lit = lines.begin(); lit != lines.end(); ++lit) {
+					QStringList sub = wrapString(*lit, 72);
+					for(QStringList::ConstIterator lit2 = sub.begin(); lit2 != sub.end(); ++lit2)
+						txt += QString("    ") + *lit2 + '\n';
+				}
+			}
+			else
+				continue;
+
+			stream << txt << endl;
+		}
+
+		// done!
+		if(id.isEmpty())
+			break;
+	}
+	delete d->exp;
+	d->exp = 0;
+	f.close();
+}
+
+//----------------------------------------------------------------------------
+// HistoryView
+//----------------------------------------------------------------------------
+HistoryView::HistoryView(QWidget *parent, const char *name)
+:QListView(parent, name)
+{
+	at_id = 0;
+	connect(this, SIGNAL(doubleClicked(QListViewItem *)), SLOT(qlv_doubleclick(QListViewItem *)));
+	connect(this, SIGNAL(rightButtonPressed(QListViewItem *, const QPoint &, int)), SLOT(qlv_contextPopup(QListViewItem *, const QPoint &, int)));
+
+	setAllColumnsShowFocus(true);
+	addColumn(tr("Type"));
+	addColumn(tr("Origin"));
+	addColumn(tr("Date"));
+	addColumn(tr("Text"));
+	setSorting(2);
+	setResizeMode(QListView::LastColumn);
+	setShowToolTips(false);
+	header()->setClickEnabled(false);
+	header()->setMovingEnabled(false);
+	header()->setResizeEnabled(false);
+}
+
+void HistoryView::resizeEvent(QResizeEvent *e)
+{
+	QListView::resizeEvent(e);
+
+	if(e->oldSize().width() != e->size().width())
+		doResize();
+}
+
+void HistoryView::keyPressEvent(QKeyEvent *e)
+{
+	if(e->key() == Key_Enter || e->key() == Key_Return)
+		doOpenEvent();
+	else
+		QListView::keyPressEvent(e);
+}
+
+void HistoryView::doResize()
+{
+	QListViewItemIterator it(this);
+	HistoryViewItem *item;
+	for(; it.current() ; ++it) {
+		item = (HistoryViewItem *)it.current();
+		item->setup();
+	}
+}
+
+void HistoryView::addEvent(PsiEvent *e, const QString &eid)
+{
+	new HistoryViewItem(e, eid, at_id++, this);
+}
+
+void HistoryView::doOpenEvent()
+{
+	HistoryViewItem *i = (HistoryViewItem *)selectedItem();
+	if(!i)
+		return;
+	aOpenEvent(i->e);
+}
+
+void HistoryView::qlv_doubleclick(QListViewItem *xi)
+{
+	HistoryViewItem *i = (HistoryViewItem *)xi;
+
+	setSelected(i, true);
+	doOpenEvent();
+}
+
+void HistoryView::qlv_contextPopup(QListViewItem *ix, const QPoint &pos, int)
+{
+	HistoryViewItem *i = (HistoryViewItem *)ix;
+	if(!i)
+		return;
+
+	QPopupMenu popup;
+	popup.insertItem(tr("Open"), 1);
+	popup.insertSeparator();
+	popup.insertItem(tr("Copy"), 2);
+
+	if(i->e->type() != PsiEvent::Message)
+		popup.setItemEnabled(2, false);
+
+	int x = popup.exec(pos);
+
+	if(x == 1)
+		doOpenEvent();
+	else if(x == 2) {
+		HistoryViewItem *i = (HistoryViewItem *)selectedItem();
+		if(!i)
+			return;
+
+		MessageEvent *me = (MessageEvent *)i->e;
+		QApplication::clipboard()->setText(me->message().body(), QClipboard::Clipboard);
+		if(QApplication::clipboard()->supportsSelection())
+			QApplication::clipboard()->setText(me->message().body(), QClipboard::Selection);
+	}
+}
+
+
+//----------------------------------------------------------------------------
+// HistoryViewItem
+//----------------------------------------------------------------------------
+HistoryViewItem::HistoryViewItem(PsiEvent *_e, const QString &eid, int xid, QListView *parent)
+:QListViewItem(parent)
+{
+	rt = 0;
+	id = xid;
+	eventId = eid;
+
+	if(_e->type() == PsiEvent::Message) {
+		MessageEvent *me = (MessageEvent *)_e;
+		e = new MessageEvent(*me);
+	}
+	else if(_e->type() == PsiEvent::Auth) {
+		AuthEvent *ae = (AuthEvent *)_e;
+		e = new AuthEvent(*ae);
+	}
+
+	Icon *a = is->event2icon(e);
+	if(e->type() == PsiEvent::Message) {
+		MessageEvent *me = (MessageEvent *)e;
+		const Message &m = me->message();
+		text = plain2rich(m.body());
+
+		if(!m.urlList().isEmpty())
+			setPixmap(0, IconsetFactory::icon("psi/www"));
+		else if(e->originLocal())
+			setPixmap(0, IconsetFactory::icon("psi/sendMessage"));
+		else
+			setPixmap(0, a->impix());
+	}
+	else if(e->type() == PsiEvent::Auth) {
+		AuthEvent *ae = (AuthEvent *)e;
+		text = ae->authType();
+		setPixmap(0, a->impix());
+	}
+
+	if(e->originLocal())
+		setText(1, HistoryView::tr("To"));
+	else
+		setText(1, HistoryView::tr("From"));
+
+	QString date;
+	const QDateTime &ts = e->timeStamp();
+	/*date.sprintf("%02d/%02d/%02d %02d:%02d:%02d",
+		ts.date().month(),
+		ts.date().day(),
+		ts.date().year(),
+		ts.time().hour(),
+		ts.time().minute(),
+		ts.time().second());*/
+	date = ts.toString(LocalDate);
+
+	setText(2, date);
+
+	rt = new QSimpleRichText(text, listView()->font());
+}
+
+HistoryViewItem::~HistoryViewItem()
+{
+	delete rt;
+	delete e;
+}
+
+// reimplemented from QListViewItem.  setup() and paintCell() are tricky stuff
+void HistoryViewItem::setup()
+{
+	widthChanged();
+
+	QListView *lv = listView();
+
+	if(rt) {
+		int w = lv->columnWidth(3);
+		rt->setWidth(w);
+	}
+
+	int y;
+	//y = lv->fontMetrics().size(AlignVCenter, displayStr).height();
+	if(!rt)
+		y = 22;
+	else
+		y = rt->height();
+
+	y += lv->itemMargin() * 2;
+
+	// ensure an even number
+	if(y & 1)
+		++y;
+
+	setHeight(y);
+}
+
+void HistoryViewItem::paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int alignment)
+{
+	QColorGroup mycg = cg;
+	if(e->originLocal())
+		mycg.setColor(QColorGroup::Text, Qt::red);
+	else
+		mycg.setColor(QColorGroup::Text, Qt::blue);
+
+	if(column == 3) {
+		QBrush br;
+		if(isSelected()) {
+			mycg.setColor(QColorGroup::Text, mycg.highlightedText());
+			br = cg.brush(QColorGroup::Highlight);
+		}
+		else {
+			br = cg.brush(QColorGroup::Base);
+		}
+
+		int h = height();
+		if(rt) {
+			QSimpleRichText tmp(QString("<qt><font color=\"%1\">" + text + "</font></qt>").arg(mycg.text().name()), listView()->font());
+			tmp.setWidth(rt->width());
+			tmp.draw(p, 0, 0, QRect(0, 0, width, h), mycg, &br);
+		}
+	}
+	else {
+		alignment = AlignTop;
+
+		QListViewItem::paintCell(p, mycg, column, width, alignment);
+	}
+}
+
+int HistoryViewItem::compare(QListViewItem *xi, int, bool) const
+{
+	HistoryViewItem *i = (HistoryViewItem *)xi;
+	return id - i->id;
+}
+
+int HistoryViewItem::rtti() const
+{
+	return 7105;
+}
+
Index: psi/tags/0.10-os2-b1/src/historydlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/historydlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/historydlg.h	(revision 38)
@@ -0,0 +1,120 @@
+/*
+ * historydlg.h - a dialog to show event history
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef HISTORYDLG_H
+#define HISTORYDLG_H
+
+#include<qlistview.h>
+#include<qsimplerichtext.h>
+#include"xmpp.h"
+
+using namespace XMPP;
+
+class PsiEvent;
+class PsiAccount;
+class EDBItem;
+class EDBResult;
+
+class HistoryViewItem : public QListViewItem
+{
+public:
+	HistoryViewItem(PsiEvent *, const QString &, int id, QListView *);
+	~HistoryViewItem();
+
+	QSimpleRichText *rt;
+	QString text;
+	int id;
+	PsiEvent *e;
+	QString eventId;
+
+	// reimplemented
+	int rtti() const;
+	void paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int alignment);
+	void setup();
+	int compare(QListViewItem *, int column, bool ascending) const;
+};
+
+class HistoryView : public QListView
+{
+	Q_OBJECT
+public:
+	HistoryView(QWidget *parent=0, const char *name=0);
+
+	void doResize();
+	void addEvent(PsiEvent *, const QString &);
+
+	// reimplemented
+	void keyPressEvent(QKeyEvent *e);
+	void resizeEvent(QResizeEvent *e);
+
+signals:
+	void aOpenEvent(PsiEvent *);
+
+private slots:
+	void doOpenEvent();
+
+	void qlv_doubleclick(QListViewItem *);
+	void qlv_contextPopup(QListViewItem *, const QPoint &, int);
+
+private:
+	int at_id;
+};
+
+class HistoryDlg : public QWidget
+{
+	Q_OBJECT
+public:
+	HistoryDlg(const Jid &, PsiAccount *);
+	~HistoryDlg();
+
+	// reimplemented
+	void keyPressEvent(QKeyEvent *e);
+	void closeEvent(QCloseEvent *e);
+
+signals:
+	void openEvent(PsiEvent *);
+
+public slots:
+	// reimplemented
+	void show();
+
+private slots:
+	void doLatest();
+	void doPrev();
+	void doNext();
+	void doSave();
+	void doErase();
+	void setButtons();
+	void actionOpenEvent(PsiEvent *);
+	void doFind();
+
+	void edb_finished();
+	void le_textChanged(const QString &);
+
+private:
+	class Private;
+	Private *d;
+
+	void loadPage(int);
+	void displayResult(const EDBResult *, int, int max=-1);
+	void exportHistory(const QString &fname);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/infodlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/infodlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/infodlg.cpp	(revision 38)
@@ -0,0 +1,549 @@
+/*
+ * infodlg.cpp - handle vcard
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"infodlg.h"
+
+#include<qlayout.h>
+#include<qlabel.h>
+#include<qtabwidget.h>
+#include<qpushbutton.h>
+#include<qlineedit.h>
+#include<qmessagebox.h>
+#include<qtextedit.h>
+#include<qfiledialog.h> 
+#include<qbuffer.h>
+#include"xmpp.h"
+#include"msgmle.h"
+#include"userlist.h"
+#include"xmpp_vcard.h"
+#include"xmpp_tasks.h"
+#include"psiaccount.h"
+#include"busywidget.h"
+#include"common.h"
+#include"vcardfactory.h"
+#include"iconwidget.h"
+
+
+using namespace XMPP;
+		
+class InfoDlg::Private
+{
+public:
+	Private() {}
+
+	int type;
+	Jid jid;
+	VCard vcard;
+	PsiAccount *pa;
+	BusyWidget *busy;
+	bool te_edited;
+	int actionType;
+	JT_VCard *jt;
+	bool cacheVCard;
+	PsiTextView* te_status;
+	QByteArray photo;
+};
+
+InfoDlg::InfoDlg(int type, const Jid &j, const VCard &vcard, PsiAccount *pa, QWidget *parent, const char *name, bool cacheVCard)
+:InfoUI(parent, name, false, WDestructiveClose)
+{
+	d = new Private;
+	d->type = type;
+	d->jid = j;
+	d->vcard = vcard;
+	d->pa = pa;
+	d->te_edited = false;
+	d->jt = 0;
+	d->pa->dialogRegister(this, j);
+	d->cacheVCard = cacheVCard;
+	d->te_status = NULL;
+	d->busy = busy;
+
+	te_desc->setTextFormat(QTextEdit::PlainText);
+
+	setCaption(d->jid.full());
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/vCard"));
+#endif
+
+	connect(pb_refresh, SIGNAL(clicked()), this, SLOT(doRefresh()));
+	connect(pb_refresh, SIGNAL(clicked()), this, SLOT(updateStatus()));
+	connect(te_desc, SIGNAL(textChanged()), this, SLOT(textChanged()));
+	connect(pb_open, SIGNAL(clicked()), this, SLOT(selectPhoto()));
+	connect(pb_clear, SIGNAL(clicked()), this, SLOT(clearPhoto()));
+
+	if(d->type == Self) {
+		connect(pb_submit, SIGNAL(clicked()), this, SLOT(doSubmit()));
+	}
+	else {
+		// Add a status tab
+		d->te_status = new PsiTextView(tabwidget);
+		d->te_status->setReadOnly(true);
+		d->te_status->setTextFormat(RichText);
+		tabwidget->addTab(d->te_status, tr("Status"));
+		updateStatus();
+
+		// Hide buttons
+		pb_submit->hide();
+		pb_open->hide();
+		pb_clear->hide();
+		ly_photo->setSpacing(0);
+		setReadOnly(true);
+	}
+
+	setData(d->vcard);
+}
+
+InfoDlg::~InfoDlg()
+{
+	d->pa->dialogUnregister(this);
+	delete d;
+}
+
+/*void InfoDlg::closeEvent(QCloseEvent *e)
+{
+	e->accept();
+	reject();
+}*/
+
+void InfoDlg::done(int r)
+{
+	// don't close if submitting
+	if(d->busy->isActive() && d->actionType == 1)
+		return;
+
+	if(d->type == Self && edited()) {
+		int n = QMessageBox::information(this, tr("Warning"), tr("You have not published your account information changes.\nAre you sure you want to discard them?"), tr("Close and discard"), tr("Don't close"));
+		if(n != 0)
+			return;
+	}
+
+	// cancel active transaction (refresh only)
+	if(d->busy->isActive() && d->actionType == 0) {
+		delete d->jt;
+		d->jt = 0;
+	}
+
+	QDialog::done(r);
+}
+
+void InfoDlg::jt_finished()
+{
+	d->busy->stop();
+	pb_refresh->setEnabled(true);
+	pb_submit->setEnabled(true);
+	pb_close->setEnabled(true);
+	fieldsEnable(true);
+
+	if(d->jt->success()) {
+		if(d->actionType == 0) {
+			d->vcard = d->jt->vcard();
+			setData(d->vcard);
+		}
+		else if(d->actionType == 1) {
+			d->vcard = d->jt->vcard();
+			if ( d->cacheVCard )
+				VCardFactory::setVCard(d->jid, d->vcard);
+			setData(d->vcard);
+		}
+
+		if(d->jid.compare(d->pa->jid(), false)) {
+			if (!d->vcard.nickName().isEmpty())
+				d->pa->setNick(d->vcard.nickName());
+			else
+				d->pa->setNick(d->pa->jid().user());
+		}
+
+		if(d->actionType == 1)
+			QMessageBox::information(this, tr("Success"), tr("Your account information has been published."));
+	}
+	else {
+		if(d->actionType == 0) {
+			if(d->type == Self)
+				QMessageBox::critical(this, tr("Error"), tr("Unable to retrieve your account information.  Perhaps you haven't entered any yet."));
+			else
+				QMessageBox::critical(this, tr("Error"), tr("Unable to retrieve information about this contact.\nReason: %1").arg(d->jt->statusString()));
+		}
+		else {
+			QMessageBox::critical(this, tr("Error"), tr("Unable to publish your account information.\nReason: %1").arg(d->jt->statusString()));
+		}
+	}
+
+	d->jt = 0;
+}
+
+void InfoDlg::setData(const VCard &i)
+{
+	le_fullname->setText( i.fullName() );
+	le_nickname->setText( i.nickName() );
+	le_bday->setText( i.bdayStr() );
+
+	QString email;
+	if ( !i.emailList().isEmpty() )
+		email = i.emailList()[0].userid;
+	le_email->setText( email );
+
+	le_homepage->setText( i.url() );
+
+	QString phone;
+	if ( !i.phoneList().isEmpty() )
+		phone = i.phoneList()[0].number;
+	le_phone->setText( phone );
+
+	VCard::Address addr;
+	if ( !i.addressList().isEmpty() )
+		addr = i.addressList()[0];
+	le_street->setText( addr.street );
+	le_ext->setText( addr.extaddr );
+	le_city->setText( addr.locality );
+	le_state->setText( addr.region );
+	le_pcode->setText( addr.pcode );
+	le_country->setText( addr.country );
+
+	le_orgName->setText( i.org().name );
+
+	QString unit;
+	if ( !i.org().unit.isEmpty() )
+		unit = i.org().unit[0];
+	le_orgUnit->setText( unit );
+
+	le_title->setText( i.title() );
+	le_role->setText( i.role() );
+	te_desc->setText( i.desc() );
+	
+	if ( !i.photo().isEmpty() ) {
+		//printf("There is a picture...\n");
+		d->photo = i.photo();
+		updatePhoto();
+	}
+	else
+		clearPhoto();
+
+	setEdited(false);
+}
+
+void InfoDlg::updatePhoto() 
+{
+	int max_width = label_photo->width() - 20; // FIXME: Ugly magic number
+	int max_height = label_photo->height() - 20; // FIXME: Ugly magic number
+	
+	QImage img(d->photo);
+	QImage img_scaled;
+	if (img.width() > max_width || img.height() > max_height) {
+		img_scaled = img.smoothScale(max_width, max_height,QImage::ScaleMin);
+	}
+	else {
+		img_scaled = img;
+	}
+	label_photo->setPixmap(QPixmap(img_scaled));
+}
+
+void InfoDlg::fieldsEnable(bool x)
+{
+	le_fullname->setEnabled(x);
+	le_nickname->setEnabled(x);
+	le_bday->setEnabled(x);
+	le_email->setEnabled(x);
+	le_homepage->setEnabled(x);
+	le_phone->setEnabled(x);
+	pb_open->setEnabled(x);
+	pb_clear->setEnabled(x);
+
+	le_street->setEnabled(x);
+	le_ext->setEnabled(x);
+	le_city->setEnabled(x);
+	le_state->setEnabled(x);
+	le_pcode->setEnabled(x);
+	le_country->setEnabled(x);
+
+	le_orgName->setEnabled(x);
+	le_orgUnit->setEnabled(x);
+	le_title->setEnabled(x);
+	le_role->setEnabled(x);
+	te_desc->setEnabled(x);
+
+	setEdited(false);
+}
+
+void InfoDlg::setEdited(bool x)
+{
+	le_fullname->setEdited(x);
+	le_nickname->setEdited(x);
+	le_bday->setEdited(x);
+	le_email->setEdited(x);
+	le_homepage->setEdited(x);
+	le_phone->setEdited(x);
+	le_street->setEdited(x);
+	le_ext->setEdited(x);
+	le_city->setEdited(x);
+	le_state->setEdited(x);
+	le_pcode->setEdited(x);
+	le_country->setEdited(x);
+	le_orgName->setEdited(x);
+	le_orgUnit->setEdited(x);
+	le_title->setEdited(x);
+	le_role->setEdited(x);
+
+	d->te_edited = x;
+}
+
+bool InfoDlg::edited()
+{
+	bool x = false;
+
+	if(le_fullname->edited()) x = true;
+	if(le_nickname->edited()) x = true;
+	if(le_bday->edited()) x = true;
+	if(le_email->edited()) x = true;
+	if(le_homepage->edited()) x = true;
+	if(le_phone->edited()) x = true;
+	if(le_street->edited()) x = true;
+	if(le_ext->edited()) x = true;
+	if(le_city->edited()) x = true;
+	if(le_state->edited()) x = true;
+	if(le_pcode->edited()) x = true;
+	if(le_country->edited()) x = true;
+	if(le_orgName->edited()) x = true;
+	if(le_orgUnit->edited()) x = true;
+	if(le_title->edited()) x = true;
+	if(le_role->edited()) x = true;
+	if(d->te_edited) x = true;
+
+	return x;
+}
+
+void InfoDlg::setReadOnly(bool x)
+{
+	le_fullname->setReadOnly(x);
+	le_nickname->setReadOnly(x);
+	le_bday->setReadOnly(x);
+	le_email->setReadOnly(x);
+	le_homepage->setReadOnly(x);
+	le_phone->setReadOnly(x);
+	le_street->setReadOnly(x);
+	le_ext->setReadOnly(x);
+	le_city->setReadOnly(x);
+	le_state->setReadOnly(x);
+	le_pcode->setReadOnly(x);
+	le_country->setReadOnly(x);
+	le_orgName->setReadOnly(x);
+	le_orgUnit->setReadOnly(x);
+	le_title->setReadOnly(x);
+	le_role->setReadOnly(x);
+	te_desc->setReadOnly(x);
+}
+
+void InfoDlg::doRefresh()
+{
+	if(!d->pa->checkConnected(this))
+		return;
+	if(!pb_refresh->isEnabled())
+		return;
+	if(d->busy->isActive())
+		return;
+
+	pb_submit->setEnabled(false);
+	pb_refresh->setEnabled(false);
+	fieldsEnable(false);
+
+	d->actionType = 0;
+	d->busy->start();
+
+	d->jt = VCardFactory::getVCard(d->jid, d->pa->client()->rootTask(), this, SLOT(jt_finished()), d->cacheVCard);
+}
+
+void InfoDlg::doSubmit()
+{
+	if(!d->pa->checkConnected(this))
+		return;
+	if(!pb_submit->isEnabled())
+		return;
+	if(d->busy->isActive())
+		return;
+
+	VCard submit_vcard = makeVCard();
+
+	pb_submit->setEnabled(false);
+	pb_refresh->setEnabled(false);
+	pb_close->setEnabled(false);
+	fieldsEnable(false);
+
+	d->actionType = 1;
+	d->busy->start();
+
+	d->jt = new JT_VCard(d->pa->client()->rootTask());
+	connect(d->jt, SIGNAL(finished()), SLOT(jt_finished()));
+	d->jt->set(submit_vcard);
+	d->jt->go(true);
+}
+
+VCard InfoDlg::makeVCard()
+{
+	VCard v;
+
+	v.setFullName( le_fullname->text() );
+	v.setNickName( le_nickname->text() );
+	v.setBdayStr( le_bday->text() );
+
+	if ( !le_email->text().isEmpty() ) {
+		VCard::Email email;
+		email.internet = true;
+		email.userid = le_email->text();
+
+		VCard::EmailList list;
+		list << email;
+		v.setEmailList( list );
+	}
+
+	v.setUrl( le_homepage->text() );
+
+	if ( !le_phone->text().isEmpty() ) {
+		VCard::Phone p;
+		p.home = true;
+		p.voice = true;
+		p.number = le_phone->text();
+
+		VCard::PhoneList list;
+		list << p;
+		v.setPhoneList( list );
+	}
+	
+	if ( !d->photo.isEmpty() ) {
+		//printf("Adding a pixmap to the vCard...\n");
+		v.setPhoto( d->photo );
+	}
+
+	if ( !le_street->text().isEmpty() ||
+	     !le_ext->text().isEmpty()    ||
+	     !le_city->text().isEmpty()   ||
+	     !le_state->text().isEmpty()  ||
+	     !le_pcode->text().isEmpty()  ||
+	     !le_country->text().isEmpty() )
+	{
+		VCard::Address addr;
+		addr.home = true;
+		addr.street = le_street->text();
+		addr.extaddr = le_ext->text();
+		addr.locality = le_city->text();
+		addr.region = le_state->text();
+		addr.pcode = le_pcode->text();
+		addr.country = le_country->text();
+
+		VCard::AddressList list;
+		list << addr;
+		v.setAddressList( list );
+	}
+
+	VCard::Org org;
+
+	org.name = le_orgName->text();
+
+	if ( !le_orgUnit->text().isEmpty() )
+		org.unit << le_orgUnit->text();
+
+	v.setOrg( org );
+
+	v.setTitle( le_title->text() );
+	v.setRole( le_role->text() );
+	v.setDesc( te_desc->text() );
+
+	return v;
+}
+
+void InfoDlg::textChanged()
+{
+	d->te_edited = true;
+}
+
+/*!
+ * Opens a file browser dialog, and if selected, calls the setPreviewPhoto with the consecuent path.
+ * \see setPreviewPhoto(const QString& path)
+*/
+void InfoDlg::selectPhoto()
+{
+	while(1) {
+		if(option.lastPath.isEmpty())
+			option.lastPath = QDir::homeDirPath();
+		QString str = QFileDialog::getOpenFileName(option.lastPath, tr("Images (*.png *.xpm *.jpg *.PNG *.XPM *.JPG)"), this, 0, tr("Choose a file"));
+		if(!str.isEmpty()) {
+			QFileInfo fi(str);
+			if(!fi.exists()) {
+				QMessageBox::information(this, tr("Error"), tr("The file specified does not exist."));
+				continue;
+			}
+			option.lastPath = fi.dirPath();
+			//printf(QDir::convertSeparators(fi.filePath()));
+			
+			// put the image in the preview box
+			setPreviewPhoto(str);
+		}
+		break;
+	}
+	
+}
+
+/*!
+ * Loads the image from the requested URL, and inserts the resized image into the preview box.
+ * \param path image file to load
+*/
+void InfoDlg::setPreviewPhoto(const QString& path)
+{
+	QFile photo_file(path);
+	if (!photo_file.open(IO_ReadOnly))
+		return;
+	
+	QByteArray photo_data = photo_file.readAll();
+	QImage photo_image(photo_data);
+	if(!photo_image.isNull()) {
+		d->photo = photo_data;
+		updatePhoto();
+		d->te_edited = true;
+	}
+}
+
+/*!
+ * Clears the preview image box and marks the te_edited signal in the private.
+*/
+void InfoDlg::clearPhoto()
+{
+	// this will cause the pixmap disappear
+	label_photo->setText(tr("Picture not\navailable"));
+	d->photo = QByteArray();
+	
+	// the picture changed, so notify there are some changes done
+	d->te_edited = true;
+}
+
+/*!
+ * Updates the status info of the contact
+ */
+void InfoDlg::updateStatus()
+{
+	if (!d->te_status)
+		return;
+
+	UserListItem *u = d->pa->find(d->jid);
+	if(u) {
+		d->te_status->setText(u->makeDesc());
+	}
+	else {
+		d->te_status->clear();
+	}
+}
Index: psi/tags/0.10-os2-b1/src/infodlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/infodlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/infodlg.h	(revision 38)
@@ -0,0 +1,72 @@
+/*
+ * infodlg.h - handle vcard
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef INFODLG_H
+#define INFODLG_H
+
+#include"ui_info.h"
+
+namespace XMPP
+{
+	class Jid;
+	class VCard;
+}
+
+class PsiAccount;
+
+class InfoDlg : public InfoUI
+{
+	Q_OBJECT
+public:
+	enum { Self, Contact };
+	InfoDlg(int type, const XMPP::Jid &, const XMPP::VCard &, PsiAccount *, QWidget *parent=0, const char *name=0, bool cacheVCard = true);
+	~InfoDlg();
+
+protected:
+	// reimplemented
+	//void closeEvent(QCloseEvent *);
+	void updatePhoto();
+
+public slots:
+	void doRefresh();
+	void updateStatus();
+	void done(int);
+
+private slots:
+	void jt_finished();
+	void doSubmit();
+	void textChanged();
+	void selectPhoto();
+	void clearPhoto();
+
+private:
+	class Private;
+	Private *d;
+
+	void setData(const XMPP::VCard &);
+	XMPP::VCard makeVCard();
+	void fieldsEnable(bool);
+	void setReadOnly(bool);
+	bool edited();
+	void setEdited(bool);
+	void setPreviewPhoto(const QString& str);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/jltest.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/jltest.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/jltest.cpp	(revision 38)
@@ -0,0 +1,268 @@
+#include"jltest.h"
+
+#include<qpushbutton.h>
+#include<qlayout.h>
+#include<qcheckbox.h>
+#include<qmessagebox.h>
+#include<qgroupbox.h>
+#include<qprogressbar.h>
+#include<qradiobutton.h>
+#include<qbuttongroup.h>
+#include<qdom.h>
+#include<qtimer.h>
+#include<stdlib.h>
+#include"im.h"
+#include"xmpp_jidlink.h"
+#include"xmpp_xmlcommon.h"
+#include"psiaccount.h"
+#include"busywidget.h"
+
+#define TOTAL_SIZE   32768
+#define PACKET_SIZE  8192
+#define PACKET_DELAY 2000
+
+class JLTestDlg::Private
+{
+public:
+	PsiAccount *pa;
+	Jid jid;
+	BusyWidget *busy;
+	QRadioButton *rb_dtcp, *rb_ibb;
+	QPushButton *pb_test;
+	QProgressBar *pr_status;
+	QButtonGroup *bg_type;
+	JidLink *jl;
+	QLabel *lb_status;
+	int mode;
+
+	int numBytes;
+};
+
+JLTestDlg::JLTestDlg(const Jid &j, PsiAccount *pa)
+:QDialog(0, 0, false, WDestructiveClose)
+{
+	d = new Private;
+	d->pa = pa;
+	d->jid = j;
+	d->mode = ModeConnect;
+	d->jl = 0;
+	setCaption("Link Test");
+	init();
+	d->pb_test->setText(tr("Connect"));
+
+	d->rb_dtcp->setChecked(true);
+}
+
+JLTestDlg::JLTestDlg(const Jid &j, JidLink *jl, PsiAccount *pa)
+:QDialog(0, 0, false, WDestructiveClose)
+{
+	d = new Private;
+	d->pa = pa;
+	d->jid = j;
+	d->mode = ModeAccept;
+	d->jl = jl;
+	setCaption("Link Test");
+	init();
+	d->pb_test->setText(tr("Accept"));
+	if(jl->type() == JidLink::DTCP)
+		d->rb_dtcp->setChecked(true);
+	else
+		d->rb_ibb->setChecked(true);
+	d->bg_type->setEnabled(false);
+}
+
+void JLTestDlg::init()
+{
+	QVBoxLayout *vb = new QVBoxLayout(this, 8);
+
+	// JID
+	QLabel *l = new QLabel(QString("Link test with %1").arg(d->jid.full()), this);
+	vb->addWidget(l);
+
+	// type selection
+	d->bg_type = new QButtonGroup(1, Vertical, "Type", this);
+	vb->addWidget(d->bg_type);
+	d->rb_dtcp = new QRadioButton("Direct (S5B)", d->bg_type);
+	d->rb_ibb = new QRadioButton("Thru-server (IBB)", d->bg_type);
+
+	// status area
+	QGroupBox *gr = new QGroupBox(1, Horizontal, "Status", this);
+	d->pr_status = new QProgressBar(100, gr);
+	d->pr_status->setProgress(0);
+	d->lb_status = new QLabel(gr);
+	d->lb_status->setText("Idle");
+
+	vb->addWidget(gr);
+
+	vb->addStretch(1);
+	QHBoxLayout *hb = new QHBoxLayout(vb);
+	d->busy = new BusyWidget(this);
+	hb->addWidget(d->busy);
+	d->pb_test = new QPushButton(this);
+	connect(d->pb_test, SIGNAL(clicked()), SLOT(start()));
+	hb->addWidget(d->pb_test);
+	QPushButton *pb_close = new QPushButton(tr("Close"), this);
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	hb->addWidget(pb_close);
+
+	d->pb_test->setFocus();
+}
+
+JLTestDlg::~JLTestDlg()
+{
+	if(d->jl) {
+		d->jl->close();
+		delete d->jl;
+	}
+	delete d;
+}
+
+void JLTestDlg::reset()
+{
+	d->mode = ModeConnect;
+	d->pb_test->setEnabled(true);
+	d->pb_test->setText(tr("Connect"));
+	d->lb_status->setText("Idle");
+	d->pr_status->setProgress(0);
+	d->bg_type->setEnabled(true);
+}
+
+void JLTestDlg::start()
+{
+	if(d->mode == ModeConnect) {
+		int type;
+		if(d->rb_dtcp->isChecked())
+			type = JidLink::DTCP;
+		else
+			type = JidLink::IBB;
+
+		d->jl = new JidLink(d->pa->client());
+		connect(d->jl, SIGNAL(connected()), SLOT(jl_connected()));
+		connect(d->jl, SIGNAL(connectionClosed()), SLOT(jl_connectionClosed()));
+		connect(d->jl, SIGNAL(error(int)), SLOT(jl_error(int)));
+		connect(d->jl, SIGNAL(bytesWritten(int)), SLOT(jl_bytesWritten(int)));
+		connect(d->jl, SIGNAL(readyRead()), SLOT(jl_readyRead()));
+
+		QDomElement e = textTag(d->pa->client()->doc(), "comment", "this is only a test!");
+		d->jl->connectToJid(d->jid, type, e);
+
+		d->bg_type->setEnabled(false);
+		d->lb_status->setText("Requesting...");
+	}
+	else {
+		connect(d->jl, SIGNAL(connected()), SLOT(jl_connected()));
+		connect(d->jl, SIGNAL(connectionClosed()), SLOT(jl_connectionClosed()));
+		connect(d->jl, SIGNAL(error(int)), SLOT(jl_error(int)));
+		connect(d->jl, SIGNAL(bytesWritten(int)), SLOT(jl_bytesWritten(int)));
+		connect(d->jl, SIGNAL(readyRead()), SLOT(jl_readyRead()));
+
+		d->jl->accept();
+
+		d->lb_status->setText("Connecting...");
+	}
+
+	d->busy->start();
+	d->pb_test->setEnabled(false);
+}
+
+void JLTestDlg::jl_connected()
+{
+	d->lb_status->setText("Transferring data...");
+	d->numBytes = 0;
+
+	if(d->mode == ModeConnect)
+		doNext();
+}
+
+void JLTestDlg::jl_connectionClosed()
+{
+	d->busy->stop();
+
+	delete d->jl;
+	d->jl = 0;
+
+	QMessageBox::information(this, tr("Error"), tr("Connection closed."));
+
+	reset();
+}
+
+void JLTestDlg::jl_error(int)
+{
+	d->busy->stop();
+
+	QString str;
+	delete d->jl;
+	d->jl = 0;
+
+	/*if(x == DTCPConnection::ErrRequest)
+		str = tr("Error during request!");
+	else if(x == DTCPConnection::ErrConnect)
+		str = tr("Error during connect!");
+	else
+		str = tr("Socket error!");*/
+	str = tr("Stream or connection error.");
+
+	QMessageBox::information(this, tr("Error"), str);
+
+	reset();
+}
+
+void JLTestDlg::jl_readyRead()
+{
+	QByteArray r;
+	r = d->jl->read();
+	printf("got [%d] bytes\n", r.size());
+
+	d->numBytes += r.size();
+	int percent = 100 * d->numBytes / TOTAL_SIZE;
+	d->pr_status->setProgress(percent);
+
+	if(d->numBytes >= TOTAL_SIZE) {
+		d->busy->stop();
+
+		d->jl->close();
+		delete d->jl;
+		d->jl = 0;
+
+		d->lb_status->setText("Finished");
+	}
+}
+
+void JLTestDlg::jl_bytesWritten(int x)
+{
+	d->numBytes += x;
+	int percent = 100 * d->numBytes / TOTAL_SIZE;
+	d->pr_status->setProgress(percent);
+
+	if(d->jl->bytesToWrite() > 0)
+		return;
+
+	if(d->numBytes >= TOTAL_SIZE) {
+		d->busy->stop();
+
+		d->jl->close();
+		d->jl->deleteLater();
+		d->jl = 0;
+
+		d->lb_status->setText("Finished");
+	}
+	else {
+		QTimer::singleShot(PACKET_DELAY, this, SLOT(doNext()));
+	}
+}
+
+void JLTestDlg::doNext()
+{
+	if(!d->jl)
+		return;
+
+	writePacket();
+}
+
+void JLTestDlg::writePacket()
+{
+	QByteArray a(PACKET_SIZE);
+	for(int n = 0; n < (int)a.size(); ++n)
+		a[n] = rand();
+	d->jl->write(a);
+}
Index: psi/tags/0.10-os2-b1/src/jltest.h
===================================================================
--- psi/tags/0.10-os2-b1/src/jltest.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/jltest.h	(revision 38)
@@ -0,0 +1,40 @@
+#include<qdialog.h>
+#include"xmpp.h"
+
+namespace XMPP
+{
+	class JidLink;
+}
+
+using namespace XMPP;
+
+class PsiAccount;
+
+class JLTestDlg : public QDialog
+{
+	Q_OBJECT
+public:
+	enum { ModeConnect, ModeAccept };
+	JLTestDlg(const Jid &, PsiAccount *);
+	JLTestDlg(const Jid &, JidLink *, PsiAccount *);
+	~JLTestDlg();
+
+private slots:
+	void start();
+	void jl_connected();
+	void jl_connectionClosed();
+	void jl_error(int);
+	void jl_readyRead();
+	void jl_bytesWritten(int);
+
+	void doNext();
+
+private:
+	class Private;
+	Private *d;
+
+	void init();
+	void reset();
+
+	void writePacket();
+};
Index: psi/tags/0.10-os2-b1/src/main.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/main.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/main.cpp	(revision 38)
@@ -0,0 +1,421 @@
+/*
+ * main.cpp - initialization and profile/settings handling
+ * Copyright (C) 2001-2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"main.h"
+
+#include"psiapplication.h"
+#include<qtimer.h>
+
+#include<qprocess.h>
+#include<qimage.h>
+#include<qbitmap.h>
+#include<qtextcodec.h>
+#include<qsettings.h>
+#include<qsocketdevice.h>
+#include<qcombobox.h>
+#include<qcheckbox.h>
+#include<qmessagebox.h>
+#include<qca.h>
+#include<stdlib.h>
+#include<time.h>
+#include"common.h"
+#include"profiles.h"
+#include"profiledlg.h"
+#include"xmpp.h"
+
+// need to remove this later
+#include"hash.h"
+
+#include"eventdlg.h"
+#include"chatdlg.h"
+#ifdef USE_CRASH
+#	include"crash.h"
+#endif
+
+#ifdef Q_OS_WIN
+#	include <qt_windows.h> // for RegDeleteKey
+#endif
+
+using namespace XMPP;
+
+QTranslator *trans;
+QTranslator *qttrans;
+QString curLang = "en";
+QString curLangName = QT_TR_NOOP("language_name");
+
+void setLang(const QString &lang)
+{
+	//printf("changing lang: [%s]\n", lang.latin1());
+	trans->clear();
+	qttrans->clear();
+	if(lang == "en") {
+		curLang = lang;
+		curLangName = "English";
+		return;
+	}
+
+	QStringList dirs;
+	QString subdir = "";
+	dirs += "." + subdir;
+	dirs += g.pathHome + subdir;
+	dirs += g.pathBase + subdir;
+	for(QStringList::Iterator it = dirs.begin(); it != dirs.end(); ++it) {
+		if(!QFile::exists(*it))
+			continue;
+		if(trans->load("psi_" + lang, *it)) {
+			// try to load qt library translation
+			qttrans->load("qt_" + lang, *it);
+			curLang = lang;
+			return;
+		}
+	}
+}
+
+static void folderkeyRemove(QSettings &settings, const QString &d)
+{
+	QStringList entries;
+	QStringList::Iterator it;
+
+	entries = settings.subkeyList(d);
+	for(it = entries.begin(); it != entries.end(); ++it) {
+		QString str = d + "/" + *it;
+		folderkeyRemove(settings, str);
+	}
+
+	entries = settings.entryList(d);
+	for(it = entries.begin(); it != entries.end(); ++it) {
+		QString str = d + "/" + *it;
+		settings.removeEntry(str);
+	}
+	settings.removeEntry(d);
+}
+
+
+PsiMain::PsiMain(QObject *par)
+:QObject(par)
+{
+	pcon = 0;
+
+	// detect available language packs
+	langs.set("en", "English");
+
+	QStringList dirs;
+	QString subdir = "";
+	dirs += "." + subdir;
+	dirs += g.pathHome + subdir;
+	dirs += g.pathBase + subdir;
+
+	for(QStringList::Iterator it = dirs.begin(); it != dirs.end(); ++it) {
+		if(!QFile::exists(*it))
+			continue;
+		QDir d(*it);
+		QStringList entries = d.entryList();
+		for(QStringList::Iterator it2 = entries.begin(); it2 != entries.end(); ++it2) {
+			if(*it2 == "." || *it2 == "..")
+				continue;
+
+			QString str = *it2;
+			// verify that it is a language file
+			if(str.left(4) != "psi_")
+				continue;
+			int n = str.find('.', 4);
+			if(n == -1)
+				continue;
+			if(str.mid(n) != ".qm")
+				continue;
+			QString lang = str.mid(4, n-4);
+
+			//printf("found [%s], lang=[%s]\n", str.latin1(), lang.latin1());
+
+			// get the language_name
+			QString name = QString("[") + str + "]";
+			QTranslator t(0);
+			if(!t.load(str, *it))
+				continue;
+
+			if(t.contains("@default", "language_name", 0)) {
+				QString s = t.findMessage("@default", "language_name", 0).translation();
+				if(!s.isEmpty())
+					name = s;
+			}
+
+			langs.set(lang, name);
+		}
+	}
+
+	// load simple registry settings
+	QSettings *s = new QSettings;
+	s->setPath("psi.affinix.com", "Psi", QSettings::User); // make Psi load all prefs from HKCU on Windows systems
+	s->insertSearchPath(QSettings::Windows, "/Affinix");
+	s->insertSearchPath(QSettings::Unix, g.pathHome);
+	lastProfile = s->readEntry("/psi/lastProfile");
+	lastLang = s->readEntry("/psi/lastLang");
+	autoOpen = s->readBoolEntry("/psi/autoOpen", FALSE);
+	delete s;
+
+	if(lastLang.isEmpty()) {
+		lastLang = QTextCodec::locale();
+		//printf("guessing locale: [%s]\n", lastLang.latin1());
+	}
+
+	setLang(lastLang);
+
+	if(autoOpen && !lastProfile.isEmpty() && profileExists(lastProfile)) {
+		// Auto-open the last profile
+		activeProfile = lastProfile;
+		QTimer::singleShot(0, this, SLOT(sessionStart()));
+	}
+	else {
+		if (!lastProfile.isEmpty() && !getProfilesList().isEmpty()) {
+			// Select a profile
+			QTimer::singleShot(0, this, SLOT(chooseProfile()));
+		}
+		else {
+			// Create & open the default profile
+			if (!profileExists("default") && !profileNew("default")) {
+				QMessageBox::critical(0, tr("Error"), 
+					tr("There was an error creating the default profile."));
+				QTimer::singleShot(0, this, SLOT(bail()));
+			}
+			else {
+				lastProfile = activeProfile = "default";
+				autoOpen = true;
+				QTimer::singleShot(0, this, SLOT(sessionStart()));
+			}
+		}
+	}
+}
+
+PsiMain::~PsiMain()
+{
+	delete pcon;
+
+#ifdef Q_OS_WIN
+	// remove Psi's settings from HKLM
+	QSettings *rs = new QSettings;
+	rs->setPath("Affinix", "psi", QSettings::Global);
+	rs->removeEntry("/lastProfile");
+	rs->removeEntry("/lastLang");
+	rs->removeEntry("/autoOpen");
+
+	QString affinixKey = "Software\\Affinix";
+#ifdef Q_OS_TEMP
+	RegDeleteKeyW(HKEY_LOCAL_MACHINE, affinixKey.ucs2());
+#else
+	RegDeleteKeyA(HKEY_LOCAL_MACHINE, affinixKey.latin1());
+#endif
+	delete rs;
+#endif
+
+	QSettings *s = new QSettings;
+	s->setPath("psi.affinix.com", "Psi", QSettings::User); // make Psi load all prefs from HKCU on Windows systems
+	s->insertSearchPath(QSettings::Windows, "/Affinix");
+	s->insertSearchPath(QSettings::Unix, g.pathHome);
+	s->writeEntry("/psi/lastProfile", lastProfile);
+	s->writeEntry("/psi/lastLang", lastLang);
+	s->writeEntry("/psi/autoOpen", autoOpen);
+	delete s;
+}
+
+static bool loadGlobal()
+{
+	// set the paths
+	g.pathBase = getResourcesDir();
+	char *p = getenv("PSIDATADIR");
+	if(p)
+		g.pathHome = p;
+	else
+		g.pathHome = getHomeDir();
+	g.pathProfiles = g.pathHome + "/profiles";
+
+	QDir d(g.pathProfiles);
+	if(!d.exists()) {
+		QDir d(g.pathHome);
+		d.mkdir("profiles");
+	}
+
+	return TRUE;
+}
+
+void PsiMain::chooseProfile()
+{
+	if(pcon) {
+		delete pcon;
+		pcon = 0;
+	}
+
+	QString str = "";
+
+	// dirty, dirty, dirty hack
+	is = new PsiIconset;
+	is->loadSystem();
+
+	while(1) {
+		ProfileOpenDlg *w = new ProfileOpenDlg(lastProfile, langs, curLang);
+		w->ck_auto->setChecked(autoOpen);
+		int r = w->exec();
+		// lang change
+		if(r == 10) {
+			QString newLang = w->newLang;
+			delete w;
+			setLang(newLang);
+			lastLang = curLang;
+			continue;
+		}
+		else {
+			if(r == QDialog::Accepted)
+				str = w->cb_profile->currentText();
+			autoOpen = w->ck_auto->isChecked();
+			delete w;
+			break;
+		}
+	}
+
+	delete is;
+
+	if(str.isEmpty()) {
+		quit();
+		return;
+	}
+
+	// only set lastProfile if the user opened it
+	lastProfile = str;
+
+	activeProfile = str;
+	sessionStart();
+}
+
+void PsiMain::sessionStart()
+{
+	// get a PsiCon
+	pcon = new PsiCon();
+	if(!pcon->init()) {
+		delete pcon;
+		pcon = 0;
+		quit();
+		return;
+	}
+	connect(pcon, SIGNAL(quit(int)), SLOT(sessionQuit(int)));
+}
+
+void PsiMain::sessionQuit(int x)
+{
+	if(x == PsiCon::QuitProgram) {
+		QTimer::singleShot(0, this, SLOT(bail()));
+	}
+	else if(x == PsiCon::QuitProfile) {
+		QTimer::singleShot(0, this, SLOT(chooseProfile()));
+	}
+}
+
+void PsiMain::bail()
+{
+	if(pcon) {
+		delete pcon;
+		pcon = 0;
+	}
+	quit();
+}
+
+int main(int argc, char *argv[])
+{
+	// add library paths before creating QApplication
+	if (!loadGlobal())
+		return 1;
+
+	QApplication::addLibraryPath(g.pathHome);
+	QApplication::addLibraryPath(getResourcesDir());
+
+	PsiApplication *app = new PsiApplication(argc, argv);
+
+#ifdef USE_CRASH
+	int useCrash = true;
+	int i;
+	for(i = 1; i < argc; ++i) {
+		QString str = argv[i];
+		if ( str == "--nocrash" )
+			useCrash = false;
+	}
+
+	if ( useCrash )
+		Crash::registerSigsegvHandler(argv[0]);
+#endif
+
+	// seed the random number generator
+	srand(time(NULL));
+
+	//dtcp_port = 8000;
+	for(int n = 1; n < argc; ++n) {
+		QString str = argv[n];
+		QString var, val;
+		int x = str.find('=');
+		if(x == -1) {
+			var = str;
+			val = "";
+		}
+		else {
+			var = str.mid(0,x);
+			val = str.mid(x+1);
+		}
+
+		if(var == "--no-gpg")
+			use_gpg = false;
+		else if(var == "--no-gpg-agent")
+			no_gpg_agent = true;
+		//else if(var == "--linktest")
+		//	link_test = true;
+	}
+
+	//if(link_test)
+	//	printf("Link test enabled\n");
+
+	// silly winsock workaround
+	//QSocketDevice *d = new QSocketDevice;
+	//delete d;
+
+	// japanese
+	trans = new QTranslator(0);
+	app->installTranslator(trans);
+
+	qttrans = new QTranslator(0);
+	app->installTranslator(qttrans);
+
+	// need SHA1 for Iconset sound
+	if(!QCA::isSupported(QCA::CAP_SHA1))
+		QCA::insertProvider(XMPP::createProviderHash());
+
+	PsiMain *psi = new PsiMain;
+	QObject::connect(psi, SIGNAL(quit()), app, SLOT(quit()));
+	app->exec();
+	delete psi;
+
+	app->removeTranslator(trans);
+	delete trans;
+	trans = 0;
+	app->removeTranslator(qttrans);
+	delete qttrans;
+	qttrans = 0;
+
+	delete app;
+
+	QCA::unloadAllPlugins();
+
+	return 0;
+}
Index: psi/tags/0.10-os2-b1/src/main.h
===================================================================
--- psi/tags/0.10-os2-b1/src/main.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/main.h	(revision 38)
@@ -0,0 +1,55 @@
+/*
+ * main.h - initialization and profile/settings handling
+ * Copyright (C) 2001-2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include<qobject.h>
+#include<qstring.h>
+#include"varlist.h"
+#include"psicon.h"
+
+
+class PsiMain : public QObject
+{
+	Q_OBJECT
+public:
+	PsiMain(QObject *parent=0);
+	~PsiMain();
+
+signals:
+	void quit();
+
+private slots:
+	void chooseProfile();
+	void sessionStart();
+	void sessionQuit(int);
+	void bail();
+
+private:
+	VarList langs;
+
+	QString lastProfile, lastLang;
+	bool autoOpen;
+
+	PsiCon *pcon;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/mainwin.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/mainwin.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/mainwin.cpp	(revision 38)
@@ -0,0 +1,1175 @@
+/*
+ * mainwin.cpp - the main window.  holds contactlist and buttons.
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"mainwin.h"
+
+#include<qmessagebox.h>
+#include<qiconset.h>
+#include<qtooltip.h>
+#include<qstyle.h>
+#include<qapplication.h>
+#include<qptrlist.h>
+#include<qtimer.h>
+#include<qobjectlist.h>
+#include<qpainter.h>
+#include<qsignalmapper.h>
+#include<qstatusbar.h>
+#include<qmenubar.h>
+#include"im.h"
+#include"common.h"
+#include"showtextdlg.h"
+#include"psicon.h"
+#include"psiaccount.h"
+#include"psitoolbar.h"
+#include"ui_about.h"
+#include"ui_tip.h"
+#include"fancylabel.h"
+#include"psitoolbar.h"
+#include"psipopup.h"
+
+#include"mainwin_p.h"
+
+using namespace XMPP;
+
+// deletes submenus in a popupmenu
+void qpopupmenuclear(QPopupMenu *p)
+{
+	while(p->count()) {
+		QMenuItem *item = p->findItem(p->idAt(0));
+		QPopupMenu *popup = item->popup();
+		p->removeItemAt(0);
+
+		if(popup)
+			delete popup;
+	}
+}
+
+//----------------------------------------------------------------------------
+// MainWin::Private
+//----------------------------------------------------------------------------
+
+class MainWin::Private
+{
+public:
+	Private(PsiCon *, MainWin *);
+	~Private();
+
+	QVBoxLayout *vb_main;
+	bool onTop, asTool;
+	QPopupMenu *mainMenu, *statusMenu, *optionsMenu, *toolsMenu;
+	int sbState;
+	QString nickname;
+	MTray *tray;
+	QPopupMenu *trayMenu;
+	QString statusTip;
+
+	PopupAction *optionsButton, *statusButton;
+	IconActionGroup *statusGroup;
+	EventNotifierAction *eventNotifier;
+	PsiCon *psi;
+	MainWin *mainWin;
+
+	QSignalMapper *statusMapper;
+
+	Icon *nextAnim;
+	int nextAmount;
+
+	QMap<QAction *, int> statusActions;
+
+	int lastStatus;
+	QMenuBar *gm;
+
+	QString infoString;
+
+	void registerActions();
+	IconAction *getAction( QString name );
+};
+
+MainWin::Private::Private(PsiCon *_psi, MainWin *_mainWin)
+{
+	psi = _psi;
+	mainWin = _mainWin;
+
+	statusGroup   = (IconActionGroup *)getAction("status_all");
+	eventNotifier = (EventNotifierAction *)getAction("event_notifier");
+
+	optionsButton = (PopupAction *)getAction("button_options");
+	statusButton  = (PopupAction *)getAction("button_status");
+
+	statusMapper = new QSignalMapper(mainWin, "statusMapper");
+	mainWin->connect(statusMapper, SIGNAL(mapped(int)), mainWin, SLOT(activatedStatusAction(int)));
+}
+
+MainWin::Private::~Private()
+{
+}
+
+void MainWin::Private::registerActions()
+{
+	struct {
+		const char *name;
+		int id;
+	} statuslist[] = {
+		{ "status_chat",      STATUS_CHAT      },
+		{ "status_online",    STATUS_ONLINE    },
+		{ "status_away",      STATUS_AWAY      },
+		{ "status_xa",        STATUS_XA        },
+		{ "status_dnd",       STATUS_DND       },
+		{ "status_invisible", STATUS_INVISIBLE },
+		{ "status_offline",   STATUS_OFFLINE   },
+		{ "", 0 }
+	};
+
+	int i;
+	QString aName;
+	for ( i = 0; !(aName = QString(statuslist[i].name)).isEmpty(); i++ ) {
+		IconAction *action = getAction( aName );
+		connect (action, SIGNAL(activated()), statusMapper, SLOT(map()));
+
+		statusMapper->setMapping(action, statuslist[i].id);
+		statusActions[action] = statuslist[i].id;
+	}
+
+	// register all actions
+	PsiActionList::ActionsType type = PsiActionList::ActionsType( PsiActionList::Actions_MainWin | PsiActionList::Actions_Common );
+	ActionList actions = psi->actionList()->suitableActions( type );
+	QStringList names = actions.actions();
+	QStringList::Iterator it = names.begin();
+	for ( ; it != names.end(); ++it ) {
+		IconAction *action = actions.action( *it );
+		if ( action )
+			mainWin->registerAction( action );
+	}
+}
+
+IconAction *MainWin::Private::getAction( QString name )
+{
+	PsiActionList::ActionsType type = PsiActionList::ActionsType( PsiActionList::Actions_MainWin | PsiActionList::Actions_Common );
+	ActionList actions = psi->actionList()->suitableActions( type );
+	IconAction *action = actions.action( name );
+
+	if ( !action )
+		qWarning("MainWin::Private::getAction(): action %s not found!", name.latin1());
+	//else
+	//	mainWin->registerAction( action );
+
+	return action;
+}
+
+//----------------------------------------------------------------------------
+// MainWin
+//----------------------------------------------------------------------------
+
+//#ifdef Q_WS_X11
+//#define TOOLW_FLAGS WStyle_Customize
+//#else
+#define TOOLW_FLAGS 0
+//#endif
+
+MainWin::MainWin(bool _onTop, bool _asTool, PsiCon *psi, const char *name)
+:AdvancedWidget<QMainWindow>(0, name, 0 | (_onTop ? WStyle_StaysOnTop: 0) | (_asTool ? WStyle_Tool | TOOLW_FLAGS : 0))
+{
+	d = new Private(psi, this);
+
+	setIcon(is->status(STATUS_OFFLINE));
+
+	d->onTop = _onTop;
+	d->asTool = _asTool;
+
+	// sbState:
+	//   -1 : connect
+	// >= 0 : STATUS_*
+	d->sbState = STATUS_OFFLINE;
+	d->lastStatus = -2;
+
+	d->nextAmount = 0;
+	d->nextAnim = 0;
+	d->tray = 0;
+	d->trayMenu = 0;
+	d->statusTip = "";
+	d->infoString = "";
+	d->nickname = "";
+
+	QWidget *center = new QWidget (this, "Central widget");
+	setCentralWidget ( center );
+
+	d->vb_main = new QVBoxLayout(center, 2);
+	cvlist = new ContactView(center);
+	d->vb_main->addWidget(cvlist, 1);
+#ifdef Q_WS_MAC
+	// If the space isn't there, MacOS draws an empty vertical scrollbar :(
+	d->vb_main->addSpacing(1);
+#endif
+
+	d->statusMenu = new QPopupMenu(this);
+	d->optionsMenu = new QPopupMenu(this);
+
+	buildStatusMenu();
+	buildOptionsMenu();
+	connect(d->statusMenu, SIGNAL(aboutToShow()), SLOT(buildStatusMenu()));
+	connect(d->optionsMenu, SIGNAL(aboutToShow()), SLOT(buildOptionsMenu()));
+
+	d->optionsButton->setPopup( d->optionsMenu );
+	d->statusButton->setPopup( d->statusMenu );
+
+	X11WM_CLASS("main");
+
+	connect(d->psi, SIGNAL(accountCountChanged()), SLOT(numAccountsChanged()));
+	numAccountsChanged();
+
+	updateCaption();
+
+	d->registerActions();
+	buildToolbars();
+
+	decorateButton(STATUS_OFFLINE);
+
+	// show tip of the day
+	if ( option.showTips )
+		actTipActivated();
+
+#ifdef Q_WS_MAC
+	// Create a global menubar
+	d->gm = new QMenuBar(0);
+#else
+	// Create a menu for the current window
+	d->gm = new QMenuBar(this);
+#endif
+
+	// Mac-only menus
+#ifdef Q_WS_MAC
+	QPopupMenu *mainMenu = new QPopupMenu(this);
+	d->gm->insertItem(tr("Menu"), mainMenu);
+	mainMenu->insertItem(tr("Preferences"), this, SIGNAL(doOptions()));
+	mainMenu->insertItem(tr("Quit"), this, SLOT(try2tryCloseProgram()));
+	d->getAction("help_about")->addTo(mainMenu);
+	d->getAction("help_about_qt")->addTo(mainMenu);
+
+	d->mainMenu = new QPopupMenu(this);
+	d->gm->insertItem(tr("General"), d->mainMenu);
+	connect(d->mainMenu, SIGNAL(aboutToShow()), SLOT(buildMainMenu()));
+#else
+	d->gm->insertItem(tr("General"), d->optionsMenu);
+#endif
+
+	d->gm->insertItem(tr("Status"), d->statusMenu);
+
+	QPopupMenu *viewMenu = new QPopupMenu(this);
+	d->gm->insertItem(tr("View"), viewMenu);
+	d->getAction("show_offline")->addTo(viewMenu);
+	d->getAction("show_away")->addTo(viewMenu);
+	d->getAction("show_hidden")->addTo(viewMenu);
+	d->getAction("show_agents")->addTo(viewMenu);
+	d->getAction("show_self")->addTo(viewMenu);
+
+	// Mac-only menus
+#ifdef Q_WS_MAC
+	d->toolsMenu = new QPopupMenu(this);
+	d->gm->insertItem(tr("Tools"), d->toolsMenu);
+	connect(d->toolsMenu, SIGNAL(aboutToShow()), SLOT(buildToolsMenu()));
+
+	QPopupMenu *helpMenu = new QPopupMenu(this);
+	d->gm->insertItem(tr("Help"), helpMenu);
+	d->getAction("help_readme")->addTo (helpMenu);
+	d->getAction("help_tip")->addTo (helpMenu);
+	helpMenu->insertSeparator();
+	d->getAction("help_online_help")->addTo (helpMenu);
+	d->getAction("help_online_wiki")->addTo (helpMenu);
+	d->getAction("help_online_home")->addTo (helpMenu);
+	d->getAction("help_report_bug")->addTo (helpMenu);
+
+	d->gm->show();
+#else
+	if (option.hideMenubar) 
+		d->gm->hide();
+	else 
+		d->gm->show();
+#endif
+	
+#if QT_VERSION >= 0x030300
+	setWindowOpacity(double(option.rosterOpacity)/100);
+#endif
+
+	connect(qApp, SIGNAL(dockActivated()), SLOT(dockActivated()));
+}
+	
+
+MainWin::~MainWin()
+{
+	PsiPopup::deleteAll();
+
+	if (d->gm)
+		delete d->gm;
+
+	if(d->tray) {
+		delete d->tray;
+		d->tray = 0;
+		delete d->trayMenu;
+		d->trayMenu = 0;
+	}
+
+	//saveToolbarsPositions();
+	// need to find some workaround to case, when you're logging off. in that case
+	// toobars are all disabled, and when you start psi again you need to enable
+	// your toolbars
+
+	delete d;
+}
+
+void MainWin::registerAction( IconAction *action )
+{
+	char activated[] = SIGNAL( activated() );
+	char toggled[]   = SIGNAL( toggled(bool) );
+	char setOn[]     = SLOT( setOn(bool) );
+
+	struct {
+		const char *name;
+		const char *signal;
+		QObject *receiver;
+		const char *slot;
+	} actionlist[] = {
+		{ "show_offline", toggled, cvlist, SLOT( setShowOffline(bool) ) },
+		{ "show_away",    toggled, cvlist, SLOT( setShowAway(bool) ) },
+		{ "show_hidden",  toggled, cvlist, SLOT( setShowHidden(bool) ) },
+		{ "show_agents",  toggled, cvlist, SLOT( setShowAgents(bool) ) },
+		{ "show_self",    toggled, cvlist, SLOT( setShowSelf(bool) ) },
+
+		{ "button_options", activated, this, SIGNAL( doOptions() ) },
+
+		{ "menu_disco",       SIGNAL( activated(PsiAccount *, int) ), this, SLOT( activatedAccOption(PsiAccount*, int) ) },
+		{ "menu_add_contact", SIGNAL( activated(PsiAccount *, int) ), this, SLOT( activatedAccOption(PsiAccount*, int) ) },
+		{ "menu_xml_console", SIGNAL( activated(PsiAccount *, int) ), this, SLOT( activatedAccOption(PsiAccount*, int) ) },
+
+		{ "menu_new_message",    activated, this, SIGNAL( blankMessage() ) },
+		{ "menu_join_groupchat", activated, this, SIGNAL( doGroupChat() ) },
+		{ "menu_account_setup",  activated, this, SIGNAL( doManageAccounts() ) },
+		{ "menu_options",        activated, this, SIGNAL( doOptions() ) },
+		{ "menu_file_transfer",  activated, this, SIGNAL( doFileTransDlg() ) },
+		{ "menu_toolbars",       activated, this, SIGNAL( doToolbars() ) },
+		{ "menu_change_profile", activated, this, SIGNAL( changeProfile() ) },
+		{ "menu_quit",           activated, this, SLOT( try2tryCloseProgram() ) },
+		{ "menu_play_sounds",    toggled,   this, SLOT( actPlaySoundsActivated(bool) ) },
+
+		{ "event_notifier", SIGNAL( clicked(int) ), this, SLOT( statusClicked(int) ) },
+		{ "event_notifier", activated, this, SLOT( doRecvNextEvent() ) },
+
+		{ "help_readme",      activated, this, SLOT( actReadmeActivated() ) },
+		{ "help_tip",         activated, this, SLOT( actTipActivated() ) },
+		{ "help_online_help", activated, this, SLOT( actOnlineHelpActivated() ) },
+		{ "help_online_wiki", activated, this, SLOT( actOnlineWikiActivated() ) },
+		{ "help_online_home", activated, this, SLOT( actOnlineHomeActivated() ) },
+		{ "help_report_bug",  activated, this, SLOT( actBugReportActivated() ) },
+		{ "help_about",       activated, this, SLOT( actAboutActivated() ) },
+		{ "help_about_qt",    activated, this, SLOT( actAboutQtActivated() ) },
+
+		{ "", 0, 0, 0 }
+	};
+
+	int i;
+	QString aName;
+	for ( i = 0; !(aName = QString(actionlist[i].name)).isEmpty(); i++ ) {
+		if ( aName == action->name() ) {
+			disconnect( action, actionlist[i].signal, actionlist[i].receiver, actionlist[i].slot ); // for safety
+			connect( action, actionlist[i].signal, actionlist[i].receiver, actionlist[i].slot );
+
+			// special cases
+			if ( aName == "menu_play_sounds" )
+				action->setOn( useSound );
+			//else if ( aName == "foobar" )
+			//	;
+		}
+	}
+
+	struct {
+		const char *name;
+		QObject *sender;
+		const char *signal;
+		const char *slot;
+	} reverseactionlist[] = {
+		{ "show_away",    cvlist, SIGNAL( showAway(bool) ), setOn },
+		{ "show_hidden",  cvlist, SIGNAL( showHidden(bool) ), setOn },
+		{ "show_offline", cvlist, SIGNAL( showOffline(bool) ), setOn },
+		{ "show_self",    cvlist, SIGNAL( showSelf(bool) ), setOn },
+		{ "show_agents",  cvlist, SIGNAL( showAgents(bool) ), setOn },
+		{ "", 0, 0, 0 }
+	};
+
+	for ( i = 0; !(aName = QString(reverseactionlist[i].name)).isEmpty(); i++ ) {
+		if ( aName == action->name() ) {
+			disconnect( reverseactionlist[i].sender, reverseactionlist[i].signal, action, reverseactionlist[i].slot ); // for safety
+			connect( reverseactionlist[i].sender, reverseactionlist[i].signal, action, reverseactionlist[i].slot );
+
+			action->setOn( true );
+		}
+	}
+}
+
+PsiCon *MainWin::psiCon() const
+{
+	return d->psi;
+}
+
+void MainWin::setWindowOpts(bool _onTop, bool _asTool)
+{
+	if(_onTop == d->onTop && _asTool == d->asTool)
+		return;
+
+	d->onTop = _onTop;
+	d->asTool = _asTool;
+
+	WFlags flags = 0;
+	if(d->onTop)
+		flags |= WStyle_StaysOnTop;
+	if(d->asTool)
+		flags |= WStyle_Tool | TOOLW_FLAGS;
+
+	QPoint p = pos();
+	reparent(parentWidget(), flags, p, FALSE);
+	move(p);
+	show();
+}
+
+void MainWin::setUseDock(bool use)
+{
+#ifdef Q_WS_PM
+	/// @todo (dmik) disable the docklet until the xCenter plugin is implemented
+	//  (this ifdef is necessary in order to share the same config between
+	//  different platforms where the Use Docklet option may be set to true.
+	return;
+#else	
+	if(use == false || (d->tray && option.isWMDock != d->tray->isWMDock())) {
+		if(d->tray) {
+			delete d->tray;
+			d->tray = 0;
+			delete d->trayMenu;
+			d->trayMenu = 0;
+		}
+
+		if (use == false)
+			return;
+	}
+
+	if(d->tray)
+		return;
+
+	d->trayMenu = new QPopupMenu;
+	connect(d->trayMenu, SIGNAL(aboutToShow()), SLOT(buildTrayMenu()));
+
+	d->tray = new MTray("Psi", d->trayMenu);
+	d->tray->setIcon( is->statusPtr( STATUS_OFFLINE ));
+	d->tray->setToolTip(PROG_NAME);
+	connect(d->tray, SIGNAL(clicked(const QPoint &, int)), SLOT(trayClicked(const QPoint &, int)));
+	connect(d->tray, SIGNAL(doubleClicked(const QPoint &)), SLOT(trayDoubleClicked()));
+	connect(d->tray, SIGNAL(closed()), SLOT(dockActivated()));
+	connect(qApp, SIGNAL(trayOwnerDied()), SLOT(dockActivated()));
+
+	updateReadNext(d->nextAnim, d->nextAmount);
+
+	d->tray->show();
+#endif	
+}
+
+void MainWin::setInfo(const QString &str)
+{
+	d->infoString = str;
+
+	if(d->nextAmount == 0)
+		d->eventNotifier->setText(d->infoString);
+}
+
+void MainWin::buildStatusMenu()
+{
+	//statusMenu->clear();
+	qpopupmenuclear(d->statusMenu);
+
+	d->statusGroup->addTo(d->statusMenu);
+}
+
+void MainWin::activatedStatusAction(int id)
+{
+	QObjectList *l = d->statusGroup->queryList( "IconAction" );
+	QObjectListIt it( *l );
+	QObject *obj;
+	for ( ; (obj = it.current()); ++it) {
+		IconAction *action = (IconAction *)obj;
+		action->setOn ( d->statusActions[action] == id );
+	}
+	delete l;
+
+	statusChanged(id);
+}
+
+void MainWin::buildToolbars()
+{
+	bool dockBottom = false;
+
+	while ( option.toolbars.count() < toolbars.count() && toolbars.count() ) {
+		PsiToolBar *tb = toolbars.last();
+		toolbars.removeLast();
+		delete tb;
+	}
+
+	uint i;
+	for (i = 0; i < option.toolbars["mainWin"].count(); i++) {
+		PsiToolBar *tb = 0;
+		if ( i < toolbars.count() )
+			tb = toolbars.at(i);
+
+		Options::ToolbarPrefs &tbPref = option.toolbars["mainWin"][i];
+		if ( tb && !tbPref.dirty )
+			continue;
+
+		if ( tb )
+			delete tb;
+
+		tb = new PsiToolBar (tbPref.name, this, this);
+		moveDockWindow ( tb, tbPref.dock, tbPref.nl, tbPref.index, tbPref. extraOffset );
+
+		tb->setGroup( "mainWin", i );
+		tb->setPsiCon( d->psi );
+		tb->setType( PsiActionList::Actions_MainWin );
+		//connect( tb, SIGNAL( registerAction( IconAction * ) ), SLOT( registerAction( IconAction * ) ) );
+		tb->initialize( tbPref, false );
+
+		// MacOS stuff
+		dockBottom = dockBottom || (tbPref.dock == Qt::DockBottom && tbPref.on);
+
+		if ( i < toolbars.count() )
+			toolbars.remove(i);
+		toolbars.insert(i, tb);
+	}
+
+#ifdef Q_WS_MAC
+	if (dockBottom) {
+		statusBar()->show();
+	}
+	else {
+		statusBar()->hide();
+	}
+#endif
+}
+
+void MainWin::saveToolbarsPositions()
+{
+	for (uint i = 0; i < toolbars.count(); i++) {
+		Options::ToolbarPrefs &tbPref = option.toolbars["mainWin"][i];
+		getLocation ( toolbars.at(i), tbPref.dock, tbPref.index, tbPref.nl, tbPref.extraOffset );
+		tbPref.on = toolbars.at(i)->isVisible();
+	}
+}
+
+bool MainWin::showDockMenu(const QPoint &)
+{
+	return false;
+}
+
+void MainWin::buildOptionsMenu()
+{
+	d->optionsMenu->clear();
+
+	// help menu
+	QPopupMenu *helpMenu = new QPopupMenu(d->optionsMenu);
+
+	QStringList actionNames;
+	actionNames << "help_readme";
+	actionNames << "help_tip";
+	actionNames << "separator";
+	actionNames << "help_online_help";
+	actionNames << "help_online_wiki";
+	actionNames << "help_online_home";
+	actionNames << "help_report_bug";
+	actionNames << "separator";
+	actionNames << "help_about";
+	actionNames << "help_about_qt";
+
+	QStringList::Iterator it;
+	IconAction *action;
+	for ( it = actionNames.begin(); it != actionNames.end(); ++it )
+		if ( (action = d->getAction(*it)) )
+			action->addTo( helpMenu );
+
+	buildGeneralMenu( d->optionsMenu );
+
+	d->optionsMenu->insertSeparator();
+	d->optionsMenu->insertItem(IconsetFactory::icon("psi/help"), tr("&Help"), helpMenu);
+	d->getAction("menu_quit")->addTo( d->optionsMenu );
+
+}
+
+void MainWin::buildMainMenu()
+{
+	d->mainMenu->clear();
+
+	QStringList actionNames;
+
+	// options menu
+	if(!option.lockdown.roster)
+		actionNames << "menu_add_contact";
+	actionNames << "menu_new_message";
+	if(!option.lockdown.services)
+		actionNames << "menu_disco";
+	actionNames << "menu_join_groupchat";
+	actionNames << "";
+	if(!option.lockdown.accounts)
+		actionNames << "menu_account_setup";
+	if(!option.lockdown.profiles)
+		actionNames << "menu_change_profile";
+	actionNames << "menu_toolbars";
+	actionNames << "menu_play_sounds";
+
+	QStringList::Iterator it;
+	IconAction *action;
+	for ( it = actionNames.begin(); it != actionNames.end(); ++it ) {
+		if ((*it).isEmpty())
+			d->mainMenu->insertSeparator();
+		else if ( (action = d->getAction(*it)) )
+			action->addTo( d->mainMenu );
+	}
+}
+
+void MainWin::buildToolsMenu()
+{
+	IconAction* action;
+
+	d->toolsMenu->clear();
+	if ( (action = d->getAction("menu_file_transfer")) )
+		action->addTo(d->toolsMenu);
+	d->toolsMenu->insertSeparator();
+	if ( (action = d->getAction("menu_xml_console")) )
+		action->addTo(d->toolsMenu);
+}
+	
+void MainWin::buildGeneralMenu(QPopupMenu *menu)
+{
+	menu->clear();
+
+	QStringList actionNames;
+
+	// options menu
+	if(!option.lockdown.roster)
+		actionNames << "menu_add_contact";
+	actionNames << "menu_new_message";
+	if(!option.lockdown.services)
+		actionNames << "menu_disco";
+	actionNames << "menu_join_groupchat";
+	if(!option.lockdown.accounts)
+		actionNames << "menu_account_setup";
+	if(!option.lockdown.options)
+		actionNames << "menu_options";
+	actionNames << "menu_file_transfer"; // TODO: probably we want to lock down filetransfer, right?
+	if(!option.lockdown.profiles)
+		actionNames << "menu_change_profile";
+	actionNames << "menu_toolbars";
+	actionNames << "menu_play_sounds";
+
+	QStringList::Iterator it;
+	IconAction *action;
+	for ( it = actionNames.begin(); it != actionNames.end(); ++it )
+		if ( (action = d->getAction(*it)) )
+			action->addTo( menu );
+}
+
+// WTF??
+void MainWin::testme()
+{
+	d->optionsMenu->setItemEnabled(10, false);
+}
+
+void MainWin::actReadmeActivated ()
+{
+#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
+	ShowTextDlg *w = new ShowTextDlg(g.pathBase + "/readme.txt");
+#else
+	ShowTextDlg *w = new ShowTextDlg(g.pathBase + "/README");
+#endif
+	w->setCaption(CAP(tr("ReadMe")));
+	w->show();
+}
+
+void MainWin::actOnlineHelpActivated ()
+{
+	openURL("http://psi-im.org/wiki/User_Guide");
+}
+
+void MainWin::actOnlineWikiActivated ()
+{
+	openURL("http://psi-im.org/wiki");
+}
+
+void MainWin::actOnlineHomeActivated ()
+{
+	openURL("http://psi-im.org");
+}
+
+void MainWin::actBugReportActivated ()
+{
+	openURL("http://psi-im.org/forum/forum/2");
+}
+
+void MainWin::actAboutActivated ()
+{
+	AboutDlg *about = new AboutDlg (this, "AboutDlg", false, WDestructiveClose);
+	about->show();
+}
+
+void MainWin::actTipActivated ()
+{
+	TipUI *tip = new TipUI (this, "TipDlg", false, WDestructiveClose);
+	tip->show();
+}
+
+void MainWin::actAboutQtActivated ()
+{
+	QMessageBox::aboutQt(this);
+}
+
+void MainWin::actPlaySoundsActivated (bool state)
+{
+	useSound = state;
+}
+
+void MainWin::activatedAccOption(PsiAccount *pa, int x)
+{
+	if(x == 0)
+		pa->openAddUserDlg();
+	else if(x == 2)
+		pa->showXmlConsole();
+	else if(x == 3)
+		pa->doDisco();
+}
+
+void MainWin::buildTrayMenu()
+{
+	if(!d->trayMenu)
+		return;
+	d->trayMenu->clear();
+
+	if(d->nextAmount > 0) {
+		d->trayMenu->insertItem(tr("Receive next event"), this, SLOT(doRecvNextEvent()));
+		d->trayMenu->insertSeparator();
+	}
+
+	if(isHidden())
+		d->trayMenu->insertItem(tr("Un&hide"), this, SLOT(trayShow()));
+	else
+		d->trayMenu->insertItem(tr("&Hide"), this, SLOT(trayHide()));
+	d->optionsButton->addTo(d->trayMenu);
+	d->statusButton->addTo(d->trayMenu);
+	d->trayMenu->insertSeparator();
+	// TODO!
+	d->getAction("menu_quit")->addTo(d->trayMenu);
+}
+
+void MainWin::setTrayToolTip(int status)
+{
+	if (!d->tray)
+		return;
+	d->tray->setToolTip(QString("Psi - " + status2txt(status)));
+}
+
+void MainWin::decorateButton(int status)
+{
+	// update the 'change status' buttons
+	QObjectList *l = d->statusGroup->queryList( "IconAction" );
+	QObjectListIt it( *l );
+	QObject *obj;
+	for ( ; (obj = it.current()); ++it) {
+		IconAction *action = (IconAction *)obj;
+		action->setOn ( d->statusActions[action] == status );
+	}
+	delete l;
+
+	if(d->lastStatus == status)
+		return;
+	d->lastStatus = status;
+
+	QIconSet icon;
+	icon.setPixmap(is->status(status), QIconSet::Small);
+
+	if(status == -1) {
+		d->statusButton->setText(tr("Connecting"));
+		if (option.alertStyle != 0)
+			d->statusButton->setAlert(IconsetFactory::iconPtr("psi/connect"));
+		else
+			d->statusButton->setIcon(is->statusPtr(STATUS_OFFLINE));
+
+		setIcon(is->status(STATUS_OFFLINE));
+	}
+	else {
+		d->statusButton->setText(status2txt(status));
+		d->statusButton->setIcon(is->statusPtr(status));
+
+		setIcon(is->status(status));
+	}
+
+	updateTray();
+}
+
+bool MainWin::askQuit()
+{
+	return TRUE;
+}
+
+void MainWin::try2tryCloseProgram()
+{
+	QTimer::singleShot(0, this, SLOT(tryCloseProgram()));
+}
+
+void MainWin::tryCloseProgram()
+{
+	if(askQuit())
+		closeProgram();
+}
+
+void MainWin::closeEvent(QCloseEvent *e)
+{
+#ifdef Q_WS_MAC
+	trayHide();
+	e->accept();
+#else
+	if(d->tray) {
+		trayHide();
+		e->accept();
+		return;
+	}
+
+	if(!askQuit())
+		return;
+
+        closeProgram();
+	e->accept();
+#endif
+}
+
+void MainWin::keyPressEvent(QKeyEvent *e)
+{
+#ifdef Q_WS_MAC
+	bool allowed = true;
+#else
+	bool allowed = d->tray ? true: false;
+#endif
+
+	bool closekey = false;
+	if(e->key() == Key_Escape)
+		closekey = true;
+#ifdef Q_WS_MAC
+	else if(e->key() == Key_W && e->state() & ControlButton)
+		closekey = true;
+#endif
+
+	if(closekey) {
+#ifdef Q_WS_MAC
+		if(allowed) {
+			close();
+			e->accept();
+			return;
+		}
+#else			
+		if(allowed)
+			close();
+		else
+			showMinimized();
+		e->accept();
+		return;
+#endif
+	}
+
+	QWidget::keyPressEvent(e);
+}
+
+void MainWin::updateCaption()
+{
+	QString str = "";
+
+	if(d->nextAmount > 0)
+		str += "* ";
+
+	if(d->nickname.isEmpty())
+		str += PROG_NAME;
+	else
+		str += d->nickname;
+
+	if(str == caption())
+		return;
+
+	setCaption(str);
+}
+
+void MainWin::optionsUpdate()
+{
+	int status = d->lastStatus;
+	d->lastStatus = -2;
+	decorateButton(status);
+
+	if (option.hideMenubar) 
+		d->gm->hide();
+	else 
+		d->gm->show();
+	
+#if QT_VERSION >= 0x030300
+	setWindowOpacity(double(option.rosterOpacity)/100);
+#endif
+	
+	updateTray();
+}
+
+void MainWin::toggleVisible()
+{
+	if(!isHidden())
+		trayHide();
+	else
+		trayShow();
+}
+
+void MainWin::setTrayToolTip(const Status &status)
+{
+	if (!d->tray)
+		return;
+	QString s = "Psi";
+
+ 	QString show = status.show();
+	if(!show.isEmpty()) {
+		show[0] = show[0].upper();
+		s += " - "+show;
+	}
+
+	QString text = status.status();
+	if(!text.isEmpty())
+		s += ": "+text;
+
+	d->tray->setToolTip(s);
+}
+
+void MainWin::trayClicked(const QPoint &, int button)
+{
+	if(option.dockDCstyle)
+		return;
+
+	if(button == MidButton) {
+		doRecvNextEvent();
+		return;
+	}
+
+	if(!isHidden())
+		trayHide();
+	else
+		trayShow();
+}
+
+void MainWin::trayDoubleClicked()
+{
+	if(!option.dockDCstyle)
+		return;
+
+	if(d->nextAmount > 0) {
+		doRecvNextEvent();
+		return;
+	}
+
+
+	if(!isHidden())
+		trayHide();
+	else
+		trayShow();
+}
+
+void MainWin::trayShow()
+{
+	bringToFront(this);
+}
+
+void MainWin::trayHide()
+{
+	emit geomChanged(x(), y(), width(), height());
+	hide();
+}
+
+void MainWin::updateReadNext(Icon *anim, int amount)
+{
+	d->nextAnim = anim;
+	if(anim == 0)
+		d->nextAmount = 0;
+	else
+		d->nextAmount = amount;
+
+	if(d->nextAmount <= 0) {
+		d->eventNotifier->hide();
+		d->eventNotifier->setText("");
+	}
+	else {
+		d->eventNotifier->setText(QString("<b>") + numEventsString(d->nextAmount) + "</b>");
+		d->eventNotifier->show();
+		// make sure it shows
+		//qApp->processEvents();
+	}
+
+	updateTray();
+	updateCaption();
+}
+
+QString MainWin::numEventsString(int x) const
+{
+	QString s;
+	if(x <= 0)
+		s = "";
+	else if(x == 1)
+		s = tr("1 event received");
+	else
+		s = tr("%1 events received").arg(x);
+
+	return s;
+}
+
+void MainWin::updateTray()
+{
+	if(!d->tray)
+		return;
+
+	if ( d->nextAmount > 0 )
+		d->tray->setAlert(d->nextAnim);
+	else if ( d->lastStatus == -1 )
+		d->tray->setAlert(IconsetFactory::iconPtr("psi/connect"));
+	else
+		d->tray->setIcon(is->statusPtr(d->lastStatus));
+}
+
+void MainWin::doRecvNextEvent()
+{
+	recvNextEvent();
+}
+
+void MainWin::statusClicked(int x)
+{
+	if(x == MidButton)
+		recvNextEvent();
+}
+
+void MainWin::numAccountsChanged()
+{
+	bool enabled = d->psi->accountList(TRUE).isEmpty() ? false : true;
+	d->statusButton->setEnabled (enabled);
+}
+
+void MainWin::dockActivated()
+{
+	if(isHidden())
+		show();
+}
+
+
+#ifdef Q_WS_MAC
+void MainWin::setIcon(const QPixmap&)
+{
+}
+#else
+void MainWin::setIcon(const QPixmap& p)
+{
+	QMainWindow::setIcon(p);
+}
+#endif
+
+#if defined(Q_WS_WIN)
+#include<windows.h>
+void MainWin::showNoFocus()
+{
+	clearWState( WState_ForceHide );
+
+	if ( testWState(WState_Visible) ) {
+		SetWindowPos(winId(),HWND_TOPMOST,0,0,0,0, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
+		if(!d->onTop)
+			SetWindowPos(winId(),HWND_NOTOPMOST,0,0,0,0, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);
+		return; // nothing to do
+	}
+
+	if ( isTopLevel() && !testWState( WState_Resized ) )  {
+		// do this before sending the posted resize events. Otherwise
+		// the layout would catch the resize event and may expand the
+		// minimum size.
+		QSize s = sizeHint();
+		QSizePolicy::ExpandData exp;
+#ifndef QT_NO_LAYOUT
+		if ( layout() ) {
+			if ( layout()->hasHeightForWidth() )
+				s.setHeight( layout()->totalHeightForWidth( s.width() ) );
+			exp =  layout()->expanding();
+		} else
+#endif
+		{
+			if ( sizePolicy().hasHeightForWidth() )
+				s.setHeight( heightForWidth( s.width() ) );
+			exp = sizePolicy().expanding();
+		}
+		if ( exp & QSizePolicy::Horizontally )
+			s.setWidth( QMAX( s.width(), 200 ) );
+		if ( exp & QSizePolicy::Vertically )
+			s.setHeight( QMAX( s.height(), 150 ) );
+		QRect screen = QApplication::desktop()->screenGeometry( QApplication::desktop()->screenNumber( pos() ) );
+		s.setWidth( QMIN( s.width(), screen.width()*2/3 ) );
+		s.setHeight( QMIN( s.height(), screen.height()*2/3 ) );
+		if ( !s.isEmpty() )
+			resize( s );
+	}
+
+	QApplication::sendPostedEvents( this, QEvent::Move );
+	QApplication::sendPostedEvents( this, QEvent::Resize );
+
+	setWState( WState_Visible );
+
+	if ( testWFlags(WStyle_Tool) || isPopup() ) {
+		raise();
+	} else if ( testWFlags(WType_TopLevel) ) {
+		while ( QApplication::activePopupWidget() )
+		QApplication::activePopupWidget()->close();
+	}
+
+	if ( !testWState(WState_Polished) )
+		polish();
+
+	if ( children() ) {
+		QObjectListIt it(*children());
+		register QObject *object;
+		QWidget *widget;
+		while ( it ) {				// show all widget children
+			object = it.current();		//   (except popups and other toplevels)
+			++it;
+			if ( object->isWidgetType() ) {
+				widget = (QWidget*)object;
+				if ( !widget->isHidden() && !widget->isTopLevel() )
+				widget->show();
+			}
+		}
+	}
+
+#if defined(QT_ACCESSIBILITY_SUPPORT)
+	QAccessible::updateAccessibility( this, 0, QAccessible::ObjectShow );
+#endif
+
+	SetWindowPos(winId(),HWND_TOP,0,0,0,0, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
+	UpdateWindow(winId());
+}
+
+#else
+
+void MainWin::showNoFocus()
+{
+	bringToFront(this);
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/src/mainwin.h
===================================================================
--- psi/tags/0.10-os2-b1/src/mainwin.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/mainwin.h	(revision 38)
@@ -0,0 +1,151 @@
+/*
+ * mainwin.h - the main window.  holds contactlist and buttons.
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef MAINWIN_H
+#define MAINWIN_H
+
+#include<qmainwindow.h>
+#include<qmap.h>
+#include"im.h"
+#include"contactview.h"
+#include"psitoolbar.h"
+#include"advwidget.h"
+
+class PsiCon;
+class PsiAccount;
+class QIconSet;
+class IconLabel;
+
+class QAction;
+class QActionGroup;
+class IconAction;
+class QLabel;
+class QVBoxLayout;
+class QMenuBar;
+
+class MainWin : public AdvancedWidget<QMainWindow>
+{
+	Q_OBJECT
+public:
+	MainWin(bool onTop, bool asTool, PsiCon *, const char *name=0);
+	~MainWin();
+
+	void setWindowOpts(bool onTop, bool asTool);
+	void setUseDock(bool);
+	void setInfo(const QString &);
+
+	// evil stuff! remove ASAP!!
+	QStringList actionList;
+	QMap<QString, QAction*> actions;
+
+	ContactView *cvlist;
+	QPtrList<PsiToolBar> toolbars;
+
+	void buildToolbars();
+	void saveToolbarsPositions();
+	PsiCon *psiCon() const;
+
+protected:
+	void closeEvent(QCloseEvent *);
+	void keyPressEvent(QKeyEvent *);
+
+signals:
+	void statusChanged(int);
+	void changeProfile();
+	void blankMessage();
+	void closeProgram();
+	void doOptions();
+	void doToolbars();
+	void doManageAccounts();
+	void doGroupChat();
+	void doFileTransDlg();
+	void accountInfo();
+	void recvNextEvent();
+
+	void geomChanged(int x, int y, int w, int h);
+
+private slots:
+	void buildStatusMenu();
+	void buildOptionsMenu();
+	void buildTrayMenu();
+	void buildMainMenu();
+	void buildToolsMenu();
+
+	void setTrayToolTip(int);
+
+	void activatedStatusAction(int);
+
+	void trayClicked(const QPoint &, int);
+	void trayDoubleClicked();
+	void trayShow();
+	void trayHide();
+
+	void doRecvNextEvent();
+	void statusClicked(int);
+	void try2tryCloseProgram();
+	void tryCloseProgram();
+
+	void numAccountsChanged();
+	void testme();
+	void activatedAccOption(PsiAccount *, int);
+
+	void actReadmeActivated ();
+	void actOnlineHelpActivated ();
+	void actOnlineWikiActivated ();
+	void actOnlineHomeActivated ();
+	void actBugReportActivated ();
+	void actAboutActivated ();
+	void actAboutQtActivated ();
+	void actPlaySoundsActivated (bool);
+	void actTipActivated();
+
+	bool showDockMenu(const QPoint &);
+	void dockActivated();
+
+	void registerAction( IconAction * );
+
+public slots:
+	void setIcon(const QPixmap&);
+	void showNoFocus();
+
+	void decorateButton(int);
+	void updateReadNext(Icon *nextAnim, int nextAmount);
+
+	void optionsUpdate();
+	void setTrayToolTip(const Status &);
+
+	void toggleVisible();
+
+private:
+	void buildGeneralMenu(QPopupMenu *);
+	QString numEventsString(int) const;
+
+	bool askQuit();
+
+	void updateCaption();
+	void updateTray();
+
+private:
+	class Private;
+	Private *d;
+	friend class Private;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/mainwin_p.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/mainwin_p.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/mainwin_p.cpp	(revision 38)
@@ -0,0 +1,854 @@
+/*
+ * mainwin_p.cpp - classes used privately by the main window.
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "common.h"
+#include "mainwin_p.h"
+
+#include <qapplication.h>
+#include <qstyle.h>
+#include <qtoolbar.h>
+#include <qtimer.h>
+#include <qsignalmapper.h>
+#include <qobjectlist.h>
+#include <qpixmapcache.h>
+#include "psiaccount.h"
+#include "iconwidget.h"
+#include "alerticon.h"
+
+//----------------------------------------------------------------------------
+// PopupActionButton
+//----------------------------------------------------------------------------
+
+class PopupActionButton : public QPushButton
+{
+	Q_OBJECT
+public:
+	PopupActionButton(QWidget *parent = 0, const char *name = 0);
+	~PopupActionButton();
+
+	void setIcon(Icon *, bool showText);
+
+private slots:
+	void pixmapUpdated(const QPixmap &);
+
+private:
+	void drawButtonLabel(QPainter *p);
+	bool hasToolTip;
+	Icon *icon;
+	bool showText;
+};
+
+PopupActionButton::PopupActionButton(QWidget *parent, const char *name)
+: QPushButton(parent, name)
+{
+	setWFlags(getWFlags() | WRepaintNoErase);
+
+	hasToolTip = false;
+	icon = 0;
+}
+
+PopupActionButton::~PopupActionButton()
+{
+	if (icon)
+		icon->stop();
+}
+
+void PopupActionButton::setIcon(Icon *i, bool st)
+{
+	if ( icon ) {
+		icon->stop();
+		disconnect (icon, 0, this, 0);
+		icon = 0;
+	}
+
+	icon = i;
+	showText = st;
+
+	if ( icon ) {
+		pixmapUpdated(icon->pixmap());
+
+		connect(icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(pixmapUpdated(const QPixmap &)));
+		icon->activated();
+	}
+}
+
+void PopupActionButton::pixmapUpdated(const QPixmap &pix)
+{
+	if ( showText )
+		setIconSet(pix);
+	else
+		setPixmap(pix);
+}
+
+void PopupActionButton::drawButtonLabel(QPainter *p)
+{
+	// code by Justin Karneges :-)
+	// crazy code ahead!  watch out for potholes and deer.
+
+	// this gets us the width of the "text area" on the button.
+	// adapted from qt/src/styles/qcommonstyle.cpp and qt/src/widgets/qpushbutton.cpp
+	QRect r = style().subRect(QStyle::SR_PushButtonContents, this);
+	if(isMenuButton())
+		r.setWidth(r.width() - style().pixelMetric(QStyle::PM_MenuButtonIndicator, this));
+	if(iconSet() && !iconSet()->isNull())
+		r.setWidth(r.width() - (iconSet()->pixmap(QIconSet::Small, QIconSet::Normal, QIconSet::Off).width() + 4));
+
+	// font metrics
+	QFontMetrics fm(font());
+
+	// w1 = width of button text, w2 = width of text area
+	int w1 = fm.width(text());
+	int w2 = r.width();
+
+	// backup original text
+	QString oldtext = text();
+
+	// button text larger than what will fit?
+	if(w1 > w2) {
+		if( !hasToolTip ) {
+			QToolTip::add(this, text());
+			hasToolTip = TRUE;
+		}
+
+		// make a string that fits
+		bool found = FALSE;
+		QString newtext;
+		int n;
+		for(n = oldtext.length(); n > 0; --n) {
+			if(fm.width(oldtext, n) < w2) {
+				found = TRUE;
+				break;
+			}
+		}
+		if(found)
+			newtext = oldtext.mid(0, n);
+		else
+			newtext = "";
+
+		// set the new text that fits.  updates must be off, or we recurse.
+		setUpdatesEnabled(FALSE);
+		QButton::setText(newtext);
+		setUpdatesEnabled(TRUE);
+	}
+	else {
+		if( hasToolTip ) {
+			QToolTip::remove(this);
+			hasToolTip = FALSE;
+		}
+	}
+
+	// draw!
+	QPushButton::drawButtonLabel(p);
+
+	// restore original button text now that we are done drawing.
+	setUpdatesEnabled(FALSE);
+	QButton::setText(oldtext);
+	setUpdatesEnabled(TRUE);
+}
+
+//----------------------------------------------------------------------------
+// PopupAction -- the IconButton with popup or QPopupMenu
+//----------------------------------------------------------------------------
+
+class PopupAction::Private : public QObject
+{
+public:
+	QSizePolicy size;
+	QPtrList<PopupActionButton> buttons;
+	Icon *icon;
+	bool showText;
+
+	Private (QObject *parent)
+	: QObject (parent)
+	{
+		icon = 0;
+		showText = true;
+	}
+
+	~Private()
+	{
+		buttons.clear();
+		if (icon)
+			delete icon;
+	}
+};
+
+PopupAction::PopupAction (const QString &label, QPopupMenu *_menu, QObject *parent, const char *name)
+: IconAction (label, label, 0, parent, name)
+{
+	d = new Private (this);
+	setPopup( _menu );
+}
+
+void PopupAction::setSizePolicy (const QSizePolicy &p)
+{
+	d->size = p;
+}
+
+void PopupAction::setAlert (const Icon *icon)
+{
+	setIcon(icon, d->showText, true);
+}
+
+void PopupAction::setIcon (const Icon *icon, bool showText, bool alert)
+{
+	Icon *oldIcon = 0;
+	if ( d->icon ) {
+		oldIcon = d->icon;
+		d->icon = 0;
+	}
+
+	d->showText = showText;
+
+	if ( icon ) {
+		if ( !alert )
+			d->icon = new Icon(*icon);
+		else
+			d->icon = new AlertIcon(icon);
+
+		IconAction::setIconSet(*icon);
+	}
+	else {
+		d->icon = 0;
+		IconAction::setIconSet(QIconSet());
+	}
+
+	for ( QPtrListIterator<PopupActionButton> it(d->buttons); it.current(); ++it ) {
+		PopupActionButton *btn = it.current();
+		btn->setIcon (d->icon, showText);
+	}
+
+	if ( oldIcon ) {
+		delete oldIcon;
+	}
+}
+
+void PopupAction::setText (const QString &text)
+{
+	IconAction::setText (text);
+	for ( QPtrListIterator<PopupActionButton> it(d->buttons); it.current(); ++it ) {
+		PopupActionButton *btn = it.current();
+		btn->setText (text);
+	}
+}
+
+bool PopupAction::addTo (QWidget *w)
+{
+	if ( w->inherits("QPopupMenu") ) {
+		QPopupMenu *m = (QPopupMenu*)w;
+#ifndef Q_WS_MAC
+		m->insertItem (iconSet(), menuText(), popup());
+#else
+		m->insertItem (QIconSet(), menuText(), popup());
+#endif
+	}
+	else if ( w->inherits("QToolBar") ) {
+		QCString bname = name() + QCString("_action_button");
+		PopupActionButton *btn = new PopupActionButton ( (QToolBar*)w, bname );
+		d->buttons.append ( btn );
+		btn->setPopup ( popup() );
+		btn->setText ( text() );
+		btn->setIcon ( d->icon, d->showText );
+		btn->setSizePolicy ( d->size );
+		btn->setEnabled ( isEnabled() );
+
+		connect( btn, SIGNAL( destroyed() ), SLOT( objectDestroyed() ) );
+	}
+	else
+		return false;
+
+	return true;
+}
+
+void PopupAction::objectDestroyed ()
+{
+	const QObject *obj = sender();
+	d->buttons.removeRef( (PopupActionButton *) obj );
+}
+
+void PopupAction::setEnabled (bool e)
+{
+	IconAction::setEnabled (e);
+	for ( QPtrListIterator<PopupActionButton> it(d->buttons); it.current(); ++it ) {
+		PopupActionButton *btn = it.current();
+		btn->setEnabled (e);
+	}
+}
+
+IconAction *PopupAction::copy() const
+{
+	PopupAction *act = new PopupAction(text(), popup(), 0, name());
+
+	*act = *this;
+
+	return act;
+}
+
+PopupAction &PopupAction::operator=( const PopupAction &from )
+{
+	*( (IconAction *)this ) = from;
+
+	d->size = from.d->size;
+	setIcon( from.d->icon );
+	d->showText = from.d->showText;
+
+	return *this;
+}
+
+//----------------------------------------------------------------------------
+// MLabel -- a clickable label
+//----------------------------------------------------------------------------
+
+MLabel::MLabel(QWidget *parent, const char *name)
+:QLabel(parent, name)
+{
+	setMinimumWidth(48);
+	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+	setFrameStyle(QFrame::Panel | QFrame::Sunken);
+}
+
+void MLabel::mouseReleaseEvent(QMouseEvent *e)
+{
+	emit clicked(e->button());
+	e->ignore();
+}
+
+void MLabel::mouseDoubleClickEvent(QMouseEvent *e)
+{
+	if(e->button() == LeftButton)
+		emit doubleClicked();
+
+	e->ignore();
+}
+
+//----------------------------------------------------------------------------
+// MTray
+//----------------------------------------------------------------------------
+
+class MTray::Private
+{
+public:
+	Private() {
+		icon	= 0;
+		ti	= 0;
+	}
+
+	~Private() {
+		if ( icon )
+			delete icon;
+
+		delete ti;
+	}
+
+	TrayIcon *ti;
+	Icon *icon;
+
+	QPixmap makeIcon();
+	QRgb pixelBlend(QRgb p1, QRgb p2);
+};
+
+MTray::MTray(const QString &tip, QPopupMenu *popup, QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+
+	d->ti = new TrayIcon(d->makeIcon(), tip, popup);
+	d->ti->setWMDock(option.isWMDock);
+	connect(d->ti, SIGNAL(clicked(const QPoint &, int)), SIGNAL(clicked(const QPoint &, int)));
+	connect(d->ti, SIGNAL(doubleClicked(const QPoint &)), SIGNAL(doubleClicked(const QPoint &)));
+	connect(d->ti, SIGNAL(closed()), SIGNAL(closed()));
+	connect(qApp, SIGNAL(newTrayOwner()), d->ti, SLOT(newTrayOwner()));
+	connect(qApp, SIGNAL(trayOwnerDied()), d->ti, SLOT(hide()));
+	d->ti->show();
+}
+
+MTray::~MTray()
+{
+	delete d;
+}
+
+void MTray::setToolTip(const QString &str)
+{
+	d->ti->setToolTip(str);
+}
+
+void MTray::setIcon(const Icon *icon, bool alert)
+{
+	if ( d->icon ) {
+		disconnect(d->icon, 0, this, 0 );
+		d->icon->stop();
+
+		delete d->icon;
+		d->icon = 0;
+	}
+
+	if ( icon ) {
+		if ( !alert )
+			d->icon = new Icon(*icon);
+		else
+			d->icon = new AlertIcon(icon);
+
+		connect(d->icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(animate()));
+		d->icon->activated();
+	}
+	else
+		d->icon = new Icon();
+
+	animate();
+}
+
+void MTray::setAlert(const Icon *icon)
+{
+	setIcon(icon, true);
+}
+
+bool MTray::isAnimating() const
+{
+	return d->icon->isAnimated();
+}
+
+bool MTray::isWMDock()
+{
+	return d->ti->isWMDock();
+}
+
+void MTray::show()
+{
+	d->ti->show();
+}
+
+void MTray::hide()
+{
+	d->ti->hide();
+}
+
+
+// a function to blend 2 pixels taking their alpha channels
+// into consideration
+// p1 is in the 1st layer, p2 is in the 2nd layer (over p1)
+QRgb MTray::Private::pixelBlend(QRgb p1, QRgb p2)
+{
+	int a2 = qAlpha(p2);
+	if (a2 == 255) return p2; // don't calculate anything if p2 is completely opaque
+	int a1 = qAlpha(p1);
+	double prop1 = double(a1*(255-a2))/double(255*255);
+	double prop2 = double(a2)/255.0;
+	int r = int( qRed(p1)*prop1 + qRed(p2)*prop2 );
+	int g = int( qGreen(p1)*prop1 + qGreen(p2)*prop2 );
+	int b = int( qBlue(p1)*prop1 + qBlue(p2)*prop2 );
+	return qRgba(r, g, b, (a1>a2) ? a1:a2);
+}
+
+
+QPixmap MTray::Private::makeIcon()
+{
+	if ( !icon )
+		return QPixmap();
+
+#ifdef Q_WS_X11
+	// on X11, the KDE dock is 22x22.  let's make our icon "seem" bigger.
+	QImage real(22,22,32);
+	QImage in = icon->image();
+	in.detach();
+	real.setAlphaBuffer(true);
+
+	// make sure it is no bigger than 16x16
+	if(in.width() > 16 || in.height() > 16)
+		in = in.smoothScale(16,16);
+
+	int xo = (real.width() - in.width()) / 2;
+	int yo = (real.height() - in.height()) / 2;
+
+	int n, n2;
+
+	// clear the output and make it transparent
+	// deprecates real.fill(0)
+	for(n2 = 0; n2 < real.height(); ++n2)
+		for(n = 0; n < real.width(); ++n)
+			real.setPixel(n, n2, qRgba(0,0,0,0));
+
+	// draw a dropshadow
+	for(n2 = 0; n2 < in.height(); ++n2) {
+		for(n = 0; n < in.width(); ++n) {
+			if(int a = qAlpha(in.pixel(n,n2))) {
+				int x = n + xo + 2;
+				int y = n2 + yo + 2;
+				real.setPixel(x, y, qRgba(0x80,0x80,0x80,a));
+			}
+		}
+	}
+
+	// draw the image
+	for(n2 = 0; n2 < in.height(); ++n2) {
+		for(n = 0; n < in.width(); ++n) {
+			if(qAlpha(in.pixel(n,n2))) {
+				QRgb pold = real.pixel(n + xo , n2 + yo);
+				QRgb pnew = in.pixel(n , n2);
+				real.setPixel(n + xo, n2 + yo, pixelBlend(pold, pnew));
+			}
+		}
+	}
+
+	QPixmap pixmap;
+	pixmap.convertFromImage(real);
+	return pixmap;
+#else
+	return icon->pixmap();
+#endif
+}
+
+void MTray::animate()
+{
+#ifdef Q_WS_X11
+	if ( !d->icon )
+		return;
+
+	QString cachedName = "PsiTray/" + option.defaultRosterIconset + "/" + d->icon->name() + "/" + QString::number( d->icon->framenumber() );
+
+	QPixmap p;
+	if ( !QPixmapCache::find(cachedName, p) ) {
+		p = d->makeIcon();
+		QPixmapCache::insert( cachedName, p );
+	}
+
+	d->ti->setIcon(p);
+#else
+	d->ti->setIcon( d->makeIcon() );
+#endif
+}
+
+//----------------------------------------------------------------------------
+// MAction
+//----------------------------------------------------------------------------
+
+class MAction::Private : public QObject
+{
+public:
+	int id;
+	PsiCon *psi;
+	QSignalMapper *sm;
+
+	Private (int _id, PsiCon *_psi, QObject *parent)
+	: QObject (parent)
+	{
+		id = _id;
+		psi = _psi;
+		sm = new QSignalMapper(this, "MAction::Private::SignalMapper");
+	}
+
+	QPopupMenu *subMenu(QWidget *p)
+	{
+		QPopupMenu *pm = new QPopupMenu (p);
+		uint i = 0;
+		for ( PsiAccountListIt it(psi->accountList(TRUE)); it.current(); ++it, i++ )
+		{
+			PsiAccount *acc = it.current();
+			pm->insertItem( acc->name(), parent(), SLOT(itemActivated(int)), 0, id*1000 + i );
+			pm->setItemParameter ( id*1000 + i, i );
+		}
+		return pm;
+	}
+};
+
+MAction::MAction(Icon i, const QString &s, int id, PsiCon *psi, QObject *parent)
+: IconAction(s, s, 0, parent)
+{
+	init (i, id, psi);
+}
+
+MAction::MAction(const QString &s, int id, PsiCon *psi, QObject *parent)
+: IconAction(s, s, 0, parent)
+{
+	init (Icon(), id, psi);
+}
+
+void MAction::init(Icon i, int id, PsiCon *psi)
+{
+	d = new Private(id, psi, this);
+	setIcon (&i);
+	connect(psi, SIGNAL(accountCountChanged()), SLOT(numAccountsChanged()));
+	setEnabled ( !d->psi->accountList(TRUE).isEmpty() );
+	connect (d->sm, SIGNAL(mapped(int)), SLOT(itemActivated(int)));
+}
+
+bool MAction::addTo(QWidget *w)
+{
+	if ( w->inherits("QPopupMenu") )
+	{
+		QPopupMenu *menu = (QPopupMenu*)w;
+        QIconSet iconset;
+#ifndef Q_WS_MAC
+		iconset = iconSet();
+#endif
+		if ( d->psi->accountList(TRUE).count() < 2 ) {
+			menu->insertItem ( iconset, menuText(), this, SLOT(itemActivated(int)), 0, d->id*1000 + 0 );
+			menu->setItemEnabled (d->id*1000 + 0, isEnabled());
+			menu->setItemParameter ( d->id*1000 + 0, 0 );
+		}
+		else
+			menu->insertItem(iconset, menuText(), d->subMenu(w));
+	}
+	else
+		return IconAction::addTo(w);
+
+	return true;
+}
+
+void MAction::addingToolButton(IconToolButton *btn)
+{
+	connect (btn, SIGNAL(clicked()), d->sm, SLOT(map()));
+	d->sm->setMapping (btn, 0);
+
+	if ( d->psi->accountList(TRUE).count() >= 2 )
+		btn->setPopup ( d->subMenu(btn) );
+	else
+		btn->setPopup (0);
+}
+
+void MAction::itemActivated(int n)
+{
+	PsiAccountList list = (PsiAccountList)d->psi->accountList(TRUE);
+
+	if (n >= (int)list.count()) // just in case
+		return;
+
+	emit activated((PsiAccount *)list.at(n), d->id);
+}
+
+void MAction::numAccountsChanged()
+{
+	setEnabled ( !d->psi->accountList(TRUE).isEmpty() );
+
+	QPtrList<IconToolButton> btns = buttonList();
+	for ( QPtrListIterator<IconToolButton> it(btns); it.current(); ++it ) {
+		QToolButton *btn = it.current();
+
+		if ( btn->popup() )
+			delete btn->popup();
+		btn->setPopup (0);
+
+		if ( d->psi->accountList(TRUE).count() >= 2 )
+			btn->setPopup ( d->subMenu(btn) );
+	}
+}
+
+IconAction *MAction::copy() const
+{
+	MAction *act = new MAction(text(), d->id, d->psi, 0);
+
+	*act = *this;
+
+	return act;
+}
+
+MAction &MAction::operator=( const MAction &from )
+{
+	*( (IconAction *)this ) = from;
+
+	return *this;
+}
+
+//----------------------------------------------------------------------------
+// SpacerAction
+//----------------------------------------------------------------------------
+
+SpacerAction::SpacerAction(QObject *parent, const char *name)
+: IconAction(parent, name)
+{
+	setText(tr("<Spacer>"));
+	setMenuText(tr("<Spacer>"));
+	setWhatsThis(tr("Spacer provides spacing to separate actions"));
+}
+
+SpacerAction::~SpacerAction()
+{
+}
+
+bool SpacerAction::addTo(QWidget *w)
+{
+	if ( w->inherits("QToolBar") ) {
+		new StretchWidget(w);
+		return true;
+	}
+
+	return false;
+}
+
+IconAction *SpacerAction::copy() const
+{
+	return new SpacerAction( 0 );
+}
+
+//----------------------------------------------------------------------------
+// SeparatorAction
+//----------------------------------------------------------------------------
+
+class SeparatorAction::Private
+{
+public:
+	Private() { }
+
+	QAction *separator;
+};
+
+SeparatorAction::SeparatorAction( QObject *parent, const char *name )
+	: IconAction( tr("<Separator>"), tr("<Separator>"), 0, parent, name )
+{
+	d = new Private();
+	d->separator = new QAction(this, "qt_separator_action");
+
+	setWhatsThis (tr("Separator"));
+}
+
+SeparatorAction::~SeparatorAction()
+{
+	delete d;
+}
+
+bool SeparatorAction::addTo (QWidget *w)
+{
+	return d->separator->addTo( w );
+}
+
+IconAction *SeparatorAction::copy() const
+{
+	return new SeparatorAction( 0 );
+}
+
+//----------------------------------------------------------------------------
+// EventNotifierAction
+//----------------------------------------------------------------------------
+
+class EventNotifierAction::Private
+{
+public:
+	Private() { }
+
+	QPtrList<MLabel> labels;
+	bool hide;
+};
+
+EventNotifierAction::EventNotifierAction(QObject *parent, const char *name)
+: IconAction(parent, name)
+{
+	d = new Private;
+	setMenuText(tr("<Event notifier>"));
+	d->hide = true;
+}
+
+EventNotifierAction::~EventNotifierAction()
+{
+	delete d;
+}
+
+bool EventNotifierAction::addTo(QWidget *w)
+{
+	if ( w->inherits("QToolBar") ) {
+		MLabel *label = new MLabel(w, "EventNotifierAction::MLabel");
+		label->setText(text());
+		d->labels.append(label);
+		connect(label, SIGNAL(destroyed()), SLOT(objectDestroyed()));
+		connect(label, SIGNAL(doubleClicked()), SIGNAL(activated()));
+		connect(label, SIGNAL(clicked(int)), SIGNAL(clicked(int)));
+
+		if ( d->hide )
+			hide();
+
+		return true;
+	}
+
+	return false;
+}
+
+void EventNotifierAction::setText(const QString &t)
+{
+	IconAction::setText("<nobr>" + t + "</nobr>");
+
+	QPtrListIterator<MLabel> it ( d->labels );
+	for ( ; it.current(); ++it) {
+		MLabel *label = it.current();
+		label->setText(text());
+	}
+}
+
+void EventNotifierAction::objectDestroyed()
+{
+	MLabel *label = (MLabel *)sender();
+	d->labels.removeRef(label);
+}
+
+void EventNotifierAction::hide()
+{
+	d->hide = true;
+
+	QPtrListIterator<MLabel> it ( d->labels );
+	for ( ; it.current(); ++it) {
+		MLabel *label = it.current();
+		label->hide();
+		QToolBar *toolBar = (QToolBar *)label->parent();
+
+		QObjectList *l = toolBar->queryList( "QWidget" );
+		int found = 0;
+
+		QObjectListIt it( *l );
+		QObject *obj;
+		for ( ; (obj = it.current()); ++it) {
+			if ( QCString(obj->name()).left(3) != "qt_" ) // misc internal Qt objects
+				found++;
+		}
+		delete l;
+
+		if ( found == 1 ) // only MLabel is on ToolBar
+			toolBar->hide();
+	}
+}
+
+void EventNotifierAction::show()
+{
+	d->hide = false;
+
+	QPtrListIterator<MLabel> it ( d->labels );
+	for ( ; it.current(); ++it) {
+		MLabel *label = it.current();
+		QToolBar *toolBar = (QToolBar *)label->parent();
+		toolBar->show();
+		label->show();
+	}
+}
+
+IconAction *EventNotifierAction::copy() const
+{
+	EventNotifierAction *act = new EventNotifierAction( 0 );
+
+	*act = *this;
+
+	return act;
+}
+
+EventNotifierAction &EventNotifierAction::operator=( const EventNotifierAction &from )
+{
+	*( (IconAction *)this ) = from;
+
+	d->hide = from.d->hide;
+
+	return *this;
+}
+
+#include "mainwin_p.moc"
Index: psi/tags/0.10-os2-b1/src/mainwin_p.h
===================================================================
--- psi/tags/0.10-os2-b1/src/mainwin_p.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/mainwin_p.h	(revision 38)
@@ -0,0 +1,194 @@
+/*
+ * mainwin_p.h - classes used privately by the main window.
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef MAINWIN_P_H
+
+#include<qpushbutton.h>
+#include<qtoolbutton.h>
+#include<qpopupmenu.h>
+#include<qlayout.h>
+#include<qlabel.h>
+#include<qptrvector.h>
+#include<qtooltip.h>
+#include<qaction.h>
+#include"trayicon.h"
+#include"common.h"
+#include"psicon.h"
+#include"iconaction.h"
+
+class SeparatorAction : public IconAction
+{
+	Q_OBJECT
+public:
+	SeparatorAction(QObject *parent, const char *name = 0);
+	~SeparatorAction();
+
+	virtual bool addTo (QWidget *w);
+
+	virtual IconAction *copy() const;
+
+private:
+	class Private;
+	Private *d;
+};
+
+class SpacerAction : public IconAction
+{
+	Q_OBJECT
+public:
+	SpacerAction(QObject *parent, const char *name = 0);
+	~SpacerAction();
+
+	virtual bool addTo (QWidget *w);
+
+	virtual IconAction *copy() const;
+};
+
+class EventNotifierAction : public IconAction
+{
+	Q_OBJECT
+public:
+	EventNotifierAction(QObject *parent, const char *name = 0);
+	~EventNotifierAction();
+
+	void setText(const QString &);
+	bool addTo (QWidget *w);
+
+	void hide();
+	void show();
+
+	virtual IconAction *copy() const;
+	virtual EventNotifierAction &operator=( const EventNotifierAction & );
+
+signals:
+	void clicked(int);
+
+private slots:
+	void objectDestroyed ();
+
+private:
+	class Private;
+	Private *d;
+};
+
+class PopupAction : public IconAction
+{
+	Q_OBJECT
+private:
+	class Private;
+	Private *d;
+
+private slots:
+	void objectDestroyed ();
+
+public slots:
+	void setEnabled (bool);
+
+public:
+	PopupAction (const QString &label, QPopupMenu *_menu, QObject *parent, const char *name);
+	void setSizePolicy (const QSizePolicy &p);
+	void setAlert (const Icon *);
+	void setIcon (const Icon *, bool showText = true, bool alert = false);
+	void setText (const QString &text);
+	bool addTo (QWidget *w);
+
+	virtual IconAction *copy() const;
+	virtual PopupAction &operator=( const PopupAction & );
+};
+
+class MLabel : public QLabel
+{
+	Q_OBJECT
+public:
+	MLabel(QWidget *parent=0, const char *name=0);
+
+protected:
+	// reimplemented
+	void mouseReleaseEvent(QMouseEvent *);
+	void mouseDoubleClickEvent(QMouseEvent *);
+
+signals:
+	void clicked(int);
+	void doubleClicked();
+};
+
+class MTray : public QObject
+{
+	Q_OBJECT
+public:
+	MTray(const QString &tip, QPopupMenu *popup, QObject *parent=0);
+	~MTray();
+
+	void setToolTip(const QString &);
+	void setIcon(const Icon *, bool alert = false);
+	void setAlert(const Icon *);
+	bool isAnimating() const;
+
+	bool isWMDock();
+
+signals:
+	void clicked(const QPoint &, int);
+	void doubleClicked(const QPoint &);
+	void closed();
+
+public slots:
+	void show();
+	void hide();
+
+private slots:
+	void animate();
+
+private:
+	class Private;
+	Private *d;
+};
+
+class MAction : public IconAction
+{
+	Q_OBJECT
+
+public:
+	MAction(Icon, const QString &, int id, PsiCon *, QObject *parent);
+	MAction(const QString &, int id, PsiCon *, QObject *parent);
+
+	bool addTo(QWidget *w);
+
+	virtual IconAction *copy() const;
+	virtual MAction &operator=( const MAction & );
+
+signals:
+	void activated(PsiAccount *, int);
+
+private slots:
+	void numAccountsChanged();
+	void itemActivated(int n);
+
+protected:
+	void addingToolButton(IconToolButton *);
+
+private:
+	class Private;
+	Private *d;
+
+	void init (Icon, int id, PsiCon *psi);
+};
+
+#endif
+
Index: psi/tags/0.10-os2-b1/src/msgmle.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/msgmle.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/msgmle.cpp	(revision 38)
@@ -0,0 +1,380 @@
+/*
+ * msgmle.cpp - subclass of PsiTextView to handle various hotkeys
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include<qapplication.h>
+#include<qlayout.h>
+#include<qtimer.h>
+
+#include"common.h"
+#include"msgmle.h"
+
+//----------------------------------------------------------------------------
+// ChatView
+//----------------------------------------------------------------------------
+ChatView::ChatView(QWidget *parent, const char *name)
+: PsiTextView(parent, name)
+{
+	setWordWrap(WidgetWidth);
+	setWrapPolicy(AtWordOrDocumentBoundary);
+
+	setTextFormat(RichText);
+	setReadOnly(true);
+	setUndoRedoEnabled(false);
+	setHScrollBarMode(QScrollView::AlwaysOff);
+
+#ifndef Q_WS_X11	// linux has this feature built-in
+	connect(this, SIGNAL(copyAvailable(bool)), SLOT(autoCopy(bool)));
+#endif
+
+}
+
+ChatView::~ChatView()
+{
+}
+
+bool ChatView::focusNextPrevChild(bool next)
+{
+	return QWidget::focusNextPrevChild(next);
+}
+
+void ChatView::keyPressEvent(QKeyEvent *e)
+{
+	if(e->key() == Key_Escape)
+		e->ignore();
+#ifdef Q_WS_MAC
+	else if(e->key() == Key_W && e->state() & ControlButton)
+		e->ignore();
+#endif
+	else if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) )
+		e->ignore();
+	else if(e->key() == Key_H && (e->state() & ControlButton))
+		e->ignore();
+	else if(e->key() == Key_I && (e->state() & ControlButton))
+		e->ignore();
+	else if(e->key() == Key_M && (e->state() & ControlButton)) // newline
+		insert("\n");
+	else if(e->key() == Key_U && (e->state() & ControlButton))
+		setText("");
+	else
+		QTextEdit::keyPressEvent(e);
+}
+
+void ChatView::resizeEvent(QResizeEvent *e)
+{
+	// This fixes flyspray #45
+	if(contentsY() == contentsHeight() - visibleHeight())
+		scrollToBottom();
+
+	QTextEdit::resizeEvent(e);
+}
+
+/*!
+	Copies any selected text (from selection 0) to the clipboard
+	if option.autoCopy is TRUE, \a copyAvailable is TRUE
+	and ChatView is in read-only mode.
+	In any other case it does nothing.
+
+	This slot is connected with copyAvailable(bool) signal
+	in ChatView's constructor.
+
+	\sa copyAvailable()
+*/
+
+void ChatView::autoCopy(bool copyAvailable)
+{
+	if ( isReadOnly() && copyAvailable && option.autoCopy ) {
+		copy();
+	}
+}
+
+//----------------------------------------------------------------------------
+// ChatEdit
+//----------------------------------------------------------------------------
+ChatEdit::ChatEdit(QWidget *parent, const char *name)
+: PsiTextView(parent, name)
+{
+	setWordWrap(QTextEdit::WidgetWidth);
+
+	setReadOnly(false);
+	setUndoRedoEnabled(true);
+
+	setTextFormat(PlainText);
+	setMinimumHeight(48);
+}
+
+ChatEdit::~ChatEdit()
+{
+}
+
+void ChatEdit::keyPressEvent(QKeyEvent *e)
+{
+	if(e->key() == Key_Escape || (e->key() == Key_W && e->state() & ControlButton))
+		e->ignore();
+	else if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) )
+		e->ignore();
+	else if(e->key() == Key_M && (e->state() & ControlButton)) // newline
+		insert("\n");
+	else if(e->key() == Key_H && (e->state() & ControlButton)) // history
+		e->ignore();
+	else if(e->key() == Key_S && (e->state() & AltButton))
+		e->ignore();
+	else if(e->key() == Key_Backtab && (e->state() & ControlButton))
+		e->ignore();
+	else if(e->key() == Key_Tab && (e->state() & ControlButton))
+		e->ignore();
+	else if(e->key() == Key_U && (e->state() & ControlButton))
+		setText("");
+	else if((e->key() == Key_Return || e->key() == Key_Enter) && !(e->state() & ShiftButton) && option.chatSoftReturn)
+		e->ignore();
+	else if((e->key() == Key_PageUp || e->key() == Key_PageDown) && (e->state() & ShiftButton))
+		e->ignore();
+	else if((e->key() == Key_PageUp || e->key() == Key_PageDown) && (e->state() & ControlButton))
+		e->ignore();
+	else
+		QTextEdit::keyPressEvent(e);
+}
+
+//----------------------------------------------------------------------------
+// LineEdit
+//----------------------------------------------------------------------------
+LineEdit::LineEdit( QWidget *parent, const char *name )
+	: ChatEdit( parent, name )
+{
+	lastSize = QSize( 0, 0 );
+	initialWindowGeometry = QRect( 0, 0, 0, 0 );
+
+	QWidget *topParent = topLevelWidget();
+	topParent->installEventFilter( this );
+	moveTo = QPoint(topParent->x(), topParent->y());
+
+	moveTimer = new QTimer( this );
+	connect( moveTimer, SIGNAL(timeout()), SLOT(checkMoved()) );
+
+	// LineEdit's size hint is to be vertically as small as possible
+	setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Maximum );
+
+	setWrapPolicy( AtWordOrDocumentBoundary ); // no need for horizontal scrollbar with this
+	setHScrollBarMode(QScrollView::AlwaysOff);
+
+	setMinimumHeight(-1);
+
+	connect( this, SIGNAL( textChanged() ), SLOT( recalculateSize() ) );
+}
+
+LineEdit::~LineEdit()
+{
+}
+
+/*!
+ * Returns true if the dialog could be automatically resized by LineEdit.
+ */
+bool LineEdit::allowResize() const
+{
+	QWidget *topParent = topLevelWidget();
+
+	QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
+	float desktopArea = desktop.width() * desktop.height();
+	float dialogArea  = topParent->frameGeometry().width() * topParent->frameGeometry().height();
+
+	// maximized and large chat windows shoulnd't resize the dialog
+	if ( (dialogArea / desktopArea) > 0.9 )
+		return false;
+
+	return true;
+}
+
+/*!
+ * In this implementation, it is quivalent to sizeHint().
+ */
+QSize LineEdit::minimumSizeHint() const
+{
+	return sizeHint();
+}
+
+/*!
+ * All magic is contained within this function. It determines the possible maximum
+ * height, and controls the appearance of vertical scrollbar.
+ */
+QSize LineEdit::sizeHint() const
+{
+	if ( lastSize.width() != 0 && lastSize.height() != 0 )
+		return lastSize;
+
+	lastSize.setWidth( QTextEdit::sizeHint().width() );
+	int h = 7;
+
+	if ( paragraphs() > 0 ) {
+		for ( int i = 0; i < paragraphs(); i++ ) {
+			h += paragraphRect( i ).height();
+		}
+	}
+
+	QWidget *topParent = topLevelWidget();
+	QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
+	int dh = h - height();
+
+	bool showScrollBar = false;
+
+	// check that our dialog's height doesn't exceed the desktop's
+	if ( allowResize() && (topParent->frameGeometry().height() + dh) >= desktop.height() ) {
+		// handles the case when the dialog could be resized,
+		// but lineedit wants to occupy too much space, so we should limit it
+		h = desktop.height() - ( topParent->frameGeometry().height() - height() );
+		showScrollBar = true;
+	}
+	else if ( !allowResize() && (h > topParent->geometry().height()/2) ) {
+		// handles the case when the dialog could not be resized(i.e. it's maximized).
+		// in this case we limit maximum height of lineedit to the half of dialog's
+		// full height
+		h = topParent->geometry().height() / 2;
+		showScrollBar = true;
+	}
+
+	// enable vertical scrollbar only when we're surely in need for it
+	QTextEdit *textEdit = (QTextEdit *)this;
+	if ( showScrollBar )
+		textEdit->setVScrollBarMode( AlwaysOn );
+	else
+		textEdit->setVScrollBarMode( AlwaysOff );
+
+	lastSize.setHeight( h );
+	return lastSize;
+}
+
+/*!
+ * Handles automatic dialog resize.
+ */
+void LineEdit::recalculateSize()
+{
+	if ( !isUpdatesEnabled() )
+		return;
+
+	QSize oldSize = lastSize;
+	lastSize = QSize( 0, 0 ); // force sizeHint() to update
+	QSize newSize = sizeHint();
+
+	if ( QABS(newSize.height() - oldSize.height()) > 1 ) {
+		if ( allowResize() ) {
+			parentWidget()->layout()->setEnabled( false ); // try to reduce some flicker
+
+			QWidget *topParent = topLevelWidget();
+			int dh = newSize.height() - oldSize.height();
+
+			// if we're going to shrink dialog considerably, minimum
+			// size will prevent us from doing it. Activating main
+			// layout after resize will reset minimum sizes to sensible values
+			topParent->setMinimumSize( 10, 10 );
+
+			topParent->resize( topParent->width(),
+					   topParent->height() + dh );
+
+			bool canMove = dh > 0;
+			int  newy    = topParent->y();
+
+			// try to move window to its old position
+			if ( movedWindow() && dh < 0 ) {
+				newy = initialWindowGeometry.y();
+				canMove = true;
+			}
+
+			// check, if we need to move dialog upper
+			QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
+			if ( canMove && ( newy + topParent->frameGeometry().height() >= desktop.bottom() ) ) {
+				// initialize default window position
+				if ( !movedWindow() ) {
+					initialWindowGeometry = topParent->frameGeometry();
+				}
+
+				newy = QMAX(0, desktop.bottom() - topParent->frameGeometry().height());
+			}
+
+			if ( canMove && newy != topParent->y() ) {
+				topParent->move( topParent->x(), newy );
+				moveTo = topParent->pos();
+			}
+
+			parentWidget()->layout()->setEnabled( true );
+		}
+
+		// issue a layout update
+		parentWidget()->layout()->activate();
+	}
+}
+
+void LineEdit::resizeEvent( QResizeEvent *e )
+{
+	// issue a re-layout, just in case
+	lastSize = QSize( 0, 0 ); // force sizeHint() to update
+	sizeHint(); // update the size hint, and cache the value
+	topLevelWidget()->layout()->activate();
+
+	PsiTextView::resizeEvent( e );
+}
+
+void LineEdit::setUpdatesEnabled( bool enable )
+{
+	bool ue = isUpdatesEnabled();
+	ChatEdit::setUpdatesEnabled( enable );
+
+	if ( !ue && enable )
+		recalculateSize();
+}
+
+bool LineEdit::eventFilter(QObject *watched, QEvent *e)
+{
+	if ( !parentWidget()->layout()->isEnabled() )
+		return ChatEdit::eventFilter( watched, e );
+
+	if ( e->type() == QEvent::Reparent ) {
+		// In case of tabbed chats, dialog could be reparented to a higher-level dialog
+		// we need to get move events from it too. And unnecessary event filters
+		// are automatically cleaned up by Qt.
+		topLevelWidget()->installEventFilter( this );
+	}
+	else if ( e->type() == QEvent::Move ) {
+		QWidget *topParent = topLevelWidget();
+		if ( watched == topParent ) {
+			moveTimer->start( 100, true );
+		}
+	}
+
+	return ChatEdit::eventFilter( watched, e );
+}
+
+//! This function serves as a workaround for multiple move events, some of which
+//! have incorrect coordinates (at least on KDE)
+void LineEdit::checkMoved()
+{
+	QWidget *topParent = topLevelWidget();
+	if ( QABS(moveTo.x() - topParent->x()) > 1 ||
+	     QABS(moveTo.y() - topParent->y()) > 1 ) {
+		moveTo = topParent->pos();
+		initialWindowGeometry = QRect( 0, 0, 0, 0 );
+	}
+}
+
+bool LineEdit::movedWindow() const
+{
+	return initialWindowGeometry.left()  ||
+	       initialWindowGeometry.top()   ||
+	       initialWindowGeometry.width() ||
+	       initialWindowGeometry.height();
+}
+
Index: psi/tags/0.10-os2-b1/src/msgmle.h
===================================================================
--- psi/tags/0.10-os2-b1/src/msgmle.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/msgmle.h	(revision 38)
@@ -0,0 +1,87 @@
+/*
+ * msgmle.h - subclass of PsiTextView to handle various hotkeys
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef MSGMLE_H
+#define MSGMLE_H
+
+#include"psitextview.h"
+
+class QTimer;
+
+class ChatView : public PsiTextView
+{
+	Q_OBJECT
+public:
+	ChatView(QWidget *parent, const char *name = 0);
+	~ChatView();
+
+protected:
+	// override the tab/esc behavior
+	bool focusNextPrevChild(bool next);
+	void keyPressEvent(QKeyEvent *);
+	void resizeEvent(QResizeEvent *);
+
+protected slots:
+	void autoCopy(bool copyAvailable);
+};
+
+class ChatEdit : public PsiTextView
+{
+public:
+	ChatEdit(QWidget *parent, const char *name = 0);
+	~ChatEdit();
+
+protected:
+	void keyPressEvent(QKeyEvent *);
+};
+
+
+class LineEdit : public ChatEdit
+{
+	Q_OBJECT
+public:
+	LineEdit(QWidget *parent, const char *name = 0);
+	~LineEdit();
+
+	QSize minimumSizeHint() const;
+	QSize sizeHint() const;
+	bool eventFilter(QObject *watched, QEvent *e);
+
+public slots:
+	void recalculateSize();
+	void setUpdatesEnabled( bool enable );
+
+private slots:
+	void checkMoved();
+
+protected:
+	void resizeEvent( QResizeEvent * );
+	bool allowResize() const;
+	bool movedWindow() const;
+
+private:
+	QTimer *moveTimer;
+	mutable QSize lastSize;
+	mutable QRect initialWindowGeometry; // used to return window to its original location
+	mutable QPoint moveTo; // used to differentiate user move message from the ones generated by LineEdit
+};
+
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_advanced-ui.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_advanced-ui.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_advanced-ui.ui	(revision 38)
@@ -0,0 +1,157 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>OptAdvancedUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>OptAdvancedUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>320</width>
+            <height>348</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>OptAdvancedUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_messageevents</cstring>
+            </property>
+            <property name="text">
+                <string>Enable 'Contact is typing ...' events</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_singleclick</cstring>
+            </property>
+            <property name="text">
+                <string>Single-click triggers default action</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autocopy</cstring>
+            </property>
+            <property name="text">
+                <string>Automatically copy selected text to clipboad</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_grabUrls</cstring>
+            </property>
+            <property name="text">
+                <string>Grab URLs from clipboard</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_jidComplete</cstring>
+            </property>
+            <property name="text">
+                <string>Jabber ID completion</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_showSubjects</cstring>
+            </property>
+            <property name="text">
+                <string>Show subject line in events</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_showCounter</cstring>
+            </property>
+            <property name="text">
+                <string>Show character counter</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autoVCardOnLogin</cstring>
+            </property>
+            <property name="text">
+                <string>Automatically get vCard when becoming online</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autoVersion</cstring>
+            </property>
+            <property name="text">
+                <string>Check client version when a contact comes online</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_rosterAnim</cstring>
+            </property>
+            <property name="text">
+                <string>Animate the roster when a contact comes online</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_scrollTo</cstring>
+            </property>
+            <property name="text">
+                <string>Scroll roster to contact on event</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_ignoreHeadline</cstring>
+            </property>
+            <property name="text">
+                <string>Ignore "Headline" events (e.g. MSN alerts)</string>
+            </property>
+        </widget>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>TextLabel2</cstring>
+            </property>
+            <property name="text">
+                <string>Treat incoming messages and chats as:</string>
+            </property>
+        </widget>
+        <widget class="QComboBox">
+            <item>
+                <property name="text">
+                    <string>Determined by sender</string>
+                </property>
+            </item>
+            <item>
+                <property name="text">
+                    <string>Messages</string>
+                </property>
+            </item>
+            <item>
+                <property name="text">
+                    <string>Chats</string>
+                </property>
+            </item>
+            <item>
+                <property name="text">
+                    <string>Messages (Chats if Chatting)</string>
+                </property>
+            </item>
+            <property name="name">
+                <cstring>cb_incomingAs</cstring>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_advanced.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_advanced.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_advanced.cpp	(revision 38)
@@ -0,0 +1,117 @@
+#include "opt_advanced.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+
+#include "opt_advanced-ui.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabAdvanced
+//----------------------------------------------------------------------------
+
+OptionsTabAdvanced::OptionsTabAdvanced(QObject *parent)
+: OptionsTab(parent, "advanced", "", tr("Advanced"), tr("Options for advanced users"), "psi/advanced")
+{
+	w = 0;
+}
+
+OptionsTabAdvanced::~OptionsTabAdvanced()
+{
+}
+
+QWidget *OptionsTabAdvanced::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new OptAdvancedUI();
+	OptAdvancedUI *d = (OptAdvancedUI *)w;
+
+#ifdef Q_WS_X11	// auto-copy is a built-in feature on linux, we don't want user to use our own one
+	d->ck_autocopy->hide();
+#endif
+
+	QWhatsThis::add(d->ck_messageevents,
+		tr("Enables the sending and requesting of message events such as "
+		"'Contact is Typing', ..."));
+	QWhatsThis::add(d->ck_autocopy,
+		tr("Check this option if you want the selected text in incoming messages and chat log to be automatically copied to clipboard"));
+	QWhatsThis::add(d->ck_singleclick,
+		tr("Normally, a double-click on a contact will invoke the default action."
+		"  Check this option if you'd rather invoke with a single-click."));
+	QWhatsThis::add(d->ck_jidComplete,
+		tr("Enables as-you-type JID autocompletion in message dialog."));
+	QWhatsThis::add(d->ck_grabUrls,
+		tr("Automatically attaches URLs from clipboard to the messages when enabled"));
+	QWhatsThis::add(d->cb_incomingAs,
+		tr("<P>Specifies how to treat incoming events:</P>"
+		"<P><B>Normal</B> - messages come as messages, chats come as chats.</P>"
+		"<P><B>Messages</B> - All messages/chats come as messages, no matter what their original form was.</P>"
+		"<P><B>Chats</B> - All messages/chats come as chats, no matter what their original form was.</P>"));
+	QWhatsThis::add(d->ck_showSubjects,
+		tr("Makes Psi show separate subject line in messages. Uncheck this if you want to save some screen space."));
+	QWhatsThis::add(d->ck_showCounter,
+		tr("Makes Psi show message length counter. Check this if you want to know how long is your message. Can be useful when you're using SMS transport."));
+	QWhatsThis::add(d->ck_autoVersion,
+		tr("Automatically sends iq:version query to contact when he becomes online. This allows you to determine what client he is using to get online. Note: results in increased traffic."));
+	QWhatsThis::add(d->ck_autoVCardOnLogin,
+		tr("By default, Psi always checks your vCard on login. If you want to save some traffic, you can uncheck this option."));
+	QWhatsThis::add(d->ck_rosterAnim,
+		tr("Makes Psi animate contact names in the main window when they come online."));
+	QWhatsThis::add(d->ck_scrollTo,
+		tr("Makes Psi scroll the main window automatically so you can see new incoming events."));
+	QWhatsThis::add(d->ck_ignoreHeadline,
+		tr("Makes Psi ignore all incoming \"headline\" events,"
+		" like system-wide news on MSN, announcements, etc."));
+
+	return w;
+}
+
+void OptionsTabAdvanced::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptAdvancedUI *d = (OptAdvancedUI *)w;
+
+	opt->messageEvents = d->ck_messageevents->isChecked();
+	opt->autoCopy = d->ck_autocopy->isChecked();
+	opt->singleclick = d->ck_singleclick->isChecked();
+	opt->jidComplete = d->ck_jidComplete->isChecked();
+	opt->grabUrls    = d->ck_grabUrls->isChecked();
+	opt->incomingAs = d->cb_incomingAs->currentItem();
+	opt->showSubjects = d->ck_showSubjects->isChecked();
+	opt->showCounter = d->ck_showCounter->isChecked();
+	opt->autoVersion = d->ck_autoVersion->isChecked();
+	opt->autoVCardOnLogin = d->ck_autoVCardOnLogin->isChecked();
+	opt->rosterAnim = d->ck_rosterAnim->isChecked();
+	opt->scrollTo = d->ck_scrollTo->isChecked();
+	opt->ignoreHeadline = d->ck_ignoreHeadline->isChecked();
+}
+
+void OptionsTabAdvanced::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptAdvancedUI *d = (OptAdvancedUI *)w;
+
+	d->ck_messageevents->setChecked( opt->messageEvents );
+	d->ck_autocopy->setChecked( opt->autoCopy );
+	d->ck_singleclick->setChecked( opt->singleclick );
+	d->ck_jidComplete->setChecked( opt->jidComplete );
+	d->ck_grabUrls->setChecked( opt->grabUrls );
+	d->cb_incomingAs->setCurrentItem( opt->incomingAs );
+	d->ck_showSubjects->setChecked( opt->showSubjects );
+	d->ck_showCounter->setChecked( opt->showCounter );
+	d->ck_autoVersion->setChecked( opt->autoVersion );
+	d->ck_autoVCardOnLogin->setChecked( opt->autoVCardOnLogin );
+	d->ck_rosterAnim->setChecked( opt->rosterAnim );
+	d->ck_scrollTo->setChecked( opt->scrollTo );
+	d->ck_ignoreHeadline->setChecked( opt->ignoreHeadline );
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_advanced.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_advanced.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_advanced.h	(revision 38)
@@ -0,0 +1,24 @@
+#ifndef OPT_ADVANCED_H
+#define OPT_ADVANCED_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+
+class OptionsTabAdvanced : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabAdvanced(QObject *parent);
+	~OptionsTabAdvanced();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private:
+	QWidget *w;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_appearance-ui.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_appearance-ui.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_appearance-ui.ui	(revision 38)
@@ -0,0 +1,523 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>OptAppearanceUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>OptAppearanceUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>314</width>
+            <height>461</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>OptAppearanceUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox11</cstring>
+            </property>
+            <property name="title">
+                <string>Colors</string>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="spacing">
+                    <number>3</number>
+                </property>
+                <widget class="QLabel" row="5" column="0">
+                    <property name="name">
+                        <cstring>text_1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Account Heading Background</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="7" column="0">
+                    <property name="name">
+                        <cstring>text</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Group Heading Background</string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="8" column="1">
+                    <property name="name">
+                        <cstring>pb_cListBack</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="10" column="1">
+                    <property name="name">
+                        <cstring>pb_cAnimBack</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1_2_3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Offline Contacts</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Away Contacts</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1_2_6</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Online Contacts</string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="5" column="1">
+                    <property name="name">
+                        <cstring>pb_cProfileBack</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="7" column="1">
+                    <property name="name">
+                        <cstring>pb_cGroupBack</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="3" column="1">
+                    <property name="name">
+                        <cstring>pb_cDND</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="1" column="1">
+                    <property name="name">
+                        <cstring>pb_cOffline</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="4" column="1">
+                    <property name="name">
+                        <cstring>pb_cProfileFore</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="6" column="1">
+                    <property name="name">
+                        <cstring>pb_cGroupFore</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="9" column="1">
+                    <property name="name">
+                        <cstring>pb_cAnimFront</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="0" column="1">
+                    <property name="name">
+                        <cstring>pb_cOnline</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="9" column="0">
+                    <property name="name">
+                        <cstring>text3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Nick Animation Foreground</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="3" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1_2_6_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>DND Contacts</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="8" column="0">
+                    <property name="name">
+                        <cstring>text2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Contact List Background</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="10" column="0">
+                    <property name="name">
+                        <cstring>text4</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Nick Animation Background</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="4" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1_2_5_1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Account Heading Foreground</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="6" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1_2_5</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Group Heading Foreground</string>
+                    </property>
+                </widget>
+                <widget class="QToolButton" row="2" column="1">
+                    <property name="name">
+                        <cstring>pb_cAway</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>0</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox12</cstring>
+            </property>
+            <property name="title">
+                <string>Fonts</string>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="spacing">
+                    <number>3</number>
+                </property>
+                <widget class="QPushButton" row="1" column="2">
+                    <property name="name">
+                        <cstring>pb_fMessage</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Choose</string>
+                    </property>
+                </widget>
+                <widget class="QPushButton" row="0" column="2">
+                    <property name="name">
+                        <cstring>pb_fRoster</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Choose</string>
+                    </property>
+                </widget>
+                <widget class="QPushButton" row="2" column="2">
+                    <property name="name">
+                        <cstring>pb_fChat</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>true</bool>
+                    </property>
+                    <property name="text">
+                        <string>Choose</string>
+                    </property>
+                </widget>
+                <widget class="QPushButton" row="3" column="2">
+                    <property name="name">
+                        <cstring>pb_fPopup</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>true</bool>
+                    </property>
+                    <property name="text">
+                        <string>Choose</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="3" column="0">
+                    <property name="name">
+                        <cstring>textLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Popup</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Roster</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1_4</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Message</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1_3</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>true</bool>
+                    </property>
+                    <property name="text">
+                        <string>Chat</string>
+                    </property>
+                </widget>
+                <widget class="FontLabel" row="3" column="1">
+                    <property name="name">
+                        <cstring>le_fPopup</cstring>
+                    </property>
+                </widget>
+                <widget class="FontLabel" row="2" column="1">
+                    <property name="name">
+                        <cstring>le_fChat</cstring>
+                    </property>
+                </widget>
+                <widget class="FontLabel" row="1" column="1">
+                    <property name="name">
+                        <cstring>le_fMessage</cstring>
+                    </property>
+                </widget>
+                <widget class="FontLabel" row="0" column="1">
+                    <property name="name">
+                        <cstring>le_fRoster</cstring>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+    <customwidget>
+        <class>FontLabel</class>
+        <header location="local">opt_appearance.h</header>
+        <sizehint>
+            <width>5</width>
+            <height>15</height>
+        </sizehint>
+        <container>0</container>
+        <sizepolicy>
+            <hordata>7</hordata>
+            <verdata>4</verdata>
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+        </sizepolicy>
+        <pixmap>image0</pixmap>
+    </customwidget>
+</customwidgets>
+<images>
+    <image name="image0">
+        <data format="PNG" length="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154789cad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a19017a725d8c60000000049454e44ae426082</data>
+    </image>
+</images>
+<tabstops>
+    <tabstop>pb_cOnline</tabstop>
+    <tabstop>pb_cOffline</tabstop>
+    <tabstop>pb_cAway</tabstop>
+    <tabstop>pb_cDND</tabstop>
+    <tabstop>pb_cProfileFore</tabstop>
+    <tabstop>pb_cProfileBack</tabstop>
+    <tabstop>pb_cGroupFore</tabstop>
+    <tabstop>pb_cGroupBack</tabstop>
+    <tabstop>pb_cListBack</tabstop>
+    <tabstop>pb_cAnimFront</tabstop>
+    <tabstop>pb_cAnimBack</tabstop>
+    <tabstop>pb_fRoster</tabstop>
+    <tabstop>pb_fMessage</tabstop>
+    <tabstop>pb_fChat</tabstop>
+    <tabstop>pb_fPopup</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_appearance.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_appearance.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_appearance.cpp	(revision 38)
@@ -0,0 +1,305 @@
+#include "opt_appearance.h"
+#include "opt_iconset.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qslider.h>
+#include <qlabel.h>
+#include <qcombobox.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qcolordialog.h>
+#include <qfontdialog.h>
+#include <qlineedit.h>
+
+#include "opt_appearance-ui.h"
+#include "opt_appearance_misc-ui.h"
+
+//----------------------------------------------------------------------------
+// FontLabel
+//----------------------------------------------------------------------------
+
+FontLabel::FontLabel(QWidget *parent, const char *name)
+	: QLineEdit(parent, name)
+{
+	setReadOnly(true);
+	setPaletteBackgroundColor(parent->paletteBackgroundColor());
+
+	m_defaultHeight = QLineEdit::sizeHint().height();
+}
+
+void FontLabel::setFont(QString fontName)
+{
+	QFont f;
+	f.fromString(fontName);
+	m_font = fontName;
+	setText( tr("%1 %2").arg( f.family() ).arg( f.pointSize() ) );
+	QLineEdit::setFont(f);
+}
+
+QString FontLabel::fontName() const
+{
+	return m_font;
+}
+
+QSize FontLabel::sizeHint() const
+{
+	return QSize(QLineEdit::sizeHint().width(), m_defaultHeight);
+}
+
+
+//----------------------------------------------------------------------------
+// OptionsTabAppearance
+//----------------------------------------------------------------------------
+OptionsTabAppearance::OptionsTabAppearance(QObject *parent) : MetaOptionsTab(parent, "appearance", "", tr("Appearance"), tr("Psi's Appearance"), "psi/appearance")
+{    
+	addTab( new OptionsTabAppearanceGeneral(this) );
+	addTab( new OptionsTabIconsetEmoticons(this) );
+	addTab( new OptionsTabIconsetRoster(this) );
+	addTab( new OptionsTabIconsetSystem(this) );
+	addTab( new OptionsTabAppearanceMisc(this) );
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabAppearanceMisc
+//----------------------------------------------------------------------------
+
+OptionsTabAppearanceMisc::OptionsTabAppearanceMisc(QObject *parent)
+: OptionsTab(parent, "appearance_misc", "", tr("Misc."), tr("Miscellaneous Settings"))
+{
+	w = 0;
+	o = new Options;
+}
+
+OptionsTabAppearanceMisc::~OptionsTabAppearanceMisc()
+{
+	delete o;
+}
+
+QWidget *OptionsTabAppearanceMisc::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new OptAppearanceMiscUI();
+
+#if defined(Q_WS_X11) || (QT_VERSION < 0x030300)
+	OptAppearanceMiscUI *d = (OptAppearanceMiscUI *)w;
+	d->gb_opacity->hide();
+#endif
+
+	return w;
+}
+
+void OptionsTabAppearanceMisc::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptAppearanceMiscUI *d = (OptAppearanceMiscUI *)w;
+
+	opt->clNewHeadings = d->ck_newHeadings->isChecked();	
+	opt->outlineHeadings = d->ck_outlineHeadings->isChecked();	
+	opt->rosterOpacity = d->sl_rosterop->value();
+	opt->chatOpacity = d->sl_chatdlgop->value();
+}
+
+void OptionsTabAppearanceMisc::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptAppearanceMiscUI *d = (OptAppearanceMiscUI *)w;
+
+	d->ck_newHeadings->setChecked( opt->clNewHeadings );
+	d->ck_outlineHeadings->setChecked( opt->outlineHeadings );
+	
+	d->sl_rosterop->setValue( opt->rosterOpacity );
+	d->sl_chatdlgop->setValue( opt->chatOpacity );
+}
+
+void OptionsTabAppearanceMisc::setData(PsiCon *, QWidget *parentDialog)
+{
+	parentWidget = parentDialog;
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabAppearanceGeneral: Fonts & Colours
+//----------------------------------------------------------------------------
+
+OptionsTabAppearanceGeneral::OptionsTabAppearanceGeneral(QObject *parent)
+: OptionsTab(parent, "appearance_general", "", tr("Fonts && Colors"), tr("Fonts && Color Settings"))
+{
+	w = 0;
+	bg_font = 0;
+	bg_color = 0;
+	o = new Options;
+}
+
+OptionsTabAppearanceGeneral::~OptionsTabAppearanceGeneral()
+{
+	if ( bg_font )
+		delete bg_font;
+	if ( bg_color )
+		delete bg_color;
+	delete o;
+}
+
+static QPixmap name2color(QString name) // taken from opt_general.cpp
+{
+	QColor c(name);
+	QPixmap pix(16, 16);
+	QPainter p(&pix);
+
+	p.fillRect(0, 0, pix.width(), pix.height(), QBrush(c));
+	p.setPen( QColor(0, 0, 0) );
+	p.drawRect(0, 0, pix.width(), pix.height());
+	p.end();
+
+	return pix;
+}
+
+QWidget *OptionsTabAppearanceGeneral::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new OptAppearanceUI();
+	OptAppearanceUI *d = (OptAppearanceUI *)w;
+
+	le_font[0] = d->le_fRoster;
+	le_font[1] = d->le_fMessage;
+	le_font[2] = d->le_fChat;
+	le_font[3] = d->le_fPopup;
+
+	bg_font = new QButtonGroup;
+	bg_font->insert(d->pb_fRoster, 0);
+	bg_font->insert(d->pb_fMessage, 1);
+	bg_font->insert(d->pb_fChat, 2);
+	bg_font->insert(d->pb_fPopup, 3);
+	connect(bg_font, SIGNAL(clicked(int)), SLOT(chooseFont(int)));
+
+	QWhatsThis::add(le_font[0],
+		tr("Specifies the font style for the main window."));
+	QWhatsThis::add(le_font[1],
+		tr("Specifies the font style for message windows."));
+	QWhatsThis::add(le_font[2],
+		tr("Specifies the font style for chat windows."));
+	QWhatsThis::add(le_font[3],
+		tr("Specifies the font style for popup windows."));
+	QWhatsThis::add(d->pb_fRoster,
+		tr("Selects a font for the a roster window using the font selection dialog."));
+	QWhatsThis::add(d->pb_fMessage,
+		tr("Selects a font for message windows using the font selection dialog."));
+	QWhatsThis::add(d->pb_fChat,
+		tr("Selects a font for chat windows using the font selection dialog."));
+
+	bg_color = new QButtonGroup;
+	bg_color->insert(d->pb_cOnline, 0);
+	bg_color->insert(d->pb_cOffline, 1);
+	bg_color->insert(d->pb_cAway, 2);
+	bg_color->insert(d->pb_cDND, 3);
+	bg_color->insert(d->pb_cProfileFore, 4);
+	bg_color->insert(d->pb_cProfileBack, 5);
+	bg_color->insert(d->pb_cGroupFore, 6);
+	bg_color->insert(d->pb_cGroupBack, 7);
+	bg_color->insert(d->pb_cListBack, 8);
+	bg_color->insert(d->pb_cAnimFront, 9);
+	bg_color->insert(d->pb_cAnimBack, 10);
+	connect(bg_color, SIGNAL(clicked(int)), SLOT(chooseColor(int)));
+
+	QString s = tr("Specifies the text color for a contact name in the main window when that user is \"%1\".");
+	QWhatsThis::add(d->pb_cOnline,
+		s.arg(tr("online")));
+	QWhatsThis::add(d->pb_cOffline,
+		s.arg(tr("offline")));
+	QWhatsThis::add(d->pb_cAway,
+		s.arg(tr("away")));
+	QWhatsThis::add(d->pb_cDND,
+		s.arg(tr("do not disturb")));
+	QWhatsThis::add(d->pb_cProfileBack,
+		tr("Specifies the background color for an account name in the main window."));
+	QWhatsThis::add(d->pb_cGroupBack,
+		tr("Specifies the background color for a group name in the main window."));
+	QWhatsThis::add(d->pb_cListBack,
+		tr("Specifies the background color for the main window."));
+	QWhatsThis::add(d->pb_cAnimFront,
+		tr("Specifies the foreground animation color for nicks."));
+	QWhatsThis::add(d->pb_cAnimBack,
+		tr("Specifies the background animation color for nicks."));
+
+	// Avatars
+	//QWhatsThis::add(d->ck_avatarsChatdlg,
+	//	tr("Toggles displaying of avatars in the chat dialog"));
+
+	return w;
+}
+
+void OptionsTabAppearanceGeneral::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	//OptAppearanceUI *d = (OptAppearanceUI *)w;
+	//opt->avatarsChatdlgEnabled = d->ck_avatarsChatdlg->isChecked(); // Avatars
+
+	int n;
+	for (n = 0; n < 4; ++n)
+		opt->font[n] = le_font[n]->fontName();
+
+	for (n = 0; n < 11; ++n)
+		opt->color[n] = o->color[n];
+}
+
+void OptionsTabAppearanceGeneral::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	//OptAppearanceUI *d = (OptAppearanceUI *)w;
+	//d->ck_avatarsChatdlg->setChecked( opt->avatarsChatdlgEnabled ); // Avatars
+
+	int n;
+	for (n = 0; n < 4; ++n)
+		le_font[n]->setFont(opt->font[n]);
+
+	for (n = 0; n < 11; ++n) {
+		o->color[n] = opt->color[n];
+		((QPushButton *)bg_color->find(n))->setPixmap(name2color(opt->color[n].name()));
+	}
+}
+
+void OptionsTabAppearanceGeneral::setData(PsiCon *, QWidget *parentDialog)
+{
+	parentWidget = parentDialog;
+}
+
+void OptionsTabAppearanceGeneral::chooseFont(int x)
+{
+	bool ok;
+	QFont font;
+	font.fromString( le_font[x]->fontName() );
+
+	QString fnt = QFontDialog::getFont(&ok, font, parentWidget).toString();
+	le_font[x]->setFont(fnt);
+
+	if(ok)
+		emit dataChanged();
+}
+
+void OptionsTabAppearanceGeneral::chooseColor(int x)
+{
+	QColor c = o->color[x];
+
+	c = QColorDialog::getColor(c, parentWidget);
+	if(c.isValid()) {
+		o->color[x] = c;
+		((QPushButton *)bg_color->find(x))->setPixmap(name2color(o->color[x].name()));
+
+		emit dataChanged();
+	}
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_appearance.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_appearance.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_appearance.h	(revision 38)
@@ -0,0 +1,79 @@
+#ifndef OPT_APPEARANCEGENERAL_H
+#define OPT_APPEARANCEGENERAL_H
+
+#include "optionstab.h"
+
+#include <qlineedit.h>
+
+class FontLabel : public QLineEdit
+{
+	Q_OBJECT
+public:
+	FontLabel(QWidget *parent = 0, const char *name = 0);
+
+	void setFont(QString);
+	QString fontName() const;
+
+	QSize sizeHint() const;
+
+private:
+	QString m_font;
+	int m_defaultHeight;
+};
+
+class QWidget;
+struct Options;
+class QButtonGroup;
+class QLineEdit;
+
+class OptionsTabAppearance : public MetaOptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabAppearance(QObject *parent);
+};
+
+class OptionsTabAppearanceMisc : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabAppearanceMisc(QObject *parent);
+	~OptionsTabAppearanceMisc();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private slots:
+	void setData(PsiCon *, QWidget *);
+
+private:
+	QWidget *w, *parentWidget;
+	Options *o;
+};
+
+class OptionsTabAppearanceGeneral : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabAppearanceGeneral(QObject *parent);
+	~OptionsTabAppearanceGeneral();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private slots:
+	void setData(PsiCon *, QWidget *);
+	void chooseColor(int);
+	void chooseFont(int);
+
+private:
+	QWidget *w, *parentWidget;
+	QButtonGroup *bg_color;
+	FontLabel *le_font[4];
+	QButtonGroup *bg_font;
+	Options *o;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_appearance_misc-ui.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_appearance_misc-ui.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_appearance_misc-ui.ui	(revision 38)
@@ -0,0 +1,165 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>OptAppearanceMiscUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>OptAppearanceMiscUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>354</width>
+            <height>189</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>OptAppearanceMiscUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox4</cstring>
+            </property>
+            <property name="title">
+                <string>Headings</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_outlineHeadings</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Outline headings</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_newHeadings</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Slim group headings</string>
+                    </property>
+                </widget>
+            </vbox>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>gb_opacity</cstring>
+            </property>
+            <property name="title">
+                <string>Opacity</string>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel" row="2" column="2">
+                    <property name="name">
+                        <cstring>lb_chatdlgop_val</cstring>
+                    </property>
+                    <property name="minimumSize">
+                        <size>
+                            <width>30</width>
+                            <height>0</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string>100</string>
+                    </property>
+                    <property name="alignment">
+                        <set>AlignCenter</set>
+                    </property>
+                </widget>
+                <widget class="QSlider" row="2" column="1">
+                    <property name="name">
+                        <cstring>sl_chatdlgop</cstring>
+                    </property>
+                    <property name="minValue">
+                        <number>10</number>
+                    </property>
+                    <property name="maxValue">
+                        <number>100</number>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0" rowspan="2" colspan="1">
+                    <property name="name">
+                        <cstring>lb_chatdlgop</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Chat Dialog Opacity:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="2">
+                    <property name="name">
+                        <cstring>lb_rosterop_val</cstring>
+                    </property>
+                    <property name="minimumSize">
+                        <size>
+                            <width>30</width>
+                            <height>0</height>
+                        </size>
+                    </property>
+                    <property name="text">
+                        <string>100</string>
+                    </property>
+                    <property name="alignment">
+                        <set>AlignCenter</set>
+                    </property>
+                </widget>
+                <widget class="QSlider" row="0" column="1" rowspan="2" colspan="1">
+                    <property name="name">
+                        <cstring>sl_rosterop</cstring>
+                    </property>
+                    <property name="minValue">
+                        <number>10</number>
+                    </property>
+                    <property name="maxValue">
+                        <number>100</number>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>lb_rosterop</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Roster Opacity:</string>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>sl_rosterop</sender>
+        <signal>valueChanged(int)</signal>
+        <receiver>lb_rosterop_val</receiver>
+        <slot>setNum(int)</slot>
+    </connection>
+    <connection>
+        <sender>sl_chatdlgop</sender>
+        <signal>valueChanged(int)</signal>
+        <receiver>lb_chatdlgop_val</receiver>
+        <slot>setNum(int)</slot>
+    </connection>
+</connections>
+<tabstops>
+    <tabstop>ck_outlineHeadings</tabstop>
+    <tabstop>ck_newHeadings</tabstop>
+    <tabstop>sl_rosterop</tabstop>
+    <tabstop>sl_chatdlgop</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_application-ui.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_application-ui.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_application-ui.ui	(revision 38)
@@ -0,0 +1,328 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>OptApplicationUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>OptApplicationUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>294</width>
+            <height>500</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>OptApplicationUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_alwaysOnTop</cstring>
+            </property>
+            <property name="text">
+                <string>Roster window always on top</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autoRosterSize</cstring>
+            </property>
+            <property name="text">
+                <string>Auto-resize contact list</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_keepSizes</cstring>
+            </property>
+            <property name="text">
+                <string>Remember Window Sizes</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_useleft</cstring>
+            </property>
+            <property name="enabled">
+                <bool>true</bool>
+            </property>
+            <property name="text">
+                <string>Use left-click for menu</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_hideMenubar</cstring>
+            </property>
+            <property name="text">
+                <string>Hide menubar</string>
+            </property>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>gb_links</cstring>
+            </property>
+            <property name="title">
+                <string>Links</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>11</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>Layout18</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>0</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>TextLabel2_3</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Open With:</string>
+                            </property>
+                        </widget>
+                        <widget class="QComboBox">
+                            <property name="name">
+                                <cstring>cb_link</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>7</hsizetype>
+                                    <vsizetype>0</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+                <widget class="QGroupBox">
+                    <property name="name">
+                        <cstring>gb_linkCustom</cstring>
+                    </property>
+                    <property name="title">
+                        <string>Custom</string>
+                    </property>
+                    <grid>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>11</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLineEdit" row="1" column="1">
+                            <property name="name">
+                                <cstring>le_linkMailer</cstring>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit" row="0" column="1">
+                            <property name="name">
+                                <cstring>le_linkBrowser</cstring>
+                            </property>
+                        </widget>
+                        <widget class="QLabel" row="1" column="0">
+                            <property name="name">
+                                <cstring>TextLabel4</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Mailer</string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel" row="0" column="0">
+                            <property name="name">
+                                <cstring>TextLabel3_2</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Browser</string>
+                            </property>
+                        </widget>
+                    </grid>
+                </widget>
+            </vbox>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_docklet</cstring>
+            </property>
+            <property name="text">
+                <string>Enable Docklet</string>
+            </property>
+            <property name="checked">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>gb_docklet</cstring>
+            </property>
+            <property name="title">
+                <string>Docklet</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_dockDCstyle</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Use "Double-click" style (like Mirabilis ICQ)</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_dockHideMW</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Hide roster window on startup</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_dockToolMW</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Make roster window a "Tool-Window"</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_dockUseWM</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Use WindowMaker docklet</string>
+                    </property>
+                </widget>
+            </vbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout15</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>textLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Data Transfer Port:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit">
+                    <property name="name">
+                        <cstring>le_dtPort</cstring>
+                    </property>
+                    <property name="minimumSize">
+                        <size>
+                            <width>56</width>
+                            <height>0</height>
+                        </size>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>56</width>
+                            <height>32767</height>
+                        </size>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer10</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>40</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+            </hbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout3</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>textLabel2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Data Transfer External Address:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit">
+                    <property name="name">
+                        <cstring>le_dtExternal</cstring>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>ck_docklet</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>gb_docklet</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+</connections>
+<tabstops>
+    <tabstop>ck_alwaysOnTop</tabstop>
+    <tabstop>ck_autoRosterSize</tabstop>
+    <tabstop>ck_keepSizes</tabstop>
+    <tabstop>ck_useleft</tabstop>
+    <tabstop>ck_hideMenubar</tabstop>
+    <tabstop>cb_link</tabstop>
+    <tabstop>le_linkBrowser</tabstop>
+    <tabstop>le_linkMailer</tabstop>
+    <tabstop>ck_docklet</tabstop>
+    <tabstop>ck_dockDCstyle</tabstop>
+    <tabstop>ck_dockHideMW</tabstop>
+    <tabstop>ck_dockToolMW</tabstop>
+    <tabstop>ck_dockUseWM</tabstop>
+    <tabstop>le_dtPort</tabstop>
+    <tabstop>le_dtExternal</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_application.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_application.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_application.cpp	(revision 38)
@@ -0,0 +1,242 @@
+#include "opt_application.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qgroupbox.h>
+
+#include "opt_application-ui.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabApplication
+//----------------------------------------------------------------------------
+
+OptionsTabApplication::OptionsTabApplication(QObject *parent)
+: OptionsTab(parent, "application", "", tr("Application"), tr("General application options"), "psi/psi16")
+{
+	w = 0;
+}
+
+OptionsTabApplication::~OptionsTabApplication()
+{
+}
+
+QWidget *OptionsTabApplication::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new OptApplicationUI();
+	OptApplicationUI *d = (OptApplicationUI *)w;
+
+	QWhatsThis::add(d->ck_alwaysOnTop,
+		tr("Makes the main Psi window always be in front of other windows."));
+	QWhatsThis::add(d->ck_autoRosterSize,
+		tr("Makes the main Psi window resize automatically to fit all contacts."));
+	QWhatsThis::add(d->ck_keepSizes,
+		tr("Makes Psi remember window size and positions for chats and messages."
+		"  If disabled, the windows will always appear in their default positions and sizes."));
+	QWhatsThis::add(d->ck_useleft,
+		tr("Normally, right-clicking with the mouse on a contact will activate the context-menu."
+		"  Check this option if you'd rather use a left-click."));
+	QWhatsThis::add(d->ck_hideMenubar,
+		tr("Hides the menubar in the application window."));
+	connect(d->ck_hideMenubar, SIGNAL(toggled(bool)), this, SLOT(hideMenubarToggled(bool)));
+
+	// links
+	QWhatsThis::add(d->cb_link,
+		tr("Selects what applications to use for handling URLs and e-mail addresses."
+		"  You can choose between the system default and custom applications."));
+
+	QString s = tr("Enter the path to the application's executable and choose \"Custom\" in the list above.");
+	QWhatsThis::add(d->le_linkBrowser,
+		tr("Specify what custom browser application to use for handling URLs here.") + "  " + s);
+	QWhatsThis::add(d->le_linkMailer,
+		tr("Specify what custom mailer application to use for handling e-mail addresses here.") + "  " + s);
+
+	// docklet
+	QWhatsThis::add(d->ck_docklet,
+		tr("Makes Psi use a docklet icon, also known as system tray icon."));
+	QWhatsThis::add(d->ck_dockDCstyle,
+		tr("Normally, single-clicking on the Psi docklet icon brings the main window to"
+		" the foreground.  Check this option if you would rather use a double-click."));
+	QWhatsThis::add(d->ck_dockHideMW,
+		tr("Starts Psi with only the docklet icon visible."));
+	QWhatsThis::add(d->ck_dockToolMW,
+		tr("Prevents Psi from taking up a slot on the taskbar and makes the main "
+		"window use a small titlebar."));
+	QWhatsThis::add(d->ck_dockUseWM,
+		tr("If checked, Psi will use the Window Maker docklet instead of FreeDesktop one."));
+
+#ifdef Q_WS_MAC
+	d->ck_alwaysOnTop->hide();
+	d->ck_hideMenubar->hide();
+	d->gb_links->hide();
+	d->ck_docklet->hide();
+	d->gb_docklet->hide();
+#endif
+#ifdef Q_WS_PM
+	/// @todo (dmik) disable "always on top" until implemented in Qt/2  
+	d->ck_alwaysOnTop->hide();
+	/// @todo (dmik) disable the docklet until the xCenter plugin is implemented
+	d->ck_docklet->hide();
+	d->gb_docklet->hide();
+#endif
+#ifndef Q_WS_X11
+	d->ck_dockUseWM->hide();
+#endif
+
+	return w;
+}
+
+#ifdef Q_WS_X11
+static int om_x11browse[] = { 0, 2, 1 };
+#endif
+
+void OptionsTabApplication::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptApplicationUI *d = (OptApplicationUI *)w;
+
+	opt->alwaysOnTop = d->ck_alwaysOnTop->isChecked();
+	opt->autoRosterSize = d->ck_autoRosterSize->isChecked();
+	opt->keepSizes   = d->ck_keepSizes->isChecked();
+	opt->useleft = d->ck_useleft->isChecked();
+	opt->hideMenubar = d->ck_hideMenubar->isChecked();
+
+	// links
+#ifdef Q_WS_X11
+	opt->browser = om_x11browse[ d->cb_link->currentItem() ];
+#else
+	opt->browser = d->cb_link->currentItem();
+#endif
+	opt->customBrowser = d->le_linkBrowser->text();
+	opt->customMailer  = d->le_linkMailer->text();
+
+	// docklet
+	opt->useDock = d->ck_docklet->isChecked();
+	opt->dockDCstyle = d->ck_dockDCstyle->isChecked();
+	opt->dockHideMW = d->ck_dockHideMW->isChecked();
+	opt->dockToolMW = d->ck_dockToolMW->isChecked();
+	opt->isWMDock  = d->ck_dockUseWM->isChecked();
+
+	// data transfer
+	opt->dtPort = d->le_dtPort->text().toInt();
+	opt->dtExternal = d->le_dtExternal->text();
+}
+
+void OptionsTabApplication::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptApplicationUI *d = (OptApplicationUI *)w;
+
+	d->ck_alwaysOnTop->setChecked( opt->alwaysOnTop );
+	d->ck_autoRosterSize->setChecked( opt->autoRosterSize );
+	d->ck_keepSizes->setChecked( opt->keepSizes );
+	d->ck_hideMenubar->setChecked( opt->hideMenubar );
+	d->ck_useleft->setChecked( opt->useleft );
+
+	// links
+	connect(d->cb_link, SIGNAL(activated(int)), SLOT(selectBrowser(int)));
+#ifdef Q_WS_WIN
+	d->cb_link->insertItem(tr("Windows Default Browser/Mail"));
+	d->cb_link->insertItem(tr("Custom"));
+	d->cb_link->setCurrentItem( opt->browser );
+	selectBrowser( opt->browser );
+#endif
+#ifdef Q_WS_PM
+	d->cb_link->insertItem(tr("PM Default Browser/Mail"));
+	d->cb_link->insertItem(tr("Custom"));
+	d->cb_link->setCurrentItem( opt->browser );
+	selectBrowser( opt->browser );
+#endif
+#ifdef Q_WS_X11
+	d->cb_link->insertItem(tr("KDE Default Browser/Mail"));
+	d->cb_link->insertItem(tr("GNOME2 Default Browser/Mail"));
+	d->cb_link->insertItem(tr("Custom"));
+	int rbi = om_x11browse[ opt->browser ];
+	d->cb_link->setCurrentItem( rbi );
+	selectBrowser( rbi );
+#endif
+#ifdef Q_WS_MAC
+	d->cb_link->insertItem(tr("Mac OS Default Browser/Mail"));
+	d->cb_link->setCurrentItem( opt->browser );
+	selectBrowser( opt->browser );
+#endif
+	d->le_linkBrowser->setText( opt->customBrowser );
+	d->le_linkMailer->setText( opt->customMailer );
+
+	// docklet
+	d->ck_docklet->setChecked( opt->useDock );
+	d->ck_dockDCstyle->setChecked( opt->dockDCstyle );
+	d->ck_dockHideMW->setChecked( opt->dockHideMW );
+	d->ck_dockToolMW->setChecked( opt->dockToolMW );
+	d->ck_dockUseWM->setChecked( opt->isWMDock );
+
+	// data transfer
+	d->le_dtPort->setText( QString::number(opt->dtPort) );
+	d->le_dtExternal->setText( opt->dtExternal );
+}
+
+void OptionsTabApplication::selectBrowser(int x)
+{
+	if ( !w )
+		return;
+
+	bool enableCustom = TRUE;
+
+#ifdef Q_WS_WIN
+	if(x == 0)
+		enableCustom = FALSE;
+#endif
+#ifdef Q_WS_PM
+	if(x == 0)
+		enableCustom = FALSE;
+#endif
+#ifdef Q_WS_X11
+	if(x == 0 || x == 1)
+		enableCustom = FALSE;
+#endif
+#ifdef Q_WS_MAC
+	if(x == 0)
+		enableCustom = FALSE;
+#endif
+
+	OptApplicationUI *d = (OptApplicationUI *)w;
+	d->gb_linkCustom->setEnabled(enableCustom);
+}
+
+#ifndef Q_WS_MAC
+void OptionsTabApplication::hideMenubarToggled(bool toggle)
+{
+	if (toggle == true) {
+		// check if all toolbars are disabled
+		bool toolbarsVisible = false;
+		QValueList<Options::ToolbarPrefs>::ConstIterator it = option.toolbars["mainWin"].begin();
+		for ( ; it != option.toolbars["mainWin"].end() && !toolbarsVisible; ++it) {
+			toolbarsVisible = toolbarsVisible || (*it).on;
+		}
+
+		// Check whether it is legal to disable the menubar
+		if ( !toolbarsVisible ) {
+			QMessageBox::warning(0, tr("Warning"),
+				tr("You can not disable <i>all</i> toolbars <i>and</i> the menubar. If you do so, you will be unable to enable them back, when you'll change your mind.\n"
+					"<br><br>\n"
+					"If you really-really want to disable all toolbars and the menubar, you need to edit the config.xml file by hand."),
+				tr("I understand"));
+			((OptApplicationUI *)w)->ck_hideMenubar->setChecked(false);
+		}
+	}
+}
+#else
+void OptionsTabApplication::hideMenubarToggled(bool)
+{
+}
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_application.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_application.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_application.h	(revision 38)
@@ -0,0 +1,29 @@
+#ifndef OPT_APPLICATION_H
+#define OPT_APPLICATION_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+class QButtonGroup;
+
+class OptionsTabApplication : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabApplication(QObject *parent);
+	~OptionsTabApplication();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private slots:
+	void selectBrowser(int);
+	void hideMenubarToggled(bool);
+
+private:
+	QWidget *w;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_chat-ui.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_chat-ui.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_chat-ui.ui	(revision 38)
@@ -0,0 +1,158 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>OptChatUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>OptChatUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>319</width>
+            <height>375</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>OptChatUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_chatSays</cstring>
+            </property>
+            <property name="text">
+                <string>Contact "says" instead of IRC style</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_chatSoftReturn</cstring>
+            </property>
+            <property name="text">
+                <string>Enter key sends chat messages</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_alertOpenChats</cstring>
+            </property>
+            <property name="text">
+                <string>Alert chat messages even if chat window is open</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_raiseChatWindow</cstring>
+            </property>
+            <property name="text">
+                <string>Raise chat window on receiving new messages</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_smallChats</cstring>
+            </property>
+            <property name="text">
+                <string>Open chats in compact mode</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_tabChats</cstring>
+            </property>
+            <property name="text">
+                <string>Open chats in tabbed mode</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autoResize</cstring>
+            </property>
+            <property name="text">
+                <string>Auto-resize text input field</string>
+            </property>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox18</cstring>
+            </property>
+            <property name="title">
+                <string>Default Action</string>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_defActMsg</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Message</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_defActChat</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Chat</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox17</cstring>
+            </property>
+            <property name="title">
+                <string>Delete chat window contents (end conversation)</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_delChatsClose</cstring>
+                    </property>
+                    <property name="text">
+                        <string>When chat window is closed</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_delChatsHour</cstring>
+                    </property>
+                    <property name="text">
+                        <string>One hour after chat window was closed</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_delChatsDay</cstring>
+                    </property>
+                    <property name="text">
+                        <string>One day after chat window was closed</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_delChatsNever</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Never</string>
+                    </property>
+                </widget>
+            </vbox>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_chat.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_chat.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_chat.cpp	(revision 38)
@@ -0,0 +1,129 @@
+#include "opt_chat.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+
+#include "opt_chat-ui.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabChat
+//----------------------------------------------------------------------------
+
+OptionsTabChat::OptionsTabChat(QObject *parent)
+: OptionsTab(parent, "chat", "", tr("Chat"), tr("Configure the chat dialog"), "psi/start-chat")
+{
+	w = 0;
+	bg_delChats = bg_defAct = 0;
+}
+
+OptionsTabChat::~OptionsTabChat()
+{
+	if ( bg_defAct )
+		delete bg_defAct;
+	if ( bg_delChats )
+		delete bg_delChats;
+}
+
+QWidget *OptionsTabChat::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new OptChatUI();
+	OptChatUI *d = (OptChatUI *)w;
+
+	bg_defAct = new QButtonGroup;
+	bg_defAct->setRadioButtonExclusive( true );
+	bg_defAct->insert( d->rb_defActMsg,  0 );
+	bg_defAct->insert( d->rb_defActChat, 1 );
+
+	bg_delChats = new QButtonGroup;
+	bg_delChats->setRadioButtonExclusive( true );
+	bg_delChats->insert( d->rb_delChatsClose, 0 );
+	bg_delChats->insert( d->rb_delChatsHour,  1 );
+	bg_delChats->insert( d->rb_delChatsDay,   2 );
+	bg_delChats->insert( d->rb_delChatsNever, 3 );
+
+	QWhatsThis::add(d->rb_defActMsg,
+		tr("Make the default action open a normal message window."));
+	QWhatsThis::add(d->rb_defActChat,
+		tr("Make the default action open a chat window."));
+	QWhatsThis::add(d->ck_chatSays,
+		tr("<P>Changes the normal chat style from:</P>"
+		"<P>[01:23:45] &lt;MyName&gt; Hi</P>"
+		"<P>[01:23:56] &lt;YourName&gt; How are you?</P>"
+		"<P>to:</P>"
+		"<P>[01:23:45] MyName says:</P>"
+		"<P>Hi</P>"
+		"<P>[01:23:56] YourName says:</P>"
+		"<P>How are you?</P>"));
+	QWhatsThis::add(d->ck_chatSoftReturn,
+		tr("<P>When checked, pressing Enter in a chat window will send your message."
+		   "  You must use Shift+Enter in order to create a newline in the chat message."
+		   "  If unchecked, messages are sent by pressing Alt-S or Control-Enter, just as they are with regular messages.</P>"));
+	QWhatsThis::add(d->ck_alertOpenChats,
+		tr("Normally, Psi will not alert you when a new chat message"
+		" is received in a chat window that is already open."
+		"  Check this option if you want to receive these alerts anyway."));
+	QWhatsThis::add(d->ck_raiseChatWindow,
+		tr("Makes Psi bring an open chat window to the front of your screen when you receive a new message."
+		" It does not take the keyboard focus, so it will not interfere with your work."));
+	QWhatsThis::add(d->ck_smallChats,
+		tr("Makes Psi open chat windows in compact mode."));
+	QWhatsThis::add(d->ck_tabChats,
+		tr("Makes Psi open chats in a tabbed window."));
+	QString s = tr("<P>Controls how long the chat log will be kept in memory after the"
+		" chat window is closed.</P>");
+	QWhatsThis::add(d->rb_delChatsClose, s +
+		tr("<P>This option does not keep the chat log in memory.</P>"));
+	QWhatsThis::add(d->rb_delChatsHour, s +
+		tr("<P>This option keeps the chat log for 1 hour before deleting it.</P>"));
+	QWhatsThis::add(d->rb_delChatsDay, s +
+		tr("<P>This option keeps the chat log for 1 day before deleting it.</P>"));
+	QWhatsThis::add(d->rb_delChatsNever, s +
+		tr("<P>This options keeps the chat log forever.</P>"));
+
+	return w;
+}
+
+void OptionsTabChat::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptChatUI *d = (OptChatUI *)w;
+
+	opt->defaultAction   = bg_defAct->id(bg_defAct->selected());
+	opt->chatSays        = d->ck_chatSays->isChecked();
+	opt->chatSoftReturn  = d->ck_chatSoftReturn->isChecked();
+	opt->alertOpenChats  = d->ck_alertOpenChats->isChecked();
+	opt->raiseChatWindow = d->ck_raiseChatWindow->isChecked();
+	opt->oldSmallChats 	 = opt->smallChats;
+	opt->smallChats      = d->ck_smallChats->isChecked();
+	opt->delChats        = bg_delChats->id( bg_delChats->selected() );
+	opt->useTabs		 = d->ck_tabChats->isChecked();
+	opt->chatLineEdit    = d->ck_autoResize->isChecked();
+}
+
+void OptionsTabChat::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptChatUI *d = (OptChatUI *)w;
+
+	bg_defAct->setButton( opt->defaultAction );
+	d->ck_chatSays->setChecked( opt->chatSays );
+	d->ck_chatSoftReturn->setChecked( opt->chatSoftReturn );
+	d->ck_alertOpenChats->setChecked( opt->alertOpenChats );
+	d->ck_raiseChatWindow->setChecked( opt->raiseChatWindow );
+	d->ck_smallChats->setChecked( opt->smallChats );
+	d->ck_tabChats->setChecked( opt->useTabs );
+	d->ck_autoResize->setChecked( opt->chatLineEdit );
+	bg_delChats->setButton( opt->delChats );
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_chat.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_chat.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_chat.h	(revision 38)
@@ -0,0 +1,26 @@
+#ifndef OPT_CHAT_H
+#define OPT_CHAT_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+class QButtonGroup;
+
+class OptionsTabChat : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabChat(QObject *parent);
+	~OptionsTabChat();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private:
+	QWidget *w;
+	QButtonGroup *bg_defAct, *bg_delChats;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_events-ui.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_events-ui.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_events-ui.ui	(revision 38)
@@ -0,0 +1,283 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>OptEventsUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>OptEventsUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>351</width>
+            <height>532</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>OptEventsUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_popupMsgs</cstring>
+            </property>
+            <property name="text">
+                <string>Auto-open New Messages</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_popupHeadlines</cstring>
+            </property>
+            <property name="text">
+                <string>Auto-open New Headlines</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_popupFiles</cstring>
+            </property>
+            <property name="text">
+                <string>Auto-open New Files</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_allowAwayPopup</cstring>
+            </property>
+            <property name="text">
+                <string>Allow auto-open even when Away/XA</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_allowUnlistedPopup</cstring>
+            </property>
+            <property name="text">
+                <string>Allow auto-open even from unlisted users</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_raise</cstring>
+            </property>
+            <property name="text">
+                <string>Raise Roster Window On New Event</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_ignoreNonRoster</cstring>
+            </property>
+            <property name="text">
+                <string>Ignore events from contacts not already in your roster</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autoAuth</cstring>
+            </property>
+            <property name="text">
+                <string>Auto-authorize contacts</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_notifyAuth</cstring>
+            </property>
+            <property name="text">
+                <string>Notify when authorization was received</string>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout3</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_bounce</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Bounce Dock:</string>
+                    </property>
+                </widget>
+                <widget class="QComboBox">
+                    <item>
+                        <property name="text">
+                            <string>Never</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Once</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Forever</string>
+                        </property>
+                    </item>
+                    <property name="name">
+                        <cstring>cb_bounce</cstring>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QButtonGroup">
+            <property name="name">
+                <cstring>bg_alerts</cstring>
+            </property>
+            <property name="title">
+                <string>Alerts</string>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>11</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_aSolid</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Solid</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_aBlink</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Blink</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_aAnimate</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Animate</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_popupOn</cstring>
+            </property>
+            <property name="text">
+                <string>Enable popup notifications</string>
+            </property>
+            <property name="checked">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox1</cstring>
+            </property>
+            <property name="title">
+                <string>Popup Notifications</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnMessage</cstring>
+                    </property>
+                    <property name="text">
+                        <string>On incoming Message event</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnHeadline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>On incoming Headline event</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnFile</cstring>
+                    </property>
+                    <property name="text">
+                        <string>On incoming File event</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnOnline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>When a contact becomes available</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnOffline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>When a contact becomes unavailable</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnStatus</cstring>
+                    </property>
+                    <property name="text">
+                        <string>When a contact changes status</string>
+                    </property>
+                </widget>
+            </vbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>ck_popupOn</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>groupBox1</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+</connections>
+<tabstops>
+    <tabstop>ck_popupMsgs</tabstop>
+    <tabstop>ck_popupHeadlines</tabstop>
+    <tabstop>ck_popupFiles</tabstop>
+    <tabstop>ck_allowAwayPopup</tabstop>
+    <tabstop>ck_allowUnlistedPopup</tabstop>
+    <tabstop>ck_raise</tabstop>
+    <tabstop>ck_ignoreNonRoster</tabstop>
+    <tabstop>ck_autoAuth</tabstop>
+    <tabstop>ck_notifyAuth</tabstop>
+    <tabstop>cb_bounce</tabstop>
+    <tabstop>rb_aSolid</tabstop>
+    <tabstop>rb_aBlink</tabstop>
+    <tabstop>rb_aAnimate</tabstop>
+    <tabstop>ck_popupOn</tabstop>
+    <tabstop>ck_popupOnMessage</tabstop>
+    <tabstop>ck_popupOnHeadline</tabstop>
+    <tabstop>ck_popupOnFile</tabstop>
+    <tabstop>ck_popupOnOnline</tabstop>
+    <tabstop>ck_popupOnOffline</tabstop>
+    <tabstop>ck_popupOnStatus</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_events.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_events.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_events.cpp	(revision 38)
@@ -0,0 +1,131 @@
+#include "opt_events.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qbuttongroup.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qlabel.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+
+#include "opt_events-ui.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabEvents
+//----------------------------------------------------------------------------
+
+OptionsTabEvents::OptionsTabEvents(QObject *parent)
+: OptionsTab(parent, "events", "", tr("Events"), tr("The events behaviour"), "psi/events")
+{
+	w = 0;
+}
+
+QWidget *OptionsTabEvents::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new OptEventsUI();
+	OptEventsUI *d = (OptEventsUI *)w;
+	connect(d->bg_alerts, SIGNAL(clicked(int)), SLOT(selectAlertStyle(int)));
+
+	QWhatsThis::add(d->ck_popupMsgs,
+		tr("Makes new incoming message windows pop up automatically when received."));
+	QWhatsThis::add(d->ck_popupHeadlines,
+		tr("Makes new incoming headlines pop up automatically when received."));
+	QWhatsThis::add(d->ck_popupFiles,
+		tr("Makes new incoming file requests pop up automatically when received."));
+	QWhatsThis::add(d->ck_allowAwayPopup,
+		tr("Normally, Psi will not autopopup events when you are away.  "
+		"Set this option if you want them to popup anyway."));
+	QWhatsThis::add(d->ck_allowUnlistedPopup,
+		tr("Normally, Psi will not autopopup events from users not in your roster.  "
+		"Set this option if you want them to popup anyway."));
+	QWhatsThis::add(d->ck_raise,
+		tr("Makes new incoming events bring the main window to the foreground."));
+	QWhatsThis::add(d->ck_ignoreNonRoster,
+		tr("Makes Psi ignore all incoming events from contacts"
+		" not already in your list of contacts."));
+	QWhatsThis::add(d->rb_aSolid,
+		tr("Does not animate or blink incoming event icons on the main window as they are received."));
+	QWhatsThis::add(d->rb_aBlink,
+		tr("Makes all incoming event icons blink on the main window as events are received."));
+	QWhatsThis::add(d->rb_aAnimate,
+		tr("Animates incoming event icons on the main window as events are recieved."));
+	QWhatsThis::add(d->ck_autoAuth,
+		tr("Makes Psi automatically accept all authorization requests from <b>anyone</b>."));
+	QWhatsThis::add(d->ck_notifyAuth,
+		tr("Makes Psi notify you when your authorization request was approved."));
+
+#ifndef Q_WS_MAC
+	d->cb_bounce->hide();
+	d->lb_bounce->hide();
+#endif
+
+	return w;
+}
+
+void OptionsTabEvents::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptEventsUI *d = (OptEventsUI *)w;
+	opt->popupMsgs  = d->ck_popupMsgs->isChecked();
+	opt->popupChats = d->ck_popupMsgs->isChecked();
+	opt->popupHeadlines = d->ck_popupHeadlines->isChecked();
+	opt->popupFiles = d->ck_popupFiles->isChecked();
+	opt->noAwayPopup = !d->ck_allowAwayPopup->isChecked();
+	opt->noUnlistedPopup = !d->ck_allowUnlistedPopup->isChecked();
+	opt->raise = d->ck_raise->isChecked();
+	opt->ignoreNonRoster = d->ck_ignoreNonRoster->isChecked();
+	opt->alertStyle = alertStyle;
+	opt->autoAuth = d->ck_autoAuth->isChecked();
+	opt->notifyAuth = d->ck_notifyAuth->isChecked();
+	opt->bounceDock = (Options::BounceDockSetting) d->cb_bounce->currentItem();
+
+	opt->ppIsOn = d->ck_popupOn->isChecked();
+	opt->ppMessage = d->ck_popupOnMessage->isChecked();
+	opt->ppChat    = d->ck_popupOnMessage->isChecked();
+	opt->ppHeadline = d->ck_popupOnHeadline->isChecked();
+	opt->ppFile    = d->ck_popupOnFile->isChecked();
+	opt->ppOnline  = d->ck_popupOnOnline->isChecked();
+	opt->ppOffline = d->ck_popupOnOffline->isChecked();
+	opt->ppStatus  = d->ck_popupOnStatus->isChecked();
+}
+
+void OptionsTabEvents::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptEventsUI *d = (OptEventsUI *)w;
+	d->ck_popupMsgs->setChecked( opt->popupMsgs || opt->popupChats );
+	d->ck_popupHeadlines->setChecked( opt->popupHeadlines );
+	d->ck_popupFiles->setChecked( opt->popupFiles );
+	d->ck_allowAwayPopup->setChecked( !opt->noAwayPopup );
+	d->ck_allowUnlistedPopup->setChecked( !opt->noUnlistedPopup );
+	d->ck_raise->setChecked( opt->raise );
+	d->ck_ignoreNonRoster->setChecked( opt->ignoreNonRoster );
+	alertStyle = opt->alertStyle;
+	d->bg_alerts->setButton( alertStyle );
+	d->ck_autoAuth->setChecked( opt->autoAuth );
+	d->ck_notifyAuth->setChecked( opt->notifyAuth );
+	d->cb_bounce->setCurrentItem( opt->bounceDock );
+
+	d->ck_popupOn->setChecked( true );
+	d->ck_popupOn->setChecked( opt->ppIsOn );
+	d->ck_popupOnMessage->setChecked( opt->ppMessage || opt->ppChat );
+	d->ck_popupOnHeadline->setChecked( opt->ppHeadline );
+	d->ck_popupOnFile->setChecked( opt->ppFile );
+	d->ck_popupOnOnline->setChecked( opt->ppOnline );
+	d->ck_popupOnOffline->setChecked( opt->ppOffline );
+	d->ck_popupOnStatus->setChecked( opt->ppStatus );
+}
+
+void OptionsTabEvents::selectAlertStyle(int a)
+{
+	alertStyle = a;
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_events.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_events.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_events.h	(revision 38)
@@ -0,0 +1,27 @@
+#ifndef OPT_EVENTS_H
+#define OPT_EVENTS_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+
+class OptionsTabEvents : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabEvents(QObject *parent);
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private slots:
+	void selectAlertStyle(int);
+
+private:
+	QWidget *w;
+	int alertStyle;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_events_misc.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_events_misc.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_events_misc.ui	(revision 38)
@@ -0,0 +1,84 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>EventsMiscUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>EventsMiscUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>350</width>
+            <height>160</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string></string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_ignoreHeadline</cstring>
+            </property>
+            <property name="text">
+                <string>Ignore "Headline" events (e.g. MSN alerts)</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_ignoreNonRoster</cstring>
+            </property>
+            <property name="text">
+                <string>Ignore events from contacts not already in your roster</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_scrollTo</cstring>
+            </property>
+            <property name="text">
+                <string>Scroll roster to contact on event</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_alertOpenChats</cstring>
+            </property>
+            <property name="text">
+                <string>Alert chat messages even if chat window is open</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_raiseChatWindow</cstring>
+            </property>
+            <property name="text">
+                <string>Raise chat window on receiving new messages</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_showSubjects</cstring>
+            </property>
+            <property name="text">
+                <string>Show subject line in events</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_showCounter</cstring>
+            </property>
+            <property name="text">
+                <string>Show character counter</string>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_events_receive.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_events_receive.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_events_receive.ui	(revision 38)
@@ -0,0 +1,152 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>EventsReceiveUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>EventsReceiveUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>284</width>
+            <height>241</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string></string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QButtonGroup">
+            <property name="name">
+                <cstring>bg_alerts</cstring>
+            </property>
+            <property name="title">
+                <string>Alerts</string>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>11</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_aSolid</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Solid</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_aBlink</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Blink</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_aAnimate</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Animate</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_popupMsgs</cstring>
+            </property>
+            <property name="text">
+                <string>Autopopup New Messages</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_popupChats</cstring>
+            </property>
+            <property name="text">
+                <string>Autopopup New Chats</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_popupHeadlines</cstring>
+            </property>
+            <property name="text">
+                <string>Autopopup New Headlines</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_allowAwayPopup</cstring>
+            </property>
+            <property name="text">
+                <string>Allow Autopopups even when Away/XA</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_allowUnlistedPopup</cstring>
+            </property>
+            <property name="text">
+                <string>Allow Autopopups even from unlisted users</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_raise</cstring>
+            </property>
+            <property name="text">
+                <string>Raise Window On New Event</string>
+            </property>
+        </widget>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>TextLabel2</cstring>
+            </property>
+            <property name="text">
+                <string>Treat incoming messages and chats as:</string>
+            </property>
+        </widget>
+        <widget class="QComboBox">
+            <item>
+                <property name="text">
+                    <string>Determined by sender</string>
+                </property>
+            </item>
+            <item>
+                <property name="text">
+                    <string>Messages</string>
+                </property>
+            </item>
+            <item>
+                <property name="text">
+                    <string>Chats</string>
+                </property>
+            </item>
+            <item>
+                <property name="text">
+                    <string>Messages (Chats if Chatting)</string>
+                </property>
+            </item>
+            <property name="name">
+                <cstring>cb_incomingAs</cstring>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_general.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_general.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_general.cpp	(revision 38)
@@ -0,0 +1,606 @@
+#include "opt_general.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qbuttongroup.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qsignalmapper.h>
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qcolordialog.h>
+
+#include "opt_general_roster.h"
+#include "opt_general_docking.h"
+#include "opt_general_misc.h"
+#include "opt_general_notify.h"
+#include "opt_general_groupchat.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabGeneral
+//----------------------------------------------------------------------------
+
+OptionsTabGeneral::OptionsTabGeneral(QObject *parent)
+: MetaOptionsTab(parent, "general", "", tr("General"), tr("General options"), "psi/psi16")
+{
+	addTab( new OptionsTabGeneralRoster(this) );
+	addTab( new OptionsTabGeneralDocking(this) );
+	addTab( new OptionsTabGeneralMisc(this) );
+	addTab( new OptionsTabGeneralNotifications(this) );
+	addTab( new OptionsTabGeneralGroupchat(this) );
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabGeneralRoster
+//----------------------------------------------------------------------------
+
+OptionsTabGeneralRoster::OptionsTabGeneralRoster(QObject *parent)
+: OptionsTab(parent, "general_roster", "general", tr("Roster"), tr("Configure your roster"))
+{
+	w = 0;
+	bg_defAct = 0;
+}
+
+OptionsTabGeneralRoster::~OptionsTabGeneralRoster()
+{
+	if ( bg_defAct )
+		delete bg_defAct;
+}
+
+QWidget *OptionsTabGeneralRoster::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new GeneralRosterUI();
+	GeneralRosterUI *d = (GeneralRosterUI *)w;
+
+	bg_defAct = new QButtonGroup;
+	bg_defAct->setRadioButtonExclusive( true );
+	bg_defAct->insert( d->rb_defActMsg,  0 );
+	bg_defAct->insert( d->rb_defActChat, 1 );
+
+	QWhatsThis::add(d->ck_useleft,
+		tr("Normally, right-clicking with the mouse on a contact will activate the context-menu."
+		"  Check this option if you'd rather use a left-click."));
+	QWhatsThis::add(d->ck_singleclick,
+		tr("Normally, a double-click on a contact will invoke the default action."
+		"  Check this option if you'd rather invoke with a single-click."));
+
+	QWhatsThis::add(d->rb_defActMsg,
+		tr("Make the default action open a chat window."));
+	QWhatsThis::add(d->rb_defActChat,
+		tr("Make the default action open a normal message window."));
+
+	QWhatsThis::add(d->ck_showself,
+		tr("If checked, always show the self-contact in roster, "
+		   "otherwise it is shown only when multiple resources are connected to your account."));
+
+	return w;
+}
+
+void OptionsTabGeneralRoster::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralRosterUI *d = (GeneralRosterUI *)w;
+	opt->useleft = d->ck_useleft->isChecked();
+	opt->singleclick = d->ck_singleclick->isChecked();
+	opt->defaultAction = bg_defAct->id(bg_defAct->selected());
+	opt->alwaysShowSelf = d->ck_showself->isChecked();
+}
+
+void OptionsTabGeneralRoster::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralRosterUI *d = (GeneralRosterUI *)w;
+	d->ck_useleft->setChecked( opt->useleft );
+	d->ck_singleclick->setChecked( opt->singleclick );
+	bg_defAct->setButton( opt->defaultAction );
+	d->ck_showself->setChecked( opt->alwaysShowSelf );
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabGeneralDocking
+//----------------------------------------------------------------------------
+
+OptionsTabGeneralDocking::OptionsTabGeneralDocking(QObject *parent)
+: OptionsTab(parent, "general_docking", "general", tr("Docking"), tr("Configure the Psi docklet"))
+{
+	w = 0;
+}
+
+QWidget *OptionsTabGeneralDocking::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new GeneralDockingUI();
+	GeneralDockingUI *d = (GeneralDockingUI *)w;
+
+	connect(d->ck_docklet, SIGNAL(toggled(bool)), d->ck_dockDCstyle, SLOT(setEnabled(bool)));
+	connect(d->ck_docklet, SIGNAL(toggled(bool)), d->ck_dockHideMW,  SLOT(setEnabled(bool)));
+	connect(d->ck_docklet, SIGNAL(toggled(bool)), d->ck_dockToolMW,  SLOT(setEnabled(bool)));
+
+	QWhatsThis::add(d->ck_docklet,
+		tr("Makes Psi use a docklet icon, also known as system tray icon."));
+	QWhatsThis::add(d->ck_dockDCstyle,
+		tr("Normally, single-clicking on the Psi docklet icon brings the main window to"
+		" the foreground.  Check this option if you would rather use a double-click."));
+	QWhatsThis::add(d->ck_dockHideMW,
+		tr("Starts Psi with only the docklet icon visible."));
+	QWhatsThis::add(d->ck_dockToolMW,
+		tr("Prevents Psi from taking up a slot on the taskbar and makes the main "
+		"window use a small titlebar."));
+
+	return w;
+}
+
+void OptionsTabGeneralDocking::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralDockingUI *d = (GeneralDockingUI *)w;
+	opt->useDock = d->ck_docklet->isChecked();
+	opt->dockDCstyle = d->ck_dockDCstyle->isChecked();
+	opt->dockHideMW = d->ck_dockHideMW->isChecked();
+	opt->dockToolMW = d->ck_dockToolMW->isChecked();
+}
+
+void OptionsTabGeneralDocking::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralDockingUI *d = (GeneralDockingUI *)w;
+	d->ck_docklet->setChecked( opt->useDock );
+	d->ck_dockDCstyle->setChecked( opt->dockDCstyle );
+	d->ck_dockHideMW->setChecked( opt->dockHideMW );
+	d->ck_dockToolMW->setChecked( opt->dockToolMW );
+
+	if( !d->ck_docklet->isChecked() ) {
+		d->ck_dockDCstyle->setEnabled(false);
+		d->ck_dockHideMW->setEnabled(false);
+		d->ck_dockToolMW->setEnabled(false);
+	}
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabGeneralMisc
+//----------------------------------------------------------------------------
+
+OptionsTabGeneralMisc::OptionsTabGeneralMisc(QObject *parent)
+: OptionsTab(parent, "general_misc", "general", tr("Misc"), tr("Miscellaneous settings"))
+{
+	w = 0;
+	bg_delChats = 0;
+}
+
+OptionsTabGeneralMisc::~OptionsTabGeneralMisc()
+{
+	if ( bg_delChats )
+		delete bg_delChats;
+}
+
+QWidget *OptionsTabGeneralMisc::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new GeneralMiscUI();
+	GeneralMiscUI *d = (GeneralMiscUI *)w;
+
+	// chat
+	QWhatsThis::add(d->ck_chatSays,
+		tr("<P>Changes the normal chat style from:</P>"
+		"<P>[01:23:45] &lt;MyName&gt; Hi</P>"
+		"<P>[01:23:56] &lt;YourName&gt; How are you?</P>"
+		"<P>to:</P>"
+		"<P>[01:23:45] MyName says:</P>"
+		"<P>Hi</P>"
+		"<P>[01:23:56] YourName says:</P>"
+		"<P>How are you?</P>"));
+	QWhatsThis::add(d->ck_chatSoftReturn,
+		tr("<P>When checked, pressing Enter in a chat window will send your message."
+		   "  You must use Shift+Enter in order to create a newline in the chat message."
+		   "  If unchecked, messages are sent by pressing Alt-S or Control-Enter, just as they are with regular messages.</P>"));
+
+	// misc
+	bg_delChats = new QButtonGroup;
+	bg_delChats->setRadioButtonExclusive( true );
+	bg_delChats->insert( d->rb_delChatsClose, 0 );
+	bg_delChats->insert( d->rb_delChatsHour,  1 );
+	bg_delChats->insert( d->rb_delChatsDay,   2 );
+	bg_delChats->insert( d->rb_delChatsNever, 3 );
+
+	QWhatsThis::add(d->ck_alwaysOnTop,
+		tr("Makes the main Psi window always be in front of other windows."));
+	QWhatsThis::add(d->ck_keepSizes,
+		tr("Makes Psi remember window size and positions for chats and messages."
+		"  If disabled, the windows will always appear in their default positions and sizes."));
+	QWhatsThis::add(d->ck_jidComplete,
+		tr("Enables as-you-type JID autocompletion in message dialog."));
+	QWhatsThis::add(d->ck_grabUrls,
+		tr("Automatically attaches URLs from clipboard to the messages when enabled"));
+
+	QString s = tr("<P>Controls how long the chat log will be kept in memory after the"
+		" chat window is closed.</P>");
+	QWhatsThis::add(d->rb_delChatsClose, s +
+		tr("<P>This option does not keep the chat log in memory.</P>"));
+	QWhatsThis::add(d->rb_delChatsHour, s +
+		tr("<P>This option keeps the chat log for 1 hour before deleting it.</P>"));
+	QWhatsThis::add(d->rb_delChatsDay, s +
+		tr("<P>This option keeps the chat log for 1 day before deleting it.</P>"));
+	QWhatsThis::add(d->rb_delChatsNever, s +
+		tr("<P>This options keeps the chat log forever.</P>"));
+
+	// links
+	QWhatsThis::add(d->cb_link,
+		tr("Selects what applications to use for handling URLs and e-mail addresses."
+		"  You can choose between the system default and custom applications."));
+
+	s = tr("Enter the path to the application's executable and choose \"Custom\" in the list above.");
+	QWhatsThis::add(d->le_linkBrowser,
+		tr("Specify what custom browser application to use for handling URLs here.") + "  " + s);
+	QWhatsThis::add(d->le_linkMailer,
+		tr("Specify what custom mailer application to use for handling e-mail addresses here.") + "  " + s);
+
+	return w;
+}
+
+static int om_x11browse[] = { 0, 2, 1 };
+
+void OptionsTabGeneralMisc::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralMiscUI *d = (GeneralMiscUI *)w;
+
+	// chat
+	opt->chatSays       = d->ck_chatSays->isChecked();
+	opt->chatSoftReturn = d->ck_chatSoftReturn->isChecked();
+
+	// misc
+	opt->alwaysOnTop = d->ck_alwaysOnTop->isChecked();
+	opt->keepSizes   = d->ck_keepSizes->isChecked();
+	opt->jidComplete = d->ck_jidComplete->isChecked();
+	opt->grabUrls    = d->ck_grabUrls->isChecked();
+	opt->delChats    = bg_delChats->id( bg_delChats->selected() );
+
+	// links
+#ifdef Q_WS_X11
+	opt->browser = om_x11browse[ d->cb_link->currentItem() ];
+#else
+	opt->browser = d->cb_link->currentItem();
+#endif
+	opt->customBrowser = d->le_linkBrowser->text();
+	opt->customMailer  = d->le_linkMailer->text();
+}
+
+void OptionsTabGeneralMisc::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralMiscUI *d = (GeneralMiscUI *)w;
+
+	// chat
+	d->ck_chatSays->setChecked( opt->chatSays );
+	d->ck_chatSoftReturn->setChecked( opt->chatSoftReturn );
+
+	// misc
+#ifdef Q_WS_MAC
+	d->ck_alwaysOnTop->setEnabled( false );
+#else
+	d->ck_alwaysOnTop->setChecked( opt->alwaysOnTop );
+#endif
+	d->ck_keepSizes->setChecked( opt->keepSizes );
+	d->ck_jidComplete->setChecked( opt->jidComplete );
+	d->ck_grabUrls->setChecked( opt->grabUrls );
+
+	bg_delChats->setButton( opt->delChats );
+
+	// links
+	connect(d->cb_link, SIGNAL(activated(int)), SLOT(selectBrowser(int)));
+#ifdef Q_WS_WIN
+	d->cb_link->insertItem(tr("Windows Default Browser/Mail"));
+	d->cb_link->insertItem(tr("Custom"));
+	d->cb_link->setCurrentItem( opt->browser );
+	selectBrowser( opt->browser );
+#endif
+#ifdef Q_WS_PM
+	d->cb_link->insertItem(tr("PM Default Browser/Mail"));
+	d->cb_link->insertItem(tr("Custom"));
+	d->cb_link->setCurrentItem( opt->browser );
+	selectBrowser( opt->browser );
+#endif
+#ifdef Q_WS_X11
+	d->cb_link->insertItem(tr("KDE Default Browser/Mail"));
+	d->cb_link->insertItem(tr("GNOME2 Default Browser/Mail"));
+	d->cb_link->insertItem(tr("Custom"));
+	int rbi = om_x11browse[ opt->browser ];
+	d->cb_link->setCurrentItem( rbi );
+	selectBrowser( rbi );
+#endif
+#ifdef Q_WS_MAC
+	d->cb_link->insertItem(tr("MacOS Default Browser/Mail"));
+	d->cb_link->setCurrentItem( opt->browser );
+	selectBrowser( opt->browser );
+#endif
+	d->le_linkBrowser->setText( opt->customBrowser );
+	d->le_linkMailer->setText( opt->customMailer );
+}
+
+void OptionsTabGeneralMisc::selectBrowser(int x)
+{
+	if ( !w )
+		return;
+
+	bool enableCustom = TRUE;
+
+#ifdef Q_WS_WIN
+	if(x == 0)
+		enableCustom = FALSE;
+#endif
+#ifdef Q_WS_PM
+	if(x == 0)
+		enableCustom = FALSE;
+#endif
+#ifdef Q_WS_X11
+	if(x == 0 || x == 1)
+		enableCustom = FALSE;
+#endif
+#ifdef Q_WS_MAC
+	if(x == 0)
+		enableCustom = FALSE;
+#endif
+
+	GeneralMiscUI *d = (GeneralMiscUI *)w;
+	d->gb_linkCustom->setEnabled(enableCustom);
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabGeneralNotifications
+//----------------------------------------------------------------------------
+
+OptionsTabGeneralNotifications::OptionsTabGeneralNotifications(QObject *parent)
+: OptionsTab(parent, "general_notifications", "general", tr("Notifications"), tr("Configure the popup notifications"))
+{
+	w = 0;
+}
+
+QWidget *OptionsTabGeneralNotifications::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new GeneralNotifyUI();
+	//GeneralNotifyUI *d = (GeneralNotifyUI *)w;
+
+	// TODO: add QWhatsThis for all controls on widget
+
+	return w;
+}
+
+void OptionsTabGeneralNotifications::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralNotifyUI *d = (GeneralNotifyUI *)w;
+	opt->ppIsOn = d->ck_popupOn->isChecked();
+	opt->ppMessage = d->ck_popupOnMessage->isChecked();
+	opt->ppChat    = d->ck_popupOnChat->isChecked();
+	opt->ppHeadline = d->ck_popupOnHeadline->isChecked();
+	opt->ppFile    = d->ck_popupOnFile->isChecked();
+	opt->ppStatus  = d->ck_popupOnStatus->isChecked();
+	opt->ppOnline  = d->ck_popupOnOnline->isChecked();
+	opt->ppOffline = d->ck_popupOnOffline->isChecked();
+}
+
+void OptionsTabGeneralNotifications::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralNotifyUI *d = (GeneralNotifyUI *)w;
+	d->ck_popupOn->setChecked( true );
+	d->ck_popupOn->setChecked( opt->ppIsOn );
+	d->ck_popupOnMessage->setChecked( opt->ppMessage );
+	d->ck_popupOnChat->setChecked( opt->ppChat );
+	d->ck_popupOnHeadline->setChecked( opt->ppHeadline );
+	d->ck_popupOnFile->setChecked( opt->ppFile );
+	d->ck_popupOnStatus->setChecked( opt->ppStatus );
+	d->ck_popupOnOnline->setChecked( opt->ppOnline );
+	d->ck_popupOnOffline->setChecked( opt->ppOffline );
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabGeneralGroupchat -- TODO: simplify the code
+//----------------------------------------------------------------------------
+
+OptionsTabGeneralGroupchat::OptionsTabGeneralGroupchat(QObject *parent)
+: OptionsTab(parent, "general_groupchat", "general", tr("Groupchat"), tr("Configure the groupchat"))
+{
+	w = 0;
+}
+
+void OptionsTabGeneralGroupchat::setData(PsiCon *, QWidget *_dlg)
+{
+	dlg = _dlg;
+}
+
+QWidget *OptionsTabGeneralGroupchat::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new GeneralGroupchatUI();
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+
+	connect(d->pb_nickColor,	   SIGNAL(clicked()), SLOT(chooseGCNickColor()));
+	connect(d->lb_nickColors,	   SIGNAL(highlighted(int)), SLOT(selectedGCNickColor()));
+
+	connect(d->pb_addHighlightWord,	   SIGNAL(clicked()), SLOT(addGCHighlight()));
+	connect(d->pb_removeHighlightWord, SIGNAL(clicked()), SLOT(removeGCHighlight()));
+
+	connect(d->pb_addNickColor,	   SIGNAL(clicked()), SLOT(addGCNickColor()));
+	connect(d->pb_removeNickColor,	   SIGNAL(clicked()), SLOT(removeGCNickColor()));
+
+	// TODO: add QWhatsThis for all controls on widget
+
+	return w;
+}
+
+void OptionsTabGeneralGroupchat::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	opt->gcHighlighting = d->ck_gcHighlights->isChecked();
+	opt->gcNickColoring = d->ck_gcNickColoring->isChecked();
+
+	QStringList highlight;
+	int i;
+	for (i = 0; i < (int)d->lb_highlightWords->count(); i++)
+		highlight << d->lb_highlightWords->item(i)->text();
+	opt->gcHighlights = highlight;
+
+	QStringList colors;
+	for (i = 0; i < (int)d->lb_nickColors->count(); i++)
+		colors << d->lb_nickColors->item(i)->text();
+	opt->gcNickColors = colors;
+}
+
+void OptionsTabGeneralGroupchat::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+
+	// no need to call dataChanged() when these widgets are modified
+	disconnect(d->le_newNickColor,     SIGNAL(textChanged(const QString &)), 0, 0);
+	disconnect(d->le_newHighlightWord, SIGNAL(textChanged(const QString &)), 0, 0);
+	connect(d->le_newNickColor,	   SIGNAL(textChanged(const QString &)), SLOT(displayGCNickColor()));
+
+	d->ck_gcHighlights->setChecked( true );
+	d->ck_gcHighlights->setChecked( opt->gcHighlighting );
+	d->ck_gcNickColoring->setChecked( true );
+	d->ck_gcNickColoring->setChecked( opt->gcNickColoring );
+	d->lb_highlightWords->clear();
+	d->lb_highlightWords->insertStringList( opt->gcHighlights );
+	d->lb_nickColors->clear();
+
+	QStringList::ConstIterator it = opt->gcNickColors.begin();
+	for ( ; it != opt->gcNickColors.end(); ++it)
+		addNickColor( *it );
+
+	d->le_newHighlightWord->setText("");
+	d->le_newNickColor->setText("#FFFFFF");
+}
+
+static QPixmap name2color(QString name)
+{
+	QColor c(name);
+	QPixmap pix(16, 16);
+	QPainter p(&pix);
+
+	p.fillRect(0, 0, pix.width(), pix.height(), QBrush(c));
+	p.setPen( QColor(0, 0, 0) );
+	p.drawRect(0, 0, pix.width(), pix.height());
+	p.end();
+
+	return pix;
+}
+
+void OptionsTabGeneralGroupchat::addNickColor(QString name)
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	d->lb_nickColors->insertItem(name2color(name), name);
+}
+
+void OptionsTabGeneralGroupchat::addGCHighlight()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	if ( d->le_newHighlightWord->text().isEmpty() )
+		return;
+
+	d->lb_highlightWords->insertItem( d->le_newHighlightWord->text() );
+	d->le_newHighlightWord->setFocus();
+	d->le_newHighlightWord->setText("");
+
+	emit dataChanged();
+}
+
+void OptionsTabGeneralGroupchat::removeGCHighlight()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	int id = d->lb_highlightWords->currentItem();
+	if ( id == -1 )
+		return;
+
+	d->lb_highlightWords->removeItem(id);
+
+	emit dataChanged();
+}
+
+void OptionsTabGeneralGroupchat::addGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	if ( d->le_newNickColor->text().isEmpty() )
+		return;
+
+	addNickColor( d->le_newNickColor->text() );
+	d->le_newNickColor->setFocus();
+	d->le_newNickColor->setText("");
+
+	emit dataChanged();
+}
+
+void OptionsTabGeneralGroupchat::removeGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	int id = d->lb_nickColors->currentItem();
+	if ( id == -1 )
+		return;
+
+	d->lb_nickColors->removeItem(id);
+
+	emit dataChanged();
+}
+
+void OptionsTabGeneralGroupchat::chooseGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	QColor c = QColorDialog::getColor(QColor(d->le_newNickColor->text()), dlg);
+	if ( c.isValid() ) {
+		QString cs = c.name();
+		d->le_newNickColor->setText(cs);
+	}
+}
+
+void OptionsTabGeneralGroupchat::selectedGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	int id = d->lb_nickColors->currentItem();
+	if ( id == -1 )
+		return;
+
+	d->le_newNickColor->setText( d->lb_nickColors->text(id) );
+}
+
+void OptionsTabGeneralGroupchat::displayGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	d->pb_nickColor->setPixmap( name2color(d->le_newNickColor->text()) );
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_general.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_general.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_general.h	(revision 38)
@@ -0,0 +1,107 @@
+#ifndef OPT_GENERAL_H
+#define OPT_GENERAL_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+class QButtonGroup;
+
+class OptionsTabGeneral : public MetaOptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabGeneral(QObject *parent);
+};
+
+class OptionsTabGeneralRoster : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabGeneralRoster(QObject *parent);
+	~OptionsTabGeneralRoster();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private:
+	QWidget *w;
+	QButtonGroup *bg_defAct;
+};
+
+class OptionsTabGeneralDocking : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabGeneralDocking(QObject *parent);
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private:
+	QWidget *w;
+};
+
+class OptionsTabGeneralMisc : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabGeneralMisc(QObject *parent);
+	~OptionsTabGeneralMisc();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private slots:
+	void selectBrowser(int);
+
+private:
+	QWidget *w;
+	QButtonGroup *bg_delChats;
+};
+
+class OptionsTabGeneralNotifications : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabGeneralNotifications(QObject *parent);
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private:
+	QWidget *w;
+};
+
+class OptionsTabGeneralGroupchat : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabGeneralGroupchat(QObject *parent);
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+	void setData(PsiCon *, QWidget *);
+	bool stretchable() const { return true; }
+
+private slots:
+	void addNickColor(QString);
+	void addGCHighlight();
+	void removeGCHighlight();
+	void addGCNickColor();
+	void removeGCNickColor();
+	void chooseGCNickColor();
+	void displayGCNickColor();
+	void selectedGCNickColor();
+
+private:
+	QWidget *w, *dlg;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_general_docking.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_general_docking.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_general_docking.ui	(revision 38)
@@ -0,0 +1,60 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>GeneralDockingUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>GeneralDockingUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>264</width>
+            <height>102</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string></string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_docklet</cstring>
+            </property>
+            <property name="text">
+                <string>Enable Docklet</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_dockDCstyle</cstring>
+            </property>
+            <property name="text">
+                <string>Use "Double-click" style (like Mirabilis ICQ)</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_dockHideMW</cstring>
+            </property>
+            <property name="text">
+                <string>Hide roster window on startup</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_dockToolMW</cstring>
+            </property>
+            <property name="text">
+                <string>Make roster window a "Tool-Window"</string>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_general_groupchat.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_general_groupchat.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_general_groupchat.ui	(revision 38)
@@ -0,0 +1,334 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>GeneralGroupchatUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>GeneralGroupchatUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>420</width>
+            <height>186</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>GeneralGroupchatUI</string>
+    </property>
+    <hbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout5</cstring>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_gcHighlights</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Enable word highlighting</string>
+                    </property>
+                </widget>
+                <widget class="QGroupBox">
+                    <property name="name">
+                        <cstring>alertGroupBox</cstring>
+                    </property>
+                    <property name="title">
+                        <string>Word Highlighting</string>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Highlight lines and alert the user if this text is included in chat</string>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QListBox">
+                            <property name="name">
+                                <cstring>lb_highlightWords</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>7</hsizetype>
+                                    <vsizetype>7</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                            <property name="toolTip" stdset="0">
+                                <string>Words which will cause a notification in group chat.</string>
+                            </property>
+                            <property name="whatsThis" stdset="0">
+                                <string>This is the list of words which when said in group chat will cause the user to be notified.</string>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit">
+                            <property name="name">
+                                <cstring>le_newHighlightWord</cstring>
+                            </property>
+                            <property name="frameShape">
+                                <enum>LineEditPanel</enum>
+                            </property>
+                            <property name="frameShadow">
+                                <enum>Sunken</enum>
+                            </property>
+                            <property name="toolTip" stdset="0">
+                                <string>Type a new word here and press "Add Word" for it to be added to the highlight list</string>
+                            </property>
+                            <property name="whatsThis" stdset="0">
+                                <string>Type a new word here and press "Add Word" for it to be added to the highlight list</string>
+                            </property>
+                        </widget>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout1</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <spacer>
+                                    <property name="name">
+                                        <cstring>spacer3</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Expanding</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>40</width>
+                                            <height>20</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                                <widget class="QPushButton">
+                                    <property name="name">
+                                        <cstring>pb_addHighlightWord</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Add</string>
+                                    </property>
+                                    <property name="toolTip" stdset="0">
+                                        <string>Adds the new word to the list</string>
+                                    </property>
+                                    <property name="whatsThis" stdset="0">
+                                        <string>Adds the new word to the list</string>
+                                    </property>
+                                </widget>
+                                <widget class="QPushButton">
+                                    <property name="name">
+                                        <cstring>pb_removeHighlightWord</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Remove</string>
+                                    </property>
+                                    <property name="toolTip" stdset="0">
+                                        <string>Removes the selected word from the highlight list</string>
+                                    </property>
+                                    <property name="whatsThis" stdset="0">
+                                        <string>Removes the selected word from the highlight list</string>
+                                    </property>
+                                </widget>
+                                <spacer>
+                                    <property name="name">
+                                        <cstring>spacer4</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Expanding</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>40</width>
+                                            <height>20</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                            </hbox>
+                        </widget>
+                    </vbox>
+                </widget>
+            </vbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout4</cstring>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_gcNickColoring</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Enable Nick Coloring</string>
+                    </property>
+                </widget>
+                <widget class="QGroupBox">
+                    <property name="name">
+                        <cstring>ncGroupBox</cstring>
+                    </property>
+                    <property name="title">
+                        <string>Nick Coloring</string>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QListBox">
+                            <property name="name">
+                                <cstring>lb_nickColors</cstring>
+                            </property>
+                            <property name="toolTip" stdset="0">
+                                <string>Colors used for nicks in groupchat</string>
+                            </property>
+                            <property name="whatsThis" stdset="0">
+                                <string>This is the list of colours with which nicks will be drawn in group chat.</string>
+                            </property>
+                        </widget>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout2</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <widget class="QLineEdit">
+                                    <property name="name">
+                                        <cstring>le_newNickColor</cstring>
+                                    </property>
+                                    <property name="toolTip" stdset="0">
+                                        <string>Type a colourcode here, or press the pallette to the right</string>
+                                    </property>
+                                    <property name="whatsThis" stdset="0">
+                                        <string>Type a colourcode here, or press the pallette to the right</string>
+                                    </property>
+                                </widget>
+                                <widget class="QPushButton">
+                                    <property name="name">
+                                        <cstring>pb_nickColor</cstring>
+                                    </property>
+                                    <property name="sizePolicy">
+                                        <sizepolicy>
+                                            <hsizetype>4</hsizetype>
+                                            <vsizetype>4</vsizetype>
+                                            <horstretch>0</horstretch>
+                                            <verstretch>0</verstretch>
+                                        </sizepolicy>
+                                    </property>
+                                    <property name="text">
+                                        <string></string>
+                                    </property>
+                                </widget>
+                            </hbox>
+                        </widget>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout3</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <spacer>
+                                    <property name="name">
+                                        <cstring>spacer5</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Expanding</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>40</width>
+                                            <height>20</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                                <widget class="QPushButton">
+                                    <property name="name">
+                                        <cstring>pb_addNickColor</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Add</string>
+                                    </property>
+                                    <property name="toolTip" stdset="0">
+                                        <string>Adds the color to the list</string>
+                                    </property>
+                                    <property name="whatsThis" stdset="0">
+                                        <string>Adds the new color to the list</string>
+                                    </property>
+                                </widget>
+                                <widget class="QPushButton">
+                                    <property name="name">
+                                        <cstring>pb_removeNickColor</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Remove</string>
+                                    </property>
+                                    <property name="toolTip" stdset="0">
+                                        <string>Removes the selected color from the list</string>
+                                    </property>
+                                    <property name="whatsThis" stdset="0">
+                                        <string>Removes the selected color from the list</string>
+                                    </property>
+                                </widget>
+                                <spacer>
+                                    <property name="name">
+                                        <cstring>spacer7</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Expanding</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>40</width>
+                                            <height>20</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                            </hbox>
+                        </widget>
+                    </vbox>
+                </widget>
+            </vbox>
+        </widget>
+    </hbox>
+</widget>
+<tabstops>
+    <tabstop>ck_gcHighlights</tabstop>
+    <tabstop>lb_highlightWords</tabstop>
+    <tabstop>le_newHighlightWord</tabstop>
+    <tabstop>pb_removeHighlightWord</tabstop>
+    <tabstop>pb_addHighlightWord</tabstop>
+    <tabstop>ck_gcNickColoring</tabstop>
+    <tabstop>lb_nickColors</tabstop>
+    <tabstop>le_newNickColor</tabstop>
+    <tabstop>pb_nickColor</tabstop>
+    <tabstop>pb_removeNickColor</tabstop>
+    <tabstop>pb_addNickColor</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_general_misc.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_general_misc.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_general_misc.ui	(revision 38)
@@ -0,0 +1,270 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>GeneralMiscUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>GeneralMiscUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>314</width>
+            <height>499</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>GeneralMiscUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>GroupBox18_2</cstring>
+            </property>
+            <property name="title">
+                <string>Chat</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>11</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_chatSays</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Contact "says" instead of IRC style</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_chatSoftReturn</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Enter key sends chat messages</string>
+                    </property>
+                </widget>
+            </vbox>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>GroupBox13_2</cstring>
+            </property>
+            <property name="title">
+                <string>Misc</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>11</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_alwaysOnTop</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Always on top</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_keepSizes</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Remember Window Sizes</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_jidComplete</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Jabber ID completion</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_grabUrls</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Grab URLs from clipboard</string>
+                    </property>
+                </widget>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>TextLabel1_7</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Delete chat window contents (end conversation)</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_delChatsClose</cstring>
+                    </property>
+                    <property name="text">
+                        <string>When chat window is closed</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_delChatsHour</cstring>
+                    </property>
+                    <property name="text">
+                        <string>One hour after chat window was closed</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_delChatsDay</cstring>
+                    </property>
+                    <property name="text">
+                        <string>One day after chat window was closed</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_delChatsNever</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Never</string>
+                    </property>
+                </widget>
+            </vbox>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>GroupBox16</cstring>
+            </property>
+            <property name="title">
+                <string>Links</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>11</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>Layout18</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>0</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>TextLabel2_3</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Open With:</string>
+                            </property>
+                        </widget>
+                        <widget class="QComboBox">
+                            <property name="name">
+                                <cstring>cb_link</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>7</hsizetype>
+                                    <vsizetype>0</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+                <widget class="QGroupBox">
+                    <property name="name">
+                        <cstring>gb_linkCustom</cstring>
+                    </property>
+                    <property name="title">
+                        <string>Custom</string>
+                    </property>
+                    <grid>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>11</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLineEdit" row="1" column="1">
+                            <property name="name">
+                                <cstring>le_linkMailer</cstring>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit" row="0" column="1">
+                            <property name="name">
+                                <cstring>le_linkBrowser</cstring>
+                            </property>
+                        </widget>
+                        <widget class="QLabel" row="1" column="0">
+                            <property name="name">
+                                <cstring>TextLabel4</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Mailer</string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel" row="0" column="0">
+                            <property name="name">
+                                <cstring>TextLabel3_2</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Browser</string>
+                            </property>
+                        </widget>
+                    </grid>
+                </widget>
+            </vbox>
+        </widget>
+    </vbox>
+</widget>
+<tabstops>
+    <tabstop>ck_chatSays</tabstop>
+    <tabstop>ck_chatSoftReturn</tabstop>
+    <tabstop>ck_alwaysOnTop</tabstop>
+    <tabstop>ck_keepSizes</tabstop>
+    <tabstop>ck_jidComplete</tabstop>
+    <tabstop>ck_grabUrls</tabstop>
+    <tabstop>rb_delChatsClose</tabstop>
+    <tabstop>rb_delChatsHour</tabstop>
+    <tabstop>rb_delChatsDay</tabstop>
+    <tabstop>rb_delChatsNever</tabstop>
+    <tabstop>cb_link</tabstop>
+    <tabstop>le_linkBrowser</tabstop>
+    <tabstop>le_linkMailer</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_general_notify.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_general_notify.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_general_notify.ui	(revision 38)
@@ -0,0 +1,116 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>GeneralNotifyUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>GeneralNotifyUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>441</width>
+            <height>195</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>GeneralNotifyUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_popupOn</cstring>
+            </property>
+            <property name="text">
+                <string>Enable popup notifications</string>
+            </property>
+            <property name="checked">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox1</cstring>
+            </property>
+            <property name="title">
+                <string>Popup Notifications</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnMessage</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Popup on incoming Message event</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnChat</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Popup on incoming Chat event</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnHeadline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Popup on incoming Headline event</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnFile</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Popup on incoming File event</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnOnline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Popup when a contact comes Online.</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnStatus</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Popup when a contact changes unavailable status (away/XA/DND)</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_popupOnOffline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Popup when a contact signs off</string>
+                    </property>
+                </widget>
+            </vbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>ck_popupOn</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>groupBox1</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_general_roster.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_general_roster.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_general_roster.ui	(revision 38)
@@ -0,0 +1,95 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>GeneralRosterUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>GeneralRosterUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>216</width>
+            <height>123</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string></string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_useleft</cstring>
+            </property>
+            <property name="enabled">
+                <bool>true</bool>
+            </property>
+            <property name="text">
+                <string>Use left-click for menu</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_singleclick</cstring>
+            </property>
+            <property name="text">
+                <string>Single-click triggers default action</string>
+            </property>
+        </widget>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>TextLabel1_6</cstring>
+            </property>
+            <property name="text">
+                <string>Default Action:</string>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout2</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_defActMsg</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Message</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_defActChat</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Chat</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_showself</cstring>
+            </property>
+            <property name="text">
+                <string>Always show self-contact</string>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_groupchat.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_groupchat.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_groupchat.cpp	(revision 38)
@@ -0,0 +1,198 @@
+#include "opt_groupchat.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qbuttongroup.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qsignalmapper.h>
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qcolordialog.h>
+
+#include "opt_general_groupchat.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabGroupchat -- TODO: simplify the code
+//----------------------------------------------------------------------------
+
+OptionsTabGroupchat::OptionsTabGroupchat(QObject *parent)
+: OptionsTab(parent, "groupchat", "", tr("Groupchat"), tr("Configure the groupchat"), "psi/groupChat")
+{
+	w = 0;
+}
+
+void OptionsTabGroupchat::setData(PsiCon *, QWidget *_dlg)
+{
+	dlg = _dlg;
+}
+
+QWidget *OptionsTabGroupchat::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new GeneralGroupchatUI();
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+
+	connect(d->pb_nickColor,	   SIGNAL(clicked()), SLOT(chooseGCNickColor()));
+	connect(d->lb_nickColors,	   SIGNAL(highlighted(int)), SLOT(selectedGCNickColor()));
+
+	connect(d->pb_addHighlightWord,	   SIGNAL(clicked()), SLOT(addGCHighlight()));
+	connect(d->pb_removeHighlightWord, SIGNAL(clicked()), SLOT(removeGCHighlight()));
+
+	connect(d->pb_addNickColor,	   SIGNAL(clicked()), SLOT(addGCNickColor()));
+	connect(d->pb_removeNickColor,	   SIGNAL(clicked()), SLOT(removeGCNickColor()));
+
+	// TODO: add QWhatsThis for all controls on widget
+
+	return w;
+}
+
+void OptionsTabGroupchat::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	opt->gcHighlighting = d->ck_gcHighlights->isChecked();
+	opt->gcNickColoring = d->ck_gcNickColoring->isChecked();
+
+	QStringList highlight;
+	int i;
+	for (i = 0; i < (int)d->lb_highlightWords->count(); i++)
+		highlight << d->lb_highlightWords->item(i)->text();
+	opt->gcHighlights = highlight;
+
+	QStringList colors;
+	for (i = 0; i < (int)d->lb_nickColors->count(); i++)
+		colors << d->lb_nickColors->item(i)->text();
+	opt->gcNickColors = colors;
+}
+
+void OptionsTabGroupchat::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+
+	// no need to call dataChanged() when these widgets are modified
+	disconnect(d->le_newNickColor,     SIGNAL(textChanged(const QString &)), 0, 0);
+	disconnect(d->le_newHighlightWord, SIGNAL(textChanged(const QString &)), 0, 0);
+	connect(d->le_newNickColor,	   SIGNAL(textChanged(const QString &)), SLOT(displayGCNickColor()));
+
+	d->ck_gcHighlights->setChecked( true );
+	d->ck_gcHighlights->setChecked( opt->gcHighlighting );
+	d->ck_gcNickColoring->setChecked( true );
+	d->ck_gcNickColoring->setChecked( opt->gcNickColoring );
+	d->lb_highlightWords->clear();
+	d->lb_highlightWords->insertStringList( opt->gcHighlights );
+	d->lb_nickColors->clear();
+
+	QStringList::ConstIterator it = opt->gcNickColors.begin();
+	for ( ; it != opt->gcNickColors.end(); ++it)
+		addNickColor( *it );
+
+	d->le_newHighlightWord->setText("");
+	d->le_newNickColor->setText("#FFFFFF");
+}
+
+static QPixmap name2color(QString name)
+{
+	QColor c(name);
+	QPixmap pix(16, 16);
+	QPainter p(&pix);
+
+	p.fillRect(0, 0, pix.width(), pix.height(), QBrush(c));
+	p.setPen( QColor(0, 0, 0) );
+	p.drawRect(0, 0, pix.width(), pix.height());
+	p.end();
+
+	return pix;
+}
+
+void OptionsTabGroupchat::addNickColor(QString name)
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	d->lb_nickColors->insertItem(name2color(name), name);
+}
+
+void OptionsTabGroupchat::addGCHighlight()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	if ( d->le_newHighlightWord->text().isEmpty() )
+		return;
+
+	d->lb_highlightWords->insertItem( d->le_newHighlightWord->text() );
+	d->le_newHighlightWord->setFocus();
+	d->le_newHighlightWord->setText("");
+
+	emit dataChanged();
+}
+
+void OptionsTabGroupchat::removeGCHighlight()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	int id = d->lb_highlightWords->currentItem();
+	if ( id == -1 )
+		return;
+
+	d->lb_highlightWords->removeItem(id);
+
+	emit dataChanged();
+}
+
+void OptionsTabGroupchat::addGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	if ( d->le_newNickColor->text().isEmpty() )
+		return;
+
+	addNickColor( d->le_newNickColor->text() );
+	d->le_newNickColor->setFocus();
+	d->le_newNickColor->setText("");
+
+	emit dataChanged();
+}
+
+void OptionsTabGroupchat::removeGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	int id = d->lb_nickColors->currentItem();
+	if ( id == -1 )
+		return;
+
+	d->lb_nickColors->removeItem(id);
+
+	emit dataChanged();
+}
+
+void OptionsTabGroupchat::chooseGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	QColor c = QColorDialog::getColor(QColor(d->le_newNickColor->text()), dlg);
+	if ( c.isValid() ) {
+		QString cs = c.name();
+		d->le_newNickColor->setText(cs);
+	}
+}
+
+void OptionsTabGroupchat::selectedGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	int id = d->lb_nickColors->currentItem();
+	if ( id == -1 )
+		return;
+
+	d->le_newNickColor->setText( d->lb_nickColors->text(id) );
+}
+
+void OptionsTabGroupchat::displayGCNickColor()
+{
+	GeneralGroupchatUI *d = (GeneralGroupchatUI *)w;
+	d->pb_nickColor->setPixmap( name2color(d->le_newNickColor->text()) );
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_groupchat.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_groupchat.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_groupchat.h	(revision 38)
@@ -0,0 +1,36 @@
+#ifndef OPT_GROUPCHAT_H
+#define OPT_GROUPCHAT_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+
+class OptionsTabGroupchat : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabGroupchat(QObject *parent);
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+	void setData(PsiCon *, QWidget *);
+	bool stretchable() const { return true; }
+
+private slots:
+	void addNickColor(QString);
+	void addGCHighlight();
+	void removeGCHighlight();
+	void addGCNickColor();
+	void removeGCNickColor();
+	void chooseGCNickColor();
+	void displayGCNickColor();
+	void selectedGCNickColor();
+
+private:
+	QWidget *w, *dlg;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_iconset.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_iconset.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_iconset.cpp	(revision 38)
@@ -0,0 +1,1032 @@
+#include "opt_iconset.h"
+#include "common.h"
+#include "iconwidget.h"
+#include "psicon.h"
+
+#include <qbuttongroup.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qfileinfo.h>
+#include <qlistview.h>
+#include <qheader.h>
+#include <qapplication.h>
+#include <qthread.h>
+#include <qmutex.h>
+#include <qevent.h>
+#include <qprogressbar.h>
+#include <qcursor.h>
+#include <qpalette.h>
+#include <qtabwidget.h>
+
+#include "opt_iconset_emo.h"
+#include "opt_iconset_system.h"
+#include "opt_iconset_roster.h"
+#include "ui_isdetails.h"
+
+static void isDetails(const Iconset &is, QWidget *parent)
+{
+	IconsetDetailsDlg *isd = new IconsetDetailsDlg(parent, "IconsetDetailsDlg", false, QWidget::WDestructiveClose);
+	isd->setIconset(is);
+	isd->show();
+}
+
+static QStringList dirs;
+
+static int countIconsets(QString addDir, QStringList excludeList)
+{
+	int count = 0;
+
+	QStringList::Iterator it = dirs.begin();
+	for ( ; it != dirs.end(); ++it) {
+		QString fileName = *it + "/iconsets" + addDir;
+		QDir dir (fileName);
+
+		QStringList list = dir.entryList("*");
+		QStringList::Iterator it2 = list.begin();
+		for ( ; it2 != list.end(); ++it2) {
+			if ( *it2 == "." || *it2 == ".." )
+				continue;
+
+			bool found = false;
+			QStringList::Iterator it3 = excludeList.begin();
+			for ( ; it3 != excludeList.end(); ++it3) {
+				if ( *it2 == *it3 ) {
+					found = true;
+					break;
+				}
+			}
+
+			if ( found )
+				continue;
+
+			count++;
+			excludeList << *it2;
+		}
+	}
+
+	return count;
+}
+
+//----------------------------------------------------------------------------
+// IconsetLoadEvent
+//----------------------------------------------------------------------------
+
+class IconsetLoadEvent : public QEvent
+{
+public:
+	IconsetLoadEvent(IconsetLoadThread *par, Iconset *i)
+	: QEvent(QEvent::User)
+	{
+		p  = par;
+		is = i;
+	}
+
+	IconsetLoadThread *thread() const { return p; }
+
+	// if iconset() is '0' then it means that iconset wans't loaded successfully
+	Iconset *iconset() const { return is; }
+
+private:
+	IconsetLoadThread *p;
+	Iconset *is;
+};
+
+//----------------------------------------------------------------------------
+// IconsetFinishEvent
+//----------------------------------------------------------------------------
+
+class IconsetFinishEvent : public QEvent
+{
+public:
+	IconsetFinishEvent()
+	: QEvent( (QEvent::Type)(QEvent::User + 1) )
+	{
+	}
+};
+
+
+//----------------------------------------------------------------------------
+// IconsetLoadThreadDestroyEvent
+//----------------------------------------------------------------------------
+
+class IconsetLoadThreadDestroyEvent : public QEvent
+{
+public:
+	IconsetLoadThreadDestroyEvent(QThread *t)
+	: QEvent( (QEvent::Type)(QEvent::User + 2) )
+	{
+		thread = t;
+	}
+
+	~IconsetLoadThreadDestroyEvent()
+	{
+		thread->wait();
+		delete thread;
+	}
+
+private:
+	QThread *thread;
+};
+
+//----------------------------------------------------------------------------
+// IconsetLoadThread
+//----------------------------------------------------------------------------
+
+class IconsetLoadThread : public QThread
+{
+public:
+	IconsetLoadThread(QObject *parent, QString addPath);
+	void excludeIconsets(QStringList);
+
+	bool cancelled;
+
+protected:
+	void run();
+	void postEvent(QEvent *);
+
+private:
+	QObject *parent;
+	QString addPath;
+	QStringList excludeList;
+};
+
+IconsetLoadThread::IconsetLoadThread(QObject *p, QString path)
+{
+	cancelled = false;
+	parent  = p;
+	addPath = path;
+}
+
+void IconsetLoadThread::excludeIconsets(QStringList l)
+{
+	excludeList += l;
+}
+
+static QMutex threadCancelled, threadMutex;
+
+void IconsetLoadThread::postEvent(QEvent *e)
+{
+	threadCancelled.lock();
+	bool cancel = cancelled;
+	threadCancelled.unlock();
+
+	if ( cancel ) {
+		delete e;
+		return;
+	}
+
+	QApplication::postEvent(parent, e);
+}
+
+void IconsetLoadThread::run()
+{
+	threadMutex.lock();
+	QStringList dirs = ::dirs;
+	threadMutex.unlock();
+
+	QStringList::Iterator it = dirs.begin();
+	for ( ; it != dirs.end(); ++it) {
+		QString fileName = *it + "/iconsets" + addPath;
+		QDir dir (fileName);
+
+		QStringList list = dir.entryList("*");
+		QStringList::Iterator it2 = list.begin();
+		for ( ; it2 != list.end(); ++it2) {
+			if ( *it2 == "." || *it2 == ".." )
+				continue;
+
+			threadCancelled.lock();
+			bool cancel = cancelled;
+			threadCancelled.unlock();
+
+			if ( cancel )
+				goto getout;
+
+			bool found = false;
+			QStringList::Iterator it3 = excludeList.begin();
+			for ( ; it3 != excludeList.end(); ++it3) {
+				if ( *it2 == *it3 ) {
+					found = true;
+					break;
+				}
+			}
+
+			if ( found )
+				continue;
+
+			IconsetLoadEvent *event = 0;
+			Iconset *is = new Iconset;
+
+			if ( is->load (fileName + "/" + *it2) ) {
+				excludeList << *it2;
+
+				// don't forget to delete iconset in ::event()!
+				event = new IconsetLoadEvent(this, is);
+			}
+			else {
+				delete is;
+				event = new IconsetLoadEvent(this, 0);
+
+				// without excluding corrupted iconset,
+				// counter will go nuts! read more comments
+				// about that...
+				excludeList << *it2;
+				// TODO: there is possibility,
+				// that there's a bunch of same-named
+				// iconsets, and some of them are corrupted.
+				// It is possible to write a hack that
+				// loads iconset even in that case.
+
+				// logic:
+				//   tried to load iconset --> unable to load
+				//   checking if there's same-named iconsets in
+				//   other directories
+				//   emit IconsetLoadEvent() only on success
+				//   or when last corrupted iconset was unable
+				//   to load
+			}
+
+			postEvent(event);
+		}
+	}
+
+getout:
+	postEvent(new IconsetFinishEvent());
+	QApplication::postEvent(qApp, new IconsetLoadThreadDestroyEvent(this)); // self destruct
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabIconsetSystem
+//----------------------------------------------------------------------------
+
+OptionsTabIconsetSystem::OptionsTabIconsetSystem(QObject *parent)
+: OptionsTab(parent, "iconset_system", "", tr("System Icons"), tr("Select the system iconset"))
+{
+	w = 0;
+	thread = 0;
+
+	if ( dirs.isEmpty() ) {
+		dirs << ".";
+		dirs << g.pathHome;
+		dirs << g.pathBase;
+	}
+}
+
+OptionsTabIconsetSystem::~OptionsTabIconsetSystem()
+{
+	cancelThread();
+}
+
+QWidget *OptionsTabIconsetSystem::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new IconsetSystemUI;
+	IconsetSystemUI *d = (IconsetSystemUI *)w;
+
+	connect(d->pb_sysDetails, SIGNAL(clicked()), SLOT(previewIconset()));
+
+	// TODO: add QWhatsThis
+
+	return w;
+}
+
+void OptionsTabIconsetSystem::applyOptions(Options *opt)
+{
+	if ( !w || thread )
+		return;
+
+	IconsetSystemUI *d = (IconsetSystemUI *)w;
+	const Iconset *is = d->iss_system->iconset();
+	if ( is ) {
+		QFileInfo fi( is->fileName() );
+		opt->systemIconset = fi.fileName();
+	}
+}
+
+void OptionsTabIconsetSystem::restoreOptions(const Options *opt)
+{
+	if ( !w || thread )
+		return;
+
+	o = (Options *)opt;
+	IconsetSystemUI *d = (IconsetSystemUI *)w;
+	d->iss_system->clear();
+	QStringList loaded;
+
+	d->setCursor(WaitCursor);
+
+	QPalette customPal = d->palette();
+	customPal.setDisabled(customPal.inactive());
+	d->iss_system->setEnabled(false);
+	d->iss_system->setPalette(customPal);
+
+	d->pb_sysDetails->setEnabled(false);
+	d->pb_sysDetails->setPalette(customPal);
+
+	d->progress->show();
+	d->progress->setProgress( 0 );
+
+	numIconsets = countIconsets("/system", loaded);
+	iconsetsLoaded = 0;
+
+	cancelThread();
+
+	thread = new IconsetLoadThread(this, "/system");
+	thread->start();
+}
+
+bool OptionsTabIconsetSystem::event(QEvent *e)
+{
+	IconsetSystemUI *d = (IconsetSystemUI *)w;
+	if ( e->type() == QEvent::User ) { // iconset load event
+		IconsetLoadEvent *le = (IconsetLoadEvent *)e;
+
+		if ( thread != le->thread() )
+			return false;
+
+		if ( !numIconsets )
+			numIconsets = 1;
+		d->progress->setProgress( (int)((float)100 * ++iconsetsLoaded / numIconsets) );
+
+		Iconset *i = le->iconset();
+		if ( i ) {
+			is->stripFirstAnimFrame(i);
+			d->iss_system->insert(*i);
+
+			QFileInfo fi( i->fileName() );
+			if ( fi.fileName() == o->systemIconset )
+				d->iss_system->setSelected(d->iss_system->count()-1, true);
+
+			delete i;
+		}
+
+		return true;
+	}
+	else if ( e->type() == QEvent::User + 1 ) { // finish event
+		d->iss_system->setEnabled(true);
+		d->iss_system->unsetPalette();
+
+		d->pb_sysDetails->setEnabled(true);
+		d->pb_sysDetails->unsetPalette();
+
+		connect(d->iss_system, SIGNAL(selectionChanged()), SIGNAL(dataChanged()));
+
+		d->progress->hide();
+
+		d->unsetCursor();
+		thread = 0;
+
+		return true;
+	}
+
+	return false;
+}
+
+void OptionsTabIconsetSystem::previewIconset()
+{
+	IconsetSystemUI *d = (IconsetSystemUI *)w;
+	const Iconset *is = d->iss_system->iconset();
+	if ( is )
+		isDetails(*is, parentWidget);
+}
+
+void OptionsTabIconsetSystem::setData(PsiCon *, QWidget *p)
+{
+	parentWidget = p;
+}
+
+void OptionsTabIconsetSystem::cancelThread()
+{
+	if ( thread ) {
+		threadCancelled.lock();
+		thread->cancelled = true;
+		threadCancelled.unlock();
+
+		thread = 0;
+	}
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabIconsetEmoticons
+//----------------------------------------------------------------------------
+
+OptionsTabIconsetEmoticons::OptionsTabIconsetEmoticons(QObject *parent)
+: OptionsTab(parent, "iconset_emoticons", "", tr("Emoticons"), tr("Select your emoticon iconsets"))
+{
+	w = 0;
+	thread = 0;
+}
+
+OptionsTabIconsetEmoticons::~OptionsTabIconsetEmoticons()
+{
+	cancelThread();
+}
+
+QWidget *OptionsTabIconsetEmoticons::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new IconsetEmoUI;
+	IconsetEmoUI *d = (IconsetEmoUI *)w;
+
+	connect(d->pb_emoUp, SIGNAL(clicked()), d->iss_emoticons, SLOT(moveItemUp()));
+	connect(d->pb_emoUp, SIGNAL(clicked()), SIGNAL(dataChanged()));
+	connect(d->pb_emoDown, SIGNAL(clicked()), d->iss_emoticons, SLOT(moveItemDown()));
+	connect(d->pb_emoDown, SIGNAL(clicked()), SIGNAL(dataChanged()));
+	connect(d->pb_emoDetails, SIGNAL(clicked()), SLOT(previewIconset()));
+
+	QWhatsThis::add(d->ck_useEmoticons,
+		tr("<P>Emoticons are short sequences of characters that are used to convey an emotion or idea.</P>"
+		"<P>Enable this option if you want Psi to replace common emoticons with a graphical image.</P>"
+		"<P>For example, <B>:-)</B> would be replaced by <icon name=\"psi/smile\"></P>"));
+
+	// TODO: add QWhatsThis
+
+	return w;
+}
+
+void OptionsTabIconsetEmoticons::applyOptions(Options *opt)
+{
+	if ( !w || thread )
+		return;
+
+	IconsetEmoUI *d = (IconsetEmoUI *)w;
+	opt->useEmoticons = d->ck_useEmoticons->isChecked();
+
+	opt->emoticons.clear();
+	IconWidgetItem *item = (IconWidgetItem *)d->iss_emoticons->firstItem();
+	while ( item ) {
+		if ( item->isSelected() ) {
+			const Iconset *is = item->iconset();
+			if ( is ) {
+				QFileInfo fi( is->fileName() );
+				opt->emoticons << fi.fileName();
+			}
+		}
+
+		item = (IconWidgetItem *)item->next();
+	}
+
+}
+
+void OptionsTabIconsetEmoticons::restoreOptions(const Options *opt)
+{
+	if ( !w || thread )
+		return;
+
+	IconsetEmoUI *d = (IconsetEmoUI *)w;
+	d->ck_useEmoticons->setChecked( opt->useEmoticons );
+
+	// fill in the iconset view
+	d->iss_emoticons->clear();
+
+	{
+		QPtrListIterator<Iconset> it ( is->emoticons );
+		Iconset *is;
+		for ( ; (is = it.current()); ++it) {
+			d->iss_emoticons->insert(*is);
+			d->iss_emoticons->setSelected(d->iss_emoticons->count()-1, true);
+		}
+	}
+
+
+	{
+		QStringList loaded;
+		{
+			QPtrListIterator<Iconset> it( is->emoticons );
+			Iconset *tmp;
+			for ( ; (tmp = it.current()); ++it) {
+				QFileInfo fi ( tmp->fileName() );
+				loaded << fi.fileName();
+			}
+		}
+
+		d->setCursor(WaitCursor);
+
+		QPalette customPal = d->palette();
+		customPal.setDisabled(customPal.inactive());
+		d->ck_useEmoticons->setEnabled(false);
+		d->ck_useEmoticons->setPalette(customPal);
+
+		d->groupBox9->setEnabled(false);
+		d->groupBox9->setPalette(customPal);
+
+		d->progress->show();
+		d->progress->setProgress( 0 );
+
+		numIconsets = countIconsets("/emoticons", loaded);
+		iconsetsLoaded = 0;
+
+		cancelThread();
+
+		thread = new IconsetLoadThread(this, "/emoticons");
+		thread->excludeIconsets(loaded);
+		thread->start();
+	}
+}
+
+bool OptionsTabIconsetEmoticons::event(QEvent *e)
+{
+	IconsetEmoUI *d = (IconsetEmoUI *)w;
+	if ( e->type() == QEvent::User ) { // iconset load event
+		IconsetLoadEvent *le = (IconsetLoadEvent *)e;
+
+		if ( thread != le->thread() )
+			return false;
+
+		if ( !numIconsets )
+			numIconsets = 1;
+		d->progress->setProgress( (int)((float)100 * ++iconsetsLoaded / numIconsets) );
+
+		Iconset *is = le->iconset();
+		if ( is ) {
+			PsiIconset::removeAnimation(is);
+			d->iss_emoticons->insert(*is);
+			delete is;
+		}
+
+		return true;
+	}
+	else if ( e->type() == QEvent::User + 1 ) { // finish event
+		d->ck_useEmoticons->setEnabled(true);
+		d->ck_useEmoticons->unsetPalette();
+
+		d->groupBox9->setEnabled(true);
+		d->groupBox9->unsetPalette();
+
+		connect(d->iss_emoticons, SIGNAL(selectionChanged()), SIGNAL(dataChanged()));
+
+		bool checked = d->ck_useEmoticons->isChecked();
+		d->ck_useEmoticons->setChecked( true );
+		d->ck_useEmoticons->setChecked( checked );
+
+		d->progress->hide();
+
+		d->unsetCursor();
+		thread = 0;
+
+		return true;
+	}
+
+	return false;
+}
+
+void OptionsTabIconsetEmoticons::previewIconset()
+{
+	IconsetEmoUI *d = (IconsetEmoUI *)w;
+	const Iconset *is = d->iss_emoticons->iconset();
+	if ( is )
+		isDetails(*is, parentWidget);
+}
+
+void OptionsTabIconsetEmoticons::setData(PsiCon *, QWidget *p)
+{
+	parentWidget = p;
+}
+
+void OptionsTabIconsetEmoticons::cancelThread()
+{
+	if ( thread ) {
+		threadCancelled.lock();
+		thread->cancelled = true;
+		threadCancelled.unlock();
+
+		thread = 0;
+	}
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabIconsetRoster
+//----------------------------------------------------------------------------
+
+OptionsTabIconsetRoster::OptionsTabIconsetRoster(QObject *parent)
+: OptionsTab(parent, "iconset_roster", "", tr("Roster Icons"), tr("Select iconsets for your roster"))
+{
+	w = 0;
+	thread = 0;
+}
+
+OptionsTabIconsetRoster::~OptionsTabIconsetRoster()
+{
+	cancelThread();
+}
+
+QWidget *OptionsTabIconsetRoster::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new IconsetRosterUI;
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+
+	connect(d->pb_defRosterDetails, SIGNAL(clicked()), SLOT(defaultDetails()));
+
+	connect(d->iss_servicesRoster, SIGNAL(selectionChanged(QListBoxItem *)), SLOT(isServices_iconsetSelected(QListBoxItem *)));
+	connect(d->lv_isServices, SIGNAL(selectionChanged(QListViewItem *)), SLOT(isServices_selectionChanged(QListViewItem *)));
+	connect(d->pb_servicesDetails, SIGNAL(clicked()), SLOT(servicesDetails()));
+	isServices_selectionChanged(0);
+
+	connect(d->iss_customRoster, SIGNAL(selectionChanged(QListBoxItem *)), SLOT(isCustom_iconsetSelected(QListBoxItem *)));
+	connect(d->lv_customRoster, SIGNAL(selectionChanged(QListViewItem *)), SLOT(isCustom_selectionChanged(QListViewItem *)));
+	connect(d->pb_customRosterDetails, SIGNAL(clicked()), SLOT(customDetails()));
+	connect(d->le_customRoster, SIGNAL(textChanged(const QString &)), SLOT(isCustom_textChanged()));
+	connect(d->pb_customRosterAdd, SIGNAL(clicked()), SLOT(isCustom_add()));
+	connect(d->pb_customRosterDelete, SIGNAL(clicked()), SLOT(isCustom_delete()));
+	isCustom_selectionChanged(0);
+
+	QWhatsThis::add(d->ck_useTransportIconsForContacts,
+		tr("Toggles use of transport icons to the contacts, that use that transports."));
+
+	// TODO: add QWhatsThis
+
+	return w;
+}
+
+void OptionsTabIconsetRoster::applyOptions(Options *opt)
+{
+	if ( !w || thread )
+		return;
+
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	opt->useTransportIconsForContacts = d->ck_useTransportIconsForContacts->isChecked();
+
+	// roster - default
+	{
+		const Iconset *is = d->iss_defRoster->iconset();
+		if ( is ) {
+			QFileInfo fi( is->fileName() );
+			opt->defaultRosterIconset = fi.fileName();
+		}
+	}
+
+	// roster - services
+	{
+		opt->serviceRosterIconset.clear();
+
+		QListViewItemIterator it( d->lv_isServices );
+		for ( ; it.current(); ++it) {
+			QListViewItem *item = it.current();
+
+			opt->serviceRosterIconset[item->text(2)] = item->text(3);
+		}
+	}
+
+	// roster - custom
+	{
+		opt->customRosterIconset.clear();
+
+		QListViewItemIterator it( d->lv_customRoster );
+		for ( ; it.current(); ++it) {
+			QListViewItem *item = it.current();
+
+			opt->customRosterIconset[item->text(2)] = item->text(3);
+		}
+	}
+}
+
+void OptionsTabIconsetRoster::restoreOptions(const Options *opt)
+{
+	if ( !w || thread )
+		return;
+
+	o = (Options *)opt;
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	d->ck_useTransportIconsForContacts->setChecked( opt->useTransportIconsForContacts );
+
+	d->iss_defRoster->clear();
+	d->iss_servicesRoster->clear();
+	d->iss_customRoster->clear();
+
+	QStringList loaded;
+
+	d->setCursor(WaitCursor);
+
+	QPalette customPal = d->palette();
+	customPal.setDisabled(customPal.inactive());
+	d->tabWidget3->setEnabled(false);
+	d->tabWidget3->setPalette(customPal);
+
+	d->progress->show();
+	d->progress->setProgress( 0 );
+
+	numIconsets = countIconsets("/roster", loaded);
+	iconsetsLoaded = 0;
+
+	cancelThread();
+
+	thread = new IconsetLoadThread(this, "/roster");
+	thread->start();
+}
+
+bool OptionsTabIconsetRoster::event(QEvent *e)
+{
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	if ( e->type() == QEvent::User ) { // iconset load event
+		IconsetLoadEvent *le = (IconsetLoadEvent *)e;
+
+		if ( thread != le->thread() )
+			return false;
+
+		if ( !numIconsets )
+			numIconsets = 1;
+		d->progress->setProgress( (int)((float)100 * ++iconsetsLoaded / numIconsets) );
+
+		Iconset *i = le->iconset();
+		if ( i ) {
+			is->stripFirstAnimFrame(i);
+			QFileInfo fi( i->fileName() );
+
+			// roster - default
+			d->iss_defRoster->insert(*i);
+			if ( fi.fileName() == o->defaultRosterIconset )
+				d->iss_defRoster->setSelected(d->iss_defRoster->count()-1, true);
+
+			// roster - service
+			d->iss_servicesRoster->insert(*i);
+
+			// roster - custom
+			d->iss_customRoster->insert(*i);
+
+			delete i;
+		}
+
+		return true;
+	}
+	else if ( e->type() == QEvent::User + 1 ) { // finish event
+		// roster - service
+		{
+			// fill the QListView
+			QListViewItemIterator it( d->lv_isServices );
+			for ( ; it.current(); ++it) {
+				QListViewItem *i = it.current();
+				if ( !i->text(2).isEmpty() ) {
+					Iconset *iss = is->roster[o->serviceRosterIconset[i->text(2)]];
+					if ( iss ) {
+						i->setText(1, iss->name());
+
+						QFileInfo fi ( iss->fileName() );
+						i->setText(3, fi.fileName());
+					}
+				}
+			}
+
+			QHeader *head = d->lv_isServices->header();
+			head->removeLabel(2);
+		}
+
+		// roster - custom
+		{
+			// Then, fill the QListView
+			QListViewItem *last = 0;
+			QMap<QString, QString>::ConstIterator it = o->customRosterIconset.begin();
+			for ( ; it != o->customRosterIconset.end(); ++it) {
+				QListViewItem *item = new QListViewItem(d->lv_customRoster, last);
+				last = item;
+
+				item->setText(0, clipCustomText(it.key())); // RegExp
+				item->setText(2, it.key());
+
+				Iconset *iss = is->roster[it.data()];
+				if ( iss ) {
+					item->setText(1, iss->name());
+
+					QFileInfo fi ( iss->fileName() );
+					item->setText(3, fi.fileName());
+				}
+			}
+		}
+
+		d->tabWidget3->setEnabled(true);
+		d->tabWidget3->unsetPalette();
+
+		connect(d->iss_defRoster, SIGNAL(selectionChanged()), SIGNAL(dataChanged()));
+		connect(d->iss_servicesRoster, SIGNAL(selectionChanged()), SIGNAL(dataChanged()));
+		connect(d->iss_customRoster, SIGNAL(selectionChanged()), SIGNAL(dataChanged()));
+
+		d->progress->hide();
+
+		d->unsetCursor();
+		thread = 0;
+
+		return true;
+	}
+
+	return false;
+}
+
+void OptionsTabIconsetRoster::setData(PsiCon *, QWidget *p)
+{
+	parentWidget = p;
+}
+
+void OptionsTabIconsetRoster::defaultDetails()
+{
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	const Iconset *is = d->iss_defRoster->iconset();
+	if ( is )
+		isDetails(*is, parentWidget);
+}
+
+void OptionsTabIconsetRoster::servicesDetails()
+{
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	const Iconset *is = d->iss_servicesRoster->iconset();
+	if ( is )
+		isDetails(*is, parentWidget);
+}
+
+void OptionsTabIconsetRoster::customDetails()
+{
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	const Iconset *is = d->iss_customRoster->iconset();
+	if ( is )
+		isDetails(*is, parentWidget);
+}
+
+//------------------------------------------------------------
+
+void OptionsTabIconsetRoster::isServices_iconsetSelected(QListBoxItem *item)
+{
+	if ( !item )
+		return;
+
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	QListViewItem *it = d->lv_isServices->selectedItem();
+	if ( !it )
+		return;
+
+	const Iconset *is = ((IconWidgetItem *)item)->iconset();
+	if ( !is )
+		return;
+
+	it->setText(1, is->name());
+	QFileInfo fi ( is->fileName() );
+	it->setText(3, fi.fileName());
+}
+
+void OptionsTabIconsetRoster::isServices_selectionChanged(QListViewItem *it)
+{
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	d->iss_servicesRoster->setEnabled( it != 0 );
+	d->pb_servicesDetails->setEnabled( it != 0 );
+	d->iss_servicesRoster->clearSelection();
+	if ( !it )
+		return;
+
+	if ( it->text(3).isEmpty() )
+		return;
+
+	QString name = it->text(3);
+
+	emit noDirty(true);
+	IconWidgetItem *item = (IconWidgetItem *)d->iss_servicesRoster->firstItem();
+	for ( ; item; item = (IconWidgetItem *)item->next()) {
+		const Iconset *is = item->iconset();
+		if ( is ) {
+			QFileInfo fi ( is->fileName() );
+			if ( fi.fileName() == name ) {
+				emit noDirty(true);
+				d->iss_servicesRoster->setSelected(item, true);
+				d->iss_servicesRoster->ensureCurrentVisible();
+				emit noDirty(false);
+				break;
+			}
+		}
+	}
+	qApp->processEvents();
+	emit noDirty(false);
+}
+
+//------------------------------------------------------------
+
+void OptionsTabIconsetRoster::isCustom_iconsetSelected(QListBoxItem *item)
+{
+	if ( !item )
+		return;
+
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	QListViewItem *it = d->lv_customRoster->selectedItem();
+	if ( !it )
+		return;
+
+	const Iconset *is = ((IconWidgetItem *)item)->iconset();
+	if ( !is )
+		return;
+
+	it->setText(1, is->name());
+	QFileInfo fi ( is->fileName() );
+	it->setText(3, fi.fileName());
+}
+
+void OptionsTabIconsetRoster::isCustom_selectionChanged(QListViewItem *it)
+{
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	d->iss_customRoster->setEnabled( it != 0 );
+	d->pb_customRosterDetails->setEnabled( it != 0 );
+	//d->pb_customRosterAdd->setEnabled( it != 0 );
+	d->pb_customRosterDelete->setEnabled( it != 0 );
+	d->le_customRoster->setEnabled( it != 0 );
+	d->iss_customRoster->clearSelection();
+	if ( !it )
+		return;
+
+	if ( it->text(3).isEmpty() )
+		return;
+
+	emit noDirty(true);
+	d->le_customRoster->setText(it->text(2));
+	QString name = it->text(3);
+
+	IconWidgetItem *item = (IconWidgetItem *)d->iss_customRoster->firstItem();
+	for ( ; item; item = (IconWidgetItem *)item->next()) {
+		const Iconset *is = item->iconset();
+		if ( is ) {
+			QFileInfo fi ( is->fileName() );
+			if ( fi.fileName() == name ) {
+				d->iss_customRoster->setSelected(item, true);
+				d->iss_customRoster->ensureCurrentVisible();
+				break;
+			}
+		}
+	}
+	qApp->processEvents();
+	emit noDirty(false);
+}
+
+void OptionsTabIconsetRoster::isCustom_textChanged()
+{
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	QListViewItem *item = d->lv_customRoster->selectedItem();
+	if ( !item )
+		return;
+
+	item->setText( 0, clipCustomText(d->le_customRoster->text()) );
+	item->setText( 2, d->le_customRoster->text() );
+}
+
+void OptionsTabIconsetRoster::isCustom_add()
+{
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+
+	QString def;
+	const Iconset *iconset = d->iss_defRoster->iconset();
+	if ( is ) {
+		QFileInfo fi( iconset->fileName() );
+		def = fi.fileName();
+	}
+	else
+		qWarning("OptionsTabIconsetRoster::isCustom_add(): 'def' is null!");
+
+	QListViewItem *item = new QListViewItem(d->lv_customRoster, d->lv_customRoster->lastItem());
+	const Iconset *i = is->roster[def];
+	if ( i ) {
+		item->setText(1, i->name());
+
+		QFileInfo fi(i->fileName());
+		item->setText(3, fi.fileName());
+	}
+
+	d->lv_customRoster->setSelected(item, true);
+	emit dataChanged();
+
+	d->le_customRoster->setFocus();
+}
+
+void OptionsTabIconsetRoster::isCustom_delete()
+{
+	IconsetRosterUI *d = (IconsetRosterUI *)w;
+	QListViewItem *item = d->lv_customRoster->selectedItem();
+	if ( !item )
+		return;
+
+	delete item;
+	isCustom_selectionChanged(0);
+	emit dataChanged();
+}
+
+QString OptionsTabIconsetRoster::clipCustomText(QString s)
+{
+	if ( s.length() > 10 ) {
+		s = s.left(9);
+		s += "...";
+	}
+
+	return s;
+}
+
+void OptionsTabIconsetRoster::cancelThread()
+{
+	if ( thread ) {
+		threadCancelled.lock();
+		thread->cancelled = true;
+		threadCancelled.unlock();
+
+		thread = 0;
+	}
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_iconset.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_iconset.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_iconset.h	(revision 38)
@@ -0,0 +1,108 @@
+#ifndef OPT_ICONSET_H
+#define OPT_ICONSET_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+class QListBoxItem;
+class QListViewItem;
+class IconsetLoadThread;
+
+class OptionsTabIconsetSystem : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabIconsetSystem(QObject *parent);
+	~OptionsTabIconsetSystem();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+	bool stretchable() const { return true; }
+
+private slots:
+	void setData(PsiCon *, QWidget *);
+	void previewIconset();
+
+protected:
+	bool event(QEvent *);
+	void cancelThread();
+
+private:
+	QWidget *w, *parentWidget;
+
+	int numIconsets, iconsetsLoaded;
+	IconsetLoadThread *thread;
+	Options *o;
+};
+
+class OptionsTabIconsetEmoticons : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabIconsetEmoticons(QObject *parent);
+	~OptionsTabIconsetEmoticons();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+	bool stretchable() const { return true; }
+
+private slots:
+	void setData(PsiCon *, QWidget *);
+	void previewIconset();
+
+protected:
+	bool event(QEvent *);
+	void cancelThread();
+
+private:
+	QWidget *w, *parentWidget;
+
+	int numIconsets, iconsetsLoaded;
+	IconsetLoadThread *thread;
+};
+
+class OptionsTabIconsetRoster : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabIconsetRoster(QObject *parent);
+	~OptionsTabIconsetRoster();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+	bool stretchable() const { return true; }
+
+private slots:
+	void setData(PsiCon *, QWidget *);
+
+	void defaultDetails();
+	void servicesDetails();
+	void customDetails();
+
+	void isServices_iconsetSelected(QListBoxItem *);
+	void isServices_selectionChanged(QListViewItem *);
+
+	void isCustom_iconsetSelected(QListBoxItem *);
+	void isCustom_selectionChanged(QListViewItem *);
+	void isCustom_textChanged();
+	void isCustom_add();
+	void isCustom_delete();
+	QString clipCustomText(QString);
+
+protected:
+	bool event(QEvent *);
+	void cancelThread();
+
+private:
+	QWidget *w, *parentWidget;
+
+	int numIconsets, iconsetsLoaded;
+	IconsetLoadThread *thread;
+	Options *o;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_iconset_emo.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_iconset_emo.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_iconset_emo.ui	(revision 38)
@@ -0,0 +1,142 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>IconsetEmoUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>IconsetEmoUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>256</width>
+            <height>195</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>IconsetEmoUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_useEmoticons</cstring>
+            </property>
+            <property name="text">
+                <string>Enable Emoticons</string>
+            </property>
+            <property name="checked">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox9</cstring>
+            </property>
+            <property name="title">
+                <string>Emoticon iconsets</string>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="IconsetSelect" row="0" column="0" rowspan="1" colspan="4">
+                    <property name="name">
+                        <cstring>iss_emoticons</cstring>
+                    </property>
+                    <property name="font">
+                        <font>
+                            <bold>1</bold>
+                        </font>
+                    </property>
+                    <property name="selectionMode">
+                        <enum>Multi</enum>
+                    </property>
+                </widget>
+                <widget class="IconButton" row="1" column="3">
+                    <property name="name">
+                        <cstring>pb_emoDetails</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Show Details</string>
+                    </property>
+                </widget>
+                <spacer row="1" column="2">
+                    <property name="name">
+                        <cstring>spacer23</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>204</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton" row="1" column="0">
+                    <property name="name">
+                        <cstring>pb_emoUp</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                    <property name="iconName" stdset="0">
+                        <string>psi/arrowUp</string>
+                    </property>
+                    <property name="textVisible">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+                <widget class="IconButton" row="1" column="1">
+                    <property name="name">
+                        <cstring>pb_emoDown</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                    <property name="iconName" stdset="0">
+                        <string>psi/arrowDown</string>
+                    </property>
+                    <property name="textVisible">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QProgressBar">
+            <property name="name">
+                <cstring>progress</cstring>
+            </property>
+            <property name="progress">
+                <number>0</number>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+    <connection>
+        <sender>ck_useEmoticons</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>groupBox9</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_iconset_roster.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_iconset_roster.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_iconset_roster.ui	(revision 38)
@@ -0,0 +1,510 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>IconsetRosterUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>IconsetRosterUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>375</width>
+            <height>341</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>IconsetRosterUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QTabWidget">
+            <property name="name">
+                <cstring>tabWidget3</cstring>
+            </property>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Default</string>
+                </attribute>
+                <grid>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="IconsetSelect" row="1" column="0" rowspan="1" colspan="2">
+                        <property name="name">
+                            <cstring>iss_defRoster</cstring>
+                        </property>
+                    </widget>
+                    <spacer row="2" column="0">
+                        <property name="name">
+                            <cstring>spacer25</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Horizontal</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>151</width>
+                                <height>20</height>
+                            </size>
+                        </property>
+                    </spacer>
+                    <widget class="IconButton" row="2" column="1">
+                        <property name="name">
+                            <cstring>pb_defRosterDetails</cstring>
+                        </property>
+                        <property name="text">
+                            <string>&amp;Show Details</string>
+                        </property>
+                    </widget>
+                    <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+                        <property name="name">
+                            <cstring>textLabel3_2</cstring>
+                        </property>
+                        <property name="text">
+                            <string>Default roster iconset:</string>
+                        </property>
+                    </widget>
+                </grid>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Services</string>
+                </attribute>
+                <grid>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+                        <property name="name">
+                            <cstring>ck_useTransportIconsForContacts</cstring>
+                        </property>
+                        <property name="text">
+                            <string>Use transport specific icons for contacts</string>
+                        </property>
+                    </widget>
+                    <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2">
+                        <property name="name">
+                            <cstring>textLabel4</cstring>
+                        </property>
+                        <property name="text">
+                            <string>Service iconsets:</string>
+                        </property>
+                    </widget>
+                    <widget class="QLayoutWidget" row="2" column="1">
+                        <property name="name">
+                            <cstring>layout23</cstring>
+                        </property>
+                        <grid>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="IconsetSelect" row="0" column="0" rowspan="1" colspan="2">
+                                <property name="name">
+                                    <cstring>iss_servicesRoster</cstring>
+                                </property>
+                            </widget>
+                            <widget class="IconButton" row="1" column="1">
+                                <property name="name">
+                                    <cstring>pb_servicesDetails</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>&amp;Show Details</string>
+                                </property>
+                            </widget>
+                            <spacer row="1" column="0">
+                                <property name="name">
+                                    <cstring>spacer26</cstring>
+                                </property>
+                                <property name="orientation">
+                                    <enum>Horizontal</enum>
+                                </property>
+                                <property name="sizeType">
+                                    <enum>Expanding</enum>
+                                </property>
+                                <property name="sizeHint">
+                                    <size>
+                                        <width>41</width>
+                                        <height>20</height>
+                                    </size>
+                                </property>
+                            </spacer>
+                        </grid>
+                    </widget>
+                    <widget class="QListView" row="2" column="0">
+                        <column>
+                            <property name="text">
+                                <string>Service</string>
+                            </property>
+                            <property name="clickable">
+                                <bool>true</bool>
+                            </property>
+                            <property name="resizable">
+                                <bool>true</bool>
+                            </property>
+                        </column>
+                        <column>
+                            <property name="text">
+                                <string>Iconset</string>
+                            </property>
+                            <property name="clickable">
+                                <bool>true</bool>
+                            </property>
+                            <property name="resizable">
+                                <bool>true</bool>
+                            </property>
+                        </column>
+                        <column>
+                            <property name="text">
+                                <string>name</string>
+                            </property>
+                            <property name="clickable">
+                                <bool>true</bool>
+                            </property>
+                            <property name="resizable">
+                                <bool>true</bool>
+                            </property>
+                        </column>
+                    <item>
+                        <property name="text">
+                            <string>Default</string>
+                        </property>
+                        <property name="text">
+                            <string></string>
+                        </property>
+                        <property name="text">
+                            <string>transport</string>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>AIM</string>
+                        </property>
+                        <property name="text">
+                            <string></string>
+                        </property>
+                        <property name="text">
+                            <string>aim</string>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Gadu-Gadu</string>
+                        </property>
+                        <property name="text">
+                            <string></string>
+                        </property>
+                        <property name="text">
+                            <string>gadugadu</string>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>ICQ</string>
+                        </property>
+                        <property name="text">
+                            <string></string>
+                        </property>
+                        <property name="text">
+                            <string>icq</string>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>MSN</string>
+                        </property>
+                        <property name="text">
+                            <string></string>
+                        </property>
+                        <property name="text">
+                            <string>msn</string>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>SMS</string>
+                        </property>
+                        <property name="text">
+                            <string></string>
+                        </property>
+                        <property name="text">
+                            <string>sms</string>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Yahoo!</string>
+                        </property>
+                        <property name="text">
+                            <string></string>
+                        </property>
+                        <property name="text">
+                            <string>yahoo</string>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                        <property name="pixmap">
+                            <pixmap></pixmap>
+                        </property>
+                    </item>
+                        <property name="name">
+                            <cstring>lv_isServices</cstring>
+                        </property>
+                        <property name="sizePolicy">
+                            <sizepolicy>
+                                <hsizetype>4</hsizetype>
+                                <vsizetype>7</vsizetype>
+                                <horstretch>0</horstretch>
+                                <verstretch>0</verstretch>
+                            </sizepolicy>
+                        </property>
+                        <property name="allColumnsShowFocus">
+                            <bool>true</bool>
+                        </property>
+                    </widget>
+                </grid>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Custom</string>
+                </attribute>
+                <grid>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+                        <property name="name">
+                            <cstring>textLabel6</cstring>
+                        </property>
+                        <property name="text">
+                            <string>Custom iconsets:</string>
+                        </property>
+                    </widget>
+                    <widget class="QLayoutWidget" row="1" column="0">
+                        <property name="name">
+                            <cstring>layout25</cstring>
+                        </property>
+                        <grid>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="IconButton" row="1" column="0">
+                                <property name="name">
+                                    <cstring>pb_customRosterAdd</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>&amp;Add</string>
+                                </property>
+                            </widget>
+                            <widget class="IconButton" row="1" column="1">
+                                <property name="name">
+                                    <cstring>pb_customRosterDelete</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>&amp;Delete</string>
+                                </property>
+                            </widget>
+                            <widget class="QListView" row="0" column="0" rowspan="1" colspan="2">
+                                <column>
+                                    <property name="text">
+                                        <string>RegExp</string>
+                                    </property>
+                                    <property name="clickable">
+                                        <bool>true</bool>
+                                    </property>
+                                    <property name="resizable">
+                                        <bool>true</bool>
+                                    </property>
+                                </column>
+                                <column>
+                                    <property name="text">
+                                        <string>Iconset</string>
+                                    </property>
+                                    <property name="clickable">
+                                        <bool>true</bool>
+                                    </property>
+                                    <property name="resizable">
+                                        <bool>true</bool>
+                                    </property>
+                                </column>
+                                <property name="name">
+                                    <cstring>lv_customRoster</cstring>
+                                </property>
+                                <property name="allColumnsShowFocus">
+                                    <bool>true</bool>
+                                </property>
+                            </widget>
+                        </grid>
+                    </widget>
+                    <widget class="QLayoutWidget" row="1" column="1">
+                        <property name="name">
+                            <cstring>layout26</cstring>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QLabel">
+                                <property name="name">
+                                    <cstring>textLabel7</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Regular Expression:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit">
+                                <property name="name">
+                                    <cstring>le_customRoster</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLabel">
+                                <property name="name">
+                                    <cstring>textLabel8</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Iconset:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout24</cstring>
+                                </property>
+                                <grid>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <spacer row="1" column="0">
+                                        <property name="name">
+                                            <cstring>spacer27</cstring>
+                                        </property>
+                                        <property name="orientation">
+                                            <enum>Horizontal</enum>
+                                        </property>
+                                        <property name="sizeType">
+                                            <enum>Expanding</enum>
+                                        </property>
+                                        <property name="sizeHint">
+                                            <size>
+                                                <width>91</width>
+                                                <height>31</height>
+                                            </size>
+                                        </property>
+                                    </spacer>
+                                    <widget class="IconButton" row="1" column="1">
+                                        <property name="name">
+                                            <cstring>pb_customRosterDetails</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>&amp;Show Details</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="IconsetSelect" row="0" column="0" rowspan="1" colspan="2">
+                                        <property name="name">
+                                            <cstring>iss_customRoster</cstring>
+                                        </property>
+                                    </widget>
+                                </grid>
+                            </widget>
+                        </vbox>
+                    </widget>
+                </grid>
+            </widget>
+        </widget>
+        <widget class="QProgressBar">
+            <property name="name">
+                <cstring>progress</cstring>
+            </property>
+            <property name="progress">
+                <number>0</number>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_iconset_system.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_iconset_system.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_iconset_system.ui	(revision 38)
@@ -0,0 +1,85 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>IconsetSystemUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>IconsetSystemUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>178</width>
+            <height>203</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>IconsetSystemUI</string>
+    </property>
+    <grid>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+            <property name="name">
+                <cstring>textLabel1_2_2</cstring>
+            </property>
+            <property name="text">
+                <string>System Iconset:</string>
+            </property>
+        </widget>
+        <widget class="IconsetSelect" row="1" column="0" rowspan="1" colspan="2">
+            <property name="name">
+                <cstring>iss_system</cstring>
+            </property>
+            <property name="font">
+                <font>
+                    <bold>1</bold>
+                </font>
+            </property>
+        </widget>
+        <spacer row="2" column="0">
+            <property name="name">
+                <cstring>spacer22_2</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>305</width>
+                    <height>20</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="IconButton" row="2" column="1">
+            <property name="name">
+                <cstring>pb_sysDetails</cstring>
+            </property>
+            <property name="text">
+                <string>&amp;Show Details</string>
+            </property>
+            <property name="accel">
+                <string>Alt+S</string>
+            </property>
+        </widget>
+        <widget class="QProgressBar" row="3" column="0" rowspan="1" colspan="2">
+            <property name="name">
+                <cstring>progress</cstring>
+            </property>
+        </widget>
+    </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_lookfeel.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_lookfeel.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_lookfeel.cpp	(revision 38)
@@ -0,0 +1,40 @@
+#include "opt_lookfeel.h"
+#include "common.h"
+#include "iconwidget.h"
+#include "mainwin.h"
+#include "psicon.h"
+#include "psitoolbar.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabLookFeelToolbars
+//----------------------------------------------------------------------------
+
+OptionsTabLookFeelToolbars::OptionsTabLookFeelToolbars(QObject *parent)
+: OptionsTab(parent, "", "", "", "")
+{
+}
+
+QWidget *OptionsTabLookFeelToolbars::widget()
+{
+	return 0;
+}
+
+void OptionsTabLookFeelToolbars::applyOptions(Options *o)
+{
+	o->toolbars = option.toolbars;
+
+	// get current toolbars' positions
+	MainWin *mainWin = (MainWin *)psi->mainWin();
+	for (uint i = 0; i < o->toolbars.count() && i < mainWin->toolbars.count(); i++) {
+		//if ( toolbarPositionInProgress && posTbDlg->n() == (int)i )
+		//	continue;
+
+		Options::ToolbarPrefs &tbPref = o->toolbars["mainWin"][i];
+		mainWin->getLocation ( mainWin->toolbars.at(i), tbPref.dock, tbPref.index, tbPref.nl, tbPref.extraOffset );
+	}
+}
+
+void OptionsTabLookFeelToolbars::setData(PsiCon *p, QWidget *)
+{
+	psi = p;
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_lookfeel.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_lookfeel.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_lookfeel.h	(revision 38)
@@ -0,0 +1,25 @@
+#ifndef OPT_LOOKFEEL_H
+#define OPT_LOOKFEEL_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+
+class OptionsTabLookFeelToolbars : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabLookFeelToolbars(QObject *parent);
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+
+private slots:
+	void setData(PsiCon *, QWidget *);
+
+private:
+	PsiCon *psi;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_lookfeel_colors.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_lookfeel_colors.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_lookfeel_colors.ui	(revision 38)
@@ -0,0 +1,255 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>LookFeelColorsUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>LookFeelColorsUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>202</width>
+            <height>288</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>LookFeelColorsUI</string>
+    </property>
+    <grid>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QLabel" row="1" column="0">
+            <property name="name">
+                <cstring>TextLabel1_2_3</cstring>
+            </property>
+            <property name="text">
+                <string>Offline</string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="3" column="0">
+            <property name="name">
+                <cstring>TextLabel1_2_6_2</cstring>
+            </property>
+            <property name="text">
+                <string>DND</string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="3" column="1">
+            <property name="name">
+                <cstring>pb_cDND</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="2" column="1">
+            <property name="name">
+                <cstring>pb_cAway</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="6" column="1">
+            <property name="name">
+                <cstring>pb_cListBack</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="0" column="1">
+            <property name="name">
+                <cstring>pb_cOnline</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="0" column="0">
+            <property name="name">
+                <cstring>TextLabel1_2_6</cstring>
+            </property>
+            <property name="text">
+                <string>Online</string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="5" column="0">
+            <property name="name">
+                <cstring>text</cstring>
+            </property>
+            <property name="text">
+                <string>Group Background</string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="8" column="1">
+            <property name="name">
+                <cstring>pb_cAnimBack</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="5" column="1">
+            <property name="name">
+                <cstring>pb_cGroupBack</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="7" column="1">
+            <property name="name">
+                <cstring>pb_cAnimFront</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="4" column="0">
+            <property name="name">
+                <cstring>TextLabel1_2_5</cstring>
+            </property>
+            <property name="text">
+                <string>Group Foreground</string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="8" column="0">
+            <property name="name">
+                <cstring>text4</cstring>
+            </property>
+            <property name="text">
+                <string>Nick Anim Background</string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="4" column="1">
+            <property name="name">
+                <cstring>pb_cGroupFore</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="6" column="0">
+            <property name="name">
+                <cstring>text2</cstring>
+            </property>
+            <property name="text">
+                <string>List Background</string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="1" column="1">
+            <property name="name">
+                <cstring>pb_cOffline</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>0</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="7" column="0">
+            <property name="name">
+                <cstring>text3</cstring>
+            </property>
+            <property name="text">
+                <string>Nick Anim Foreground</string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="2" column="0">
+            <property name="name">
+                <cstring>TextLabel1_2</cstring>
+            </property>
+            <property name="text">
+                <string>Away</string>
+            </property>
+        </widget>
+    </grid>
+</widget>
+<tabstops>
+    <tabstop>pb_cOnline</tabstop>
+    <tabstop>pb_cOffline</tabstop>
+    <tabstop>pb_cAway</tabstop>
+    <tabstop>pb_cDND</tabstop>
+    <tabstop>pb_cGroupFore</tabstop>
+    <tabstop>pb_cGroupBack</tabstop>
+    <tabstop>pb_cListBack</tabstop>
+    <tabstop>pb_cAnimFront</tabstop>
+    <tabstop>pb_cAnimBack</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_lookfeel_fonts.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_lookfeel_fonts.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_lookfeel_fonts.ui	(revision 38)
@@ -0,0 +1,117 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>LookFeelFontsUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>LookFeelFontsUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>251</width>
+            <height>96</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>LookFeelFontsUI</string>
+    </property>
+    <grid>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QLabel" row="1" column="0">
+            <property name="name">
+                <cstring>TextLabel1_4</cstring>
+            </property>
+            <property name="text">
+                <string>Message</string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="2" column="0">
+            <property name="name">
+                <cstring>TextLabel1_3</cstring>
+            </property>
+            <property name="enabled">
+                <bool>true</bool>
+            </property>
+            <property name="text">
+                <string>Chat</string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="0" column="0">
+            <property name="name">
+                <cstring>TextLabel1</cstring>
+            </property>
+            <property name="text">
+                <string>Roster</string>
+            </property>
+        </widget>
+        <widget class="QLineEdit" row="1" column="1">
+            <property name="name">
+                <cstring>le_fMessage</cstring>
+            </property>
+            <property name="readOnly">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="1" column="2">
+            <property name="name">
+                <cstring>pb_fMessage</cstring>
+            </property>
+            <property name="text">
+                <string>Choose</string>
+            </property>
+        </widget>
+        <widget class="QLineEdit" row="2" column="1">
+            <property name="name">
+                <cstring>le_fChat</cstring>
+            </property>
+            <property name="enabled">
+                <bool>true</bool>
+            </property>
+            <property name="readOnly">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="2" column="2">
+            <property name="name">
+                <cstring>pb_fChat</cstring>
+            </property>
+            <property name="enabled">
+                <bool>true</bool>
+            </property>
+            <property name="text">
+                <string>Choose</string>
+            </property>
+        </widget>
+        <widget class="QLineEdit" row="0" column="1">
+            <property name="name">
+                <cstring>le_fRoster</cstring>
+            </property>
+            <property name="readOnly">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="0" column="2">
+            <property name="name">
+                <cstring>pb_fRoster</cstring>
+            </property>
+            <property name="text">
+                <string>Choose</string>
+            </property>
+        </widget>
+    </grid>
+</widget>
+<tabstops>
+    <tabstop>le_fRoster</tabstop>
+    <tabstop>pb_fRoster</tabstop>
+    <tabstop>le_fMessage</tabstop>
+    <tabstop>pb_fMessage</tabstop>
+    <tabstop>le_fChat</tabstop>
+    <tabstop>pb_fChat</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_lookfeel_misc.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_lookfeel_misc.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_lookfeel_misc.ui	(revision 38)
@@ -0,0 +1,36 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>LookFeelMiscUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>LookFeelMiscUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>253</width>
+            <height>27</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>LookFeelMiscUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_newHeadings</cstring>
+            </property>
+            <property name="text">
+                <string>Small group headings</string>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_lookfeel_toolbars.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_lookfeel_toolbars.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_lookfeel_toolbars.ui	(revision 38)
@@ -0,0 +1,573 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>LookFeelToolbarsUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>LookFeelToolbarsUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>441</width>
+            <height>339</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>LookFeelToolbarsUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout15</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>textLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Toolbar:</string>
+                    </property>
+                </widget>
+                <widget class="QComboBox">
+                    <property name="name">
+                        <cstring>cb_toolbars</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>7</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer11</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Maximum</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>30</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_addToolbar</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Add</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+A</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_deleteToolbar</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Delete</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+D</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line2</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout11</cstring>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout129</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>textLabel20</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Name:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit">
+                            <property name="name">
+                                <cstring>le_toolbarName</cstring>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout10</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QCheckBox">
+                            <property name="name">
+                                <cstring>ck_toolbarOn</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Enabled</string>
+                            </property>
+                            <property name="whatsThis" stdset="0">
+                                <string>Specifies, whether toolbar is enabled and visible.</string>
+                            </property>
+                        </widget>
+                        <widget class="QCheckBox">
+                            <property name="name">
+                                <cstring>ck_toolbarLocked</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Locked</string>
+                            </property>
+                            <property name="whatsThis" stdset="0">
+                                <string>Specifies, whether it is possible to move toolbar.</string>
+                            </property>
+                        </widget>
+                        <widget class="QCheckBox">
+                            <property name="name">
+                                <cstring>ck_toolbarStretch</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Stretchable</string>
+                            </property>
+                            <property name="whatsThis" stdset="0">
+                                <string>Specifies, whether toolbar is stretchable.
+Note: it is impossible to dock stretchable toolbar. Uncheck that check box, click apply, dock the toolbar, and then check stretchable again.</string>
+                            </property>
+                        </widget>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_toolbarPosition</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Change Position (Advanced)</string>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>spacer9_2</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Horizontal</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>40</width>
+                                    <height>20</height>
+                                </size>
+                            </property>
+                        </spacer>
+                    </hbox>
+                </widget>
+            </vbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line3</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout14</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout11</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>textLabel25</cstring>
+                            </property>
+                            <property name="text">
+                                <string>A&amp;vailable Actions:</string>
+                            </property>
+                            <property name="buddy" stdset="0">
+                                <cstring>lv_availActions</cstring>
+                            </property>
+                        </widget>
+                        <widget class="QListView">
+                            <column>
+                                <property name="text">
+                                    <string>Name</string>
+                                </property>
+                                <property name="clickable">
+                                    <bool>true</bool>
+                                </property>
+                                <property name="resizable">
+                                    <bool>true</bool>
+                                </property>
+                            </column>
+                            <property name="name">
+                                <cstring>lv_availActions</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>7</hsizetype>
+                                    <vsizetype>7</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                            <property name="minimumSize">
+                                <size>
+                                    <width>0</width>
+                                    <height>200</height>
+                                </size>
+                            </property>
+                        </widget>
+                    </vbox>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout13</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <spacer>
+                            <property name="name">
+                                <cstring>spacer9</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Vertical</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>20</width>
+                                    <height>40</height>
+                                </size>
+                            </property>
+                        </spacer>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout7</cstring>
+                            </property>
+                            <grid>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <property name="spacing">
+                                    <number>0</number>
+                                </property>
+                                <spacer row="2" column="0">
+                                    <property name="name">
+                                        <cstring>spacer7</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Maximum</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>20</width>
+                                            <height>20</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                                <widget class="IconToolButton" row="0" column="1">
+                                    <property name="name">
+                                        <cstring>tb_up</cstring>
+                                    </property>
+                                    <property name="sizePolicy">
+                                        <sizepolicy>
+                                            <hsizetype>4</hsizetype>
+                                            <vsizetype>1</vsizetype>
+                                            <horstretch>0</horstretch>
+                                            <verstretch>0</verstretch>
+                                        </sizepolicy>
+                                    </property>
+                                    <property name="text">
+                                        <string>...</string>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/arrowUp</string>
+                                    </property>
+                                </widget>
+                                <spacer row="1" column="1">
+                                    <property name="name">
+                                        <cstring>spacer6</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Maximum</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>20</width>
+                                            <height>20</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                                <widget class="IconToolButton" row="1" column="0">
+                                    <property name="name">
+                                        <cstring>tb_left</cstring>
+                                    </property>
+                                    <property name="sizePolicy">
+                                        <sizepolicy>
+                                            <hsizetype>4</hsizetype>
+                                            <vsizetype>1</vsizetype>
+                                            <horstretch>0</horstretch>
+                                            <verstretch>0</verstretch>
+                                        </sizepolicy>
+                                    </property>
+                                    <property name="text">
+                                        <string>...</string>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/arrowLeft</string>
+                                    </property>
+                                </widget>
+                                <spacer row="0" column="2">
+                                    <property name="name">
+                                        <cstring>spacer5</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Maximum</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>20</width>
+                                            <height>20</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                                <spacer row="0" column="0">
+                                    <property name="name">
+                                        <cstring>spacer4</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Maximum</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>20</width>
+                                            <height>20</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                                <spacer row="2" column="2">
+                                    <property name="name">
+                                        <cstring>spacer8</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Maximum</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>20</width>
+                                            <height>20</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                                <widget class="IconToolButton" row="2" column="1">
+                                    <property name="name">
+                                        <cstring>tb_down</cstring>
+                                    </property>
+                                    <property name="sizePolicy">
+                                        <sizepolicy>
+                                            <hsizetype>4</hsizetype>
+                                            <vsizetype>1</vsizetype>
+                                            <horstretch>0</horstretch>
+                                            <verstretch>0</verstretch>
+                                        </sizepolicy>
+                                    </property>
+                                    <property name="text">
+                                        <string>...</string>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/arrowDown</string>
+                                    </property>
+                                </widget>
+                                <widget class="IconToolButton" row="1" column="2">
+                                    <property name="name">
+                                        <cstring>tb_right</cstring>
+                                    </property>
+                                    <property name="sizePolicy">
+                                        <sizepolicy>
+                                            <hsizetype>4</hsizetype>
+                                            <vsizetype>1</vsizetype>
+                                            <horstretch>0</horstretch>
+                                            <verstretch>0</verstretch>
+                                        </sizepolicy>
+                                    </property>
+                                    <property name="text">
+                                        <string>...</string>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/arrowRight</string>
+                                    </property>
+                                </widget>
+                            </grid>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>spacer10</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Vertical</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>20</width>
+                                    <height>40</height>
+                                </size>
+                            </property>
+                        </spacer>
+                    </vbox>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout12</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>textLabel24</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Curr&amp;ent Actions:</string>
+                            </property>
+                            <property name="buddy" stdset="0">
+                                <cstring>lv_selectedActions</cstring>
+                            </property>
+                        </widget>
+                        <widget class="QListView">
+                            <column>
+                                <property name="text">
+                                    <string>Name</string>
+                                </property>
+                                <property name="clickable">
+                                    <bool>true</bool>
+                                </property>
+                                <property name="resizable">
+                                    <bool>true</bool>
+                                </property>
+                            </column>
+                            <property name="name">
+                                <cstring>lv_selectedActions</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>7</hsizetype>
+                                    <vsizetype>7</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                        </widget>
+                    </vbox>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<tabstops>
+    <tabstop>cb_toolbars</tabstop>
+    <tabstop>pb_addToolbar</tabstop>
+    <tabstop>pb_deleteToolbar</tabstop>
+    <tabstop>le_toolbarName</tabstop>
+    <tabstop>ck_toolbarOn</tabstop>
+    <tabstop>ck_toolbarLocked</tabstop>
+    <tabstop>ck_toolbarStretch</tabstop>
+    <tabstop>pb_toolbarPosition</tabstop>
+    <tabstop>lv_availActions</tabstop>
+    <tabstop>lv_selectedActions</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_presence.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_presence.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_presence.cpp	(revision 38)
@@ -0,0 +1,328 @@
+#include "opt_presence.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qbuttongroup.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qtextedit.h>
+#include <qinputdialog.h>
+
+#include "opt_presence_auto.h"
+#include "opt_presence_presets.h"
+#include "opt_presence_misc.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabPresence
+//----------------------------------------------------------------------------
+
+OptionsTabPresence::OptionsTabPresence(QObject *parent)
+: MetaOptionsTab(parent, "presence", "", tr("Presence"), tr("Configure the presence options"), "status/online")
+{
+	addTab( new OptionsTabPresenceAuto(this) );
+	addTab( new OptionsTabPresencePresets(this) );
+	addTab( new OptionsTabPresenceMisc(this) );
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabPresenceAuto
+//----------------------------------------------------------------------------
+
+OptionsTabPresenceAuto::OptionsTabPresenceAuto(QObject *parent)
+: OptionsTab(parent, "presence_auto", "presence", tr("Auto status"), tr("Configure your auto-idle status"))
+{
+	w = 0;
+}
+
+QWidget *OptionsTabPresenceAuto::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new PresenceAutoUI;
+	PresenceAutoUI *d = (PresenceAutoUI *)w;
+
+	QString s = tr("Makes Psi automatically set your status to \"away\" if your"
+		" computer is idle for the specified amount of time.");
+	QWhatsThis::add(d->ck_asAway, s);
+	QWhatsThis::add(d->sb_asAway, s);
+	s = tr("Makes Psi automatically set your status to \"extended away\" if your"
+		" computer is idle for the specified amount of time.");
+	QWhatsThis::add(d->ck_asXa, s);
+	QWhatsThis::add(d->sb_asXa, s);
+	s = tr("Makes Psi automatically set your status to \"offline\" if your"
+		" computer is idle for the specified amount of time."
+		"  This will disconnect you from the Jabber server.");
+	QWhatsThis::add(d->ck_asOffline, s);
+	QWhatsThis::add(d->sb_asOffline, s);
+
+	QWhatsThis::add(d->te_asMessage,
+		tr("Specifies an extended message to use if you allow Psi"
+		" to set your status automatically.  See options above."));
+
+	return w;
+}
+
+void OptionsTabPresenceAuto::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	PresenceAutoUI *d = (PresenceAutoUI *)w;
+	opt->asAway = d->sb_asAway->value();
+	opt->asXa = d->sb_asXa->value();
+	opt->asOffline = d->sb_asOffline->value();
+	opt->use_asAway = d->ck_asAway->isChecked();
+	opt->use_asXa = d->ck_asXa->isChecked();
+	opt->use_asOffline = d->ck_asOffline->isChecked();
+	opt->asMessage = d->te_asMessage->text();
+}
+
+void OptionsTabPresenceAuto::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	PresenceAutoUI *d = (PresenceAutoUI *)w;
+	d->sb_asAway->setMinValue(0);
+	d->sb_asAway->setValue( opt->asAway );
+	d->sb_asXa->setMinValue(0);
+	d->sb_asXa->setValue( opt->asXa );
+	d->sb_asOffline->setMinValue(0);
+	d->sb_asOffline->setValue( opt->asOffline );
+	/*if (opt->asAway <= 0 )
+		opt->use_asAway = FALSE;
+	if (opt->asXa <= 0 )
+		opt->use_asXa = FALSE;
+	if(d->opt.asOffline <= 0)
+		opt->use_asOffline = FALSE;*/
+	d->ck_asAway->setChecked( opt->use_asAway );
+	d->ck_asXa->setChecked( opt->use_asXa );
+	d->ck_asOffline->setChecked( opt->use_asOffline );
+	d->te_asMessage->setText( opt->asMessage );
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabPresencePresets
+//----------------------------------------------------------------------------
+
+OptionsTabPresencePresets::OptionsTabPresencePresets(QObject *parent)
+: OptionsTab(parent, "presence_presets", "presence", tr("Presets"), tr("Set up custom status presets"))
+{
+	w = 0;
+	o = new Options;
+}
+
+OptionsTabPresencePresets::~OptionsTabPresencePresets()
+{
+	delete o;
+}
+
+QWidget *OptionsTabPresencePresets::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new PresencePresetsUI;
+	PresencePresetsUI *d = (PresencePresetsUI *)w;
+
+	d->pb_spNew->setEnabled(TRUE);
+	d->pb_spDelete->setEnabled(FALSE);
+	d->te_sp->setEnabled(FALSE);
+	connect(d->pb_spNew, SIGNAL(clicked()), SLOT(newStatusPreset()));
+	connect(d->pb_spDelete, SIGNAL(clicked()), SLOT(removeStatusPreset()));
+	connect(d->lb_sp, SIGNAL(highlighted(int)), SLOT(selectStatusPreset(int)));
+	connect(d->te_sp, SIGNAL(textChanged()), SLOT(changeStatusPreset()));
+
+	QWhatsThis::add(d->pb_spNew,
+		tr("Press this button to create a new status message preset."));
+	QWhatsThis::add(d->pb_spDelete,
+		tr("Press this button to delete a status message preset."));
+	QWhatsThis::add(d->lb_sp,
+		tr("Use this list to select a status message preset"
+		" to view or edit in the box to the right."));
+	QWhatsThis::add(d->te_sp,
+		tr("You may edit the message here for the currently selected"
+		" status message preset in the list to the left."));
+
+	return w;
+}
+
+void OptionsTabPresencePresets::setData(PsiCon *, QWidget *parentDialog)
+{
+	parentWidget = parentDialog;
+}
+
+void OptionsTabPresencePresets::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	opt->sp = o->sp;
+}
+
+void OptionsTabPresencePresets::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	PresencePresetsUI *d = (PresencePresetsUI *)w;
+
+	o->sp = opt->sp;
+	d->lb_sp->insertStringList(o->sp.varsToStringList());
+	if(d->lb_sp->count() >= 1)
+		d->lb_sp->setSelected(0, TRUE);
+}
+
+void OptionsTabPresencePresets::selectStatusPreset(int x)
+{
+	PresencePresetsUI *d = (PresencePresetsUI *)w;
+
+	//noDirty = TRUE;
+	disconnect(d->te_sp, SIGNAL(textChanged()), 0, 0);
+	if ( x == -1 ) {
+		d->pb_spDelete->setEnabled(false);
+		d->te_sp->setText("");
+		d->te_sp->setEnabled(false);
+
+		//noDirty = FALSE;
+		connect(d->te_sp, SIGNAL(textChanged()), SLOT(changeStatusPreset()));
+		return;
+	}
+
+	d->pb_spDelete->setEnabled(true);
+
+	d->te_sp->setText( o->sp.get( d->lb_sp->text(x)) );
+	d->te_sp->setEnabled(true);
+
+	//noDirty = FALSE;
+	connect(d->te_sp, SIGNAL(textChanged()), SLOT(changeStatusPreset()));
+}
+
+void OptionsTabPresencePresets::newStatusPreset()
+{
+	PresencePresetsUI *d = (PresencePresetsUI *)w;
+
+	QString text;
+
+	while(1) {
+		bool ok = FALSE;
+		text = QInputDialog::getText(
+			CAP(tr("New Status Preset")),
+			tr("Please enter a name for the new status preset:"),
+			QLineEdit::Normal, text, &ok, parentWidget);
+		if(!ok)
+			return;
+
+		if(text.isEmpty())
+			QMessageBox::information(parentWidget, tr("Error"), tr("Can't create a blank preset!"));
+		else if(o->sp.findByKey(text) != o->sp.end())
+			QMessageBox::information(parentWidget, tr("Error"), tr("You already have a preset with that name!"));
+		else
+			break;
+	}
+
+	o->sp.set(text, "");
+	d->lb_sp->insertItem(text);
+	d->lb_sp->setSelected(d->lb_sp->count()-1, TRUE);
+	d->te_sp->setFocus();
+
+	emit dataChanged();
+}
+
+void OptionsTabPresencePresets::removeStatusPreset()
+{
+	PresencePresetsUI *d = (PresencePresetsUI *)w;
+	int id = d->lb_sp->currentItem();
+	if(id == -1)
+		return;
+
+	emit dataChanged();
+
+	o->sp.unset(d->lb_sp->text(id));
+	d->lb_sp->removeItem(id);
+
+	// select a new entry if possible
+	if(d->lb_sp->count() == 0) {
+		selectStatusPreset(-1);
+		return;
+	}
+
+	if(id >= (int)d->lb_sp->count())
+		id = d->lb_sp->count()-1;
+
+	d->lb_sp->setSelected(id, TRUE);
+	selectStatusPreset(id);
+}
+
+void OptionsTabPresencePresets::changeStatusPreset()
+{
+	PresencePresetsUI *d = (PresencePresetsUI *)w;
+	int id = d->lb_sp->currentItem();
+	if(id == -1)
+		return;
+
+	o->sp.set(d->lb_sp->text(id), d->te_sp->text());
+	emit dataChanged();
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabPresenceMisc
+//----------------------------------------------------------------------------
+
+OptionsTabPresenceMisc::OptionsTabPresenceMisc(QObject *parent)
+: OptionsTab(parent, "presence_misc", "presence", tr("Misc"), tr("Miscellaneous settings"))
+{
+	w = 0;
+}
+
+QWidget *OptionsTabPresenceMisc::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new PresenceMiscUI;
+	PresenceMiscUI *d = (PresenceMiscUI *)w;
+
+	QWhatsThis::add(d->ck_askOnline,
+		tr("Jabber allows you to put extended status messages on"
+		" all status types.  Normally, Psi does not prompt you for"
+		" an extended message when you set your status to \"online\"."
+		"  Check this option if you want to have this prompt."));
+	QWhatsThis::add(d->ck_rosterAnim,
+		tr("Makes Psi animate contact names in the main window when they come online."));
+	QWhatsThis::add(d->ck_autoVersion,
+		tr("Automatically sends iq:version query to contact when he becomes online. This allows you to determine what clien he is using to get online. Note: results in increased traffic."));
+	QWhatsThis::add(d->ck_autoVCardOnLogin,
+		tr("By default, Psi always checks your vCard on login. If you want to save some traffic, you can uncheck this option."));
+
+	return w;
+}
+
+void OptionsTabPresenceMisc::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	PresenceMiscUI *d = (PresenceMiscUI *)w;
+	opt->askOnline = d->ck_askOnline->isChecked();
+	opt->rosterAnim = d->ck_rosterAnim->isChecked();
+	opt->autoVersion = d->ck_autoVersion->isChecked();
+	opt->autoVCardOnLogin = d->ck_autoVCardOnLogin->isChecked();
+}
+
+void OptionsTabPresenceMisc::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	PresenceMiscUI *d = (PresenceMiscUI *)w;
+	d->ck_askOnline->setChecked( opt->askOnline );
+	d->ck_rosterAnim->setChecked( opt->rosterAnim );
+	d->ck_autoVersion->setChecked( opt->autoVersion );
+	d->ck_autoVCardOnLogin->setChecked( opt->autoVCardOnLogin );
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_presence.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_presence.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_presence.h	(revision 38)
@@ -0,0 +1,68 @@
+#ifndef OPT_PRESENCE_H
+#define OPT_PRESENCE_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+
+class OptionsTabPresence : public MetaOptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabPresence(QObject *parent);
+};
+
+class OptionsTabPresenceAuto : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabPresenceAuto(QObject *parent);
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private:
+	QWidget *w;
+};
+
+class OptionsTabPresencePresets : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabPresencePresets(QObject *parent);
+	~OptionsTabPresencePresets();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+	void setData(PsiCon *, QWidget *parentDialog);
+
+private slots:
+	void selectStatusPreset(int x);
+	void newStatusPreset();
+	void removeStatusPreset();
+	void changeStatusPreset();
+
+private:
+	QWidget *w, *parentWidget;
+	Options *o;
+};
+
+class OptionsTabPresenceMisc : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabPresenceMisc(QObject *parent);
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private:
+	QWidget *w;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_presence_auto.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_presence_auto.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_presence_auto.ui	(revision 38)
@@ -0,0 +1,185 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PresenceAutoUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>PresenceAutoUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>242</width>
+            <height>245</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>PresenceAutoUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout15</cstring>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QSpinBox" row="2" column="1">
+                    <property name="name">
+                        <cstring>sb_asOffline</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="2">
+                    <property name="name">
+                        <cstring>lb_asOffline</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="text">
+                        <string>Minutes</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="2">
+                    <property name="name">
+                        <cstring>lb_asXa</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="text">
+                        <string>Minutes</string>
+                    </property>
+                </widget>
+                <widget class="QSpinBox" row="1" column="1">
+                    <property name="name">
+                        <cstring>sb_asXa</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="2">
+                    <property name="name">
+                        <cstring>lb_asAway</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="text">
+                        <string>Minutes</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox" row="2" column="0">
+                    <property name="name">
+                        <cstring>ck_asOffline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Auto Offline After:</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox" row="1" column="0">
+                    <property name="name">
+                        <cstring>ck_asXa</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Auto XA After:</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox" row="0" column="0">
+                    <property name="name">
+                        <cstring>ck_asAway</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Auto Away After:</string>
+                    </property>
+                </widget>
+                <widget class="QSpinBox" row="0" column="1">
+                    <property name="name">
+                        <cstring>sb_asAway</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>TextLabel1_8</cstring>
+            </property>
+            <property name="text">
+                <string>Auto Status Message:</string>
+            </property>
+        </widget>
+        <widget class="QTextEdit">
+            <property name="name">
+                <cstring>te_asMessage</cstring>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>ck_asAway</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>sb_asAway</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asAway</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>lb_asAway</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asXa</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>sb_asXa</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asXa</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>lb_asXa</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asOffline</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>sb_asOffline</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asOffline</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>lb_asOffline</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+</connections>
+<tabstops>
+    <tabstop>ck_asAway</tabstop>
+    <tabstop>sb_asAway</tabstop>
+    <tabstop>ck_asXa</tabstop>
+    <tabstop>sb_asXa</tabstop>
+    <tabstop>ck_asOffline</tabstop>
+    <tabstop>sb_asOffline</tabstop>
+    <tabstop>te_asMessage</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_presence_misc.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_presence_misc.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_presence_misc.ui	(revision 38)
@@ -0,0 +1,60 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PresenceMiscUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>PresenceMiscUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>329</width>
+            <height>106</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string></string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_askOnline</cstring>
+            </property>
+            <property name="text">
+                <string>Prompt for status message when choosing "Online"</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_rosterAnim</cstring>
+            </property>
+            <property name="text">
+                <string>Animate the roster when a contact comes online</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autoVersion</cstring>
+            </property>
+            <property name="text">
+                <string>Check client version when a contact comes online</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autoVCardOnLogin</cstring>
+            </property>
+            <property name="text">
+                <string>Automatically get vCard when becoming online</string>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_presence_presets.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_presence_presets.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_presence_presets.ui	(revision 38)
@@ -0,0 +1,103 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PresencePresetsUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>PresencePresetsUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>238</width>
+            <height>200</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string></string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout29</cstring>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QListBox" row="0" column="0" rowspan="3" colspan="1">
+                    <property name="name">
+                        <cstring>lb_sp</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>7</hsizetype>
+                            <vsizetype>5</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>32767</width>
+                            <height>32767</height>
+                        </size>
+                    </property>
+                </widget>
+                <widget class="QPushButton" row="0" column="1">
+                    <property name="name">
+                        <cstring>pb_spNew</cstring>
+                    </property>
+                    <property name="text">
+                        <string>New</string>
+                    </property>
+                </widget>
+                <spacer row="1" column="1">
+                    <property name="name">
+                        <cstring>Spacer6</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Vertical</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Maximum</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>16</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QPushButton" row="2" column="1">
+                    <property name="name">
+                        <cstring>pb_spDelete</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Delete</string>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>TextLabel2_2</cstring>
+            </property>
+            <property name="text">
+                <string>Content:</string>
+            </property>
+        </widget>
+        <widget class="QTextEdit">
+            <property name="name">
+                <cstring>te_sp</cstring>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_sound-ui.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_sound-ui.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_sound-ui.ui	(revision 38)
@@ -0,0 +1,468 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>OptSoundUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>OptSoundUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>355</width>
+            <height>437</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>OptSoundUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout18</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_player</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Player:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit">
+                    <property name="name">
+                        <cstring>le_player</cstring>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_awaySound</cstring>
+            </property>
+            <property name="text">
+                <string>Play sounds when Away/XA</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_gcSound</cstring>
+            </property>
+            <property name="text">
+                <string>Play sounds for all messages in groupchat</string>
+            </property>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line8</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout9</cstring>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="IconToolButton" row="9" column="3">
+                    <property name="name">
+                        <cstring>tb_seFTCompletePlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="9" column="1">
+                    <property name="name">
+                        <cstring>le_oeFTComplete</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="3" column="0">
+                    <property name="name">
+                        <cstring>TextLabel_Headline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Headline:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="1" column="1">
+                    <property name="name">
+                        <cstring>le_oeChat1</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="5" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_4_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive Online Status:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="5" column="1">
+                    <property name="name">
+                        <cstring>le_oeOnline</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="6" column="2">
+                    <property name="name">
+                        <cstring>tb_seOffline</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="0" column="2">
+                    <property name="name">
+                        <cstring>tb_seMessage</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="8" column="1">
+                    <property name="name">
+                        <cstring>le_oeIncomingFT</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_6</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive Message:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="7" column="2">
+                    <property name="name">
+                        <cstring>tb_seSend</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="6" column="3">
+                    <property name="name">
+                        <cstring>tb_seOfflinePlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="5" column="2">
+                    <property name="name">
+                        <cstring>tb_seOnline</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="4" column="2">
+                    <property name="name">
+                        <cstring>tb_seSystem</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="0" column="1">
+                    <property name="name">
+                        <cstring>le_oeMessage</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="8" column="3">
+                    <property name="name">
+                        <cstring>tb_seIncomingFTPlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="7" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_5_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Send Message:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="3" column="3">
+                    <property name="name">
+                        <cstring>tb_seHeadlinePlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="3" column="1">
+                    <property name="name">
+                        <cstring>le_oeHeadline</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="4" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_3_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>System Message:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="8" column="2">
+                    <property name="name">
+                        <cstring>tb_seIncomingFT</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="5" column="3">
+                    <property name="name">
+                        <cstring>tb_seOnlinePlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="4" column="3">
+                    <property name="name">
+                        <cstring>tb_seSystemPlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="2" column="3">
+                    <property name="name">
+                        <cstring>tb_seChat2Play</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="3" column="2">
+                    <property name="name">
+                        <cstring>tb_seHeadline</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="2" column="2">
+                    <property name="name">
+                        <cstring>tb_seChat2</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="1" column="3">
+                    <property name="name">
+                        <cstring>tb_seChat1Play</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="1" column="2">
+                    <property name="name">
+                        <cstring>tb_seChat1</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="2" column="1">
+                    <property name="name">
+                        <cstring>le_oeChat2</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="8" column="0">
+                    <property name="name">
+                        <cstring>textLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Incoming File Transfer:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="9" column="0">
+                    <property name="name">
+                        <cstring>textLabel2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>File Transfer Complete:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="6" column="1">
+                    <property name="name">
+                        <cstring>le_oeOffline</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="7" column="1">
+                    <property name="name">
+                        <cstring>le_oeSend</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="7" column="3">
+                    <property name="name">
+                        <cstring>tb_seSendPlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="4" column="1">
+                    <property name="name">
+                        <cstring>le_oeSystem</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_2_3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive Next Chat:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="0" column="3">
+                    <property name="name">
+                        <cstring>tb_seMessagePlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_2_2_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive First Chat:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="9" column="2">
+                    <property name="name">
+                        <cstring>tb_seFTComplete</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="6" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_4_2_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive Offline Status:</string>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout27</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer9_2</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>276</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>pb_soundReset</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Reset to defaults</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<tabstops>
+    <tabstop>le_player</tabstop>
+    <tabstop>ck_awaySound</tabstop>
+    <tabstop>ck_gcSound</tabstop>
+    <tabstop>le_oeMessage</tabstop>
+    <tabstop>le_oeChat1</tabstop>
+    <tabstop>le_oeChat2</tabstop>
+    <tabstop>le_oeHeadline</tabstop>
+    <tabstop>le_oeSystem</tabstop>
+    <tabstop>le_oeOnline</tabstop>
+    <tabstop>le_oeOffline</tabstop>
+    <tabstop>le_oeSend</tabstop>
+    <tabstop>le_oeIncomingFT</tabstop>
+    <tabstop>le_oeFTComplete</tabstop>
+    <tabstop>pb_soundReset</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_sound.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_sound.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_sound.cpp	(revision 38)
@@ -0,0 +1,194 @@
+#include "opt_sound.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qbuttongroup.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qfiledialog.h>
+#include <qlabel.h>
+
+#include "opt_sound-ui.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabSound
+//----------------------------------------------------------------------------
+
+OptionsTabSound::OptionsTabSound(QObject *parent)
+: OptionsTab(parent, "sound", "", tr("Sound"), tr("Configure how Psi sounds"), "psi/playSounds")
+{
+	w = 0;
+	bg_se = bg_sePlay = 0;
+}
+
+OptionsTabSound::~OptionsTabSound()
+{
+	if ( bg_se )
+		delete bg_se;
+	if ( bg_sePlay )
+		delete bg_sePlay;
+}
+
+QWidget *OptionsTabSound::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new OptSoundUI();
+	OptSoundUI *d = (OptSoundUI *)w;
+
+	le_oe[0] = d->le_oeMessage;
+	le_oe[1] = d->le_oeChat1;
+	le_oe[2] = d->le_oeChat2;
+	le_oe[3] = d->le_oeHeadline;
+	le_oe[4] = d->le_oeSystem;
+	le_oe[5] = d->le_oeOnline;
+	le_oe[6] = d->le_oeOffline;
+	le_oe[7] = d->le_oeSend;
+	le_oe[8] = d->le_oeIncomingFT;
+	le_oe[9] = d->le_oeFTComplete;
+
+	bg_se = new QButtonGroup;
+	bg_se->insert(d->tb_seMessage, 0);
+	bg_se->insert(d->tb_seChat1, 1);
+	bg_se->insert(d->tb_seChat2, 2);
+	bg_se->insert(d->tb_seHeadline, 3);
+	bg_se->insert(d->tb_seSystem, 4);
+	bg_se->insert(d->tb_seOnline, 5);
+	bg_se->insert(d->tb_seOffline, 6);
+	bg_se->insert(d->tb_seSend, 7);
+	bg_se->insert(d->tb_seIncomingFT, 8);
+	bg_se->insert(d->tb_seFTComplete, 9);
+	connect(bg_se, SIGNAL(clicked(int)), SLOT(chooseSoundEvent(int)));
+
+	bg_sePlay = new QButtonGroup;
+	bg_sePlay->insert(d->tb_seMessagePlay, 0);
+	bg_sePlay->insert(d->tb_seChat1Play, 1);
+	bg_sePlay->insert(d->tb_seChat2Play, 2);
+	bg_sePlay->insert(d->tb_seHeadlinePlay, 3);
+	bg_sePlay->insert(d->tb_seSystemPlay, 4);
+	bg_sePlay->insert(d->tb_seOnlinePlay, 5);
+	bg_sePlay->insert(d->tb_seOfflinePlay, 6);
+	bg_sePlay->insert(d->tb_seSendPlay, 7);
+	bg_sePlay->insert(d->tb_seIncomingFTPlay, 8);
+	bg_sePlay->insert(d->tb_seFTCompletePlay, 9);
+	connect(bg_sePlay, SIGNAL(clicked(int)), SLOT(previewSoundEvent(int)));
+
+	connect(d->pb_soundReset, SIGNAL(clicked()), SLOT(soundReset()));
+
+	// set up proper tool button icons
+	int n;
+	for (n = 0; n < 10; n++) {
+		IconToolButton *tb = (IconToolButton *)bg_se->find(n);
+		tb->setIcon( IconsetFactory::iconPtr("psi/browse") );
+		tb = (IconToolButton *)bg_sePlay->find(n);
+		tb->setIcon( IconsetFactory::iconPtr("psi/play") );
+	}
+
+	// TODO: add QWhatsThis for all widgets
+
+	QWhatsThis::add(d->le_player,
+		tr("If your system supports multiple sound players, you may"
+		" choose your preferred sound player application here."));
+	QWhatsThis::add(d->ck_awaySound,
+		tr("Enable this option if you wish to hear sound alerts when your status is \"away\" or \"extended away\"."));
+	QWhatsThis::add(d->ck_gcSound,
+		tr("Play sounds for all events in groupchat, not only for mentioning of your nick."));
+
+#if defined(Q_WS_WIN)
+	d->lb_player->hide();
+	d->le_player->hide();
+#elif defined(Q_WS_PM)
+	d->lb_player->hide();
+	d->le_player->hide();
+#elif defined(Q_WS_MAC)
+	d->lb_player->hide();
+	d->le_player->hide();
+#endif
+
+	return w;
+}
+
+void OptionsTabSound::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptSoundUI *d = (OptSoundUI *)w;
+	opt->player = d->le_player->text();
+	opt->noAwaySound = !d->ck_awaySound->isChecked();
+	opt->noGCSound = !d->ck_gcSound->isChecked();
+
+	for(int n = 0; n < 10; ++n)
+		opt->onevent[n] = le_oe[n]->text();
+}
+
+void OptionsTabSound::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptSoundUI *d = (OptSoundUI *)w;
+
+#if defined(Q_WS_WIN)
+	d->le_player->setText(tr("Windows Sound"));
+#elif defined(Q_WS_PM)
+	d->le_player->setText(tr("OS/2 MMPM Sound"));
+#elif defined(Q_WS_MAC)
+	d->le_player->setText(tr("Mac OS Sound"));
+#else
+	d->le_player->setText( opt->player );
+#endif
+
+	d->ck_awaySound->setChecked( !opt->noAwaySound );
+	d->ck_gcSound->setChecked( !opt->noGCSound );
+
+	for(int n = 0; n < 10; ++n)
+		le_oe[n]->setText(opt->onevent[n]);
+}
+
+void OptionsTabSound::setData(PsiCon *, QWidget *p)
+{
+	parentWidget = p;
+}
+
+void OptionsTabSound::chooseSoundEvent(int x)
+{
+	if(option.lastPath.isEmpty())
+		option.lastPath = QDir::homeDirPath();
+	QString str = QFileDialog::getOpenFileName(option.lastPath, tr("Sound (*.wav)"), parentWidget, "", tr("Choose a sound file"));
+	if(!str.isEmpty()) {
+		QFileInfo fi(str);
+		option.lastPath = fi.dirPath();
+		le_oe[x]->setText(str);
+		emit dataChanged();
+	}
+}
+
+void OptionsTabSound::previewSoundEvent(int x)
+{
+	soundPlay(le_oe[x]->text());
+}
+
+void OptionsTabSound::soundReset()
+{
+	Options opt;
+	opt.onevent[eMessage]    = g.pathBase + "/sound/chat2.wav";
+	opt.onevent[eChat1]      = g.pathBase + "/sound/chat1.wav";
+	opt.onevent[eChat2]      = g.pathBase + "/sound/chat2.wav";
+	opt.onevent[eHeadline]   = g.pathBase + "/sound/chat2.wav";
+	opt.onevent[eSystem]     = g.pathBase + "/sound/chat2.wav";
+	opt.onevent[eOnline]     = g.pathBase + "/sound/online.wav";
+	opt.onevent[eOffline]    = g.pathBase + "/sound/offline.wav";
+	opt.onevent[eSend]       = g.pathBase + "/sound/send.wav";
+	opt.onevent[eIncomingFT] = g.pathBase + "/sound/ft_incoming.wav";
+	opt.onevent[eFTComplete] = g.pathBase + "/sound/ft_complete.wav";
+
+	for(int n = 0; n < 10; ++n)
+		le_oe[n]->setText(opt.onevent[n]);
+
+	emit dataChanged();
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_sound.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_sound.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_sound.h	(revision 38)
@@ -0,0 +1,34 @@
+#ifndef OPT_SOUND_H
+#define OPT_SOUND_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+class QLineEdit;
+class QButtonGroup;
+
+class OptionsTabSound : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabSound(QObject *parent);
+	~OptionsTabSound();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+private slots:
+	void chooseSoundEvent(int x);
+	void previewSoundEvent(int x);
+	void soundReset();
+	void setData(PsiCon *, QWidget *);
+
+private:
+	QWidget *w, *parentWidget;
+	QLineEdit *le_oe[10];
+	QButtonGroup *bg_se, *bg_sePlay;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/opt_sound_events.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_sound_events.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_sound_events.ui	(revision 38)
@@ -0,0 +1,357 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>SoundEventsUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>SoundEventsUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>403</width>
+            <height>239</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>SoundEventsUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>1</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout33</cstring>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="IconToolButton" row="1" column="2">
+                    <property name="name">
+                        <cstring>tb_seChat1</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="3" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_3_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>System Message:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_2_3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive Next Chat:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="7" column="2">
+                    <property name="name">
+                        <cstring>tb_seSend</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="0" column="1">
+                    <property name="name">
+                        <cstring>le_oeMessage</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="3" column="3">
+                    <property name="name">
+                        <cstring>tb_seSystemPlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="2" column="1">
+                    <property name="name">
+                        <cstring>le_oeChat2</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="5" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_4_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive Online Status:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="6" column="3">
+                    <property name="name">
+                        <cstring>tb_seOfflinePlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="6" column="2">
+                    <property name="name">
+                        <cstring>tb_seOffline</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="2" column="2">
+                    <property name="name">
+                        <cstring>tb_seChat2</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="5" column="3">
+                    <property name="name">
+                        <cstring>tb_seOnlinePlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="2" column="3">
+                    <property name="name">
+                        <cstring>tb_seChat2Play</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="3" column="1">
+                    <property name="name">
+                        <cstring>le_oeSystem</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="3" column="2">
+                    <property name="name">
+                        <cstring>tb_seSystem</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_6</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive Message:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="1" column="1">
+                    <property name="name">
+                        <cstring>le_oeChat1</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_2_2_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive First Chat:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="6" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_4_2_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive Offline Status:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="0" column="3">
+                    <property name="name">
+                        <cstring>tb_seMessagePlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="7" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4_5_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Send Message:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="7" column="3">
+                    <property name="name">
+                        <cstring>tb_seSendPlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="5" column="1">
+                    <property name="name">
+                        <cstring>le_oeOnline</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="1" column="3">
+                    <property name="name">
+                        <cstring>tb_seChat1Play</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="0" column="2">
+                    <property name="name">
+                        <cstring>tb_seMessage</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="5" column="2">
+                    <property name="name">
+                        <cstring>tb_seOnline</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="7" column="1">
+                    <property name="name">
+                        <cstring>le_oeSend</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="6" column="1">
+                    <property name="name">
+                        <cstring>le_oeOffline</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="4" column="2">
+                    <property name="name">
+                        <cstring>tb_seHeadline</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="4" column="1">
+                    <property name="name">
+                        <cstring>le_oeHeadline</cstring>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Enter a filename or !beep for a system beep</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="4" column="0">
+                    <property name="name">
+                        <cstring>TextLabel_Headline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Receive Headline:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="4" column="3">
+                    <property name="name">
+                        <cstring>tb_seHeadlinePlay</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout27</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer9_2</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>276</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>pb_soundReset</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Reset to defaults</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+    <tabstop>le_oeMessage</tabstop>
+    <tabstop>le_oeChat1</tabstop>
+    <tabstop>le_oeChat2</tabstop>
+    <tabstop>le_oeSystem</tabstop>
+    <tabstop>le_oeOnline</tabstop>
+    <tabstop>le_oeOffline</tabstop>
+    <tabstop>le_oeSend</tabstop>
+    <tabstop>pb_soundReset</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_sound_prefs.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_sound_prefs.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_sound_prefs.ui	(revision 38)
@@ -0,0 +1,73 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>SoundPrefsUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>SoundPrefsUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>274</width>
+            <height>83</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string></string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout18</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_player</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Player:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit">
+                    <property name="name">
+                        <cstring>le_player</cstring>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_awaySound</cstring>
+            </property>
+            <property name="text">
+                <string>Play sounds when Away/XA</string>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_gcSound</cstring>
+            </property>
+            <property name="text">
+                <string>Play sounds for all messages in groupchat</string>
+            </property>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_status-ui.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_status-ui.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_status-ui.ui	(revision 38)
@@ -0,0 +1,332 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>OptStatusUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>OptStatusUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>300</width>
+            <height>418</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>OptStatusUI</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <property name="spacing">
+            <number>2</number>
+        </property>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox1</cstring>
+            </property>
+            <property name="title">
+                <string>Prompt for status message when choosing</string>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_askOnline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Online</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_askOffline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Offline</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout15</cstring>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QSpinBox" row="2" column="1">
+                    <property name="name">
+                        <cstring>sb_asOffline</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="2">
+                    <property name="name">
+                        <cstring>lb_asOffline</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="text">
+                        <string>Minutes</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="2">
+                    <property name="name">
+                        <cstring>lb_asXa</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="text">
+                        <string>Minutes</string>
+                    </property>
+                </widget>
+                <widget class="QSpinBox" row="1" column="1">
+                    <property name="name">
+                        <cstring>sb_asXa</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="2">
+                    <property name="name">
+                        <cstring>lb_asAway</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="text">
+                        <string>Minutes</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox" row="2" column="0">
+                    <property name="name">
+                        <cstring>ck_asOffline</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Auto Offline After:</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox" row="1" column="0">
+                    <property name="name">
+                        <cstring>ck_asXa</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Auto XA After:</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox" row="0" column="0">
+                    <property name="name">
+                        <cstring>ck_asAway</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Auto Away After:</string>
+                    </property>
+                </widget>
+                <widget class="QSpinBox" row="0" column="1">
+                    <property name="name">
+                        <cstring>sb_asAway</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>TextLabel1_8</cstring>
+            </property>
+            <property name="text">
+                <string>Auto Status Message:</string>
+            </property>
+        </widget>
+        <widget class="QTextEdit">
+            <property name="name">
+                <cstring>te_asMessage</cstring>
+            </property>
+            <property name="maximumSize">
+                <size>
+                    <width>32767</width>
+                    <height>50</height>
+                </size>
+            </property>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>groupBox10</cstring>
+            </property>
+            <property name="title">
+                <string>Presets</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="spacing">
+                    <number>2</number>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout29</cstring>
+                    </property>
+                    <grid>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QListBox" row="0" column="0" rowspan="3" colspan="1">
+                            <property name="name">
+                                <cstring>lb_sp</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>7</hsizetype>
+                                    <vsizetype>7</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                            <property name="maximumSize">
+                                <size>
+                                    <width>32767</width>
+                                    <height>62</height>
+                                </size>
+                            </property>
+                        </widget>
+                        <widget class="QPushButton" row="0" column="1">
+                            <property name="name">
+                                <cstring>pb_spNew</cstring>
+                            </property>
+                            <property name="text">
+                                <string>New</string>
+                            </property>
+                        </widget>
+                        <spacer row="1" column="1">
+                            <property name="name">
+                                <cstring>Spacer6</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Vertical</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Maximum</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>16</width>
+                                    <height>0</height>
+                                </size>
+                            </property>
+                        </spacer>
+                        <widget class="QPushButton" row="2" column="1">
+                            <property name="name">
+                                <cstring>pb_spDelete</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Delete</string>
+                            </property>
+                        </widget>
+                    </grid>
+                </widget>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>TextLabel2_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Content:</string>
+                    </property>
+                </widget>
+                <widget class="QTextEdit">
+                    <property name="name">
+                        <cstring>te_sp</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>7</hsizetype>
+                            <vsizetype>7</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>32767</width>
+                            <height>90</height>
+                        </size>
+                    </property>
+                </widget>
+            </vbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>ck_asAway</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>sb_asAway</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asAway</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>lb_asAway</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asXa</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>sb_asXa</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asXa</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>lb_asXa</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asOffline</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>sb_asOffline</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+    <connection>
+        <sender>ck_asOffline</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>lb_asOffline</receiver>
+        <slot>setEnabled(bool)</slot>
+    </connection>
+</connections>
+<tabstops>
+    <tabstop>ck_asAway</tabstop>
+    <tabstop>sb_asAway</tabstop>
+    <tabstop>ck_asXa</tabstop>
+    <tabstop>sb_asXa</tabstop>
+    <tabstop>ck_asOffline</tabstop>
+    <tabstop>sb_asOffline</tabstop>
+    <tabstop>te_asMessage</tabstop>
+    <tabstop>lb_sp</tabstop>
+    <tabstop>pb_spNew</tabstop>
+    <tabstop>pb_spDelete</tabstop>
+    <tabstop>te_sp</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/opt_status.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_status.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_status.cpp	(revision 38)
@@ -0,0 +1,237 @@
+#include "opt_status.h"
+#include "common.h"
+#include "iconwidget.h"
+
+#include <qbuttongroup.h>
+#include <qwhatsthis.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qtextedit.h>
+#include <qinputdialog.h>
+
+#include "opt_status-ui.h"
+
+//----------------------------------------------------------------------------
+// OptionsTabStatus
+//----------------------------------------------------------------------------
+
+OptionsTabStatus::OptionsTabStatus(QObject *parent)
+: OptionsTab(parent, "status", "", tr("Status"), tr("Status preferences"), "psi/status")
+{
+	w = 0;
+	o = new Options;
+}
+
+OptionsTabStatus::~OptionsTabStatus()
+{
+	delete o;
+}
+
+QWidget *OptionsTabStatus::widget()
+{
+	if ( w )
+		return 0;
+
+	w = new OptStatusUI();
+	OptStatusUI *d = (OptStatusUI *)w;
+
+	QString s = tr("Makes Psi automatically set your status to \"away\" if your"
+		" computer is idle for the specified amount of time.");
+	QWhatsThis::add(d->ck_asAway, s);
+	QWhatsThis::add(d->sb_asAway, s);
+	s = tr("Makes Psi automatically set your status to \"extended away\" if your"
+		" computer is idle for the specified amount of time.");
+	QWhatsThis::add(d->ck_asXa, s);
+	QWhatsThis::add(d->sb_asXa, s);
+	s = tr("Makes Psi automatically set your status to \"offline\" if your"
+		" computer is idle for the specified amount of time."
+		"  This will disconnect you from the Jabber server.");
+	QWhatsThis::add(d->ck_asOffline, s);
+	QWhatsThis::add(d->sb_asOffline, s);
+
+	QWhatsThis::add(d->te_asMessage,
+		tr("Specifies an extended message to use if you allow Psi"
+		" to set your status automatically.  See options above."));
+
+	d->pb_spNew->setEnabled(TRUE);
+	d->pb_spDelete->setEnabled(FALSE);
+	d->te_sp->setEnabled(FALSE);
+	connect(d->pb_spNew, SIGNAL(clicked()), SLOT(newStatusPreset()));
+	connect(d->pb_spDelete, SIGNAL(clicked()), SLOT(removeStatusPreset()));
+	connect(d->lb_sp, SIGNAL(highlighted(int)), SLOT(selectStatusPreset(int)));
+	connect(d->te_sp, SIGNAL(textChanged()), SLOT(changeStatusPreset()));
+
+	QWhatsThis::add(d->pb_spNew,
+		tr("Press this button to create a new status message preset."));
+	QWhatsThis::add(d->pb_spDelete,
+		tr("Press this button to delete a status message preset."));
+	QWhatsThis::add(d->lb_sp,
+		tr("Use this list to select a status message preset"
+		" to view or edit in the box to the bottom."));
+	QWhatsThis::add(d->te_sp,
+		tr("You may edit the message here for the currently selected"
+		" status message preset in the list to the above."));
+
+	QWhatsThis::add(d->ck_askOnline,
+		tr("Jabber allows you to put extended status messages on"
+		" all status types.  Normally, Psi does not prompt you for"
+		" an extended message when you set your status to \"online\"."
+		"  Check this option if you want to have this prompt."));
+
+	return w;
+}
+
+void OptionsTabStatus::applyOptions(Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptStatusUI *d = (OptStatusUI *)w;
+
+	opt->asAway = d->sb_asAway->value();
+	opt->asXa = d->sb_asXa->value();
+	opt->asOffline = d->sb_asOffline->value();
+	opt->use_asAway = d->ck_asAway->isChecked();
+	opt->use_asXa = d->ck_asXa->isChecked();
+	opt->use_asOffline = d->ck_asOffline->isChecked();
+	opt->asMessage = d->te_asMessage->text();
+
+	opt->sp = o->sp;
+
+	opt->askOnline = d->ck_askOnline->isChecked();
+	opt->askOffline = d->ck_askOffline->isChecked();
+}
+
+void OptionsTabStatus::restoreOptions(const Options *opt)
+{
+	if ( !w )
+		return;
+
+	OptStatusUI *d = (OptStatusUI *)w;
+
+	d->sb_asAway->setMinValue(0);
+	d->sb_asAway->setValue( opt->asAway );
+	d->sb_asXa->setMinValue(0);
+	d->sb_asXa->setValue( opt->asXa );
+	d->sb_asOffline->setMinValue(0);
+	d->sb_asOffline->setValue( opt->asOffline );
+	/*if (opt->asAway <= 0 )
+		opt->use_asAway = FALSE;
+	if (opt->asXa <= 0 )
+		opt->use_asXa = FALSE;
+	if(d->opt.asOffline <= 0)
+		opt->use_asOffline = FALSE;*/
+	d->ck_asAway->setChecked( opt->use_asAway );
+	d->ck_asXa->setChecked( opt->use_asXa );
+	d->ck_asOffline->setChecked( opt->use_asOffline );
+	d->te_asMessage->setText( opt->asMessage );
+
+	o->sp = opt->sp;
+	d->lb_sp->clear();
+	d->lb_sp->insertStringList(o->sp.varsToStringList());
+	if(d->lb_sp->count() >= 1)
+		d->lb_sp->setSelected(0, TRUE);
+
+	d->ck_askOnline->setChecked( opt->askOnline );
+	d->ck_askOffline->setChecked( opt->askOffline );
+}
+
+void OptionsTabStatus::setData(PsiCon *, QWidget *parentDialog)
+{
+	parentWidget = parentDialog;
+}
+
+void OptionsTabStatus::selectStatusPreset(int x)
+{
+	OptStatusUI *d = (OptStatusUI *)w;
+
+	//noDirty = TRUE;
+	disconnect(d->te_sp, SIGNAL(textChanged()), 0, 0);
+	if ( x == -1 ) {
+		d->pb_spDelete->setEnabled(false);
+		d->te_sp->setText("");
+		d->te_sp->setEnabled(false);
+
+		//noDirty = FALSE;
+		connect(d->te_sp, SIGNAL(textChanged()), SLOT(changeStatusPreset()));
+		return;
+	}
+
+	d->pb_spDelete->setEnabled(true);
+
+	d->te_sp->setText( o->sp.get( d->lb_sp->text(x)) );
+	d->te_sp->setEnabled(true);
+
+	//noDirty = FALSE;
+	connect(d->te_sp, SIGNAL(textChanged()), SLOT(changeStatusPreset()));
+}
+
+void OptionsTabStatus::newStatusPreset()
+{
+	OptStatusUI *d = (OptStatusUI *)w;
+
+	QString text;
+
+	while(1) {
+		bool ok = FALSE;
+		text = QInputDialog::getText(
+			CAP(tr("New Status Preset")),
+			tr("Please enter a name for the new status preset:"),
+			QLineEdit::Normal, text, &ok, parentWidget);
+		if(!ok)
+			return;
+
+		if(text.isEmpty())
+			QMessageBox::information(parentWidget, tr("Error"), tr("Can't create a blank preset!"));
+		else if(o->sp.findByKey(text) != o->sp.end())
+			QMessageBox::information(parentWidget, tr("Error"), tr("You already have a preset with that name!"));
+		else
+			break;
+	}
+
+	o->sp.set(text, "");
+	d->lb_sp->insertItem(text);
+	d->lb_sp->setSelected(d->lb_sp->count()-1, TRUE);
+	d->te_sp->setFocus();
+
+	emit dataChanged();
+}
+
+void OptionsTabStatus::removeStatusPreset()
+{
+	OptStatusUI *d = (OptStatusUI *)w;
+	int id = d->lb_sp->currentItem();
+	if(id == -1)
+		return;
+
+	emit dataChanged();
+
+	o->sp.unset(d->lb_sp->text(id));
+	d->lb_sp->removeItem(id);
+
+	// select a new entry if possible
+	if(d->lb_sp->count() == 0) {
+		selectStatusPreset(-1);
+		return;
+	}
+
+	if(id >= (int)d->lb_sp->count())
+		id = d->lb_sp->count()-1;
+
+	d->lb_sp->setSelected(id, TRUE);
+	selectStatusPreset(id);
+}
+
+void OptionsTabStatus::changeStatusPreset()
+{
+	OptStatusUI *d = (OptStatusUI *)w;
+	int id = d->lb_sp->currentItem();
+	if(id == -1)
+		return;
+
+	o->sp.set(d->lb_sp->text(id), d->te_sp->text());
+	emit dataChanged();
+}
Index: psi/tags/0.10-os2-b1/src/options/opt_status.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/opt_status.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/opt_status.h	(revision 38)
@@ -0,0 +1,34 @@
+#ifndef OPT_STATUS_H
+#define OPT_STATUS_H
+
+#include "optionstab.h"
+
+class QWidget;
+struct Options;
+
+class OptionsTabStatus : public OptionsTab
+{
+	Q_OBJECT
+public:
+	OptionsTabStatus(QObject *parent);
+	~OptionsTabStatus();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+	void setData(PsiCon *, QWidget *parentDialog);
+	//bool stretchable() const { return true; }
+
+private slots:
+	void selectStatusPreset(int x);
+	void newStatusPreset();
+	void removeStatusPreset();
+	void changeStatusPreset();
+
+private:
+	QWidget *w, *parentWidget;
+	Options *o;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/options.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/options/options.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/options.pri	(revision 38)
@@ -0,0 +1,54 @@
+# base dialog stuff
+HEADERS += \
+	$$PSI_CPP/options/optionsdlg.h \
+	$$PSI_CPP/options/optionstab.h \
+	$$PSI_CPP/options/toolbardlg.h
+SOURCES += \
+	$$PSI_CPP/options/optionstab.cpp \
+	$$PSI_CPP/options/optionsdlg.cpp \
+	$$PSI_CPP/options/toolbardlg.cpp
+INTERFACES += \
+	$$PSI_CPP/options/ui_options.ui
+
+
+# additional tabs
+HEADERS += \
+	$$PSI_CPP/options/opt_application.h \
+	$$PSI_CPP/options/opt_chat.h \
+	$$PSI_CPP/options/opt_events.h \
+	$$PSI_CPP/options/opt_status.h \
+	$$PSI_CPP/options/opt_appearance.h \
+	$$PSI_CPP/options/opt_lookfeel.h \
+	$$PSI_CPP/options/opt_iconset.h \
+	$$PSI_CPP/options/opt_groupchat.h \
+	$$PSI_CPP/options/opt_sound.h \
+	$$PSI_CPP/options/opt_advanced.h
+
+SOURCES += \
+	$$PSI_CPP/options/opt_application.cpp \
+	$$PSI_CPP/options/opt_chat.cpp \
+	$$PSI_CPP/options/opt_events.cpp \
+	$$PSI_CPP/options/opt_status.cpp \
+	$$PSI_CPP/options/opt_appearance.cpp \
+	$$PSI_CPP/options/opt_lookfeel.cpp \
+	$$PSI_CPP/options/opt_iconset.cpp \
+	$$PSI_CPP/options/opt_groupchat.cpp \
+	$$PSI_CPP/options/opt_sound.cpp \
+	$$PSI_CPP/options/opt_advanced.cpp
+
+INTERFACES += \
+	$$PSI_CPP/options/opt_application-ui.ui \
+	$$PSI_CPP/options/opt_chat-ui.ui \
+	$$PSI_CPP/options/opt_events-ui.ui \
+	$$PSI_CPP/options/opt_status-ui.ui \
+	$$PSI_CPP/options/opt_appearance-ui.ui \
+	$$PSI_CPP/options/opt_appearance_misc-ui.ui \
+	$$PSI_CPP/options/opt_sound-ui.ui \
+	$$PSI_CPP/options/opt_advanced-ui.ui \
+	$$PSI_CPP/options/opt_lookfeel_toolbars.ui \
+	$$PSI_CPP/options/ui_positiontoolbar.ui \
+	$$PSI_CPP/options/ui_isdetails.ui \
+	$$PSI_CPP/options/opt_iconset_emo.ui \
+	$$PSI_CPP/options/opt_iconset_system.ui \
+	$$PSI_CPP/options/opt_iconset_roster.ui \
+	$$PSI_CPP/options/opt_general_groupchat.ui
Index: psi/tags/0.10-os2-b1/src/options/optionsdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/optionsdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/optionsdlg.cpp	(revision 38)
@@ -0,0 +1,557 @@
+#include "optionsdlg.h"
+#include "optionstab.h"
+#include "common.h"
+#include "psicon.h"
+#include "fancylabel.h"
+#include "iconwidget.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistview.h>
+#include <qwidgetstack.h>
+#include <qheader.h>
+#include <qobjectlist.h>
+#include <qpen.h>
+#include <qpainter.h>
+
+// tabs
+#include "opt_application.h"
+#include "opt_appearance.h"
+#include "opt_chat.h"
+#include "opt_events.h"
+#include "opt_status.h"
+#include "opt_iconset.h"
+#include "opt_groupchat.h"
+#include "opt_sound.h"
+#include "opt_advanced.h"
+
+#include "opt_lookfeel.h"
+
+//----------------------------------------------------------------------------
+// FancyItem
+//----------------------------------------------------------------------------
+
+class FancyItem : public QListViewItem
+{
+public:
+	FancyItem(QListView *, QListViewItem *after);
+
+	void setup();
+	int width(const QFontMetrics &, const QListView *lv, int c) const;
+	void paintFocus(QPainter *, const QColorGroup &, const QRect &);
+	void paintCell(QPainter *p, const QColorGroup &, int c, int width, int align);
+};
+
+FancyItem::FancyItem(QListView *lv, QListViewItem *after)
+: QListViewItem(lv, after)
+{
+}
+
+void FancyItem::setup()
+{
+	QListView *lv = listView();
+	int ph = 0;
+	for(int i = 0; i < lv->columns(); ++i) {
+		if(pixmap(i))
+			ph = QMAX(ph, pixmap(i)->height());
+	}
+	int y = QMAX(ph, lv->fontMetrics().height());
+	y += 8;
+	setHeight(y);
+}
+
+int FancyItem::width(const QFontMetrics &fm, const QListView *, int c) const
+{
+	int x = 0;
+	const QPixmap *pix = pixmap(c);
+	if(pix)
+		x += pix->width();
+	else
+		x += 16;
+	x += 8;
+	x += fm.width(text(c));
+	x += 8;
+	return x;
+}
+
+void FancyItem::paintFocus(QPainter *, const QColorGroup &, const QRect &)
+{
+	// re-implimented to do nothing.  selection is enough of a focus
+}
+
+void FancyItem::paintCell(QPainter *p, const QColorGroup &cg, int c, int w, int)
+{
+	int h = height();
+	QFontMetrics fm(p->font());
+	if(isSelected())
+		p->fillRect(0, 0, w, h-1, cg.highlight());
+	else
+		p->fillRect(0, 0, w, h, cg.base());
+
+	int x = 0;
+	const QPixmap *pix = pixmap(c);
+	if(pix) {
+		p->drawPixmap(4, (h - pix->height()) / 2, *pix);
+		x += pix->width();
+	}
+	else
+		x += 16;
+	x += 8;
+	int y = ((h - fm.height()) / 2) + fm.ascent();
+	p->setPen(isSelected() ? cg.highlightedText() : cg.text());
+	p->drawText(x, y, text(c));
+
+	p->setPen(QPen(QColor(0xE0, 0xE0, 0xE0), 0, DotLine));
+	p->drawLine(0, h-1, w-1, h-1);
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabBase
+//----------------------------------------------------------------------------
+
+//class OptionsTabBase : public OptionsTab
+//{
+//	Q_OBJECT
+//public:
+//	OptionsTabBase(QObject *parent, QCString id, QCString parentId, QString iconName, QString name, QString desc)
+//		: OptionsTab(parent, id, parentId, name, desc, iconName)
+//	{
+//		w = new QWidget();
+//		QGridLayout *layout = new QGridLayout(w, 0, 2, 0, 5);
+//		layout->setAutoAdd(true);
+//	}
+//	~OptionsTabBase()
+//	{
+//		w->deleteLater();
+//	}
+//
+//	QWidget *widget() { return w; }
+//
+//public slots:
+//	void tabAdded(OptionsTab *tab);
+//
+//private:
+//	QWidget *w;
+//};
+//
+//void OptionsTabBase::tabAdded(OptionsTab *tab)
+//{
+//	//qWarning("OptionsTabBase::tabAdded(): id = %s, tab_id = %s", (const char *)id(), (const char *)tab->id());
+//	QLabel *name = new QLabel(w);
+//	name->setText("<b>" + tab->name() + "</b>");
+//	name->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
+//
+//	IconLabel *desc = new IconLabel(w);
+//	desc->setText(tab->desc());
+//	desc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+//}
+
+//----------------------------------------------------------------------------
+// OptionsDlg::Private
+//----------------------------------------------------------------------------
+
+class OptionsDlg::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(OptionsDlg *dlg, PsiCon *_psi, const Options &_opt);
+
+public slots:
+	void doApply();
+
+private slots:
+	void itemSelected(QListViewItem *);
+	void openTab(QString id);
+	void dataChanged();
+	void noDirtySlot(bool);
+	void createTabs();
+	void createChangedMap();
+
+	//void addWidgetChangedSignal(QString widgetName, QCString signal);
+	void connectDataChanged(QWidget *);
+
+public:
+	OptionsDlg *dlg;
+	PsiCon *psi;
+	Options opt;
+	bool dirty, noDirty;
+	QDict<QWidget> id2widget;
+	QPtrList<OptionsTab> tabs;
+
+	QMap<QString, QCString> changedMap;
+};
+
+OptionsDlg::Private::Private(OptionsDlg *d, PsiCon *_psi, const Options &_opt)
+{
+	dlg = d;
+	psi = _psi;
+	opt = _opt; // option
+	noDirty = false;
+	dirty = false;
+
+	dlg->lb_pageTitle->setScaledContents(32, 32);
+
+	dlg->lv_tabs->setSorting( -1 );
+	dlg->lv_tabs->header()->hide();
+	connect(dlg->lv_tabs, SIGNAL(selectionChanged(QListViewItem *)), SLOT(itemSelected(QListViewItem *)));
+
+	createTabs();
+	createChangedMap();
+
+	// fill the QListView
+	QPtrListIterator<OptionsTab> it ( tabs );
+	OptionsTab *opttab;
+	for ( ; it.current(); ++it) {
+		opttab = it.current();
+		//qWarning("Adding tab %s...", (const char *)opttab->id());
+		opttab->setData(psi, dlg);
+		connect(opttab, SIGNAL(dataChanged()), SLOT(dataChanged()));
+		//connect(opttab, SIGNAL(addWidgetChangedSignal(QString, QCString)), SLOT(addWidgetChangedSignal(QString, QCString)));
+		connect(opttab, SIGNAL(noDirty(bool)), SLOT(noDirtySlot(bool)));
+		connect(opttab, SIGNAL(connectDataChanged(QWidget *)), SLOT(connectDataChanged(QWidget *)));
+
+		// search for parent
+		QListViewItem *parent = 0, *prev = 0;
+		QString parentId = opttab->parentId();
+		if ( !parentId.isEmpty() ) {
+			QListViewItemIterator it2( dlg->lv_tabs );
+			for ( ; it2.current(); ++it2) {
+				//qWarning("Searching the QListView %s...", it2.current()->text(1).latin1());
+				if ( it2.current()->text(1) == parentId ) {
+					//qWarning("...done");
+					parent = it2.current();
+
+					// notify the parent about the child
+					QPtrListIterator<OptionsTab> it3 ( tabs );
+					OptionsTab *opttab2;
+					for ( ; it3.current(); ++it3) {
+						opttab2 = it3.current();
+						//qWarning("Searching tabs %s...", (const char *)opttab2->id());
+						if ( opttab2->id() == opttab->parentId() ) {
+							//qWarning("...done");
+							opttab2->tabAdded( opttab );
+							break;
+						}
+					}
+
+					parent->setOpen( true );
+					break;
+				}
+			}
+		}
+		//qWarning("****************");
+
+		// search for previous item
+		QListViewItem *top;
+		if ( parent )
+			top = parent->firstChild();
+		else
+			top = dlg->lv_tabs->firstChild();
+		prev = top;
+		while ( prev ) {
+			if ( !prev->nextSibling() )
+				break;
+			prev = prev->nextSibling();
+		}
+
+		if ( opttab->id().isEmpty() )
+			continue;
+
+		// create tab
+		QListViewItem *item;
+		//if ( parent )
+		//	item = new FancyItem(parent, prev);
+		//else
+			item = new FancyItem(dlg->lv_tabs, prev);
+
+		item->setText(0, opttab->tabName());
+		if ( opttab->tabIcon() )
+			item->setPixmap(0, opttab->tabIcon()->impix().pixmap());
+		item->setText(1, opttab->id());
+
+		// create separator
+		//if ( !parent ) {
+		//	new ListItemSeparator(dlg->lv_tabs, item);
+		//}
+	}
+
+	// fix the width of the listview based on the largest item
+	int largestWidth = 0;
+	QFontMetrics fm(dlg->lv_tabs->font());
+	for(QListViewItem *i = dlg->lv_tabs->firstChild(); i; i = i->nextSibling())
+		largestWidth = QMAX(largestWidth, i->width(fm, dlg->lv_tabs, 0));
+	dlg->lv_tabs->setFixedWidth(largestWidth + 32);
+
+	openTab( "application" );
+
+	dirty = false;
+	dlg->pb_apply->setEnabled(false);
+}
+
+void OptionsDlg::Private::createTabs()
+{
+	// tabs - base
+	/*tabs.append( new OptionsTabGeneral(this) );
+	//tabs.append( new OptionsTabBase(this, "general",  "", "psi/psi16",	tr("General"),		tr("General preferences list")) );
+	tabs.append( new OptionsTabEvents(this) );
+	//tabs.append( new OptionsTabBase(this, "events",   "", "psi/system",	tr("Events"),		tr("Change the events behaviour")) );
+	tabs.append( new OptionsTabPresence(this) );
+	//tabs.append( new OptionsTabBase(this, "presence", "", "status/online",	tr("Presence"),		tr("Presence configuration")) );
+	tabs.append( new OptionsTabLookFeel(this) );
+	tabs.append( new OptionsTabIconset(this) );
+	//tabs.append( new OptionsTabBase(this, "lookfeel", "", "psi/smile",	tr("Look and Feel"),	tr("Change the Psi's Look and Feel")) );
+	tabs.append( new OptionsTabSound(this) );
+	//tabs.append( new OptionsTabBase(this, "sound",    "", "psi/playSounds",	tr("Sound"),		tr("Configure how Psi sounds")) );
+	*/
+
+	tabs.append( new OptionsTabApplication(this) );
+	tabs.append( new OptionsTabChat(this) );
+	tabs.append( new OptionsTabEvents(this) );
+	tabs.append( new OptionsTabStatus(this) );
+	tabs.append( new OptionsTabAppearance(this) );
+	//tabs.append( new OptionsTabIconsetSystem(this) );
+	//tabs.append( new OptionsTabIconsetRoster(this) );
+	//tabs.append( new OptionsTabIconsetEmoticons(this) );
+	tabs.append( new OptionsTabGroupchat(this) );
+	tabs.append( new OptionsTabSound(this) );
+	tabs.append( new OptionsTabAdvanced(this) );
+
+	tabs.append( new OptionsTabLookFeelToolbars(this) ); // it needs to be there, otherwise toolbar options would not be saved correctly in all cases
+
+	// tabs - general
+	/*tabs.append( new OptionsTabGeneralRoster(this) );
+	tabs.append( new OptionsTabGeneralDocking(this) );
+	tabs.append( new OptionsTabGeneralNotifications(this) );
+	tabs.append( new OptionsTabGeneralGroupchat(this) );
+	tabs.append( new OptionsTabGeneralMisc(this) );*/
+
+	// tabs - events
+	/*tabs.append( new OptionsTabEventsReceive(this) );
+	tabs.append( new OptionsTabEventsMisc(this) );*/
+
+	// tabs - presence
+	/*tabs.append( new OptionsTabPresenceAuto(this) );
+	tabs.append( new OptionsTabPresencePresets(this) );
+	tabs.append( new OptionsTabPresenceMisc(this) );*/
+
+	// tabs - look and feel
+	/*tabs.append( new OptionsTabLookFeelColors(this) );
+	tabs.append( new OptionsTabLookFeelFonts(this) );
+	tabs.append( new OptionsTabIconsetSystem(this) );
+	tabs.append( new OptionsTabIconsetEmoticons(this) );
+	tabs.append( new OptionsTabIconsetRoster(this) );
+	tabs.append( new OptionsTabLookFeelToolbars(this) );
+	tabs.append( new OptionsTabLookFeelMisc(this) );*/
+
+	// tabs - sound
+	/*tabs.append( new OptionsTabSoundPrefs(this) );
+	tabs.append( new OptionsTabSoundEvents(this) );*/
+}
+
+void OptionsDlg::Private::createChangedMap()
+{
+	// NOTE about commented out signals:
+	//   Do NOT call addWidgetChangedSignal() for them.
+	//   Instead, connect the widget's signal to your tab own dataChaged() signal
+	changedMap.insert("QButton", SIGNAL(stateChanged(int)));
+	changedMap.insert("QCheckBox", SIGNAL(stateChanged(int)));
+	changedMap.insert("QPushButton", SIGNAL(stateChanged(int)));
+	changedMap.insert("QRadioButton", SIGNAL(stateChanged(int)));
+	changedMap.insert("QComboBox", SIGNAL(activated (int)));
+	//changedMap.insert("QComboBox", SIGNAL(textChanged(const QString &)));
+	changedMap.insert("QDateEdit", SIGNAL(valueChanged(const QDate &)));
+	changedMap.insert("QDateTimeEdit", SIGNAL(valueChanged(const QDateTime &)));
+	changedMap.insert("QDial", SIGNAL(valueChanged (int)));
+	changedMap.insert("QLineEdit", SIGNAL(textChanged(const QString &)));
+	changedMap.insert("QSlider", SIGNAL(valueChanged(int)));
+	changedMap.insert("QSpinBox", SIGNAL(valueChanged(int)));
+	changedMap.insert("QTimeEdit", SIGNAL(valueChanged(const QTime &)));
+	changedMap.insert("QTextEdit", SIGNAL(textChanged()));
+	changedMap.insert("QTextBrowser", SIGNAL(sourceChanged(const QString &)));
+	changedMap.insert("QMultiLineEdit", SIGNAL(textChanged()));
+	//changedMap.insert("QListBox", SIGNAL(selectionChanged()));
+	//changedMap.insert("QTabWidget", SIGNAL(currentChanged(QWidget *)));
+}
+
+//void OptionsDlg::Private::addWidgetChangedSignal(QString widgetName, QCString signal)
+//{
+//	changedMap.insert(widgetName, signal);
+//}
+
+void OptionsDlg::Private::openTab(QString id)
+{
+	if ( id.isEmpty() )
+		return;
+
+	QWidget *tab = id2widget[id];
+	if ( !tab ) {
+		bool found = false;
+		QPtrListIterator<OptionsTab> it ( tabs );
+		OptionsTab *opttab;
+		for ( ; it.current(); ++it) {
+			opttab = it.current();
+
+			if ( opttab->id() == id.latin1() ) {
+				tab = opttab->widget(); // create the widget
+				if ( !tab )
+					continue;
+
+				// TODO: how about QScrollView for large tabs?
+				// idea: maybe do it only for those, whose sizeHint is bigger than ws_tabs'
+				QWidget *w = new QWidget(dlg->ws_tabs, "QWidgetStack/tab");
+				QVBoxLayout *vbox = new QVBoxLayout(w);
+
+				/*FancyLabel *toplbl = new FancyLabel(w, "QWidgetStack/tab/FancyLabel");
+				toplbl->setText( opttab->name() );
+				toplbl->setHelp( opttab->desc() );
+				toplbl->setIcon( opttab->icon() );
+				vbox->addWidget( toplbl );
+				vbox->addSpacing( 5 );*/
+
+				tab->reparent(w, 0, QPoint(0, 0));
+				vbox->addWidget(tab);
+				if ( !opttab->stretchable() )
+					vbox->addStretch();
+
+				dlg->ws_tabs->addWidget(w);
+				id2widget.insert( id, w );
+				connectDataChanged( tab ); // no need to connect to dataChanged() slot by hands anymore
+
+				bool d = dirty;
+
+				opttab->restoreOptions( &opt ); // initialize widgets' values
+
+				dirty = d;
+				dlg->pb_apply->setEnabled( dirty );
+
+				tab = w;
+				found = true;
+				break;
+			}
+		}
+
+		if ( !found ) {
+			qWarning("OptionsDlg::Private::itemSelected(): could not create widget for id '%s'", id.latin1());
+			return;
+		}
+	}
+
+	{
+		QPtrListIterator<OptionsTab> it ( tabs );
+		OptionsTab *opttab;
+		for ( ; it.current(); ++it) {
+			opttab = it.current();
+
+			if ( opttab->id() == id.latin1() ) {
+				dlg->lb_pageTitle->setText( opttab->name() );
+				dlg->lb_pageTitle->setHelp( opttab->desc() );
+				dlg->lb_pageTitle->setIcon( opttab->icon() );
+
+				break;
+			}
+		}
+	}
+
+	dlg->ws_tabs->raiseWidget( tab );
+
+	// and select item in lv_tabs...
+	QListViewItemIterator it( dlg->lv_tabs );
+	while ( it.current() ) {
+		it.current()->setSelected( it.current()->text(1) == id );
+		++it;
+	}
+}
+
+void OptionsDlg::Private::connectDataChanged(QWidget *widget)
+{
+	QObjectList *l = widget->queryList( "QWidget", 0, false, true ); // search for all QWidget children of widget
+	QObjectListIterator it( *l );
+
+	for ( ; it.current(); ++it) {
+		QWidget *w = (QWidget *)it.current();
+
+		QMap<QString, QCString>::Iterator it2 = changedMap.find( w->className() );
+		if ( it2 != changedMap.end() ) {
+			disconnect(w, changedMap[w->className()], this, SLOT(dataChanged()));
+			connect(w, changedMap[w->className()], SLOT(dataChanged()));
+		}
+	}
+
+	delete l;
+}
+
+void OptionsDlg::Private::itemSelected(QListViewItem *item)
+{
+	if ( !item )
+		return;
+
+	openTab( item->text(1) );
+}
+
+void OptionsDlg::Private::dataChanged()
+{
+	if ( dirty )
+		return;
+
+	if ( !noDirty ) {
+		dirty = true;
+		dlg->pb_apply->setEnabled(true);
+	}
+}
+
+void OptionsDlg::Private::noDirtySlot(bool d)
+{
+	noDirty = d;
+}
+
+void OptionsDlg::Private::doApply()
+{
+	if ( !dirty )
+		return;
+
+	QPtrListIterator<OptionsTab> it ( tabs );
+	OptionsTab *opttab;
+	for ( ; it.current(); ++it) {
+		opttab = it.current();
+
+		opttab->applyOptions( &opt );
+	}
+
+	emit dlg->applyOptions( opt );
+
+	dirty = false;
+	dlg->pb_apply->setEnabled(false);
+}
+
+//----------------------------------------------------------------------------
+// OptionsDlg
+//----------------------------------------------------------------------------
+
+OptionsDlg::OptionsDlg(PsiCon *psi, const Options &opt, QWidget *parent, const char *name)
+: OptionsUI(parent, name, false, WDestructiveClose)
+{
+	d = new Private(this, psi, opt);
+	d->psi->dialogRegister(this);
+
+	setCaption(CAP(caption()));
+	resize(640, 480);
+}
+
+OptionsDlg::~OptionsDlg()
+{
+	d->psi->dialogUnregister(this);
+	delete d;
+}
+
+void OptionsDlg::doOk()
+{
+	doApply();
+	accept();
+}
+
+void OptionsDlg::doApply()
+{
+	d->doApply();
+}
+
+#include "optionsdlg.moc"
Index: psi/tags/0.10-os2-b1/src/options/optionsdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/optionsdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/optionsdlg.h	(revision 38)
@@ -0,0 +1,30 @@
+#ifndef OPTIONSDLG_H
+#define OPTIONSDLG_H
+
+#include "ui_options.h"
+
+class PsiCon;
+struct Options;
+
+class OptionsDlg : public OptionsUI
+{
+	Q_OBJECT
+public:
+	OptionsDlg(PsiCon *, const Options &, QWidget *parent = 0, const char *name = 0);
+	~OptionsDlg();
+
+signals:
+	void applyOptions(const Options &);
+
+private slots:
+	void doOk();
+	void doApply();
+
+public:
+	class Private;
+private:
+	Private *d;
+	friend class Private;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/optionsdlgiface.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/optionsdlgiface.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/optionsdlgiface.h	(revision 38)
@@ -0,0 +1,15 @@
+#ifndef OPTIONSDLGIFACE_H
+#define OPTIONSDLGIFACE_H
+
+class OptionsDlgIface : public QObject
+{
+	Q_OBJECT
+public:
+	OptionsDlgIface(QObject *parent, const char *name);
+	~OptionsDlgIface();
+
+public slots:
+
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/optionstab.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/optionstab.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/optionstab.cpp	(revision 38)
@@ -0,0 +1,274 @@
+#include "optionstab.h"
+#include "iconset.h"
+
+#include <qtabwidget.h>
+#include <qlayout.h>
+#include <qmap.h>
+
+//----------------------------------------------------------------------------
+// OptionsTab
+//----------------------------------------------------------------------------
+
+OptionsTab::OptionsTab(QObject *parent, const char *name)
+: QObject(parent, name)
+{
+}
+
+OptionsTab::OptionsTab(QObject *parent, QCString _id, QCString _parentId, QString _name, QString _desc, QString _tabIconName, QString _iconName)
+: QObject(parent, _name.latin1())
+{
+	v_id = _id;
+	v_parentId = _parentId;
+	v_name = _name;
+	v_desc = _desc;
+	v_tabIconName = _tabIconName;
+	v_iconName = _iconName;
+}
+
+OptionsTab::~OptionsTab()
+{
+}
+
+QCString OptionsTab::id() const
+{
+	return v_id;
+}
+
+QCString OptionsTab::parentId() const
+{
+	return v_parentId;
+}
+
+QString OptionsTab::tabName() const
+{
+	return v_name;
+}
+
+Icon *OptionsTab::tabIcon() const
+{
+	if ( v_tabIconName.isEmpty() )
+		return 0;
+
+	return (Icon *)IconsetFactory::iconPtr( v_tabIconName );
+}
+
+QString OptionsTab::name() const
+{
+	return v_name;
+}
+
+QString OptionsTab::desc() const
+{
+	return v_desc;
+}
+
+Icon *OptionsTab::icon() const
+{
+	if ( v_iconName.isEmpty() ) {
+		//if ( tabIcon() )
+		//	return tabIcon();
+
+		return (Icon *)IconsetFactory::iconPtr("psi/psi32");
+	}
+
+	return (Icon *)IconsetFactory::iconPtr( v_iconName );
+}
+
+void OptionsTab::applyOptions(Options *)
+{
+}
+
+void OptionsTab::restoreOptions(const Options *)
+{
+}
+
+void OptionsTab::tabAdded(OptionsTab *)
+{
+}
+
+bool OptionsTab::stretchable() const
+{
+	return false;
+}
+
+void OptionsTab::setData(PsiCon *, QWidget *)
+{
+}
+
+//----------------------------------------------------------------------------
+// OptionsTabWidget
+//----------------------------------------------------------------------------
+
+class OptionsTabWidget : public QTabWidget
+{
+	Q_OBJECT
+public:
+	OptionsTabWidget(QWidget *parent);
+	void addTab(OptionsTab *);
+	void restoreOptions(const Options *);
+
+signals:
+	void connectDataChanged(QWidget *);
+	void noDirty(bool);
+
+private slots:
+	void updateCurrent(QWidget *);
+
+private:
+	struct TabData {
+		TabData() { tab = 0; initialized = false; }
+		TabData(OptionsTab *t) { tab = t; initialized = false; }
+		OptionsTab *tab;
+		bool initialized;
+	};
+	QMap<QWidget *, TabData> w2tab;
+	Options *opt;
+};
+
+OptionsTabWidget::OptionsTabWidget(QWidget *parent)
+: QTabWidget(parent)
+{
+	connect(this, SIGNAL(currentChanged(QWidget *)), SLOT(updateCurrent(QWidget *)));
+	opt = 0;
+}
+
+void OptionsTabWidget::addTab(OptionsTab *tab)
+{
+	if ( tab->tabName().isEmpty() )
+		return; // skip the dummy tabs
+
+	QWidget *w = new QWidget(this, tab->name().latin1());
+
+	if ( tab->tabIcon() )
+		insertTab(w, tab->tabIcon()->iconSet(), tab->tabName());
+	else
+		insertTab(w, tab->tabName());
+
+	if ( !tab->desc().isEmpty() )
+		setTabToolTip(w, tab->desc());
+
+	w2tab[w] = TabData(tab);
+}
+
+void OptionsTabWidget::updateCurrent(QWidget *w)
+{
+	if ( !w2tab[w].initialized ) {
+		QVBoxLayout *vbox = new QVBoxLayout(w, 5);
+		OptionsTab *opttab = w2tab[w].tab;
+
+		QWidget *tab = opttab->widget();
+		if ( !tab )
+			return;
+
+		tab->reparent(w, 0, QPoint(0, 0));
+		vbox->addWidget(tab);
+		if ( !opttab->stretchable() )
+			vbox->addStretch();
+
+		if ( opt ) {
+			emit noDirty(true);
+			opttab->restoreOptions(opt);
+			emit noDirty(false);
+		}
+		emit connectDataChanged(tab);
+
+		tab->show();
+		w2tab[w].initialized = true;
+	}
+}
+
+void OptionsTabWidget::restoreOptions(const Options *o)
+{
+	bool doRestore = !opt;
+	opt = (Options *)o;
+
+	if ( doRestore ) {
+		emit noDirty(true);
+		w2tab[currentPage()].tab->restoreOptions(opt);
+		emit noDirty(false);
+	}
+}
+
+//----------------------------------------------------------------------------
+// MetaOptionsTab
+//----------------------------------------------------------------------------
+
+MetaOptionsTab::MetaOptionsTab(QObject *parent, const char *name)
+: OptionsTab(parent, name)
+{
+	init();
+}
+
+MetaOptionsTab::MetaOptionsTab(QObject *parent, QCString id, QCString parentId, QString name, QString desc, QString tabIconName, QString iconName)
+: OptionsTab(parent, id, parentId, name, desc, tabIconName, iconName)
+{
+	init();
+}
+
+MetaOptionsTab::~MetaOptionsTab()
+{
+	if ( w )
+		delete w;
+}
+
+void MetaOptionsTab::init()
+{
+	w = 0;
+	tabs.setAutoDelete(true);
+}
+
+void MetaOptionsTab::addTab(OptionsTab *tab)
+{
+	connect(tab, SIGNAL(dataChanged()), SIGNAL(dataChanged()));
+	//connect(tab, SIGNAL(addWidgetChangedSignal(QString, QCString)), SIGNAL(addWidgetChangedSignal(QString, QCString)));
+	connect(tab, SIGNAL(noDirty(bool)), SIGNAL(noDirty(bool)));
+	connect(tab, SIGNAL(connectDataChanged(QWidget *)), SIGNAL(connectDataChanged(QWidget *)));
+
+	tabs.append(tab);
+}
+
+QWidget *MetaOptionsTab::widget()
+{
+	if ( w )
+		return w;
+
+	OptionsTabWidget *t = new OptionsTabWidget(0);
+	w = t;
+
+	connect(w, SIGNAL(connectDataChanged(QWidget *)), SIGNAL(connectDataChanged(QWidget *)));
+	connect(w, SIGNAL(noDirty(bool)), SIGNAL(noDirty(bool)));
+
+	QPtrListIterator<OptionsTab> it(tabs);
+	for ( ; it.current(); ++it)
+		t->addTab(it.current());
+
+	return w;
+}
+
+void MetaOptionsTab::applyOptions(Options *opt)
+{
+	QPtrListIterator<OptionsTab> it(tabs);
+	for ( ; it.current(); ++it)
+		it.current()->applyOptions(opt);
+}
+
+void MetaOptionsTab::restoreOptions(const Options *opt)
+{
+	if ( w ) {
+		OptionsTabWidget *d = (OptionsTabWidget *)w;
+		d->restoreOptions(opt);
+	}
+
+	QPtrListIterator<OptionsTab> it(tabs);
+	for ( ; it.current(); ++it)
+		it.current()->restoreOptions(opt);
+}
+
+void MetaOptionsTab::setData(PsiCon *psi, QWidget *w)
+{
+	QPtrListIterator<OptionsTab> it(tabs);
+	for ( ; it.current(); ++it)
+		it.current()->setData(psi, w);
+}
+
+#include "optionstab.moc"
Index: psi/tags/0.10-os2-b1/src/options/optionstab.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/optionstab.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/optionstab.h	(revision 38)
@@ -0,0 +1,76 @@
+#ifndef OPTIONSTAB_H
+#define OPTIONSTAB_H
+
+#include <qcstring.h>
+#include <qstring.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+struct Options;
+class Icon;
+class QWidget;
+class PsiCon;
+
+class OptionsTab : public QObject
+{
+	Q_OBJECT
+public:
+	OptionsTab(QObject *parent, const char *name = 0);
+	OptionsTab(QObject *parent, QCString id, QCString parentId, QString name, QString desc, QString tabIconName = QString::null, QString iconName = QString::null);
+	~OptionsTab();
+
+	virtual QCString id() const;		// Unique identifier, i.e. "plugins_misha's_cool-plugin"
+	virtual QCString parentId() const;	// Identifier of parent tab, i.e. "general"
+
+	virtual QString tabName() const;	// "General"
+	virtual Icon *tabIcon() const;		// default implementation returns 0
+
+	virtual QString name() const;		// "Roster"
+	virtual QString desc() const;		// "You can configure your roster here"
+	virtual Icon *icon() const;		// default implementation returns 0
+
+	virtual QWidget *widget() = 0;		// Actual widget that contains checkboxes, pushbuttons, etc.
+						// the widget is reparented after this call
+	virtual bool stretchable() const;	// return 'true' if widget() is stretchable and wants a lot of space
+
+signals:
+	void dataChanged();
+	//void addWidgetChangedSignal(QString widgetName, QCString signal);
+	void noDirty(bool);
+	void connectDataChanged(QWidget *);
+
+public slots:
+	virtual void setData(PsiCon *, QWidget *parentDialog);
+	virtual void applyOptions(Options *opt);
+	virtual void restoreOptions(const Options *opt);
+	virtual void tabAdded(OptionsTab *tab);	// called when tab 'tab' specifies this tab as parent
+
+private:
+	QCString v_id, v_parentId;
+	QString v_name, v_desc, v_tabIconName, v_iconName;
+};
+
+class MetaOptionsTab : public OptionsTab
+{
+	Q_OBJECT
+public:
+	MetaOptionsTab(QObject *parent, const char *name = 0);
+	MetaOptionsTab(QObject *parent, QCString id, QCString parentId, QString name, QString desc, QString tabIconName = QString::null, QString iconName = QString::null);
+	~MetaOptionsTab();
+
+	QWidget *widget();
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+
+	void setData(PsiCon *, QWidget *);
+	bool stretchable() const { return true; }
+
+	void addTab(OptionsTab *);
+
+private:
+	void init();
+	QWidget *w;
+	QPtrList<OptionsTab> tabs;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/toolbardlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/options/toolbardlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/toolbardlg.cpp	(revision 38)
@@ -0,0 +1,715 @@
+#include "toolbardlg.h"
+
+#include "psicon.h"
+#include "common.h"
+#include "iconwidget.h"
+#include "mainwin.h"
+#include "psitoolbar.h"
+#include "iconaction.h"
+#include "psiactionlist.h"
+
+#include "opt_lookfeel_toolbars.h"
+#include "ui_positiontoolbar.h"
+
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qframe.h>
+#include <qlistview.h>
+#include <qcombobox.h>
+#include <qaction.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qheader.h>
+#include <qspinbox.h>
+
+//----------------------------------------------------------------------------
+// PositionToolbarDlg
+//----------------------------------------------------------------------------
+
+class PositionToolbarDlg : public PositionToolbarUI
+{
+	Q_OBJECT
+public:
+	PositionToolbarDlg(QWidget *parent, Options::ToolbarPrefs *, int);
+	~PositionToolbarDlg();
+
+	int n();
+	bool dirty;
+
+	bool eventFilter(QObject *watched, QEvent *e);
+
+signals:
+	void applyPressed();
+
+private slots:
+	void dataChanged();
+	void apply();
+
+private:
+	int id;
+	Options::ToolbarPrefs *tb;
+};
+
+PositionToolbarDlg::PositionToolbarDlg(QWidget *parent, Options::ToolbarPrefs *_tb, int _id)
+: PositionToolbarUI(parent, "PositionToolbarDlg", true)
+{
+	tb = _tb;
+	id = _id;
+
+	connect(pb_ok, SIGNAL(clicked()), SLOT(apply()));
+	connect(pb_ok, SIGNAL(clicked()), SLOT(accept()));
+	connect(pb_apply, SIGNAL(clicked()), SLOT(apply()));
+	connect(pb_cancel, SIGNAL(clicked()), SLOT(reject()));
+
+	connect(cb_dock, SIGNAL(highlighted(int)), SLOT(dataChanged()));
+	sb_index->installEventFilter( this );
+	sb_extraOffset->installEventFilter( this );
+	connect(ck_nl, SIGNAL(toggled(bool)), SLOT(dataChanged()));
+
+	le_name->setText( tb->name );
+	if ( tb->dock >= DockUnmanaged && tb->dock <= DockTornOff ) {
+		cb_dock->setCurrentItem( tb->dock + DockMinimized - DockTornOff );
+	}
+	else {
+		cb_dock->setCurrentItem( tb->dock - DockTop );
+	}
+	sb_index->setValue( tb->index );
+	sb_extraOffset->setValue( tb->extraOffset );
+	ck_nl->setChecked( tb->nl );
+
+	dirty = false;
+	pb_apply->setEnabled(false);
+
+	resize(sizeHint());
+}
+
+PositionToolbarDlg::~PositionToolbarDlg()
+{
+}
+
+int PositionToolbarDlg::n()
+{
+	return id;
+}
+
+void PositionToolbarDlg::dataChanged()
+{
+	dirty = true;
+	pb_apply->setEnabled(true);
+}
+
+void PositionToolbarDlg::apply()
+{
+	tb->dirty = true;
+	if ( cb_dock->currentItem() >= 0 && cb_dock->currentItem() < 5 ) {
+		// Top, Bottom, Left, Right and Minimised
+		tb->dock = (Dock)(cb_dock->currentItem() + DockTop);
+	}
+	else {
+		// Unmanaged and TornOff
+		tb->dock = (Dock)(cb_dock->currentItem() - (DockMinimized - DockTornOff));
+	}
+
+	tb->index = sb_index->value();
+	tb->extraOffset = sb_extraOffset->value();
+	tb->nl = ck_nl->isChecked();
+
+	if ( dirty )
+		emit applyPressed();
+	dirty = false;
+	pb_apply->setEnabled(false);
+}
+
+bool PositionToolbarDlg::eventFilter(QObject *watched, QEvent *e)
+{
+	if ( watched->inherits("QSpinBox") && e->type() == QEvent::KeyRelease )
+		dataChanged();
+	return false;
+}
+
+//----------------------------------------------------------------------------
+// ToolbarDlg
+//----------------------------------------------------------------------------
+
+class ToolbarDlg::Private
+{
+public:
+	struct ToolbarItem {
+		QString group;
+		int index;
+		ToolbarItem() {
+			group = "";
+			index = -1;
+		}
+		ToolbarItem( QString _w, int _i ) {
+			group = _w;
+			index = _i;
+		}
+	};
+
+	QMap<int, ToolbarItem> toolbars;
+
+	PsiActionList::ActionsType class2id( QString name ) {
+		int ret = (int)PsiActionList::Actions_Common;
+
+		if ( name == "mainWin" )
+			ret |= (int)PsiActionList::Actions_MainWin;
+
+		return (PsiActionList::ActionsType)ret;
+	}
+};
+
+ToolbarDlg::ToolbarDlg(PsiCon *_psi, QWidget *parent, const char *name)
+: QDialog(parent, name, false, WDestructiveClose)
+{
+	p = new Private();
+
+	psi  = _psi;
+	psi->dialogRegister(this);
+	noDirty = false;
+	opt = new Options;
+
+	setCaption(CAP(tr("Configure Toolbars")));
+
+	connect(this, SIGNAL(dataChanged()), SLOT(dataChangedSlot()));
+
+	QVBoxLayout *vbox = new QVBoxLayout(this, 11, 6);
+	w = new LookFeelToolbarsUI(this);
+	vbox->addWidget(w);
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+
+	connect(d->pb_addToolbar, SIGNAL(clicked()), SLOT(toolbarAdd()));
+	connect(d->pb_deleteToolbar, SIGNAL(clicked()), SLOT(toolbarDelete()));
+	connect(d->cb_toolbars, SIGNAL(activated(int)), SLOT(toolbarSelectionChanged(int)));
+	connect(d->le_toolbarName, SIGNAL(textChanged(const QString &)), SLOT(toolbarNameChanged()));
+	connect(d->pb_toolbarPosition, SIGNAL(clicked()), SLOT(toolbarPosition()));
+	connect(d->tb_up, SIGNAL(clicked()), SLOT(toolbarActionUp()));
+	connect(d->tb_down, SIGNAL(clicked()), SLOT(toolbarActionDown()));
+	connect(d->tb_right, SIGNAL(clicked()), SLOT(toolbarAddAction()));
+	connect(d->tb_left, SIGNAL(clicked()), SLOT(toolbarRemoveAction()));
+
+	connect(d->ck_toolbarOn, SIGNAL(toggled(bool)), SLOT(toolbarDataChanged()));
+	connect(d->ck_toolbarLocked, SIGNAL(toggled(bool)), SLOT(toolbarDataChanged()));
+	connect(d->ck_toolbarStretch, SIGNAL(toggled(bool)), SLOT(toolbarDataChanged()));
+	connect(d->lv_selectedActions, SIGNAL(selectionChanged(QListViewItem *)), SLOT(selAct_selectionChanged(QListViewItem *)));
+	connect(d->lv_availActions, SIGNAL(selectionChanged(QListViewItem *)), SLOT(avaAct_selectionChanged(QListViewItem *)));
+
+	connect(d->pb_deleteToolbar, SIGNAL(clicked()), SIGNAL(dataChanged()));
+	connect(d->tb_up, SIGNAL(clicked()), SIGNAL(dataChanged()));
+	connect(d->tb_down, SIGNAL(clicked()), SIGNAL(dataChanged()));
+	connect(d->tb_left, SIGNAL(clicked()), SIGNAL(dataChanged()));
+	connect(d->tb_right, SIGNAL(clicked()), SIGNAL(dataChanged()));
+	connect(d->pb_addToolbar, SIGNAL(clicked()), SIGNAL(dataChanged()));
+	connect(d->pb_deleteToolbar, SIGNAL(clicked()), SIGNAL(dataChanged()));
+
+	d->lv_selectedActions->header()->hide();
+	d->lv_availActions->header()->hide();
+
+	d->lv_selectedActions->setSorting(-1);
+	d->lv_availActions->setSorting(-1);
+
+	// TODO: add QWhatsThis to all widgets
+
+	QFrame *line = new QFrame( this );
+	line->setFrameShape( QFrame::HLine );
+	line->setFrameShadow( QFrame::Sunken );
+	line->setFrameShape( QFrame::HLine );
+	vbox->addWidget( line );
+
+	QHBoxLayout *hbox = new QHBoxLayout( 0, 0, 6 );
+	vbox->addLayout(hbox);
+
+	QSpacerItem *spacer = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
+	hbox->addItem( spacer );
+
+	IconButton *pb_ok = new IconButton( this );
+	hbox->addWidget( pb_ok );
+	pb_ok->setText( tr("&OK") );
+	connect(pb_ok, SIGNAL(clicked()), SLOT(doApply()));
+	connect(pb_ok, SIGNAL(clicked()), SLOT(accept()));
+
+	//pb_apply = 0;
+	pb_apply = new IconButton( this );
+	hbox->addWidget( pb_apply );
+	pb_apply->setText( tr("&Apply") );
+	connect(pb_apply, SIGNAL(clicked()), SLOT(doApply()));
+	pb_apply->setEnabled(false);
+
+	IconButton *pb_cancel = new IconButton( this );
+	hbox->addWidget( pb_cancel );
+	pb_cancel->setText( tr("&Cancel") );
+	connect(pb_cancel, SIGNAL(clicked()), SLOT(reject()));
+
+	restoreOptions( &option );
+	resize( minimumSize() );
+}
+
+ToolbarDlg::~ToolbarDlg()
+{
+	psi->dialogUnregister(this);
+	delete opt;
+	delete p;
+}
+
+void ToolbarDlg::setCurrentToolbar(PsiToolBar *t)
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+
+	if ( pb_apply->isEnabled() )
+		return;
+
+	QMap<int, Private::ToolbarItem>::Iterator it = p->toolbars.begin();
+	for ( ; it != p->toolbars.end(); ++it ) {
+		if ( it.data().group == t->group() && it.data().index == t->groupIndex() ) {
+			d->cb_toolbars->setCurrentItem( it.key() );
+			toolbarSelectionChanged( it.key() );
+			break;
+		}
+	}
+}
+
+void ToolbarDlg::applyOptions(Options *o)
+{
+	if ( !w )
+		opt->toolbars = o->toolbars;
+
+	// get current toolbars' positions
+	MainWin *mainWin = (MainWin *)psi->mainWin();
+	for (uint i = 0; i < opt->toolbars.count() && i < mainWin->toolbars.count(); i++) {
+		//if ( toolbarPositionInProgress && posTbDlg->n() == (int)i )
+		//	continue;
+
+		Options::ToolbarPrefs &tbPref = opt->toolbars["mainWin"][i];
+		mainWin->getLocation ( mainWin->toolbars.at(i), tbPref.dock, tbPref.index, tbPref.nl, tbPref.extraOffset );
+	}
+
+	// apply options
+	o->toolbars = opt->toolbars;
+}
+
+void ToolbarDlg::rebuildToolbarList()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+
+	d->cb_toolbars->clear();
+	p->toolbars.clear();
+
+	QMap< QString, QValueList<Options::ToolbarPrefs> >::Iterator it = opt->toolbars.begin();
+	for ( ; it != opt->toolbars.end(); ++it ) {
+		if ( p->toolbars.count() )
+			d->cb_toolbars->insertItem( "---" ); // TODO: think of better separator
+
+		int i = 0;
+		QValueList<Options::ToolbarPrefs>::Iterator it2 = it.data().begin();
+		for ( ; it2 != it.data().end(); ++it2, ++i ) {
+			d->cb_toolbars->insertItem( (*it2).name );
+			p->toolbars[d->cb_toolbars->count()-1] = Private::ToolbarItem( it.key(), i );
+		}
+	}
+}
+
+void ToolbarDlg::restoreOptions(const Options *o)
+{
+	if ( !w )
+		return;
+
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	opt->toolbars = o->toolbars;
+	opt->hideMenubar = o->hideMenubar;
+
+	rebuildToolbarList();
+
+	if(d->cb_toolbars->count() > 0) {
+		d->cb_toolbars->setCurrentItem( 0 );
+		toolbarSelectionChanged( 0 );
+	}
+	else
+		toolbarSelectionChanged( -1 );
+}
+
+//----------------------------------------------------------------------------
+
+void ToolbarDlg::toolbarAdd()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+
+	int i = d->cb_toolbars->count();
+
+	Options::ToolbarPrefs tb;
+	tb.name = QObject::tr("<unnamed>");
+	tb.on = false;
+	tb.locked = false;
+	tb.stretchable = false;
+	tb.keys.clear();
+
+	tb.dock = Qt::DockTop;
+	tb.index = i;
+	tb.nl = true;
+	tb.extraOffset = 0;
+
+	tb.dirty = true;
+
+	opt->toolbars["mainWin"].append(tb);
+
+	rebuildToolbarList();
+/*	TODO
+	d->cb_toolbars->insertItem( tb.name );
+	d->cb_toolbars->setCurrentItem( d->cb_toolbars->count() - 1 );
+	toolbarSelectionChanged( d->cb_toolbars->count() - 1 );
+
+	d->le_toolbarName->setFocus();
+*/
+}
+
+void ToolbarDlg::toolbarDelete()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	int n = d->cb_toolbars->currentItem();
+
+	Options::ToolbarPrefs tb = opt->toolbars[p->toolbars[n].group][p->toolbars[n].index];
+	PsiToolBar *toolBar = psi->findToolBar( p->toolbars[n].group, p->toolbars[n].index );
+	if ( !toolBar )
+		return;
+
+	// delete current toolbar
+	{
+		int idx = p->toolbars[n].index;
+		QValueList<Options::ToolbarPrefs>::Iterator it = opt->toolbars[p->toolbars[n].group].begin();
+		for (int i = 0; i < idx; i++)
+			++it;
+
+		noDirty = true;
+		opt->toolbars[p->toolbars[n].group].remove(it);
+
+		rebuildToolbarList();
+		noDirty = false;
+		toolbarSelectionChanged( n - 1 ); // TODO FIXME
+	}
+}
+
+void ToolbarDlg::addToolbarAction(QListView *parent, QString name, int toolbarId)
+{
+	ActionList actions = psi->actionList()->suitableActions( (PsiActionList::ActionsType)toolbarId );
+	const QAction *action = (QAction *)actions.action( name );
+	if ( !action )
+		return;
+
+	addToolbarAction(parent, action, name);
+}
+
+void ToolbarDlg::addToolbarAction(QListView *parent, const QAction *action, QString name)
+{
+	QListViewItem *item = new QListViewItem(parent, parent->lastItem());
+
+	QString n = actionName(action);
+	if ( !action->whatsThis().isEmpty() )
+		n += " - " + action->whatsThis();
+	item->setText(0, n);
+	item->setText(1, name);
+	item->setPixmap(0, action->iconSet().pixmap());
+}
+
+void ToolbarDlg::toolbarSelectionChanged(int item)
+{
+	if ( noDirty )
+		return;
+
+	int n = item;
+	Options::ToolbarPrefs tb = opt->toolbars[p->toolbars[n].group][p->toolbars[n].index];
+	PsiToolBar *toolBar = psi->findToolBar( p->toolbars[n].group, p->toolbars[n].index );
+
+	bool customizeable = toolBar ? toolBar->isCustomizeable() : false;
+	bool moveable      = toolBar ? toolBar->isMoveable() : false;
+
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	bool enable = (item == -1) ? false: true;
+	d->le_toolbarName->setEnabled( enable );
+	d->pb_toolbarPosition->setEnabled( enable && moveable );
+	d->ck_toolbarOn->setEnabled( enable );
+	d->ck_toolbarLocked->setEnabled( enable && moveable );
+	d->ck_toolbarStretch->setEnabled( enable && moveable );
+	d->lv_selectedActions->setEnabled( enable && customizeable );
+	d->lv_availActions->setEnabled( enable && customizeable );
+	d->tb_up->setEnabled( enable && customizeable );
+	d->tb_down->setEnabled( enable && customizeable );
+	d->tb_left->setEnabled( enable && customizeable );
+	d->tb_right->setEnabled( enable && customizeable );
+	d->pb_deleteToolbar->setEnabled( enable && p->toolbars[n].group == "mainWin" );
+	d->cb_toolbars->setEnabled( enable );
+
+	d->lv_availActions->clear();
+	d->lv_selectedActions->clear();
+
+	if ( !enable )
+		return;
+
+	noDirty = true;
+
+	d->le_toolbarName->setText( tb.name );
+	d->ck_toolbarOn->setChecked( tb.on );
+	d->ck_toolbarLocked->setChecked( tb.locked || !moveable );
+	d->ck_toolbarStretch->setChecked( tb.stretchable );
+
+	{
+		// Fill the ListView with toolbar-specific actions
+		QPtrList<ActionList> actionLists = psi->actionList()->actionLists( p->class2id( p->toolbars[n].group ) );
+		QPtrListIterator<ActionList> it ( actionLists );
+		ActionList *actionList;
+		QListView *lv = d->lv_availActions;
+		QListViewItem *lastRoot = 0;
+		for ( ; (actionList = it.current()); ++it ) {
+			QListViewItem *root = new QListViewItem(lv, lastRoot);
+			lastRoot = root;
+			root->setText( 0, actionList->name() );
+			root->setOpen( true );
+
+			QListViewItem *last = 0;
+			QStringList actionNames = actionList->actions();
+			QStringList::Iterator it2 = actionNames.begin();
+			for ( ; it2 != actionNames.end(); ++it2 ) {
+				IconAction *action = actionList->action( *it2 );
+		        	QListViewItem *item = new QListViewItem( root, last );
+				last = item;
+
+				QString n = actionName((QAction *)action);
+				if ( !action->whatsThis().isEmpty() )
+					n += " - " + action->whatsThis();
+				item->setText(0, n);
+				item->setText(1, action->name());
+				item->setPixmap(0, action->iconSet().pixmap());
+			}
+		}
+	}
+
+	QStringList::Iterator it = tb.keys.begin();
+	for ( ; it != tb.keys.end(); ++it) {
+		addToolbarAction(d->lv_selectedActions, *it, p->class2id( p->toolbars[n].group ) );
+	}
+	updateArrows();
+
+	noDirty = false;
+}
+
+void ToolbarDlg::rebuildToolbarKeys()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	if ( !d->cb_toolbars->count() )
+		return;
+	int n = d->cb_toolbars->currentItem();
+
+	QStringList keys;
+	QListViewItemIterator it( d->lv_selectedActions );
+        for ( ; it.current(); ++it) {
+		QListViewItem *item = it.current();
+
+		keys << item->text(1);
+        }
+
+	opt->toolbars["mainWin"][n].keys  = keys;
+	opt->toolbars["mainWin"][n].dirty = true;
+	emit dataChanged();
+}
+
+void ToolbarDlg::updateArrows()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	bool up = false, down = false, left = false, right = false;
+
+	if(d->lv_availActions->selectedItem() && !d->lv_availActions->selectedItem()->text(1).isEmpty())
+		right = true;
+	QListViewItem *i = d->lv_selectedActions->selectedItem();
+	if(i) {
+		left = true;
+
+		// get numeric index of item
+		int n = 0;
+		for(QListViewItem *it = d->lv_selectedActions->firstChild(); it != i; it = it->nextSibling()) {
+			++n;
+		}
+
+		if(n > 0)
+			up = true;
+		if(n < d->lv_selectedActions->childCount()-1)
+			down = true;
+	}
+
+	d->tb_up->setEnabled(up);
+	d->tb_down->setEnabled(down);
+	d->tb_left->setEnabled(left);
+	d->tb_right->setEnabled(right);
+}
+
+void ToolbarDlg::toolbarNameChanged()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	if ( !d->cb_toolbars->count() )
+		return;
+
+	int n = d->cb_toolbars->currentItem();
+	d->cb_toolbars->changeItem(d->le_toolbarName->text(), n);
+	opt->toolbars["mainWin"][n].name = d->le_toolbarName->text();
+
+	emit dataChanged();
+}
+
+void ToolbarDlg::toolbarActionUp()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	QListViewItem *item = d->lv_selectedActions->selectedItem();
+	if ( !item )
+		return;
+
+	if ( item->itemAbove() )
+		item->itemAbove()->moveItem(item);
+	rebuildToolbarKeys();
+	updateArrows();
+}
+
+void ToolbarDlg::toolbarActionDown()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	QListViewItem *item = d->lv_selectedActions->selectedItem();
+	if ( !item )
+		return;
+
+	if ( item->itemBelow() )
+		item->moveItem( item->itemBelow() );
+	rebuildToolbarKeys();
+	updateArrows();
+}
+
+void ToolbarDlg::toolbarAddAction()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	QListViewItem *item = d->lv_availActions->selectedItem();
+	if ( !item || item->text(1).isEmpty() )
+		return;
+
+	addToolbarAction(d->lv_selectedActions, item->text(1), p->class2id( p->toolbars[d->cb_toolbars->currentItem()].group ) );
+	rebuildToolbarKeys();
+	updateArrows();
+}
+
+void ToolbarDlg::toolbarRemoveAction()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	QListViewItem *item = d->lv_selectedActions->selectedItem();
+	if ( !item )
+		return;
+
+	delete item;
+
+	if(d->lv_selectedActions->currentItem())
+		d->lv_selectedActions->setSelected(d->lv_selectedActions->currentItem(), true);
+
+	rebuildToolbarKeys();
+	updateArrows();
+}
+
+void ToolbarDlg::toolbarDataChanged()
+{
+	if ( noDirty )
+		return;
+
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	if ( !d->cb_toolbars->count() )
+		return;
+	int n = d->cb_toolbars->currentItem();
+
+	opt->toolbars["mainWin"][n].dirty = true;
+	opt->toolbars["mainWin"][n].name = d->le_toolbarName->text();
+	opt->toolbars["mainWin"][n].on = d->ck_toolbarOn->isChecked();
+	opt->toolbars["mainWin"][n].locked = d->ck_toolbarLocked->isChecked();
+	opt->toolbars["mainWin"][n].stretchable = d->ck_toolbarStretch->isChecked();
+
+	emit dataChanged();
+}
+
+QString ToolbarDlg::actionName(const QAction *a)
+{
+	QString n = a->menuText(), n2;
+	for (int i = 0; i < (int)n.length(); i++) {
+		if ( n[i] == '&' && n[i+1] != '&' )
+			continue;
+		else if ( n[i] == '&' && n[i+1] == '&' )
+			n2 += '&';
+		else
+			n2 += n[i];
+	}
+
+	return n2;
+}
+
+void ToolbarDlg::toolbarPosition()
+{
+	LookFeelToolbarsUI *d = (LookFeelToolbarsUI *)w;
+	if ( !d->cb_toolbars->count() )
+		return;
+	int n = d->cb_toolbars->currentItem();
+
+	PositionToolbarDlg *posTbDlg = new PositionToolbarDlg(this, &opt->toolbars["mainWin"][n], n);
+	connect(posTbDlg, SIGNAL(applyPressed()), SLOT(toolbarPositionApply()));
+
+	posTbDlg->exec();
+	delete posTbDlg;
+}
+
+void ToolbarDlg::toolbarPositionApply()
+{
+	emit dataChanged();
+
+	option.toolbars = opt->toolbars;
+	MainWin *mainWin = (MainWin *)psi->mainWin();
+	mainWin->buildToolbars();
+}
+
+void ToolbarDlg::doApply()
+{
+	// Check if all toolbars are disabled
+	bool toolbarsVisible = false;
+	QValueList<Options::ToolbarPrefs>::ConstIterator it = opt->toolbars["mainWin"].begin();
+	for ( ; it != opt->toolbars["mainWin"].end() && !toolbarsVisible; ++it) {
+		toolbarsVisible = toolbarsVisible || (*it).on;
+	}
+
+	// Undar MacOS, we have the toolbar in a menu anyway
+#ifndef Q_WS_MAC
+	if ( !toolbarsVisible && opt->hideMenubar) {
+		QMessageBox::warning(this, tr("Warning"),
+			tr("You can not disable <i>all</i> toolbars <i>and</i> the menubar. If you do so, you will be unable to enable them back, when you'll change your mind.\n"
+				"<br><br>\n"
+				"If you really-really want to disable all toolbars and the menubar, you need to edit the config.xml file by hand."),
+			tr("OK"));
+		return;
+	}
+#endif
+
+	applyOptions(opt);
+	option.toolbars = opt->toolbars;
+	MainWin *mainWin = (MainWin *)psi->mainWin();
+	mainWin->buildToolbars();
+
+	if ( pb_apply )
+		pb_apply->setEnabled(false);
+}
+
+void ToolbarDlg::dataChangedSlot()
+{
+	if ( noDirty )
+		return;
+
+	if ( pb_apply )
+		pb_apply->setEnabled(true);
+}
+
+void ToolbarDlg::selAct_selectionChanged(QListViewItem *)
+{
+	updateArrows();
+}
+
+void ToolbarDlg::avaAct_selectionChanged(QListViewItem *)
+{
+	updateArrows();
+}
+
+#include "toolbardlg.moc"
Index: psi/tags/0.10-os2-b1/src/options/toolbardlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/toolbardlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/toolbardlg.h	(revision 38)
@@ -0,0 +1,69 @@
+#ifndef TOOLBARDLG_H
+#define TOOLBARDLG_H
+
+#include <qdialog.h>
+
+class PsiCon;
+class PsiToolBar;
+struct Options;
+class QAction;
+class IconButton;
+class QListView;
+class QListViewItem;
+
+class ToolbarDlg : public QDialog
+{
+	Q_OBJECT
+public:
+	ToolbarDlg(PsiCon *, QWidget *parent = 0, const char *name = 0);
+	~ToolbarDlg();
+
+	void setCurrentToolbar(PsiToolBar *);
+
+signals:
+	void applyOptions(const Options &);
+
+private slots:
+	void toolbarAdd();
+	void toolbarDelete();
+	void addToolbarAction(QListView *, QString name, int toolbarId);
+	void addToolbarAction(QListView *, const QAction *action, QString name);
+	void toolbarSelectionChanged(int);
+	void rebuildToolbarKeys();
+	void toolbarNameChanged();
+	void toolbarActionUp();
+	void toolbarActionDown();
+	void toolbarAddAction();
+	void toolbarRemoveAction();
+	void toolbarDataChanged();
+	QString actionName(const QAction *a);
+	void toolbarPosition();
+	void selAct_selectionChanged(QListViewItem *);
+	void avaAct_selectionChanged(QListViewItem *);
+
+	void doApply();
+	void toolbarPositionApply();
+
+	void dataChangedSlot();
+
+	void applyOptions(Options *opt);
+	void restoreOptions(const Options *opt);
+	void rebuildToolbarList();
+
+signals:
+	void dataChanged();
+
+private:
+	void updateArrows();
+
+	QWidget *w;
+	Options *opt;
+	PsiCon *psi;
+	bool noDirty;
+	IconButton *pb_apply;
+
+	class Private;
+	Private *p;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/options/ui_isdetails.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/ui_isdetails.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/ui_isdetails.ui	(revision 38)
@@ -0,0 +1,291 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>IconsetDetailsDlg</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>IconsetDetailsDlg</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>557</width>
+            <height>419</height>
+        </rect>
+    </property>
+    <property name="minimumSize">
+        <size>
+            <width>550</width>
+            <height>0</height>
+        </size>
+    </property>
+    <property name="baseSize">
+        <size>
+            <width>550</width>
+            <height>0</height>
+        </size>
+    </property>
+    <property name="caption">
+        <string>Iconset Details: %1</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QSplitter">
+            <property name="name">
+                <cstring>splitter4</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+            <widget class="QLayoutWidget">
+                <property name="name">
+                    <cstring>layout8</cstring>
+                </property>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QLayoutWidget">
+                        <property name="name">
+                            <cstring>layout7</cstring>
+                        </property>
+                        <grid>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="URLLabel" row="4" column="0" rowspan="1" colspan="2">
+                                <property name="name">
+                                    <cstring>url_home</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="0" column="1">
+                                <property name="name">
+                                    <cstring>lb_name</cstring>
+                                </property>
+                                <property name="text">
+                                    <string></string>
+                                </property>
+                                <property name="alignment">
+                                    <set>WordBreak|AlignVCenter</set>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="0" column="0">
+                                <property name="name">
+                                    <cstring>textLabel9</cstring>
+                                </property>
+                                <property name="font">
+                                    <font>
+                                        <bold>1</bold>
+                                    </font>
+                                </property>
+                                <property name="text">
+                                    <string>Name:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="7" column="0" rowspan="1" colspan="2">
+                                <property name="name">
+                                    <cstring>lb_authors</cstring>
+                                </property>
+                                <property name="font">
+                                    <font>
+                                        <bold>1</bold>
+                                    </font>
+                                </property>
+                                <property name="text">
+                                    <string>Authors:</string>
+                                </property>
+                            </widget>
+                            <widget class="PsiTextView" row="8" column="0" rowspan="1" colspan="2">
+                                <property name="name">
+                                    <cstring>ptv_authors</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="1" column="0">
+                                <property name="name">
+                                    <cstring>textLabel11</cstring>
+                                </property>
+                                <property name="font">
+                                    <font>
+                                        <bold>1</bold>
+                                    </font>
+                                </property>
+                                <property name="text">
+                                    <string>Version:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="5" column="0" rowspan="1" colspan="2">
+                                <property name="name">
+                                    <cstring>lb_desc2</cstring>
+                                </property>
+                                <property name="font">
+                                    <font>
+                                        <bold>1</bold>
+                                    </font>
+                                </property>
+                                <property name="text">
+                                    <string>Description:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="6" column="0" rowspan="1" colspan="2">
+                                <property name="name">
+                                    <cstring>lb_desc</cstring>
+                                </property>
+                                <property name="text">
+                                    <string></string>
+                                </property>
+                                <property name="alignment">
+                                    <set>WordBreak|AlignVCenter</set>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="1" column="1">
+                                <property name="name">
+                                    <cstring>lb_version</cstring>
+                                </property>
+                                <property name="text">
+                                    <string></string>
+                                </property>
+                                <property name="alignment">
+                                    <set>WordBreak|AlignVCenter</set>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="2" column="1">
+                                <property name="name">
+                                    <cstring>lb_date</cstring>
+                                </property>
+                                <property name="text">
+                                    <string></string>
+                                </property>
+                                <property name="alignment">
+                                    <set>WordBreak|AlignVCenter</set>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="3" column="0" rowspan="1" colspan="2">
+                                <property name="name">
+                                    <cstring>lb_home</cstring>
+                                </property>
+                                <property name="font">
+                                    <font>
+                                        <bold>1</bold>
+                                    </font>
+                                </property>
+                                <property name="text">
+                                    <string>Home URL:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="2" column="0">
+                                <property name="name">
+                                    <cstring>lb_date2</cstring>
+                                </property>
+                                <property name="font">
+                                    <font>
+                                        <bold>1</bold>
+                                    </font>
+                                </property>
+                                <property name="text">
+                                    <string>Creation date:</string>
+                                </property>
+                            </widget>
+                        </grid>
+                    </widget>
+                    <spacer>
+                        <property name="name">
+                            <cstring>spacer158</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>20</width>
+                                <height>27</height>
+                            </size>
+                        </property>
+                    </spacer>
+                </vbox>
+            </widget>
+            <widget class="IconsetDisplay">
+                <property name="name">
+                    <cstring>isd_iconset</cstring>
+                </property>
+            </widget>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line7</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout114</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer155</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>61</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>iconButton85</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Done</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+    <connection>
+        <sender>iconButton85</sender>
+        <signal>clicked()</signal>
+        <receiver>IconsetDetailsDlg</receiver>
+        <slot>accept()</slot>
+    </connection>
+</connections>
+<includes>
+    <include location="local" impldecl="in declaration">iconset.h</include>
+    <include location="local" impldecl="in implementation">ui_isdetails.ui.h</include>
+</includes>
+<slots>
+    <slot>setIconset( const Iconset &amp; is )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>psitextview.h</includehint>
+    <includehint>psitextview.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/ui_isdetails.ui.h
===================================================================
--- psi/tags/0.10-os2-b1/src/options/ui_isdetails.ui.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/ui_isdetails.ui.h	(revision 38)
@@ -0,0 +1,62 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+#include <qdatetime.h>
+
+void IconsetDetailsDlg::setIconset( const Iconset &is )
+{
+	setCaption(caption().arg(is.name()));
+	
+	lb_name->setText(is.name());
+	lb_version->setText(is.version());
+	
+	if ( is.description().isEmpty() ) {
+		lb_desc->hide();
+		lb_desc2->hide();
+	}
+	else
+		lb_desc->setText(is.description());
+	
+	if ( !is.homeUrl().isEmpty() ) {
+		url_home->setTitle(is.homeUrl());
+		url_home->setUrl(is.homeUrl());
+	}
+	else {
+		url_home->hide();
+		lb_home->hide();
+	}
+		
+	if ( is.creation().isEmpty() ) {
+		lb_date->hide();
+		lb_date2->hide();
+	}
+	else {
+		QDate date = QDate::fromString(is.creation(), ISODate);	
+		lb_date->setText(date.toString(LocalDate));
+	}
+	
+	if ( is.authors().count() == 0 ) {
+		ptv_authors->hide();
+		lb_authors->hide();
+	}
+	else {
+		QString authors;
+		QStringList::ConstIterator it = is.authors().begin();
+		for ( ; it != is.authors().end(); ++it) {
+			if ( !authors.isEmpty() )
+				authors += "<br><br>";
+			authors += *it;
+		}
+		ptv_authors->setText( "<qt><nobr>" + authors + "</nobr></qt>" );
+	}
+	
+	isd_iconset->setIconset(is);
+	
+	resize(sizeHint());
+}
Index: psi/tags/0.10-os2-b1/src/options/ui_options.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/ui_options.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/ui_options.ui	(revision 38)
@@ -0,0 +1,202 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>OptionsUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>OptionsUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>408</width>
+            <height>292</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Options</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout4</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QListView">
+                    <column>
+                        <property name="text">
+                            <string></string>
+                        </property>
+                        <property name="clickable">
+                            <bool>true</bool>
+                        </property>
+                        <property name="resizable">
+                            <bool>true</bool>
+                        </property>
+                    </column>
+                    <property name="name">
+                        <cstring>lv_tabs</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>4</hsizetype>
+                            <vsizetype>7</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="resizeMode">
+                        <enum>LastColumn</enum>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout3</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="FancyLabel">
+                            <property name="name">
+                                <cstring>lb_pageTitle</cstring>
+                            </property>
+                        </widget>
+                        <widget class="QWidgetStack">
+                            <property name="name">
+                                <cstring>ws_tabs</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>7</hsizetype>
+                                    <vsizetype>3</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                            <widget class="QWidget">
+                                <property name="name">
+                                    <cstring>WStackPage</cstring>
+                                </property>
+                                <attribute name="id">
+                                    <number>0</number>
+                                </attribute>
+                            </widget>
+                        </widget>
+                    </vbox>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line1</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout1</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer1</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>40</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>iconButton1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;OK</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+O</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_apply</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Apply</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+A</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>iconButton3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Cancel</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+C</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>iconButton1</sender>
+        <signal>clicked()</signal>
+        <receiver>OptionsUI</receiver>
+        <slot>doOk()</slot>
+    </connection>
+    <connection>
+        <sender>pb_apply</sender>
+        <signal>clicked()</signal>
+        <receiver>OptionsUI</receiver>
+        <slot>doApply()</slot>
+    </connection>
+    <connection>
+        <sender>iconButton3</sender>
+        <signal>clicked()</signal>
+        <receiver>OptionsUI</receiver>
+        <slot>reject()</slot>
+    </connection>
+</connections>
+<slots>
+    <slot>doOk()</slot>
+    <slot>doApply()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>fancylabel.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/options/ui_positiontoolbar.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/options/ui_positiontoolbar.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/options/ui_positiontoolbar.ui	(revision 38)
@@ -0,0 +1,287 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PositionToolbarUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>PositionToolbarUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>334</width>
+            <height>278</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Position Toolbar</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="FancyLabel">
+            <property name="name">
+                <cstring>fancyLabel41</cstring>
+            </property>
+            <property name="text">
+                <string>Change Toolbar Position</string>
+            </property>
+            <property name="help">
+                <string>Here you can fine-tune the exact position of your toolbar</string>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout46</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>textLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Name:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit">
+                    <property name="name">
+                        <cstring>le_name</cstring>
+                    </property>
+                    <property name="readOnly">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line6</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout47</cstring>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>textLabel5</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Extra offset:</string>
+                    </property>
+                </widget>
+                <widget class="QSpinBox" row="1" column="1">
+                    <property name="name">
+                        <cstring>sb_index</cstring>
+                    </property>
+                    <property name="maxValue">
+                        <number>100</number>
+                    </property>
+                    <property name="minValue">
+                        <number>-100</number>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>textLabel2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Dock position:</string>
+                    </property>
+                </widget>
+                <widget class="QSpinBox" row="2" column="1">
+                    <property name="name">
+                        <cstring>sb_extraOffset</cstring>
+                    </property>
+                    <property name="maxValue">
+                        <number>32000</number>
+                    </property>
+                    <property name="minValue">
+                        <number>0</number>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>textLabel3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Index:</string>
+                    </property>
+                </widget>
+                <widget class="QComboBox" row="0" column="1">
+                    <item>
+                        <property name="text">
+                            <string>Top</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Bottom</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Right</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Left</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Minimized</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Unmanaged (advanced)</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>Torn off (advanced)</string>
+                        </property>
+                    </item>
+                    <property name="name">
+                        <cstring>cb_dock</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>7</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_nl</cstring>
+            </property>
+            <property name="text">
+                <string>Starts from new line</string>
+            </property>
+        </widget>
+        <spacer>
+            <property name="name">
+                <cstring>spacer56</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Vertical</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>20</width>
+                    <height>40</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line5</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout45</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer55</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>40</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_ok</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;OK</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+O</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_apply</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Apply</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+A</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_cancel</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Cancel</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+C</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>fancylabel.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/passphrasedlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/passphrasedlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/passphrasedlg.cpp	(revision 38)
@@ -0,0 +1,65 @@
+/*
+ * passphrasedlg.cpp - class to handle entering of OpenPGP passphrase
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"passphrasedlg.h"
+
+#include<qlineedit.h>
+#include<qradiobutton.h>
+#include<qpushbutton.h>
+#include"iconwidget.h"
+
+PassphraseDlg::PassphraseDlg(QWidget *parent, const char *name)
+:PassphraseUI(parent, name, true, WDestructiveClose)
+{
+	connect(pb_ok, SIGNAL(clicked()), SLOT(doOK()));
+	connect(pb_cancel, SIGNAL(clicked()), SLOT(reject()));
+	resize(minimumSize());
+}
+
+PassphraseDlg::~PassphraseDlg()
+{
+}
+
+QString PassphraseDlg::passphrase() const
+{
+	return le_pass->text();
+}
+
+void PassphraseDlg::reject()
+{
+	rejectPassphrase();
+	QDialog::reject();
+}
+
+void PassphraseDlg::doOK()
+{
+	le_pass->setEnabled(false);
+	pb_ok->setEnabled(false);
+
+	submitPassphrase(le_pass->text());
+}
+
+void PassphraseDlg::unblock()
+{
+	le_pass->setText("");
+	le_pass->setEnabled(true);
+	pb_ok->setEnabled(true);
+	le_pass->setFocus();
+}
Index: psi/tags/0.10-os2-b1/src/passphrasedlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/passphrasedlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/passphrasedlg.h	(revision 38)
@@ -0,0 +1,47 @@
+/*
+ * passphrasedlg.h - class to handle entering of OpenPGP passphrase
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PASSPHRASEDLG_H
+#define PASSPHRASEDLG_H
+
+#include"ui_passphrase.h"
+
+class PassphraseDlg : public PassphraseUI
+{
+	Q_OBJECT
+public:
+	PassphraseDlg(QWidget *parent=0, const char *name=0);
+	~PassphraseDlg();
+
+	QString passphrase() const;
+	void unblock();
+
+signals:
+	void submitPassphrase(const QString &);
+	void rejectPassphrase();
+
+public slots:
+	void reject();
+
+private slots:
+	void doOK();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/profiledlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/profiledlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/profiledlg.cpp	(revision 38)
@@ -0,0 +1,333 @@
+/*
+ * profiledlg.cpp - dialogs for manipulating profiles
+ * Copyright (C) 2001-2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"profiledlg.h"
+#include"common.h"
+
+#include<qlabel.h>
+#include<qcombobox.h>
+#include<qcheckbox.h>
+#include<qpushbutton.h>
+#include<qgroupbox.h>
+#include<qlistbox.h>
+#include<qinputdialog.h>
+#include<qfile.h>
+#include<qfileinfo.h>
+#include<qbuttongroup.h>
+#include<qwhatsthis.h>
+#include"profiles.h"
+#include"iconwidget.h"
+
+#include <qpainter.h>
+class StretchLogoLabel : public QLabel
+{
+public:
+	StretchLogoLabel(QPixmap pix, QWidget *label, const char *name = 0)
+	: QLabel((QWidget*)label->parent(), name)
+	{
+		setPixmap(pix);
+		setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
+		replaceWidget(label, this);
+	}
+
+	void drawContents(QPainter *p)
+	{
+		p->drawTiledPixmap(0, 0, width(), height(), *pixmap());
+	}
+};
+
+ProfileOpenDlg::ProfileOpenDlg(const QString &def, const VarList &_langs, const QString &curLang, QWidget *parent, const char *name)
+:ProfileOpenUI(parent, name, TRUE)
+{
+	setCaption(CAP(caption()));
+
+	langs = _langs;
+
+	// insert the logo
+	QPixmap logo = (QPixmap)IconsetFactory::icon("psi/psiLogo").pixmap();
+	lb_logo->setPixmap(logo);
+	lb_logo->setFixedSize(logo.width(), logo.height());
+	lb_logo->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
+	//setFixedWidth(logo->width());
+
+	QImage logoImg = logo.convertToImage();
+
+	QPixmap tmp;
+	tmp.convertFromImage( logoImg.copy(0, 0, 1, logoImg.height()) );
+	StretchLogoLabel *stretch = new StretchLogoLabel(tmp, lb_left);
+
+	tmp.convertFromImage( logoImg.copy(logoImg.width()-1, 0, 1, logoImg.height()) );
+	stretch = new StretchLogoLabel(tmp, lb_right);
+
+	// setup signals
+	connect(pb_open, SIGNAL(clicked()), SLOT(accept()));
+	connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
+	connect(pb_profiles, SIGNAL(clicked()), SLOT(manageProfiles()));
+	connect(cb_lang, SIGNAL(activated(int)), SLOT(langChange(int)));
+
+	int x = 0;
+	langSel = x;
+	for(VarList::ConstIterator it = langs.begin(); it != langs.end(); ++it) {
+		cb_lang->insertItem((*it).data());
+		if((curLang.isEmpty() && x == 0) || (curLang == (*it).key())) {
+			cb_lang->setCurrentItem(x);
+			langSel = x;
+		}
+		++x;
+	}
+
+	// QWhatsThis helpers
+	QWhatsThis::add(cb_profile,
+		tr("Select a profile to open from this list."));
+	QWhatsThis::add(cb_lang,
+		tr("Select a language you would like Psi to use from this "
+		"list.  You can download extra language packs from the Psi homepage."));
+	QWhatsThis::add(ck_auto,
+		tr("Automatically open this profile when Psi is started.  Useful if "
+		"you only have one profile."));
+
+	reload(def);
+}
+
+ProfileOpenDlg::~ProfileOpenDlg()
+{
+}
+
+void ProfileOpenDlg::reload(const QString &choose)
+{
+	QStringList list = getProfilesList();
+
+	cb_profile->clear();
+
+	if(list.count() == 0) {
+		gb_open->setEnabled(FALSE);
+		pb_open->setEnabled(FALSE);
+		pb_profiles->setFocus();
+	}
+	else {
+		int x = 0;
+		for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+			cb_profile->insertItem(*it);
+			if((choose.isEmpty() && x == 0) || (choose == *it)) {
+				cb_profile->setCurrentItem(x);
+			}
+			++x;
+		}
+
+		gb_open->setEnabled(TRUE);
+		pb_open->setEnabled(TRUE);
+		pb_open->setFocus();
+	}
+}
+
+void ProfileOpenDlg::manageProfiles()
+{
+	ProfileManageDlg *w = new ProfileManageDlg(cb_profile->currentText(), this);
+	w->exec();
+	QString last = w->lbx_profiles->text(w->lbx_profiles->currentItem());
+	delete w;
+
+	reload(last);
+}
+
+void ProfileOpenDlg::langChange(int x)
+{
+	if(x == langSel)
+		return;
+	langSel = x;
+
+	VarList::Iterator it = langs.findByNum(x);
+	newLang = (*it).key();
+	done(10);
+}
+
+
+ProfileManageDlg::ProfileManageDlg(const QString &choose, QWidget *parent, const char *name)
+:ProfileManageUI(parent, name, TRUE)
+{
+	setCaption(CAP(caption()));
+
+	// setup signals
+	connect(pb_new, SIGNAL(clicked()), SLOT(slotProfileNew()));
+	connect(pb_rename, SIGNAL(clicked()), SLOT(slotProfileRename()));
+	connect(pb_delete, SIGNAL(clicked()), SLOT(slotProfileDelete()));
+	connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
+	connect(lbx_profiles, SIGNAL(highlighted(int)), SLOT(updateSelection()));
+
+	// load the listing
+	QStringList list = getProfilesList();
+	int x = 0;
+	for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+		lbx_profiles->insertItem(*it);
+		if(*it == choose)
+			lbx_profiles->setCurrentItem(x);
+		++x;
+	}
+
+	updateSelection();
+}
+
+void ProfileManageDlg::slotProfileNew()
+{
+	QString name;
+
+	ProfileNewDlg *w = new ProfileNewDlg(this);
+	int r = w->exec();
+	if(r == QDialog::Accepted) {
+		name = w->name;
+
+		lbx_profiles->insertItem(name);
+		lbx_profiles->setCurrentItem(lbx_profiles->count()-1);
+	}
+	delete w;
+
+	if(r == QDialog::Accepted) {
+		close();
+	}
+}
+
+void ProfileManageDlg::slotProfileRename()
+{
+	int x = lbx_profiles->currentItem();
+	if(x == -1)
+		return;
+
+	QString oldname = lbx_profiles->text(x);
+	QString name;
+
+	while(1) {
+		bool ok = FALSE;
+		name = QInputDialog::getText(CAP(tr("Rename Profile")), tr("Please enter a new name for the profile.  Keep it simple.\nOnly use letters or numbers.  No punctuation or spaces."), QLineEdit::Normal, name, &ok, this);
+		if(!ok)
+			return;
+
+		if(profileExists(name)) {
+			QMessageBox::information(this, CAP(tr("Rename Profile")), tr("There is already another profile with this name.  Please choose another."));
+			continue;
+		}
+		else if(!profileRename(oldname, name)) {
+			QMessageBox::information(this, CAP(tr("Rename Profile")), tr("Unable to rename the profile.  Please do not use any special characters."));
+			continue;
+		}
+		break;
+	}
+
+	lbx_profiles->changeItem(name, x);
+}
+
+void ProfileManageDlg::slotProfileDelete()
+{
+	int x = lbx_profiles->currentItem();
+	if(x == -1)
+		return;
+	QString name = lbx_profiles->text(x);
+	QString path = g.pathProfiles + "/" + name;
+
+	// prompt first
+	int r = QMessageBox::warning(this,
+		CAP(tr("Delete Profile")),
+		tr(
+		"<qt>Are you sure you want to delete the \"<b>%1</b>\" profile?  "
+		"This will delete all of the profile's message history as well as associated settings!</qt>"
+		).arg(name),
+		tr("No, I changed my mind"),
+		tr("Delete it!"));
+
+	if(r != 1)
+		return;
+
+	r = QMessageBox::information(this,
+		CAP(tr("Delete Profile")),
+		tr(
+		"<qt>As a precaution, you are being asked one last time if this is what you really want.  "
+		"The following folder will be deleted!<br><br>\n"
+		"&nbsp;&nbsp;<b>%1</b><br><br>\n"
+		"Proceed?"
+		).arg(path),
+		tr("&No"),
+		tr("&Yes"));
+
+	if(r == 1) {
+		if(!profileDelete(path)) {
+			QMessageBox::critical(this, CAP("Error"), tr("Unable to delete the folder completely.  Ensure you have the proper permission."));
+			return;
+		}
+
+		lbx_profiles->removeItem(x);
+	}
+}
+
+void ProfileManageDlg::updateSelection()
+{
+	int x = lbx_profiles->currentItem();
+
+	if(x == -1) {
+		pb_rename->setEnabled(FALSE);
+		pb_delete->setEnabled(FALSE);
+	}
+	else {
+		pb_rename->setEnabled(TRUE);
+		pb_delete->setEnabled(TRUE);
+	}
+}
+
+
+ProfileNewDlg::ProfileNewDlg(QWidget *parent, const char *name)
+:ProfileNewUI(parent, name, TRUE)
+{
+	setCaption(CAP(caption()));
+
+	bg_defAct->setButton(bg_defAct->id((QButton *)rb_chat));
+	le_name->setFocus();
+
+	connect(pb_create, SIGNAL(clicked()), SLOT(slotCreate()));
+	connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
+	connect(le_name, SIGNAL(textChanged(const QString &)), SLOT(nameModified()));
+
+	nameModified();
+}
+
+void ProfileNewDlg::slotCreate()
+{
+	name = le_name->text();
+
+	if(profileExists(name)) {
+		QMessageBox::information(this, CAP(tr("New Profile")), tr("There is already an existing profile with this name.  Please choose another."));
+		return;
+	}
+
+	if(!profileNew(name)) {
+		QMessageBox::information(this, CAP(tr("New Profile")), tr("Unable to create the profile.  Please do not use any special characters."));
+		return;
+	}
+
+	// save config
+	UserProfile p;
+	p.prefs.defaultAction = bg_defAct->selected() == (QButton *)rb_message ? 0: 1;
+	p.prefs.useEmoticons = ck_useEmoticons->isChecked();
+	p.toFile(pathToProfileConfig(name));
+
+	accept();
+}
+
+void ProfileNewDlg::nameModified()
+{
+	pb_create->setEnabled(!le_name->text().isEmpty());
+}
Index: psi/tags/0.10-os2-b1/src/profiledlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/profiledlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/profiledlg.h	(revision 38)
@@ -0,0 +1,78 @@
+/*
+ * profiledlg.h - dialogs for manipulating profiles
+ * Copyright (C) 2001-2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PROFILEDLG_H
+#define PROFILEDLG_H
+
+#include<qdialog.h>
+#include<qstringlist.h>
+#include"ui_profileopen.h"
+#include"ui_profilemanage.h"
+#include"ui_profilenew.h"
+#include"varlist.h"
+
+
+class ProfileOpenDlg : public ProfileOpenUI
+{
+	Q_OBJECT
+public:
+	ProfileOpenDlg(const QString &, const VarList &, const QString &, QWidget *parent=0, const char *name=0);
+	~ProfileOpenDlg();
+
+	QString newLang;
+
+private slots:
+	void manageProfiles();
+	void langChange(int);
+
+private:
+	void reload(const QString &);
+
+	VarList langs;
+	int langSel;
+};
+
+class ProfileManageDlg : public ProfileManageUI
+{
+	Q_OBJECT
+public:
+	ProfileManageDlg(const QString &, QWidget *parent=0, const char *name=0);
+
+private slots:
+	void slotProfileNew();
+	void slotProfileRename();
+	void slotProfileDelete();
+	void updateSelection();
+};
+
+class ProfileNewDlg : public ProfileNewUI
+{
+	Q_OBJECT
+public:
+	ProfileNewDlg(QWidget *parent=0, const char *name=0);
+
+	QString name;
+
+private slots:
+	void slotCreate();
+	void nameModified();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/profiles.h
===================================================================
--- psi/tags/0.10-os2-b1/src/profiles.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/profiles.h	(revision 38)
@@ -0,0 +1,101 @@
+/*
+ * profiles.h - deal with profiles
+ * Copyright (C) 2001-2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PROFILES_H
+#define PROFILES_H
+
+#include<qstring.h>
+#include<qstringlist.h>
+#include<qvaluelist.h>
+#include"im.h"
+#include"proxy.h"
+#include"common.h"
+
+class UserAccount
+{
+public:
+	UserAccount();
+	~UserAccount();
+
+	void reset();
+
+	QDomElement toXml(QDomDocument &, const QString &tagName);
+	void fromXml(const QDomElement &);
+
+	QString name;
+	QString jid, pass, host, resource;
+	int port, priority;
+ 	bool opt_enabled, opt_ssl, opt_pass, opt_host, opt_auto, opt_keepAlive, opt_plain, opt_log, opt_reconn, opt_ignoreSSLWarnings;
+	bool tog_offline, tog_away, tog_agents, tog_hidden, tog_self;
+
+	int proxy_index;
+	int proxy_type, proxy_port;
+	QString proxy_host, proxy_user, proxy_pass;
+
+	QString olr_string;
+	XMPP::Roster roster;
+
+	struct GroupData {
+		bool open;
+		int  rank;
+	};
+	QMap<QString, GroupData> groupState;
+
+	QString pgpSecretKeyID;
+
+	VarList keybind;
+
+	XMPP::Jid dtProxy;
+};
+
+typedef QValueList<UserAccount> UserAccountList;
+
+
+class UserProfile
+{
+public:
+	UserProfile();
+
+	void reset();
+	bool toFile(const QString &);
+	bool fromFile(const QString &);
+
+	QString progver;
+	UserAccountList acc;
+	Options prefs;
+
+	QRect mwgeom;
+	QStringList recentGCList;
+	QStringList recentBrowseList;
+	QString lastStatusString;
+	bool useSound;
+
+	ProxyItemList proxyList;
+};
+
+QString pathToProfile(const QString &);
+QString pathToProfileConfig(const QString &);
+QStringList getProfilesList();
+bool profileExists(const QString &);
+bool profileNew(const QString &);
+bool profileRename(const QString &, const QString &);
+bool profileDelete(const QString &);
+
+#endif
Index: psi/tags/0.10-os2-b1/src/proxy.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/proxy.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/proxy.cpp	(revision 38)
@@ -0,0 +1,691 @@
+/*
+ * proxy.cpp - classes for handling proxy profiles
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"proxy.h"
+
+#include<qlabel.h>
+#include<qlineedit.h>
+#include<qcheckbox.h>
+#include<qvgroupbox.h>
+#include<qlayout.h>
+#include<qgrid.h>
+#include<qhbox.h>
+#include<qvbox.h>
+#include<qcombobox.h>
+#include<qpushbutton.h>
+#include<qlistbox.h>
+#include<qdom.h>
+#include<qguardedptr.h>
+#include<qwhatsthis.h>
+#include<qapplication.h>
+#include"common.h"
+#include"iconwidget.h"
+
+static QDomElement textTag(QDomDocument &doc, const QString &name, const QString &content)
+{
+	QDomElement tag = doc.createElement(name);
+	QDomText text = doc.createTextNode(content);
+	tag.appendChild(text);
+
+	return tag;
+}
+
+static QDomElement textTag(QDomDocument &doc, const QString &name, bool content)
+{
+	QDomElement tag = doc.createElement(name);
+	QDomText text = doc.createTextNode(content ? "true" : "false");
+	tag.appendChild(text);
+
+	return tag;
+}
+
+static QDomElement findSubTag(const QDomElement &e, const QString &name)
+{
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+		if(i.tagName() == name)
+			return i;
+	}
+
+	return QDomElement();
+}
+
+//----------------------------------------------------------------------------
+// HostPortEdit
+//----------------------------------------------------------------------------
+class HostPortEdit::Private
+{
+public:
+	Private() {}
+
+	QLineEdit *le_host, *le_port;
+};
+
+HostPortEdit::HostPortEdit(QWidget *parent, const char *name)
+:QWidget(parent, name)
+{
+	d = new Private;
+
+	QLabel *l;
+	QHBoxLayout *hb = new QHBoxLayout(this, 0, 4);
+	l = new QLabel(tr("Host:"), this);
+	hb->addWidget(l);
+	d->le_host = new QLineEdit(this);
+	d->le_host->setMinimumWidth(128);
+	hb->addWidget(d->le_host);
+	l = new QLabel(tr("Port:"), this);
+	hb->addWidget(l);
+	d->le_port = new QLineEdit(this);
+	d->le_port->setFixedWidth(64);
+	hb->addWidget(d->le_port);
+	setPort(0);
+}
+
+HostPortEdit::~HostPortEdit()
+{
+	delete d;
+}
+
+QString HostPortEdit::host() const
+{
+	return d->le_host->text();
+}
+
+int HostPortEdit::port() const
+{
+	return d->le_port->text().toInt();
+}
+
+void HostPortEdit::setHost(const QString &s)
+{
+	d->le_host->setText(s);
+}
+
+void HostPortEdit::setPort(int x)
+{
+	d->le_port->setText(QString::number(x));
+}
+
+void HostPortEdit::fixTabbing(QWidget *a, QWidget *b)
+{
+	setTabOrder(a, d->le_host);
+	setTabOrder(d->le_host, d->le_port);
+	setTabOrder(d->le_port, b);
+}
+
+
+//----------------------------------------------------------------------------
+// ProxySettings
+//----------------------------------------------------------------------------
+ProxySettings::ProxySettings()
+{
+	port = 0;
+	useAuth = false;
+}
+
+QDomElement ProxySettings::toXml(QDomDocument *doc) const
+{
+	QDomElement e = doc->createElement("proxySettings");
+	e.appendChild(textTag(*doc, "host", host));
+	e.appendChild(textTag(*doc, "port", QString::number(port)));
+	e.appendChild(textTag(*doc, "url", url));
+	e.appendChild(textTag(*doc, "useAuth", useAuth));
+	e.appendChild(textTag(*doc, "user", user));
+	e.appendChild(textTag(*doc, "pass", pass));
+	return e;
+}
+
+bool ProxySettings::fromXml(const QDomElement &e)
+{
+	host = findSubTag(e, "host").text();
+	port = findSubTag(e, "port").text().toInt();
+	url = findSubTag(e, "url").text();
+	useAuth = (findSubTag(e, "useAuth").text() == "true") ? true: false;
+	user = findSubTag(e, "user").text();
+	pass = findSubTag(e, "pass").text();
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// ProxyEdit
+//----------------------------------------------------------------------------
+class ProxyEdit::Private
+{
+public:
+	Private() {}
+
+	HostPortEdit *hp_host;
+	QCheckBox *ck_auth;
+	QLabel *lb_url;
+	QLineEdit *le_url, *le_user, *le_pass;
+	QGrid *gr_auth;
+};
+
+ProxyEdit::ProxyEdit(QWidget *parent, const char *name)
+:QGroupBox(1, Horizontal, tr("Settings"), parent, name)
+{
+	d = new Private;
+
+	//QVBoxLayout *vb = new QVBoxLayout(this);
+	//QVBox *gb = new QVBox(this);
+	//gb->setSpacing(4);
+	//vb->addWidget(gb);
+
+	QLabel *l;
+
+	d->hp_host = new HostPortEdit(this);
+
+	QHBox *hb = new QHBox(this);
+	d->lb_url = new QLabel(tr("Polling URL:"), hb);
+	d->le_url = new QLineEdit(hb);
+
+	d->ck_auth = new QCheckBox(tr("Use authentication"), this);
+	connect(d->ck_auth, SIGNAL(toggled(bool)), SLOT(ck_toggled(bool)));
+
+	d->gr_auth = new QGrid(2, Horizontal, this);
+	d->gr_auth->setSpacing(4);
+	l = new QLabel(tr("Username:"), d->gr_auth);
+	d->le_user = new QLineEdit(d->gr_auth);
+	l = new QLabel(tr("Password:"), d->gr_auth);
+	d->le_pass = new QLineEdit(d->gr_auth);
+	d->le_pass->setEchoMode(QLineEdit::Password);
+	d->gr_auth->setEnabled(false);
+
+	QWhatsThis::add(d->hp_host,
+		tr("Enter the hostname and port of your proxy server.") + "  " +
+		tr("Consult your network administrator if necessary."));
+	QWhatsThis::add(d->le_user,
+		tr("Enter your proxy server login (username) "
+		"or leave this field blank if the proxy server does not require it.") + "  " +
+		tr("Consult your network administrator if necessary."));
+	QWhatsThis::add(d->le_pass,
+		tr("Enter your proxy server password "
+		"or leave this field blank if the proxy server does not require it.") + "  " +
+		tr("Consult your network administrator if necessary."));
+}
+
+ProxyEdit::~ProxyEdit()
+{
+	delete d;
+}
+
+void ProxyEdit::reset()
+{
+	d->hp_host->setHost("");
+	d->hp_host->setPort(0);
+	d->le_url->setText("");
+	d->ck_auth->setChecked(false);
+	d->le_user->setText("");
+	d->le_pass->setText("");
+}
+
+void ProxyEdit::setType(const QString &s)
+{
+	if(s == "poll") {
+		d->lb_url->setEnabled(true);
+		d->le_url->setEnabled(true);
+	}
+	else {
+		d->lb_url->setEnabled(false);
+		d->le_url->setEnabled(false);
+	}
+}
+
+ProxySettings ProxyEdit::proxySettings() const
+{
+	ProxySettings s;
+	s.host = d->hp_host->host();
+	s.port = d->hp_host->port();
+	if(d->le_url->isEnabled())
+		s.url = d->le_url->text();
+	s.useAuth = d->ck_auth->isChecked();
+	s.user = d->le_user->text();
+	s.pass = d->le_pass->text();
+	return s;
+}
+
+void ProxyEdit::setProxySettings(const ProxySettings &s)
+{
+	d->hp_host->setHost(s.host);
+	d->hp_host->setPort(s.port);
+	d->le_url->setText(s.url);
+	d->ck_auth->setChecked(s.useAuth);
+	d->le_user->setText(s.user);
+	d->le_pass->setText(s.pass);
+}
+
+void ProxyEdit::ck_toggled(bool b)
+{
+	d->gr_auth->setEnabled(b);
+}
+
+void ProxyEdit::fixTabbing(QWidget *a, QWidget *b)
+{
+	d->hp_host->fixTabbing(a, d->le_url);
+	setTabOrder(d->le_url, d->ck_auth);
+	setTabOrder(d->ck_auth, d->le_user);
+	setTabOrder(d->le_user, d->le_pass);
+	setTabOrder(d->le_pass, b);
+}
+
+
+//----------------------------------------------------------------------------
+// ProxyDlg
+//----------------------------------------------------------------------------
+class ProxyDlg::Private
+{
+public:
+	Private() {}
+
+	ProxyEdit *pe_settings;
+	ProxyItemList list;
+	int last;
+};
+
+ProxyDlg::ProxyDlg(const ProxyItemList &list, const QStringList &methods, int def, QWidget *parent, const char *name)
+:ProxyUI(parent, name, QApplication::activeModalWidget() ? true: false, WDestructiveClose)
+{
+	d = new Private;
+	setCaption(CAP(caption()));
+
+	d->list = list;
+	d->last = -1;
+	d->pe_settings = new ProxyEdit(gb_prop);
+	replaceWidget(lb_proxyedit, d->pe_settings);
+	d->pe_settings->fixTabbing(cb_type, pb_close);
+
+	hookEdit();
+
+	connect(pb_new, SIGNAL(clicked()), SLOT(proxy_new()));
+	connect(pb_remove, SIGNAL(clicked()), SLOT(proxy_remove()));
+	connect(pb_save, SIGNAL(clicked()), SLOT(doSave()));
+	connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
+	gb_prop->setEnabled(false);
+	pb_remove->setEnabled(false);
+	pb_save->setDefault(true);
+
+	cb_type->insertStringList(methods);
+	connect(cb_type, SIGNAL(activated(int)), SLOT(cb_activated(int)));
+
+	for(ProxyItemList::ConstIterator it = d->list.begin(); it != d->list.end(); ++it)
+		lbx_proxy->insertItem((*it).name);
+	if(!list.isEmpty()) {
+		if(def < 0)
+			def = 0;
+		lbx_proxy->setCurrentItem(def);
+		selectCurrent();
+	}
+
+	QWhatsThis::add(cb_type,
+		tr("If you require a proxy server to connect, select the type of proxy here.") + "  " +
+		tr("Consult your network administrator if necessary."));
+	// TODO: whatsthis for name
+}
+
+ProxyDlg::~ProxyDlg()
+{
+	delete d;
+}
+
+void ProxyDlg::proxy_new()
+{
+	ProxyItem s;
+	s.id = -1; // id of -1 means 'new'
+	s.name = getUniqueName();
+	d->list += s;
+
+	lbx_proxy->insertItem(s.name);
+	lbx_proxy->setCurrentItem(lbx_proxy->count()-1);
+	selectCurrent();
+}
+
+void ProxyDlg::proxy_remove()
+{
+	int x = lbx_proxy->currentItem();
+	if(x != -1) {
+		ProxyItemList::Iterator it = d->list.begin();
+		for(int n = 0; n < x; ++n)
+			++it;
+		d->list.remove(it);
+
+		d->last = -1;
+		lbx_proxy->removeItem(x);
+		selectCurrent();
+	}
+}
+
+void ProxyDlg::cb_activated(int x)
+{
+	if(x == 0)
+		d->pe_settings->setType("http");
+	else if(x == 1)
+		d->pe_settings->setType("socks");
+	else if(x == 2)
+		d->pe_settings->setType("poll");
+}
+
+void ProxyDlg::selectCurrent()
+{
+	int x = lbx_proxy->currentItem();
+	if(x != -1)
+		lbx_proxy->setSelected(x, true);
+}
+
+QString ProxyDlg::getUniqueName() const
+{
+	QString str;
+	int x = 0;
+	bool found;
+	do {
+		str = QString("Proxy %1").arg(++x);
+		found = false;
+		for(ProxyItemList::ConstIterator it = d->list.begin(); it != d->list.end(); ++it) {
+			if(str == (*it).name) {
+				found = true;
+				break;
+			}
+		}
+	} while(found);
+	return str;
+}
+
+void ProxyDlg::saveIntoItem(int x)
+{
+	ProxyItem &s = d->list[x];
+	s.name = le_name->text();
+	int i = cb_type->currentItem();
+	s.type = "http";
+	if(i == 0)
+		s.type = "http";
+	else if(i == 1)
+		s.type = "socks";
+	else if(i == 2)
+		s.type = "poll";
+	s.settings = d->pe_settings->proxySettings();
+}
+
+void ProxyDlg::qlbx_highlighted(int x)
+{
+	// if we are moving off of another item, save its content
+	if(d->last != -1)
+		saveIntoItem(d->last);
+	d->last = x;
+
+	// display the new item's content
+	if(x == -1) {
+		le_name->setText("");
+		cb_type->setCurrentItem(0);
+		d->pe_settings->reset();
+		gb_prop->setEnabled(false);
+		pb_remove->setEnabled(false);
+	}
+	else {
+		ProxyItem &s = d->list[x];
+		le_name->setText(s.name);
+		int i = 0;
+		if(s.type == "http")
+			i = 0;
+		else if(s.type == "socks")
+			i = 1;
+		else if(s.type == "poll")
+			i = 2;
+		cb_type->setCurrentItem(i);
+		d->pe_settings->setProxySettings(s.settings);
+		cb_activated(i);
+		gb_prop->setEnabled(true);
+		pb_remove->setEnabled(true);
+	}
+}
+
+void ProxyDlg::qle_textChanged(const QString &s)
+{
+	int x = lbx_proxy->currentItem();
+	if(x != -1) {
+		unhookEdit();
+		lbx_proxy->changeItem(s, x);
+		hookEdit();
+	}
+}
+
+// hookEdit / unhookEdit - disconnect some signals to prevent recursion
+void ProxyDlg::hookEdit()
+{
+	connect(lbx_proxy, SIGNAL(highlighted(int)), this, SLOT(qlbx_highlighted(int)));
+	connect(le_name, SIGNAL(textChanged(const QString &)), this, SLOT(qle_textChanged(const QString &)));
+}
+
+void ProxyDlg::unhookEdit()
+{
+	disconnect(lbx_proxy, SIGNAL(highlighted(int)), this, SLOT(qlbx_highlighted(int)));
+	disconnect(le_name, SIGNAL(textChanged(const QString &)), this, SLOT(qle_textChanged(const QString &)));
+}
+
+void ProxyDlg::doSave()
+{
+	int x = lbx_proxy->currentItem();
+	if(x != -1)
+		saveIntoItem(x);
+	applyList(d->list, x);
+	accept();
+}
+
+
+//----------------------------------------------------------------------------
+// ProxyChooser
+//----------------------------------------------------------------------------
+class ProxyChooser::Private
+{
+public:
+	Private() {}
+
+	QComboBox *cb_proxy;
+	QPushButton *pb_edit;
+	ProxyManager *m;
+};
+
+ProxyChooser::ProxyChooser(ProxyManager *m, QWidget *parent, const char *name)
+:QWidget(parent, name)
+{
+	d = new Private;
+	d->m = m;
+	connect(m, SIGNAL(settingsChanged()), SLOT(pm_settingsChanged()));
+	QHBoxLayout *hb = new QHBoxLayout(this, 0, 4);
+	d->cb_proxy = new QComboBox(this);
+	QSizePolicy sp = d->cb_proxy->sizePolicy();
+	d->cb_proxy->setSizePolicy( QSizePolicy(QSizePolicy::Expanding, sp.verData()) );
+	hb->addWidget(d->cb_proxy);
+	d->pb_edit = new QPushButton(tr("Edit..."), this);
+	connect(d->pb_edit, SIGNAL(clicked()), SLOT(doOpen()));
+	hb->addWidget(d->pb_edit);
+
+	buildComboBox();
+}
+
+ProxyChooser::~ProxyChooser()
+{
+	delete d;
+}
+
+int ProxyChooser::currentItem() const
+{
+	return d->cb_proxy->currentItem();
+}
+
+void ProxyChooser::setCurrentItem(int x)
+{
+	d->cb_proxy->setCurrentItem(x);
+}
+
+void ProxyChooser::pm_settingsChanged()
+{
+	int x = d->cb_proxy->currentItem();
+	buildComboBox();
+	if(x >= 1) {
+		x = d->m->findOldIndex(x-1);
+		if(x == -1)
+			d->cb_proxy->setCurrentItem(0);
+		else
+			d->cb_proxy->setCurrentItem(x+1);
+	}
+	else {
+		x = d->m->lastEdited();
+		if(x != -1)
+			d->cb_proxy->setCurrentItem(x+1);
+	}
+}
+
+void ProxyChooser::buildComboBox()
+{
+	d->cb_proxy->clear();
+	d->cb_proxy->insertItem(tr("None"));
+	ProxyItemList list = d->m->itemList();
+	for(ProxyItemList::ConstIterator it = list.begin(); it != list.end(); ++it)
+		d->cb_proxy->insertItem((*it).name);
+}
+
+void ProxyChooser::doOpen()
+{
+	int x = d->cb_proxy->currentItem();
+	if(x < 1)
+		x = -1;
+	else
+		--x;
+	d->m->openDialog(x);
+}
+
+void ProxyChooser::fixTabbing(QWidget *a, QWidget *b)
+{
+	setTabOrder(a, d->cb_proxy);
+	setTabOrder(d->cb_proxy, d->pb_edit);
+	setTabOrder(d->pb_edit, b);
+}
+
+
+//----------------------------------------------------------------------------
+// ProxyManager
+//----------------------------------------------------------------------------
+class ProxyManager::Private
+{
+public:
+	Private() {}
+
+	ProxyItemList list;
+	QGuardedPtr<ProxyDlg> pd;
+	QValueList<int> prevMap;
+	int lastEdited;
+};
+
+ProxyManager::ProxyManager(QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+}
+
+ProxyManager::~ProxyManager()
+{
+	delete d;
+}
+
+ProxyChooser *ProxyManager::createProxyChooser(QWidget *parent)
+{
+	return new ProxyChooser(this, parent);
+}
+
+ProxyItemList ProxyManager::itemList() const
+{
+	return d->list;
+}
+
+const ProxyItem & ProxyManager::getItem(int x) const
+{
+	return d->list[x];
+}
+
+int ProxyManager::lastEdited() const
+{
+	return d->lastEdited;
+}
+
+void ProxyManager::setItemList(const ProxyItemList &list)
+{
+	d->list = list;
+	assignIds();
+}
+
+QStringList ProxyManager::methodList() const
+{
+	QStringList list;
+	list += "HTTP \"Connect\"";
+	list += "SOCKS Version 5";
+	list += "HTTP Polling";
+	return list;
+}
+
+void ProxyManager::openDialog(int def)
+{
+	if(d->pd)
+		bringToFront(d->pd);
+	else {
+		d->pd = new ProxyDlg(d->list, methodList(), def, 0);
+		connect(d->pd, SIGNAL(applyList(const ProxyItemList &, int)), SLOT(pd_applyList(const ProxyItemList &, int)));
+		d->pd->show();
+	}
+}
+
+void ProxyManager::pd_applyList(const ProxyItemList &list, int x)
+{
+	d->list = list;
+	d->lastEdited = x;
+
+	// grab old id list
+	d->prevMap.clear();
+	for(ProxyItemList::ConstIterator it = d->list.begin(); it != d->list.end(); ++it)
+		d->prevMap += (*it).id;
+	assignIds(); // re-assign proper ids
+
+	settingsChanged();
+}
+
+void ProxyManager::assignIds()
+{
+	int n = 0;
+	for(ProxyItemList::Iterator it = d->list.begin(); it != d->list.end(); ++it)
+		(*it).id = n++;
+}
+
+int ProxyManager::findOldIndex(int x) const
+{
+	int newPos = 0;
+	bool found = false;
+	for(QValueList<int>::ConstIterator it = d->prevMap.begin(); it != d->prevMap.end(); ++it) {
+		if(*it == x) {
+			found = true;
+			break;
+		}
+		++newPos;
+	}
+	if(found)
+		return newPos;
+	else
+		return -1;
+}
Index: psi/tags/0.10-os2-b1/src/proxy.h
===================================================================
--- psi/tags/0.10-os2-b1/src/proxy.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/proxy.h	(revision 38)
@@ -0,0 +1,179 @@
+/*
+ * proxy.h - classes for handling proxy profiles
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PROXYDLG_H
+#define PROXYDLG_H
+
+#include<qvaluelist.h>
+#include<qgroupbox.h>
+#include<qdom.h>
+#include"ui_proxy.h"
+
+class HostPortEdit : public QWidget
+{
+	Q_OBJECT
+public:
+	HostPortEdit(QWidget *parent=0, const char *name=0);
+	~HostPortEdit();
+
+	QString host() const;
+	int port() const;
+	void setHost(const QString &);
+	void setPort(int);
+	void fixTabbing(QWidget *a, QWidget *b);
+
+private:
+	class Private;
+	Private *d;
+};
+
+class ProxyItem;
+class ProxyManager;
+typedef QValueList<ProxyItem> ProxyItemList;
+
+class ProxySettings
+{
+public:
+	ProxySettings();
+
+	QString host, user, pass;
+	int port;
+	bool useAuth;
+	QString url;
+
+	QDomElement toXml(QDomDocument *) const;
+	bool fromXml(const QDomElement &);
+};
+
+class ProxyEdit : public QGroupBox
+{
+	Q_OBJECT
+public:
+	ProxyEdit(QWidget *parent=0, const char *name=0);
+	~ProxyEdit();
+
+	void reset();
+	void setType(const QString &s);
+	ProxySettings proxySettings() const;
+	void setProxySettings(const ProxySettings &);
+	void fixTabbing(QWidget *a, QWidget *b);
+
+private slots:
+	void ck_toggled(bool);
+
+private:
+	class Private;
+	Private *d;
+};
+
+class ProxyDlg : public ProxyUI
+{
+	Q_OBJECT
+public:
+	ProxyDlg(const ProxyItemList &, const QStringList &, int def, QWidget *parent=0, const char *name=0);
+	~ProxyDlg();
+
+signals:
+	void applyList(const ProxyItemList &, int cur);
+
+private slots:
+	void proxy_new();
+	void proxy_remove();
+	void cb_activated(int);
+	void qlbx_highlighted(int);
+	void qle_textChanged(const QString &);
+	void doSave();
+
+private:
+	class Private;
+	Private *d;
+
+	void selectCurrent();
+	QString getUniqueName() const;
+	void hookEdit();
+	void unhookEdit();
+	void saveIntoItem(int);
+};
+
+class ProxyChooser : public QWidget
+{
+	Q_OBJECT
+public:
+	ProxyChooser(ProxyManager *, QWidget *parent=0, const char *name=0);
+	~ProxyChooser();
+
+	int currentItem() const;
+	void setCurrentItem(int);
+	void fixTabbing(QWidget *a, QWidget *b);
+
+private slots:
+	void pm_settingsChanged();
+	void doOpen();
+
+private:
+	class Private;
+	Private *d;
+
+	void buildComboBox();
+};
+
+class ProxyItem
+{
+public:
+	ProxyItem() {}
+
+	int id; // used to keep track of 'old' position in a list
+	QString name;
+	QString type;
+	ProxySettings settings;
+};
+
+class ProxyManager : public QObject
+{
+	Q_OBJECT
+public:
+	ProxyManager(QObject *parent=0);
+	~ProxyManager();
+
+	ProxyChooser *createProxyChooser(QWidget *parent=0);
+	ProxyItemList itemList() const;
+	const ProxyItem & getItem(int) const;
+	int lastEdited() const;
+	void setItemList(const ProxyItemList &);
+	QStringList methodList() const;
+	int findOldIndex(int) const;
+
+signals:
+	void settingsChanged();
+
+public slots:
+	void openDialog(int);
+
+private slots:
+	void pd_applyList(const ProxyItemList &, int cur);
+
+private:
+	class Private;
+	Private *d;
+
+	void assignIds();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/psi_profiles.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psi_profiles.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psi_profiles.cpp	(revision 38)
@@ -0,0 +1,1914 @@
+/*
+ * profiles.cpp - deal with profiles
+ * Copyright (C) 2001-2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"profiles.h"
+#include"common.h"
+#include<qdir.h>
+#include<qfileinfo.h>
+#include<qdom.h>
+
+#include<qapplication.h>
+#include"eventdlg.h"
+#include"chatdlg.h"
+#include"contactview.h"
+#include"xmpp_xmlcommon.h"
+#include"fancylabel.h"
+#include"advwidget.h"
+
+using namespace XMPP;
+using namespace XMLHelper;
+
+UserAccount::UserAccount()
+{
+	reset();
+}
+
+void UserAccount::reset()
+{
+	name = "Default";
+	opt_enabled = TRUE;
+	opt_auto = FALSE;
+	opt_ssl = FALSE;
+	tog_offline = TRUE;
+	tog_away = TRUE;
+	tog_hidden = TRUE;
+	tog_agents = TRUE;
+	tog_self = FALSE;
+	jid = "";
+	pass = "";
+	opt_pass = FALSE;
+	port = 5222;
+	opt_host = FALSE;
+	host = "";
+	resource = "Psi";
+	priority = 5;
+	olr_string = "";
+	opt_keepAlive = TRUE;
+	opt_plain = FALSE;
+	opt_log = TRUE;
+	opt_reconn = FALSE;
+	opt_ignoreSSLWarnings = false;
+	pgpSecretKeyID = "";
+
+	proxy_index = 0;
+	proxy_type = PROXY_NONE;
+	proxy_host = "";
+	proxy_port = 8080;
+	proxy_user = "";
+	proxy_pass = "";
+
+	keybind.clear();
+
+	roster.clear();
+}
+
+UserAccount::~UserAccount()
+{
+}
+
+// FIXME: this should be a const function (as should other ::toXml functions)
+QDomElement UserAccount::toXml(QDomDocument &doc, const QString &tagName)
+{
+	QDomElement a = doc.createElement(tagName);
+
+	setBoolAttribute(a, "enabled", opt_enabled);
+	setBoolAttribute(a, "auto", opt_auto);
+	setBoolAttribute(a, "ssl", opt_ssl);
+	setBoolAttribute(a, "showOffline", tog_offline);
+	setBoolAttribute(a, "showAway", tog_away);
+	setBoolAttribute(a, "showHidden", tog_hidden);
+	setBoolAttribute(a, "showAgents", tog_agents);
+	setBoolAttribute(a, "showSelf", tog_self);
+	setBoolAttribute(a, "keepAlive", opt_keepAlive);
+	setBoolAttribute(a, "plain", opt_plain);
+	setBoolAttribute(a, "log", opt_log);
+	setBoolAttribute(a, "reconn", opt_reconn);
+	setBoolAttribute(a, "ignoreSSLWarnings", opt_ignoreSSLWarnings);
+	//setBoolAttribute(a, "gpg", opt_gpg);
+
+	//QString jid = user + '@' + vhost;
+	a.appendChild(textTag(doc, "name", name));
+	a.appendChild(textTag(doc, "jid", jid));
+	if(opt_pass)
+		a.appendChild(textTag(doc, "password", encodePassword(pass, jid) ));
+	a.appendChild(textTag(doc, "useHost", opt_host));
+	a.appendChild(textTag(doc, "host", host));
+	a.appendChild(textTag(doc, "port", QString::number(port)));
+	a.appendChild(textTag(doc, "resource", resource));
+	a.appendChild(textTag(doc, "priority", QString::number(priority)));
+	a.appendChild(textTag(doc, "OLR", olr_string));
+	a.appendChild(textTag(doc, "pgpSecretKeyID", pgpSecretKeyID));
+
+	QDomElement r = doc.createElement("roster");
+	a.appendChild(r);
+	Roster::ConstIterator rit = roster.begin();
+	for ( ; rit != roster.end(); ++rit) {
+		const RosterItem &i = *rit;
+		QDomElement tag = i.toXml(&doc);
+		r.appendChild(tag);
+	}
+
+	// now we check for redundant entries
+	QStringList groupList, removeList;
+	groupList << "/\\/" + name + "\\/\\"; // account name is a very 'special' group
+
+	// special groups that should also have their state remembered
+	groupList << ContactProfile::tr("General");
+	groupList << ContactProfile::tr("Agents/Transports");
+
+	// first, add all groups' names to groupList
+	rit = roster.begin();
+	for ( ; rit != roster.end(); ++rit) {
+		const RosterItem &i = *rit;
+		groupList += i.groups();
+	}
+
+	// now, check if there's groupState name entry in groupList
+	QMap<QString, GroupData>::Iterator git = groupState.begin();
+	for ( ; git != groupState.end(); ++git) {
+		bool found = false;
+
+		QStringList::Iterator it = groupList.begin();
+		for ( ; it != groupList.end(); ++it)
+			if ( git.key() == *it ) {
+				found = true;
+				break;
+			}
+
+		if ( !found )
+			removeList << git.key();
+	}
+
+	// remove redundant groups
+	QStringList::Iterator it = removeList.begin();
+	for ( ; it != removeList.end(); ++it)
+		groupState.remove( *it );
+
+	// and finally, save the data
+	QDomElement gs = doc.createElement("groupState");
+	a.appendChild(gs);
+	git = groupState.begin();
+	for ( ; git != groupState.end(); ++git) {
+		//if ( !git.data().open || git.data().rank ) { // don't save unnecessary entries (the default is 'true' to open, and '0' to rank)
+			QDomElement group = doc.createElement("group");
+			group.setAttribute("name", git.key());
+			group.setAttribute("open", git.data().open ? "true" : "false");
+			group.setAttribute("rank", QString::number(git.data().rank));
+			gs.appendChild(group);
+		//}
+	}
+
+	a.appendChild(textTag(doc, "proxyindex", QString::number(proxy_index)));
+	//a.appendChild(textTag(doc, "proxytype", QString::number(proxy_type)));
+	//a.appendChild(textTag(doc, "proxyhost", proxy_host));
+	//a.appendChild(textTag(doc, "proxyport", QString::number(proxy_port)));
+	//a.appendChild(textTag(doc, "proxyuser", proxy_user));
+	//a.appendChild(textTag(doc, "proxypass", encodePassword(proxy_pass, jid)));
+
+	a.appendChild(keybind.toXml(doc, "pgpkeybindings"));
+	a.appendChild(textTag(doc, "dtProxy", dtProxy.full()));
+
+	return a;
+}
+
+void UserAccount::fromXml(const QDomElement &a)
+{
+	reset();
+
+	bool found;
+
+	readEntry(a, "name", &name);
+	readBoolAttribute(a, "enabled", &opt_enabled);
+	readBoolAttribute(a, "auto", &opt_auto);
+	readBoolAttribute(a, "ssl", &opt_ssl);
+	readBoolAttribute(a, "showOffline", &tog_offline);
+	readBoolAttribute(a, "showAway", &tog_away);
+	readBoolAttribute(a, "showHidden", &tog_hidden);
+	readBoolAttribute(a, "showAgents", &tog_agents);
+	readBoolAttribute(a, "showSelf", &tog_self);
+	readBoolAttribute(a, "keepAlive", &opt_keepAlive);
+	readBoolAttribute(a, "plain", &opt_plain);
+	readBoolAttribute(a, "log", &opt_log);
+	readBoolAttribute(a, "reconn", &opt_reconn);
+	readBoolAttribute(a, "ignoreSSLWarnings", &opt_ignoreSSLWarnings);
+	//readBoolAttribute(a, "gpg", &opt_gpg);
+
+	readEntry(a, "host", &host);
+	readNumEntry(a, "port", &port);
+
+	// 0.8.6 and >= 0.9
+	QDomElement j = findSubTag(a, "jid", &found);
+	if(found) {
+		readBoolAttribute(j, "manual", &opt_host);
+		jid = tagContent(j);
+	}
+	// 0.8.7
+	else {
+		QString user, vhost;
+		readEntry(a, "username", &user);
+		QDomElement j = findSubTag(a, "vhost", &found);
+		if(found) {
+			readBoolAttribute(j, "manual", &opt_host);
+			vhost = tagContent(j);
+		}
+		else {
+			opt_host = false;
+			vhost = host;
+			host = "";
+			port = 0;
+		}
+		jid = user + '@' + vhost;
+	}
+
+	readBoolEntry(a, "useHost", &opt_host);
+
+	// read password (we must do this after reading the jid, to decode properly)
+	readEntry(a, "password", &pass);
+	if(!pass.isEmpty()) {
+		opt_pass = TRUE;
+		pass = decodePassword(pass, jid);
+	}
+
+	readEntry(a, "resource", &resource);
+	readNumEntry(a, "priority", &priority);
+	readEntry(a, "OLR", &olr_string);
+	readEntry(a, "pgpSecretKeyID", &pgpSecretKeyID);
+
+	QDomElement r = findSubTag(a, "roster", &found);
+	if(found) {
+		for(QDomNode n = r.firstChild(); !n.isNull(); n = n.nextSibling()) {
+			QDomElement i = n.toElement();
+			if(i.isNull())
+				continue;
+
+			if(i.tagName() == "item") {
+				RosterItem ri;
+				if(!ri.fromXml(i))
+					continue;
+				roster += ri;
+			}
+		}
+	}
+
+	groupState.clear();
+	QDomElement gs = findSubTag(a, "groupState", &found);
+	if (found) {
+		for (QDomNode n = gs.firstChild(); !n.isNull(); n = n.nextSibling()) {
+			QDomElement i = n.toElement();
+			if (i.isNull())
+				continue;
+
+			if (i.tagName() == "group") {
+				GroupData gd;
+				gd.open = i.attribute("open") == "true";
+				gd.rank = i.attribute("rank").toInt();
+                		groupState.insert(i.attribute("name"), gd);
+			}
+		}
+	}
+
+	readNumEntry(a, "proxyindex", &proxy_index);
+	readNumEntry(a, "proxytype", &proxy_type);
+	readEntry(a, "proxyhost", &proxy_host);
+	readNumEntry(a, "proxyport", &proxy_port);
+	readEntry(a, "proxyuser", &proxy_user);
+	readEntry(a, "proxypass", &proxy_pass);
+	if(!proxy_pass.isEmpty())
+		proxy_pass = decodePassword(proxy_pass, jid);
+
+	r = findSubTag(a, "pgpkeybindings", &found);
+	if(found)
+		keybind.fromXml(r);
+
+	QString str;
+	readEntry(a, "dtProxy", &str);
+	dtProxy = str;
+}
+
+UserProfile::UserProfile()
+{
+	reset();
+}
+
+void UserProfile::reset()
+{
+	bool nix, win, mac, os2;
+	nix = win = mac = os2 = FALSE;
+
+#ifdef Q_WS_X11
+	nix = TRUE;
+#endif
+#ifdef Q_WS_WIN
+	win = TRUE;
+#endif
+#ifdef Q_WS_MAC
+	mac = TRUE;
+#endif
+#ifdef Q_WS_PM
+	os2 = TRUE;
+#endif
+
+	progver = PROG_VERSION;
+
+	// global
+	mwgeom.setRect(64, 64, 150, 360);
+	lastStatusString = "";
+	useSound = TRUE;
+	proxyList.clear();
+	acc.clear();
+
+	// prefs
+	prefs.useleft = FALSE;
+	prefs.singleclick = FALSE;
+ 	prefs.hideMenubar = TRUE;
+	prefs.defaultAction = 1;
+	prefs.delChats = dcHour;
+	prefs.showJoins = TRUE;
+	prefs.browser = 0;
+	prefs.customBrowser = "";
+	prefs.customMailer = "";
+	prefs.alwaysOnTop = FALSE;
+	prefs.keepSizes = TRUE;
+	prefs.ignoreHeadline = FALSE;
+	prefs.ignoreNonRoster = FALSE;
+	prefs.excludeGroupChatsFromIgnore = TRUE;
+	prefs.useDock = win ? TRUE: FALSE;
+	prefs.dockDCstyle = win ? TRUE: FALSE;
+	prefs.dockHideMW = FALSE;
+	prefs.dockToolMW = FALSE;
+	prefs.isWMDock = false;
+	prefs.alertStyle = 2;
+	prefs.popupMsgs = FALSE;
+	prefs.popupChats = FALSE;
+	prefs.popupHeadlines = FALSE;
+	prefs.popupFiles = FALSE;
+	prefs.noAwayPopup = FALSE;
+	prefs.noUnlistedPopup = false;
+	prefs.raise = FALSE;
+	prefs.incomingAs = 0;
+	prefs.askOnline = FALSE;
+	prefs.askOffline = FALSE;
+	prefs.rosterAnim = TRUE;
+	prefs.autoVersion = false;
+	prefs.autoVCardOnLogin = true;
+	prefs.xmlConsoleOnLogin = false;
+	prefs.asAway = 10;
+	prefs.asXa = 30;
+	prefs.asOffline = 0;
+	prefs.use_asAway = TRUE;
+	prefs.use_asXa = TRUE;
+	prefs.use_asOffline = FALSE;
+	prefs.asMessage = QObject::tr("Auto Status (idle)");
+	prefs.scrollTo = TRUE;
+	prefs.useEmoticons = false;
+	prefs.alertOpenChats = false;
+	prefs.raiseChatWindow = false;
+	prefs.showSubjects = true;
+	prefs.showCounter = false;
+	prefs.chatSays = false;
+	prefs.chatSoftReturn = true;
+	prefs.showGroupCounts = true;
+	prefs.smallChats = false;
+	prefs.chatLineEdit = true;
+	prefs.useTabs = false;
+	prefs.usePerTabCloseButton = true;
+	prefs.putTabsAtBottom = false;
+	prefs.autoRosterSize = false;
+	prefs.autoRosterSizeGrowTop = false;
+	prefs.autoResolveNicksOnAdd = false;
+	prefs.chatBgImage = "";
+	prefs.rosterBgImage = "";
+	prefs.autoCopy = false;
+
+	prefs.sp.clear();
+	prefs.sp.set(QObject::tr("Away from desk"), QObject::tr("I am away from my desk.  Leave a message."));
+	prefs.sp.set(QObject::tr("Showering"), QObject::tr("I'm in the shower.  You'll have to wait for me to get out."));
+	prefs.sp.set(QObject::tr("Eating"), QObject::tr("Out eating.  Mmmm.. food."));
+	prefs.sp.set(QObject::tr("Sleep"), QObject::tr("Sleep is good.  Zzzzz"));
+	prefs.sp.set(QObject::tr("Work"), QObject::tr("Can't chat.  Gotta work."));
+	prefs.sp.set(QObject::tr("Air"), QObject::tr("Stepping out to get some fresh air."));
+	prefs.sp.set(QObject::tr("Movie"), QObject::tr("Out to a movie.  Is that OK with you?"));
+	prefs.sp.set(QObject::tr("Secret"), QObject::tr("I'm not available right now and that's all you need to know."));
+	prefs.sp.set(QObject::tr("Out for the night"), QObject::tr("Out for the night."));
+	prefs.sp.set(QObject::tr("Greece"), QObject::tr("I have gone to a far away place.  I will be back someday!"));
+	prefs.recentStatus.clear();
+
+	prefs.color[cOnline]    = QColor("#000000");
+	prefs.color[cListBack]  = QColor("#FFFFFF");
+	prefs.color[cAway]      = QColor("#004BB4");
+	prefs.color[cDND]       = QColor("#7E0000");
+	prefs.color[cOffline]   = QColor("#646464");
+	prefs.color[cProfileFore] = QColor("#FFFFFF");
+	prefs.color[cProfileBack] = QColor("#969696");
+	prefs.color[cGroupFore] = QColor("#5A5A5A");
+	prefs.color[cGroupBack] = QColor("#F0F0F0");
+	prefs.color[cAnimFront] = QColor("#000000");
+	prefs.color[cAnimBack] 	= QColor("#969696");
+
+	prefs.font[fRoster] = QApplication::font().toString();
+	prefs.font[fMessage] = QApplication::font().toString();
+	prefs.font[fChat] = QApplication::font().toString();
+	{
+		QFont font = QApplication::font();
+		font.setPointSize( font.pointSize() - 2 );
+		prefs.font[fPopup] = font.toString();
+	}
+
+	// calculate the small font size
+	const int minimumFontSize = 7;
+	prefs.smallFontSize = qApp->font().pointSize();
+	prefs.smallFontSize -= 2;
+	if ( prefs.smallFontSize < minimumFontSize )
+		prefs.smallFontSize = minimumFontSize;
+	FancyLabel::setSmallFontSize( prefs.smallFontSize );
+
+	prefs.clNewHeadings = false;
+	prefs.chatOpacity = 100;
+	prefs.rosterOpacity = 95;
+	prefs.outlineHeadings = false;
+
+	prefs.player = "play";
+	prefs.noAwaySound = FALSE;
+
+	prefs.onevent[eMessage]    = g.pathBase + "/sound/chat2.wav";
+	prefs.onevent[eChat1]      = g.pathBase + "/sound/chat1.wav";
+	prefs.onevent[eChat2]      = g.pathBase + "/sound/chat2.wav";
+	prefs.onevent[eSystem]     = g.pathBase + "/sound/chat2.wav";
+	prefs.onevent[eHeadline]   = g.pathBase + "/sound/chat2.wav";
+	prefs.onevent[eOnline]     = g.pathBase + "/sound/online.wav";
+	prefs.onevent[eOffline]    = g.pathBase + "/sound/offline.wav";
+	prefs.onevent[eSend]       = g.pathBase + "/sound/send.wav";
+	prefs.onevent[eIncomingFT] = g.pathBase + "/sound/ft_incoming.wav";
+	prefs.onevent[eFTComplete] = g.pathBase + "/sound/ft_complete.wav";
+
+	// Added by Kiko 020621: sets up the default certificate store
+	prefs.trustCertStoreDir = g.pathBase + "/certs";
+
+	prefs.sizeEventDlg = EventDlg::defaultSize();
+	prefs.sizeChatDlg = ChatDlg::defaultSize();
+	prefs.sizeTabDlg = ChatDlg::defaultSize(); //TODO: no!
+
+	prefs.jidComplete = true;
+	prefs.grabUrls = false;
+	prefs.noGCSound = true;
+
+	prefs.pgp = "";
+
+	// toolbars
+	prefs.toolbars.clear();
+
+	Options::ToolbarPrefs tbDef;
+	tbDef.name = QString::null;
+	tbDef.on = false;
+	tbDef.locked = false;
+	tbDef.stretchable = false;
+	tbDef.keys.clear();
+
+	tbDef.dock = Qt::DockTop;
+	tbDef.nl = true;
+	tbDef.extraOffset = 0;
+
+	tbDef.dirty = true;
+
+	{
+		Options::ToolbarPrefs tb[3];
+		uint i;
+		for (i = 0; i < sizeof(tb)/sizeof(Options::ToolbarPrefs); i++) {
+			tb[i] = tbDef;
+			tb[i].index = i + 1;
+		}
+
+		bool defaultEnableToolbars;
+#ifdef Q_WS_MAC
+		defaultEnableToolbars = false;
+#else
+		defaultEnableToolbars = true;
+#endif
+
+		// Imitate old Psi button layout by default
+		tb[0].name = QObject::tr("Buttons");
+		tb[0].on = defaultEnableToolbars;
+		tb[0].locked = true;
+		tb[0].stretchable = true;
+		tb[0].keys << "button_options" << "button_status";
+		tb[0].dock = Qt::DockBottom;
+
+		tb[1].name = QObject::tr("Show contacts");
+		tb[1].on = defaultEnableToolbars;
+		tb[1].locked = true;
+		tb[1].keys << "show_offline" << "show_away" << "show_agents" << "show_self";
+
+		tb[2].name = QObject::tr("Event notifier");
+		tb[2].on = false;
+		tb[2].locked = true;
+		tb[2].stretchable = true;
+		tb[2].keys << "event_notifier";
+		tb[2].dock = Qt::DockBottom;
+		tb[2].index = 0;
+
+		for ( i = 0; i < sizeof(tb)/sizeof(Options::ToolbarPrefs); i++ )
+			prefs.toolbars["mainWin"].append( tb[i] );
+	}
+
+	// groupchat
+	prefs.gcHighlighting = true;
+	prefs.gcHighlights.clear();
+
+	prefs.gcNickColoring = true;
+	prefs.gcNickColors.clear();
+
+	prefs.gcNickColors << "Blue";
+	prefs.gcNickColors << "Green";
+	prefs.gcNickColors << "Orange";
+	prefs.gcNickColors << "Purple";
+	prefs.gcNickColors << "Red";
+
+	// popups
+	prefs.ppIsOn        = false;
+	prefs.ppOnline      = true;
+	prefs.ppOffline     = true;
+	prefs.ppStatus      = false;
+	prefs.ppMessage     = true;
+	prefs.ppChat        = true;
+	prefs.ppHeadline    = true;
+	prefs.ppFile        = true;
+	prefs.ppJidClip     = 25;
+	prefs.ppStatusClip  = -1;
+	prefs.ppTextClip    = 300;
+	prefs.ppHideTime    = 10000; // 10 sec
+	prefs.ppBorderColor = QColor (0x52, 0x97, 0xF9);
+
+	// Bouncing of the dock (Mac OS X)
+	prefs.bounceDock = Options::BounceForever;
+
+	// lockdown
+	prefs.lockdown.roster   = false;
+	prefs.lockdown.options  = false;
+	prefs.lockdown.profiles = false;
+	prefs.lockdown.services = false;
+	prefs.lockdown.accounts = false;
+
+	prefs.useTransportIconsForContacts = false;
+
+	// iconsets
+	prefs.systemIconset = "default";
+	prefs.emoticons = "default";
+	prefs.defaultRosterIconset = "default";
+
+	prefs.serviceRosterIconset.clear();
+	prefs.serviceRosterIconset["transport"]	= "crystal-service.jisp";
+	prefs.serviceRosterIconset["aim"]	= "crystal-aim.jisp";
+	prefs.serviceRosterIconset["gadugadu"]	= "crystal-gadugadu.jisp";
+	prefs.serviceRosterIconset["icq"]	= "crystal-icq.jisp";
+	prefs.serviceRosterIconset["msn"]	= "crystal-msn.jisp";
+	prefs.serviceRosterIconset["yahoo"]	= "crystal-yahoo.jisp";
+	prefs.serviceRosterIconset["sms"]	= "crystal-sms.jisp";
+
+	prefs.customRosterIconset.clear();
+
+	// roster sorting
+	prefs.rosterContactSortStyle = Options::ContactSortStyle_Status;
+	prefs.rosterGroupSortStyle   = Options::GroupSortStyle_Alpha;
+	prefs.rosterAccountSortStyle = Options::AccountSortStyle_Alpha;
+
+	// tip of the day
+	prefs.showTips = true;
+	prefs.tipNum   = 0;
+
+	// disco dialog
+	prefs.discoItems = true;
+	prefs.discoInfo  = true;
+
+	// auto-auth
+	prefs.autoAuth = false;
+
+	// Notify authorization
+	prefs.notifyAuth = true;
+
+	// message events
+	prefs.messageEvents = true;
+
+	// event priority
+	prefs.eventPriorityHeadline = 0;
+	prefs.eventPriorityChat     = 1;
+	prefs.eventPriorityMessage  = 1;
+	prefs.eventPriorityAuth     = 2;
+	prefs.eventPriorityFile     = 3;
+
+	// Last used path remembering
+	prefs.lastPath = QDir::homeDirPath();
+	prefs.lastSavePath = QDir::homeDirPath();
+
+	// data transfer
+	prefs.dtPort = 8010;
+	prefs.dtExternal = "";
+
+	// Avatars
+	prefs.avatarsEnabled = true;
+	prefs.avatarsChatdlgEnabled = true;
+	prefs.avatarsSize = 48;
+
+	// global accelerators
+	prefs.globalAccels.clear();
+	prefs.globalAccels.append(QKeySequence()); // process next event
+	prefs.globalAccels.append(QKeySequence()); // show/hide roster
+
+	// advanced widget
+	GAdvancedWidget::setStickEnabled( false ); //until this is bugless
+	GAdvancedWidget::setStickToWindows( false ); //again
+	GAdvancedWidget::setStickAt( 5 );
+}
+
+static Options::ToolbarPrefs loadToolbarData( const QDomElement &e )
+{
+	QDomElement tb_prefs = e;
+	Options::ToolbarPrefs tb;
+
+	readEntry(tb_prefs, "name",		&tb.name);
+	readBoolEntry(tb_prefs, "on",		&tb.on);
+	readBoolEntry(tb_prefs, "locked",	&tb.locked);
+	readBoolEntry(tb_prefs, "stretchable",	&tb.stretchable);
+	xmlToStringList(tb_prefs, "keys",	&tb.keys);
+
+	bool found3 = false;
+	QDomElement tb_position = findSubTag(tb_prefs, "position", &found3);
+	if (found3)
+	{
+		QString dockStr;
+		Qt::Dock dock = Qt::DockTop;
+		readEntry(tb_position, "dock", &dockStr);
+		if (dockStr == "DockTop")
+			dock = Qt::DockTop;
+		else if (dockStr == "DockBottom")
+			dock = Qt::DockBottom;
+		else if (dockStr == "DockLeft")
+			dock = Qt::DockLeft;
+		else if (dockStr == "DockRight")
+			dock = Qt::DockRight;
+		else if (dockStr == "DockMinimized")
+			dock = Qt::DockMinimized;
+		else if (dockStr == "DockTornOff")
+			dock = Qt::DockTornOff;
+		else if (dockStr == "DockUnmanaged")
+			dock = Qt::DockUnmanaged;
+
+		tb.dock = dock;
+
+		readNumEntry(tb_position, "index",		&tb.index);
+		readBoolEntry(tb_position, "nl",		&tb.nl);
+		readNumEntry(tb_position, "extraOffset",	&tb.extraOffset);
+	}
+
+	return tb;
+}
+
+bool UserProfile::toFile(const QString &fname)
+{
+	QDomDocument doc;
+
+	QDomElement base = doc.createElement("psiconf");
+	base.setAttribute("version", "1.0");
+	doc.appendChild(base);
+
+	base.appendChild(textTag(doc, "progver", PROG_VERSION));
+	base.appendChild(textTag(doc, "geom", mwgeom));
+	base.appendChild(stringListToXml(doc, "recentGCList", recentGCList));
+	base.appendChild(stringListToXml(doc, "recentBrowseList", recentBrowseList));
+	base.appendChild(textTag(doc, "lastStatusString", lastStatusString));
+	base.appendChild(textTag(doc, "useSound", useSound));
+
+	QDomElement accs = doc.createElement("accounts");
+	base.appendChild(accs);
+	for(UserAccountList::Iterator ai = acc.begin(); ai != acc.end(); ++ai)
+		accs.appendChild((*ai).toXml(doc, "account"));
+
+	QDomElement prox = doc.createElement("proxies");
+	base.appendChild(prox);
+	for(ProxyItemList::Iterator pi = proxyList.begin(); pi != proxyList.end(); ++pi) {
+		const ProxyItem &p = *pi;
+		QDomElement i = doc.createElement("proxy");
+		i.appendChild(textTag(doc, "name", p.name));
+		i.appendChild(textTag(doc, "type", p.type));
+		i.appendChild(p.settings.toXml(&doc));
+		prox.appendChild(i);
+	}
+
+	QDomElement p = doc.createElement("preferences");
+	base.appendChild(p);
+
+	{
+		QDomElement p_general = doc.createElement("general");
+		p.appendChild(p_general);
+
+		{
+			QDomElement p_roster = doc.createElement("roster");
+			p_general.appendChild(p_roster);
+
+			p_roster.appendChild(textTag(doc, "useleft", prefs.useleft));
+			p_roster.appendChild(textTag(doc, "rosterBgImage", prefs.rosterBgImage));
+			p_roster.appendChild(textTag(doc, "singleclick", prefs.singleclick));
+			p_roster.appendChild(textTag(doc, "hideMenubar", prefs.hideMenubar));
+			p_roster.appendChild(textTag(doc, "defaultAction", prefs.defaultAction));
+			p_roster.appendChild(textTag(doc, "useTransportIconsForContacts", prefs.useTransportIconsForContacts));
+
+			{
+				QDomElement sorting = doc.createElement("sortStyle");
+				p_roster.appendChild(sorting);
+
+				QString name = "status";
+				if ( prefs.rosterContactSortStyle == Options::ContactSortStyle_Alpha )
+					name = "alpha";
+				sorting.appendChild(textTag(doc, "contact", name));
+
+				name = "alpha";
+				if ( prefs.rosterGroupSortStyle == Options::GroupSortStyle_Rank )
+					name = "rank";
+				sorting.appendChild(textTag(doc, "group", name));
+
+				name = "alpha";
+				if ( prefs.rosterAccountSortStyle == Options::AccountSortStyle_Rank )
+					name = "rank";
+				sorting.appendChild(textTag(doc, "account", name));
+			}
+		}
+
+		// Avatars
+		{
+			QDomElement p_avatars = doc.createElement("avatars");
+			p_general.appendChild(p_avatars);
+			p_avatars.appendChild(textTag(doc, "use", prefs.avatarsEnabled));
+			p_avatars.appendChild(textTag(doc, "use_chatdlg", prefs.avatarsChatdlgEnabled));
+			p_avatars.appendChild(textTag(doc, "size", prefs.avatarsSize));
+		}
+		{
+			QDomElement p_misc = doc.createElement("misc");
+			p_general.appendChild(p_misc);
+
+			p_misc.appendChild(textTag(doc, "chatBgImage", prefs.chatBgImage));
+			p_misc.appendChild(textTag(doc, "smallChats", prefs.smallChats));
+			p_misc.appendChild(textTag(doc, "chatLineEdit", prefs.chatLineEdit));
+			p_misc.appendChild(textTag(doc, "useTabs", prefs.useTabs));
+			p_misc.appendChild(textTag(doc, "usePerTabCloseButton", prefs.usePerTabCloseButton));
+			p_misc.appendChild(textTag(doc, "putTabsAtBottom", prefs.putTabsAtBottom));
+			p_misc.appendChild(textTag(doc, "autoRosterSize", prefs.autoRosterSize));
+			p_misc.appendChild(textTag(doc, "autoRosterSizeGrowTop", prefs.autoRosterSizeGrowTop));
+			p_misc.appendChild(textTag(doc, "autoResolveNicksOnAdd", prefs.autoResolveNicksOnAdd));
+			p_misc.appendChild(textTag(doc, "delChats", prefs.delChats));
+			p_misc.appendChild(textTag(doc, "showJoins", prefs.showJoins));
+			p_misc.appendChild(textTag(doc, "browser", prefs.browser));
+			p_misc.appendChild(textTag(doc, "customBrowser", prefs.customBrowser));
+			p_misc.appendChild(textTag(doc, "customMailer", prefs.customMailer));
+			p_misc.appendChild(textTag(doc, "alwaysOnTop", prefs.alwaysOnTop));
+			p_misc.appendChild(textTag(doc, "keepSizes", prefs.keepSizes));
+			p_misc.appendChild(textTag(doc, "ignoreHeadline", prefs.ignoreHeadline));
+			p_misc.appendChild(textTag(doc, "ignoreNonRoster", prefs.ignoreNonRoster));
+			p_misc.appendChild(textTag(doc, "excludeGroupChatIgnore", prefs.excludeGroupChatsFromIgnore));
+			p_misc.appendChild(textTag(doc, "scrollTo", prefs.scrollTo));
+			p_misc.appendChild(textTag(doc, "useEmoticons", prefs.useEmoticons));
+			p_misc.appendChild(textTag(doc, "alertOpenChats", prefs.alertOpenChats));
+			p_misc.appendChild(textTag(doc, "raiseChatWindow", prefs.raiseChatWindow));
+			p_misc.appendChild(textTag(doc, "showSubjects", prefs.showSubjects));
+			p_misc.appendChild(textTag(doc, "showCounter", prefs.showCounter));
+			p_misc.appendChild(textTag(doc, "chatSays", prefs.chatSays));
+			p_misc.appendChild(textTag(doc, "chatSoftReturn", prefs.chatSoftReturn));
+			p_misc.appendChild(textTag(doc, "showGroupCounts", prefs.showGroupCounts));
+			p_misc.appendChild(textTag(doc, "jidComplete", prefs.jidComplete));
+			p_misc.appendChild(textTag(doc, "grabUrls", prefs.grabUrls));
+			p_misc.appendChild(textTag(doc, "messageEvents", prefs.messageEvents));
+			p_misc.appendChild(textTag(doc, "lastPath", prefs.lastPath));
+			p_misc.appendChild(textTag(doc, "lastSavePath", prefs.lastSavePath));
+			p_misc.appendChild(textTag(doc, "autoCopy", prefs.autoCopy));
+		}
+		{
+			QDomElement p_dock = doc.createElement("dock");
+			p_general.appendChild(p_dock);
+
+			p_dock.appendChild(textTag(doc, "useDock", prefs.useDock));
+			p_dock.appendChild(textTag(doc, "dockDCstyle", prefs.dockDCstyle));
+			p_dock.appendChild(textTag(doc, "dockHideMW", prefs.dockHideMW));
+			p_dock.appendChild(textTag(doc, "dockToolMW", prefs.dockToolMW));
+			p_dock.appendChild(textTag(doc, "isWMDock", prefs.isWMDock));
+		}
+		/*{
+			QDomElement p_sec = doc.createElement("security");
+			p_general.appendChild(p_sec);
+
+			p_sec.appendChild(textTag(doc, "pgp", prefs.pgp));
+		}*/
+	}
+
+	// Added by Kiko 020621: stores SSL cert chain validation configuration
+	{
+		QDomElement p_ssl = doc.createElement("ssl");
+		p.appendChild(p_ssl);
+
+		p_ssl.appendChild(textTag(doc, "trustedcertstoredir", prefs.trustCertStoreDir));
+	}
+
+	{
+		QDomElement p_events = doc.createElement("events");
+		p.appendChild(p_events);
+
+		p_events.appendChild(textTag(doc, "alertstyle", prefs.alertStyle));
+		p_events.appendChild(textTag(doc, "autoAuth", prefs.autoAuth));
+		p_events.appendChild(textTag(doc, "notifyAuth", prefs.notifyAuth));
+
+
+		{
+			QDomElement p_receive = doc.createElement("receive");
+			p_events.appendChild(p_receive);
+
+			p_receive.appendChild(textTag(doc, "popupMsgs", prefs.popupMsgs));
+			p_receive.appendChild(textTag(doc, "popupChats", prefs.popupChats));
+			p_receive.appendChild(textTag(doc, "popupHeadlines", prefs.popupHeadlines));
+			p_receive.appendChild(textTag(doc, "popupFiles", prefs.popupFiles));
+			p_receive.appendChild(textTag(doc, "noAwayPopup", prefs.noAwayPopup));
+			p_receive.appendChild(textTag(doc, "noUnlistedPopup", prefs.noUnlistedPopup));
+			p_receive.appendChild(textTag(doc, "raise", prefs.raise));
+			p_receive.appendChild(textTag(doc, "incomingAs", prefs.incomingAs));
+		}
+
+		{
+			QDomElement p_priority = doc.createElement("priority");
+			p_events.appendChild(p_priority);
+
+			p_priority.appendChild(textTag(doc, "message",  prefs.eventPriorityMessage));
+			p_priority.appendChild(textTag(doc, "chat",     prefs.eventPriorityChat));
+			p_priority.appendChild(textTag(doc, "headline", prefs.eventPriorityHeadline));
+			p_priority.appendChild(textTag(doc, "auth",     prefs.eventPriorityAuth));
+			p_priority.appendChild(textTag(doc, "file",     prefs.eventPriorityFile));
+		}
+	}
+
+	{
+		QDomElement p_pres = doc.createElement("presence");
+		p.appendChild(p_pres);
+
+		{
+			QDomElement tag = doc.createElement("misc");
+			p_pres.appendChild(tag);
+
+			tag.appendChild(textTag(doc, "askOnline", prefs.askOnline));
+			tag.appendChild(textTag(doc, "askOffline", prefs.askOffline));
+			tag.appendChild(textTag(doc, "rosterAnim", prefs.rosterAnim));
+			tag.appendChild(textTag(doc, "autoVersion", prefs.autoVersion));
+			tag.appendChild(textTag(doc, "autoVCardOnLogin", prefs.autoVCardOnLogin));
+			tag.appendChild(textTag(doc, "xmlConsoleOnLogin", prefs.xmlConsoleOnLogin));
+		}
+		{
+			QDomElement tag = doc.createElement("autostatus");
+			p_pres.appendChild(tag);
+
+			QDomElement e;
+
+			e = textTag(doc, "away", prefs.asAway);
+			setBoolAttribute(e, "use", prefs.use_asAway);
+			tag.appendChild(e);
+
+			e = textTag(doc, "xa", prefs.asXa);
+			setBoolAttribute(e, "use", prefs.use_asXa);
+			tag.appendChild(e);
+
+			e = textTag(doc, "offline", prefs.asOffline);
+			setBoolAttribute(e, "use", prefs.use_asOffline);
+			tag.appendChild(e);
+
+			tag.appendChild(textTag(doc, "message", prefs.asMessage));
+		}
+		{
+			p_pres.appendChild(prefs.sp.toXml(doc, "statuspresets"));
+		}
+		{
+			p_pres.appendChild(stringListToXml(doc, "recentstatus",prefs.recentStatus));
+		}
+	}
+
+	{
+		QDomElement p_lnf = doc.createElement("lookandfeel");
+		p.appendChild(p_lnf);
+
+		p_lnf.appendChild(textTag(doc, "newHeadings", prefs.clNewHeadings));
+		p_lnf.appendChild(textTag(doc, "chat-opacity", prefs.chatOpacity));
+		p_lnf.appendChild(textTag(doc, "roster-opacity", prefs.rosterOpacity));
+		p_lnf.appendChild(textTag(doc, "outline-headings", prefs.outlineHeadings));
+
+		{
+			QDomElement tag = doc.createElement("colors");
+			p_lnf.appendChild(tag);
+
+			tag.appendChild(textTag(doc, "online", prefs.color[cOnline].name() ));
+			tag.appendChild(textTag(doc, "listback", prefs.color[cListBack].name() ));
+			tag.appendChild(textTag(doc, "away", prefs.color[cAway].name() ));
+			tag.appendChild(textTag(doc, "dnd", prefs.color[cDND].name() ));
+			tag.appendChild(textTag(doc, "offline", prefs.color[cOffline].name() ));
+			tag.appendChild(textTag(doc, "profilefore", prefs.color[cProfileFore].name() ));
+			tag.appendChild(textTag(doc, "profileback", prefs.color[cProfileBack].name() ));
+			tag.appendChild(textTag(doc, "groupfore", prefs.color[cGroupFore].name() ));
+			tag.appendChild(textTag(doc, "groupback", prefs.color[cGroupBack].name() ));
+			tag.appendChild(textTag(doc, "animfront", prefs.color[cAnimFront].name() ));
+			tag.appendChild(textTag(doc, "animback", prefs.color[cAnimBack].name() ));
+		}
+
+		{
+			QDomElement tag = doc.createElement("fonts");
+			p_lnf.appendChild(tag);
+
+			tag.appendChild(textTag(doc, "roster", prefs.font[fRoster] ));
+			tag.appendChild(textTag(doc, "message", prefs.font[fMessage] ));
+			tag.appendChild(textTag(doc, "chat", prefs.font[fChat] ));
+			tag.appendChild(textTag(doc, "popup", prefs.font[fPopup] ));
+		}
+	}
+
+	{
+		QDomElement p_sound = doc.createElement("sound");
+		p.appendChild(p_sound);
+
+		p_sound.appendChild(textTag(doc, "player", prefs.player));
+		p_sound.appendChild(textTag(doc, "noawaysound", prefs.noAwaySound));
+		p_sound.appendChild(textTag(doc, "noGCSound", prefs.noGCSound));
+
+		{
+			QDomElement p_onevent = doc.createElement("onevent");
+			p_sound.appendChild(p_onevent);
+
+			p_onevent.appendChild(textTag(doc, "message", prefs.onevent[eMessage]));
+			p_onevent.appendChild(textTag(doc, "chat1", prefs.onevent[eChat1]));
+			p_onevent.appendChild(textTag(doc, "chat2", prefs.onevent[eChat2]));
+			p_onevent.appendChild(textTag(doc, "system", prefs.onevent[eSystem]));
+			p_onevent.appendChild(textTag(doc, "headline", prefs.onevent[eHeadline]));
+			p_onevent.appendChild(textTag(doc, "online", prefs.onevent[eOnline]));
+			p_onevent.appendChild(textTag(doc, "offline", prefs.onevent[eOffline]));
+			p_onevent.appendChild(textTag(doc, "send", prefs.onevent[eSend]));
+			p_onevent.appendChild(textTag(doc, "incoming_ft", prefs.onevent[eIncomingFT]));
+			p_onevent.appendChild(textTag(doc, "ft_complete", prefs.onevent[eFTComplete]));
+		}
+	}
+
+	{
+		QDomElement p_sizes = doc.createElement("sizes");
+		p.appendChild(p_sizes);
+
+		p_sizes.appendChild(textTag(doc, "eventdlg", prefs.sizeEventDlg));
+		p_sizes.appendChild(textTag(doc, "chatdlg", prefs.sizeChatDlg));
+		p_sizes.appendChild(textTag(doc, "tabdlg", prefs.sizeTabDlg));
+	}
+
+	{
+		QDomElement p_toolbars = doc.createElement("toolbars");
+		p.appendChild(p_toolbars);
+
+		QMap< QString, QValueList<Options::ToolbarPrefs> >::Iterator itBars = prefs.toolbars.begin();
+		for ( ; itBars != prefs.toolbars.end(); ++itBars ) {
+			QValueList<Options::ToolbarPrefs> toolbars = itBars.data();
+			QDomElement p_bars = doc.createElement( itBars.key() );
+			p_toolbars.appendChild( p_bars );
+
+			QValueList<Options::ToolbarPrefs>::Iterator it = toolbars.begin();
+			for ( ; it != toolbars.end(); ++it) {
+				Options::ToolbarPrefs tb = *it;
+				if ( tb.keys.size() ||
+				     !tb.name.isEmpty() )
+				{
+					QDomElement tb_prefs = doc.createElement("toolbar");
+					p_bars.appendChild(tb_prefs);
+
+					tb_prefs.appendChild(textTag(doc, "name",		tb.name));
+					tb_prefs.appendChild(textTag(doc, "on",			tb.on));
+					tb_prefs.appendChild(textTag(doc, "locked",		tb.locked));
+					tb_prefs.appendChild(textTag(doc, "stretchable",	tb.stretchable));
+					tb_prefs.appendChild(stringListToXml(doc, "keys",	tb.keys));
+
+					QDomElement tb_position = doc.createElement("position");
+					tb_prefs.appendChild(tb_position);
+
+					QString dockStr;
+					Qt::Dock dock = tb.dock;
+					if (dock == Qt::DockTop)
+						dockStr = "DockTop";
+					else if (dock == Qt::DockBottom)
+						dockStr = "DockBottom";
+					else if (dock == Qt::DockLeft)
+						dockStr = "DockLeft";
+					else if (dock == Qt::DockRight)
+						dockStr = "DockRight";
+					else if (dock == Qt::DockMinimized)
+						dockStr = "DockMinimized";
+					else if (dock == Qt::DockTornOff)
+						dockStr = "DockTornOff";
+					else if (dock == Qt::DockUnmanaged)
+						dockStr = "DockUnmanaged";
+
+					tb_position.appendChild(textTag(doc, "dock",		dockStr));
+					tb_position.appendChild(textTag(doc, "index",		tb.index));
+					tb_position.appendChild(textTag(doc, "nl",		tb.nl));
+					tb_position.appendChild(textTag(doc, "extraOffset",	tb.extraOffset));
+				}
+			}
+		}
+	}
+
+	{
+		QDomElement pp = doc.createElement("popups");
+		p.appendChild(pp);
+		pp.appendChild(textTag(doc, "on", prefs.ppIsOn));
+		pp.appendChild(textTag(doc, "online", prefs.ppOnline));
+		pp.appendChild(textTag(doc, "offline", prefs.ppOffline));
+		pp.appendChild(textTag(doc, "statusChange", prefs.ppStatus));
+		pp.appendChild(textTag(doc, "message", prefs.ppMessage));
+		pp.appendChild(textTag(doc, "chat", prefs.ppChat));
+		pp.appendChild(textTag(doc, "headline", prefs.ppHeadline));
+		pp.appendChild(textTag(doc, "file", prefs.ppFile));
+		pp.appendChild(textTag(doc, "jidClip", prefs.ppJidClip));
+		pp.appendChild(textTag(doc, "statusClip", prefs.ppStatusClip));
+		pp.appendChild(textTag(doc, "textClip", prefs.ppTextClip));
+		pp.appendChild(textTag(doc, "hideTime", prefs.ppHideTime));
+		pp.appendChild(textTag(doc, "borderColor", prefs.ppBorderColor.name()));
+	}
+	
+	{
+		// Bouncing dock (on Mac OS X)
+		QDomElement dock = doc.createElement("dock");
+		p.appendChild(dock);
+		switch (prefs.bounceDock) {
+			case Options::NoBounce :
+				dock.setAttribute("bounce", "never");
+				break;
+			case Options::BounceOnce :
+				dock.setAttribute("bounce", "once");
+				break;
+			case Options::BounceForever :
+				dock.setAttribute("bounce", "forever");
+				break;
+			default:
+				break;
+		}
+	}
+
+	{
+		//group chat
+		QDomElement gc = doc.createElement("groupchat");
+		p.appendChild(gc);
+		gc.appendChild(stringListToXml(doc, "highlightwords", prefs.gcHighlights));
+		gc.appendChild(stringListToXml(doc, "nickcolors", prefs.gcNickColors));
+		gc.appendChild(textTag(doc, "nickcoloring", prefs.gcNickColoring));
+		gc.appendChild(textTag(doc, "highlighting", prefs.gcHighlighting));
+	}
+
+	{
+		QDomElement p_lockdown = doc.createElement("lockdown");
+		p.appendChild(p_lockdown);
+		p_lockdown.appendChild(textTag(doc, "roster", prefs.lockdown.roster));
+		p_lockdown.appendChild(textTag(doc, "options", prefs.lockdown.options));
+		p_lockdown.appendChild(textTag(doc, "profiles", prefs.lockdown.profiles));
+		p_lockdown.appendChild(textTag(doc, "services", prefs.lockdown.services));
+		p_lockdown.appendChild(textTag(doc, "accounts", prefs.lockdown.accounts));
+	}
+
+	{
+		QDomElement is = doc.createElement("iconset");
+		p.appendChild(is);
+
+		// system
+		is.appendChild(textTag(doc, "system", prefs.systemIconset));
+
+		// roster - default
+		QDomElement is_roster = doc.createElement("roster");
+		is.appendChild(is_roster);
+		is_roster.appendChild(textTag(doc, "default", prefs.defaultRosterIconset));
+
+		{
+			// roster - service
+			QDomElement roster_service = doc.createElement("service");
+			is_roster.appendChild(roster_service);
+
+			QMapIterator<QString, QString> it = prefs.serviceRosterIconset.begin();
+			for ( ; it != prefs.serviceRosterIconset.end(); ++it) {
+				QDomElement item = doc.createElement("item");
+				roster_service.appendChild(item);
+
+				item.setAttribute("service", it.key());
+				item.setAttribute("iconset", it.data());
+			}
+		}
+
+		{
+			// roster - custom
+			QDomElement roster_custom = doc.createElement("custom");
+			is_roster.appendChild(roster_custom);
+
+			QMapIterator<QString, QString> it = prefs.customRosterIconset.begin();
+			for ( ; it != prefs.customRosterIconset.end(); ++it) {
+				QDomElement item = doc.createElement("item");
+				roster_custom.appendChild(item);
+
+				item.setAttribute("regExp", it.key());
+				item.setAttribute("iconset", it.data());
+			}
+		}
+
+		{
+			// emoticons
+			QDomElement is_emoticons = doc.createElement("emoticons");
+			is.appendChild(is_emoticons);
+
+			QStringList::Iterator it = prefs.emoticons.begin();
+			for ( ; it != prefs.emoticons.end(); ++it)
+				is_emoticons.appendChild(textTag(doc, "item", *it));
+		}
+	}
+
+	{
+		// tip of the day
+		QDomElement p_tip = doc.createElement("tipOfTheDay");
+		p.appendChild(p_tip);
+
+		p_tip.appendChild( textTag(doc, "show", prefs.showTips ) );
+		p_tip.appendChild( textTag(doc, "num",  prefs.tipNum ) );
+	}
+
+	{
+		// disco dialog
+		QDomElement p_disco = doc.createElement("disco");
+		p.appendChild(p_disco);
+
+		p_disco.appendChild( textTag(doc, "items", prefs.discoItems ) );
+		p_disco.appendChild( textTag(doc, "info",  prefs.discoInfo ) );
+	}
+
+	{
+		// data transfer
+		QDomElement p_dt = doc.createElement("dt");
+		p.appendChild(p_dt);
+		p_dt.appendChild( textTag(doc, "port", prefs.dtPort ) );
+		p_dt.appendChild( textTag(doc, "external", prefs.dtExternal ) );
+	}
+
+	{
+		// global accelerators
+		QDomElement p_globalAccel = doc.createElement("globalAccel");
+		p.appendChild(p_globalAccel);
+
+		QStringList accelNames;
+		accelNames << "processNextEvent";
+		accelNames << "showHideRoster";
+
+		QStringList::Iterator it1 = accelNames.begin();
+		QValueList<QKeySequence>::iterator it2 = prefs.globalAccels.begin();
+
+		for ( ; it2 != prefs.globalAccels.end(); ++it1, ++it2 ) {
+			QDomElement item = doc.createElement("command");
+			p_globalAccel.appendChild(item);
+			item.setAttribute("type", *it1);
+			item.appendChild( doc.createTextNode(*it2) );
+		}
+	}
+
+	{
+		// advanced widget
+		QDomElement p_advWidget = doc.createElement("advancedWidget");
+		p.appendChild(p_advWidget);
+
+		QDomElement stick = doc.createElement("sticky");
+		p_advWidget.appendChild(stick);
+
+		setBoolAttribute(stick, "enabled", GAdvancedWidget::stickEnabled());
+		stick.appendChild( textTag(doc, "offset", GAdvancedWidget::stickAt()) );
+		stick.appendChild( textTag(doc, "stickToWindows", GAdvancedWidget::stickToWindows()) );
+	}
+
+	QFile f(fname);
+	if(!f.open(IO_WriteOnly))
+		return FALSE;
+	QTextStream t;
+	t.setDevice(&f);
+	t.setEncoding(QTextStream::UnicodeUTF8);
+	t << doc.toString();
+	t.unsetDevice();
+	f.close();
+
+	return TRUE;
+}
+
+bool UserProfile::fromFile(const QString &fname)
+{
+	QString confver;
+	QDomDocument doc;
+
+	QFile f(fname);
+	if(!f.open(IO_ReadOnly))
+		return FALSE;
+	if(!doc.setContent(&f))
+		return FALSE;
+	f.close();
+
+	QDomElement base = doc.documentElement();
+	if(base.tagName() != "psiconf")
+		return FALSE;
+	confver = base.attribute("version");
+	if(confver != "1.0")
+		return FALSE;
+
+	readEntry(base, "progver", &progver);
+
+	readRectEntry(base, "geom", &mwgeom);
+	xmlToStringList(base, "recentGCList", &recentGCList);
+	xmlToStringList(base, "recentBrowseList", &recentBrowseList);
+	readEntry(base, "lastStatusString", &lastStatusString);
+	readBoolEntry(base, "useSound", &useSound);
+
+	bool found;
+	QDomElement accs = findSubTag(base, "accounts", &found);
+	if(found) {
+		for(QDomNode n = accs.firstChild(); !n.isNull(); n = n.nextSibling()) {
+			QDomElement a = n.toElement();
+			if(a.isNull())
+				continue;
+
+			if(a.tagName() == "account") {
+				UserAccount ua;
+				ua.fromXml(a);
+				acc.append(ua);
+			}
+		}
+	}
+
+	// convert old proxy config into new
+	for(UserAccountList::Iterator it = acc.begin(); it != acc.end(); ++it) {
+		UserAccount &a = *it;
+		if(a.proxy_type > 0) {
+			ProxyItem p;
+			p.name = QObject::tr("%1 Proxy").arg(a.name);
+			p.type = "http";
+			p.settings.host = a.proxy_host;
+			p.settings.port = a.proxy_port;
+			p.settings.useAuth = !a.proxy_user.isEmpty();
+			p.settings.user = a.proxy_user;
+			p.settings.pass = a.proxy_pass;
+			proxyList.append(p);
+
+			a.proxy_index = proxyList.count(); // 1 and up are proxies
+		}
+	}
+
+	QDomElement prox = findSubTag(base, "proxies", &found);
+	if(found) {
+		QDomNodeList list = prox.elementsByTagName("proxy");
+		for(uint n = 0; n < list.count(); ++n) {
+			QDomElement e = list.item(n).toElement();
+			ProxyItem p;
+			p.name = "";
+			p.type = "";
+			readEntry(e, "name", &p.name);
+			readEntry(e, "type", &p.type);
+			if(p.type == "0")
+				p.type = "http";
+			QDomElement pset = e.elementsByTagName("proxySettings").item(0).toElement();
+			if(!pset.isNull())
+				p.settings.fromXml(pset);
+			proxyList.append(p);
+		}
+	}
+
+	QDomElement p = findSubTag(base, "preferences", &found);
+	if(found) {
+		bool found;
+
+		QDomElement p_general = findSubTag(p, "general", &found);
+		if(found) {
+			bool found;
+
+			QDomElement p_roster = findSubTag(p_general, "roster", &found);
+			if(found) {
+				readEntry(p_roster, "rosterBgImage", &prefs.rosterBgImage);
+				readBoolEntry(p_roster, "useleft", &prefs.useleft);
+				readBoolEntry(p_roster, "singleclick", &prefs.singleclick);
+				readBoolEntry(p_roster, "hideMenubar", &prefs.hideMenubar);
+				readNumEntry(p_roster, "defaultAction", &prefs.defaultAction);
+				readBoolEntry(p_roster, "useTransportIconsForContacts", &prefs.useTransportIconsForContacts);
+
+				QDomElement sorting = findSubTag(p_roster, "sortStyle", &found);
+				if(found) {
+					QString name;
+
+					readEntry(sorting, "contact", &name);
+					if ( name == "alpha" )
+						prefs.rosterContactSortStyle = Options::ContactSortStyle_Alpha;
+					else
+						prefs.rosterContactSortStyle = Options::ContactSortStyle_Status;
+
+					readEntry(sorting, "group", &name);
+					if ( name == "rank" )
+						prefs.rosterGroupSortStyle = Options::GroupSortStyle_Rank;
+					else
+						prefs.rosterGroupSortStyle = Options::GroupSortStyle_Alpha;
+
+					readEntry(sorting, "account", &name);
+					if ( name == "rank" )
+						prefs.rosterAccountSortStyle = Options::AccountSortStyle_Rank;
+					else
+						prefs.rosterAccountSortStyle = Options::AccountSortStyle_Alpha;
+				}
+			}
+
+			// Avatars
+			QDomElement avTag = findSubTag(p_general, "avatars", &found);
+			if(found) {
+				readBoolEntry(avTag, "use", &prefs.avatarsEnabled);
+				readBoolEntry(avTag, "use_chatdlg", &prefs.avatarsChatdlgEnabled);
+				readNumEntry(avTag, "size", &prefs.avatarsSize);
+			}
+
+			QDomElement tag = findSubTag(p_general, "misc", &found);
+			if(found) {
+				readEntry(tag, "chatBgImage", &prefs.chatBgImage);
+				readNumEntry(tag, "delChats", &prefs.delChats);
+				readBoolEntry(tag, "showJoins", &prefs.showJoins);
+				readNumEntry(tag, "browser", &prefs.browser);
+				readEntry(tag, "customBrowser", &prefs.customBrowser);
+				readEntry(tag, "customMailer", &prefs.customMailer);
+				readBoolEntry(tag, "alwaysOnTop", &prefs.alwaysOnTop);
+				readBoolEntry(tag, "keepSizes", &prefs.keepSizes);
+				readBoolEntry(tag, "ignoreHeadline", &prefs.ignoreHeadline);
+				readBoolEntry(tag, "ignoreNonRoster", &prefs.ignoreNonRoster);
+				readBoolEntry(tag, "excludeGroupChatIgnore", &prefs.excludeGroupChatsFromIgnore);
+				readBoolEntry(tag, "scrollTo", &prefs.scrollTo);
+				readBoolEntry(tag, "useEmoticons", &prefs.useEmoticons);
+				readBoolEntry(tag, "alertOpenChats", &prefs.alertOpenChats);
+				readBoolEntry(tag, "raiseChatWindow", &prefs.raiseChatWindow);
+				readBoolEntry(tag, "showSubjects", &prefs.showSubjects);
+				readBoolEntry(tag, "chatSoftReturn", &prefs.chatSoftReturn);
+				readBoolEntry(tag, "showGroupCounts", &prefs.showGroupCounts);
+				readBoolEntry(tag, "showCounter", &prefs.showCounter);
+				readBoolEntry(tag, "chatSays", &prefs.chatSays);
+				readBoolEntry(tag, "jidComplete", &prefs.jidComplete);
+				readBoolEntry(tag, "grabUrls", &prefs.grabUrls);
+				readBoolEntry(tag, "smallChats", &prefs.smallChats);
+				readBoolEntry(tag, "chatLineEdit", &prefs.chatLineEdit);
+				readBoolEntry(tag, "useTabs", &prefs.useTabs);
+				readBoolEntry(tag, "usePerTabCloseButton", &prefs.usePerTabCloseButton);
+				readBoolEntry(tag, "putTabsAtBottom", &prefs.putTabsAtBottom);
+				readBoolEntry(tag, "autoRosterSize", &prefs.autoRosterSize);
+				readBoolEntry(tag, "autoRosterSizeGrowTop", &prefs.autoRosterSizeGrowTop);
+				readBoolEntry(tag, "autoResolveNicksOnAdd", &prefs.autoResolveNicksOnAdd);
+				readBoolEntry(tag, "messageEvents", &prefs.messageEvents);
+				readEntry(tag, "lastPath", &prefs.lastPath);
+				readEntry(tag, "lastSavePath", &prefs.lastSavePath);
+				readBoolEntry(tag, "autoCopy", &prefs.autoCopy);
+			}
+
+			tag = findSubTag(p_general, "dock", &found);
+			if(found) {
+				readBoolEntry(tag, "useDock", &prefs.useDock);
+				readBoolEntry(tag, "dockDCstyle", &prefs.dockDCstyle);
+				readBoolEntry(tag, "dockHideMW", &prefs.dockHideMW);
+				readBoolEntry(tag, "dockToolMW", &prefs.dockToolMW);
+				readBoolEntry(tag, "isWMDock", &prefs.isWMDock);
+			}
+
+			/*tag = findSubTag(p_general, "security", &found);
+			if(found) {
+				readEntry(tag, "pgp", &prefs.pgp);
+			}*/
+		}
+
+		// Added by Kiko 020621: retrieves SSL cert chain validation configuration
+		QDomElement p_ssl = findSubTag(p,"ssl",&found);
+		if(found) {
+			readEntry(p_ssl, "trustedcertstoredir", &prefs.trustCertStoreDir);
+		}
+
+		QDomElement p_events = findSubTag(p, "events", &found);
+		if(found) {
+			bool found;
+
+			readNumEntry(p_events, "alertstyle", &prefs.alertStyle);
+			readBoolEntry(p_events, "autoAuth", &prefs.autoAuth);
+			readBoolEntry(p_events, "notifyAuth", &prefs.notifyAuth);
+
+			QDomElement tag = findSubTag(p_events, "receive", &found);
+			if(found) {
+				readBoolEntry(tag, "popupMsgs", &prefs.popupMsgs);
+				readBoolEntry(tag, "popupChats", &prefs.popupChats);
+				readBoolEntry(tag, "popupHeadlines", &prefs.popupHeadlines);
+				readBoolEntry(tag, "popupFiles", &prefs.popupFiles);
+				readBoolEntry(tag, "noAwayPopup", &prefs.noAwayPopup);
+				readBoolEntry(tag, "noUnlistedPopup", &prefs.noUnlistedPopup);
+				readBoolEntry(tag, "raise", &prefs.raise);
+				readNumEntry(tag, "incomingAs", &prefs.incomingAs);
+			}
+
+			tag = findSubTag(p_events, "priority", &found);
+			if(found) {
+				readNumEntry(tag, "message",  &prefs.eventPriorityMessage);
+				readNumEntry(tag, "chat",     &prefs.eventPriorityChat);
+				readNumEntry(tag, "headline", &prefs.eventPriorityHeadline);
+				readNumEntry(tag, "auth",     &prefs.eventPriorityAuth);
+				readNumEntry(tag, "file",     &prefs.eventPriorityFile);
+ 			}
+		}
+
+		QDomElement p_pres = findSubTag(p, "presence", &found);
+		if(found) {
+			bool found;
+
+			QDomElement tag = findSubTag(p_pres, "misc", &found);
+			if(found) {
+				readBoolEntry(tag, "askOnline", &prefs.askOnline);
+				readBoolEntry(tag, "askOffline", &prefs.askOffline);
+				readBoolEntry(tag, "rosterAnim", &prefs.rosterAnim);
+				readBoolEntry(tag, "autoVersion", &prefs.autoVersion);
+				readBoolEntry(tag, "autoVCardOnLogin", &prefs.autoVCardOnLogin);
+				readBoolEntry(tag, "xmlConsoleOnLogin", &prefs.xmlConsoleOnLogin);
+			}
+
+			tag = findSubTag(p_pres, "autostatus", &found);
+			if(found) {
+				bool found;
+				QDomElement e;
+				e = findSubTag(tag, "away", &found);
+				if(found) {
+					if(e.hasAttribute("use"))
+						readBoolAttribute(e, "use", &prefs.use_asAway);
+					else
+						prefs.use_asAway = TRUE;
+				}
+				e = findSubTag(tag, "xa", &found);
+				if(found) {
+					if(e.hasAttribute("use"))
+						readBoolAttribute(e, "use", &prefs.use_asXa);
+					else
+						prefs.use_asXa = TRUE;
+				}
+				e = findSubTag(tag, "offline", &found);
+				if(found) {
+					if(e.hasAttribute("use"))
+						readBoolAttribute(e, "use", &prefs.use_asOffline);
+					else
+						prefs.use_asOffline = TRUE;
+				}
+
+				readNumEntry(tag, "away", &prefs.asAway);
+				readNumEntry(tag, "xa", &prefs.asXa);
+				readNumEntry(tag, "offline", &prefs.asOffline);
+
+				readEntry(tag, "message", &prefs.asMessage);
+			}
+
+			tag = findSubTag(p_pres, "statuspresets", &found);
+			if(found)
+				prefs.sp.fromXml(tag);
+			xmlToStringList(p_pres, "recentstatus", &prefs.recentStatus);
+		}
+
+		QDomElement p_lnf = findSubTag(p, "lookandfeel", &found);
+		if(found) {
+			bool found;
+
+			readBoolEntry(p_lnf, "newHeadings", &prefs.clNewHeadings);
+			readBoolEntry(p_lnf, "outline-headings", &prefs.outlineHeadings);
+			readNumEntry(p_lnf, "chat-opacity", &prefs.chatOpacity);
+			readNumEntry(p_lnf, "roster-opacity", &prefs.rosterOpacity);
+
+			QDomElement tag = findSubTag(p_lnf, "colors", &found);
+			if(found) {
+				readColorEntry(tag, "online", &prefs.color[cOnline]);
+				readColorEntry(tag, "listback", &prefs.color[cListBack]);
+				readColorEntry(tag, "away", &prefs.color[cAway]);
+				readColorEntry(tag, "dnd", &prefs.color[cDND]);
+				readColorEntry(tag, "offline", &prefs.color[cOffline]);
+				readColorEntry(tag, "groupfore", &prefs.color[cGroupFore]);
+				readColorEntry(tag, "groupback", &prefs.color[cGroupBack]);
+				readColorEntry(tag, "profilefore", &prefs.color[cProfileFore]);
+				readColorEntry(tag, "profileback", &prefs.color[cProfileBack]);
+				readColorEntry(tag, "animfront", &prefs.color[cAnimFront]);
+				readColorEntry(tag, "animback", &prefs.color[cAnimBack]);
+			}
+
+			tag = findSubTag(p_lnf, "fonts", &found);
+			if(found) {
+				readEntry(tag, "roster", &prefs.font[fRoster]);
+				readEntry(tag, "message", &prefs.font[fMessage]);
+				readEntry(tag, "chat", &prefs.font[fChat]);
+				readEntry(tag, "popup", &prefs.font[fPopup]);
+			}
+		}
+
+		QDomElement p_sound = findSubTag(p, "sound", &found);
+		if(found) {
+			bool found;
+
+			readEntry(p_sound, "player", &prefs.player);
+			readBoolEntry(p_sound, "noawaysound", &prefs.noAwaySound);
+			readBoolEntry(p_sound, "noGCSound", &prefs.noGCSound);
+
+			QDomElement tag = findSubTag(p_sound, "onevent", &found);
+			if(found) {
+				readEntry(tag, "message", &prefs.onevent[eMessage]);
+				readEntry(tag, "chat1", &prefs.onevent[eChat1]);
+				readEntry(tag, "chat2", &prefs.onevent[eChat2]);
+				readEntry(tag, "system", &prefs.onevent[eSystem]);
+				readEntry(tag, "headline", &prefs.onevent[eHeadline]);
+				readEntry(tag, "online", &prefs.onevent[eOnline]);
+				readEntry(tag, "offline", &prefs.onevent[eOffline]);
+				readEntry(tag, "send", &prefs.onevent[eSend]);
+				readEntry(tag, "incoming_ft", &prefs.onevent[eIncomingFT]);
+				readEntry(tag, "ft_complete", &prefs.onevent[eFTComplete]);
+			}
+		}
+
+		QDomElement p_sizes = findSubTag(p, "sizes", &found);
+		if(found) {
+			readSizeEntry(p_sizes, "eventdlg", &prefs.sizeEventDlg);
+			readSizeEntry(p_sizes, "chatdlg", &prefs.sizeChatDlg);
+			readSizeEntry(p_sizes, "tabdlg", &prefs.sizeTabDlg);
+		}
+
+
+		QDomElement p_toolbars = findSubTag(p, "toolbars", &found);
+		if (found) {
+			QStringList goodTags;
+			goodTags << "toolbar";
+			goodTags << "mainWin";
+
+			bool mainWinCleared = false;
+			bool oldStyle = true;
+
+			for(QDomNode n = p_toolbars.firstChild(); !n.isNull(); n = n.nextSibling()) {
+				QDomElement e = n.toElement();
+				if( e.isNull() )
+					continue;
+
+				QString tbGroup;
+				bool isGood = false;
+				QStringList::Iterator it = goodTags.begin();
+				for ( ; it != goodTags.end(); ++it ) {
+					if ( e.tagName().left( (*it).length() ) == *it ) {
+						isGood = true;
+
+						if ( e.tagName().left(7) == "toolbar" )
+							tbGroup = "mainWin";
+						else {
+							tbGroup = *it;
+							oldStyle = false;
+						}
+
+						break;
+					}
+				}
+
+				if ( isGood ) {
+					if ( tbGroup != "mainWin" || !mainWinCleared ) {
+						prefs.toolbars[tbGroup].clear();
+						if ( tbGroup == "mainWin" )
+							mainWinCleared = true;
+					}
+
+					if ( oldStyle ) {
+						Options::ToolbarPrefs tb = loadToolbarData( e );
+						prefs.toolbars[tbGroup].append(tb);
+					}
+					else {
+						for(QDomNode nn = e.firstChild(); !nn.isNull(); nn = nn.nextSibling()) {
+							QDomElement ee = nn.toElement();
+							if( ee.isNull() )
+								continue;
+
+							if ( ee.tagName() == "toolbar" ) {
+								Options::ToolbarPrefs tb = loadToolbarData( ee );
+								prefs.toolbars[tbGroup].append(tb);
+							}
+						}
+					}
+				}
+			}
+
+			// event notifier in these versions was not implemented as an action, so add it
+			if ( progver == "0.9" || progver == "0.9-CVS" ) {
+				// at first, we need to scan the options, to determine, whether event_notifier already available
+				bool found = false;
+				QValueList<Options::ToolbarPrefs>::Iterator it = prefs.toolbars["mainWin"].begin();
+				for ( ; it != prefs.toolbars["mainWin"].end(); ++it) {
+					QStringList::Iterator it2 = (*it).keys.begin();
+					for ( ; it2 != (*it).keys.end(); ++it2) {
+						if ( *it2 == "event_notifier" ) {
+							found = true;
+							break;
+						}
+					}
+				}
+
+				if ( !found ) {
+					Options::ToolbarPrefs tb;
+					tb.name = QObject::tr("Event notifier");
+					tb.on = false;
+					tb.locked = true;
+					tb.stretchable = true;
+					tb.keys << "event_notifier";
+					tb.dock  = Qt::DockBottom;
+					tb.index = 0;
+					prefs.toolbars["mainWin"].append(tb);
+				}
+			}
+		}
+
+		//group chat
+		QDomElement p_groupchat = findSubTag(p, "groupchat", &found);
+		if (found) {
+			readBoolEntry(p_groupchat, "nickcoloring", &prefs.gcNickColoring);
+			readBoolEntry(p_groupchat, "highlighting", &prefs.gcHighlighting);
+			xmlToStringList(p_groupchat, "highlightwords", &prefs.gcHighlights);
+			xmlToStringList(p_groupchat, "nickcolors", &prefs.gcNickColors);
+		}
+
+		// Bouncing dock icon (Mac OS X)
+		QDomElement p_dock = findSubTag(p, "dock", &found);
+		if(found) {
+			if (p_dock.attribute("bounce") == "once") {
+				prefs.bounceDock = Options::BounceOnce;
+			}
+			else if (p_dock.attribute("bounce") == "forever") {
+				prefs.bounceDock = Options::BounceForever;
+			}
+			else if (p_dock.attribute("bounce") == "never") {
+				prefs.bounceDock = Options::NoBounce;
+			}
+		}
+
+		QDomElement p_popup = findSubTag(p, "popups", &found);
+		if(found) {
+			readBoolEntry(p_popup, "on", &prefs.ppIsOn);
+			readBoolEntry(p_popup, "online", &prefs.ppOnline);
+			readBoolEntry(p_popup, "offline", &prefs.ppOffline);
+			readBoolEntry(p_popup, "statusChange", &prefs.ppStatus);
+			readBoolEntry(p_popup, "message", &prefs.ppMessage);
+			readBoolEntry(p_popup, "chat", &prefs.ppChat);
+			readBoolEntry(p_popup, "headline", &prefs.ppHeadline);
+			readBoolEntry(p_popup, "file", &prefs.ppFile);
+			readNumEntry(p_popup,  "jidClip", &prefs.ppJidClip);
+			readNumEntry(p_popup,  "statusClip", &prefs.ppStatusClip);
+			readNumEntry(p_popup,  "textClip", &prefs.ppTextClip);
+			readNumEntry(p_popup,  "hideTime", &prefs.ppHideTime);
+			readColorEntry(p_popup, "borderColor", &prefs.ppBorderColor);
+		}
+
+		QDomElement p_lockdown = findSubTag(p, "lockdown", &found);
+		if(found) {
+			readBoolEntry(p_lockdown, "roster", &prefs.lockdown.roster);
+			readBoolEntry(p_lockdown, "options", &prefs.lockdown.options);
+			readBoolEntry(p_lockdown, "profiles", &prefs.lockdown.profiles);
+			readBoolEntry(p_lockdown, "services", &prefs.lockdown.services);
+			readBoolEntry(p_lockdown, "accounts", &prefs.lockdown.accounts);
+		}
+
+		QDomElement p_iconset = findSubTag(p, "iconset", &found);
+		if(found) {
+			readEntry(p_iconset, "system", &prefs.systemIconset);
+
+			QDomElement roster = findSubTag(p_iconset, "roster", &found);
+			if (found) {
+				readEntry(roster, "default", &prefs.defaultRosterIconset);
+
+				QDomElement service = findSubTag(roster, "service", &found);
+				if (found) {
+					prefs.serviceRosterIconset.clear();
+					for (QDomNode n = service.firstChild(); !n.isNull(); n = n.nextSibling()) {
+						QDomElement i = n.toElement();
+						if ( i.isNull() )
+							continue;
+
+						prefs.serviceRosterIconset[i.attribute("service")] = i.attribute("iconset");
+					}
+				}
+
+				QDomElement custom = findSubTag(roster, "custom", &found);
+				if (found) {
+					prefs.customRosterIconset.clear();
+					for (QDomNode n = custom.firstChild(); !n.isNull(); n = n.nextSibling()) {
+						QDomElement i = n.toElement();
+						if ( i.isNull() )
+							continue;
+
+						prefs.customRosterIconset[i.attribute("regExp")] = i.attribute("iconset");
+					}
+				}
+			}
+
+			QDomElement emoticons = findSubTag(p_iconset, "emoticons", &found);
+			if (found) {
+				prefs.emoticons.clear();
+				for (QDomNode n = emoticons.firstChild(); !n.isNull(); n = n.nextSibling()) {
+					QDomElement i = n.toElement();
+					if ( i.isNull() )
+						continue;
+
+					if ( i.tagName() == "item" ) {
+						QString is = i.text();
+						prefs.emoticons << is;
+					}
+				}
+			}
+		}
+
+		QDomElement p_tip = findSubTag(p, "tipOfTheDay", &found);
+		if (found) {
+			readBoolEntry(p_tip, "show", &prefs.showTips);
+			readNumEntry(p_tip, "num", &prefs.tipNum);
+		}
+
+		QDomElement p_disco = findSubTag(p, "disco", &found);
+		if (found) {
+			readBoolEntry(p_disco, "items", &prefs.discoItems);
+			readBoolEntry(p_disco, "info", &prefs.discoInfo);
+		}
+
+		QDomElement p_dt = findSubTag(p, "dt", &found);
+		if (found) {
+			readNumEntry(p_dt, "port", &prefs.dtPort);
+			readEntry(p_dt, "external", &prefs.dtExternal);
+		}
+
+		QDomElement p_globalAccel = findSubTag(p, "globalAccel", &found);
+		if (found) {
+			QMap<QString, int> accelNames;
+			accelNames["processNextEvent"] = 0;
+			accelNames["showHideRoster"] = 1;
+
+			for (QDomNode n = p_globalAccel.firstChild(); !n.isNull(); n = n.nextSibling()) {
+				QDomElement i = n.toElement();
+				if ( i.isNull() )
+					continue;
+
+				if ( i.tagName() == "command" && i.hasAttribute("type") ) {
+					int n = accelNames[i.attribute("type")];
+					prefs.globalAccels[n] = QKeySequence(i.text());
+				}
+			}
+		}
+
+		QDomElement p_advWidget = findSubTag(p, "advancedWidget", &found);
+		if (found) {
+			QDomElement stick = findSubTag(p_advWidget, "sticky", &found);
+			if (found) {
+				bool enabled, toWindows;
+				int  offs;
+
+				readBoolAttribute(stick, "enabled", &enabled);
+				readNumEntry(stick, "offset", &offs);
+				readBoolEntry(stick, "stickToWindows", &toWindows);
+
+				GAdvancedWidget::setStickEnabled( enabled );
+				GAdvancedWidget::setStickAt( offs );
+				GAdvancedWidget::setStickToWindows( toWindows );
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+
+QString pathToProfile(const QString &name)
+{
+	return g.pathProfiles + "/" + name;
+}
+
+QString pathToProfileConfig(const QString &name)
+{
+	return pathToProfile(name) + "/config.xml";
+}
+
+QStringList getProfilesList()
+{
+	QStringList list;
+
+	QDir d(g.pathProfiles);
+	if(!d.exists())
+		return list;
+
+	QStringList entries = d.entryList();
+	for(QStringList::Iterator it = entries.begin(); it != entries.end(); ++it) {
+		if(*it == "." || *it == "..")
+			continue;
+		QFileInfo info(d, *it);
+		if(!info.isDir())
+			continue;
+
+		list.append(*it);
+	}
+
+	list.sort();
+
+	return list;
+}
+
+bool profileExists(const QString &_name)
+{
+	QString name = _name.lower();
+
+	QStringList list = getProfilesList();
+	for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+		if((*it).lower() == name)
+			return TRUE;
+	}
+	return FALSE;
+}
+
+bool profileNew(const QString &name)
+{
+	if(name.isEmpty())
+		return FALSE;
+
+	// verify the string is sane
+	for(int n = 0; n < (int)name.length(); ++n) {
+		if(!name.at(n).isLetterOrNumber())
+			return FALSE;
+	}
+
+	// make it
+	QDir d(g.pathProfiles);
+	if(!d.exists())
+		return FALSE;
+	QDir p(g.pathProfiles + "/" + name);
+	if(!p.exists()) {
+		d.mkdir(name);
+		if(!p.exists())
+			return FALSE;
+	}
+
+	p.mkdir("history");
+	p.mkdir("vcard");
+
+	return TRUE;
+}
+
+bool profileRename(const QString &oldname, const QString &name)
+{
+	// verify the string is sane
+	for(int n = 0; n < (int)name.length(); ++n) {
+		if(!name.at(n).isLetterOrNumber())
+			return FALSE;
+	}
+
+	// locate the folder
+	QDir d(g.pathProfiles);
+	if(!d.exists())
+		return FALSE;
+	if(!d.rename(oldname, name))
+		return FALSE;
+
+	return TRUE;
+}
+
+static bool folderRemove(const QDir &_d)
+{
+	QDir d = _d;
+
+	QStringList entries = d.entryList();
+	for(QStringList::Iterator it = entries.begin(); it != entries.end(); ++it) {
+		if(*it == "." || *it == "..")
+			continue;
+		QFileInfo info(d, *it);
+		if(info.isDir()) {
+			if(!folderRemove(QDir(info.filePath())))
+				return FALSE;
+		}
+		else {
+			//printf("deleting [%s]\n", info.filePath().latin1());
+			d.remove(info.fileName());
+		}
+	}
+	QString name = d.dirName();
+	if(!d.cdUp())
+		return FALSE;
+	//printf("removing folder [%s]\n", d.filePath(name).latin1());
+	d.rmdir(name);
+
+	return TRUE;
+}
+
+bool profileDelete(const QString &path)
+{
+	QDir d(path);
+	if(!d.exists())
+		return TRUE;
+
+	return folderRemove(QDir(path));
+}
Index: psi/tags/0.10-os2-b1/src/psiaccount.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psiaccount.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psiaccount.cpp	(revision 38)
@@ -0,0 +1,4040 @@
+/*
+ * psiaccount.cpp - handles a Psi account
+ * Copyright (C) 2001-2005  Justin Karneges
+ *
+ * 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.
+ *
+ * You can also redistribute and/or modify this program under the
+ * terms of the Psi License, specified in the accompanied COPYING
+ * file, as published by the Psi Project; either dated January 1st,
+ * 2005, 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"psiaccount.h"
+
+#include<qinputdialog.h>
+#include<qptrlist.h>
+#include<qcstring.h>
+#include<qtimer.h>
+#include<qmessagebox.h>
+#include<qguardedptr.h>
+#include<qapplication.h>
+#include<qpushbutton.h>
+#include<qlayout.h>
+#include<qobjectlist.h>
+#include<qurl.h>
+#include<qmap.h>
+#include<qca.h>
+#include<qfileinfo.h>
+
+#include"psicon.h"
+#include"profiles.h"
+#include"im.h"
+//#include"xmpp_client.h"
+//#include"xmpp_stream.h"
+//#include"xmpp_message.h"
+#include"xmpp_tasks.h"
+#include"xmpp_jidlink.h"
+#include"s5b.h"
+#include"filetransfer.h"
+#include"accountdlg.h"
+#include"changepwdlg.h"
+#include"xmlconsole.h"
+#include"userlist.h"
+#include"eventdlg.h"
+#include"chatdlg.h"
+#include"contactview.h"
+#include"groupchatdlg.h"
+#include"statusdlg.h"
+#include"infodlg.h"
+#include"adduserdlg.h"
+#include"historydlg.h"
+#include"servicesdlg.h"
+#include"discodlg.h"
+#include"eventdb.h"
+#include"jltest.h"
+#include"passphrasedlg.h"
+#include"vcardfactory.h"
+//#include"qssl.h"
+#include"sslcertdlg.h"
+#include"qwextend.h"
+#include"psipopup.h"
+#include"fancylabel.h"
+#include"iconwidget.h"
+#include"base64.h"
+#include"filetransdlg.h"
+#include"avatars.h"
+#include"tabdlg.h"
+
+#if defined(Q_WS_MAC) && defined(HAVE_GROWL)
+#include "psigrowlnotifier.h"
+#endif
+
+#include"bsocket.h"
+/*#ifdef Q_WS_WIN
+#include<windows.h>
+typedef int socklen_t;
+#else
+#include<sys/socket.h>
+#include<netinet/in.h>
+#endif*/
+
+//----------------------------------------------------------------------------
+// AccountLabel
+//----------------------------------------------------------------------------
+AccountLabel::AccountLabel(PsiAccount *_pa, QWidget *par, bool smode)
+:QLabel(par)
+{
+	pa = _pa;
+	simpleMode = smode;
+	setFrameStyle( QFrame::Panel | QFrame::Sunken );
+
+	updateName();
+	connect(pa, SIGNAL(updatedAccount()), this, SLOT(updateName()));
+	connect(pa, SIGNAL(destroyed()), this, SLOT(deleteMe()));
+}
+
+AccountLabel::~AccountLabel()
+{
+}
+
+void AccountLabel::updateName()
+{
+	setText(simpleMode ? pa->name() : pa->nameWithJid());
+}
+
+void AccountLabel::deleteMe()
+{
+	delete this;
+}
+
+
+//----------------------------------------------------------------------------
+// PGPTransaction
+//----------------------------------------------------------------------------
+static int transid = 0;
+class PGPTransaction::Private
+{
+public:
+	Private() {}
+
+	int id;
+	Message m;
+	QDomElement e;
+	Jid j;
+};
+
+PGPTransaction::PGPTransaction(OpenPGP::Engine *pgp)
+:OpenPGP::Request(pgp)
+{
+	d = new Private;
+	d->id = transid++;
+}
+
+PGPTransaction::~PGPTransaction()
+{
+	delete d;
+}
+
+int PGPTransaction::id() const
+{
+	return d->id;
+}
+
+void PGPTransaction::setMessage(const Message &m)
+{
+	d->m = m;
+}
+
+const Message & PGPTransaction::message() const
+{
+	return d->m;
+}
+
+const QDomElement & PGPTransaction::xml() const
+{
+	return d->e;
+}
+
+void PGPTransaction::setXml(const QDomElement &e)
+{
+	d->e = e;
+}
+
+Jid PGPTransaction::jid() const
+{
+	return d->j;
+}
+
+void PGPTransaction::setJid(const Jid &j)
+{
+	d->j = j;
+}
+
+//----------------------------------------------------------------------------
+// BlockTransportPopup -- blocks popups on transport status changes
+//----------------------------------------------------------------------------
+
+class BlockTransportPopupList;
+
+class BlockTransportPopup : public QObject
+{
+	Q_OBJECT
+public:
+	BlockTransportPopup(QObject *, const Jid &);
+
+	Jid jid() const;
+
+private slots:
+	void timeout();
+
+private:
+	Jid j;
+	int userCounter;
+	friend class BlockTransportPopupList;
+};
+
+BlockTransportPopup::BlockTransportPopup(QObject *parent, const Jid &_j)
+: QObject(parent)
+{
+	j = _j;
+	userCounter = 0;
+
+	// Hack for ICQ SMS
+	if ( j.host().left(3) == "icq" ) {
+		new BlockTransportPopup(parent, "sms." + j.host()); // sms.icq.host.com
+		new BlockTransportPopup(parent, "sms"  + j.host().right(j.host().length() - 3)); // sms.host.com
+	}
+
+	QTimer::singleShot(15000, this, SLOT(timeout()));
+}
+
+void BlockTransportPopup::timeout()
+{
+	if ( userCounter > 1 ) {
+		QTimer::singleShot(15000, this, SLOT(timeout()));
+		userCounter = 0;
+	}
+	else
+		deleteLater();
+}
+
+Jid BlockTransportPopup::jid() const
+{
+	return j;
+}
+
+//----------------------------------------------------------------------------
+// BlockTransportPopupList
+//----------------------------------------------------------------------------
+
+class BlockTransportPopupList : public QObject
+{
+	Q_OBJECT
+public:
+	BlockTransportPopupList();
+
+	bool find(const Jid &, bool online = false);
+};
+
+BlockTransportPopupList::BlockTransportPopupList()
+: QObject()
+{
+}
+
+bool BlockTransportPopupList::find(const Jid &j, bool online)
+{
+	if ( j.user().isEmpty() ) // always show popups for transports
+		return false;
+
+	QObjectList *list = queryList("BlockTransportPopup");
+	QObjectListIt it( *list );
+
+	BlockTransportPopup *btp;
+	for ( ; it.current(); ++it) {
+		btp = (BlockTransportPopup *)it.current();
+		if ( j.host() == btp->jid().host() ) {
+			if ( online )
+				btp->userCounter++;
+			return true;
+		}
+	}
+	delete list;
+
+	return false;
+}
+
+//----------------------------------------------------------------------------
+// PsiAccount
+//----------------------------------------------------------------------------
+struct item_dialog2
+{
+	QWidget *widget;
+	QString className;
+	Jid jid;
+};
+
+QMap<QString,QString> pgp_passphrases;
+
+class PsiAccount::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(PsiAccount *parent) {
+		account = parent;
+	}
+
+	PsiCon *psi;
+	PsiAccount *account;
+	Client *client;
+	ContactProfile *cp;
+	UserAccount acc, accnext;
+	Jid jid, nextJid;
+	Status loginStatus;
+	QPtrList<item_dialog2> dialogList;
+	EventQueue *eventQueue;
+	XmlConsole *xmlConsole;
+	UserList userList;
+	UserListItem self;
+	int lastIdle;
+	Status lastStatus, origStatus;
+	bool nickFromVCard;
+	QString cur_pgpSecretKeyID;
+	QPtrList<Message> messageQueue;
+	BlockTransportPopupList *blockTransportPopupList;
+	int userCounter;
+
+	// Avatars
+	AvatarFactory* avatarFactory;
+
+	// pgp related
+	PassphraseDlg *ppdlg;
+	QGuardedPtr<OpenPGP::Request> ppreq;
+
+	QPtrList<GCContact> gcbank;
+	QStringList groupchats;
+
+	AdvancedConnector *conn;
+	ClientStream *stream;
+	QCA::TLS *tls;
+	QCATLSHandler *tlsHandler;
+	QPtrList<QCA::Cert> certList;
+	bool usingSSL;
+	bool doPopups;
+
+	QHostAddress localAddress;
+
+	QString pathToProfileEvents()
+	{
+		return pathToProfile(activeProfile) + "/events-" + acc.name + ".xml";
+	}
+
+public slots:
+	void queueChanged()
+	{
+		eventQueue->toFile(pathToProfileEvents());
+	}
+
+	void loadQueue()
+	{
+		bool soundEnabled = useSound;
+		useSound = FALSE; // disable the sound and popups
+		doPopups = FALSE;
+
+		QFileInfo fi( pathToProfileEvents() );
+		if ( fi.exists() )
+			eventQueue->fromFile(pathToProfileEvents());
+
+		useSound = soundEnabled;
+		doPopups = TRUE;
+	}
+
+	void setEnabled( bool e )
+	{
+		acc.opt_enabled = e;
+		psi->enableAccount(account, e);
+		cp->setEnabled(e);
+		account->cpUpdate(self);
+
+		// signals
+		account->enabledChanged();
+		account->updatedAccount();
+	}
+};
+
+PsiAccount::PsiAccount(const UserAccount &acc, PsiCon *parent)
+:QObject(0)
+{
+	d = new Private( this );
+	d->psi = parent;
+	d->client = 0;
+	d->cp = 0;
+	d->userCounter = 0;
+
+#ifdef AVATARS
+	d->avatarFactory = new AvatarFactory(this);
+#endif
+
+	d->blockTransportPopupList = new BlockTransportPopupList();
+
+	d->doPopups = true;
+	v_isActive = false;
+	isDisconnecting = false;
+	notifyOnlineOk = false;
+	doReconnect = false;
+	usingAutoStatus = false;
+	presenceSent = false;
+
+	d->loginStatus = Status("", "");
+	d->lastIdle = 0;
+	d->lastStatus = Status("", "", 0, false);
+
+	d->dialogList.setAutoDelete(true);
+	d->eventQueue = new EventQueue(this);
+	connect(d->eventQueue, SIGNAL(queueChanged()), SIGNAL(queueChanged()));
+	connect(d->eventQueue, SIGNAL(queueChanged()), d, SLOT(queueChanged()));
+	connect(d->eventQueue, SIGNAL(handleEvent(PsiEvent *)), SLOT(handleEvent(PsiEvent *)));
+	d->userList.setAutoDelete(true);
+	d->self = UserListItem(true);
+	d->self.setSubscription(Subscription::Both);
+	d->nickFromVCard = false;
+
+#ifdef AVATARS
+	d->self.setAvatarFactory(d->avatarFactory);
+#endif
+
+	d->messageQueue.setAutoDelete(true);
+	d->ppdlg = 0;
+	d->ppreq = 0;
+
+	d->gcbank.setAutoDelete(true);
+
+	// we need to copy groupState, because later initialization will depend on that
+	d->acc.groupState = acc.groupState;
+
+	// stream
+	d->conn = 0;
+	d->tls = 0;
+	d->tlsHandler = 0;
+	d->stream = 0;
+	d->certList.setAutoDelete(true);
+	d->usingSSL = false;
+
+	// create Jabber::Client
+	d->client = new Client;
+	d->client->setOSName(getOSName());
+	d->client->setTimeZone(getTZString(), getTZOffset());
+	d->client->setClientName(PROG_NAME);
+	d->client->setClientVersion(PROG_VERSION);
+
+	d->client->setFileTransferEnabled(true);
+
+	//connect(d->client, SIGNAL(connected()), SLOT(client_connected()));
+	//connect(d->client, SIGNAL(handshaken()), SLOT(client_handshaken()));
+	//connect(d->client, SIGNAL(error(const StreamError &)), SLOT(client_error(const StreamError &)));
+	//connect(d->client, SIGNAL(sslCertReady(const QSSLCert &)), SLOT(client_sslCertReady(const QSSLCert &)));
+	//connect(d->client, SIGNAL(closeFinished()), SLOT(client_closeFinished()));
+	//connect(d->client, SIGNAL(authFinished(bool, int, const QString &)), SLOT(client_authFinished(bool, int, const QString &)));
+	connect(d->client, SIGNAL(rosterRequestFinished(bool, int, const QString &)), SLOT(client_rosterRequestFinished(bool, int, const QString &)));
+	connect(d->client, SIGNAL(rosterItemAdded(const RosterItem &)), SLOT(client_rosterItemAdded(const RosterItem &)));
+	connect(d->client, SIGNAL(rosterItemAdded(const RosterItem &)), SLOT(client_rosterItemUpdated(const RosterItem &)));
+	connect(d->client, SIGNAL(rosterItemUpdated(const RosterItem &)), SLOT(client_rosterItemUpdated(const RosterItem &)));
+	connect(d->client, SIGNAL(rosterItemRemoved(const RosterItem &)), SLOT(client_rosterItemRemoved(const RosterItem &)));
+	connect(d->client, SIGNAL(resourceAvailable(const Jid &, const Resource &)), SLOT(client_resourceAvailable(const Jid &, const Resource &)));
+	connect(d->client, SIGNAL(resourceUnavailable(const Jid &, const Resource &)), SLOT(client_resourceUnavailable(const Jid &, const Resource &)));
+	connect(d->client, SIGNAL(presenceError(const Jid &, int, const QString &)), SLOT(client_presenceError(const Jid &, int, const QString &)));
+	connect(d->client, SIGNAL(messageReceived(const Message &)), SLOT(client_messageReceived(const Message &)));
+	connect(d->client, SIGNAL(subscription(const Jid &, const QString &)), SLOT(client_subscription(const Jid &, const QString &)));
+	connect(d->client, SIGNAL(debugText(const QString &)), SLOT(client_debugText(const QString &)));
+	connect(d->client, SIGNAL(groupChatJoined(const Jid &)), SLOT(client_groupChatJoined(const Jid &)));
+	connect(d->client, SIGNAL(groupChatLeft(const Jid &)), SLOT(client_groupChatLeft(const Jid &)));
+	connect(d->client, SIGNAL(groupChatPresence(const Jid &, const Status &)), SLOT(client_groupChatPresence(const Jid &, const Status &)));
+	connect(d->client, SIGNAL(groupChatError(const Jid &, int, const QString &)), SLOT(client_groupChatError(const Jid &, int, const QString &)));
+	connect(d->client, SIGNAL(incomingJidLink()), SLOT(client_incomingJidLink()));
+	connect(d->client->fileTransferManager(), SIGNAL(incomingReady()), SLOT(client_incomingFileTransfer()));
+
+	// contactprofile context
+	d->cp = new ContactProfile(this, acc.name, d->psi->contactView());
+	connect(d->cp, SIGNAL(actionDefault(const Jid &)),SLOT(actionDefault(const Jid &)));
+	connect(d->cp, SIGNAL(actionRecvEvent(const Jid &)),SLOT(actionRecvEvent(const Jid &)));
+	connect(d->cp, SIGNAL(actionSendMessage(const Jid &)),SLOT(actionSendMessage(const Jid &)));
+	connect(d->cp, SIGNAL(actionSendMessage(const JidList &)),SLOT(actionSendMessage(const JidList &)));
+	connect(d->cp, SIGNAL(actionSendUrl(const Jid &)),SLOT(actionSendUrl(const Jid &)));
+	connect(d->cp, SIGNAL(actionRemove(const Jid &)),SLOT(actionRemove(const Jid &)));
+	connect(d->cp, SIGNAL(actionRename(const Jid &, const QString &)),SLOT(actionRename(const Jid &, const QString &)));
+	connect(d->cp, SIGNAL(actionGroupRename(const QString &, const QString &)),SLOT(actionGroupRename(const QString &, const QString &)));
+	connect(d->cp, SIGNAL(actionHistory(const Jid &)),SLOT(actionHistory(const Jid &)));
+	connect(d->cp, SIGNAL(actionOpenChat(const Jid &)),SLOT(actionOpenChat(const Jid &)));
+	connect(d->cp, SIGNAL(actionOpenChatSpecific(const Jid &)),SLOT(actionOpenChatSpecific(const Jid &)));
+	connect(d->cp, SIGNAL(actionAgentSetStatus(const Jid &, Status &)),SLOT(actionAgentSetStatus(const Jid &, Status &)));
+	connect(d->cp, SIGNAL(actionInfo(const Jid &)),SLOT(actionInfo(const Jid &)));
+	connect(d->cp, SIGNAL(actionAuth(const Jid &)),SLOT(actionAuth(const Jid &)));
+	connect(d->cp, SIGNAL(actionAuthRequest(const Jid &)),SLOT(actionAuthRequest(const Jid &)));
+	connect(d->cp, SIGNAL(actionAuthRemove(const Jid &)),SLOT(actionAuthRemove(const Jid &)));
+	connect(d->cp, SIGNAL(actionAdd(const Jid &)),SLOT(actionAdd(const Jid &)));
+	connect(d->cp, SIGNAL(actionGroupAdd(const Jid &, const QString &)),SLOT(actionGroupAdd(const Jid &, const QString &)));
+	connect(d->cp, SIGNAL(actionGroupRemove(const Jid &, const QString &)),SLOT(actionGroupRemove(const Jid &, const QString &)));
+	connect(d->cp, SIGNAL(actionTest(const Jid &)),SLOT(actionTest(const Jid &)));
+	connect(d->cp, SIGNAL(actionSendFile(const Jid &)),SLOT(actionSendFile(const Jid &)));
+	connect(d->cp, SIGNAL(actionSendFiles(const Jid &, const QStringList&)),SLOT(actionSendFiles(const Jid &, const QStringList&)));
+	connect(d->cp, SIGNAL(actionDisco(const Jid &, const QString &)),SLOT(actionDisco(const Jid &, const QString &)));
+	connect(d->cp, SIGNAL(actionInvite(const Jid &, const QString &)),SLOT(actionInvite(const Jid &, const QString &)));
+	connect(d->cp, SIGNAL(actionAssignKey(const Jid &)),SLOT(actionAssignKey(const Jid &)));
+	connect(d->cp, SIGNAL(actionUnassignKey(const Jid &)),SLOT(actionUnassignKey(const Jid &)));
+
+	// restore cached roster
+	for(Roster::ConstIterator it = acc.roster.begin(); it != acc.roster.end(); ++it)
+		client_rosterItemUpdated(*it);
+
+	// restore pgp key bindings
+	for(VarList::ConstIterator kit = acc.keybind.begin(); kit != acc.keybind.end(); ++kit) {
+		const VarListItem &i = *kit;
+		UserListItem *u = find(Jid(i.key()));
+		if(u) {
+			u->setPublicKeyID(i.data());
+			cpUpdate(*u);
+		}
+	}
+
+	setUserAccount(acc);
+
+	d->psi->link(this);
+	connect(d->psi, SIGNAL(emitOptionsUpdate()), SLOT(optionsUpdate()));
+	connect(d->psi, SIGNAL(pgpToggled(bool)), SLOT(pgpToggled(bool)));
+	connect(d->psi, SIGNAL(pgpKeysUpdated()), SLOT(pgpKeysUpdated()));
+
+	d->psi->setToggles(d->acc.tog_offline, d->acc.tog_away, d->acc.tog_agents, d->acc.tog_hidden,d->acc.tog_self);
+
+	d->setEnabled(d->acc.opt_enabled);
+
+	// auto-login ?
+	if(d->acc.opt_auto && d->acc.opt_enabled)
+		setStatus(Status("", "", d->acc.priority));
+
+	//printf("PsiAccount: [%s] loaded\n", name().latin1());
+	d->xmlConsole = new XmlConsole(this);
+	if(option.xmlConsoleOnLogin && d->acc.opt_enabled) {
+		this->showXmlConsole();
+		d->xmlConsole->enable();
+	}
+
+	// load event queue from disk
+	QTimer::singleShot(0, d, SLOT(loadQueue()));
+}
+
+PsiAccount::~PsiAccount()
+{
+	logout(true);
+	QString str = name();
+
+	d->messageQueue.clear();
+
+	// nuke all related dialogs
+	deleteAllDialogs();
+
+	d->psi->ftdlg()->killTransfers(this);
+
+	delete d->cp;
+	delete d->client;
+	cleanupStream();
+	delete d->eventQueue;
+
+	delete d->blockTransportPopupList;
+
+	d->psi->unlink(this);
+	delete d;
+
+	//printf("PsiAccount: [%s] unloaded\n", str.latin1());
+}
+
+void PsiAccount::cleanupStream()
+{
+	delete d->stream;
+	d->stream = 0;
+
+	delete d->tls;
+	d->tls = 0;
+	d->tlsHandler = 0;
+
+	delete d->conn;
+	d->conn = 0;
+
+	d->certList.clear();
+	d->usingSSL = false;
+
+	d->localAddress = QHostAddress();
+}
+
+bool PsiAccount::enabled() const
+{
+	return d->acc.opt_enabled;
+}
+
+void PsiAccount::setEnabled(bool e)
+{
+	if ( d->acc.opt_enabled == e )
+		return;
+		
+	if (!e) {
+		if (eventQueue()->count()) {
+			QMessageBox::information(0, tr("Error"), tr("Unable to disable the account, as it has pending events."));
+			return;
+		}
+		if (isActive()) {
+			if (QMessageBox::information(0, tr("Disable Account"), tr("The account is currently active.\nDo you want to log out ?"),QMessageBox::Yes,QMessageBox::No | QMessageBox::Default | QMessageBox::Escape, QMessageBox::NoButton) == QMessageBox::Yes) {
+				logout();
+			}
+			else {
+				return;
+			}
+		}
+	}
+
+	d->setEnabled( e );
+}
+
+bool PsiAccount::isActive() const
+{
+	return v_isActive;
+}
+
+bool PsiAccount::isConnected() const
+{
+	return (d->stream && d->stream->isAuthenticated());
+}
+
+const QString & PsiAccount::name() const
+{
+	return d->acc.name;
+}
+
+const UserAccount & PsiAccount::userAccount() const
+{
+	d->psi->getToggles(&d->acc.tog_offline, &d->acc.tog_away, &d->acc.tog_agents, &d->acc.tog_hidden,&d->acc.tog_self);
+
+	// save the roster and pgp key bindings
+	d->acc.roster.clear();
+	d->acc.keybind.clear();
+	UserListIt it(d->userList);
+	for(UserListItem *u; (u = it.current()); ++it) {
+		if(u->inList())
+			d->acc.roster += *u;
+
+		if(!u->publicKeyID().isEmpty())
+			d->acc.keybind.set(u->jid().full(), u->publicKeyID());
+	}
+
+	return d->acc;
+}
+
+UserList *PsiAccount::userList() const
+{
+	return &d->userList;
+}
+
+Client *PsiAccount::client() const
+{
+	return d->client;
+}
+
+ContactProfile *PsiAccount::contactProfile() const
+{
+	return d->cp;
+}
+
+EventQueue *PsiAccount::eventQueue() const
+{
+	return d->eventQueue;
+}
+
+EDB *PsiAccount::edb() const
+{
+	return d->psi->edb();
+}
+
+PsiCon *PsiAccount::psi() const
+{
+	return d->psi;
+}
+
+AvatarFactory *PsiAccount::avatarFactory() const
+{
+	return d->avatarFactory;
+}
+
+QString PsiAccount::pgpKey() const
+{
+	return d->cur_pgpSecretKeyID;
+}
+
+QHostAddress *PsiAccount::localAddress() const
+{
+	QString s = d->localAddress.toString();
+	if(s == "0.0.0.0")
+		return 0;
+	return &d->localAddress;
+}
+
+void PsiAccount::setUserAccount(const UserAccount &acc)
+{
+	bool renamed = false;
+	QString oldfname;
+	if(d->acc.name != acc.name) {
+		renamed = true;
+		oldfname = d->pathToProfileEvents();
+	}
+
+	d->acc = acc;
+
+	// rename queue file?
+	if(renamed) {
+		QFileInfo oldfi(oldfname);
+		QFileInfo newfi(d->pathToProfileEvents());
+		if(oldfi.exists()) {
+			QDir dir = oldfi.dir();
+			dir.rename(oldfi.fileName(), newfi.fileName());
+		}
+	}
+
+	if(d->stream) {
+		if(d->acc.opt_keepAlive)
+			d->stream->setNoopTime(55000);  // prevent NAT timeouts every minute
+		else
+			d->stream->setNoopTime(0);
+	}
+
+	d->cp->setName(d->acc.name);
+
+	Jid j = acc.jid;
+	d->nextJid = j;
+	if(!isActive()) {
+		d->jid = j;
+		d->self.setJid(j);
+		d->cp->updateEntry(d->self);
+	}
+	if(!d->nickFromVCard)
+		setNick(j.user());
+
+	d->self.setPublicKeyID(d->acc.pgpSecretKeyID);
+	if(d->psi->pgp()) {
+		if(d->acc.pgpSecretKeyID != d->cur_pgpSecretKeyID && loggedIn()) {
+			d->cur_pgpSecretKeyID = d->acc.pgpSecretKeyID;
+			d->loginStatus.setXSigned("");
+			setStatusDirect(d->loginStatus);
+			pgpKeyChanged();
+		}
+	}
+
+	cpUpdate(d->self);
+	updatedAccount();
+}
+
+void PsiAccount::deleteQueueFile()
+{
+	QFileInfo fi(d->pathToProfileEvents());
+	if(fi.exists()) {
+		QDir dir = fi.dir();
+		dir.remove(fi.fileName());
+	}
+}
+
+const Jid & PsiAccount::jid() const
+{
+	return d->jid;
+}
+
+QString PsiAccount::nameWithJid() const
+{
+	return (name() + " (" + jid().full() + ')');
+}
+
+static QCA::Cert readCertXml(const QDomElement &e)
+{
+	QCA::Cert cert;
+	// there should be one child data tag
+	QDomElement data = e.elementsByTagName("data").item(0).toElement();
+	if(!data.isNull())
+		cert.fromDER(Base64::stringToArray(data.text()));
+	return cert;
+}
+
+static QPtrList<QCA::Cert> getRootCerts(const QStringList &stores)
+{
+	QPtrList<QCA::Cert> list;
+
+	for(QStringList::ConstIterator dit = stores.begin(); dit != stores.end(); ++dit) {
+		QDir dir(*dit);
+		if(!dir.exists())
+			continue;
+		dir.setNameFilter("*.xml");
+		QStringList entries = dir.entryList();
+		for(QStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it) {
+			QFile f(dir.filePath(*it));
+			if(!f.open(IO_ReadOnly))
+				continue;
+			QDomDocument doc;
+			bool ok = doc.setContent(&f);
+			f.close();
+			if(!ok)
+				continue;
+
+			QDomElement base = doc.documentElement();
+			if(base.tagName() != "store")
+				continue;
+			QDomNodeList cl = base.elementsByTagName("certificate");
+
+			int num = 0;
+			for(int n = 0; n < (int)cl.count(); ++n) {
+				QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
+				if(cert->isNull()) {
+					delete cert;
+					continue;
+				}
+
+				++num;
+				list.append(cert);
+			}
+		}
+	}
+
+	return list;
+}
+
+// logs on with the active account settings
+void PsiAccount::login()
+{
+	if(isActive() && !doReconnect)
+		return;
+
+	if(d->acc.opt_ssl && !QCA::isSupported(QCA::CAP_TLS)) {
+		QMessageBox::information(0, tr("%1: SSL Error").arg(name()), tr("Cannot login: SSL is enabled but no SSL/TLS (plugin) support is available."));
+		return;
+	}
+
+	d->jid = d->nextJid;
+	if(d->psi->pgp()) {
+		d->cur_pgpSecretKeyID = d->acc.pgpSecretKeyID;
+		pgpKeyChanged();
+	}
+
+	v_isActive = true;
+	isDisconnecting = false;
+	notifyOnlineOk = false;
+	rosterDone = false;
+	presenceSent = false;
+
+	stateChanged();
+
+	QString host;
+	int port;
+	if(d->acc.opt_host) {
+		host = d->acc.host;
+		port = d->acc.port;
+	}
+	else {
+		host = d->jid.host();
+		if(d->acc.opt_ssl)
+			port = 5223;
+		else
+			port = 5222;
+	}
+
+	AdvancedConnector::Proxy p;
+	if(d->acc.proxy_index > 0) {
+		const ProxyItem &pi = d->psi->proxy()->getItem(d->acc.proxy_index-1);
+		if(pi.type == "http") // HTTP Connect
+			p.setHttpConnect(pi.settings.host, pi.settings.port);
+		else if(pi.type == "socks") // SOCKS
+			p.setSocks(pi.settings.host, pi.settings.port);
+		else if(pi.type == "poll") { // HTTP Poll
+			QUrl u = pi.settings.url;
+			if(u.query().isEmpty()) {
+				QString v = host + ':' + QString::number(port);
+				QUrl::encode(v);
+				u.setQuery(QString("server=") + v);
+			}
+			p.setHttpPoll(pi.settings.host, pi.settings.port, u.toString());
+			p.setPollInterval(2);
+		}
+
+		if(pi.settings.useAuth)
+			p.setUserPass(pi.settings.user, pi.settings.pass);
+	}
+
+	// stream
+	d->conn = new AdvancedConnector;
+	if(d->acc.opt_ssl) {
+		QStringList certDirs;
+		certDirs += g.pathHome + "/certs";
+		certDirs += g.pathBase + "/certs";
+		d->certList = getRootCerts(certDirs);
+
+		d->tls = new QCA::TLS;
+		d->tls->setCertificateStore(d->certList);
+		d->tlsHandler = new QCATLSHandler(d->tls);
+		connect(d->tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
+	}
+	d->conn->setProxy(p);
+	d->conn->setOptHostPort(host, port);
+	d->conn->setOptSSL(d->acc.opt_ssl);
+
+	d->stream = new ClientStream(d->conn, d->tlsHandler);
+	d->stream->setOldOnly(true);
+	d->stream->setAllowPlain(d->acc.opt_plain);
+	if(d->acc.opt_keepAlive)
+		d->stream->setNoopTime(55000);  // prevent NAT timeouts every minute
+	else
+		d->stream->setNoopTime(0);
+	connect(d->stream, SIGNAL(connected()), SLOT(cs_connected()));
+	connect(d->stream, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated()));
+	connect(d->stream, SIGNAL(needAuthParams(bool, bool, bool)), SLOT(cs_needAuthParams(bool, bool, bool)));
+	connect(d->stream, SIGNAL(authenticated()), SLOT(cs_authenticated()));
+	connect(d->stream, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
+	connect(d->stream, SIGNAL(delayedCloseFinished()), SLOT(cs_delayedCloseFinished()));
+	connect(d->stream, SIGNAL(warning(int)), SLOT(cs_warning(int)));
+	connect(d->stream, SIGNAL(error(int)), SLOT(cs_error(int)));
+
+	Jid j = d->jid.withResource(d->acc.resource);
+	d->client->connectToServer(d->stream, j);
+}
+
+// disconnect or stop reconnecting
+void PsiAccount::logout(bool fast, const Status &s)
+{
+	if(!isActive())
+		return;
+
+	// cancel reconnect
+	doReconnect = false;
+
+	if(loggedIn()) {
+		// send logout status
+		d->client->setPresence(s);
+	}
+
+	isDisconnecting = true;
+
+	if(!fast)
+		simulateRosterOffline();
+	v_isActive = false;
+	stateChanged();
+
+	QTimer::singleShot(0, this, SLOT(disconnect()));
+}
+
+// skz note: I had to split logout() because server seem to need some time to store status
+// before stream is closed (weird, I know)
+void PsiAccount::disconnect()
+{
+	// disconnect
+	d->client->close();
+	cleanupStream();
+
+	disconnected();
+}
+
+bool PsiAccount::loggedIn() const
+{
+	return (v_isActive && presenceSent);
+}
+
+void PsiAccount::tls_handshaken()
+{
+	QCA::Cert cert = d->tls->peerCertificate();
+	int r = d->tls->certificateValidityResult();
+	if(r != QCA::TLS::Valid && !d->acc.opt_ignoreSSLWarnings) {
+		QString str = resultToString(r);
+		while(1) {
+			int n = QMessageBox::warning(0,
+				tr("%1: Server Authentication").arg(name()),
+				tr("The %1 certificate failed the authenticity test.").arg(d->jid.host()) + '\n' + tr("Reason: %1").arg(str),
+				tr("&Details..."),
+				tr("Co&ntinue"),
+				tr("&Cancel"), 0, 2);
+			if(n == 0) {
+				SSLCertDlg::showCert(cert, r);
+			}
+			else if(n == 1) {
+				d->tlsHandler->continueAfterHandshake();
+				break;
+			}
+			else if(n == 2) {
+				logout();
+				break;
+			}
+		}
+	}
+	else
+		d->tlsHandler->continueAfterHandshake();
+}
+
+void PsiAccount::cs_connected()
+{
+	// get IP address
+	ByteStream *bs = d->conn->stream();
+	if(bs->inherits("BSocket") || bs->inherits("XMPP::BSocket")) {
+//#if QT_VERSION >= 0x030300
+		d->localAddress = ((BSocket *)bs)->address();
+/*#else
+		int s = ((BSocket *)bs)->socket();
+		struct sockaddr addr;
+		socklen_t size = sizeof(struct sockaddr);
+		if(!getsockname(s, &addr, &size)) {
+			Q_UINT32 ipv4addr;
+			struct sockaddr_in *in = (struct sockaddr_in *)&addr;
+			memcpy(&ipv4addr, &in->sin_addr.s_addr, 4);
+			ipv4addr = ntohl(ipv4addr);
+			d->localAddress = QHostAddress(ipv4addr);
+		}
+#endif*/
+	}
+}
+
+void PsiAccount::cs_securityLayerActivated()
+{
+	d->usingSSL = true;
+	stateChanged();
+}
+
+void PsiAccount::cs_needAuthParams(bool, bool pass, bool)
+{
+	if(pass)
+		d->stream->setPassword(d->acc.pass);
+	d->stream->continueAfterParams();
+}
+
+void PsiAccount::cs_authenticated()
+{
+	d->conn->changePollInterval(10); // for http poll, slow down after login
+
+	d->client->start(d->jid.host(), d->jid.user(), d->acc.pass, d->acc.resource);
+
+	//printf("PsiAccount: [%s] authenticated\n", name().latin1());
+
+	// flag roster for delete
+	UserListIt it(d->userList);
+	for(UserListItem *u; (u = it.current()); ++it) {
+		if(u->inList())
+			u->setFlagForDelete(true);
+	}
+
+	// ask for roster
+	d->client->rosterRequest();
+}
+
+void PsiAccount::cs_connectionClosed()
+{
+	cs_error(-1);
+}
+
+void PsiAccount::cs_delayedCloseFinished()
+{
+	//printf("PsiAccount: [%s] connection closed\n", name().latin1());
+}
+
+void PsiAccount::cs_warning(int)
+{
+	d->stream->continueAfterWarning();
+}
+
+void PsiAccount::getErrorInfo(int err, AdvancedConnector *conn, Stream *stream, QCATLSHandler *tlsHandler, QString *_str, bool *_reconn)
+{
+	QString str;
+	bool reconn = false;
+
+	if(err == -1) {
+		str = tr("Disconnected");
+		reconn = true;
+	}
+	else if(err == XMPP::ClientStream::ErrParse) {
+		str = tr("XML Parsing Error");
+		reconn = true;
+	}
+	else if(err == XMPP::ClientStream::ErrProtocol) {
+		str = tr("XMPP Protocol Error");
+		reconn = true;
+	}
+	else if(err == XMPP::ClientStream::ErrStream) {
+		int x = stream->errorCondition();
+		QString s;
+		reconn = true;
+		if(x == XMPP::Stream::GenericStreamError)
+			s = tr("Generic stream error");
+		else if(x == XMPP::ClientStream::Conflict) {
+			s = tr("Conflict (remote login replacing this one)");
+			reconn = false;
+		}
+		else if(x == XMPP::ClientStream::ConnectionTimeout)
+			s = tr("Timed out from inactivity");
+		else if(x == XMPP::ClientStream::InternalServerError)
+			s = tr("Internal server error");
+		else if(x == XMPP::ClientStream::InvalidXml)
+			s = tr("Invalid XML");
+		else if(x == XMPP::ClientStream::PolicyViolation) {
+			s = tr("Policy violation");
+			reconn = false;
+		}
+		else if(x == XMPP::ClientStream::ResourceConstraint) {
+			s = tr("Server out of resources");
+			reconn = false;
+		}
+		else if(x == XMPP::ClientStream::SystemShutdown)
+			s = tr("Server is shutting down");
+		str = tr("XMPP Stream Error: %1").arg(s);
+	}
+	else if(err == XMPP::ClientStream::ErrConnection) {
+		int x = conn->errorCode();
+		QString s;
+		reconn = true;
+		if(x == XMPP::AdvancedConnector::ErrConnectionRefused)
+			s = tr("Unable to connect to server");
+		else if(x == XMPP::AdvancedConnector::ErrHostNotFound)
+			s = tr("Host not found");
+		else if(x == XMPP::AdvancedConnector::ErrProxyConnect)
+			s = tr("Error connecting to proxy");
+		else if(x == XMPP::AdvancedConnector::ErrProxyNeg)
+			s = tr("Error during proxy negotiation");
+		else if(x == XMPP::AdvancedConnector::ErrProxyAuth) {
+			s = tr("Proxy authentication failed");
+			reconn = false;
+		}
+		else if(x == XMPP::AdvancedConnector::ErrStream)
+			s = tr("Socket/stream error");
+		str = tr("Connection Error: %1").arg(s);
+	}
+	else if(err == XMPP::ClientStream::ErrNeg) {
+		int x = stream->errorCondition();
+		QString s;
+		if(x == XMPP::ClientStream::HostGone)
+			s = tr("Host no longer hosted");
+		else if(x == XMPP::ClientStream::HostUnknown)
+			s = tr("Host unknown");
+		else if(x == XMPP::ClientStream::RemoteConnectionFailed) {
+			s = tr("A required remote connection failed");
+			reconn = true;
+		}
+		else if(x == XMPP::ClientStream::SeeOtherHost)
+			s = tr("See other host: %1").arg(stream->errorText());
+		else if(x == XMPP::ClientStream::UnsupportedVersion)
+			s = tr("Server does not support proper XMPP version");
+		str = tr("Stream Negotiation Error: %1").arg(s);
+	}
+	else if(err == XMPP::ClientStream::ErrTLS) {
+		int x = stream->errorCondition();
+		QString s;
+		if(x == XMPP::ClientStream::TLSStart)
+			s = tr("Server rejected STARTTLS");
+		else if(x == XMPP::ClientStream::TLSFail) {
+			int t = tlsHandler->tlsError();
+			if(t == QCA::TLS::ErrHandshake)
+				s = tr("TLS handshake error");
+			else
+				s = tr("Broken security layer (TLS)");
+		}
+		str = s;
+	}
+	else if(err == XMPP::ClientStream::ErrAuth) {
+		int x = stream->errorCondition();
+		QString s;
+		if(x == XMPP::ClientStream::GenericAuthError)
+			s = tr("Unable to login");
+		else if(x == XMPP::ClientStream::NoMech)
+			s = tr("No appropriate mechanism available for given security settings");
+		else if(x == XMPP::ClientStream::BadProto)
+			s = tr("Bad server response");
+		else if(x == XMPP::ClientStream::BadServ)
+			s = tr("Server failed mutual authentication");
+		else if(x == XMPP::ClientStream::EncryptionRequired)
+			s = tr("Encryption required for chosen SASL mechanism");
+		else if(x == XMPP::ClientStream::InvalidAuthzid)
+			s = tr("Invalid account information");
+		else if(x == XMPP::ClientStream::InvalidMech)
+			s = tr("Invalid SASL mechanism");
+		else if(x == XMPP::ClientStream::InvalidRealm)
+			s = tr("Invalid realm");
+		else if(x == XMPP::ClientStream::MechTooWeak)
+			s = tr("SASL mechanism too weak for this account");
+		else if(x == XMPP::ClientStream::NotAuthorized)
+			s = tr("Not authorized");
+		else if(x == XMPP::ClientStream::TemporaryAuthFailure)
+			s = tr("Temporary auth failure");
+		str = tr("Authentication error: %1").arg(s);
+	}
+	else if(err == XMPP::ClientStream::ErrSecurityLayer)
+		str = tr("Broken security layer (SASL)");
+	else
+		str = tr("None");
+	//printf("str[%s], reconn=%d\n", str.latin1(), reconn);
+	*_str = str;
+	*_reconn = reconn;
+}
+
+void PsiAccount::cs_error(int err)
+{
+	QString str;
+	bool reconn;
+
+	getErrorInfo(err, d->conn, d->stream, d->tlsHandler, &str, &reconn);
+
+	d->client->close();
+	cleanupStream();
+
+	//printf("Error: [%s]\n", str.latin1());
+
+	isDisconnecting = true;
+
+	if ( loggedIn() ) { // FIXME: is this condition okay?
+		simulateRosterOffline();
+	}
+
+	presenceSent = false; // this stops the idle detector?? (FIXME)
+
+	// Auto-Reconnect?
+	if(d->acc.opt_reconn && reconn) {
+		// reconnect in 5 seconds
+		doReconnect = true;
+		stateChanged();
+		QTimer::singleShot(5000, this, SLOT(reconnect()));
+		return;
+	}
+
+	v_isActive = false;
+	stateChanged();
+	disconnected();
+
+	QMessageBox::critical(0, tr("%1: Server Error").arg(name()), tr("There was an error communicating with the Jabber server.\nDetails: %1").arg(str));
+}
+
+void PsiAccount::client_rosterRequestFinished(bool success, int, const QString &)
+{
+	if(success) {
+		//printf("PsiAccount: [%s] roster retrieved ok.  %d entries.\n", name().latin1(), d->client->roster().count());
+
+		// delete flagged items
+		UserListIt it(d->userList);
+		for(UserListItem *u; (u = it.current());) {
+			if(u->flagForDelete()) {
+				//QMessageBox::information(0, "blah", QString("deleting: [%1]").arg(u->jid().full()));
+
+				d->eventQueue->clear(u->jid());
+				updateReadNext(u->jid());
+
+				d->cp->removeEntry(u->jid());
+				d->userList.removeRef(u);
+			}
+			else
+				++it;
+		}
+	}
+	else {
+		//printf("PsiAccount: [%s] error retrieving roster: [%d, %s]\n", name().latin1(), code, str.latin1());
+	}
+
+	rosterDone = true;
+	setStatusDirect(d->loginStatus);
+}
+
+void PsiAccount::resolveContactName()
+{
+	JT_VCard *j = (JT_VCard *)sender();
+	if ( j->success() ) {
+		QString nick = j->vcard().nickName();
+		if ( !nick.isEmpty() ) {
+			actionRename( j->jid(), nick );
+		}
+	}
+}
+
+void PsiAccount::client_rosterItemAdded(const RosterItem &r)
+{
+	if ( r.isPush() && r.name().isEmpty() && option.autoResolveNicksOnAdd ) {
+		// automatically resolve nickname from vCard, if newly added item doesn't have any
+		VCardFactory::getVCard(r.jid(), d->client->rootTask(), this, SLOT(resolveContactName()));
+	}
+}
+
+void PsiAccount::client_rosterItemUpdated(const RosterItem &r)
+{
+	// see if the item added is already in our local list
+	UserListItem *u = d->userList.find(r.jid());
+	if(u) {
+		u->setFlagForDelete(false);
+		u->setRosterItem(r);
+	}
+	else {
+		// we don't have it at all, so add it
+		u = new UserListItem;
+		u->setRosterItem(r);
+#ifdef AVATARS
+		u->setAvatarFactory(d->avatarFactory);
+#endif
+		d->userList.append(u);
+	}
+	u->setInList(true);
+
+	d->cp->updateEntry(*u);
+}
+
+void PsiAccount::client_rosterItemRemoved(const RosterItem &r)
+{
+	UserListItem *u = d->userList.find(r.jid());
+	if(!u)
+		return;
+
+	simulateContactOffline(u);
+
+	// if the item has messages queued, then move them to 'not in list'
+	if(d->eventQueue->count(r.jid()) > 0) {
+		u->setInList(false);
+		d->cp->updateEntry(*u);
+	}
+	// else remove them for good!
+	else {
+		d->cp->removeEntry(u->jid());
+		d->userList.removeRef(u);
+	}
+}
+
+void PsiAccount::tryVerify(UserListItem *u, UserResource *ur)
+{
+	if(d->psi->pgp())
+		verifyStatus(u->jid().withResource(ur->name()), ur->status());
+}
+
+void PsiAccount::client_resourceAvailable(const Jid &j, const Resource &r)
+{
+	enum PopupType {
+		PopupOnline = 0,
+		PopupStatusChange = 1
+	};
+	PopupType popupType = PopupOnline;
+
+	if ( j.user().isEmpty() )
+		new BlockTransportPopup(d->blockTransportPopupList, j);
+
+	bool doSound = false;
+	bool doPopup = false;
+	QPtrList<UserListItem> list = findRelavent(j);
+	QPtrListIterator<UserListItem> it(list);
+	for(UserListItem *u; (u = it.current()); ++it) {
+		bool doAnim = false;
+		bool local = false;
+		if(u->isSelf() && r.name() == d->client->resource())
+			local = true;
+
+		// add/update the resource
+		QString oldStatus, oldKey;
+		UserResource *rp;
+		UserResourceList::Iterator rit = u->userResourceList().find(j.resource());
+		bool found = (rit == u->userResourceList().end()) ? false: true;
+		if(!found) {
+			popupType = PopupOnline;
+
+			UserResource ur(r);
+			//ur.setSecurityEnabled(true);
+			if(local)
+				ur.setClient(PROG_NAME,PROG_VERSION,getOSName());
+			rp = &(*u->userResourceList().append(ur));
+
+			if(notifyOnlineOk && !local) {
+				doAnim = true;
+				if (!u->isHidden()) {
+					doSound = true;
+					doPopup = true;
+				}
+			}
+
+			if(!local && option.autoVersion && !status().isInvisible()) {
+				// do a client-version request (too easy!)
+				JT_ClientVersion *jcv = new JT_ClientVersion(d->client->rootTask());
+				connect(jcv, SIGNAL(finished()), SLOT(slotClientVersionFinished()));
+				jcv->get(j);
+				jcv->go(true);
+			}
+		}
+		else {
+			if ( !doPopup )
+				popupType = PopupStatusChange;
+
+			oldStatus = (*rit).status().status();
+			oldKey = (*rit).status().keyID();
+			rp = &(*rit);
+
+			(*rit).setResource(r);
+
+			if (!local && !u->isHidden())
+				doPopup = true;
+		}
+
+		rp->setPGPVerifyStatus(-1);
+		if(!rp->status().xsigned().isEmpty())
+			tryVerify(u, rp);
+
+		u->setPresenceError("");
+		cpUpdate(*u, r.name(), true);
+
+		if(doAnim && option.rosterAnim)
+			d->cp->animateNick(u->jid());
+	}
+	
+	if(doSound)
+		playSound(option.onevent[eOnline]);
+
+#if !defined(Q_WS_MAC) || !defined(HAVE_GROWL)
+	// Do the popup test earlier (to avoid needless JID lookups)
+	if ((popupType == PopupOnline && option.ppOnline) || (popupType == PopupStatusChange && option.ppStatus)) 
+#endif
+	if(notifyOnlineOk && doPopup && d->doPopups && !d->blockTransportPopupList->find(j, popupType == PopupOnline) && makeSTATUS(status()) != STATUS_DND ) {
+		QString name;
+		UserListItem *u = findFirstRelavent(j);
+
+		PsiPopup::PopupType pt = PsiPopup::AlertNone;
+		if ( popupType == PopupOnline )
+			pt = PsiPopup::AlertOnline;
+		else if ( popupType == PopupStatusChange )
+			pt = PsiPopup::AlertStatusChange;
+
+		if ((popupType == PopupOnline && option.ppOnline) || (popupType == PopupStatusChange && option.ppStatus)) {
+			PsiPopup *popup = new PsiPopup(pt, this);
+			popup->setData(j, r, u);
+		}
+#if defined(Q_WS_MAC) && defined(HAVE_GROWL)
+		PsiGrowlNotifier::instance()->popup(this, pt, j, r, u);
+#endif
+	}
+	else if ( !notifyOnlineOk )
+		d->userCounter++;
+}
+
+void PsiAccount::client_resourceUnavailable(const Jid &j, const Resource &r)
+{
+	bool doSound = false;
+	bool doPopup = false;
+
+	if ( j.user().isEmpty() )
+		new BlockTransportPopup(d->blockTransportPopupList, j);
+
+	QPtrList<UserListItem> list = findRelavent(j);
+	QPtrListIterator<UserListItem> it(list);
+	for(UserListItem *u; (u = it.current()); ++it) {
+		bool local = false;
+		if(u->isSelf() && r.name() == d->client->resource())
+			local = true;
+
+		// remove resource
+		UserResourceList::Iterator rit = u->userResourceList().find(j.resource());
+		bool found = (rit == u->userResourceList().end()) ? false: true;
+		if(found) {
+			u->setLastUnavailableStatus(r.status());
+			u->userResourceList().remove(rit);
+
+			if(!u->isAvailable())
+				u->setLastAvailable(QDateTime::currentDateTime());
+
+			if(!u->isAvailable() || u->isSelf()) {
+				// don't sound for our own resource
+				if(!isDisconnecting && !local && !u->isHidden()) {
+					doSound = true;
+					doPopup = true;
+				}
+			}
+		}
+
+		u->setPresenceError("");
+		cpUpdate(*u, r.name(), true);
+	}
+	if(doSound)
+		playSound(option.onevent[eOffline]);
+
+#if !defined(Q_WS_MAC) || !defined(HAVE_GROWL)
+	// Do the popup test earlier (to avoid needless JID lookups)
+	if (option.ppOffline) 
+#endif
+	if(doPopup && d->doPopups && !d->blockTransportPopupList->find(j) && makeSTATUS(status()) != STATUS_DND ) {
+		QString name;
+		UserListItem *u = findFirstRelavent(j);
+
+		if (option.ppOffline) {
+			PsiPopup *popup = new PsiPopup(PsiPopup::AlertOffline, this);
+			popup->setData(j, r, u);
+		}
+#if defined(Q_WS_MAC) && defined(HAVE_GROWL)
+		PsiGrowlNotifier::instance()->popup(this, PsiPopup::AlertOffline, j, r, u);
+#endif
+	}
+}
+
+void PsiAccount::client_presenceError(const Jid &j, int, const QString &str)
+{
+	QPtrList<UserListItem> list = findRelavent(j);
+	QPtrListIterator<UserListItem> it(list);
+	for(UserListItem *u; (u = it.current()); ++it) {
+		simulateContactOffline(u);
+		u->setPresenceError(str);
+		cpUpdate(*u, j.resource(), false);
+	}
+}
+
+void PsiAccount::client_messageReceived(const Message &m)
+{
+	//check if it's a server message without a from, and set the from appropriately
+	Message _m(m);
+	if (_m.from().isEmpty())
+	{
+		_m.setFrom(jid().domain());
+	}
+
+	// if the sender is already in the queue, then queue this message also
+	QPtrListIterator<Message> it(d->messageQueue);
+	for(Message *mi; (mi = it.current()); ++it) {
+		if(mi->from().compare(_m.from())) {
+			Message *m = new Message(_m);
+			d->messageQueue.append(m);
+			return;
+		}
+	}
+
+	// encrypted message?
+	if(!pgpKey().isEmpty() && !_m.xencrypted().isEmpty()) {
+		Message *m = new Message(_m);
+		d->messageQueue.append(m);
+		processMessageQueue();
+		return;
+	}
+
+	processIncomingMessage(_m);
+}
+
+void PsiAccount::processIncomingMessage(const Message &_m)
+{
+	// skip empty messages
+	if(_m.body().isEmpty() && _m.urlList().isEmpty() && _m.invite().isEmpty() && !_m.containsEvents())
+		return;
+
+	// skip headlines?
+	if(_m.type() == "headline" && option.ignoreHeadline)
+		return;
+
+	//skip if on ignore list? KEVIN, must not forget
+
+	if(_m.type() == "groupchat") {
+		GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(_m.from().userHost()));
+		if(w)
+			w->message(_m);
+		return;
+	}
+
+	// only toggle if not an invite or body is not empty
+	if(_m.invite().isEmpty() && !_m.body().isEmpty())
+		toggleSecurity(_m.from(), _m.wasEncrypted());
+
+	UserListItem *u = findFirstRelavent(_m.from());
+	if(u) {
+		if(_m.type() == "chat") u->setLastMessageType(1);
+		else u->setLastMessageType(0);
+	}
+
+	Message m = _m;
+
+	// smartchat: try to match up the incoming event to an existing chat
+	// (prior to 0.9, m.from() always contained a resource)
+	Jid j;
+	ChatDlg *c;
+	QPtrList<UserListItem> ul = findRelavent(m.from());
+
+	// ignore events from non-roster JIDs?
+	if (ul.isEmpty() && option.ignoreNonRoster)
+	{
+		if (option.excludeGroupChatsFromIgnore)
+		{
+			GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(_m.from().userHost()));
+			if(!w)
+			{
+				return;
+			}
+
+		}
+		else
+		{
+			return;
+		}
+	}
+
+	if(ul.isEmpty())
+		j = m.from().userHost();
+	else
+		j = ul.first()->jid();
+
+	c = (ChatDlg *)dialogFind("ChatDlg", j);
+	if(!c)
+		c = (ChatDlg *)dialogFind("ChatDlg", m.from().full());
+
+	if(m.type() == "error")
+		m.setBody(m.error().text + "\n------\n" + m.body());
+
+	// change the type?
+	if(m.type() != "headline" && m.invite().isEmpty()) {
+		if(option.incomingAs == 1)
+			m.setType("");
+		else if(option.incomingAs == 2)
+			m.setType("chat");
+		else if(option.incomingAs == 3) {
+			if(c != NULL && !c->isHidden())
+				m.setType("chat");
+			else
+				m.setType("");
+			}
+	}
+
+	// urls or subject on a chat message?  convert back to regular message
+	//if(m.type() == "chat" && (!m.urlList().isEmpty() || !m.subject().isEmpty()))
+	//	m.setType("");
+
+	MessageEvent *me = new MessageEvent(m, this);
+	me->setOriginLocal(false);
+	handleEvent(me);
+}
+
+void PsiAccount::client_subscription(const Jid &j, const QString &str)
+{
+	// if they remove our subscription, then we lost presence
+	if(str == "unsubscribed") {
+		UserListItem *u = d->userList.find(j);
+		if(u)
+			simulateContactOffline(u);
+	}
+
+	AuthEvent *ae = new AuthEvent(j, str, this);
+	ae->setTimeStamp(QDateTime::currentDateTime());
+	handleEvent(ae);
+}
+
+void PsiAccount::client_debugText(const QString &)
+{
+	//printf("%s", str.latin1());
+	//fflush(stdout);
+}
+
+void PsiAccount::client_incomingJidLink()
+{
+	JidLink *jl = d->client->jidLinkManager()->takeIncoming();
+	if(!jl)
+		return;
+	//if(link_test) {
+	//	printf("[%s] -- Incoming JidLink request from [%s]\n", name().latin1(), jl->peer().full().latin1());
+	//	JLTestDlg *w = new JLTestDlg(jl->peer(), jl, this);
+	//	w->show();
+	//}
+	//else
+		jl->deleteLater();
+}
+
+void PsiAccount::client_incomingFileTransfer()
+{
+	FileTransfer *ft = d->client->fileTransferManager()->takeIncoming();
+	if(!ft)
+		return;
+
+	/*printf("psiaccount: incoming file transfer:\n");
+	printf("  From: [%s]\n", ft->peer().full().latin1());
+	printf("  Name: [%s]\n", ft->fileName().latin1());
+	printf("  Size: %d bytes\n", ft->fileSize());*/
+
+	FileEvent *fe = new FileEvent(ft->peer().full(), ft, this);
+	fe->setTimeStamp(QDateTime::currentDateTime());
+	handleEvent(fe);
+}
+
+void PsiAccount::reconnect()
+{
+	if(doReconnect) {
+		//printf("PsiAccount: [%s] reconnecting...\n", name().latin1());
+		v_isActive = false;
+		doReconnect = false;
+		login();
+	}
+}
+
+Status PsiAccount::status() const
+{
+	return d->loginStatus;
+}
+
+void PsiAccount::setStatus(const Status &_s)
+{
+	// Block all transports' contacts' status change popups from popping
+	{
+		Roster::ConstIterator rit = d->acc.roster.begin();
+		for ( ; rit != d->acc.roster.end(); ++rit) {
+			const RosterItem &i = *rit;
+			if ( i.jid().user().isEmpty() /*&& i.jid().resource() == "registered"*/ ) // it is very likely then, that it's transport
+				new BlockTransportPopup(d->blockTransportPopupList, i.jid());
+		}
+	}
+
+	// cancel auto-status and reconnect
+	usingAutoStatus = false;
+	doReconnect = false;
+
+	Status s = _s;
+	s.setPriority(d->acc.priority);
+
+	d->loginStatus = s;
+
+	if(s.isAvailable()) {
+		// if client is not active then attempt to login
+		if(!isActive()) {
+			Jid j = d->jid;
+			if(d->acc.resource.isEmpty() || !j.isValid()) {
+				QMessageBox::information(0, CAP(tr("Error")), tr("Unable to login.  Ensure your account information is filled out."));
+				modify();
+				return;
+			}
+			if(!d->acc.opt_pass) {
+				bool ok = false;
+				QString text = QInputDialog::getText(
+					tr("Need Password"),
+					tr("Please enter the password for %1:").arg(j.full()),
+					QLineEdit::Password, QString::null, &ok, 0);
+				if(ok && !text.isEmpty())
+					d->acc.pass = text;
+				else
+					return;
+			}
+
+			login();
+		}
+		// change status
+		else {
+			if(rosterDone)
+				setStatusDirect(s);
+
+			if(s.isInvisible()) {//&&Pass invis to transports KEVIN
+				//this is a nasty hack to let the transports know we're invisible, since they get an offline packet when we go invisible
+				QPtrListIterator<UserListItem> it(d->userList);
+				for(UserListItem *u; (u = it.current()); ++it) {
+					if(u->isTransport()) {
+						JT_Presence *j = new JT_Presence(d->client->rootTask());
+						j->pres(u->jid(), s);
+						j->go(true);
+					}
+				}
+			}
+		}
+	}
+	else {
+		if(isActive())
+			logout(false, s);
+	}
+}
+
+void PsiAccount::setStatusDirect(const Status &_s)
+{
+	Status s = _s;
+	s.setPriority(d->acc.priority);
+
+	//printf("setting status to [%s]\n", s.status().latin1());
+
+	// using pgp?
+	if(d->psi->pgp() && !d->cur_pgpSecretKeyID.isEmpty()) {
+		d->loginStatus = s;
+
+		// sign presence
+		trySignPresence();
+	}
+	else {
+		/*if(d->psi->pgp() && !d->cur_pgpSecretKeyID.isEmpty())
+			s.setKeyID(d->cur_pgpSecretKeyID);
+		else
+			s.setKeyID("");*/
+
+		// send presence normally
+		setStatusActual(s);
+	}
+}
+
+void PsiAccount::setStatusActual(const Status &s)
+{
+	d->loginStatus = s;
+
+	d->client->setPresence(s);
+	if(presenceSent) {
+		stateChanged();
+	}
+	else {
+		presenceSent = true;
+		stateChanged();
+		QTimer::singleShot(15000, this, SLOT(enableNotifyOnline()));
+
+		const VCard *vcard = VCardFactory::vcard(d->jid);
+		if ( option.autoVCardOnLogin || !vcard || vcard->isEmpty() || vcard->nickName().isEmpty() )
+			VCardFactory::getVCard(d->jid, d->client->rootTask(), this, SLOT(slotCheckVCard()));
+		else {
+			d->nickFromVCard = true;
+			setNick( vcard->nickName() );
+		}
+	}
+}
+
+void PsiAccount::secondsIdle(int x)
+{
+	if(!loggedIn())
+		return;
+
+	int lastIdle = d->lastIdle;
+	Status lastStatus = d->lastStatus;
+
+	Resource r = *(d->client->resourceList().find(d->client->resource()));
+	Status ls = r.status();
+
+	//must avoid status change when invisible KEVIN
+	if(ls.isInvisible())
+		return;
+
+	if(ls.isAvailable()) {
+		// no longer idle?
+		if(lastIdle > x) {
+			if(ls.isAway() && usingAutoStatus) {
+				lastStatus = d->origStatus;
+				setStatusDirect(lastStatus);
+				usingAutoStatus = false;
+			}
+		}
+		else if( !(ls.isAway() && !usingAutoStatus) ) {
+			int minutes = x / 60;
+
+			if(option.use_asOffline && option.asOffline > 0 && minutes >= option.asOffline) {
+				lastStatus = Status("", "", d->acc.priority, false);
+				usingAutoStatus = false;
+				logout();
+			}
+			else if(option.use_asXa && option.asXa > 0 && minutes >= option.asXa) {
+				if(ls.show() != "xa" && lastStatus.show() != "xa") {
+					lastStatus = Status("xa", option.asMessage, d->acc.priority);
+					if(!usingAutoStatus)
+						d->origStatus = d->loginStatus;
+					setStatusDirect(lastStatus);
+					usingAutoStatus = true;
+				}
+			}
+			else if(option.use_asAway && option.asAway > 0 && minutes >= option.asAway) {
+				if(ls.show() != "away" && lastStatus.show() != "away") {
+					lastStatus = Status("away", option.asMessage, d->acc.priority);
+					if(!usingAutoStatus)
+						d->origStatus = d->loginStatus;
+					setStatusDirect(lastStatus);
+					usingAutoStatus = true;
+				}
+			}
+		}
+	}
+
+	d->lastIdle = x;
+	d->lastStatus = lastStatus;
+}
+
+void PsiAccount::playSound(const QString &str)
+{
+	if(str.isEmpty())
+		return;
+
+	int s = STATUS_OFFLINE;
+	if(loggedIn())
+		s = makeSTATUS(status());
+
+	if(s == STATUS_DND)
+		return;
+
+	// no away sounds?
+	if(option.noAwaySound && (s == STATUS_AWAY || s == STATUS_XA))
+		return;
+
+	d->psi->playSound(str);
+}
+
+QWidget *PsiAccount::dialogFind(const char *className, const Jid &j)
+{
+	QPtrListIterator<item_dialog2> it(d->dialogList);
+	for(item_dialog2 *i; (i = it.current()); ++it) {
+		// does the classname and jid match?
+		if(i->className == className && i->jid.compare(j)) {
+			return i->widget;
+		}
+	}
+	return 0;
+}
+
+void PsiAccount::dialogRegister(QWidget *w, const Jid &j)
+{
+	item_dialog2 *i = new item_dialog2;
+	i->widget = w;
+	i->className = w->className();
+	i->jid = j;
+	d->dialogList.append(i);
+}
+
+void PsiAccount::dialogUnregister(QWidget *w)
+{
+	QPtrListIterator<item_dialog2> it(d->dialogList);
+	for(item_dialog2 *i; (i = it.current()); ++it) {
+		if(i->widget == w) {
+			d->dialogList.removeRef(i);
+			return;
+		}
+	}
+}
+
+void PsiAccount::deleteAllDialogs()
+{
+	QPtrListIterator<item_dialog2> it(d->dialogList);
+	for(item_dialog2 *i; (i = it.current());)
+		delete i->widget;
+	d->dialogList.clear();
+}
+
+bool PsiAccount::checkConnected(QWidget *par)
+{
+	if(!loggedIn()) {
+		QMessageBox::information(par, CAP(tr("Error")), tr("You must be connected to the server in order to do this."));
+		return false;
+	}
+
+	return true;
+}
+
+void PsiAccount::modify()
+{
+	AccountModifyDlg *w = (AccountModifyDlg *)dialogFind("AccountModifyDlg");
+	if(w)
+		bringToFront(w);
+	else {
+		w = new AccountModifyDlg(this, 0);
+		w->show();
+	}
+}
+
+void PsiAccount::changeVCard()
+{
+	actionInfo(d->jid);
+}
+
+void PsiAccount::changePW()
+{
+	if(!checkConnected())
+		return;
+
+	ChangePasswordDlg *w = (ChangePasswordDlg *)dialogFind("ChangePasswordDlg");
+	if(w)
+		bringToFront(w);
+	else {
+		w = new ChangePasswordDlg(this);
+		w->show();
+	}
+}
+
+void PsiAccount::showXmlConsole()
+{
+	bringToFront(d->xmlConsole);
+}
+
+void PsiAccount::openAddUserDlg()
+{
+	if(!checkConnected())
+		return;
+
+	AddUserDlg *w = (AddUserDlg *)dialogFind("AddUserDlg");
+	if(w)
+		bringToFront(w);
+	else {
+		QStringList gl, services, names;
+		UserListIt it(d->userList);
+		for(UserListItem *u; (u = it.current()); ++it) {
+			if(u->isTransport()) {
+				services += u->jid().full();
+				names += jidnick(u->jid().full(), u->name());
+			}
+			const QStringList &groups = u->groups();
+			if(groups.isEmpty())
+				continue;
+			for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) {
+				if(qstringlistmatch(gl, *git) == -1)
+					gl.append(*git);
+			}
+		}
+
+		w = new AddUserDlg(services, names, gl, this);
+		connect(w, SIGNAL(add(const Jid &, const QString &, const QStringList &, bool)), SLOT(dj_add(const Jid &, const QString &, const QStringList &, bool)));
+		w->show();
+	}
+}
+
+void PsiAccount::doDisco()
+{
+	actionDisco(d->jid.host(), "");
+}
+
+void PsiAccount::actionDisco(const Jid &j, const QString &node)
+{
+	DiscoDlg *w = new DiscoDlg(this, j, node);
+	connect(w, SIGNAL(featureActivated(QString, Jid, QString)), SLOT(featureActivated(QString, Jid, QString)));
+	w->show();
+}
+
+void PsiAccount::featureActivated(QString feature, Jid jid, QString node)
+{
+	Features f(feature);
+
+	if ( f.canRegister() )
+		actionRegister(jid);
+	else if ( f.canSearch() )
+		actionSearch(jid);
+	else if ( f.canGroupchat() )
+		actionJoin(jid);
+	else if ( f.canDisco() )
+		actionDisco(jid, node);
+	else if ( f.isGateway() )
+		; // TODO
+	else if ( f.haveVCard() )
+		actionInfo(jid);
+	else if ( f.id() == Features::FID_Add ) {
+		QStringList sl;
+		dj_add(jid, QString::null, sl, true);
+	}
+}
+
+void PsiAccount::actionJoin(const Jid &j)
+{
+	GCJoinDlg *w = new GCJoinDlg(psi(), this);
+
+	w->le_host->setText ( j.host() );
+	w->le_room->setText ( j.user() );
+
+	w->show();
+}
+
+void PsiAccount::stateChanged()
+{
+	if(loggedIn())
+		d->cp->setState(makeSTATUS(status()));
+	else {
+		if(isActive()) {
+			d->cp->setState(-1);
+			if(d->usingSSL)
+				d->cp->setUsingSSL(true);
+			else
+				d->cp->setUsingSSL(false);
+		}
+		else {
+			d->cp->setState(STATUS_OFFLINE);
+			d->cp->setUsingSSL(false);
+		}
+	}
+
+	updatedActivity();
+}
+
+void PsiAccount::simulateContactOffline(UserListItem *u)
+{
+	UserResourceList rl = u->userResourceList();
+	u->setPresenceError("");
+	if(!rl.isEmpty()) {
+		for(UserResourceList::ConstIterator rit = rl.begin(); rit != rl.end(); ++rit) {
+			const UserResource &r = *rit;
+			Jid j = u->jid();
+			if(u->jid().resource().isEmpty())
+				j.setResource(r.name());
+			client_resourceUnavailable(j, r);
+		}
+	}
+	else
+		cpUpdate(*u);
+}
+
+void PsiAccount::simulateRosterOffline()
+{
+	UserListIt it(d->userList);
+	for(UserListItem *u; (u = it.current()); ++it)
+		simulateContactOffline(u);
+
+	// self
+	{
+		UserListItem *u = &d->self;
+		UserResourceList rl = u->userResourceList();
+		for(UserResourceList::ConstIterator rit = rl.begin(); rit != rl.end(); ++rit) {
+			Jid j = u->jid();
+			if(u->jid().resource().isEmpty())
+				j.setResource((*rit).name());
+			u->setPresenceError("");
+			client_resourceUnavailable(j, *rit);
+		}
+	}
+
+	d->gcbank.clear();
+}
+
+void PsiAccount::enableNotifyOnline()
+{
+	if ( d->userCounter > 1 ) {
+		QTimer::singleShot(15000, this, SLOT(enableNotifyOnline()));
+		d->userCounter = 0;
+	}
+	else
+		notifyOnlineOk = true;
+}
+
+void PsiAccount::slotClientVersionFinished()
+{
+	JT_ClientVersion *j = (JT_ClientVersion *)sender();
+	if(j->success()) {
+		QPtrList<UserListItem> list = findRelavent(j->jid());
+		QPtrListIterator<UserListItem> it(list);
+		for(UserListItem *u; (u = it.current()); ++it) {
+			UserResourceList::Iterator rit = u->userResourceList().find(j->jid().resource());
+			bool found = (rit == u->userResourceList().end()) ? false: true;
+			if(!found)
+				continue;
+
+			(*rit).setClient(j->name(),j->version(),j->os());
+
+			cpUpdate(*u);
+			/*if(u->isSelf()) {
+				if(d->cp->self())
+					d->cp->updateSelf(*u);
+			}
+			else
+				d->cp->updateEntry(*u);*/
+		}
+	}
+}
+
+QPtrList<UserListItem> PsiAccount::findRelavent(const Jid &j) const
+{
+	QPtrList<UserListItem> list;
+
+	// self?
+	if(j.compare(d->self.jid(), false))
+		list.append(&d->self);
+	else {
+		QPtrListIterator<UserListItem> it(d->userList);
+		for(UserListItem *u; (u = it.current()); ++it) {
+			if(!u->jid().compare(j, false))
+				continue;
+
+			if(!u->jid().resource().isEmpty()) {
+				if(u->jid().resource() != j.resource())
+					continue;
+			}
+			list.append(u);
+		}
+	}
+
+	return list;
+}
+
+UserListItem *PsiAccount::findFirstRelavent(const Jid &j) const
+{
+	QPtrList<UserListItem> list = findRelavent(j);
+	if(list.isEmpty())
+		return 0;
+	else
+		return list.first();
+}
+
+UserListItem *PsiAccount::find(const Jid &j) const
+{
+	UserListItem *u;
+	if(j.compare(d->self.jid()))
+		u = &d->self;
+	else
+		u = d->userList.find(j);
+
+	return u;
+}
+
+void PsiAccount::cpUpdate(const UserListItem &u, const QString &rname, bool fromPresence)
+{
+	PsiEvent *e = d->eventQueue->peek(u.jid());
+
+	d->cp->updateEntry(u);
+
+	if(e) {
+		d->cp->setAlert(u.jid(), is->event2icon(e));
+	}
+	else
+		d->cp->clearAlert(u.jid());
+
+	updateContact(u);
+	Jid j = u.jid();
+	if(!rname.isEmpty())
+		j.setResource(rname);
+	updateContact(j);
+	updateContact(j, fromPresence);
+	d->psi->updateContactGlobal(this, j);
+}
+
+QLabel *PsiAccount::accountLabel(QWidget *par, bool simpleMode)
+{
+	AccountLabel *al = new AccountLabel(this, par, simpleMode);
+	return al;
+}
+
+EventDlg *PsiAccount::ensureEventDlg(const Jid &j)
+{
+	EventDlg *w = (EventDlg *)dialogFind("EventDlg", j);
+	if(!w) {
+		w = new EventDlg(j, this, true);
+		connect(w, SIGNAL(aReadNext(const Jid &)), SLOT(processReadNext(const Jid &)));
+		connect(w, SIGNAL(aChat(const Jid &)), SLOT(actionOpenChat(const Jid&)));
+		connect(w, SIGNAL(aReply(const Jid &, const QString &, const QString &, const QString &)), SLOT(dj_composeMessage(const Jid &, const QString &, const QString &, const QString &)));
+		connect(w, SIGNAL(aAuth(const Jid &)), SLOT(dj_addAuth(const Jid &)));
+		connect(w, SIGNAL(aDeny(const Jid &)), SLOT(dj_deny(const Jid &)));
+		connect(d->psi, SIGNAL(emitOptionsUpdate()), w, SLOT(optionsUpdate()));
+		connect(this, SIGNAL(updateContact(const Jid &)), w, SLOT(updateContact(const Jid &)));
+	}
+
+	return w;
+}
+
+ChatDlg *PsiAccount::ensureChatDlg(const Jid &j)
+{
+	ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", j);
+	if(!c) {
+		// create the chatbox
+		c = new ChatDlg(j, this);
+		if (option.useTabs)
+		{
+			//get a tab from the mainwin
+			d->psi->getTabs()->addChat(c);
+		}
+		connect(c, SIGNAL(aSend(const Message &)), SLOT(dj_sendMessage(const Message &)));
+		connect(c, SIGNAL(messagesRead(const Jid &)), SLOT(chatMessagesRead(const Jid &)));
+		connect(c, SIGNAL(aInfo(const Jid &)), SLOT(actionInfo(const Jid &)));
+		connect(c, SIGNAL(aHistory(const Jid &)), SLOT(actionHistory(const Jid &)));
+		connect(c, SIGNAL(aFile(const Jid &)), SLOT(actionSendFile(const Jid &)));
+		connect(d->psi, SIGNAL(emitOptionsUpdate()), c, SLOT(optionsUpdate()));
+		connect(this, SIGNAL(updateContact(const Jid &, bool)), c, SLOT(updateContact(const Jid &, bool)));
+	}
+	else {
+		// on X11, do a special reparent to open on the right desktop
+#ifdef Q_WS_X11
+		/* KIS added an exception for tabs here. We do *not* want chats flying 
+		 * randomlyi, it pulls them out of tabsets. So instead, we move the 
+		 * tabset instead. It's just as filthy, unfortunately, but it's the 
+		 * only way */
+		//TODO: This doesn't work as expected atm, it doesn't seem to reparent the tabset
+		QWidget *window=c;
+		if ( option.useTabs )
+			window = d->psi->getManagingTabs(c);
+		if(window && window->isHidden()) {
+			const QPixmap *pp = c->icon();
+			QPixmap p;
+			if(pp)
+				p = *pp;
+			reparent_good(window, 0, false);
+			if(!p.isNull())
+				c->setIcon(p);
+		}
+#endif
+	}
+
+	return c;
+}
+
+void PsiAccount::changeStatus(int x)
+{
+	if(x == STATUS_OFFLINE && !option.askOffline) {
+		setStatus(Status("","Logged out",0,false));
+	}
+	else {
+		if(x == STATUS_ONLINE && !option.askOnline) {
+			setStatus(Status());
+		}
+		else if(x == STATUS_INVISIBLE){
+			Status s("","",0,true);
+			s.setIsInvisible(true);
+			setStatus(s);
+		}
+		else {
+			StatusSetDlg *w = new StatusSetDlg(this, makeStatus(x, ""));
+			connect(w, SIGNAL(set(const Status &)), SLOT(setStatus(const Status &)));
+			w->show();
+		}
+	}
+}
+
+void PsiAccount::actionTest(const Jid &j)
+{
+	UserListItem *u = find(j);
+	if(!u)
+		return;
+	Jid j2 = j;
+	if(j.resource().isEmpty()) {
+		if(u->isAvailable())
+			j2.setResource((*u->userResourceList().priority()).name());
+	}
+
+	//S5BConnection *c = new S5BConnection(d->client->s5bManager());
+	//c->connectToJid(j2, d->client->s5bManager()->genUniqueSID(j2));
+	JLTestDlg *w = new JLTestDlg(j2, this);
+	w->show();
+}
+
+void PsiAccount::actionSendFile(const Jid &j)
+{
+	QStringList l;
+	actionSendFiles(j, l);
+}
+
+void PsiAccount::actionSendFiles(const Jid &j, const QStringList& l)
+{
+	Jid j2 = j;
+	if(j.resource().isEmpty()) {
+		UserListItem *u = find(j);
+		if(u && u->isAvailable())
+			j2.setResource((*u->userResourceList().priority()).name());
+	}
+
+	// Create a dialog for each file in the list. Once the xfer dialog itself
+	// supports multiple files, only the 'else' branch needs to stay.
+	if (!l.isEmpty()) {
+		for (QStringList::ConstIterator f = l.begin(); f != l.end(); ++f ) {
+			QStringList fl(*f);
+			FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, fl);
+			w->show();
+		}
+	}
+	else {
+		FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, l);
+		w->show();
+	}
+}
+
+void PsiAccount::actionDefault(const Jid &j)
+{
+	UserListItem *u = find(j);
+	if(!u)
+		return;
+
+	if(d->eventQueue->count(u->jid()) > 0)
+		openNextEvent(*u);
+	else {
+		if(option.defaultAction == 0)
+			actionSendMessage(u->jid());
+		else
+			actionOpenChat(u->jid());
+	}
+}
+
+void PsiAccount::actionRecvEvent(const Jid &j)
+{
+	UserListItem *u = find(j);
+	if(!u)
+		return;
+
+	openNextEvent(*u);
+}
+
+void PsiAccount::actionSendMessage(const Jid &j)
+{
+	EventDlg *w = d->psi->createEventDlg(j.full(), this);
+	w->show();
+}
+
+void PsiAccount::actionSendMessage(const JidList &j)
+{
+	QString str;
+	bool first = true;
+	for(QValueList<Jid>::ConstIterator it = j.begin(); it != j.end(); ++it) {
+		if(!first)
+			str += ", ";
+		first = false;
+
+		str += (*it).full();
+	}
+
+	EventDlg *w = d->psi->createEventDlg(str, this);
+	w->show();
+}
+
+void PsiAccount::actionSendUrl(const Jid &j)
+{
+	EventDlg *w = d->psi->createEventDlg(j.full(), this);
+	w->setUrlOnShow();
+	w->show();
+}
+
+void PsiAccount::actionRemove(const Jid &j)
+{
+#ifdef AVATARS
+	avatarFactory()->removeManualAvatar(j);
+#endif
+	dj_remove(j);
+}
+
+void PsiAccount::actionRename(const Jid &j, const QString &name)
+{
+	dj_rename(j, name);
+}
+
+void PsiAccount::actionGroupRename(const QString &oldname, const QString &newname)
+{
+	UserListIt it(d->userList);
+	QPtrList<UserListItem> nu;
+	for(UserListItem *u; (u = it.current()); ++it) {
+		if(u->inGroup(oldname)) {
+			u->removeGroup(oldname);
+			u->addGroup(newname);
+			cpUpdate(*u);
+			if(u->inList())
+				nu.append(u);
+		}
+	}
+
+	if(!nu.isEmpty()) {
+		JT_Roster *r = new JT_Roster(d->client->rootTask());
+
+		QPtrListIterator<UserListItem> it(nu);
+		for(UserListItem *u; (u = it.current()); ++it)
+			r->set(u->jid(), u->name(), u->groups());
+
+		r->go(true);
+	}
+}
+
+void PsiAccount::actionHistory(const Jid &j)
+{
+	HistoryDlg *w = (HistoryDlg *)dialogFind("HistoryDlg", j);
+	if(w)
+		bringToFront(w);
+	else {
+		w = new HistoryDlg(j, this);
+		connect(w, SIGNAL(openEvent(PsiEvent *)), SLOT(actionHistoryBox(PsiEvent *)));
+		w->show();
+	}
+}
+
+void PsiAccount::actionHistoryBox(PsiEvent *e)
+{
+	EventDlg *w = new EventDlg(e->from(), this, false);
+	connect(w, SIGNAL(aChat(const Jid &)), SLOT(actionOpenChat(const Jid&)));
+	connect(w, SIGNAL(aReply(const Jid &, const QString &, const QString &, const QString &)), SLOT(dj_composeMessage(const Jid &, const QString &, const QString &, const QString &)));
+	connect(w, SIGNAL(aAuth(const Jid &)), SLOT(dj_addAuth(const Jid &)));
+	connect(w, SIGNAL(aDeny(const Jid &)), SLOT(dj_deny(const Jid &)));
+	connect(d->psi, SIGNAL(emitOptionsUpdate()), w, SLOT(optionsUpdate()));
+	connect(this, SIGNAL(updateContact(const Jid &)), w, SLOT(updateContact(const Jid &)));
+	w->updateEvent(e);
+	w->show();
+}
+
+void PsiAccount::actionOpenChat(const Jid &j)
+{
+	UserListItem *u = find(j);
+	if(!u)
+		return;
+
+	// if 'j' is bare, we might want to switch to a specific resource
+	QString res;
+	if(j.resource().isEmpty()) {
+		// first, are there any queued chats?
+		/*PsiEvent *e = d->eventQueue->peekFirstChat(j, false);
+		if(e) {
+			res = e->from().resource();
+			// if we have a bare chat, change to 'res'
+			ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", j);
+			if(c)
+				c->setJid(j.withResource(res));
+		}
+		// else, is there a priority chat window available?
+		else*/ if(u->isAvailable()) {
+			QString pr = (*u->userResourceList().priority()).name();
+			if(!pr.isEmpty() && dialogFind("ChatDlg", j.withResource(pr)))
+				res = pr;
+		}
+		else {
+			QStringList list = hiddenChats(j);
+			if(!list.isEmpty())
+				res = list.first();
+		}
+	}
+
+	if(!res.isEmpty())
+		openChat(j.withResource(res));
+	else
+		openChat(j);
+}
+
+void PsiAccount::actionOpenChatSpecific(const Jid &j)
+{
+	openChat(j);
+}
+
+void PsiAccount::actionAgentSetStatus(const Jid &j, Status &s)
+{
+	if ( j.user().isEmpty() ) // add all transport popups to block list
+		new BlockTransportPopup(d->blockTransportPopupList, j);
+
+	JT_Presence *p = new JT_Presence(d->client->rootTask());
+	p->pres(j, s);
+	p->go(true);
+}
+
+void PsiAccount::actionInfo(const Jid &_j)
+{
+	bool useCache = true;
+	Jid j;
+	if(findGCContact(_j)) {
+		useCache = false;
+		j = _j;
+	}
+	else {
+		j = _j.userHost();
+	}
+
+	InfoDlg *w = (InfoDlg *)dialogFind("InfoDlg", j);
+	if(w) {
+		w->updateStatus();
+		bringToFront(w);
+	}
+	else {
+		const VCard *vcard = VCardFactory::vcard(j);
+
+		VCard tmp;
+		if ( vcard )
+			tmp = *vcard;
+		w = new InfoDlg(j.compare(d->jid) ? InfoDlg::Self : InfoDlg::Contact, j, tmp, this, 0, 0, useCache);
+		w->show();
+
+		// automatically retrieve info if it doesn't exist
+		if(!vcard && loggedIn())
+			w->doRefresh();
+	}
+}
+
+void PsiAccount::actionAuth(const Jid &j)
+{
+	dj_auth(j);
+}
+
+void PsiAccount::actionAuthRequest(const Jid &j)
+{
+	dj_authReq(j);
+}
+
+void PsiAccount::actionAuthRemove(const Jid &j)
+{
+	dj_deny(j);
+}
+
+void PsiAccount::actionAdd(const Jid &j)
+{
+	dj_addAuth(j);
+}
+
+void PsiAccount::actionGroupAdd(const Jid &j, const QString &g)
+{
+	UserListItem *u = d->userList.find(j);
+	if(!u)
+		return;
+
+	if(!u->addGroup(g))
+		return;
+	cpUpdate(*u);
+
+	JT_Roster *r = new JT_Roster(d->client->rootTask());
+	r->set(u->jid(), u->name(), u->groups());
+	r->go(true);
+}
+
+void PsiAccount::actionGroupRemove(const Jid &j, const QString &g)
+{
+	UserListItem *u = d->userList.find(j);
+	if(!u)
+		return;
+
+	if(!u->removeGroup(g))
+		return;
+	cpUpdate(*u);
+
+	JT_Roster *r = new JT_Roster(d->client->rootTask());
+	r->set(u->jid(), u->name(), u->groups());
+	r->go(true);
+}
+
+void PsiAccount::actionRegister(const Jid &j)
+{
+	if(!checkConnected())
+		return;
+
+	RegistrationDlg *w = (RegistrationDlg *)dialogFind("RegistrationDlg", j);
+	if(w)
+		bringToFront(w);
+	else {
+		w = new RegistrationDlg(j, this);
+		w->show();
+	}
+}
+
+void PsiAccount::actionSearch(const Jid &j)
+{
+	if(!checkConnected())
+		return;
+
+	SearchDlg *w = (SearchDlg *)dialogFind("SearchDlg", j);
+	if(w)
+		bringToFront(w);
+	else {
+		w = new SearchDlg(j, this);
+		connect(w, SIGNAL(add(const Jid &, const QString &, const QStringList &, bool)), SLOT(dj_add(const Jid &, const QString &, const QStringList &, bool)));
+		connect(w, SIGNAL(aInfo(const Jid &)), SLOT(actionInfo(const Jid &)));
+		w->show();
+	}
+}
+
+void PsiAccount::actionInvite(const Jid &j, const QString &gc)
+{
+	Message m;
+	m.setTo(j);
+	m.setInvite(gc);
+	m.setBody(tr("You have been invited to %1").arg(gc));
+	m.setTimeStamp(QDateTime::currentDateTime());
+	dj_sendMessage(m);
+}
+
+void PsiAccount::actionAssignKey(const Jid &j)
+{
+	if(ensureKey(j)) {
+		UserListItem *u = findFirstRelavent(j);
+		if(u)
+			cpUpdate(*u);
+	}
+}
+
+void PsiAccount::actionUnassignKey(const Jid &j)
+{
+	UserListItem *u = findFirstRelavent(j);
+	if(u) {
+		u->setPublicKeyID("");
+		cpUpdate(*u);
+	}
+}
+
+void PsiAccount::dj_sendMessage(const Message &m, bool log)
+{
+	UserListItem *u = findFirstRelavent(m.to());
+	Message nm = m;
+
+        if(option.incomingAs == 3) {
+		if(u) {
+			switch(u->lastMessageType()) {
+				case 0: nm.setType(""); break;
+				case 1: nm.setType("chat"); break;
+			}
+		}
+	}
+
+	d->client->sendMessage(nm);
+
+	// only toggle if not an invite or body is not empty
+	if(m.invite().isEmpty() && !m.body().isEmpty())
+		toggleSecurity(m.to(), m.wasEncrypted());
+
+	// don't log groupchat, private messages, or encrypted messages
+	if(d->acc.opt_log && log) {
+		if(m.type() != "groupchat" && m.xencrypted().isEmpty() && !findGCContact(m.to())) {
+			MessageEvent *me = new MessageEvent(m, this);
+			me->setOriginLocal(true);
+			me->setTimeStamp(QDateTime::currentDateTime());
+			logEvent(m.to(), me);
+			delete me;
+		}
+	}
+
+	// don't sound when sending groupchat messages or message events
+	if(m.type() != "groupchat" && !m.body().isEmpty())
+		playSound(option.onevent[eSend]);
+
+	// auto close an open messagebox (if non-chat)
+	if(m.type() != "chat" && !m.body().isEmpty()) {
+		UserListItem *u = findFirstRelavent(m.to());
+		if(u) {
+			EventDlg *e = (EventDlg *)dialogFind("EventDlg", u->jid());
+			if(e)
+				e->closeAfterReply();
+		}
+	}
+}
+
+void PsiAccount::dj_composeMessage(const Jid &jid, const QString &body, const QString &subject, const QString &thread)
+{
+	EventDlg *w = d->psi->createEventDlg(jid.full(), this);
+	if(!body.isEmpty())
+		w->setText(qstrquote(body));
+
+	if(!subject.isEmpty() && subject.left(3) != "Re:")
+		w->setSubject("Re: " + subject);
+	else if (subject.left(3) == "Re:")
+		w->setSubject(subject);
+
+	if(!thread.isEmpty())
+		w->setThread(thread);
+
+	w->show();
+}
+
+void PsiAccount::dj_composeMessage(const Jid &j, const QString &body)
+{
+	dj_composeMessage(j, body, QString::null, QString::null);
+}
+
+void PsiAccount::dj_addAuth(const Jid &j)
+{
+	QString name;
+	QStringList groups;
+	UserListItem *u = d->userList.find(j);
+	if(u) {
+		name = u->name();
+		groups = u->groups();
+	}
+
+	dj_add(j, name, groups, true);
+	dj_auth(j);
+}
+
+void PsiAccount::dj_add(const Jid &j, const QString &name, const QStringList &groups, bool authReq)
+{
+	JT_Roster *r = new JT_Roster(d->client->rootTask());
+	r->set(j, name, groups);
+	r->go(true);
+
+	if(authReq)
+		dj_authReq(j);
+}
+
+void PsiAccount::dj_authReq(const Jid &j)
+{
+	d->client->sendSubscription(j, "subscribe");
+}
+
+void PsiAccount::dj_auth(const Jid &j)
+{
+	d->client->sendSubscription(j, "subscribed");
+}
+
+void PsiAccount::dj_deny(const Jid &j)
+{
+	d->client->sendSubscription(j, "unsubscribed");
+}
+
+void PsiAccount::dj_rename(const Jid &j, const QString &name)
+{
+	UserListItem *u = d->userList.find(j);
+	if(!u)
+		return;
+
+	QString str;
+	if(name == u->jid().full())
+		str = "";
+	else
+		str = name;
+
+	// strange workaround to avoid a null string ??
+	QString uname;
+	if(u->name().isEmpty())
+		uname = "";
+	else
+		uname = u->name();
+
+	if(uname == str)
+		return;
+	u->setName(str);
+
+	cpUpdate(*u);
+
+	if(u->inList()) {
+		JT_Roster *r = new JT_Roster(d->client->rootTask());
+		r->set(u->jid(), u->name(), u->groups());
+		r->go(true);
+	}
+}
+
+void PsiAccount::dj_remove(const Jid &j)
+{
+	UserListItem *u = d->userList.find(j);
+	if(!u)
+		return;
+
+	// remove all events from the queue
+	d->eventQueue->clear(j);
+	updateReadNext(j);
+
+	// TODO: delete the item immediately (to simulate local change)
+	if(!u->inList()) {
+		//simulateContactOffline(u);
+		d->userList.removeRef(u);
+	}
+	else {
+		JT_Roster *r = new JT_Roster(d->client->rootTask());
+		r->remove(j);
+		r->go(true);
+
+		// if it looks like a transport, unregister (but not if it is the server!!)
+		if(u->isTransport() && !Jid(d->client->host()).compare(u->jid())) {
+			JT_UnRegister *ju = new JT_UnRegister(d->client->rootTask());
+			ju->unreg(j);
+			ju->go(true);
+		}
+	}
+}
+
+// handle an incoming event
+void PsiAccount::handleEvent(PsiEvent *e)
+{
+	if ( e )
+		setEnabled();
+
+	bool doPopup    = false;
+	bool putToQueue = true;
+	PsiPopup::PopupType popupType = PsiPopup::AlertNone;
+
+	// find someone to accept the event
+	Jid j;
+	QPtrList<UserListItem> ul = findRelavent(e->from());
+	if(ul.isEmpty()) {
+		// if groupchat, then we want the full JID
+		if(findGCContact(e->from())) {
+			j = e->from();
+		}
+		else {
+			Jid bare = e->from().userHost();
+			Jid reg = bare.withResource("registered");
+
+			// see if we have a "registered" variant of the jid
+			if(findFirstRelavent(reg)) {
+				j = reg;
+				e->setFrom(reg); // HACK!!
+			}
+			// retain full jid if sent to "registered"
+			else if(e->from().resource() == "registered")
+				j = e->from();
+			// otherwise don't use the resource for new entries
+			else
+				j = bare;
+		}
+	}
+	else
+		j = ul.first()->jid();
+	e->setJid(j);
+
+	if(d->acc.opt_log) {
+		if(e->type() == PsiEvent::Message || e->type() == PsiEvent::Auth) {
+			// don't log private messages
+			if(!findGCContact(e->from()) && !(e->type() == PsiEvent::Message && ((MessageEvent *)e)->message().body().isEmpty()))
+				logEvent(e->from(), e);
+		}
+	}
+
+	if(e->type() == PsiEvent::Message) {
+		MessageEvent *me = (MessageEvent *)e;
+		const Message &m = me->message();
+
+		// Pass message events to chat window
+		if (m.containsEvents() && m.body().isEmpty()) {
+			if (option.messageEvents) {
+				ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", e->from());
+				if(!c)
+					c = (ChatDlg *)dialogFind("ChatDlg", e->jid());
+				if (c)
+					c->incomingMessage(m);
+			}
+			return;
+		}
+
+		// pass chat messages directly to a chat window if possible (and deal with sound)
+		if(m.type() == "chat") {
+			ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", e->from());
+			if(!c)
+				c = (ChatDlg *)dialogFind("ChatDlg", e->jid());
+
+			if(c)
+				c->setJid(e->from());
+
+			if ( !c || !c->isActiveWindow() || option.alertOpenChats ) {
+				doPopup = true;
+				popupType = PsiPopup::AlertChat;
+			}
+
+			//if the chat exists, and is either open in a tab,
+			//or in a window
+			if( c && ( d->psi->isChatTabbed(c) || !c->isHidden() ) ) {
+				c->incomingMessage(m);
+				playSound(option.onevent[eChat2]);
+				if(option.alertOpenChats && !d->psi->isChatActiveWindow(c)) {
+					// to alert the chat also, we put it in the queue
+					me->setSentToChatWindow(true);
+				}
+				else
+					putToQueue = false;
+			}
+			else {
+				bool firstChat = !d->eventQueue->hasChats(e->from());
+				playSound(option.onevent[firstChat ? eChat1: eChat2]);
+			}
+		} // /chat
+		else if (m.type() == "headline") {
+			playSound(option.onevent[eHeadline]);
+			doPopup = true;
+			popupType = PsiPopup::AlertHeadline;
+		} // /headline
+		else if (m.type() == "") {
+			playSound(option.onevent[eMessage]);
+			if (m.type() == "") {
+				doPopup = true;
+				popupType = PsiPopup::AlertMessage;
+			}
+		} // /""
+		else
+			playSound(option.onevent[eSystem]);
+
+		if(m.type() == "error") {
+			// FIXME: handle message errors
+			//msg.text = QString(tr("<big>[Error Message]</big><br>%1").arg(plain2rich(msg.text)));
+		}
+	}
+	else if(e->type() == PsiEvent::File) {
+		playSound(option.onevent[eIncomingFT]);
+		doPopup = true;
+		popupType = PsiPopup::AlertFile;
+	}
+	else {
+		playSound(option.onevent[eSystem]);
+
+		AuthEvent *ae = (AuthEvent *)e;
+		if(ae->authType() == "subscribe") {
+			if(option.autoAuth) {
+				// Check if we want to request auth as well
+				UserListItem *u = d->userList.find(ae->from());
+				if (!u || (u->subscription().type() != Subscription::Both && u->subscription().type() != Subscription::To)) {
+					dj_addAuth(ae->from());
+				}
+				else {
+					dj_auth(ae->from());
+				}
+				putToQueue = false;
+			}
+		} 
+		else if(ae->authType() == "subscribed") {
+			if(!option.notifyAuth) 
+				putToQueue = false;
+		}
+		else if(ae->authType() == "unsubscribe") {
+			putToQueue = false;
+		}
+	}
+
+#if !defined(Q_WS_MAC) || !defined(HAVE_GROWL)
+	// Do the popup test earlier (to avoid needless JID lookups)
+	if ((popupType == PsiPopup::AlertChat && option.ppChat) || (popupType == PsiPopup::AlertMessage && option.ppMessage) || (popupType == PsiPopup::AlertHeadline && option.ppHeadline) || (popupType == PsiPopup::AlertFile && option.ppFile))
+#endif
+	if ( doPopup && d->doPopups && makeSTATUS(status()) != STATUS_DND ) {
+		Resource r;
+		UserListItem *u = findFirstRelavent(j);
+		if ( u )
+			r = *(u->priority());
+
+		if (((popupType == PsiPopup::AlertChat && option.ppChat) || (popupType == PsiPopup::AlertMessage && option.ppMessage) || (popupType == PsiPopup::AlertHeadline && option.ppHeadline) || (popupType == PsiPopup::AlertFile && option.ppFile)) && makeSTATUS(status()) != STATUS_DND) {
+			PsiPopup *popup = new PsiPopup(popupType, this);
+			popup->setData(j, r, u, e);
+		}
+#if defined(Q_WS_MAC) && defined(HAVE_GROWL)
+		PsiGrowlNotifier::instance()->popup(this, popupType, j, r, u, e);
+#endif
+	}
+
+	if ( putToQueue )
+		queueEvent(e);
+	else
+		delete e;
+}
+
+// put an event into the event queue, and update the related alerts
+void PsiAccount::queueEvent(PsiEvent *e)
+{
+	// do we have roster item for this?
+	UserListItem *u = find(e->jid());
+	if(!u) {
+		// create item
+		u = new UserListItem;
+		u->setJid(e->jid());
+		u->setInList(false);
+#ifdef AVATARS
+		u->setAvatarFactory(d->avatarFactory);
+#endif
+
+		// is it a private groupchat?
+		Jid j = u->jid();
+		GCContact *c = findGCContact(j);
+		if(c) {
+			u->setName(j.resource());
+			u->setPrivate(true);
+
+			// make a resource so the contact appears online
+			UserResource ur;
+			ur.setName(j.resource());
+			ur.setStatus(c->status);
+			u->userResourceList().append(ur);
+		}
+
+		// treat it like a push  [pushinfo]
+		//VCard info;
+		//if(readUserInfo(item->jid, &info) && !info.field[vNickname].isEmpty())
+		//	item->nick = info.field[vNickname];
+		//else {
+		//	if(localStatus != STATUS_OFFLINE)
+		//		serv->getVCard(item->jid);
+		//}
+
+		d->userList.append(u);
+	}
+
+	//printf("queuing message from [%s] for [%s].\n", e->from().full().latin1(), e->jid().full().latin1());
+	d->eventQueue->enqueue(e);
+
+	updateReadNext(e->jid());
+	if(option.raise)
+		d->psi->raiseMainwin();
+
+	// udpate the roster
+	cpUpdate(*u);
+
+	bool noPopup = false;
+	if(d->loginStatus.isAvailable()) {
+		QString show = d->loginStatus.show();
+		if(show == "dnd")
+			noPopup = true;
+		else if((show == "away" || show == "xa") && option.noAwayPopup)
+			noPopup = true;
+	}
+
+	 if (!noPopup) {
+		bool doPopup = false;
+
+		// Check to see if we need to popup
+		 if(e->type() == PsiEvent::Message) {
+			MessageEvent *me = (MessageEvent *)e;
+			const Message &m = me->message();
+			 if (m.type() == "chat")
+				doPopup = option.popupChats;
+			else if (m.type() == "headline")
+				doPopup = option.popupHeadlines;
+			else
+				doPopup = option.popupMsgs;
+		} else if (e->type() == PsiEvent::File) {
+			doPopup = option.popupFiles;
+		}
+		else
+			doPopup = option.popupMsgs;
+
+		// Popup
+		if (doPopup) {
+			UserListItem *u = find(e->jid());
+			if (u && (!option.noUnlistedPopup || u->inList()))
+				openNextEvent(*u);
+		}
+
+	}
+}
+
+// take the next event from the queue and display it
+void PsiAccount::openNextEvent(const UserListItem &u)
+{
+	PsiEvent *e = d->eventQueue->peek(u.jid());
+	if(!e)
+		return;
+
+	psi()->processEvent(e);
+}
+
+void PsiAccount::openNextEvent()
+{
+	PsiEvent *e = d->eventQueue->peekNext();
+	if(!e)
+		return;
+
+	if(e->type() == PsiEvent::PGP) {
+		psi()->processEvent(e);
+		return;
+	}
+
+	UserListItem *u = find(e->jid());
+	if(!u)
+		return;
+	openNextEvent(*u);
+}
+
+void PsiAccount::updateReadNext(const Jid &j)
+{
+	// update eventdlg's read-next
+	EventDlg *w = (EventDlg *)dialogFind("EventDlg", j);
+	if(w) {
+		Icon *nextAnim = 0;
+		int nextAmount = d->eventQueue->count(j);
+		if(nextAmount > 0)
+			nextAnim = is->event2icon(d->eventQueue->peek(j));
+		w->updateReadNext(nextAnim, nextAmount);
+	}
+
+	queueChanged();
+}
+
+void PsiAccount::processReadNext(const Jid &j)
+{
+	UserListItem *u = find(j);
+	if(u)
+		processReadNext(*u);
+}
+
+void PsiAccount::processReadNext(const UserListItem &u)
+{
+	EventDlg *w = (EventDlg *)dialogFind("EventDlg", u.jid());
+	if(!w) {
+		// this should NEVER happen
+		return;
+	}
+
+	// peek the event
+	PsiEvent *e = d->eventQueue->peek(u.jid());
+	if(!e)
+		return;
+
+	bool isChat = false;
+	if(e->type() == PsiEvent::Message) {
+		MessageEvent *me = (MessageEvent *)e;
+		const Message &m = me->message();
+		if(m.type() == "chat")
+			isChat = true;
+	}
+
+	// if it's a chat message, just open the chat window.  there is no need to do
+	// further processing.  the chat window will remove it from the queue, update
+	// the cvlist, etc etc.
+	if(isChat) {
+		openChat(e->from());
+		return;
+	}
+
+	// remove from queue
+	e = d->eventQueue->dequeue(u.jid());
+
+	// update the eventdlg
+	w->updateEvent(e);
+	delete e;
+
+	// update the contact
+	cpUpdate(u);
+
+	updateReadNext(u.jid());
+}
+
+void PsiAccount::processChats(const Jid &j)
+{
+	//printf("processing chats for [%s]\n", j.full().latin1());
+	ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", j);
+	if(!c)
+		return;
+
+	// extract the chats
+	QPtrList<PsiEvent> chatList;
+	d->eventQueue->extractChats(&chatList, j);
+	chatList.setAutoDelete(true);
+
+	if(!chatList.isEmpty()) {
+		// dump the chats into the chat window, and remove the related cvlist alerts
+		QPtrListIterator<PsiEvent> it(chatList);
+		for(PsiEvent *e; (e = it.current()); ++it) {
+			MessageEvent *me = (MessageEvent *)e;
+			const Message &m = me->message();
+
+			// process the message
+			if(!me->sentToChatWindow())
+				c->incomingMessage(m);
+		}
+
+		QPtrList<UserListItem> ul = findRelavent(j);
+		if(!ul.isEmpty()) {
+			UserListItem *u = ul.first();
+			cpUpdate(*u);
+			updateReadNext(u->jid());
+		}
+	}
+}
+
+void PsiAccount::openChat(const Jid &j)
+{
+	ChatDlg *c = ensureChatDlg(j);
+	processChats(j);
+	if ( option.useTabs )
+	{
+		if ( !d->psi->isChatTabbed(c) )
+		{
+			//get a tab from the psicon
+			d->psi->getTabs()->addChat(c);
+		}
+		TabDlg* tabSet = d->psi->getManagingTabs(c);
+		tabSet->selectTab(c);
+	}
+	bringToFront(c);
+}
+
+void PsiAccount::chatMessagesRead(const Jid &j)
+{
+	if(option.alertOpenChats)
+		processChats(j);
+}
+
+void PsiAccount::logEvent(const Jid &j, PsiEvent *e)
+{
+	EDBHandle *h = new EDBHandle(d->psi->edb());
+	connect(h, SIGNAL(finished()), SLOT(edb_finished()));
+	h->append(j, e);
+}
+
+void PsiAccount::edb_finished()
+{
+	EDBHandle *h = (EDBHandle *)sender();
+	delete h;
+}
+
+void PsiAccount::openGroupChat(const Jid &j)
+{
+	QString str = j.userHost();
+	bool found = false;
+	for(QStringList::ConstIterator it = d->groupchats.begin(); it != d->groupchats.end(); ++it) {
+		if((*it) == str) {
+			found = true;
+			break;
+		}
+	}
+	if(!found)
+		d->groupchats += str;
+
+	GCMainDlg *w = new GCMainDlg(this, j);
+	connect(w, SIGNAL(aSend(const Message &)), SLOT(dj_sendMessage(const Message &)));
+	connect(d->psi, SIGNAL(emitOptionsUpdate()), w, SLOT(optionsUpdate()));
+	w->show();
+}
+
+bool PsiAccount::groupChatJoin(const QString &host, const QString &room, const QString &nick)
+{
+	return d->client->groupChatJoin(host, room, nick);
+}
+
+void PsiAccount::groupChatChangeNick(const QString &host, const QString &room, const QString& nick, const Status &s)
+{
+	d->client->groupChatChangeNick(host, room, nick, s);
+}
+
+void PsiAccount::groupChatSetStatus(const QString &host, const QString &room, const Status &s)
+{
+	d->client->groupChatSetStatus(host, room, s);
+}
+
+void PsiAccount::groupChatLeave(const QString &host, const QString &room)
+{
+	d->groupchats.remove(room + '@' + host);
+	d->client->groupChatLeave(host, room);
+}
+
+GCContact *PsiAccount::findGCContact(const Jid &j)
+{
+	QPtrListIterator<GCContact> it(d->gcbank);
+	for(GCContact *c; (c = it.current()); ++it) {
+		if(c->jid.compare(j))
+			return c;
+	}
+	return 0;
+}
+
+QStringList PsiAccount::groupchats() const
+{
+	return d->groupchats;
+}
+
+void PsiAccount::client_groupChatJoined(const Jid &j)
+{
+	d->client->groupChatSetStatus(j.host(), j.user(), d->loginStatus);
+
+	GCMainDlg *m = (GCMainDlg *)dialogFind("GCMainDlg", Jid(j.userHost()));
+	if(m) {
+		m->joined();
+		return;
+	}
+	GCJoinDlg *w = (GCJoinDlg *)dialogFind("GCJoinDlg", j);
+	if(!w)
+		return;
+	w->joined();
+
+	openGroupChat(j);
+}
+
+void PsiAccount::client_groupChatLeft(const Jid &j)
+{
+	// remove all associated groupchat contacts from the bank
+	QPtrListIterator<GCContact> it(d->gcbank);
+	for(GCContact *c; (c = it.current());) {
+		// contact from this room?
+		if(!c->jid.compare(j, false)) {
+			++it;
+			continue;
+		}
+		UserListItem *u = find(c->jid);
+		if(!u) {
+			++it;
+			continue;
+		}
+
+		simulateContactOffline(u);
+		d->gcbank.removeRef(c);
+	}
+}
+
+void PsiAccount::client_groupChatPresence(const Jid &j, const Status &s)
+{
+	GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(j.userHost()));
+	if(!w)
+		return;
+
+	GCContact *c = findGCContact(j);
+	if(!c) {
+		c = new GCContact;
+		c->jid = j;
+		c->status = s;
+		d->gcbank.append(c);
+	}
+
+	w->presence(j.resource(), s);
+
+	// pass through the core presence handling also
+	Resource r;
+	r.setName(j.resource());
+	r.setStatus(s);
+	if(s.isAvailable())
+		client_resourceAvailable(j, r);
+	else
+		client_resourceUnavailable(j, j.resource());
+}
+
+void PsiAccount::client_groupChatError(const Jid &j, int code, const QString &str)
+{
+	GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(j.userHost()));
+	if(w) {
+		w->error(code, str);
+	}
+	else {
+		GCJoinDlg *w = (GCJoinDlg *)dialogFind("GCJoinDlg", j);
+		if(w) {
+			w->error(code, str);
+		}
+	}
+}
+
+QStringList PsiAccount::hiddenChats(const Jid &j) const
+{
+	QStringList list;
+
+	QPtrListIterator<item_dialog2> it(d->dialogList);
+	for(item_dialog2 *i; (i = it.current()); ++it) {
+		if(i->className == "ChatDlg" && i->jid.compare(j, false))
+			list += i->jid.resource();
+	}
+
+	return list;
+}
+
+void PsiAccount::slotCheckVCard()
+{
+	JT_VCard *j = (JT_VCard *)sender();
+	if(!j->success() && j->statusCode() == Task::ErrDisc) {
+		setNick(d->jid.user());
+		return;
+	}
+
+	if(j->vcard().isEmpty()) {
+		changeVCard();
+		return;
+	}
+
+	if(!j->vcard().nickName().isEmpty()) {
+		d->nickFromVCard = true;
+		setNick(j->vcard().nickName());
+	}
+	else
+		setNick(d->jid.user());
+}
+
+void PsiAccount::setNick(const QString &nick)
+{
+	d->self.setName(nick);
+	cpUpdate(d->self);
+	nickChanged();
+}
+
+QString PsiAccount::nick() const
+{
+	return d->self.name();
+}
+
+void PsiAccount::pgpToggled(bool b)
+{
+	QString oldkey = d->cur_pgpSecretKeyID;
+
+	// gaining pgp?
+	if(b)
+		d->cur_pgpSecretKeyID = d->acc.pgpSecretKeyID;
+	// losing it?
+	else {
+		d->cur_pgpSecretKeyID = "";
+	}
+
+	if(oldkey != d->cur_pgpSecretKeyID) {
+		pgpKeyChanged();
+		// resend status if online
+		if(loggedIn())
+			setStatusDirect(d->loginStatus);
+	}
+}
+
+void PsiAccount::pgpKeysUpdated()
+{
+	OpenPGP::KeyList list = d->psi->pgp()->publicKeys();
+
+	// are there any sigs that need verifying?
+	QPtrListIterator<UserListItem> it(d->userList);
+	for(UserListItem *u; (u = it.current()); ++it) {
+		UserResourceList &rl = u->userResourceList();
+		for(UserResourceList::Iterator rit = rl.begin(); rit != rl.end(); ++rit) {
+			UserResource &r = *rit;
+			if(!r.status().xsigned().isEmpty() && r.pgpVerifyStatus() == OpenPGP::VerifyNoKey) {
+				bool haveKey = false;
+				QString key = r.publicKeyID();
+				for(OpenPGP::KeyList::ConstIterator kit = list.begin(); kit != list.end(); ++kit) {
+					if((*kit).keyID() == key) {
+						haveKey = true;
+						break;
+					}
+				}
+				if(haveKey)
+					tryVerify(u, &r);
+			}
+		}
+	}
+}
+
+void PsiAccount::verifyStatus(const Jid &j, const Status &s)
+{
+	PGPTransaction *t = new PGPTransaction(d->psi->pgp());
+	t->setJid(j);
+	connect(t, SIGNAL(finished(bool)), SLOT(pgp_verifyFinished(bool)));
+	QCString cs = s.status().utf8();
+	QByteArray buf(cs.length());
+	memcpy(buf.data(), cs.data(), buf.size());
+	t->verify(buf, OpenPGP::addHeaderFooter(s.xsigned(), 1));
+
+	//printf("%s: verifying\n", j.full().latin1());
+}
+
+void PsiAccount::trySignPresence()
+{
+	OpenPGP::Request *r = new OpenPGP::Request(d->psi->pgp());
+	connect(r, SIGNAL(finished(bool)), SLOT(pgp_signFinished(bool)));
+	connect(r, SIGNAL(needPassphrase()), SLOT(pgp_needPassphrase()));
+	QCString cs = d->loginStatus.status().utf8();
+	QByteArray buf(cs.length());
+	memcpy(buf.data(), cs.data(), buf.size());
+	r->sign(buf, d->cur_pgpSecretKeyID);
+}
+
+void PsiAccount::pgp_needPassphrase()
+{
+	OpenPGP::Request *r = (OpenPGP::Request *)sender();
+	d->ppreq = r;
+
+	if(pgp_passphrases.contains(d->cur_pgpSecretKeyID))
+		r->submitPassphrase(pgp_passphrases[d->cur_pgpSecretKeyID]);
+	else
+		promptPassphrase();
+}
+
+void PsiAccount::promptPassphrase()
+{
+	if(d->ppdlg)
+		d->ppdlg->unblock();
+	else {
+		PassphraseDlg *w = new PassphraseDlg(0);
+		connect(w, SIGNAL(submitPassphrase(const QString &)), SLOT(submitPassphrase(const QString &)));
+		connect(w, SIGNAL(rejectPassphrase()), SLOT(rejectPassphrase()));
+		w->setCaption(tr("%1: OpenPGP Passphrase").arg(name()));
+		w->show();
+
+		d->ppdlg = w;
+	}
+}
+
+void PsiAccount::submitPassphrase(const QString &pp)
+{
+	d->ppreq->submitPassphrase(pp);
+}
+
+void PsiAccount::rejectPassphrase()
+{
+	d->ppdlg = 0;
+
+	// cancel request
+	if(d->ppreq)
+		d->ppreq->deleteLater();
+
+	logout();
+}
+
+void PsiAccount::pgp_signFinished(bool ok)
+{
+	OpenPGP::Request *r = (OpenPGP::Request *)sender();
+	bool badPassphrase = r->badPassphrase();
+	QString sig;
+	if(ok)
+		sig = r->signature();
+	if(d->ppdlg) {
+		pgp_passphrases[d->cur_pgpSecretKeyID] = d->ppdlg->passphrase();
+	}
+	r->deleteLater();
+
+	if(ok) {
+		Status s = d->loginStatus;
+		s.setXSigned(OpenPGP::stripHeaderFooter(sig));
+		setStatusActual(s);
+	}
+	else {
+		if(badPassphrase) {
+			pgp_passphrases.erase(d->cur_pgpSecretKeyID);
+			QMessageBox::information(d->ppdlg ? d->ppdlg : 0, CAP(tr("Error")), tr("You entered a bad passphrase.  Please try again."));
+			QTimer::singleShot(0, this, SLOT(trySignPresence()));
+			return;
+		}
+		else {
+			QMessageBox::information(d->ppdlg ? d->ppdlg : 0, CAP(tr("Error")), tr("There was an error during OpenPGP processing.  Check your settings and try again."));
+			logout();
+		}
+	}
+
+	// remove the dialog if it is there
+	if(d->ppdlg) {
+		d->ppdlg->deleteLater();
+		d->ppdlg = 0;
+	}
+}
+
+void PsiAccount::pgp_verifyFinished(bool b)
+{
+	PGPTransaction *t = (PGPTransaction *)sender();
+
+	Jid j = t->jid();
+	//printf("%s: verify complete\n", j.full().latin1());
+	QPtrList<UserListItem> list = findRelavent(j);
+	QPtrListIterator<UserListItem> it(list);
+	for(UserListItem *u; (u = it.current()); ++it) {
+		UserResourceList::Iterator rit = u->userResourceList().find(j.resource());
+		bool found = (rit == u->userResourceList().end()) ? false: true;
+		if(!found)
+			continue;
+		UserResource &ur = *rit;
+
+		if(b) {
+			//printf("vergood\n");
+			ur.setPublicKeyID(t->keyID());
+			ur.setPGPVerifyStatus(t->verifyResult());
+			ur.setSigTimestamp(t->timestamp());
+
+			// if the key doesn't match the assigned key, unassign it
+			if(t->keyID() != u->publicKeyID())
+				u->setPublicKeyID("");
+		}
+		else {
+			//QMessageBox::information(0, "sig verify error", QString("error verifying [%1]").arg(u->jid().full()));
+			ur.setPGPVerifyStatus(OpenPGP::VerifyError);
+		}
+
+		//printf("updating [%s]\n", u->jid().full().latin1());
+		cpUpdate(*u);
+	}
+
+	t->deleteLater();
+}
+
+int PsiAccount::sendMessageEncrypted(const Message &_m)
+{
+	if(!ensureKey(_m.to()))
+		return -1;
+	QString key = findFirstRelavent(_m.to())->publicKeyID();
+
+	PGPTransaction *pt = new PGPTransaction(d->psi->pgp());
+	Message m = _m;
+	pt->setMessage(m); // keep a copy
+	//QByteArray a = m.generateEncryptablePayload(d->client->doc());
+	QCString cs = m.body().utf8();
+	QByteArray a(cs.length());
+	memcpy(a.data(), cs.data(), a.size());
+
+	// encrypt
+	QStringList rcpt;
+	rcpt += key;
+	connect(pt, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
+	pt->encrypt(a, rcpt);
+
+	return pt->id();
+}
+
+void PsiAccount::pgp_finished(bool b)
+{
+	PGPTransaction *pt = (PGPTransaction *)sender();
+
+	int x = pt->id();
+	if(pt->type() == OpenPGP::Encrypt) {
+		if(b) {
+			Message m = pt->message();
+			// log the message here, before we encrypt it
+			if(d->acc.opt_log) {
+				MessageEvent *me = new MessageEvent(m, this);
+				me->setOriginLocal(true);
+				me->setTimeStamp(QDateTime::currentDateTime());
+				logEvent(m.to(), me);
+				delete me;
+			}
+
+			Message mwrap;
+			mwrap.setTo(m.to());
+			mwrap.setType(m.type());
+			QString enc = OpenPGP::stripHeaderFooter(pt->encrypted());
+			mwrap.setBody(tr("[ERROR: This message is encrypted, and you are unable to decrypt it.]"));
+			mwrap.setXEncrypted(enc);
+			mwrap.setWasEncrypted(true);
+			// FIXME: Should be done cleaner, with an extra method in Iris
+			if (m.containsEvent(OfflineEvent)) mwrap.addEvent(OfflineEvent);
+			if (m.containsEvent(DeliveredEvent)) mwrap.addEvent(DeliveredEvent);
+			if (m.containsEvent(DisplayedEvent)) mwrap.addEvent(DisplayedEvent);
+			if (m.containsEvent(ComposingEvent)) mwrap.addEvent(ComposingEvent);
+			if (m.containsEvent(CancelEvent)) mwrap.addEvent(CancelEvent);
+			dj_sendMessage(mwrap);
+		}
+		encryptedMessageSent(x, b);
+	}
+
+	pt->deleteLater();
+}
+
+void PsiAccount::pgp_decryptFinished(bool b)
+{
+	PGPTransaction *pt = (PGPTransaction *)sender();
+
+	bool tryAgain = false;
+	if(b) {
+		Message m = pt->message();
+		//if(m.applyDecryptedPayload(pt->decrypted(), d->client->doc()))
+		QByteArray buf = pt->decrypted();
+		QCString cs(buf.size()+1);
+		memcpy(cs.data(), buf.data(), buf.size());
+		QString str = QString::fromUtf8(cs);
+		m.setBody(str);
+		m.setXEncrypted("");
+		m.setWasEncrypted(true);
+		processIncomingMessage(m);
+
+		//else
+		//	QMessageBox::information(0, CAP(tr("Error")), tr("A successful decryption operation resulted in an invalid message, so it has been ignored."));
+	}
+	else {
+		if(loggedIn()) {
+			Message m;
+			m.setTo(pt->message().from());
+			m.setType("error");
+			m.setBody(pt->message().body());
+			Stanza::Error err;
+			err.condition = 500;
+			err.text = "Unable to decrypt";
+			m.setError(err);
+			d->client->sendMessage(m);
+		}
+	}
+
+	pt->deleteLater();
+
+	if(tryAgain) {
+		processEncryptedMessageNext();
+	}
+	else {
+		processEncryptedMessageDone();
+	}
+}
+
+void PsiAccount::processEncryptedMessage(const Message &m)
+{
+	// decrypt
+	PGPTransaction *t = new PGPTransaction(d->psi->pgp());
+	t->setMessage(m); // keep a copy
+	connect(t, SIGNAL(needPassphrase()), SLOT(pgp_needPassphrase()));
+	connect(t, SIGNAL(finished(bool)), SLOT(pgp_decryptFinished(bool)));
+	QString str = OpenPGP::addHeaderFooter(m.xencrypted(), 0);
+	t->decrypt(str);
+}
+
+void PsiAccount::processMessageQueue()
+{
+	while(!d->messageQueue.isEmpty()) {
+		Message *mp = d->messageQueue.getFirst();
+
+		// encrypted?
+		if(d->psi->pgp() && !mp->xencrypted().isEmpty()) {
+			processEncryptedMessageNext();
+			break;
+		}
+
+		processIncomingMessage(*mp);
+		d->messageQueue.removeRef(mp);
+	}
+}
+
+void PsiAccount::processEncryptedMessageNext()
+{
+	// 'peek' and try to process it
+	Message *mp = d->messageQueue.getFirst();
+	processEncryptedMessage(*mp);
+}
+
+void PsiAccount::processEncryptedMessageDone()
+{
+	// 'pop' the message
+	Message *mp = d->messageQueue.getFirst();
+	d->messageQueue.removeRef(mp);
+
+	// do the rest of the queue
+	processMessageQueue();
+}
+
+void PsiAccount::optionsUpdate()
+{
+	d->cp->updateEntry(d->self);
+}
+
+QString PsiAccount::resultToString(int result)
+{
+	QString s;
+	switch(result) {
+		case QCA::TLS::NoCert:
+			s = tr("The server did not present a certificate.");
+			break;
+		case QCA::TLS::Valid:
+			s = tr("Certificate is valid.");
+			break;
+		case QCA::TLS::HostMismatch:
+			s = tr("The hostname does not match the one the certificate was issued to.");
+			break;
+		case QCA::TLS::Rejected:
+			s = tr("Root CA is marked to reject the specified purpose.");
+			break;
+		case QCA::TLS::Untrusted:
+			s = tr("Certificate not trusted for the required purpose.");
+			break;
+		case QCA::TLS::SignatureFailed:
+			s = tr("Invalid signature.");
+			break;
+		case QCA::TLS::InvalidCA:
+			s = tr("Invalid CA certificate.");
+			break;
+		case QCA::TLS::InvalidPurpose:
+			s = tr("Invalid certificate purpose.");
+			break;
+		case QCA::TLS::SelfSigned:
+			s = tr("Certificate is self-signed.");
+			break;
+		case QCA::TLS::Revoked:
+			s = tr("Certificate has been revoked.");
+			break;
+		case QCA::TLS::PathLengthExceeded:
+			s = tr("Maximum certificate chain length exceeded.");
+			break;
+		case QCA::TLS::Expired:
+			s = tr("Certificate has expired.");
+			break;
+		case QCA::TLS::Unknown:
+		default:
+			s = tr("General certificate validation error.");
+			break;
+	}
+	return s;
+}
+
+void PsiAccount::invokeGCMessage(const Jid &j)
+{
+	GCContact *c = findGCContact(j);
+	if(!c)
+		return;
+
+	// create dummy item, open chat, then destroy item.  HORRIBLE HACK!
+	UserListItem *u = new UserListItem;
+	u->setJid(j);
+	u->setInList(false);
+	u->setName(j.resource());
+	u->setPrivate(true);
+#ifdef AVATARS
+	u->setAvatarFactory(d->avatarFactory);
+#endif
+
+	// make a resource so the contact appears online
+	UserResource ur;
+	ur.setName(j.resource());
+	ur.setStatus(c->status);
+	u->userResourceList().append(ur);
+
+	d->userList.append(u);
+	actionSendMessage(j);
+	d->userList.remove(u);
+}
+
+void PsiAccount::invokeGCChat(const Jid &j)
+{
+	GCContact *c = findGCContact(j);
+	if(!c)
+		return;
+
+	// create dummy item, open chat, then destroy item.  HORRIBLE HACK!
+	UserListItem *u = new UserListItem;
+	u->setJid(j);
+	u->setInList(false);
+	u->setName(j.resource());
+	u->setPrivate(true);
+#ifdef AVATARS
+	u->setAvatarFactory(d->avatarFactory);
+#endif
+
+	// make a resource so the contact appears online
+	UserResource ur;
+	ur.setName(j.resource());
+	ur.setStatus(c->status);
+	u->userResourceList().append(ur);
+
+	d->userList.append(u);
+	actionOpenChat(j);
+	d->userList.remove(u);
+}
+
+void PsiAccount::invokeGCInfo(const Jid &j)
+{
+	actionInfo(j);
+}
+
+void PsiAccount::invokeGCFile(const Jid &j)
+{
+	actionSendFile(j);
+}
+
+void PsiAccount::toggleSecurity(const Jid &j, bool b)
+{
+	UserListItem *u = findFirstRelavent(j);
+	if(!u)
+		return;
+
+	bool isBare = j.resource().isEmpty();
+	bool isPriority = false;
+
+	// sick sick sick sick sick sick
+	UserResource *r1, *r2;
+	r1 = r2 = 0;
+	UserResourceList::Iterator it1 = u->userResourceList().find(j.resource());
+	UserResourceList::Iterator it2 = u->userResourceList().priority();
+	r1 = (it1 != u->userResourceList().end() ? &(*it1) : 0);
+	r2 = (it2 != u->userResourceList().end() ? &(*it2) : 0);
+	if(r1 && (r1 == r2))
+		isPriority = true;
+
+	bool needUpdate = false;
+	bool sec = u->isSecure(j.resource());
+	if(sec != b) {
+		u->setSecure(j.resource(), b);
+		needUpdate = true;
+	}
+	if(isBare && r2) {
+		sec = u->isSecure(r2->name());
+		if(sec != b) {
+			u->setSecure(r2->name(), b);
+			needUpdate = true;
+		}
+	}
+	if(isPriority) {
+		sec = u->isSecure("");
+		if(sec != b) {
+			u->setSecure("", b);
+			needUpdate = true;
+		}
+	}
+
+	if(needUpdate)
+		cpUpdate(*u);
+}
+
+bool PsiAccount::ensureKey(const Jid &j)
+{
+	if(!d->psi->pgp())
+		return false;
+
+	UserListItem *u = findFirstRelavent(j);
+	if(!u)
+		return false;
+
+	// no key?
+	if(u->publicKeyID().isEmpty()) {
+		// does the user have any presence signed with a key?
+		QString akey;
+		const UserResourceList &rl = u->userResourceList();
+		for(UserResourceList::ConstIterator it = rl.begin(); it != rl.end(); ++it) {
+			const UserResource &r = *it;
+			if(!r.publicKeyID().isEmpty()) {
+				akey = r.publicKeyID();
+				break;
+			}
+		}
+
+		bool inList = false;
+		OpenPGP::KeyList list = d->psi->pgp()->publicKeys();
+		for(OpenPGP::KeyList::ConstIterator lit = list.begin(); lit != list.end(); ++lit) {
+			const OpenPGP::Key &k = *lit;
+			if(k.keyID() == akey) {
+				inList = true;
+				break;
+			}
+		}
+
+		if(akey.isEmpty() || !inList) {
+			int n = QMessageBox::information(0, CAP(tr("No key")), tr(
+				"<p>Psi was unable to locate the OpenPGP key to use for <b>%1</b>.<br>"
+				"<br>"
+				"This can happen if you do not have the key that the contact is advertising "
+				"via signed presence, or if the contact is not advertising any key at all.</p>"
+				).arg(u->jid().full()), tr("&Choose key manually"), tr("Do &nothing"));
+			if(n != 0)
+				return false;
+		}
+
+		PGPKeyDlg *w = new PGPKeyDlg(list, akey, 0);
+		w->setCaption(tr("Public Key: %1").arg(j.full()));
+		int r = w->exec();
+		QString key;
+		if(r == QDialog::Accepted)
+			key = w->keyID();
+		delete w;
+		if(key.isEmpty())
+			return false;
+		u->setPublicKeyID(key);
+		cpUpdate(*u);
+	}
+
+	return true;
+}
+
+
+//----------------------------------------------------------------------------
+// PGPKeyDlg
+//----------------------------------------------------------------------------
+class KeyViewItem : public QListViewItem
+{
+public:
+	KeyViewItem(const QString &_keyID, QListView *par)
+	:QListViewItem(par)
+	{
+		keyID = _keyID;
+	}
+
+	QString keyID;
+};
+
+class PGPKeyDlg::Private
+{
+public:
+	Private() {}
+
+	QString keyID;
+	QString userID;
+};
+
+PGPKeyDlg::PGPKeyDlg(const OpenPGP::KeyList &list, const QString &choose, QWidget *parent, const char *name)
+:PGPKeyUI(parent, name, true)
+{
+	d = new Private;
+
+	connect(lv_keys, SIGNAL(doubleClicked(QListViewItem *)), SLOT(qlv_doubleClicked(QListViewItem *)));
+	connect(pb_ok, SIGNAL(clicked()), SLOT(do_accept()));
+	connect(pb_cancel, SIGNAL(clicked()), SLOT(reject()));
+
+	QListViewItem *isel = 0;
+	for(OpenPGP::KeyList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+		const OpenPGP::Key &k = *it;
+		KeyViewItem *i = new KeyViewItem(k.keyID(), lv_keys);
+		//i->setPixmap(0, IconsetFactory::icon("psi/gpg-yes"));
+		i->setText(0, k.keyID().right(8));
+		i->setText(1, k.userID());
+
+		if(!choose.isEmpty() && k.keyID() == choose) {
+			lv_keys->setSelected(i, true);
+			isel = i;
+		}
+	}
+	if(lv_keys->childCount() > 0 && !isel)
+		lv_keys->setSelected(lv_keys->firstChild(), true);
+	else if(isel)
+		lv_keys->ensureItemVisible(isel);
+}
+
+PGPKeyDlg::~PGPKeyDlg()
+{
+	delete d;
+}
+
+QString PGPKeyDlg::keyID() const
+{
+	return d->keyID;
+}
+
+QString PGPKeyDlg::userID() const
+{
+	return d->userID;
+}
+
+void PGPKeyDlg::qlv_doubleClicked(QListViewItem *i)
+{
+	lv_keys->setSelected(i, true);
+	do_accept();
+}
+
+void PGPKeyDlg::do_accept()
+{
+	KeyViewItem *i = (KeyViewItem *)lv_keys->selectedItem();
+	if(!i) {
+		QMessageBox::information(this, tr("Error"), tr("Please select a key."));
+		return;
+	}
+	d->keyID = i->keyID;
+	d->userID = i->text(1);
+	accept();
+}
+
+#include "psiaccount.moc"
Index: psi/tags/0.10-os2-b1/src/psiaccount.h
===================================================================
--- psi/tags/0.10-os2-b1/src/psiaccount.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psiaccount.h	(revision 38)
@@ -0,0 +1,373 @@
+/*
+ * psiaccount.h - handles a Psi account
+ * Copyright (C) 2001-2005  Justin Karneges
+ *
+ * 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.
+ *
+ * You can also redistribute and/or modify this program under the
+ * terms of the Psi License, specified in the accompanied COPYING
+ * file, as published by the Psi Project; either dated January 1st,
+ * 2005, 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIACCOUNT_H
+#define PSIACCOUNT_H
+
+#include<qlabel.h>
+#include<qptrlist.h>
+#include<qdom.h>
+#include"xmpp.h"
+#include"openpgp.h"
+#include"ui_pgpkey.h"
+#include"common.h"
+
+
+namespace XMPP
+{
+	class Status;
+	class RosterItem;
+	class Client;
+	//class StreamError;
+	class Resource;
+	class Message;
+};
+
+using namespace XMPP;
+
+class PsiCon;
+class PsiAccount;
+class PsiEvent;
+class UserAccount;
+class ContactProfile;
+class QWidget;
+class EventQueue;
+class UserResource;
+class UserListItem;
+class UserList;
+class EventDlg;
+class ChatDlg;
+class EDB;
+class QSSLCert;
+class QHostAddress;
+class AvatarFactory;
+
+class PGPKeyDlg : public PGPKeyUI
+{
+	Q_OBJECT
+public:
+	PGPKeyDlg(const OpenPGP::KeyList &, const QString &choose, QWidget *parent=0, const char *name=0);
+	~PGPKeyDlg();
+
+	QString keyID() const;
+	QString userID() const;
+
+private slots:
+	void qlv_doubleClicked(QListViewItem *);
+	void do_accept();
+
+private:
+	class Private;
+	Private *d;
+};
+
+class AccountLabel : public QLabel
+{
+	Q_OBJECT
+public:
+	AccountLabel(PsiAccount *, QWidget *parent=0, bool simpleMode=false);
+	~AccountLabel();
+
+private slots:
+	void updateName();
+	void deleteMe();
+
+private:
+	PsiAccount *pa;
+	bool simpleMode;
+};
+
+class PGPTransaction : public OpenPGP::Request
+{
+	Q_OBJECT
+public:
+	PGPTransaction(OpenPGP::Engine *);
+	~PGPTransaction();
+
+	int id() const;
+	const Message & message() const;
+	void setMessage(const Message &);
+	const QDomElement & xml() const;
+	void setXml(const QDomElement &);
+
+	Jid jid() const;
+	void setJid(const Jid &);
+
+private:
+	class Private;
+	Private *d;
+};
+
+// sick sick remove this someday please!
+#include"im.h"
+struct GCContact
+{
+	Jid jid;
+	Status status;
+};
+
+class PsiAccount : public QObject
+{
+	Q_OBJECT
+public:
+	PsiAccount(const UserAccount &acc, PsiCon *parent);
+	~PsiAccount();
+
+	bool enabled() const;
+	void setEnabled(bool e = TRUE);
+
+	bool isActive() const;
+	bool isConnected() const;
+	const QString &name() const;
+
+	const UserAccount & userAccount() const;
+	void setUserAccount(const UserAccount &);
+	const Jid & jid() const;
+	QString nameWithJid() const;
+
+	XMPP::Client *client() const;
+	ContactProfile *contactProfile() const;
+	EventQueue *eventQueue() const;
+	EDB *edb() const;
+	PsiCon *psi() const;
+	AvatarFactory *avatarFactory() const;
+	Status status() const;
+	void setStatusDirect(const Status &);
+	void setStatusActual(const Status &);
+	void login();
+	void logout(bool fast=false, const Status &s = Status("", "Logged out", 0, false));
+	bool loggedIn() const;
+	void setNick(const QString &);
+	QString nick() const;
+	QString pgpKey() const;
+	QHostAddress *localAddress() const;
+
+	QWidget *dialogFind(const char *className, const Jid &jid="");
+	void dialogRegister(QWidget *w, const Jid &jid="");
+	void dialogUnregister(QWidget *w);
+
+	void modify();
+	void changeVCard();
+	void changePW();
+	void changeStatus(int);
+	void doDisco();
+
+	void showXmlConsole();
+	void openAddUserDlg();
+	void openGroupChat(const Jid &);
+	bool groupChatJoin(const QString &host, const QString &room, const QString &nick);
+	void groupChatSetStatus(const QString &host, const QString &room, const Status &);
+	void groupChatChangeNick(const QString &host, const QString &room, const QString& nick, const Status &);
+	void groupChatLeave(const QString &host, const QString &room);
+
+	UserListItem *find(const Jid &) const;
+	QPtrList<UserListItem> findRelavent(const Jid &) const;
+	UserListItem *findFirstRelavent(const Jid &) const;
+	UserList *userList() const;
+
+	bool checkConnected(QWidget *parent=0);
+	void playSound(const QString &);
+
+	QLabel *accountLabel(QWidget *parent=0, bool simpleMode=false);
+	QStringList hiddenChats(const Jid &) const;
+
+	int sendMessageEncrypted(const Message &);
+
+	// sucks sucks sucks sucks sucks sucks sucks
+	GCContact *findGCContact(const Jid &j);
+	QStringList groupchats() const;
+
+	void toggleSecurity(const Jid &, bool);
+	bool ensureKey(const Jid &);
+	void tryVerify(UserListItem *, UserResource *);
+
+	static QString resultToString(int result);
+	static void getErrorInfo(int err, AdvancedConnector *conn, Stream *stream, QCATLSHandler *tlsHandler, QString *_str, bool *_reconn);
+
+	void deleteQueueFile();
+
+signals:
+	void disconnected();
+	void updatedActivity();
+	void updatedAccount();
+	void queueChanged();
+	void updateContact(const UserListItem &);
+	void updateContact(const Jid &);
+	void updateContact(const Jid &, bool);
+	void nickChanged();
+	void pgpKeyChanged();
+	void encryptedMessageSent(int, bool);
+	void enabledChanged();
+
+public slots:
+	void setStatus(const Status &);
+
+	void secondsIdle(int);
+	void openNextEvent();
+
+	void dj_sendMessage(const Message &, bool log=true);
+	void dj_composeMessage(const Jid &jid, const QString &body);
+	void dj_composeMessage(const Jid &jid, const QString &body, const QString &subject, const QString &thread);
+	void dj_addAuth(const Jid &);
+	void dj_add(const Jid &, const QString &, const QStringList &, bool authReq);
+	void dj_authReq(const Jid &);
+	void dj_auth(const Jid &);
+	void dj_deny(const Jid &);
+	void dj_rename(const Jid &, const QString &);
+	void dj_remove(const Jid &);
+
+	void actionDefault(const Jid &);
+	void actionRecvEvent(const Jid &);
+	void actionSendMessage(const Jid &);
+	void actionSendMessage(const JidList &);
+	void actionSendUrl(const Jid &);
+	void actionRemove(const Jid &);
+	void actionRename(const Jid &, const QString &);
+	void actionGroupRename(const QString &, const QString &);
+	void actionHistory(const Jid &);
+	void actionOpenChat(const Jid &);
+	void actionOpenChatSpecific(const Jid &);
+	void actionAgentSetStatus(const Jid &, Status &s);
+	void actionInfo(const Jid &);
+	void actionAuth(const Jid &);
+	void actionAuthRequest(const Jid &);
+	void actionAuthRemove(const Jid &);
+	void actionAdd(const Jid &);
+	void actionGroupAdd(const Jid &, const QString &);
+	void actionGroupRemove(const Jid &, const QString &);
+	void actionHistoryBox(PsiEvent *);
+	void actionTest(const Jid &);
+	void actionRegister(const Jid &);
+	void actionSearch(const Jid &);
+	void actionJoin(const Jid &);
+	void actionDisco(const Jid &, const QString &);
+	void actionInvite(const Jid &, const QString &);
+	void actionSendFile(const Jid &);
+	void actionSendFiles(const Jid &, const QStringList&);
+	void featureActivated(QString feature, Jid jid, QString node);
+
+	void actionAssignKey(const Jid &);
+	void actionUnassignKey(const Jid &);
+
+	void invokeGCMessage(const Jid &);
+	void invokeGCChat(const Jid &);
+	void invokeGCInfo(const Jid &);
+	void invokeGCFile(const Jid &);
+
+private slots:
+	void tls_handshaken();
+	void cs_connected();
+	void cs_securityLayerActivated();
+	void cs_needAuthParams(bool, bool, bool);
+	void cs_authenticated();
+	void cs_connectionClosed();
+	void cs_delayedCloseFinished();
+	void cs_warning(int);
+	void cs_error(int);
+	void client_rosterRequestFinished(bool, int, const QString &);
+	void resolveContactName();
+	void client_rosterItemAdded(const RosterItem &);
+	void client_rosterItemUpdated(const RosterItem &);
+	void client_rosterItemRemoved(const RosterItem &);
+	void client_resourceAvailable(const Jid &, const Resource &);
+	void client_resourceUnavailable(const Jid &, const Resource &);
+	void client_presenceError(const Jid &, int, const QString &);
+	void client_messageReceived(const Message &);
+	void client_subscription(const Jid &, const QString &);
+	void client_debugText(const QString &);
+	void client_groupChatJoined(const Jid &);
+	void client_groupChatLeft(const Jid &);
+	void client_groupChatPresence(const Jid &, const Status &);
+	void client_groupChatError(const Jid &, int, const QString &);
+	void client_incomingJidLink();
+	void client_incomingFileTransfer();
+
+	void reconnect();
+	void disconnect();
+	void enableNotifyOnline();
+
+	void slotClientVersionFinished();
+
+	void chatMessagesRead(const Jid &);
+
+	void slotCheckVCard();
+	void edb_finished();
+	void pgpToggled(bool);
+	void pgpKeysUpdated();
+
+	void trySignPresence();
+	void pgp_needPassphrase();
+	void pgp_signFinished(bool);
+	void pgp_verifyFinished(bool);
+	void pgp_finished(bool);
+	void pgp_decryptFinished(bool);
+
+	void submitPassphrase(const QString &);
+	void rejectPassphrase();
+
+	void optionsUpdate();
+
+	void processReadNext(const UserListItem &);
+	void processReadNext(const Jid &);
+
+private slots:
+	void handleEvent(PsiEvent *);
+
+public:
+	class Private;
+private:
+	Private *d;
+
+	void deleteAllDialogs();
+	void stateChanged();
+	void simulateContactOffline(UserListItem *);
+	void simulateRosterOffline();
+	void cpUpdate(const UserListItem &, const QString &rname="", bool fromPresence=false);
+	void logEvent(const Jid &, PsiEvent *);
+	void queueEvent(PsiEvent *);
+	void openNextEvent(const UserListItem &);
+	void updateReadNext(const Jid &);
+	ChatDlg *ensureChatDlg(const Jid &);
+	void lastStepLogin();
+	void processIncomingMessage(const Message &);
+	void processEncryptedMessage(const Message &);
+	void processMessageQueue();
+	void processEncryptedMessageNext();
+	void processEncryptedMessageDone();
+	void verifyStatus(const Jid &j, const Status &s);
+
+	void promptPassphrase();
+	void processChats(const Jid &);
+	void openChat(const Jid &);
+	EventDlg *ensureEventDlg(const Jid &);
+	friend class PsiCon;
+
+	bool isDisconnecting, notifyOnlineOk, doReconnect, usingAutoStatus, rosterDone, presenceSent, v_isActive;
+	void cleanupStream();
+
+	friend class Private;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/psiactionlist.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psiactionlist.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psiactionlist.cpp	(revision 38)
@@ -0,0 +1,356 @@
+/*
+ * psiactionlist.cpp - the customizeable action list for Psi
+ * Copyright (C) 2004  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "psiactionlist.h"
+
+#include <qobject.h>
+
+#include "mainwin_p.h"
+
+//----------------------------------------------------------------------------
+// PsiActionList::Private
+//----------------------------------------------------------------------------
+
+class PsiActionList::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(PsiActionList *_list, PsiCon *_psi);
+	~Private();
+
+private:
+	PsiActionList *list;
+	PsiCon *psi;
+
+	void createCommon();
+	void createMainWin();
+	void createMessageChatGroupchat();
+	void createMessageChat();
+	void createChatGroupchat();
+	void createMessage();
+	void createChat();
+	void createGroupchat();
+
+	struct ActionNames {
+		const char *name;
+		IconAction *action;
+	};
+
+	void createActionList( QString name, int id, ActionNames * );
+};
+
+PsiActionList::Private::Private(PsiActionList *_list, PsiCon *_psi)
+{
+	list = _list;
+	psi  = _psi;
+
+	createCommon();
+	createMainWin();
+	createMessageChatGroupchat();
+	createMessageChat();
+	createChatGroupchat();
+	createMessage();
+	createChat();
+	createGroupchat();
+}
+
+PsiActionList::Private::~Private()
+{
+	list->clear();
+}
+
+void PsiActionList::Private::createActionList( QString name, int id, ActionNames *actionlist )
+{
+	ActionList *actions = new ActionList( name, id, false );
+
+	QString aName;
+	for ( int i = 0; !(aName = QString(actionlist[i].name)).isEmpty(); i++ ) {
+		actions->addAction( aName, actionlist[i].action );
+	}
+
+	list->addList( actions );
+}
+
+void PsiActionList::Private::createCommon()
+{
+	IconAction *separatorAction = new SeparatorAction(this);
+	IconAction *spacerAction    = new SpacerAction(this);
+
+	ActionNames actions[] = {
+		{ "separator", separatorAction },
+		{ "spacer", spacerAction },
+		{ "", 0 }
+	};
+
+	createActionList( tr( "Common Actions" ), Actions_Common, actions );
+}
+
+void PsiActionList::Private::createMainWin()
+{
+	{
+		IconActionGroup *viewGroups = new IconActionGroup ( this );
+		viewGroups->setMenuText (tr("View Groups"));
+		viewGroups->setWhatsThis (tr("Toggle visibility of special roster groups"));
+		viewGroups->setUsesDropDown (true);
+		viewGroups->setExclusive (false);
+
+		IconAction *showOffline = new IconAction (tr("Show Offline Contacts"), "psi/show_offline", tr("Show Offline Contacts"), 0, viewGroups, 0, true);
+		showOffline->setWhatsThis (tr("Toggles visibility of offline contacts in roster"));
+
+		IconAction *showAway = new IconAction (tr("Show Away/XA/DnD"), "psi/show_away", tr("Show Away/XA/DnD"), 0, viewGroups, 0, true);
+		showAway->setWhatsThis (tr("Toggles visibility of away/xa/dnd contacts in roster"));
+
+		IconAction *showHidden = new IconAction (tr("Show Hidden"), "psi/show_hidden", tr("Show Hidden"), 0, viewGroups, 0, true);
+		showHidden->setWhatsThis (tr("Toggles visibility of hidden contacts in roster"));
+
+		IconAction *showAgents = new IconAction (tr("Show Agents/Transports"), "psi/disco", tr("Show Agents/Transports"), 0, viewGroups, 0, true);
+		showAgents->setWhatsThis (tr("Toggles visibility of agents/transports in roster"));
+
+		IconAction *showSelf = new IconAction (tr("Show Self Contact"), "psi/show_self", tr("Show Self Contact"), 0, viewGroups, 0, true);
+		showSelf->setWhatsThis (tr("Toggles visibility of self contact in roster"));
+
+		ActionNames actions[] = {
+			{ "view_groups",  viewGroups  },
+			{ "show_offline", showOffline },
+			{ "show_away",    showAway    },
+			{ "show_hidden",  showHidden  },
+			{ "show_agents",  showAgents  },
+			{ "show_self",    showSelf    },
+			{ "", 0 }
+		};
+
+		createActionList( tr( "Show Contacts" ), Actions_MainWin, actions );
+	}
+
+	{
+		PopupAction *optionsButton = new PopupAction (tr("&Psi"), 0, this, "optionsButton");
+		optionsButton->setWhatsThis (tr("The main Psi button, that provides access to many actions"));
+		optionsButton->setSizePolicy ( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) );
+		optionsButton->setIcon ( IconsetFactory::iconPtr("psi/psiMain"), false );
+
+		PopupAction *statusButton = new PopupAction (tr("&Status"), 0, this, "statusButton");
+		statusButton->setWhatsThis (tr("Provides a convenient way to change and to get information about current status"));
+		statusButton->setSizePolicy ( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
+
+		IconAction *eventNotifier = new EventNotifierAction(this, "EventNotifierAction");
+		eventNotifier->setWhatsThis (tr("Special item that displays number of pending events"));
+
+		ActionNames actions[] = {
+			{ "button_options", optionsButton },
+			{ "button_status",  statusButton  },
+			{ "event_notifier", eventNotifier },
+			{ "", 0 }
+		};
+
+		createActionList( tr( "Buttons" ), Actions_MainWin, actions );
+	}
+
+
+	{
+		IconAction *add_act = new MAction(IconsetFactory::icon("psi/addContact"), tr("&Add a contact"), 0, psi, this);
+
+		IconAction *lw_act = new MAction(IconsetFactory::icon("psi/xml"), tr("&XML Console"), 2, psi, this);
+
+		IconAction *actDisco = new MAction(IconsetFactory::icon("psi/disco"), tr("Service &Discovery"), 3, psi, this);
+
+//		IconAction *actReadme = new IconAction (tr("ReadMe"), tr("&ReadMe"), 0, this);
+//		actReadme->setWhatsThis (tr("Show Read Me file"));
+//
+//		IconAction *actOnlineHelp = new IconAction (tr("User Guide (Online)"), tr("User Guide (Online)"), 0, this);
+//		actOnlineHelp->setWhatsThis (tr("User Guide (Online)"));
+//		
+//		IconAction *actOnlineWiki = new IconAction (tr("Wiki (Online)"), tr("Wiki (Online)"), 0, this);
+//		actOnlineWiki->setWhatsThis (tr("Wiki (Online)"));
+//		
+//		IconAction *actOnlineHome = new IconAction (tr("Home Page (Online)"), tr("Home Page (Online)"), 0, this);
+//		actOnlineHome->setWhatsThis (tr("Home Page (Online)"));
+//
+//		IconAction *actBugReport = new IconAction (tr("Report a Bug"), tr("Report a &Bug"), 0, this);
+//		actBugReport->setWhatsThis (tr("Report a Bug"));
+
+		IconAction *actNewMessage = new IconAction (tr("New blank message"), "psi/sendMessage", tr("New &blank message"), 0, this);
+		IconAction *actJoinGroupchat = new IconAction (tr("Join Groupchat"), "psi/groupChat", tr("Join &Groupchat"), 0, this);
+		IconAction *actAccountSetup = new IconAction (tr("Account Setup"), "psi/account", tr("Acc&ount Setup"), 0, this);
+		IconAction *actOptions = new IconAction (tr("Options"), "psi/options", tr("&Options"), 0, this);
+		IconAction *actToolbars = new IconAction(tr("Configure Toolbars"), "psi/toolbars", tr("Configure Tool&bars"), 0, this);
+		IconAction *actChangeProfile = new IconAction (tr("Change Profile"), "psi/profile", tr("&Change profile"), 0, this);
+
+		IconAction *actPlaySounds = new IconAction (tr("Play sounds"), "psi/playSounds", tr("Play &sounds"), 0, this, 0, true);
+		actPlaySounds->setWhatsThis (tr("Toggles whether sound should be played or not"));
+
+		IconAction *actQuit = new IconAction (tr("Quit"), "psi/quit", tr("&Quit"), 0, this);
+		actQuit->setWhatsThis (tr("Quits Psi"));
+
+		IconAction *actTip = new IconAction (tr("Tip of the Day"), "psi/tip", tr("&Tip of the Day"), 0, this);
+		actTip->setWhatsThis (tr("See many useful tips"));
+
+		IconAction *actFileTrans = new IconAction (tr("Transfer Manager"), "psi/filemanager", tr("Trans&fer Manager"), 0, this);
+		actFileTrans->setWhatsThis (tr("Opens the Transfer Manager dialog"));
+
+		ActionNames actions[] = {
+			{ "menu_disco",           actDisco         },
+			{ "menu_add_contact",     add_act          },
+			{ "menu_new_message",     actNewMessage    },
+			{ "menu_join_groupchat",  actJoinGroupchat },
+			{ "menu_account_setup",   actAccountSetup  },
+			{ "menu_options",         actOptions       },
+			{ "menu_file_transfer",   actFileTrans     },
+			{ "menu_toolbars",        actToolbars      },
+			{ "menu_xml_console",     lw_act           },
+			{ "menu_change_profile",  actChangeProfile },
+			{ "menu_play_sounds",     actPlaySounds    },
+			{ "menu_quit",            actQuit          },
+			{ "", 0 }
+		};
+
+		createActionList( tr( "Menu Items" ), Actions_MainWin, actions );
+	}
+
+	{
+		// status actions
+		IconActionGroup *statusGroup = new IconActionGroup ( this );
+		statusGroup->setMenuText (tr("Set status"));
+		statusGroup->setWhatsThis (tr("Smaller alternative to the Status Button"));
+		statusGroup->setExclusive(false);
+		statusGroup->setUsesDropDown (true);
+
+		QString setStatusStr = tr("Changes your global status to '%1'");
+
+		bool statusExl = true;
+		IconAction *statusOnline = new IconAction (status2txt(STATUS_ONLINE), "status/online", status2txt(STATUS_ONLINE), 0, statusGroup, QString::number(STATUS_ONLINE), statusExl);
+		statusOnline->setWhatsThis (setStatusStr.arg(tr("Online")));
+
+		IconAction *statusChat = new IconAction (status2txt(STATUS_CHAT), "status/chat", status2txt(STATUS_CHAT), 0, statusGroup, QString::number(STATUS_CHAT), true);
+		statusChat->setWhatsThis (setStatusStr.arg(tr("Free for Chat")));
+
+		statusGroup->addSeparator();
+
+		IconAction *statusAway = new IconAction (status2txt(STATUS_AWAY), "status/away", status2txt(STATUS_AWAY), 0, statusGroup, QString::number(STATUS_AWAY), statusExl);
+		statusAway->setWhatsThis (setStatusStr.arg(tr("Away")));
+
+		IconAction *statusXa = new IconAction (status2txt(STATUS_XA), "status/xa", status2txt(STATUS_XA), 0, statusGroup, QString::number(STATUS_XA), statusExl);
+		statusXa->setWhatsThis (setStatusStr.arg(tr("XA")));
+
+		IconAction *statusDnd = new IconAction (status2txt(STATUS_DND), "status/dnd", status2txt(STATUS_DND), 0, statusGroup, QString::number(STATUS_DND), statusExl);
+		statusDnd->setWhatsThis (setStatusStr.arg(tr("DND")));
+
+		statusGroup->addSeparator();
+
+		IconAction *statusInvisible = new IconAction (status2txt(STATUS_INVISIBLE), "status/invisible", status2txt(STATUS_INVISIBLE), 0, statusGroup, QString::number(STATUS_INVISIBLE), statusExl);
+		statusInvisible->setWhatsThis (setStatusStr.arg(tr("Invisible")));
+
+		statusGroup->addSeparator();
+
+		IconAction *statusOffline = new IconAction (status2txt(STATUS_OFFLINE), "status/offline", status2txt(STATUS_OFFLINE), 0, statusGroup, QString::number(STATUS_OFFLINE), statusExl);
+		statusOffline->setWhatsThis (setStatusStr.arg(tr("Offline")));
+
+		ActionNames actions[] = {
+			{ "status_all",       statusGroup     },
+			{ "status_chat",      statusChat      },
+			{ "status_online",    statusOnline    },
+			{ "status_away",      statusAway      },
+			{ "status_xa",        statusXa        },
+			{ "status_dnd",       statusDnd       },
+			{ "status_invisible", statusInvisible },
+			{ "status_offline",   statusOffline   },
+			{ "", 0 }
+		};
+
+		createActionList( tr( "Status" ), Actions_MainWin, actions );
+	}
+
+	{
+		IconAction *actReadme = new IconAction (tr("ReadMe"), tr("&ReadMe"), 0, this);
+		actReadme->setWhatsThis (tr("Show Read Me file"));
+
+		IconAction *actTip = new IconAction (tr("Tip of the Day"), "psi/tip", tr("&Tip of the Day"), 0, this);
+		actTip->setWhatsThis (tr("See many useful tips"));
+
+		IconAction *actOnlineHelp = new IconAction (tr("User Guide (Online)"), tr("&User Guide (Online)"), 0, this);
+		actOnlineHelp->setWhatsThis (tr("User Guide (Online)"));
+		
+		IconAction *actOnlineWiki = new IconAction (tr("Wiki (Online)"), tr("&Wiki (Online)"), 0, this);
+		actOnlineWiki->setWhatsThis (tr("Wiki (Online)"));
+		
+		IconAction *actOnlineHome = new IconAction (tr("Home Page (Online)"), tr("&Home Page (Online)"), 0, this);
+		actOnlineHome->setWhatsThis (tr("Home Page (Online)"));
+
+		IconAction *actBugReport = new IconAction (tr("Report a Bug (Online)"), tr("Report a &Bug (Online)"), 0, this);
+		actBugReport->setWhatsThis (tr("Report a Bug (Online)"));
+
+		IconAction *actAbout = new IconAction (tr("About"), "psi/psi16", tr("&About"), 0, this);
+
+		IconAction *actAboutQt = new IconAction (tr("About Qt"), tr("About &Qt"), 0, this);
+
+		ActionNames actions[] = {
+			{ "help_readme",      actReadme     },
+			{ "help_tip",         actTip        },
+			{ "help_online_help", actOnlineHelp },
+			{ "help_online_wiki", actOnlineWiki },
+			{ "help_online_home", actOnlineHome },
+			{ "help_report_bug",  actBugReport  },
+			{ "help_about",       actAbout      },
+			{ "help_about_qt",    actAboutQt    },
+			{ "", 0 }
+		};
+
+		createActionList( tr( "Help" ), Actions_MainWin, actions );
+	}
+}
+
+void PsiActionList::Private::createMessageChatGroupchat()
+{
+}
+
+void PsiActionList::Private::createMessageChat()
+{
+}
+
+void PsiActionList::Private::createChatGroupchat()
+{
+}
+
+void PsiActionList::Private::createMessage()
+{
+}
+
+void PsiActionList::Private::createChat()
+{
+}
+
+void PsiActionList::Private::createGroupchat()
+{
+}
+
+//----------------------------------------------------------------------------
+// PsiActionList
+//----------------------------------------------------------------------------
+
+PsiActionList::PsiActionList( PsiCon *psi )
+{
+	d = new Private( this, psi );
+}
+
+PsiActionList::~PsiActionList()
+{
+	delete d;
+}
+
+#include "psiactionlist.moc"
Index: psi/tags/0.10-os2-b1/src/psiactionlist.h
===================================================================
--- psi/tags/0.10-os2-b1/src/psiactionlist.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psiactionlist.h	(revision 38)
@@ -0,0 +1,48 @@
+/*
+ * psiactionlist.h - the customizeable action list for Psi
+ * Copyright (C) 2004  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIACTIONLIST_H
+#define PSIACTIONLIST_H
+
+#include "actionlist.h"
+
+class PsiCon;
+
+class PsiActionList : public MetaActionList
+{
+public:
+	PsiActionList(PsiCon *psi);
+	~PsiActionList();
+
+	enum ActionsType {
+		Actions_Common    = (1 << 0),
+		Actions_MainWin   = (1 << 1),
+		Actions_Message   = (1 << 2),
+		Actions_Chat      = (1 << 3),
+		Actions_Groupchat = (1 << 4)
+	};
+
+public:
+	class Private;
+private:
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/psiapplication.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psiapplication.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psiapplication.cpp	(revision 38)
@@ -0,0 +1,250 @@
+/*
+ * psiapplication.cpp - subclass of QApplication to do some workarounds
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "psiapplication.h"
+
+#ifdef Q_WS_MAC
+#include<Carbon/Carbon.h>
+#endif
+
+#ifdef Q_WS_X11
+#include <time.h>
+#include <sys/time.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/SM/SMlib.h>
+
+// Atoms required for monitoring the freedesktop.org notification area
+static Atom manager_atom = 0;
+static Atom tray_selection_atom = 0;
+Window root_window = 0;
+Window tray_owner = None;
+
+//static Atom atom_KdeNetUserTime;
+static Atom kde_net_wm_user_time = 0;
+
+//#if QT_VERSION > 0x030201
+//#warning "Possibly, now it's time to clean up some 'focus stealing prevention' workaround code"
+//#endif
+
+Time   qt_x_last_input_time = CurrentTime;
+//extern Time qt_x_time;
+
+#ifdef KeyPress
+#ifndef FIXX11H_KeyPress
+#define FIXX11H_KeyPress
+const int XKeyPress = KeyPress;
+#undef KeyPress
+const int KeyPress = XKeyPress;
+#endif
+#undef KeyPress
+#endif
+#endif
+
+// mblsha:
+// currently this file contains some Anti-"focus steling prevention" code by
+// Lubos Lunak (l.lunak@kde.org)
+//
+// This should resolve all bugs with KWin3 and old Qt, but maybe it'll be useful for
+// other window managers?
+
+#ifdef Q_WS_X11
+//#undef Q_WS_X11
+#endif
+
+#ifdef Q_WS_X11
+void setTrayOwnerWindow(Display *dsp)
+{
+	/* This code is basically trying to mirror what happens in
+	 * eggtrayicon.c:egg_tray_icon_update_manager_window()
+	 */
+	// ignore events from the old tray owner
+	if (tray_owner != None)
+	{
+		XSelectInput(dsp, tray_owner, 0);
+	}
+
+	// obtain the Window handle for the new tray owner
+	XGrabServer(dsp);
+	tray_owner = XGetSelectionOwner(dsp, tray_selection_atom);
+
+	// we have to be able to spot DestroyNotify messages on the tray owner
+	if (tray_owner != None)
+	{
+		XSelectInput(dsp, tray_owner, StructureNotifyMask|PropertyChangeMask);
+	}
+	XUngrabServer(dsp);
+	XFlush(dsp);
+}
+
+#endif
+
+//----------------------------------------------------------------------------
+// PsiApplication
+//----------------------------------------------------------------------------
+
+PsiApplication::PsiApplication(int &argc, char **argv, bool GUIenabled)
+: QApplication(argc, argv, GUIenabled)
+{
+	init(GUIenabled);
+}
+
+PsiApplication::~PsiApplication()
+{
+}
+
+void PsiApplication::init(bool GUIenabled)
+{
+	Q_UNUSED(GUIenabled);
+#ifdef Q_WS_X11
+	if ( GUIenabled ) {
+		const int max = 20;
+		Atom* atoms[max];
+		char* names[max];
+		Atom atoms_return[max];
+		int n = 0;
+
+		//atoms[n] = &atom_KdeNetUserTime;
+		//names[n++] = (char *) "_KDE_NET_USER_TIME";
+
+		atoms[n] = &kde_net_wm_user_time;
+		names[n++] = (char *) "_NET_WM_USER_TIME";
+		atoms[n] = &manager_atom;
+		names[n++] = (char *) "MANAGER";
+
+		Display *dsp = qt_xdisplay();
+
+		XInternAtoms( dsp, names, n, false, atoms_return );
+
+		for (int i = 0; i < n; i++ )
+			*atoms[i] = atoms_return[i];
+
+		// get the selection type we'll use to locate the notification tray
+		char buf[32];
+		snprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", XScreenNumberOfScreen( XDefaultScreenOfDisplay(dsp) ));
+		tray_selection_atom = XInternAtom(dsp, buf, false);
+
+		// make a note of the window handle for the root window
+		root_window = QApplication::desktop()->winId();
+
+		XWindowAttributes attr;
+
+		// this is actually futile, since Qt overrides it at some
+		// unknown point in the near future.
+		XGetWindowAttributes(dsp, root_window, &attr);
+		XSelectInput(dsp, root_window, attr.your_event_mask | StructureNotifyMask);
+
+		setTrayOwnerWindow(dsp);
+	}
+#endif
+}
+
+bool PsiApplication::notify(QObject *receiver, QEvent *event)
+{
+#ifdef Q_WS_X11
+	if( event->type() == QEvent::Show && receiver->isWidgetType())
+	{
+		QWidget* w = static_cast< QWidget* >( receiver );
+		if( w->isTopLevel() && qt_x_last_input_time != CurrentTime ) // CurrentTime means no input event yet
+			XChangeProperty( qt_xdisplay(), w->winId(), kde_net_wm_user_time, XA_CARDINAL,
+					 32, PropModeReplace, (unsigned char*)&qt_x_last_input_time, 1 );
+	}
+	if( event->type() == QEvent::Hide && receiver->isWidgetType())
+	{
+		QWidget* w = static_cast< QWidget* >( receiver );
+		if( w->isTopLevel() && w->winId() != 0 )
+			XDeleteProperty( qt_xdisplay(), w->winId(), kde_net_wm_user_time );
+	}
+#endif
+	return QApplication::notify(receiver, event);
+}
+
+#ifdef Q_WS_X11
+bool PsiApplication::x11EventFilter( XEvent *_event )
+{
+	switch ( _event->type ) {
+
+		case ClientMessage:
+			if (_event->xclient.window == root_window && _event->xclient.message_type == manager_atom)
+			{
+				// A new notification area application has
+				// announced its presence
+				setTrayOwnerWindow(_event->xclient.display);
+				newTrayOwner();
+			}
+			break;
+			
+		case DestroyNotify:
+			if (_event->xdestroywindow.event == tray_owner)
+			{
+				// there is now no known notification area.
+				// We're still looking out for the MANAGER
+				// message sent to the root window, at which
+				// point we'll have another look to see
+				// whether a notification area is available.
+				tray_owner = 0;
+				trayOwnerDied();
+			}
+			break;
+
+		case ButtonPress:
+	 	case XKeyPress:
+		{
+			if( _event->type == ButtonPress )
+				qt_x_last_input_time = _event->xbutton.time;
+			else // KeyPress
+				qt_x_last_input_time = _event->xkey.time;
+			QWidget *w = activeWindow();
+			if( w ) {
+				XChangeProperty( qt_xdisplay(), w->winId(), kde_net_wm_user_time, XA_CARDINAL,
+						 32, PropModeReplace, (unsigned char*)&qt_x_last_input_time, 1 );
+				/*timeval tv;
+				gettimeofday( &tv, NULL );
+				unsigned long now = tv.tv_sec * 10 + tv.tv_usec / 100000;
+				XChangeProperty(qt_xdisplay(), w->winId(),
+						atom_KdeNetUserTime, XA_CARDINAL,
+						32, PropModeReplace, (unsigned char *)&now, 1);*/
+			}
+			break;
+		}
+
+		default:
+			break;
+	}
+
+	// process the event normally
+	return false;
+}
+#endif
+
+#ifdef Q_WS_MAC
+bool PsiApplication::macEventFilter( EventHandlerCallRef, EventRef inEvent )
+{
+	UInt32 eclass = GetEventClass(inEvent);
+	int etype = GetEventKind(inEvent);
+	if(eclass == 'eppc' && etype == kEventAppleEvent) {
+		dockActivated();
+	}
+	return false;
+}
+#endif
+
Index: psi/tags/0.10-os2-b1/src/psiapplication.h
===================================================================
--- psi/tags/0.10-os2-b1/src/psiapplication.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psiapplication.h	(revision 38)
@@ -0,0 +1,54 @@
+/*
+ * psiapplication.h - subclass of QApplication to do some workarounds
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIAPPLICATION_H
+#define PSIAPPLICATION_H
+
+#include <qapplication.h>
+
+#ifdef Q_WS_MAC
+#include <Carbon/Carbon.h>
+#endif
+
+class PsiApplication : public QApplication
+{
+	Q_OBJECT
+public:
+	PsiApplication(int &argc, char **argv, bool GUIenabled = true);
+	~PsiApplication();
+
+	bool notify(QObject *receiver, QEvent *event);
+#ifdef Q_WS_X11
+	bool x11EventFilter(XEvent *event);
+#endif
+#ifdef Q_WS_MAC
+	bool macEventFilter(EventHandlerCallRef, EventRef);
+#endif
+
+signals:
+	void dockActivated();
+	void newTrayOwner();
+	void trayOwnerDied();
+
+private:
+	void init(bool GUIenabled);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/psicon.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psicon.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psicon.cpp	(revision 38)
@@ -0,0 +1,1600 @@
+/*
+ * psicon.cpp - core of Psi
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"psicon.h"
+
+#include<qptrlist.h>
+#include<qapplication.h>
+#include<qdesktopwidget.h>
+#include<qguardedptr.h>
+#include<qiconset.h>
+#include<qcolor.h>
+#include<qimage.h>
+#include<qstylesheet.h>
+#include<qpixmapcache.h>
+
+#include"s5b.h"
+#include"psiaccount.h"
+#include"contactview.h"
+#include"common.h"
+#include"mainwin.h"
+#include"idle.h"
+#include"accountdlg.h"
+#include"statusdlg.h"
+#include"options/optionsdlg.h"
+#include"options/toolbardlg.h"
+#include"groupchatdlg.h"
+#include"userlist.h"
+#include"eventdlg.h"
+#include"eventdb.h"
+#include"openpgp.h"
+#include"gnupg.h"
+#include"proxy.h"
+#include"psipng.h"
+#ifdef PSIMNG
+#include"psimng.h"
+#endif
+#include"psitextview.h"
+#include"alerticon.h"
+#include"iconselect.h"
+#include"psitoolbar.h"
+#include"filetransfer.h"
+#include"filetransdlg.h"
+#include"psiactionlist.h"
+#include"systemwatch.h"
+#include"globalaccelman.h"
+#include "tabdlg.h"
+#include "chatdlg.h"
+
+#ifdef Q_WS_MAC
+#include "mac_dock.h"
+#endif
+
+//----------------------------------------------------------------------------
+// PsiIconFactory
+//----------------------------------------------------------------------------
+class PsiIconFactory : public QIconFactory
+{
+public:
+	PsiIconFactory();
+
+	QPixmap *createPixmap(const QIconSet &, QIconSet::Size, QIconSet::Mode, QIconSet::State);
+};
+
+PsiIconFactory::PsiIconFactory()
+{
+	setAutoDelete(true);
+}
+
+QPixmap *PsiIconFactory::createPixmap(const QIconSet &iconSet, QIconSet::Size size, QIconSet::Mode mode, QIconSet::State state)
+{
+	Q_UNUSED(size);
+	Q_UNUSED(mode);
+	Q_UNUSED(state);
+
+	QPixmap pix = iconSet.pixmap(size, QIconSet::Normal, state);
+	if ( mode == QIconSet::Disabled && !pix.isNull() ) {
+		QImage img = pix.convertToImage();
+		if ( img.depth() != 32 )
+			img = img.convertDepth(32);
+		img.setAlphaBuffer(true);
+
+		for (int x = 0; x < img.width(); x++) {
+			for (int y = 0; y < img.height(); y++) {
+				QRgb *rgb = (QRgb *)img.scanLine(y) + x;
+				QColor col(*rgb);
+
+				// bigger divisor will result in darker shades of gray
+				int gray = (col.red() + col.green() + col.blue()) / 3;
+				if ( gray > 0xFF )
+					gray = 0xFF;
+				int alpha = qAlpha(*rgb) /*+ 150*/;
+				if ( alpha > 0xFF )
+					alpha = 0xFF;
+				*rgb = qRgba(gray, gray, gray, alpha);
+			}
+		}
+
+		QPixmap *pix = new QPixmap(img);
+		return pix;
+	}
+
+	return 0;
+}
+
+//----------------------------------------------------------------------------
+// PsiConObject
+//----------------------------------------------------------------------------
+class PsiConObject : public QObject
+{
+	Q_OBJECT
+public:
+	PsiConObject(QObject *parent)
+	: QObject(parent, "PsiConObject")
+	{
+		QImage::inputFormats(); // make sure, that the Qt built-in formats are initialized before our custom ones
+
+		initPsiPngIO(); // our custom animation format
+#ifdef PSIMNG
+		initPsiMngIO();
+#endif
+		QIconFactory::installDefaultFactory(new PsiIconFactory);
+
+		QDir p(g.pathHome);
+		QDir v(g.pathHome + "/tmp-sounds");
+		if(!v.exists())
+			p.mkdir("tmp-sounds");
+		Iconset::setSoundPrefs(v.absPath(), this, SLOT(playSound(QString)));
+		URLLabel::connectOpenURL(this, SLOT(openURL(QString)));
+		QStyleSheet::setDefaultSheet( PsiTextView::styleSheet() );
+	}
+
+	~PsiConObject()
+	{
+		// removing temp dirs
+		QDir p(g.pathHome);
+		QDir v(g.pathHome + "/tmp-sounds");
+		folderRemove(v);
+	}
+
+public slots:
+	void playSound(QString file)
+	{
+		if ( file.isEmpty() || !useSound )
+			return;
+
+		soundPlay(file);
+	}
+
+	void openURL(QString url)
+	{
+		::openURL(url);
+	}
+
+private:
+	// ripped from profiles.cpp
+	bool folderRemove(const QDir &_d)
+	{
+		QDir d = _d;
+
+		QStringList entries = d.entryList();
+		for(QStringList::Iterator it = entries.begin(); it != entries.end(); ++it) {
+			if(*it == "." || *it == "..")
+				continue;
+			QFileInfo info(d, *it);
+			if(info.isDir()) {
+				if(!folderRemove(QDir(info.filePath())))
+					return FALSE;
+			}
+			else {
+				//printf("deleting [%s]\n", info.filePath().latin1());
+				d.remove(info.fileName());
+			}
+		}
+		QString name = d.dirName();
+		if(!d.cdUp())
+			return FALSE;
+		//printf("removing folder [%s]\n", d.filePath(name).latin1());
+		d.rmdir(name);
+
+		return TRUE;
+	}
+};
+
+//----------------------------------------------------------------------------
+// AccountsComboBox
+//----------------------------------------------------------------------------
+AccountsComboBox::AccountsComboBox(PsiCon *_psi, QWidget *parent, bool online_only)
+:QComboBox(parent), onlineOnly(online_only)
+{
+	psi = _psi;
+	// TODO: Status changes of accounts should be notified when onlineOnly is true
+	connect(psi, SIGNAL(accountCountChanged()), this, SLOT(updateAccounts()));
+	connect(psi, SIGNAL(destroyed()), SLOT(deleteMe()));
+	connect(this, SIGNAL(activated(int)), this, SLOT(changeAccount()));
+	if (online_only)
+		connect(psi, SIGNAL(accountActivityChanged()), this, SLOT(updateAccounts()));
+
+	pa = 0;
+	setSizePolicy(QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ));
+
+	if(!psi->accountList(TRUE).isEmpty())
+		setAccount(psi->accountList(TRUE).getFirst());
+}
+
+AccountsComboBox::~AccountsComboBox()
+{
+}
+
+void AccountsComboBox::setAccount(PsiAccount *_pa)
+{
+	pa = _pa;
+	updateAccounts();
+}
+
+void AccountsComboBox::changeAccount()
+{
+	int i = currentItem();
+
+	PsiAccountListIt it(psi->accountList(TRUE));
+	int n = 0;
+	bool found = false;
+	for(PsiAccount *p; (p = it.current()); ++it) {
+		if (!onlineOnly || p->loggedIn()) {
+			if(i == n) {
+				pa = p;
+				found = true;
+				break;
+			}
+			++n;
+		}
+	}
+	if(!found)
+		pa = 0;
+
+	activated(pa);
+}
+
+void AccountsComboBox::updateAccounts()
+{
+	clear();
+	PsiAccountListIt it(psi->accountList(TRUE));
+	int n = 0;
+	bool found = false;
+	PsiAccount *firstAccount = 0;
+	for(PsiAccount *p; (p = it.current()); ++it) {
+		if (!onlineOnly || p->loggedIn()) {
+			insertItem(p->nameWithJid());
+			if(p == pa) {
+				setCurrentItem(n);
+				found = true;
+			}
+			if (!firstAccount)
+				firstAccount = p;
+			++n;
+		}
+	}
+	if(!found) {
+		// choose a different account
+		pa = firstAccount;
+		activated(pa);
+	}
+}
+
+void AccountsComboBox::deleteMe()
+{
+	delete this;
+}
+
+
+//----------------------------------------------------------------------------
+// PsiCon::Private
+//----------------------------------------------------------------------------
+
+struct item_dialog
+{
+	QWidget *widget;
+	QString className;
+};
+
+class PsiCon::Private
+{
+public:
+	Private() {
+		iconSelect = 0;
+		//the list 'owns' the tabs
+		tabs.setAutoDelete( true );
+		tabControlledChats.setAutoDelete( false );
+	}
+	~Private() {
+		if ( iconSelect )
+			delete iconSelect;
+	}
+
+	void saveProfile(UserAccountList acc = UserAccountList())
+	{
+		pro.recentGCList = recentGCList;
+		pro.recentBrowseList = recentBrowseList;
+		pro.lastStatusString = lastStatusString;
+		pro.useSound = useSound;
+		pro.prefs = option;
+		if ( proxy )
+			pro.proxyList = proxy->itemList();
+
+		if ( acc.count() )
+			pro.acc = acc;
+
+		pro.toFile(pathToProfileConfig(activeProfile));
+	}
+
+	void updateIconSelect()
+	{
+		Iconset iss;
+		QPtrListIterator<Iconset> iconsets(is->emoticons);
+		Iconset *iconset;
+		while ( (iconset = iconsets.current()) != 0 ) {
+			iss += *iconset;
+
+			++iconsets;
+		}
+
+		iconSelect->setIconset(iss);
+	}
+
+	PsiAccount *queueLowestEventId(bool includeDND)
+	{
+		PsiAccount *low = 0;
+		int low_id = 0;
+		int low_prior = option.EventPriorityDontCare;
+
+		PsiAccountListIt it(listEnabled);
+		for ( PsiAccount *pa; (pa = it.current()); ++it ) {
+			int n = pa->eventQueue()->nextId();
+			if ( n == -1 )
+				continue;
+
+			if ( !includeDND && pa->status().show() == "dnd" )
+				continue;
+
+			int p = pa->eventQueue()->peekNext()->priority();
+			if ( !low || (n < low_id && p == low_prior) || p > low_prior ) {
+				low = pa;
+				low_id = n;
+				low_prior = p;
+			}
+		}
+
+		return low;
+	}
+
+
+	PsiAccountList list;
+	PsiAccountList listEnabled;
+	UserProfile pro;
+	QString lastStatusString;
+	MainWin *mainwin;
+	Idle idle;
+	QPtrList<item_dialog> dialogList;
+	QPtrList<TabDlg> tabs;
+	QPtrList<ChatDlg> tabControlledChats;
+	int eventId;
+	QStringList recentGCList, recentBrowseList, recentNodeList;
+	EDB *edb;
+	OpenPGP::Engine *pgp;
+	bool pgpReady;
+	S5BServer *s5bServer;
+	ProxyManager *proxy;
+	IconSelectPopup *iconSelect;
+	QRect mwgeom;
+	FileTransDlg *ftwin;
+	PsiActionList *actionList;
+	GlobalAccelManager *globalAccelManager;
+};
+
+//----------------------------------------------------------------------------
+// PsiCon
+//----------------------------------------------------------------------------
+
+PsiCon::PsiCon()
+:QObject(0)
+{
+	//pdb(DEBUG_JABCON, QString("%1 v%2\n By Justin Karneges\n    infiniti@affinix.com\n\n").arg(PROG_NAME).arg(PROG_VERSION));
+
+	d = new Private;
+
+	d->lastStatusString = "";
+	useSound = true;
+	d->mainwin = 0;
+	d->ftwin = 0;
+
+	d->dialogList.setAutoDelete(true);
+
+	d->eventId = 0;
+	d->edb = new EDBFlatFile;
+
+	d->pgp = 0;
+	d->pgpReady = false;
+	d->s5bServer = 0;
+	d->proxy = 0;
+
+	d->actionList = 0;
+}
+
+PsiCon::~PsiCon()
+{
+	deinit();
+
+	delete d->actionList;
+	delete d->pgp;
+	delete d->edb;
+	delete d;
+}
+
+bool PsiCon::init()
+{
+	// load the profile
+	d->pro.reset();
+	d->pro.fromFile(pathToProfileConfig(activeProfile));
+
+	QDir profileDir( pathToProfile( activeProfile ) );
+	profileDir.rmdir( "info" ); // remove unused dir
+
+	d->recentGCList = d->pro.recentGCList;
+	d->recentBrowseList = d->pro.recentBrowseList;
+	d->lastStatusString = d->pro.lastStatusString;
+	useSound = d->pro.useSound;
+
+	option = d->pro.prefs;
+
+	new PsiConObject(this);
+
+	// first thing, try to load the iconset
+	is = new PsiIconset();
+	if( !is->loadAll() ) {
+		//option.iconset = "stellar";
+		//if(!is.load(option.iconset)) {
+			QMessageBox::critical(0, tr("Error"), tr("Unable to load iconset!  Please make sure Psi is properly installed."));
+			return false;
+		//}
+	}
+
+	if ( !d->actionList )
+		d->actionList = new PsiActionList( this );
+
+	d->iconSelect = new IconSelectPopup(0);
+	d->updateIconSelect();
+
+	// setup the main window
+	d->mainwin = new MainWin(option.alwaysOnTop, (option.useDock && option.dockToolMW), this, "psimain");
+	d->mainwin->setUseDock(option.useDock);
+
+	connect(d->mainwin, SIGNAL(closeProgram()), SLOT(closeProgram()));
+	connect(d->mainwin, SIGNAL(changeProfile()), SLOT(changeProfile()));
+	connect(d->mainwin, SIGNAL(doManageAccounts()), SLOT(doManageAccounts()));
+	connect(d->mainwin, SIGNAL(doGroupChat()), SLOT(doGroupChat()));
+	connect(d->mainwin, SIGNAL(blankMessage()), SLOT(doNewBlankMessage()));
+	connect(d->mainwin, SIGNAL(statusChanged(int)), SLOT(statusMenuChanged(int)));
+	connect(d->mainwin, SIGNAL(doOptions()), SLOT(doOptions()));
+	connect(d->mainwin, SIGNAL(doToolbars()), SLOT(doToolbars()));
+	connect(d->mainwin, SIGNAL(doFileTransDlg()), SLOT(doFileTransDlg()));
+	connect(d->mainwin, SIGNAL(recvNextEvent()), SLOT(recvNextEvent()));
+	connect(d->mainwin, SIGNAL(geomChanged(int, int, int, int)), SLOT(mainWinGeomChanged(int, int, int, int)));
+	connect(this, SIGNAL(emitOptionsUpdate()), d->mainwin, SLOT(optionsUpdate()));
+
+	connect(this, SIGNAL(emitOptionsUpdate()), d->mainwin->cvlist, SLOT(optionsUpdate()));
+
+	// if the coordinates are out of the desktop bounds, reset to the top left
+	QRect mwgeom = d->pro.mwgeom;
+	QDesktopWidget *pdesktop = QApplication::desktop();
+	int nscreen = pdesktop->screenNumber(mwgeom.topLeft());
+	QRect r = pdesktop->screenGeometry(nscreen);
+
+	int pad = 10;
+	if((mwgeom.width() + pad * 2) > r.width())
+		mwgeom.setWidth(r.width() - pad * 2);
+	if((mwgeom.height() + pad * 2) > r.height())
+		mwgeom.setHeight(r.height() - pad * 2);
+	if(mwgeom.left() < r.left())
+		mwgeom.moveLeft(r.left());
+	if(mwgeom.right() >= r.right())
+		mwgeom.moveRight(r.right() - 1);
+	if(mwgeom.top() < r.top())
+		mwgeom.moveTop(r.top());
+	if(mwgeom.bottom() >= r.bottom())
+		mwgeom.moveBottom(r.bottom() - 1);
+
+	d->mwgeom = mwgeom;
+	d->mainwin->move(mwgeom.x(), mwgeom.y());
+	d->mainwin->resize(mwgeom.width(), mwgeom.height());
+
+	d->ftwin = new FileTransDlg(this);
+
+	if(!(option.useDock && option.dockHideMW))
+		d->mainwin->show();
+
+	d->idle.start();
+
+	// S5B
+	d->s5bServer = new S5BServer;
+	s5b_init();
+
+	// pgp
+	if(use_gpg) {
+		option.pgp = "gpg";
+		pgp_init(option.pgp);
+	}
+
+	// proxy
+	d->proxy = new ProxyManager(this);
+	d->proxy->setItemList(d->pro.proxyList);
+	connect(d->proxy, SIGNAL(settingsChanged()), SLOT(proxy_settingsChanged()));
+
+	// load accounts
+	loadAccounts(d->pro.acc);
+
+	// Connect to the system monitor
+	SystemWatch* sw = SystemWatch::instance();
+	connect(sw, SIGNAL(sleep()), this, SLOT(doSleep()));
+	connect(sw, SIGNAL(idleSleep()), this, SLOT(doSleep()));
+	connect(sw, SIGNAL(wakeup()), this, SLOT(doWakeup()));
+
+	// global accelerator manager
+	d->globalAccelManager = new GlobalAccelManager;
+	d->globalAccelManager->setAccel(option.globalAccels[0]);
+	d->globalAccelManager->setAccel(option.globalAccels[1]);
+	connect(d->globalAccelManager, SIGNAL(activated(int)), SLOT(accel_activated(int)));
+
+	return true;
+}
+
+void PsiCon::deinit()
+{
+	// this deletes all dialogs except for mainwin
+	deleteAllDialogs();
+
+	d->idle.stop();
+
+	// shut down all accounts
+	UserAccountList acc = unloadAccounts();
+
+	// delete s5b server
+	delete d->s5bServer;
+
+	delete d->ftwin;
+
+	if(d->mainwin) {
+		// shut down mainwin
+		QRect mwgeom;
+		if ( !d->mainwin->isHidden() && !d->mainwin->isMinimized() ) {
+			mwgeom.setX(d->mainwin->x());
+			mwgeom.setY(d->mainwin->y());
+			mwgeom.setWidth(d->mainwin->width());
+			mwgeom.setHeight(d->mainwin->height());
+		}
+		else
+			mwgeom = d->mwgeom;
+
+		delete d->mainwin;
+		d->mainwin = 0;
+
+		d->pro.mwgeom = mwgeom;
+	}
+
+	// unload iconset
+	delete is;
+
+	// save profile
+	d->saveProfile(acc);
+}
+
+PsiAccount *PsiCon::loadAccount(const UserAccount &acc)
+{
+	PsiAccount *pa = new PsiAccount(acc, this);
+	connect(&d->idle, SIGNAL(secondsIdle(int)), pa, SLOT(secondsIdle(int)));
+	connect(pa, SIGNAL(updatedActivity()), SLOT(pa_updatedActivity()));
+	connect(pa, SIGNAL(updatedAccount()), SLOT(pa_updatedAccount()));
+	connect(pa, SIGNAL(queueChanged()), SLOT(queueChanged()));
+	connect(pa, SIGNAL(enabledChanged()), SIGNAL(accountCountChanged()));
+	accountAdded(pa);
+	if(d->s5bServer)
+		pa->client()->s5bManager()->setServer(d->s5bServer);
+	return pa;
+}
+
+void PsiCon::loadAccounts(const UserAccountList &list)
+{
+	if(list.count() > 0) {
+		for(UserAccountList::ConstIterator it = list.begin(); it != list.end(); ++it)
+			loadAccount(*it);
+	}
+	else {
+		// if there are no accounts, then prompt the user to add one
+		AccountAddDlg *w = new AccountAddDlg(this, 0);
+		w->show();
+	}
+}
+
+UserAccountList PsiCon::unloadAccounts()
+{
+	UserAccountList acc;
+
+	QPtrListIterator<PsiAccount> it(d->list);
+	for(PsiAccount *pa; (pa = it.current());) {
+		acc += pa->userAccount();
+		delete pa;
+	}
+
+	return acc;
+}
+
+const PsiAccountList & PsiCon::accountList(bool enabledOnly) const
+{
+	if(enabledOnly)
+		return d->listEnabled;
+	else
+		return d->list;
+}
+
+ContactView *PsiCon::contactView() const
+{
+	if(d->mainwin)
+		return d->mainwin->cvlist;
+	else
+		return 0;
+}
+
+EDB *PsiCon::edb() const
+{
+	return d->edb;
+}
+
+OpenPGP::Engine *PsiCon::pgp() const
+{
+	if(d->pgp && d->pgpReady)
+		return d->pgp;
+	else
+		return 0;
+}
+
+ProxyManager *PsiCon::proxy() const
+{
+	return d->proxy;
+}
+
+FileTransDlg *PsiCon::ftdlg() const
+{
+	return d->ftwin;
+}
+
+void PsiCon::link(PsiAccount *pa)
+{
+	d->list.append(pa);
+	if(pa->enabled() && !d->listEnabled.containsRef(pa))
+		d->listEnabled.append(pa);
+	connect(pa, SIGNAL(updatedActivity()), this, SIGNAL(accountActivityChanged()));
+	accountCountChanged();
+}
+
+void PsiCon::unlink(PsiAccount *pa)
+{
+	disconnect(pa, SIGNAL(updatedActivity()), this, SIGNAL(accountActivityChanged()));
+	d->list.remove(pa);
+	d->listEnabled.remove(pa);
+	accountCountChanged();
+}
+
+void PsiCon::closeProgram()
+{
+	quit(QuitProgram);
+}
+
+void PsiCon::changeProfile()
+{
+	bool ok = true;
+	PsiAccountListIt it(d->listEnabled);
+	for(PsiAccount *pa; (pa = it.current()); ++it) {
+		if(pa->isActive()) {
+			ok = false;
+			break;
+		}
+	}
+
+	if(!ok) {
+		QMessageBox::information(0, CAP(tr("Error")), tr("Please disconnect before changing the profile."));
+		return;
+	}
+
+	quit(QuitProfile);
+}
+
+void PsiCon::doManageAccounts()
+{
+	AccountManageDlg *w = (AccountManageDlg *)dialogFind("AccountManageDlg");
+	if(w)
+		bringToFront(w);
+	else {
+		w = new AccountManageDlg(this);
+		w->show();
+	}
+}
+
+void PsiCon::doGroupChat()
+{
+	PsiAccount *pa = d->listEnabled.getFirst();
+	if(!pa)
+		return;
+
+	GCJoinDlg *w = new GCJoinDlg(this, pa);
+	w->show();
+}
+
+void PsiCon::doNewBlankMessage()
+{
+	PsiAccount *pa = d->listEnabled.getFirst();
+	if(!pa)
+		return;
+
+	EventDlg *w = createEventDlg("", pa);
+	w->show();
+}
+
+EventDlg *PsiCon::createEventDlg(const QString &to, PsiAccount *pa)
+{
+	EventDlg *w = new EventDlg(to, this, pa);
+	connect(this, SIGNAL(emitOptionsUpdate()), w, SLOT(optionsUpdate()));
+	return w;
+}
+
+void PsiCon::updateContactGlobal(PsiAccount *pa, const Jid &j)
+{
+	QPtrListIterator<item_dialog> it(d->dialogList);
+	for(item_dialog *i; (i = it.current()); ++it) {
+		if(i->className == "EventDlg") {
+			EventDlg *e = (EventDlg *)i->widget;
+			if(e->psiAccount() == pa)
+				e->updateContact(j);
+		}
+	}
+}
+
+QWidget *PsiCon::dialogFind(const char *className)
+{
+	QPtrListIterator<item_dialog> it(d->dialogList);
+	for(item_dialog *i; (i = it.current()); ++it) {
+		// does the classname and jid match?
+		if(i->className == className) {
+			return i->widget;
+		}
+	}
+	return 0;
+}
+
+TabDlg* PsiCon::newTabs()
+{
+	TabDlg *tab;
+	tab=new TabDlg(this);
+	d->tabs.append(tab);
+	connect (tab, SIGNAL ( isDying(TabDlg*) ), SLOT ( tabDying(TabDlg*) ) );
+	return tab;
+}
+
+TabDlg* PsiCon::getTabs()
+{
+	if (!d->tabs.isEmpty())
+	{
+		return d->tabs.getFirst();
+	}
+	else
+	{
+		return newTabs();
+	}
+}
+
+void PsiCon::tabDying(TabDlg* tab)
+{
+	d->tabs.remove(tab);
+}
+
+bool PsiCon::isChatTabbed(ChatDlg* chat)
+{
+	for (uint i = 0; i < d->tabs.count(); ++i)
+	{
+		if ( d->tabs.at(i)->managesChat(chat) )
+				return true;
+	}
+	return false;
+}
+
+ChatDlg* PsiCon::getChatInTabs(QString jid){
+	for (uint i = 0; i < d->tabs.count(); ++i)
+	{
+		if ( d->tabs.at(i)->getChatPointer(jid) )
+				return d->tabs.at(i)->getChatPointer(jid);
+	}
+	return NULL;
+
+}
+
+TabDlg* PsiCon::getManagingTabs(ChatDlg* chat)
+{
+	for (uint i = 0; i < d->tabs.count(); ++i)
+	{
+		if ( d->tabs.at(i)->managesChat(chat) )
+				return d->tabs.at(i);
+	}
+	return NULL;
+
+}
+
+QPtrList<TabDlg>* PsiCon::getTabSets()
+{
+	return &d->tabs;
+}
+
+bool PsiCon::isChatActiveWindow(ChatDlg* chat)
+{
+	//returns true if chat is on top of a tab pile
+	if ( chat->isHidden() )
+	{
+		return false;
+	}
+	if (!option.useTabs)
+	{
+		return chat->isActiveWindow();
+	}
+	for (uint i = 0; i < d->tabs.count(); ++i)
+	{
+		if ( d->tabs.at(i)->isActiveWindow() )
+		{
+			if ( d->tabs.at(i)->chatOnTop( chat ) )
+			{
+				return true;
+			}
+		}
+	}
+	return false;
+}
+
+void PsiCon::dialogRegister(QWidget *w)
+{
+	item_dialog *i = new item_dialog;
+	i->widget = w;
+	i->className = w->className();
+	d->dialogList.append(i);
+}
+
+void PsiCon::dialogUnregister(QWidget *w)
+{
+	QPtrListIterator<item_dialog> it(d->dialogList);
+	for(item_dialog *i; (i = it.current()); ++it) {
+		if(i->widget == w) {
+			d->dialogList.removeRef(i);
+			return;
+		}
+	}
+}
+
+void PsiCon::deleteAllDialogs()
+{
+	QPtrListIterator<item_dialog> it(d->dialogList);
+	for(item_dialog *i; (i = it.current());)
+		delete i->widget;
+	d->dialogList.clear();
+}
+
+AccountsComboBox *PsiCon::accountsComboBox(QWidget *parent, bool online_only)
+{
+	AccountsComboBox *acb = new AccountsComboBox(this, parent, online_only);
+	return acb;
+}
+
+bool PsiCon::isValid(PsiAccount *pa)
+{
+	if(d->list.findRef(pa) == -1)
+		return false;
+	else
+		return true;
+}
+
+void PsiCon::createAccount(const QString &name, const Jid &j, const QString &pass, bool opt_host, const QString &host, int port, bool ssl, int proxy)
+{
+	UserAccount acc;
+	acc.name = name;
+
+	acc.jid = j.full();
+	if(!pass.isEmpty()) { 
+		acc.opt_pass = true;
+		acc.pass = pass;
+	}
+
+	acc.opt_host = opt_host;
+	acc.host = host;
+	acc.port = port;
+
+	acc.opt_ssl = ssl;
+	acc.proxy_index = proxy;
+
+	PsiAccount *pa = loadAccount(acc);
+	saveAccounts();
+
+	// pop up the modify dialog so the user can customize the new account
+	pa->modify();
+}
+
+void PsiCon::modifyAccount(PsiAccount *pa)
+{
+	pa->modify();
+}
+
+void PsiCon::removeAccount(PsiAccount *pa)
+{
+	accountRemoved(pa);
+	pa->deleteQueueFile();
+	delete pa;
+	saveAccounts();
+}
+
+void PsiCon::enableAccount(PsiAccount *pa, bool e)
+{
+	if(e){
+		if(!d->listEnabled.containsRef(pa))
+			d->listEnabled.append(pa);
+	}else
+		d->listEnabled.remove(pa);
+}
+
+void PsiCon::statusMenuChanged(int x)
+{
+	if(x == STATUS_OFFLINE && !option.askOffline) {
+		setGlobalStatus(Status("","Logged out",0,false));
+		if(option.useDock == true)
+			d->mainwin->setTrayToolTip(Status("","",0,false));
+	}
+	else {
+		if(x == STATUS_ONLINE && !option.askOnline) {
+			setGlobalStatus(Status());
+			if(option.useDock == true)
+				d->mainwin->setTrayToolTip(Status());
+		}
+		else if(x == STATUS_INVISIBLE){
+			Status s("","",0,true);
+			s.setIsInvisible(true);
+			setGlobalStatus(s);
+			if(option.useDock == true)
+				d->mainwin->setTrayToolTip(s);
+		}
+		else {
+			// Create a dialog with the last status message
+			StatusSetDlg *w = new StatusSetDlg(this, makeStatus(x, d->lastStatusString));
+			connect(w, SIGNAL(set(const Status &)), SLOT(setStatusFromDialog(const Status &)));
+			connect(w, SIGNAL(cancelled()), SLOT(updateMainwinStatus()));
+			if(option.useDock == true)
+				connect(w, SIGNAL(set(const Status &)), d->mainwin, SLOT(setTrayToolTip(const Status &)));
+			w->show();
+		}
+	}
+}
+
+void PsiCon::setStatusFromDialog(const Status &s)
+{
+	d->lastStatusString = s.status();
+	setGlobalStatus(s);
+}
+
+void PsiCon::setGlobalStatus(const Status &s)
+{
+	// Check whether all accounts are logged off
+	bool allOffline = true;
+	PsiAccountListIt i(d->listEnabled);
+	PsiAccount *pa;
+	for( ; (pa = i.current()); ++i) {
+		if ( pa->isActive() ) {
+			allOffline = false;
+			break;
+		}
+	}
+
+	// globally set each account which is logged in
+	PsiAccountListIt it(d->listEnabled);
+	for( ; (pa = it.current()); ++it)
+		if (allOffline || pa->isActive())
+			pa->setStatus(s);
+}
+
+void PsiCon::pa_updatedActivity()
+{
+	PsiAccount *pa = (PsiAccount *)sender();
+	accountUpdated(pa);
+
+	// update s5b server
+	updateS5BServerAddresses();
+
+	updateMainwinStatus();
+}
+
+void PsiCon::pa_updatedAccount()
+{
+	PsiAccount *pa = (PsiAccount *)sender();
+	accountUpdated(pa);
+
+	saveAccounts();
+}
+
+void PsiCon::saveAccounts()
+{
+	UserAccountList acc;
+	PsiAccountListIt it(d->list);
+	for(PsiAccount *pa; (pa = it.current()); ++it)
+		acc += pa->userAccount();
+
+	d->pro.proxyList = d->proxy->itemList();
+	//d->pro.acc = acc;
+	//d->pro.toFile(pathToProfileConfig(activeProfile));
+	d->saveProfile(acc);
+}
+
+void PsiCon::updateMainwinStatus()
+{
+	bool active = false;
+	bool loggedIn = false;
+	int state = STATUS_ONLINE;
+	PsiAccountListIt it(d->listEnabled);
+	for(PsiAccount *pa; (pa = it.current()); ++it) {
+		if(pa->isActive())
+			active = true;
+		if(pa->loggedIn()) {
+			loggedIn = true;
+			state = makeSTATUS(pa->status());
+		}
+	}
+	if(loggedIn)
+		d->mainwin->decorateButton(state);
+	else {
+		if(active)
+			d->mainwin->decorateButton(-1);
+		else
+			d->mainwin->decorateButton(STATUS_OFFLINE);
+	}
+}
+
+void PsiCon::setToggles(bool tog_offline, bool tog_away, bool tog_agents, bool tog_hidden, bool tog_self)
+{
+	if(d->listEnabled.count() > 1)
+		return;
+
+	d->mainwin->cvlist->setShowOffline(tog_offline);
+	d->mainwin->cvlist->setShowAway(tog_away);
+	d->mainwin->cvlist->setShowAgents(tog_agents);
+	d->mainwin->cvlist->setShowHidden(tog_hidden);
+	d->mainwin->cvlist->setShowSelf(tog_self);
+}
+
+void PsiCon::getToggles(bool *tog_offline, bool *tog_away, bool *tog_agents, bool *tog_hidden, bool *tog_self)
+{
+	*tog_offline = d->mainwin->cvlist->isShowOffline();
+	*tog_away = d->mainwin->cvlist->isShowAway();
+	*tog_agents = d->mainwin->cvlist->isShowAgents();
+	*tog_hidden = d->mainwin->cvlist->isShowHidden();
+	*tog_self = d->mainwin->cvlist->isShowSelf();
+}
+
+void PsiCon::doOptions()
+{
+	OptionsDlg *w = (OptionsDlg *)dialogFind("OptionsDlg");
+	if(w)
+		bringToFront(w);
+	else {
+		w = new OptionsDlg(this, option);
+		connect(w, SIGNAL(applyOptions(const Options &)), SLOT(slotApplyOptions(const Options &)));
+		w->show();
+	}
+}
+
+void PsiCon::doFileTransDlg()
+{
+	bringToFront(d->ftwin);
+}
+
+QWidget *PsiCon::doToolbars()
+{
+	ToolbarDlg *w = (ToolbarDlg *)dialogFind("ToolbarDlg");
+	if (w)
+		bringToFront(w);
+	else {
+		w = new ToolbarDlg(this);
+		w->show();
+	}
+
+	return w;
+}
+
+void PsiCon::slotApplyOptions(const Options &opt)
+{
+	QString oldpgp = option.pgp;
+	Options oldOpt = option;
+	bool notifyRestart = true;
+
+	option = opt;
+
+	if ( option.useTabs != oldOpt.useTabs || opt.chatLineEdit != oldOpt.chatLineEdit ) {
+		QMessageBox::information(0, tr("Information"), tr("Some of the options you changed will only have full effect upon restart."));
+		notifyRestart = false;
+	}
+
+	// change icon set
+	if ( option.systemIconset		!= oldOpt.systemIconset		||
+	     option.emoticons			!= oldOpt.emoticons		||
+	     option.defaultRosterIconset	!= oldOpt.defaultRosterIconset	||
+	     option.serviceRosterIconset	!= oldOpt.serviceRosterIconset	||
+	     option.customRosterIconset		!= oldOpt.customRosterIconset )
+	{
+		if ( notifyRestart && is->optionsChanged(&oldOpt) )
+			QMessageBox::information(0, tr("Information"), tr("The complete iconset update will happen on next Psi start."));
+
+		// update icon selector
+		d->updateIconSelect();
+
+		// flush the QPixmapCache
+		QPixmapCache::clear();
+	}
+
+	if ( oldOpt.alertStyle != option.alertStyle )
+		alertIconUpdateAlertStyle();
+
+	mainWin()->buildToolbars();
+
+	/*// change pgp engine
+	if(option.pgp != oldpgp) {
+		if(d->pgp) {
+			delete d->pgp;
+			d->pgp = 0;
+			pgpToggled(false);
+		}
+		pgp_init(option.pgp);
+	}*/
+
+	// update s5b
+	if(oldOpt.dtPort != option.dtPort)
+		s5b_init();
+	updateS5BServerAddresses();
+
+	// mainwin stuff
+	d->mainwin->setWindowOpts(option.alwaysOnTop, (option.useDock && option.dockToolMW));
+	d->mainwin->setUseDock(option.useDock);
+
+	// notify about options change
+	emitOptionsUpdate();
+
+	// save just the options
+	//d->pro.prefs = option;
+	//d->pro.toFile(pathToProfileConfig(activeProfile));
+	d->saveProfile();
+}
+
+int PsiCon::getId()
+{
+	return d->eventId++;
+}
+
+int PsiCon::queueCount()
+{
+	int total = 0;
+	PsiAccountListIt it(d->listEnabled);
+	for(PsiAccount *pa; (pa = it.current()); ++it)
+		total += pa->eventQueue()->count();
+	return total;
+}
+
+PsiAccount *PsiCon::queueLowestEventId()
+{
+	PsiAccount *low = 0;
+
+	low = d->queueLowestEventId( false ); // first try to get event from non-dnd account
+
+	// if failed, then get the event from dnd account instead
+	if ( !low )
+		low = d->queueLowestEventId( true );
+
+	return low;
+}
+
+void PsiCon::queueChanged()
+{
+	Icon *nextAnim = 0;
+	int nextAmount = queueCount();
+	PsiAccount *pa = queueLowestEventId();
+	if(pa)
+		nextAnim = is->event2icon(pa->eventQueue()->peekNext());
+
+#ifdef Q_WS_MAC
+	{
+		// The Mac Dock icon
+
+		// Update the event count
+		if (nextAmount) 
+			MacDock::overlay(QString::number(nextAmount));
+		else 
+			MacDock::overlay(QString::null);
+
+		// Check if bouncing is necessary
+		bool doBounce = false;
+		PsiAccountListIt it(d->listEnabled);
+		for(PsiAccount *pa; (pa = it.current()); ++it) {
+			if ( pa->eventQueue()->count() && pa->status().show() != "dnd" ) {
+				doBounce = true;
+				break;
+			}
+		}
+
+		// Bouncy bouncy
+		if (doBounce) {
+			if (option.bounceDock != Options::NoBounce) {
+				MacDock::startBounce();
+				if (option.bounceDock == Options::BounceOnce) 
+					MacDock::stopBounce();
+			}
+		}
+		else 
+			MacDock::stopBounce();
+	}
+#endif
+
+	d->mainwin->updateReadNext(nextAnim, nextAmount);
+}
+
+void PsiCon::recvNextEvent()
+{
+	/*printf("--- Queue Content: ---\n");
+	PsiAccountListIt it(d->list);
+	for(PsiAccount *pa; (pa = it.current()); ++it) {
+		printf(" Account: [%s]\n", pa->name().latin1());
+		pa->eventQueue()->printContent();
+	}*/
+	PsiAccount *pa = queueLowestEventId();
+	if(pa)
+		pa->openNextEvent();
+}
+
+void PsiCon::playSound(const QString &str)
+{
+	if(str.isEmpty() || !useSound)
+		return;
+
+	soundPlay(str);
+}
+
+void PsiCon::raiseMainwin()
+{
+	d->mainwin->showNoFocus();
+}
+
+const QStringList & PsiCon::recentGCList() const
+{
+	return d->recentGCList;
+}
+
+void PsiCon::recentGCAdd(const QString &str)
+{
+	// remove it if we have it
+	for(QStringList::Iterator it = d->recentGCList.begin(); it != d->recentGCList.end(); ++it) {
+		if(*it == str) {
+			d->recentGCList.remove(it);
+			break;
+		}
+	}
+
+	// put it in the front
+	d->recentGCList.prepend(str);
+
+	// trim the list if bigger than 10
+	while(d->recentGCList.count() > 10)
+		d->recentGCList.remove(d->recentGCList.fromLast());
+}
+
+const QStringList & PsiCon::recentBrowseList() const
+{
+	return d->recentBrowseList;
+}
+
+void PsiCon::recentBrowseAdd(const QString &str)
+{
+	// remove it if we have it
+	for(QStringList::Iterator it = d->recentBrowseList.begin(); it != d->recentBrowseList.end(); ++it) {
+		if(*it == str) {
+			d->recentBrowseList.remove(it);
+			break;
+		}
+	}
+
+	// put it in the front
+	d->recentBrowseList.prepend(str);
+
+	// trim the list if bigger than 10
+	while(d->recentBrowseList.count() > 10)
+		d->recentBrowseList.remove(d->recentBrowseList.fromLast());
+}
+
+const QStringList & PsiCon::recentNodeList() const
+{
+	return d->recentNodeList;
+}
+
+void PsiCon::recentNodeAdd(const QString &str)
+{
+	// remove it if we have it
+	for(QStringList::Iterator it = d->recentNodeList.begin(); it != d->recentNodeList.end(); ++it) {
+		if(*it == str) {
+			d->recentNodeList.remove(it);
+			break;
+		}
+	}
+
+	// put it in the front
+	d->recentNodeList.prepend(str);
+
+	// trim the list if bigger than 10
+	while(d->recentNodeList.count() > 10)
+		d->recentNodeList.remove(d->recentNodeList.fromLast());
+}
+
+void PsiCon::pgp_init(const QString &id)
+{
+	if(id.isEmpty())
+		return;
+
+	d->pgp = OpenPGP::createEngine(id);
+	if(d->pgp) {
+		connect(d->pgp, SIGNAL(initFinished(bool, const QString &)), SLOT(pgp_initFinished(bool, const QString &)));
+		connect(d->pgp, SIGNAL(keysUpdated()), SLOT(pgp_keysUpdated()));
+		d->pgpReady = false;
+		d->pgp->init();
+	}
+}
+
+void PsiCon::pgp_initFinished(bool b, const QString &)
+{
+	if(b) {
+		d->pgpReady = true;
+		if(no_gpg_agent) {
+			GnuPG *gpg = (GnuPG *)d->pgp;
+			gpg->setTryAgent(false);
+		}
+		pgpToggled(true);
+	}
+}
+
+void PsiCon::pgp_keysUpdated()
+{
+	pgpKeysUpdated();
+	//QMessageBox::information(0, CAP(tr("OpenPGP")), tr("Psi has detected that you have modified your keyring.  That is all."));
+}
+
+void PsiCon::proxy_settingsChanged()
+{
+	// properly index accounts
+	PsiAccountListIt it(d->list);
+	for(PsiAccount *pa; (pa = it.current()); ++it) {
+		UserAccount acc = pa->userAccount();
+		if(acc.proxy_index > 0) {
+			int x = d->proxy->findOldIndex(acc.proxy_index-1);
+			if(x == -1)
+				acc.proxy_index = 0;
+			else
+				acc.proxy_index = x+1;
+			pa->setUserAccount(acc);
+		}
+	}
+
+	saveAccounts();
+}
+
+void PsiCon::accel_activated(int x)
+{
+	switch (x) {
+	case 1:
+		recvNextEvent();
+		break;
+	case 2:
+		d->mainwin->toggleVisible();
+		break;
+	}
+}
+
+MainWin *PsiCon::mainWin() const
+{
+	return d->mainwin;
+}
+
+IconSelectPopup *PsiCon::iconSelectPopup() const
+{
+	return d->iconSelect;
+}
+
+void PsiCon::processEvent(PsiEvent *e)
+{
+	if ( e->type() == PsiEvent::PGP ) {
+		e->account()->eventQueue()->dequeue(e);
+		e->account()->queueChanged();
+		e->account()->promptPassphrase();
+		return;
+	}
+
+	if ( !e->account() )
+		return;
+
+	UserListItem *u = e->account()->find(e->jid());
+	if ( !u ) {
+		qWarning("SYSTEM MESSAGE: Bug #1. Contact the developers and tell them what you did to make this message appear. Thank you.");
+		e->account()->eventQueue()->dequeue(e);
+		e->account()->queueChanged();
+		return;
+	}
+
+	if( e->type() == PsiEvent::File ) {
+		FileEvent *fe = (FileEvent *)e;
+		FileTransfer *ft = fe->takeFileTransfer();
+		e->account()->eventQueue()->dequeue(e);
+		e->account()->queueChanged();
+		e->account()->cpUpdate(*u);
+		if(ft) {
+			FileRequestDlg *w = new FileRequestDlg(fe->timeStamp(), ft, e->account());
+			bringToFront(w);
+		}
+		return;
+	}
+
+	bool isChat = false;
+	bool sentToChatWindow = false;
+	if ( e->type() == PsiEvent::Message ) {
+		MessageEvent *me = (MessageEvent *)e;
+		const Message &m = me->message();
+		if ( m.type() == "chat" ) {
+			isChat = true;
+			sentToChatWindow = me->sentToChatWindow();
+		}
+	}
+
+	if ( isChat ) {
+		if ( option.alertOpenChats && sentToChatWindow ) {
+			// Message already displayed, need only to pop up chat dialog, so that
+			// it will be read (or marked as read)
+			ChatDlg *c = (ChatDlg *)e->account()->dialogFind("ChatDlg", e->from());
+			if(!c)
+				c = (ChatDlg *)e->account()->dialogFind("ChatDlg", e->jid());
+			if(!c)
+				return; // should never happen
+
+			e->account()->processChats(e->from()); // this will delete all events, corresponding to that chat dialog
+			//KIS: I changed the following line with the one following that to attempt to get tabs to behave correctly. Lots of this stuff isn't great.
+			bringToFront((QWidget *)c);
+			if ( option.useTabs)
+			{
+				TabDlg* tabSet = getManagingTabs(c);
+				tabSet->selectTab(c);
+			}
+		}
+		else
+			e->account()->openChat(e->from());
+	}
+	else {
+		// search for an already opened eventdlg
+		EventDlg *w = (EventDlg *)e->account()->dialogFind("EventDlg", u->jid());
+
+		if ( !w ) {
+			// create the eventdlg
+			w = e->account()->ensureEventDlg(u->jid());
+
+			// load next message
+			e->account()->processReadNext(*u);
+		}
+
+		bringToFront(w);
+	}
+}
+
+void PsiCon::mainWinGeomChanged(int x, int y, int w, int h)
+{
+	d->mwgeom.setX(x);
+	d->mwgeom.setY(y);
+	d->mwgeom.setWidth(w);
+	d->mwgeom.setHeight(h);
+}
+
+void PsiCon::updateS5BServerAddresses()
+{
+	if(!d->s5bServer)
+		return;
+
+	QValueList<QHostAddress> list;
+
+	// grab all IP addresses
+	QPtrListIterator<PsiAccount> it(d->list);
+	for(PsiAccount *pa; (pa = it.current()); ++it) {
+		QHostAddress *a = pa->localAddress();
+		if(!a)
+			continue;
+
+		// don't take dups
+		bool found = false;
+		for(QValueList<QHostAddress>::ConstIterator hit = list.begin(); hit != list.end(); ++hit) {
+			const QHostAddress &ha = *hit;
+			if(ha == (*a)) {
+				found = true;
+				break;
+			}
+		}
+		if(!found)
+			list += (*a);
+	}
+
+	// convert to stringlist
+	QStringList slist;
+	for(QValueList<QHostAddress>::ConstIterator hit = list.begin(); hit != list.end(); ++hit)
+		slist += (*hit).toString();
+
+	// add external
+	if(!option.dtExternal.isEmpty()) {
+		bool found = false;
+		for(QStringList::ConstIterator sit = slist.begin(); sit != slist.end(); ++sit) {
+			const QString &s = *sit;
+			if(s == option.dtExternal) {
+				found = true;
+				break;
+			}
+		}
+		if(!found)
+			slist += option.dtExternal;
+	}
+
+	// set up the server
+	d->s5bServer->setHostList(slist);
+}
+
+void PsiCon::s5b_init()
+{
+	if(d->s5bServer->isActive())
+		d->s5bServer->stop();
+
+	if (option.dtPort) {
+		if(!d->s5bServer->start(option.dtPort)) {
+			QMessageBox::information(0, tr("Error"), tr("Unable to bind to port %1 for Data Transfer").arg(option.dtPort));
+		}
+	}
+}
+
+void PsiCon::doSleep()
+{
+	setGlobalStatus(Status("",tr("Computer went to sleep"),0,false));
+}
+
+void PsiCon::doWakeup()
+{
+	// TODO: Restore the status from before the log out. Make it an (hidden) option for people with a bad wireless connection.
+	//setGlobalStatus(Status());
+}
+
+
+QPtrList<PsiToolBar> *PsiCon::toolbarList() const
+{
+	QPtrList<PsiToolBar> *list = new QPtrList<PsiToolBar>();
+
+	QPtrListIterator<PsiToolBar> it ( mainWin()->toolbars );
+	for ( ; it.current(); ++it )
+		list->append( it.current() );
+
+	return list;
+}
+
+PsiToolBar *PsiCon::findToolBar(QString group, int index)
+{
+	PsiToolBar *toolBar = 0;
+
+	if ( group == "mainWin" )
+		toolBar = mainWin()->toolbars.at(index);
+
+	return toolBar;
+}
+
+PsiActionList *PsiCon::actionList() const
+{
+	return d->actionList;
+}
+
+#include "psicon.moc"
Index: psi/tags/0.10-os2-b1/src/psicon.h
===================================================================
--- psi/tags/0.10-os2-b1/src/psicon.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psicon.h	(revision 38)
@@ -0,0 +1,207 @@
+/*
+ * psicon.h - core of Psi
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSICON_H
+#define PSICON_H
+
+#include<qcombobox.h>
+#include"profiles.h"
+#include"psievent.h"
+#include"im.h"
+
+using namespace XMPP;
+
+class PsiCon;
+class PsiAccount;
+class ContactView;
+class EventDlg;
+class UserListItem;
+class EDB;
+class EDBItem;
+class ProxyManager;
+class MainWin;
+class FileTransDlg;
+class IconSelectPopup;
+class QThread;
+class PsiActionList;
+class PsiToolBar;
+class	TabDlg;
+class ChatDlg;
+
+namespace OpenPGP
+{
+	class Engine;
+}
+
+typedef QPtrList<PsiAccount> PsiAccountList;
+typedef QPtrListIterator<PsiAccount> PsiAccountListIt;
+
+class AccountsComboBox : public QComboBox
+{
+	Q_OBJECT
+public:
+	AccountsComboBox(PsiCon *, QWidget *parent=0, bool online_only = false);
+	~AccountsComboBox();
+
+	void setAccount(PsiAccount *);
+
+signals:
+	void activated(PsiAccount *);
+
+private slots:
+	void changeAccount();
+	void updateAccounts();
+	void deleteMe();
+
+private:
+	PsiCon *psi;
+	PsiAccount *pa;
+	bool onlineOnly;
+};
+
+class PsiCon : public QObject
+{
+	Q_OBJECT
+public:
+	enum { QuitProgram, QuitProfile };
+	PsiCon();
+	~PsiCon();
+
+	bool init();
+	void deinit();
+
+	ContactView *contactView() const;
+	const PsiAccountList & accountList(bool enabledOnly=FALSE) const;
+	EDB *edb() const;
+	OpenPGP::Engine *pgp() const;
+	ProxyManager *proxy() const;
+	FileTransDlg *ftdlg() const;
+
+	TabDlg* getTabs();
+	TabDlg*	newTabs();
+	bool isChatTabbed(ChatDlg*);
+	bool isChatActiveWindow(ChatDlg*);
+	ChatDlg* getChatInTabs(QString);
+	TabDlg* getManagingTabs(ChatDlg*);
+	QPtrList<TabDlg>* getTabSets();
+
+	QWidget *dialogFind(const char *className);
+	void dialogRegister(QWidget *w);
+	void dialogUnregister(QWidget *w);
+
+	bool isValid(PsiAccount *);
+	void createAccount(const QString &name, const Jid &j="", const QString &pass="", bool opt_host=false, const QString &host="", int port=5222, bool ssl=false, int proxy=0);
+	//void createAccount(const QString &, const QString &host="", int port=5222, bool ssl=false, const QString &user="", const QString &pass="");
+	void modifyAccount(PsiAccount *);
+	void removeAccount(PsiAccount *);
+	void enableAccount(PsiAccount *, bool e=TRUE);
+
+	void playSound(const QString &);
+	void raiseMainwin();
+
+	// global event handling
+	int queueCount();
+	PsiAccount *queueLowestEventId();
+
+	AccountsComboBox *accountsComboBox(QWidget *parent=0, bool online_only = false);
+
+	const QStringList & recentGCList() const;
+	void recentGCAdd(const QString &);
+	const QStringList & recentBrowseList() const;
+	void recentBrowseAdd(const QString &);
+	const QStringList & recentNodeList() const;
+	void recentNodeAdd(const QString &);
+
+	EventDlg *createEventDlg(const QString &, PsiAccount *);
+	void updateContactGlobal(PsiAccount *, const Jid &);
+
+	QPtrList<PsiToolBar> *toolbarList() const;
+	PsiToolBar *findToolBar(QString group, int index);
+	PsiActionList *actionList() const;
+
+	MainWin *mainWin() const;
+	IconSelectPopup *iconSelectPopup() const;
+	void processEvent(PsiEvent *);
+
+signals:
+	void quit(int);
+	void accountAdded(PsiAccount *);
+	void accountUpdated(PsiAccount *);
+	void accountRemoved(PsiAccount *);
+	void accountCountChanged();
+	void accountActivityChanged();
+	void emitOptionsUpdate();
+	void pgpToggled(bool);
+	void pgpKeysUpdated();
+
+public slots:
+	void setGlobalStatus(const Status &);
+	QWidget *doToolbars();
+
+private slots:
+	void doSleep();
+	void doWakeup();
+	void closeProgram();
+	void changeProfile();
+	void doManageAccounts();
+	void doGroupChat();
+	void doNewBlankMessage();
+	void doOptions();
+	void doFileTransDlg();
+	void statusMenuChanged(int);
+	void pa_updatedActivity();
+	void pa_updatedAccount();
+	void slotApplyOptions(const Options &);
+	void queueChanged();
+	void recvNextEvent();
+	void setStatusFromDialog(const Status &);
+	void pgp_initFinished(bool, const QString &);
+	void pgp_keysUpdated();
+	void proxy_settingsChanged();
+	void accel_activated(int);
+	void updateMainwinStatus();
+	void tabDying(TabDlg*);
+
+	void mainWinGeomChanged(int x, int y, int w, int h);
+
+private:
+	class Private;
+	Private *d;
+
+	PsiAccount *loadAccount(const UserAccount &);
+	void loadAccounts(const UserAccountList &);
+	UserAccountList unloadAccounts();
+	void deleteAllDialogs();
+	void saveAccounts();
+	void pgp_init(const QString &);
+	void s5b_init();
+	void updateS5BServerAddresses();
+
+	friend class PsiAccount;
+	void link(PsiAccount *);
+	void unlink(PsiAccount *);
+	void setToggles(bool tog_offline, bool tog_away, bool tog_agents, bool tog_hidden, bool tog_self);
+	void getToggles(bool *tog_offline, bool *tog_away, bool *tog_agents, bool *tog_hidden, bool *tog_self);
+
+	friend class EventQueue;
+	int getId();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/psievent.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psievent.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psievent.cpp	(revision 38)
@@ -0,0 +1,791 @@
+/*
+ * psievent.h - events
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "psievent.h"
+
+#include <qdom.h>
+
+#include "psicon.h"
+#include "psiaccount.h"
+#include "xmpp_xmlcommon.h"
+#include "filetransfer.h"
+
+using namespace XMLHelper;
+
+static PsiEvent *copyPsiEvent(PsiEvent *fe)
+{
+	PsiEvent *e = 0;
+	if ( fe->inherits("MessageEvent") )
+		e = new MessageEvent( *((MessageEvent *)fe) );
+	else if ( fe->inherits("AuthEvent") )
+		e = new AuthEvent( *((AuthEvent *)fe) );
+	else if ( fe->inherits("PGPEvent") )
+		e = new PGPEvent( *((PGPEvent *)fe) );
+	else
+		qWarning("copyPsiEvent(): Failed: unknown event type: %s", fe->className());
+
+	return 0;
+}
+
+//----------------------------------------------------------------------------
+// DummyStream
+//----------------------------------------------------------------------------
+class DummyStream : public Stream
+{
+public:
+	QDomDocument & doc() const { return v_doc; }
+	QString baseNS() const { return QString::null; }
+	bool old() const { return false; }
+
+	void close() { }
+	bool stanzaAvailable() const { return false; }
+	Stanza read() { return Stanza(); }
+	void write(const Stanza &) { }
+
+	int errorCondition() const { return 0; }
+	QString errorText() const { return QString::null; }
+	QDomElement errorAppSpec() const { return v_doc.documentElement(); }
+
+private:
+	static QDomDocument v_doc;
+};
+
+QDomDocument DummyStream::v_doc;
+
+//----------------------------------------------------------------------------
+// PsiEvent
+//----------------------------------------------------------------------------
+PsiEvent::PsiEvent(PsiAccount *acc)
+{
+	v_originLocal = false;
+	v_late = false;
+	v_account = acc;
+}
+
+PsiEvent::PsiEvent(const PsiEvent &from)
+: QObject()
+{
+	v_originLocal = from.v_originLocal;
+	v_late = from.v_late;
+	v_ts = from.v_ts;
+	v_jid = from.v_jid;
+	v_account = from.v_account;
+}
+
+PsiEvent::~PsiEvent()
+{
+}
+
+XMPP::Jid PsiEvent::jid() const
+{
+	return v_jid;
+}
+
+void PsiEvent::setJid(const XMPP::Jid &j)
+{
+	v_jid = j;
+}
+
+PsiAccount *PsiEvent::account() const
+{
+	return v_account;
+}
+
+bool PsiEvent::originLocal() const
+{
+	return v_originLocal;
+}
+
+bool PsiEvent::late() const
+{
+	return v_late;
+}
+
+QDateTime PsiEvent::timeStamp() const
+{
+	return v_ts;
+}
+
+void PsiEvent::setOriginLocal(bool b)
+{
+	v_originLocal = b;
+}
+
+void PsiEvent::setLate(bool b)
+{
+	v_late = b;
+}
+
+void PsiEvent::setTimeStamp(const QDateTime &t)
+{
+	v_ts = t;
+}
+
+QDomElement PsiEvent::toXml(QDomDocument *doc) const
+{
+	QDomElement e = doc->createElement("event");
+	e.setAttribute("type", className());
+
+	e.appendChild( textTag(*doc, "originLocal",	v_originLocal) );
+	e.appendChild( textTag(*doc, "late",		v_late) );
+	e.appendChild( textTag(*doc, "ts",		v_ts.toString( ISODate )) );
+	if ( !v_jid.full().isEmpty() )
+		e.appendChild( textTag(*doc, "jid",		v_jid.full()) );
+
+	if ( v_account )
+		e.appendChild( textTag(*doc, "account",	v_account->name()) );
+
+	return e;
+}
+
+bool PsiEvent::fromXml(PsiCon *psi, PsiAccount *account, const QDomElement *e)
+{
+	if ( e->tagName() != "event" )
+		return false;
+	if ( e->attribute("type") != className() )
+		return false;
+
+	readBoolEntry(*e, "originLocal", &v_originLocal);
+	readBoolEntry(*e, "late", &v_late);
+	v_ts  = QDateTime::fromString(subTagText(*e, "ts"), ISODate);
+	v_jid = Jid( subTagText(*e, "jid") );
+
+	if ( account ) {
+		v_account = account;
+	}
+	else if ( hasSubTag(*e, "account") ) {
+		PsiAccountList list = psi->accountList();
+		PsiAccountListIt it(list);
+		QString accName = subTagText(*e, "account");
+		for ( ; it.current(); ++it) {
+			if ( it.current()->name() == accName ) {
+				v_account = it.current();
+				break;
+			}
+		}
+	}
+
+	return true;
+}
+
+int PsiEvent::priority() const
+{
+	return Options::EventPriorityDontCare;
+}
+
+PsiEvent *PsiEvent::copy() const
+{
+	return 0;
+}
+
+//----------------------------------------------------------------------------
+// MessageEvent
+//----------------------------------------------------------------------------
+
+MessageEvent::MessageEvent(PsiAccount *acc)
+: PsiEvent(acc)
+{
+	v_sentToChatWindow = false;
+}
+
+MessageEvent::MessageEvent(const XMPP::Message &m, PsiAccount *acc)
+: PsiEvent(acc)
+{
+	v_sentToChatWindow = false;
+	setMessage(m);
+}
+
+MessageEvent::MessageEvent(const MessageEvent &from)
+: PsiEvent(from), v_m(from.v_m), v_sentToChatWindow(from.v_sentToChatWindow)
+{
+}
+
+MessageEvent::~MessageEvent()
+{
+}
+
+int MessageEvent::type() const
+{
+	return Message;
+}
+
+Jid MessageEvent::from() const
+{
+	return v_m.from();
+}
+
+void MessageEvent::setFrom(const Jid &j)
+{
+	v_m.setFrom(j);
+}
+
+bool MessageEvent::sentToChatWindow() const
+{
+	return v_sentToChatWindow;
+}
+
+const XMPP::Message & MessageEvent::message() const
+{
+	return v_m;
+}
+
+void MessageEvent::setSentToChatWindow(bool b)
+{
+	v_sentToChatWindow = b;
+}
+
+void MessageEvent::setMessage(const XMPP::Message &m)
+{
+	v_m = m;
+	setTimeStamp ( v_m.timeStamp() );
+	setLate ( v_m.spooled() );
+}
+
+QDomElement MessageEvent::toXml(QDomDocument *doc) const
+{
+	QDomElement e = PsiEvent::toXml(doc);
+
+	DummyStream stream;
+	Stanza s = v_m.toStanza(&stream);
+	e.appendChild( s.element() );
+
+	return e;
+}
+
+bool MessageEvent::fromXml(PsiCon *psi, PsiAccount *account, const QDomElement *e)
+{
+	if ( !PsiEvent::fromXml(psi, account, e) )
+		return false;
+
+	bool found = false;
+	QDomElement msg = findSubTag(*e, "message", &found);
+	if ( found ) {
+		DummyStream stream;
+		Stanza s = stream.createStanza(msg);
+		v_m.fromStanza(s, 0); // FIXME: fix tzoffset?
+		return true;
+	}
+
+	return false;
+}
+
+int MessageEvent::priority() const
+{
+	if ( v_m.type() == "headline" )
+		return option.eventPriorityHeadline;
+	else if ( v_m.type() == "chat" )
+		return option.eventPriorityChat;
+
+	return option.eventPriorityMessage;
+}
+
+PsiEvent *MessageEvent::copy() const
+{
+	return new MessageEvent( *this );
+}
+
+//----------------------------------------------------------------------------
+// AuthEvent
+//----------------------------------------------------------------------------
+
+AuthEvent::AuthEvent(const Jid &j, const QString &authType, PsiAccount *acc)
+: PsiEvent(acc)
+{
+	v_from = j;
+	v_at = authType;
+}
+
+AuthEvent::AuthEvent(const AuthEvent &from)
+: PsiEvent(from), v_from(from.v_from), v_at(from.v_at)
+{
+}
+
+AuthEvent::~AuthEvent()
+{
+}
+
+int AuthEvent::type() const
+{
+	return Auth;
+}
+
+Jid AuthEvent::from() const
+{
+	return v_from;
+}
+
+void AuthEvent::setFrom(const Jid &j)
+{
+	v_from = j;
+}
+
+QString AuthEvent::authType() const
+{
+	return v_at;
+}
+
+QDomElement AuthEvent::toXml(QDomDocument *doc) const
+{
+	QDomElement e = PsiEvent::toXml(doc);
+
+	e.appendChild( textTag(*doc, "from",	 v_from.full()) );
+	e.appendChild( textTag(*doc, "authType", v_at) );
+
+	return e;
+}
+
+bool AuthEvent::fromXml(PsiCon *psi, PsiAccount *account, const QDomElement *e)
+{
+	if ( !PsiEvent::fromXml(psi, account, e) )
+		return false;
+
+	v_from = Jid( subTagText(*e, "from") );
+	v_at   = subTagText(*e, "authType");
+
+	return false;
+}
+
+int AuthEvent::priority() const
+{
+	return option.eventPriorityAuth;
+}
+
+PsiEvent *AuthEvent::copy() const
+{
+	return new AuthEvent( *this );
+}
+
+//----------------------------------------------------------------------------
+// FileEvent
+//----------------------------------------------------------------------------
+FileEvent::FileEvent(const Jid &j, FileTransfer *_ft, PsiAccount *acc)
+:PsiEvent(acc)
+{
+	v_from = j;
+	ft = _ft;
+}
+
+FileEvent::~FileEvent()
+{
+	delete ft;
+}
+
+int FileEvent::priority() const
+{
+	return option.eventPriorityFile;
+}
+
+Jid FileEvent::from() const
+{
+	return v_from;
+}
+
+void FileEvent::setFrom(const Jid &j)
+{
+	v_from = j;
+}
+
+FileTransfer *FileEvent::takeFileTransfer()
+{
+	FileTransfer *_ft = ft;
+	ft = 0;
+	return _ft;
+}
+
+//----------------------------------------------------------------------------
+// EventQueue
+//----------------------------------------------------------------------------
+
+class EventItem
+{
+public:
+        EventItem(PsiEvent *_e, int i)
+	{
+		e = _e;
+		v_id = i;
+	}
+
+	EventItem(const EventItem &from)
+	{
+		e = copyPsiEvent(from.e);
+		v_id = from.v_id;
+	}
+
+	~EventItem()
+	{
+	}
+
+	int id() const
+	{
+		return v_id;
+	}
+
+	PsiEvent *event() const
+	{
+		return e;
+	}
+
+private:
+	PsiEvent *e;
+	int v_id;
+};
+
+class EventQueue::Private
+{
+public:
+	Private() {
+		list.setAutoDelete(true);
+	}
+
+	QPtrList<EventItem> list;
+	PsiCon *psi;
+	PsiAccount *account;
+};
+
+EventQueue::EventQueue(PsiAccount *account)
+{
+	d = new Private();
+
+	d->account = account;
+	d->psi = account->psi();
+}
+
+EventQueue::EventQueue(const EventQueue &from)
+	: QObject()
+{
+	d = new Private();
+
+	*this = from;
+}
+
+EventQueue::~EventQueue()
+{
+	delete d;
+}
+
+int EventQueue::nextId() const
+{
+	QPtrListIterator<EventItem> it(d->list);
+	EventItem *i = it.current();
+	if(!i)
+		return -1;
+	return i->id();
+}
+
+int EventQueue::count() const
+{
+	return d->list.count();
+}
+
+int EventQueue::count(const Jid &j, bool compareRes) const
+{
+	int total = 0;
+
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current()); ++it) {
+		Jid j2(i->event()->jid());
+		if(j.compare(j2, compareRes))
+			++total;
+	}
+
+	return total;
+}
+
+void EventQueue::enqueue(PsiEvent *e)
+{
+	EventItem *i = new EventItem(e, d->psi->getId());
+
+	int prior  = e->priority();
+	bool found = false;
+
+	// skip all with higher or equal priority
+	for ( EventItem *ei = d->list.first(); ei; ei = d->list.next() ) {
+		if ( ei && ei->event()->priority() < prior ) {
+			d->list.insert(d->list.find(ei), i);
+			found = true;
+			break;
+		}
+	}
+
+	// everything else
+	if ( !found )
+		d->list.append(i);
+
+	emit queueChanged();
+}
+
+void EventQueue::dequeue(PsiEvent *e)
+{
+	if ( !e )
+		return;
+
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current()); ++it) {
+		if ( e == i->event() ) {
+			d->list.remove(i);
+			return;
+		}
+	}
+
+	emit queueChanged();
+}
+
+PsiEvent *EventQueue::dequeue(const Jid &j, bool compareRes)
+{
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current()); ++it) {
+		PsiEvent *e = i->event();
+		Jid j2(e->jid());
+		if(j.compare(j2, compareRes)) {
+			d->list.removeRef(i);
+			emit queueChanged();
+			return e;
+		}
+	}
+
+	return 0;
+}
+
+PsiEvent *EventQueue::peek(const Jid &j, bool compareRes) const
+{
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current()); ++it) {
+		PsiEvent *e = i->event();
+		Jid j2(e->jid());
+		if(j.compare(j2, compareRes)) {
+			return e;
+		}
+	}
+
+	return 0;
+}
+
+PsiEvent *EventQueue::dequeueNext()
+{
+	QPtrListIterator<EventItem> it(d->list);
+	EventItem *i = it.current();
+	if(!i)
+		return 0;
+	PsiEvent *e = i->event();
+	d->list.remove(it);
+	emit queueChanged();
+	return e;
+}
+
+PsiEvent *EventQueue::peekNext() const
+{
+	QPtrListIterator<EventItem> it(d->list);
+	EventItem *i = it.current();
+	if(!i)
+		return 0;
+	return i->event();
+}
+
+PsiEvent *EventQueue::peekFirstChat(const Jid &j, bool compareRes) const
+{
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current()); ++it) {
+		PsiEvent *e = i->event();
+		if(e->type() == PsiEvent::Message) {
+			MessageEvent *me = (MessageEvent *)e;
+			if(j.compare(me->from(), compareRes) && me->message().type() == "chat")
+				return e;
+		}
+	}
+
+	return 0;
+}
+
+bool EventQueue::hasChats(const Jid &j, bool compareRes) const
+{
+	return (peekFirstChat(j, compareRes) ? true: false);
+}
+
+// this function extracts all chats from the queue, and returns a list of queue positions
+void EventQueue::extractChats(QPtrList<PsiEvent> *el, const Jid &j, bool compareRes)
+{
+	bool changed = false;
+
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current());) {
+		PsiEvent *e = i->event();
+		if(e->type() == PsiEvent::Message) {
+			MessageEvent *me = (MessageEvent *)e;
+			if(j.compare(me->from(), compareRes) && me->message().type() == "chat") {
+				el->append(me);
+				d->list.remove(it);
+				changed = true;
+				continue;
+			}
+		}
+		++it;
+	}
+
+	if ( changed )
+		emit queueChanged();
+}
+
+void EventQueue::printContent() const
+{
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current()); ++it) {
+		PsiEvent *e = i->event();
+		printf("  %d: (%d) from=[%s] jid=[%s]\n", i->id(), e->type(), e->from().full().latin1(), e->jid().full().latin1());
+	}
+}
+
+void EventQueue::clear()
+{
+	d->list.clear();
+
+	emit queueChanged();
+}
+
+// this function removes all events associated with the input jid
+void EventQueue::clear(const Jid &j, bool compareRes)
+{
+	bool changed = false;
+
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current());) {
+		PsiEvent *e = i->event();
+		Jid j2(e->jid());
+		if(j.compare(j2, compareRes)) {
+			d->list.removeRef(i);
+			changed = true;
+		}
+		else
+			++it;
+	}
+
+	if ( changed )
+		emit queueChanged();
+}
+
+EventQueue &EventQueue::operator= (const EventQueue &from)
+{
+	d->list.clear();
+	d->psi = from.d->psi;
+
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current()); ++i) {
+		PsiEvent *e = i->event();
+
+		enqueue( copyPsiEvent(e) );
+	}
+
+	return *this;
+}
+
+QDomElement EventQueue::toXml(QDomDocument *doc) const
+{
+	QDomElement e = doc->createElement("eventQueue");
+	e.setAttribute("version", "1.0");
+	e.appendChild(textTag(doc, "progver", PROG_VERSION));
+
+	QPtrListIterator<EventItem> it(d->list);
+	for(EventItem *i; (i = it.current()); ++it) {
+		QDomElement event = (*it)->event()->toXml(doc);
+		e.appendChild( event );
+	}
+
+	return e;
+}
+
+bool EventQueue::fromXml(const QDomElement *q)
+{
+	if ( !q )
+		return false;
+
+	if ( q->tagName() != "eventQueue" )
+		return false;
+
+	if ( q->attribute("version") != "1.0" )
+		return false;
+
+	QString progver = subTagText(*q, "progver");
+
+	for(QDomNode n = q->firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement e = n.toElement();
+		if( e.isNull() )
+			continue;
+
+		if ( e.tagName() != "event" )
+			continue;
+
+		PsiEvent *event = 0;
+		QString eventType = e.attribute("type");
+		if ( eventType == "MessageEvent" ) {
+			event = new MessageEvent(0);
+			if ( !event->fromXml(d->psi, d->account, &e) ) {
+				delete event;
+				event = 0;
+			}
+		}
+		else if ( eventType == "AuthEvent" ) {
+			event = new AuthEvent("", "", 0);
+			if ( !event->fromXml(d->psi, d->account, &e) ) {
+				delete event;
+				event = 0;
+			}
+		}
+
+		if ( event )
+			emit handleEvent( event );
+	}
+
+	return true;
+}
+
+bool EventQueue::toFile(const QString &fname)
+{
+	QDomDocument doc;
+
+	QDomElement element = toXml(&doc);
+	doc.appendChild(element);
+
+	QFile f( fname );
+	if( !f.open(IO_WriteOnly) )
+		return FALSE;
+	QTextStream t;
+	t.setDevice( &f );
+	t.setEncoding( QTextStream::UnicodeUTF8 );
+	t << doc.toString(4);
+	t.unsetDevice();
+	f.close();
+
+	return TRUE;
+}
+
+bool EventQueue::fromFile(const QString &fname)
+{
+	QString confver;
+	QDomDocument doc;
+
+	QFile f(fname);
+	if(!f.open(IO_ReadOnly))
+		return FALSE;
+	if(!doc.setContent(&f, true))
+		return FALSE;
+	f.close();
+
+	QDomElement base = doc.documentElement();
+	return fromXml(&base);
+}
Index: psi/tags/0.10-os2-b1/src/psievent.h
===================================================================
--- psi/tags/0.10-os2-b1/src/psievent.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psievent.h	(revision 38)
@@ -0,0 +1,226 @@
+/*
+ * psievent.h - events
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIEVENT_H
+#define PSIEVENT_H
+
+#include <qdatetime.h>
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qdom.h>
+#include <qguardedptr.h>
+
+#include"im.h"
+
+namespace XMPP
+{
+	class FileTransfer;
+};
+
+using namespace XMPP;
+
+class PsiCon;
+class PsiAccount;
+
+
+class PsiEvent : public QObject
+{
+	Q_OBJECT
+public:
+	PsiEvent(PsiAccount *);
+	PsiEvent(const PsiEvent &);
+	virtual ~PsiEvent() = 0;
+
+	enum {
+		Message,
+		Auth,
+		PGP,
+		File
+	};
+	virtual int type() const = 0;
+
+	virtual Jid from() const = 0;
+	virtual void setFrom(const Jid &j) = 0;
+
+	Jid jid() const;
+	void setJid(const Jid &);
+
+	bool originLocal() const;
+	bool late() const;
+	QDateTime timeStamp() const;
+
+	void setOriginLocal(bool b);
+	void setLate(bool b);
+	void setTimeStamp(const QDateTime &t);
+
+	PsiAccount *account() const;
+
+	virtual QDomElement toXml(QDomDocument *) const;
+	virtual bool fromXml(PsiCon *, PsiAccount *, const QDomElement *);
+
+	virtual int priority() const;
+
+	virtual PsiEvent *copy() const;
+
+private:
+	bool v_originLocal, v_late;
+	QDateTime v_ts;
+	Jid v_jid;
+	PsiAccount *v_account;
+};
+
+// normal, chat, error, headline, etc
+class MessageEvent : public PsiEvent
+{
+	Q_OBJECT
+public:
+	MessageEvent(PsiAccount *acc);
+	MessageEvent(const MessageEvent &from);
+	MessageEvent(const XMPP::Message &, PsiAccount *acc);
+	~MessageEvent();
+
+	int type() const;
+	Jid from() const;
+	void setFrom(const Jid &j);
+
+	bool sentToChatWindow() const;
+	const XMPP::Message & message() const;
+
+	void setSentToChatWindow(bool b);
+	void setMessage(const XMPP::Message &m);
+
+	QDomElement toXml(QDomDocument *) const;
+	bool fromXml(PsiCon *, PsiAccount *, const QDomElement *);
+
+	virtual int priority() const;
+
+	virtual PsiEvent *copy() const;
+
+private:
+	XMPP::Message v_m;
+	bool v_sentToChatWindow;
+};
+
+// subscribe, subscribed, unsubscribe, unsubscribed
+class AuthEvent : public PsiEvent
+{
+	Q_OBJECT
+public:
+	AuthEvent(const Jid &j, const QString &authType, PsiAccount *acc);
+	AuthEvent(const AuthEvent &from);
+	~AuthEvent();
+
+	int type() const;
+	Jid from() const;
+	void setFrom(const Jid &j);
+
+	QString authType() const;
+
+	QDomElement toXml(QDomDocument *) const;
+	bool fromXml(PsiCon *, PsiAccount *, const QDomElement *);
+
+	virtual int priority() const;
+
+	virtual PsiEvent *copy() const;
+
+private:
+	Jid v_from;
+	QString v_at;
+};
+
+// request pgp passphrase
+class PGPEvent : public PsiEvent
+{
+	Q_OBJECT
+public:
+	PGPEvent(PsiAccount *acc) : PsiEvent(acc) {}
+	PGPEvent(const PGPEvent &from)
+	: PsiEvent(from) {}
+	~PGPEvent() {}
+	int type() const { return PGP; }
+	Jid from() const { return QString(); }
+	void setFrom(const Jid &) {}
+};
+
+// incoming file transfer
+class FileEvent : public PsiEvent
+{
+	Q_OBJECT
+public:
+	FileEvent(const Jid &j, FileTransfer *ft, PsiAccount *acc);
+	~FileEvent();
+
+	int type() const { return File; }
+	Jid from() const;
+	void setFrom(const Jid &);
+	FileTransfer *takeFileTransfer();
+
+	virtual int priority() const;
+
+private:
+	Jid v_from;
+	QGuardedPtr<FileTransfer> ft;
+};
+
+// event queue
+class EventQueue : public QObject
+{
+	Q_OBJECT
+public:
+	EventQueue(PsiAccount *);
+	EventQueue(const EventQueue &);
+	~EventQueue();
+
+	EventQueue &operator= (const EventQueue &);
+
+	int nextId() const;
+	int count() const;
+	int count(const Jid &, bool compareRes=true) const;
+	void enqueue(PsiEvent *);
+	void dequeue(PsiEvent *);
+	PsiEvent *dequeue(const Jid &, bool compareRes=true);
+	PsiEvent *peek(const Jid &, bool compareRes=true) const;
+	PsiEvent *dequeueNext();
+	PsiEvent *peekNext() const;
+	bool hasChats(const Jid &, bool compareRes=true) const;
+	PsiEvent *peekFirstChat(const Jid &, bool compareRes=true) const;
+	void extractChats(QPtrList<PsiEvent> *list, const Jid &, bool compareRes=true);
+	void printContent() const;
+	void clear();
+	void clear(const Jid &, bool compareRes=true);
+
+	QDomElement toXml(QDomDocument *) const; // these work with pointers, to save inclusion of qdom.h, which is pretty large
+	bool fromXml(const QDomElement *);
+
+	bool toFile(const QString &fname);
+	bool fromFile(const QString &fname);
+
+signals:
+	void handleEvent(PsiEvent *);
+	void queueChanged();
+
+private:
+	class Private;
+	Private *d;
+};
+
+
+
+#endif
Index: psi/tags/0.10-os2-b1/src/psigrowlnotifier.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psigrowlnotifier.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psigrowlnotifier.cpp	(revision 38)
@@ -0,0 +1,197 @@
+/*
+ * psigrowlnotifier.cpp: Psi's interface to Growl
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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.
+ *
+ * You can also redistribute and/or modify this program under the
+ * terms of the Psi License, specified in the accompanied COPYING
+ * file, as published by the Psi Project; either dated January 1st,
+ * 2005, 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <qstringlist.h>
+
+#include "common.h"
+#include "psiaccount.h"
+#include "avatars.h"
+#include "growlnotifier.h"
+#include "psigrowlnotifier.h"
+#include "psievent.h"
+#include "userlist.h"
+
+/*!
+ * A class representing the notification context, which will be passed to
+ * Growl, and then passed back when a notification is clicked.
+ */
+class NotificationContext
+{
+public:
+	NotificationContext(PsiAccount* a, Jid j) : account_(a), jid_(j) { }
+	PsiAccount* account() { return account_; }
+	Jid jid() { return jid_; }
+
+
+private:
+	PsiAccount* account_;
+	Jid jid_;
+};
+
+
+/*!
+ * (Private) constructor of the PsiGrowlNotifier.
+ * Initializes notifications and registers with Growl through GrowlNotifier.
+ */
+PsiGrowlNotifier::PsiGrowlNotifier()
+{
+	// Initialize all notifications
+	QStringList nots;
+	nots << QObject::tr("Contact becomes Available");
+	nots << QObject::tr("Contact becomes Unavailable");
+	nots << QObject::tr("Contact changes Status");
+	nots << QObject::tr("Incoming Message");
+	nots << QObject::tr("Incoming Headline");
+	nots << QObject::tr("Incoming File");
+
+	// Initialize default notifications
+	QStringList defaults;
+	defaults << QObject::tr("Contact becomes Available");
+	defaults << QObject::tr("Incoming Message");
+	defaults << QObject::tr("Incoming Headline");
+	defaults << QObject::tr("Incoming File");
+
+	// Register with Growl
+	gn_ = new GrowlNotifier(nots, defaults, "Psi");
+}
+
+
+/*!
+ * Requests the global PsiGrowlNotifier instance.
+ * If PsiGrowlNotifier wasn't initialized yet, it is initialized.
+ *
+ * \see GrowlNotifier()
+ * \return A pointer to the PsiGrowlNotifier instance
+ */
+PsiGrowlNotifier* PsiGrowlNotifier::instance() 
+{
+	if (!instance_) 
+		instance_ = new PsiGrowlNotifier();
+	
+	return instance_;
+}
+
+
+/*!
+ * Requests a popup to be sent to Growl.
+ *
+ * \param account The requesting account.
+ * \param type The type of popup to be sent.
+ * \param jid The originating jid
+ * \param uli The originating userlist item. Can be NULL.
+ * \param event The originating event. Can be NULL.
+ */
+void PsiGrowlNotifier::popup(PsiAccount* account, PsiPopup::PopupType type, const Jid& jid, const Resource& r, const UserListItem* uli, PsiEvent* event)
+{
+	QString name;
+	QPixmap icon;
+	QString title, desc, contact;
+	QString statusTxt = status2txt(makeSTATUS(r.status()));
+	QString statusMsg = r.status().status();
+	if (uli && !uli->name().isEmpty())
+		contact = uli->name();
+	else
+		contact = jid.bare();
+
+#ifdef AVATARS
+	// FIXME: Lookup the client version
+	if (account->avatarFactory()) 
+		icon = account->avatarFactory()->getAvatar(jid, "Psi");
+#endif
+
+	// Default value for the title
+	title = contact;
+
+	switch(type) {
+		case PsiPopup::AlertOnline:
+			name = QObject::tr("Contact becomes Available");
+			title = QString("%1 (%2)").arg(contact).arg(statusTxt);
+			desc = statusMsg;
+			//icon = is->statusPQString(jid, r.status());
+			break;
+		case PsiPopup::AlertOffline:
+			name = QObject::tr("Contact becomes Unavailable");
+			title = QString("%1 (%2)").arg(contact).arg(statusTxt);
+			desc = statusMsg;
+			//icon = is->statusPQString(jid, r.status());
+			break;
+		case PsiPopup::AlertStatusChange:
+			name = QObject::tr("Contact changes Status");
+			title = QString("%1 (%2)").arg(contact).arg(statusTxt);
+			desc = statusMsg;
+			//icon = is->statusPQString(jid, r.status());
+			break;
+		case PsiPopup::AlertMessage: {
+			name = QObject::tr("Incoming Message");
+			title = QObject::tr("%1 says:").arg(contact);
+			const Message* jmessage = &((MessageEvent *)event)->message();
+			desc = jmessage->body();
+			//icon = IconsetFactory::iconPQString("psi/message");
+			break;
+		}
+		case PsiPopup::AlertChat: {
+			name = QObject::tr("Incoming Message");
+			const Message* jmessage = &((MessageEvent *)event)->message();
+			desc = jmessage->body();
+			//icon = IconsetFactory::iconPQString("psi/start-chat");
+			break;
+		}
+		case PsiPopup::AlertHeadline: {
+			name = QObject::tr("Incoming Headline");
+			const Message* jmessage = &((MessageEvent *)event)->message();
+			if ( !jmessage->subject().isEmpty())
+				title = jmessage->subject();
+			desc = jmessage->body();
+			//icon = IconsetFactory::iconPQString("psi/headline");
+			break;
+		}
+		case PsiPopup::AlertFile:
+			name = QObject::tr("Incoming File");
+			desc = QObject::tr("[Incoming File]");
+			//icon = IconsetFactory::iconPQString("psi/file");
+			break;
+		default:
+			break;
+	}
+
+	// Notify Growl
+	NotificationContext* context = new NotificationContext(account, jid);
+	gn_->notify(name, title, desc, icon, false, this, SLOT(notificationClicked(void*)), SLOT(notificationTimedOut(void*)), context);
+}
+
+void PsiGrowlNotifier::notificationClicked(void* c)
+{
+	NotificationContext* context = (NotificationContext*) c;
+	context->account()->actionDefault(context->jid());
+	delete context;
+}
+
+void PsiGrowlNotifier::notificationTimedOut(void* c)
+{
+	NotificationContext* context = (NotificationContext*) c;
+	delete context;
+}
+
+PsiGrowlNotifier* PsiGrowlNotifier::instance_ = 0;
Index: psi/tags/0.10-os2-b1/src/psigrowlnotifier.h
===================================================================
--- psi/tags/0.10-os2-b1/src/psigrowlnotifier.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psigrowlnotifier.h	(revision 38)
@@ -0,0 +1,61 @@
+/*
+ * psigrowlnotifier.h: Psi's interface to Growl
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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.
+ *
+ * You can also redistribute and/or modify this program under the
+ * terms of the Psi License, specified in the accompanied COPYING
+ * file, as published by the Psi Project; either dated January 1st,
+ * 2005, 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIGROWLNOTIFIER_H
+#define PSIGROWLNOTIFIER_H
+
+#include "psipopup.h"
+
+class GrowlNotifier;
+class PsiAccount;
+
+/*!
+ * An interface for Psi to Growl.
+ * This class uses GrowlNotifier to interface with Growl.
+ * There is at most 1 PsiGrowlNotifier per Psi session (Singleton). This 
+ * notifier can be retrieved using instance().
+ *
+ * \see GrowlNotifier
+ */
+class PsiGrowlNotifier : public QObject
+{
+	Q_OBJECT
+
+public:
+	static PsiGrowlNotifier* instance();
+	void popup(PsiAccount* account, PsiPopup::PopupType type, const Jid& j, const Resource& r, const UserListItem* = 0, PsiEvent* = 0);
+
+public slots:
+	void notificationClicked(void*);
+	void notificationTimedOut(void*);
+
+private:
+	PsiGrowlNotifier();
+
+	static PsiGrowlNotifier* instance_;
+	GrowlNotifier* gn_;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/psiiconset.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psiiconset.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psiiconset.cpp	(revision 38)
@@ -0,0 +1,600 @@
+/*
+ * psiiconset.cpp - the Psi iconset class
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "psiiconset.h"
+#include "psievent.h"
+#include "common.h"
+#include "userlist.h"
+#include "anim.h"
+
+#include <qfileinfo.h>
+#include <qdict.h>
+#include <qptrlist.h>
+
+//----------------------------------------------------------------------------
+// PsiIconset
+//----------------------------------------------------------------------------
+
+class PsiIconset::Private
+{
+private:
+	PsiIconset *psi;
+public:
+	Iconset system;
+
+	Private(PsiIconset *_psi) {
+		psi = _psi;
+		psi->emoticons.setAutoDelete(true);
+		psi->roster.setAutoDelete(true);
+	}
+
+	QString iconsetPath(QString name) {
+		QStringList dirs;
+		dirs << ".";
+		dirs << g.pathHome;
+		dirs << g.pathBase;
+
+		QStringList::Iterator it = dirs.begin();
+		for ( ; it != dirs.end(); ++it) {
+			QString fileName = *it + "/iconsets/" + name;
+
+			QFileInfo fi(fileName);
+			if ( fi.exists() )
+				return fileName;
+		}
+
+		return QString::null;
+	}
+
+	void stripFirstAnimFrame(Iconset &is) {
+		QPtrListIterator<Icon> it = is.iterator();
+		for ( ; it.current(); ++it) {
+			it.current()->stripFirstAnimFrame();
+		}
+	}
+
+	void loadIconset(Iconset *to, Iconset *from) {
+		if ( !to ) {
+			qWarning("PsiIconset::loadIconset(): 'to' iconset is NULL!");
+			if ( from )
+				qWarning("from->name() = '%s'", from->name().latin1());
+			return;
+		}
+		if ( !from ) {
+			qWarning("PsiIconset::loadIconset(): 'from' iconset is NULL!");
+			if ( to )
+				qWarning("to->name() = '%s'", to->name().latin1());
+			return;
+		}
+
+		QPtrListIterator<Icon> it = from->iterator();
+		for ( ; it.current(); ++it) {
+			Icon *icon = it.current();
+
+			if ( icon && !icon->name().isEmpty() ) {
+				Icon *toIcon = (Icon *)to->icon(icon->name());
+				if ( toIcon ) {
+					if ( icon->anim() ) {
+						toIcon->setAnim  ( *icon->anim(), false );
+						toIcon->setImpix ( icon->impix(), false );
+					}
+					else {
+						toIcon->setAnim  ( Anim(),        false );
+						toIcon->setImpix ( icon->impix(), false );
+					}
+				}
+				else
+					to->setIcon( icon->name(), *icon );
+			}
+		}
+		
+		to->setInformation(*from);
+	}
+
+	Icon *jid2icon(const Jid &jid, const QString &iconName)
+	{
+		// first level -- global default icon
+		Icon *icon = (Icon *)IconsetFactory::iconPtr(iconName);
+
+		// second level -- transport icon
+		if ( jid.user().isEmpty() || option.useTransportIconsForContacts ) {
+			QMap<QString, QRegExp> services;
+			services["aim"]		= QRegExp("^aim");
+			services["gadugadu"]	= QRegExp("^gg");
+			services["icq"]		= QRegExp("^icq");
+			services["msn"]		= QRegExp("^msn");
+			services["yahoo"]	= QRegExp("^yahoo");
+			services["sms"]		= QRegExp("^sms");
+
+			bool found = false;
+
+			QMapIterator<QString, QRegExp> it = services.begin();
+			for ( ; it != services.end(); ++it) {
+				QRegExp rx = it.data();
+				if ( rx.search(jid.host()) != -1 ) {
+					// get the iconset name of the current service
+					QMapIterator<QString, QString> it2 = option.serviceRosterIconset.find(it.key());
+					if ( it2 != option.serviceRosterIconset.end() ) {
+						Iconset *is = psi->roster.find(it2.data());
+						if ( is ) {
+							Icon *i = (Icon *)is->icon(iconName);
+							if ( i ) {
+								icon = i;
+								found = true;
+								break;
+							}
+						}
+					}
+				}
+			}
+
+			// let's try the default transport iconset then...
+			if ( !found && jid.user().isEmpty() ) {
+				Iconset *is = psi->roster.find(option.serviceRosterIconset["transport"]);
+				if ( is ) {
+					Icon *i = (Icon *)is->icon(iconName);
+					if ( i )
+						icon = i;
+				}
+			}
+		}
+
+		// third level -- custom icons
+		QMapIterator<QString, QString> it = option.customRosterIconset.begin();
+		for ( ; it != option.customRosterIconset.end(); ++it) {
+			QRegExp rx = QRegExp(it.key());
+			if ( rx.search(jid.userHost()) != -1 ) {
+				Iconset *is = psi->roster.find(it.data());
+				if ( is ) {
+					Icon *i = (Icon *)is->icon(iconName);
+					if ( i )
+						icon = (Icon *)is->icon(iconName);
+				}
+			}
+		}
+
+		return icon;
+	}
+
+	Iconset systemIconset(bool *ok)
+	{
+		Iconset def;
+		*ok = def.load ( iconsetPath("system/default") );
+
+		if ( option.systemIconset != "default" ) {
+			Iconset is;
+			is.load ( iconsetPath("system/" + option.systemIconset) );
+
+			loadIconset(&def, &is);
+		}
+
+		stripFirstAnimFrame( def );
+
+		return def;
+	}
+
+	Iconset *defaultRosterIconset(bool *ok)
+	{
+		Iconset *def = new Iconset;
+		*ok = def->load ( iconsetPath("roster/default") );
+
+		if ( option.defaultRosterIconset != "default" ) {
+			Iconset is;
+			is.load ( iconsetPath("roster/" + option.defaultRosterIconset) );
+
+			loadIconset(def, &is);
+		}
+
+		stripFirstAnimFrame( *def );
+
+		return def;
+	}
+
+	QPtrList<Iconset> emoticons()
+	{
+		QPtrList<Iconset> emo;
+
+		QStringList::Iterator it3 = option.emoticons.begin();
+		for ( ; it3 != option.emoticons.end(); ++it3) {
+			Iconset *is = new Iconset;
+			if ( is->load ( iconsetPath("emoticons/" + *it3) ) ) {
+				PsiIconset::removeAnimation(is);
+				is->addToFactory();
+				emo.append( is );
+			}
+			else
+				delete is;
+		}
+
+		return emo;
+	}
+};
+
+PsiIconset::PsiIconset()
+{
+	d = new Private(this);
+}
+
+PsiIconset::~PsiIconset()
+{
+	delete d;
+}
+
+bool PsiIconset::loadSystem()
+{
+	bool ok;
+	Iconset sys = d->systemIconset(&ok);
+	d->loadIconset( &d->system, &sys );
+
+	//d->system = d->systemIconset();
+	d->system.addToFactory();
+
+	return ok;
+}
+
+bool PsiIconset::loadAll()
+{
+	if ( !loadSystem() )
+		return false;
+
+	bool ok;
+
+	// load roster
+	roster.clear();
+
+	// default roster iconset
+	Iconset *def = d->defaultRosterIconset(&ok);
+	def->addToFactory();
+	roster.insert (option.defaultRosterIconset, def);
+
+	// load only necessary roster iconsets
+	QStringList rosterIconsets;
+
+	QMap<QString, QString>::Iterator it = option.serviceRosterIconset.begin();
+	for ( ; it != option.serviceRosterIconset.end(); ++it)
+		if ( rosterIconsets.findIndex( it.data() ) == -1 )
+			rosterIconsets << it.data();
+
+	it = option.customRosterIconset.begin();
+	for ( ; it != option.customRosterIconset.end(); ++it)
+		if ( rosterIconsets.findIndex( it.data() ) == -1 )
+			rosterIconsets << it.data();
+
+	QStringList::Iterator it2 = rosterIconsets.begin();
+	for ( ; it2 != rosterIconsets.end(); ++it2) {
+		if ( *it2 == option.defaultRosterIconset )
+			continue;
+
+		Iconset *is = new Iconset;
+		if ( is->load (d->iconsetPath("roster/" + *it2)) ) {
+			is->addToFactory ();
+			d->stripFirstAnimFrame( *is );
+			roster.insert (*it2, is);
+		}
+		else
+		     delete is;
+	}
+
+	// load emoticons
+	emoticons.clear();
+	emoticons = d->emoticons();
+
+	return ok;
+}
+
+bool PsiIconset::optionsChanged(const Options *old)
+{
+	bool ok = loadSystem();
+
+	// default roster iconset
+	if ( old->defaultRosterIconset != option.defaultRosterIconset ) {
+		Iconset *newDef = d->defaultRosterIconset(&ok);
+		Iconset *oldDef = roster[old->defaultRosterIconset];
+		d->loadIconset( oldDef, newDef );
+
+		roster.setAutoDelete(false);
+		roster.remove(old->defaultRosterIconset);
+		roster.setAutoDelete(true);
+
+		roster.insert (option.defaultRosterIconset, oldDef);
+		delete newDef;
+	}
+
+	// service&custom roster iconsets
+	if ( old->serviceRosterIconset != option.serviceRosterIconset || old->customRosterIconset != option.customRosterIconset ) {
+		QStringList rosterIconsets;
+
+		QMap<QString, QString>::Iterator it = option.serviceRosterIconset.begin();
+		for ( ; it != option.serviceRosterIconset.end(); ++it)
+			if ( rosterIconsets.findIndex( it.data() ) == -1 )
+				rosterIconsets << it.data();
+
+		it = option.customRosterIconset.begin();
+		for ( ; it != option.customRosterIconset.end(); ++it)
+			if ( rosterIconsets.findIndex( it.data() ) == -1 )
+				rosterIconsets << it.data();
+
+		QStringList::Iterator it2 = rosterIconsets.begin();
+		for ( ; it2 != rosterIconsets.end(); ++it2) {
+			if ( *it2 == option.defaultRosterIconset )
+				continue;
+
+			Iconset *is = new Iconset;
+			if ( is->load (d->iconsetPath("roster/" + *it2)) ) {
+				d->stripFirstAnimFrame( *is );
+				Iconset *oldis = roster[*it2];
+
+				if ( oldis )
+					d->loadIconset( oldis, is );
+				else {
+					is->addToFactory ();
+					roster.insert (*it2, is);
+				}
+			}
+			else
+				delete is;
+		}
+
+		bool clear = false;
+		while ( !clear ) {
+			clear = true;
+
+			QDictIterator<Iconset> it3 ( roster );
+			for ( ; it3.current(); ++it3) {
+				QString name = it3.currentKey();
+				if ( name == option.defaultRosterIconset )
+					continue;
+
+				it2 = rosterIconsets.find( name );
+				if ( it2 == rosterIconsets.end() ) {
+					// remove redundant iconset
+					roster.remove( name );
+					clear = false;
+					break;
+				}
+			}
+		}
+	}
+
+	// load emoticons
+	if ( old->emoticons != option.emoticons ) {
+		emoticons.clear();
+		emoticons = d->emoticons();
+	}
+
+	return old->defaultRosterIconset != option.defaultRosterIconset;
+}
+
+Icon *PsiIconset::event2icon(PsiEvent *e)
+{
+	QString icon;
+	if(e->type() == PsiEvent::Message) {
+		MessageEvent *me = (MessageEvent *)e;
+		const Message &m = me->message();
+		if(m.type() == "headline")
+			icon = "psi/headline";
+		else if(m.type() == "chat")
+			icon = "psi/chat";
+		else if(m.type() == "error")
+			icon = "psi/system";
+		else
+			icon = "psi/message";
+	}
+	else if(e->type() == PsiEvent::File) {
+		icon = "psi/file";
+	}
+	else {
+		icon = "psi/system";
+	}
+
+	return d->jid2icon(e->from(), icon);
+}
+
+static QString status2name(int s)
+{
+	QString name;
+	switch ( s ) {
+	case STATUS_OFFLINE:
+		name = "status/offline";
+		break;
+	case STATUS_AWAY:
+		name = "status/away";
+		break;
+	case STATUS_XA:
+		name = "status/xa";
+		break;
+	case STATUS_DND:
+		name = "status/dnd";
+		break;
+	case STATUS_INVISIBLE:
+		name = "status/invisible";
+		break;
+	case STATUS_CHAT:
+		name = "status/chat";
+		break;
+
+	case STATUS_ASK:
+		name = "status/ask";
+		break;
+	case STATUS_NOAUTH:
+		name = "status/noauth";
+		break;
+	case STATUS_ERROR:
+		name = "status/error";
+		break;
+
+	case -1:
+		name = "psi/connect";
+		break;
+
+	case STATUS_ONLINE:
+	default:
+		name = "status/online";
+	}
+
+	return name;
+}
+
+Icon *PsiIconset::statusPtr(int s)
+{
+	return (Icon *)IconsetFactory::iconPtr(status2name(s));
+}
+
+Icon PsiIconset::status(int s)
+{
+	Icon *icon = statusPtr(s);
+	if ( icon )
+		return *icon;
+	return Icon();
+}
+
+Icon *PsiIconset::statusPtr(const XMPP::Status &s)
+{
+	return statusPtr(makeSTATUS(s));
+}
+
+Icon PsiIconset::status(const XMPP::Status &s)
+{
+	return status(makeSTATUS(s));
+}
+
+Icon *PsiIconset::transportStatusPtr(QString name, int s)
+{
+	Icon *icon = 0;
+
+	QMapIterator<QString, QString> it = option.serviceRosterIconset.begin();
+	for ( ; it != option.serviceRosterIconset.end(); ++it) {
+		if (name == it.key()) {
+			Iconset *is = roster.find(it.data());
+			if ( is ) {
+				icon = (Icon *)is->icon(status2name(s));
+				if ( icon )
+					break;
+			}
+		}
+	}
+
+	if ( !icon )
+		icon = statusPtr(s);
+
+	return icon;
+}
+
+Icon *PsiIconset::transportStatusPtr(QString name, const XMPP::Status &s)
+{
+	return transportStatusPtr(name, makeSTATUS(s));
+}
+
+Icon PsiIconset::transportStatus(QString name, int s)
+{
+	Icon *icon = transportStatusPtr(name, s);
+	if ( icon )
+		return *icon;
+	return Icon();
+}
+
+Icon PsiIconset::transportStatus(QString name, const XMPP::Status &s)
+{
+	Icon *icon = transportStatusPtr(name, s);
+	if ( icon )
+		return *icon;
+	return Icon();
+}
+
+Icon *PsiIconset::statusPtr(const XMPP::Jid &jid, int s)
+{
+	return d->jid2icon(jid, status2name(s));
+}
+
+Icon *PsiIconset::statusPtr(const XMPP::Jid &jid, const XMPP::Status &s)
+{
+	return statusPtr(jid, makeSTATUS(s));
+}
+
+Icon PsiIconset::status(const XMPP::Jid &jid, int s)
+{
+	Icon *icon = statusPtr(jid, s);
+	if ( icon )
+		return *icon;
+	return Icon();
+}
+
+Icon PsiIconset::status(const XMPP::Jid &jid, const XMPP::Status &s)
+{
+	Icon *icon = statusPtr(jid, s);
+	if ( icon )
+		return *icon;
+	return Icon();
+}
+
+Icon *PsiIconset::statusPtr(UserListItem *u)
+{
+	if ( !u )
+		return 0;
+
+	int s = 0;
+	if ( !u->presenceError().isEmpty() )
+		s = STATUS_ERROR;
+	else if ( u->isTransport() ) {
+		if ( u->isAvailable() )
+			s = makeSTATUS( (*(u->priority())).status() );
+		else
+			s = STATUS_OFFLINE;
+	}
+	else if ( u->ask() == "subscribe" && !u->isAvailable() && !u->isTransport() )
+		s = STATUS_ASK;
+	else if ( (u->subscription().type() == Subscription::From || u->subscription().type() == Subscription::None) && !u->isAvailable() && !u->isPrivate() )
+		s = STATUS_NOAUTH;
+	else if( !u->isAvailable() )
+		s = STATUS_OFFLINE;
+	else
+		s = makeSTATUS( (*(u->priority())).status() );
+
+	return statusPtr(u->jid(), s);
+}
+
+Icon PsiIconset::status(UserListItem *u)
+{
+	Icon *icon = statusPtr(u);
+	if ( icon )
+		return *icon;
+	return Icon();
+}
+
+const Iconset &PsiIconset::system() const
+{
+	return d->system;
+}
+
+void PsiIconset::stripFirstAnimFrame(Iconset *is)
+{
+	if ( is )
+		d->stripFirstAnimFrame(*is);
+}
+
+void PsiIconset::removeAnimation(Iconset *is)
+{
+	if ( is ) {
+		QPtrListIterator<Icon> it = is->iterator();
+		for ( ; it.current(); ++it) {
+			it.current()->removeAnim(false);
+		}
+	}
+}
Index: psi/tags/0.10-os2-b1/src/psiiconset.h
===================================================================
--- psi/tags/0.10-os2-b1/src/psiiconset.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psiiconset.h	(revision 38)
@@ -0,0 +1,83 @@
+/*
+ * psiiconset.h - the Psi iconset class
+ * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIICONSET_H
+#define PSIICONSET_H
+
+#include <qptrlist.h>
+#include <qmap.h>
+
+#include "iconset.h"
+#include "im.h"
+
+class PsiEvent;
+class UserListItem;
+struct Options;
+
+class PsiIconset
+{
+public:
+	PsiIconset();
+	~PsiIconset();
+
+	bool loadSystem();
+	bool loadAll();
+
+	bool optionsChanged(const Options *old); // returns 'true' if Psi needs to be restarted
+
+	QDict<Iconset> roster;
+	QPtrList<Iconset> emoticons;
+	const Iconset &system() const;
+	void stripFirstAnimFrame(Iconset *);
+	static void removeAnimation(Iconset *);
+
+	Icon *event2icon(PsiEvent *);
+
+	// these two can possibly fail (and return 0)
+	Icon *statusPtr(int);
+	Icon *statusPtr(const XMPP::Status &);
+
+	// these two return empty Icon on failure and are safe
+	Icon status(int);
+	Icon status(const XMPP::Status &);
+
+	// JID-enabled status functions
+	Icon *statusPtr(const XMPP::Jid &, int);
+	Icon *statusPtr(const XMPP::Jid &, const XMPP::Status &);
+
+	Icon status(const XMPP::Jid &, int);
+	Icon status(const XMPP::Jid &, const XMPP::Status &);
+
+	// functions to get status icon by transport name
+	Icon *transportStatusPtr(QString name, int);
+	Icon *transportStatusPtr(QString name, const XMPP::Status &);
+
+	Icon transportStatus(QString name, int);
+	Icon transportStatus(QString name, const XMPP::Status &);
+
+	Icon *statusPtr(UserListItem *);
+	Icon status(UserListItem *);
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/psipopup.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psipopup.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psipopup.cpp	(revision 38)
@@ -0,0 +1,479 @@
+/*
+ * psipopup.cpp - the Psi passive popup class
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "psipopup.h"
+#include "common.h"
+#include "fancypopup.h"
+#include "fancylabel.h"
+#include "userlist.h"
+#include "alerticon.h"
+#include "psievent.h"
+#include "im.h"
+#include "psicon.h"
+#include "psiaccount.h"
+
+#include <qapplication.h>
+#include <qlayout.h>
+
+//----------------------------------------------------------------------------
+// PsiPopupList
+//----------------------------------------------------------------------------
+
+class PsiPopupList : public QObject, public QPtrList<PsiPopup>
+{
+	Q_OBJECT
+private:
+	int numPopups;
+
+public:
+	PsiPopupList()
+	: QObject(qApp)
+	{
+		setAutoDelete(true);
+	}
+
+	PsiPopup *last()
+	{
+		if ( !count() )
+			return 0;
+		return QPtrList<PsiPopup>::first();
+	}
+
+	void prepend(const PsiPopup *d)
+	{
+		if ( isEmpty() )
+			emit started();
+
+		connect(d, SIGNAL(destroyed(QObject *)), SLOT(popupDestroyed(QObject *)));
+		QPtrList<PsiPopup>::prepend(d);
+	}
+
+signals:
+	void started();
+	void finished();
+
+private slots:
+	void popupDestroyed(QObject *p)
+	{
+		setAutoDelete(false);
+		remove((PsiPopup *)p);
+		setAutoDelete(true);
+
+		if ( isEmpty() )
+			emit finished();
+	}
+};
+
+static PsiPopupList *psiPopupList = 0;
+
+//----------------------------------------------------------------------------
+// PsiPopup::Private
+//----------------------------------------------------------------------------
+
+class PsiPopup::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(PsiPopup *p);
+	~Private();
+
+	void init(const Icon *titleIcon, QString titleText, PsiAccount *_acc, PopupType type);
+	QString clipText(QString);
+	QBoxLayout *createContactInfo(const Icon *icon, QString text);
+
+private slots:
+	void popupDestroyed();
+	void popupClicked(int);
+	void eventDestroyed();
+
+public:
+	PsiCon *psi;
+	PsiAccount *account;
+	FancyPopup *popup;
+	PsiPopup *psiPopup;
+	QString id;
+	PopupType popupType;
+	Jid jid;
+	Status status;
+	PsiEvent *event;
+	Icon *titleIcon;
+	bool display;
+};
+
+PsiPopup::Private::Private(PsiPopup *p)
+{
+	psiPopup = p;
+	popup = 0;
+	popupType = AlertNone;
+	event = 0;
+	titleIcon = 0;
+}
+
+PsiPopup::Private::~Private()
+{
+	if ( popup )
+		delete popup;
+	if ( titleIcon )
+		delete titleIcon;
+	popup = 0;
+}
+
+void PsiPopup::Private::init(const Icon *_titleIcon, QString titleText, PsiAccount *acc, PopupType type)
+{
+	psi = acc->psi();
+	account = acc;
+	display = true;
+
+	if ( !psiPopupList )
+		psiPopupList = new PsiPopupList();
+
+	FancyPopup *last = 0;
+	if ( psiPopupList->last() )
+		last = psiPopupList->last()->popup();
+
+	if ( type != AlertNone )
+		titleIcon = new AlertIcon(_titleIcon);
+	else
+		titleIcon = new Icon(*_titleIcon);
+
+	FancyPopup::setHideTimeout( option.ppHideTime );
+	FancyPopup::setBorderColor( option.ppBorderColor );
+
+	popup = new FancyPopup(titleText, titleIcon, last, false);
+	connect(popup, SIGNAL(clicked(int)), SLOT(popupClicked(int)));
+	connect(popup, SIGNAL(destroyed()), SLOT(popupDestroyed()));
+
+	// create id
+	if ( _titleIcon )
+		id += _titleIcon->name();
+	id += titleText;
+}
+
+void PsiPopup::Private::popupDestroyed()
+{
+	popup = 0;
+	psiPopup->deleteLater();
+}
+
+void PsiPopup::Private::popupClicked(int button)
+{
+	if ( button == (int)LeftButton ) {
+		if ( event )
+			psi->processEvent( event );
+		else if ( account ) {
+			// FIXME: it should work in most cases, but
+			// maybe it's better to fix UserList::find()?
+			Jid j( jid.userHost() );
+			account->actionDefault( j );
+		}
+	}
+
+	if ( popup )
+		popup->hide();
+}
+
+void PsiPopup::Private::eventDestroyed()
+{
+	if ( (popupType == AlertMessage || popupType == AlertHeadline) && !option.popupMsgs )
+		delete popup;
+	if ( popupType == AlertChat && !option.popupChats /*&& option.alertOpenChats*/ )
+		delete popup;
+	if ( popupType == AlertHeadline && !option.popupHeadlines)
+		delete popup;
+	if ( popupType == AlertFile && !option.popupFiles)
+		delete popup;
+
+	event = 0;
+}
+
+QString PsiPopup::Private::clipText(QString text)
+{
+	if ( option.ppTextClip > 0 ) {
+		// richtext will give us trouble here
+		if ( ((int)text.length()) > option.ppTextClip ) {
+			text = text.left( option.ppTextClip );
+
+			// delete last unclosed tag
+			/*if ( text.find("</") > text.find(">") ) {
+
+				text = text.left( text.find("</") );
+			}*/
+
+			text += "...";
+		}
+	}
+
+	return text;
+}
+
+QBoxLayout *PsiPopup::Private::createContactInfo(const Icon *icon, QString text)
+{
+	QHBoxLayout *dataBox = new QHBoxLayout(0);
+
+	if ( icon ) {
+		IconLabel *iconLabel = new IconLabel(popup);
+		iconLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
+		iconLabel->setIcon(icon);
+		dataBox->addWidget(iconLabel);
+
+		dataBox->addSpacing(5);
+	}
+
+	IconLabel *textLabel = new IconLabel(popup);
+	QFont font;
+	font.fromString( option.font[fPopup] );
+	textLabel->setFont(font);
+
+	textLabel->setText(QString("<qt>%1</qt>").arg(clipText(text)));
+	textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
+	dataBox->addWidget(textLabel);
+
+	return dataBox;
+}
+
+//----------------------------------------------------------------------------
+// PsiPopup
+//----------------------------------------------------------------------------
+
+PsiPopup::PsiPopup(const Icon *titleIcon, QString titleText, PsiAccount *acc)
+{
+	d = new Private(this);
+	d->init(titleIcon, titleText, acc, AlertNone);
+}
+
+PsiPopup::~PsiPopup()
+{
+	delete d;
+}
+
+PsiPopup::PsiPopup(PopupType type, PsiAccount *acc)
+{
+	d = new Private(this);
+
+	d->popupType = type;
+	Icon *icon = 0;
+	QString text = "Psi: ";
+	bool doAlertIcon = false;
+
+	switch(type) {
+	case AlertOnline:
+		text += PsiPopup::tr("Contact online");
+		icon = (Icon *)IconsetFactory::iconPtr("status/online");
+		break;
+	case AlertOffline:
+		text += PsiPopup::tr("Contact offline");
+		icon = (Icon *)IconsetFactory::iconPtr("status/offline");
+		break;
+	case AlertStatusChange:
+		text += PsiPopup::tr("Status change");
+		icon = (Icon *)IconsetFactory::iconPtr("status/online");
+		break;
+	case AlertMessage:
+		text += PsiPopup::tr("Incoming message");
+		icon = (Icon *)IconsetFactory::iconPtr("psi/message");
+		doAlertIcon = true;
+		break;
+	case AlertChat:
+		text += PsiPopup::tr("Incoming chat message");
+		icon= (Icon *)IconsetFactory::iconPtr("psi/chat");
+		doAlertIcon = true;
+		break;
+	case AlertHeadline:
+		text += PsiPopup::tr("Headline");
+		icon= (Icon *)IconsetFactory::iconPtr("psi/headline");
+		doAlertIcon = true;
+		break;
+	case AlertFile:
+		text += PsiPopup::tr("Incoming file");
+		icon= (Icon *)IconsetFactory::iconPtr("psi/file");
+		doAlertIcon = true;
+		break;
+	default:
+		break;
+	}
+
+	d->init(icon, text, acc, doAlertIcon ? type : AlertNone);
+}
+
+void PsiPopup::setData(const Icon *icon, QString text)
+{
+	if ( !d->popup ) {
+		deleteLater();
+		return;
+	}
+
+	d->popup->addLayout( d->createContactInfo(icon, text) );
+
+	// update id
+	if ( icon )
+		d->id += icon->name();
+	d->id += text;
+
+	show();
+}
+
+void PsiPopup::setData(const Jid &j, const Resource &r, const UserListItem *u, const PsiEvent *event)
+{
+	if ( !d->popup ) {
+		deleteLater();
+		return;
+	}
+
+	d->jid    = j;
+	d->status = r.status();
+	d->event  = (PsiEvent *)event;
+
+	if ( event )
+		connect(event, SIGNAL(destroyed()), d, SLOT(eventDestroyed()));
+
+	Icon *icon = is->statusPtr(j, r.status());
+	QString text;
+
+	QString jid = j.full();
+	if ( option.ppJidClip > 0 && ((int)jid.length()) > option.ppJidClip )
+		jid = jid.left( option.ppJidClip ) + "...";
+
+	QString status;
+	if ( option.ppStatusClip != 0 )
+		status = r.status().status();
+	if ( option.ppStatusClip > 0 )
+		if ( ((int)status.length()) > option.ppStatusClip )
+			status = status.left ( option.ppStatusClip ) + "...";
+
+	QString name;
+	if ( u && !u->name().isEmpty() ) {
+		if ( !option.ppJidClip )
+			name = "<nobr>" + u->name() + "</nobr>";
+		else
+			name = "<nobr>" + u->name() + " &lt;" + jid + "&gt;" + "</nobr>";
+	}
+	else
+		name = "<nobr>&lt;" + jid + "&gt;</nobr>";
+
+	QString statusString = plain2rich(status);
+	if ( option.useEmoticons )
+		statusString = emoticonify(statusString);
+
+	if ( !statusString.isEmpty() )
+		statusString = "<br>" + statusString;
+
+	QString contactText = "<font size=\"+1\">" + name + "</font>" + statusString;
+
+	// hack for duplicate "Contact Online"/"Status Change" popups
+	PsiPopup *pp = psiPopupList->first();
+	while ( pp ) {
+		if ( d->jid.full() == pp->d->jid.full() && d->status.show() == pp->d->status.show() && d->status.status() == d->status.status() ) {
+			if ( d->popupType == AlertStatusChange && pp->d->popupType == AlertOnline ) {
+				d->display = false;
+				deleteLater();
+				break;
+			}
+		}
+
+		pp = psiPopupList->next();
+	}
+
+	// show popup
+	if ( d->popupType != AlertHeadline && (d->popupType != AlertFile || !option.popupFiles) )
+		setData(icon, contactText);
+	else if ( d->popupType == AlertHeadline ) {
+		QVBoxLayout *vbox = new QVBoxLayout(0);
+		vbox->addLayout( d->createContactInfo(icon, contactText) );
+
+		vbox->addSpacing(5);
+
+		const Message *jmessage = &((MessageEvent *)event)->message();
+		QString message;
+		if ( !jmessage->subject().isEmpty() )
+			message += "<font color=\"red\"><b>" + tr("Subject:") + " " + jmessage->subject() + "</b></font><br>";
+		message += plain2rich( jmessage->body() );
+
+		QLabel *messageLabel = new QLabel(d->popup);
+		QFont font = messageLabel->font();
+		font.setPointSize(option.smallFontSize);
+		messageLabel->setFont(font);
+
+		messageLabel->setTextFormat(RichText);
+		messageLabel->setText( d->clipText(linkify( message )) );
+		messageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+		vbox->addWidget(messageLabel);
+
+		// update id
+		if ( icon )
+			d->id += icon->name();
+		d->id += contactText;
+		d->id += message;
+
+		d->popup->addLayout( vbox );
+		show();
+	}
+}
+
+void PsiPopup::show()
+{
+	if ( !d->popup || !option.ppIsOn ) {
+		deleteLater();
+		return;
+	}
+
+	if ( !d->id.isEmpty() /*&& option.ppNoDupes*/ ) {
+		PsiPopup *pp = psiPopupList->first();
+		while ( pp ) {
+			if ( d->id == pp->id() && pp->popup() ) {
+				pp->popup()->restartHideTimer();
+
+				d->display = false;
+				break;
+			}
+
+			pp = psiPopupList->next();
+		}
+	}
+
+	if ( d->display ) {
+		psiPopupList->prepend( this );
+		d->popup->show();
+	}
+	else {
+		deleteLater();
+	}
+}
+
+QString PsiPopup::id() const
+{
+	return d->id;
+}
+
+FancyPopup *PsiPopup::popup()
+{
+	return d->popup;
+}
+
+void PsiPopup::deleteAll()
+{
+	if ( !psiPopupList )
+		return;
+
+	psiPopupList->clear();
+	delete psiPopupList;
+	psiPopupList = 0;
+}
+
+#include "psipopup.moc"
Index: psi/tags/0.10-os2-b1/src/psipopup.h
===================================================================
--- psi/tags/0.10-os2-b1/src/psipopup.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psipopup.h	(revision 38)
@@ -0,0 +1,74 @@
+/*
+ * psipopup.h - the Psi passive popup class
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSIPOPUP_H
+#define PSIPOPUP_H
+
+#include <qobject.h>
+
+#include "im.h"
+using namespace XMPP;
+
+class PsiCon;
+class PsiAccount;
+class UserListItem;
+class FancyPopup;
+class Icon;
+class PsiEvent;
+
+class PsiPopup : public QObject
+{
+	Q_OBJECT
+public:
+	PsiPopup(const Icon *titleIcon, QString titleText, PsiAccount *acc);
+	~PsiPopup();
+
+	enum PopupType {
+		AlertNone = 0,
+
+		AlertOnline,
+		AlertOffline,
+		AlertStatusChange,
+
+		AlertMessage,
+		AlertChat,
+		AlertHeadline,
+		AlertFile
+	};
+	PsiPopup(PopupType type, PsiAccount *acc);
+
+	void setData(const Icon *icon, QString text);
+	void setData(const Jid &, const Resource &, const UserListItem * = 0, const PsiEvent * = 0);
+
+	void show();
+
+	QString id() const;
+	FancyPopup *popup();
+
+	static void deleteAll();
+
+public:
+	class Private;
+private:
+	Private *d;
+	friend class Private;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/psitoolbar.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/psitoolbar.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psitoolbar.cpp	(revision 38)
@@ -0,0 +1,193 @@
+/*
+ * psitoolbar.cpp - the Psi toolbar class
+ * Copyright (C) 2003, 2004  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "common.h"
+#include "psitoolbar.h"
+#include <qpopupmenu.h>
+#include <qlabel.h>
+#include <qaction.h>
+#include "psicon.h"
+#include "mainwin.h"
+#include "psicon.h"
+#include "iconaction.h"
+#include "options/toolbardlg.h"
+
+//----------------------------------------------------------------------------
+// PsiToolBar::Private
+//----------------------------------------------------------------------------
+
+class PsiToolBar::Private
+{
+public:
+	Private();
+
+	bool customizeable, moveable;
+	PsiActionList::ActionsType type;
+	QString group;
+	int groupIndex;
+	PsiCon *psi;
+	QPtrList<IconAction> uniqueActions;
+};
+
+PsiToolBar::Private::Private()
+{
+	customizeable = true;
+	moveable = true;
+	psi = 0;
+	uniqueActions.setAutoDelete( true );
+}
+
+//----------------------------------------------------------------------------
+// PsiToolBar
+//----------------------------------------------------------------------------
+
+PsiToolBar::PsiToolBar(const QString &label, QMainWindow *mainWindow, QWidget *parent, bool newLine, const char *name, WFlags f)
+: QToolBar(label, mainWindow, parent, newLine, name, f)
+{
+	d = new Private();
+}
+
+PsiToolBar::PsiToolBar(QMainWindow *parent, const char *name)
+: QToolBar(parent, name)
+{
+	d = new Private();
+}
+
+PsiToolBar::~PsiToolBar()
+{
+	delete d;
+}
+
+bool PsiToolBar::isCustomizeable() const
+{
+	return d->customizeable;
+}
+
+void PsiToolBar::setCustomizeable( bool b )
+{
+	d->customizeable = b;
+}
+
+bool PsiToolBar::isMoveable() const
+{
+	return d->moveable;
+}
+
+void PsiToolBar::setMoveable( bool b )
+{
+	d->moveable = b;
+}
+	
+void PsiToolBar::contextMenuEvent(QContextMenuEvent *e)
+{
+	e->accept();
+	if(option.lockdown.options)
+		return;
+
+	if ( !d->customizeable )
+		return;
+
+	MainWin *mainWin = (MainWin *)mainWindow();
+	QPopupMenu pm;
+
+	pm.insertItem(IconsetFactory::icon("psi/toolbars"), tr("Configure &Toolbar..."), 0);
+
+	int ret = pm.exec( e->globalPos() );
+
+	if ( ret == 0 ) {
+		QWidget *w = mainWin->psiCon()->doToolbars();
+		if ( !w->inherits("ToolbarDlg") )
+			return;
+
+		ToolbarDlg *dlg = (ToolbarDlg *)w;
+		dlg->setCurrentToolbar(this);
+	}
+}
+
+PsiActionList::ActionsType PsiToolBar::type() const
+{
+	return d->type;
+}
+
+QString PsiToolBar::group() const
+{
+	return d->group;
+}
+
+int PsiToolBar::groupIndex() const
+{
+	return d->groupIndex;
+}
+
+void PsiToolBar::setGroup( QString group, int index )
+{
+	d->group = group;
+	d->groupIndex = index;
+}
+
+void PsiToolBar::setType( PsiActionList::ActionsType type )
+{
+	d->type = PsiActionList::ActionsType( PsiActionList::ActionsType( type ) | PsiActionList::Actions_Common );
+}
+
+void PsiToolBar::initialize( Options::ToolbarPrefs &tbPref, bool createUniqueActions )
+{
+	d->uniqueActions.clear();
+
+	setHorizontallyStretchable( tbPref.stretchable );
+	setVerticallyStretchable( tbPref.stretchable );
+
+	setMovingEnabled ( !tbPref.locked );
+
+	if ( d->psi ) {
+		ActionList actions = d->psi->actionList()->suitableActions( d->type );
+		QStringList keys = tbPref.keys;
+		for (uint j = 0; j < keys.size(); j++) {
+			IconAction *action = actions.action( keys[j] );
+
+			if ( action ) {
+				if ( createUniqueActions ) {
+					action = action->copy();
+					d->uniqueActions.append( action );
+				}
+
+				action->addTo( this );
+				emit registerAction( action );
+			}
+			else
+				qWarning("PsiToolBar::initialize(): action %s not found!", keys[j].latin1());
+		}
+	}
+	else
+		qWarning("PsiToolBar::initialize(): psi is NULL!");
+
+	if ( tbPref.on )
+		show();
+	else
+		hide();
+
+	tbPref.dirty = false;
+}
+
+void PsiToolBar::setPsiCon( PsiCon *psi )
+{
+	d->psi = psi;
+}
+
Index: psi/tags/0.10-os2-b1/src/psitoolbar.h
===================================================================
--- psi/tags/0.10-os2-b1/src/psitoolbar.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/psitoolbar.h	(revision 38)
@@ -0,0 +1,67 @@
+/*
+ * psitoolbar.h - the Psi toolbar class
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef PSITOOLBAR_H
+#define PSITOOLBAR_H
+
+#include <qtoolbar.h>
+
+#include "common.h"
+#include "psiactionlist.h"
+
+class PsiCon;
+
+class PsiToolBar : public QToolBar
+{
+	Q_OBJECT
+
+public:
+	PsiToolBar(const QString &label, QMainWindow *mainWindow, QWidget *parent, bool newLine = FALSE, const char *name = 0, WFlags f = 0);
+	PsiToolBar(QMainWindow *parent = 0, const char *name = 0);
+	~PsiToolBar();
+
+	void setPsiCon( PsiCon *psi );
+
+	PsiActionList::ActionsType type() const;
+	void setType( PsiActionList::ActionsType );
+	void initialize( Options::ToolbarPrefs &, bool createUniqueActions );
+
+	QString group() const;
+	int groupIndex() const;
+	void setGroup( QString group, int index );
+	
+	bool isCustomizeable() const;
+	void setCustomizeable( bool );
+
+	bool isMoveable() const;
+	void setMoveable( bool );
+	
+signals:
+	void registerAction( IconAction * );
+
+protected:
+	void contextMenuEvent(QContextMenuEvent *e);
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/qwextend.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/qwextend.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/qwextend.cpp	(revision 38)
@@ -0,0 +1,145 @@
+#include"qwextend.h"
+
+#ifdef Q_WS_X11
+
+#define protected public
+#define private public
+#include<qwidget.h>
+#undef protected
+#undef private
+
+#include<qcursor.h>
+#include<qobjectlist.h>
+#include<qpixmap.h>
+#include<X11/Xlib.h>
+#include<X11/Xutil.h>
+
+// taken from qt/x11 (doing this sucks sucks sucks sucks sucks)
+void reparent_good(QWidget *that, WFlags f, bool showIt)
+{
+    extern void qPRCreate( const QWidget *, Window );
+    extern void qt_XDestroyWindow( const QWidget *destroyer, Display *display, Window window );
+    extern bool qt_dnd_enable( QWidget* w, bool on );
+
+    QWidget *parent = 0;
+    Display *dpy = that->x11Display();
+    QCursor oldcurs;
+    bool setcurs = that->testWState(Qt::WState_OwnCursor);
+    if ( setcurs ) {
+	oldcurs = that->cursor();
+	that->unsetCursor();
+    }
+
+    // dnd unregister (we will register again below)
+    bool accept_drops = that->acceptDrops();
+    that->setAcceptDrops( FALSE );
+
+    // clear mouse tracking, re-enabled below
+    bool mouse_tracking = that->hasMouseTracking();
+    that->clearWState(Qt::WState_MouseTracking);
+
+    QWidget* oldtlw = that->topLevelWidget();
+    QWidget *oldparent = that->parentWidget();
+    WId old_winid = that->winid;
+    if ( that->testWFlags(Qt::WType_Desktop) )
+	old_winid = 0;
+    that->setWinId( 0 );
+
+    // hide and reparent our own window away. Otherwise we might get
+    // destroyed when emitting the child remove event below. See QWorkspace.
+    XUnmapWindow( that->x11Display(), old_winid );
+    XReparentWindow( that->x11Display(), old_winid,
+		     RootWindow( that->x11Display(), that->x11Screen() ), 0, 0 );
+
+    if ( that->isTopLevel() ) {
+        // input contexts are associated with toplevel widgets, so we need
+        // destroy the context here.  if we are reparenting back to toplevel,
+        // then we will have another context created, otherwise we will
+        // use our new toplevel's context
+        that->destroyInputContext();
+    }
+
+    if ( that->isTopLevel() || !parent ) // we are toplevel, or reparenting to toplevel
+        that->topData()->parentWinId = 0;
+
+    if ( parent != that->parentObj ) {
+	if ( that->parentObj )				// remove from parent
+	    that->parentObj->removeChild( that );
+	if ( parent )				// insert into new parent
+	    parent->insertChild( that );
+    }
+    bool     enable = that->isEnabled();		// remember status
+    QWidget::FocusPolicy fp = that->focusPolicy();
+    QSize    s	    = that->size();
+    QPixmap *bgp    = (QPixmap *)that->backgroundPixmap();
+    QColor   bgc    = that->bg_col;			// save colors
+    QString capt= that->caption();
+    that->widget_flags = f;
+    that->clearWState( Qt::WState_Created | Qt::WState_Visible | Qt::WState_ForceHide );
+    that->create();
+    if ( that->isTopLevel() || (!parent || parent->isVisible() ) )
+	that->setWState( Qt::WState_ForceHide );	// new widgets do not show up in already visible parents
+
+    const QObjectList *chlist = that->children();
+    if ( chlist ) {				// reparent children
+	QObjectListIt it( *chlist );
+	QObject *obj;
+	while ( (obj=it.current()) ) {
+	    if ( obj->isWidgetType() ) {
+		QWidget *w = (QWidget *)obj;
+		if ( !w->isTopLevel() ) {
+		    XReparentWindow( that->x11Display(), w->winId(), that->winId(),
+				     w->geometry().x(), w->geometry().y() );
+		} else if ( w->isPopup()
+			    || w->testWFlags(Qt::WStyle_DialogBorder)
+			    || w->testWFlags(Qt::WType_Dialog)
+			    || w->testWFlags(Qt::WStyle_Tool) ) {
+		    XSetTransientForHint( that->x11Display(), w->winId(), that->winId() );
+		}
+	    }
+	    ++it;
+	}
+    }
+    qPRCreate( that, old_winid );
+    if ( bgp )
+	XSetWindowBackgroundPixmap( dpy, that->winid, bgp->handle() );
+    else
+	XSetWindowBackground( dpy, that->winid, bgc.pixel(that->x11Screen()) );
+
+    // all this just to do a stupid resize instead of setGeometry
+    //setGeometry( p.x(), p.y(), s.width(), s.height() );
+    that->resize(s.width(), s.height());
+
+    that->setEnabled( enable );
+    that->setFocusPolicy( fp );
+    if ( !capt.isNull() ) {
+	that->extra->topextra->caption = QString::null;
+	that->setCaption( capt );
+    }
+    if ( showIt )
+	that->show();
+    if ( old_winid )
+	qt_XDestroyWindow( that, dpy, old_winid );
+    if ( setcurs )
+	that->setCursor(oldcurs);
+
+    that->reparentFocusWidgets( oldtlw );
+
+    // re-register dnd
+    if (oldparent)
+	oldparent->checkChildrenDnd();
+
+    if ( accept_drops )
+	that->setAcceptDrops( TRUE );
+    else {
+	that->checkChildrenDnd();
+	that->topData()->dnd = 0;
+	qt_dnd_enable(that, (that->extra && that->extra->children_use_dnd));
+    }
+
+    // re-enable mouse tracking
+    if (mouse_tracking)
+	that->setMouseTracking(mouse_tracking);
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/src/qwextend.h
===================================================================
--- psi/tags/0.10-os2-b1/src/qwextend.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/qwextend.h	(revision 38)
@@ -0,0 +1,12 @@
+#ifndef QWEXTEND_H
+#define QWEXTEND_H
+
+#include<qglobal.h>
+
+#ifdef Q_WS_X11
+class QWidget;
+typedef uint WFlags;
+void reparent_good(QWidget *that, WFlags f, bool showIt);
+#endif
+
+#endif
Index: psi/tags/0.10-os2-b1/src/rtparse.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/rtparse.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/rtparse.cpp	(revision 38)
@@ -0,0 +1,93 @@
+/****************************************************************************
+** rtparse.cpp - class for manipulating richtext
+** Copyright (C) 2001, 2002  Justin Karneges
+**
+** 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.
+**
+****************************************************************************/
+
+#include"rtparse.h"
+#include"common.h"
+
+
+RTParse::RTParse(const QString &_in)
+{
+	in = _in;
+	v_atEnd = false;
+	v_at = 0;
+	//printf("rtparse:\n");
+}
+
+const QString &RTParse::output() const
+{
+	//printf("final: [%s]\n", out.latin1());
+	return out;
+}
+
+QString RTParse::next()
+{
+	if(v_atEnd)
+		return "";
+
+	// if we're at a tag, append it to the output
+	if(in.at(v_at) == '<') {
+		QString s;
+		int n = in.find('>', v_at);
+		if(n == -1) {
+			s = in.mid(v_at);
+		}
+		else {
+			++n;
+			s = in.mid(v_at, n-v_at);
+		}
+		v_at += s.length();
+		out += s;
+	}
+
+	// now find the next tag, and grab the text in between
+	QString s;
+	int x = in.find('<', v_at);
+	if(x == -1) {
+		s = in.mid(v_at);
+		v_atEnd = true;
+	}
+	else {
+		s = in.mid(v_at, x-v_at);
+	}
+	v_at += s.length();
+	//printf("chunk = '%s'\n", s.latin1());
+	s = resolveEntities(s);
+	//printf("resolved = '%s'\n", s.latin1());
+	return s;
+}
+
+bool RTParse::atEnd() const
+{
+	return v_atEnd;
+}
+
+void RTParse::putPlain(const QString &s)
+{
+	//printf("got this: [%s]\n", s.latin1());
+	out += expandEntities(s);
+	//printf("changed to this: [%s]\n", expandEntities(s).latin1());
+}
+
+void RTParse::putRich(const QString &s)
+{
+	out += s;
+	//printf("+ '%s'\n", s.latin1());
+}
+
Index: psi/tags/0.10-os2-b1/src/rtparse.h
===================================================================
--- psi/tags/0.10-os2-b1/src/rtparse.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/rtparse.h	(revision 38)
@@ -0,0 +1,44 @@
+/****************************************************************************
+** rtparse.h - class for manipulating richtext
+** Copyright (C) 2001, 2002  Justin Karneges
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef RTPARSE_H
+#define RTPARSE_H
+
+#include<qstring.h>
+
+class RTParse
+{
+public:
+	RTParse(const QString &);
+
+	const QString &output() const;
+
+	QString next();
+	bool atEnd() const;
+	void putPlain(const QString &);
+	void putRich(const QString &);
+
+private:
+	QString in, out;
+	int v_at;
+	bool v_atEnd;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/servicesdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/servicesdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/servicesdlg.cpp	(revision 38)
@@ -0,0 +1,841 @@
+/*
+ * servicesdlg.cpp - a dialog for browsing Jabber services
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"servicesdlg.h"
+
+#include<qpushbutton.h>
+#include<qcombobox.h>
+#include<qstringlist.h>
+#include<qlistbox.h>
+#include<qlayout.h>
+#include<qlineedit.h>
+#include<qlabel.h>
+#include<qgrid.h>
+#include<qguardedptr.h>
+#include<qlistview.h>
+#include<qgroupbox.h>
+#include<qapplication.h>
+#include<qdesktopwidget.h>
+#include"psicon.h"
+#include"psiaccount.h"
+#include"im.h"
+#include"xmpp_tasks.h"
+#include"common.h"
+#include"busywidget.h"
+#include"iconwidget.h"
+#include"xdata_widget.h"
+#include"xmpp_xdata.h"
+#include"xmpp_xmlcommon.h"
+
+using namespace XMLHelper;
+
+
+//----------------------------------------------------------------------------
+// JT_XRegister
+//----------------------------------------------------------------------------
+
+class JT_XRegister : public JT_Register
+{
+	Q_OBJECT
+public:
+	JT_XRegister(Task *parent);
+
+	void setXForm(const Form &frm, const XData &_form);
+
+	bool take(const QDomElement &);
+	QDomElement iq() const;
+
+	void onGo();
+
+private:
+	QDomElement _iq;
+};
+
+JT_XRegister::JT_XRegister( Task *parent )
+	: JT_Register( parent )
+{
+}
+
+bool JT_XRegister::take( const QDomElement &x )
+{
+	_iq = x;
+
+	return JT_Register::take( x );
+}
+
+QDomElement JT_XRegister::iq() const
+{
+	return _iq;
+}
+
+void JT_XRegister::setXForm(const Form &frm, const XData &_form)
+{
+	JT_Register::setForm( frm );
+
+	_iq = createIQ(doc(), "set", frm.jid().full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:register");
+	_iq.appendChild(query);
+
+	XData form( _form );
+	form.setType( XData::Data_Submit );
+	query.appendChild( form.toXml( doc() ) );
+}
+
+void JT_XRegister::onGo()
+{
+	if ( !_iq.isNull() )
+		send( _iq );
+	else
+		JT_Register::onGo();
+}
+
+//----------------------------------------------------------------------------
+// RegistrationDlg
+//----------------------------------------------------------------------------
+class RegistrationDlg::Private
+{
+public:
+	Private() {}
+
+	Jid jid;
+	PsiAccount *pa;
+
+	QPushButton *pb_close, *pb_reg;
+	QGuardedPtr<JT_XRegister> jt;
+	int type;
+	BusyWidget *busy;
+	QLabel *lb_top;
+	QGrid *gr_form;
+	Form form;
+
+	QPtrList<QLabel> lb_field;
+	QPtrList<QLineEdit> le_field;
+	XDataWidget *xdata;
+};
+
+RegistrationDlg::RegistrationDlg(const Jid &jid, PsiAccount *pa)
+:QDialog(0, 0, false, WDestructiveClose)
+{
+	d = new Private;
+	d->jid = jid;
+	d->pa = pa;
+	d->pa->dialogRegister(this, d->jid);
+	d->jt = 0;
+	d->xdata = 0;
+
+	d->lb_field.setAutoDelete(true);
+	d->le_field.setAutoDelete(true);
+
+	setCaption(tr("Registration: %1").arg(d->jid.full()));
+
+	QVBoxLayout *vb1 = new QVBoxLayout(this, 4);
+	d->lb_top = new QLabel(this);
+	d->lb_top->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+	d->lb_top->hide();
+	vb1->addWidget(d->lb_top);
+
+	d->gr_form = new QGrid(2, Horizontal, this);
+	d->gr_form->setSpacing(4);
+	vb1->addWidget(d->gr_form);
+	d->gr_form->hide();
+
+	QFrame *line = new QFrame(this);
+	line->setFixedHeight(2);
+	line->setFrameStyle(QFrame::HLine | QFrame::Sunken);
+	vb1->addWidget(line);
+
+	QHBoxLayout *hb1 = new QHBoxLayout(vb1);
+	d->busy = new BusyWidget(this);
+	hb1->addWidget(d->busy);
+	hb1->addStretch(1);
+	d->pb_reg = new QPushButton(tr("&Register"), this);
+	d->pb_reg->setDefault(true);
+	connect(d->pb_reg, SIGNAL(clicked()), SLOT(doRegSet()));
+	hb1->addWidget(d->pb_reg);
+	d->pb_close = new QPushButton(tr("&Close"), this);
+	connect(d->pb_close, SIGNAL(clicked()), SLOT(close()));
+	hb1->addWidget(d->pb_close);
+
+	d->pb_reg->hide();
+
+	doRegGet();
+}
+
+RegistrationDlg::~RegistrationDlg()
+{
+	delete d->jt;
+	d->pa->dialogUnregister(this);
+	delete d;
+}
+
+/*void RegistrationDlg::closeEvent(QCloseEvent *e)
+{
+	e->ignore();
+	reject();
+}*/
+
+void RegistrationDlg::done(int r)
+{
+	if(d->busy->isActive() && d->type == 1) {
+		int n = QMessageBox::information(this, tr("Busy"), tr("<qt>Registration has already been submitted, so closing this window will not prevent the registration from happening.  Do you still wish to close?</qt>"), tr("&Yes"), tr("&No"));
+		if(n != 0)
+			return;
+	}
+	QDialog::done(r);
+}
+
+void RegistrationDlg::doRegGet()
+{
+	d->lb_top->setText(tr("Fetching registration form for %1 ...").arg(d->jid.full()));
+	d->lb_top->show();
+	d->busy->start();
+
+	d->type = 0;
+	d->jt = new JT_XRegister(d->pa->client()->rootTask());
+	connect(d->jt, SIGNAL(finished()), SLOT(jt_finished()));
+	d->jt->getForm(d->jid);
+	d->jt->go(true);
+}
+
+void RegistrationDlg::doRegSet()
+{
+	if(!d->pa->checkConnected(this))
+		return;
+
+	d->jt = new JT_XRegister(d->pa->client()->rootTask());
+
+	if ( !d->xdata ) {
+		Form submitForm = d->form;
+
+		// import the changes back into the form.
+		// the QPtrList of QLineEdits should be in the same order
+		QPtrListIterator<QLineEdit> lit(d->le_field);
+		for(Form::Iterator it = submitForm.begin(); it != submitForm.end(); ++it) {
+			FormField &f = *it;
+			QLineEdit *le = lit.current();
+			f.setValue(le->text());
+			++lit;
+		}
+
+		d->jt->setForm(submitForm);
+	}
+	else {
+		XData form;
+		form.setFields( d->xdata->fields() );
+
+		d->jt->setXForm( d->form, form );
+	}
+
+	d->gr_form->setEnabled(false);
+	d->pb_reg->setEnabled(false);
+	d->busy->start();
+
+	d->type = 1;
+	connect(d->jt, SIGNAL(finished()), SLOT(jt_finished()));
+	d->jt->go(true);
+}
+
+void RegistrationDlg::jt_finished()
+{
+	d->busy->stop();
+	d->gr_form->setEnabled(true);
+	d->pb_reg->setEnabled(true);
+	JT_XRegister *jt = d->jt;
+	d->jt = 0;
+
+	if(jt->success()) {
+		if(d->type == 0) {
+			d->form = jt->form();
+
+			bool useXData = false;
+			{
+				QDomNode n = queryTag( jt->iq() ).firstChild();
+				for( ; !n.isNull(); n = n.nextSibling()) {
+					QDomElement i = n.toElement();
+					if(i.isNull())
+						continue;
+
+					if( i.attribute( "xmlns" ) == "jabber:x:data" ) {
+						useXData = true;
+
+						XData form;
+						form.fromXml( i );
+
+						if ( !form.title().isEmpty() )
+							setCaption( form.title() );
+
+						QString str = tr("<b>Registration for \"%1\":</b><br><br>").arg(d->jid.full());
+						str += plain2rich( form.instructions() );
+						d->lb_top->setText(str);
+
+						d->xdata = new XDataWidget( d->gr_form );
+						d->xdata->setFields( form.fields() );
+
+						d->xdata->show();
+
+						break;
+					}
+				}
+			}
+
+			if ( !useXData ) {
+				QString str = tr("<b>Registration for \"%1\":</b><br><br>").arg(d->jid.full());
+				str += plain2rich(d->form.instructions());
+				d->lb_top->setText(str);
+				d->lb_top->setFixedWidth(300);
+
+				for(Form::ConstIterator it = d->form.begin(); it != d->form.end(); ++it) {
+					const FormField &f = *it;
+
+					QLabel *lb = new QLabel(f.fieldName(), d->gr_form);
+					QLineEdit *le = new QLineEdit(d->gr_form);
+					if(f.isSecret())
+						le->setEchoMode(QLineEdit::Password);
+					le->setText(f.value());
+
+					d->lb_field.append(lb);
+					d->le_field.append(le);
+				}
+
+				if (!d->le_field.isEmpty())
+					d->le_field.first()->setFocus();
+			}
+
+			d->gr_form->show();
+			d->pb_reg->show();
+			show();
+
+			qApp->processEvents();
+			resize(sizeHint());
+		}
+		else {
+			closeDialogs(this);
+			QMessageBox::information(this, tr("Success"), tr("Registration successful."));
+			close();
+		}
+	}
+	else {
+		if(d->type == 0) {
+			QMessageBox::critical(this, tr("Error"), tr("Unable to retrieve registration form.\nReason: %1").arg(jt->statusString()));
+			close();
+		}
+		else {
+			closeDialogs(this);
+			QMessageBox::critical(this, tr("Error"), tr("Error submitting registration form.\nReason: %1").arg(jt->statusString()));
+			close();
+		}
+	}
+}
+
+//----------------------------------------------------------------------------
+// JT_XSearch
+//----------------------------------------------------------------------------
+
+class JT_XSearch : public JT_Search
+{
+	Q_OBJECT
+public:
+	JT_XSearch(Task *parent);
+
+	void setForm(const Form &frm, const XData &_form);
+
+	bool take(const QDomElement &);
+	QDomElement iq() const;
+
+	void onGo();
+
+private:
+	QDomElement _iq;
+};
+
+JT_XSearch::JT_XSearch( Task *parent )
+	: JT_Search( parent )
+{
+}
+
+bool JT_XSearch::take( const QDomElement &x )
+{
+	_iq = x;
+
+	return JT_Search::take( x );
+}
+
+QDomElement JT_XSearch::iq() const
+{
+	return _iq;
+}
+
+void JT_XSearch::setForm(const Form &frm, const XData &_form)
+{
+	JT_Search::set( frm );
+
+	_iq = createIQ(doc(), "set", frm.jid().full(), id());
+	QDomElement query = doc()->createElement("query");
+	query.setAttribute("xmlns", "jabber:iq:search");
+	_iq.appendChild(query);
+
+	XData form( _form );
+	form.setType( XData::Data_Submit );
+	query.appendChild( form.toXml( doc() ) );
+}
+
+void JT_XSearch::onGo()
+{
+	if ( !_iq.isNull() )
+		send( _iq );
+	else
+		JT_Search::onGo();
+}
+
+//----------------------------------------------------------------------------
+// SearchDlg
+//----------------------------------------------------------------------------
+class SearchDlg::Private
+{
+public:
+	Private() {}
+
+	PsiAccount *pa;
+	Jid jid;
+	Form form;
+	BusyWidget *busy;
+	QGuardedPtr<JT_XSearch> jt;
+	QGrid *gr_form;
+	int type;
+
+	QPtrList<QLabel> lb_field;
+	QPtrList<QLineEdit> le_field;
+	XDataWidget *xdata;
+	XData xdata_form;
+};
+
+SearchDlg::SearchDlg(const Jid &jid, PsiAccount *pa)
+:SearchUI(0, 0, WDestructiveClose)
+{
+	d = new Private;
+	d->pa = pa;
+	d->jid = jid;
+	d->pa->dialogRegister(this, d->jid);
+	d->jt = 0;
+	d->xdata = 0;
+
+	setCaption(caption().arg(d->jid.full()));
+
+	d->busy = busy;
+
+	d->gr_form = new QGrid(2, Horizontal, gb_search);
+	d->gr_form->setSpacing(4);
+	replaceWidget(lb_form, d->gr_form);
+	d->gr_form->hide();
+
+	pb_add->setEnabled(false);
+	pb_info->setEnabled(false);
+	pb_stop->setEnabled(false);
+	pb_search->setEnabled(false);
+
+	lv_results->setMultiSelection(true);
+	lv_results->setSelectionMode( QListView::Extended );
+	connect(lv_results, SIGNAL(selectionChanged()), SLOT(selectionChanged()));
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	connect(pb_search, SIGNAL(clicked()), SLOT(doSearchSet()));
+	connect(pb_stop, SIGNAL(clicked()), SLOT(doStop()));
+	connect(pb_add, SIGNAL(clicked()), SLOT(doAdd()));
+	connect(pb_info, SIGNAL(clicked()), SLOT(doInfo()));
+
+	resize(600,440);
+
+	doSearchGet();
+}
+
+SearchDlg::~SearchDlg()
+{
+	delete d->jt;
+	d->pa->dialogUnregister(this);
+	delete d;
+}
+
+/*void SearchDlg::localUpdate(const JabRosterEntry &e)
+{
+	int oldstate = localStatus;
+	localStatus = e.localStatus();
+
+	if(localStatus == STATUS_OFFLINE) {
+		if(isBusy) {
+			busy->stop();
+			isBusy = FALSE;
+		}
+
+		pb_search->setEnabled(FALSE);
+		pb_stop->setEnabled(FALSE);
+		clear();
+	}
+	else {
+		// coming online?
+		if(oldstate == STATUS_OFFLINE) {
+			pb_search->setEnabled(TRUE);
+		}
+	}
+}*/
+
+void SearchDlg::addEntry(const QString &jid, const QString &nick, const QString &first, const QString &last, const QString &email)
+{
+	QListViewItem *lvi = new QListViewItem(lv_results);
+	lvi->setText(0, nick);
+	lvi->setText(1, first);
+	lvi->setText(2, last);
+	lvi->setText(3, email);
+	lvi->setText(4, jid);
+}
+
+void SearchDlg::doSearchGet()
+{
+	lb_instructions->setText(tr("<qt>Fetching search form for %1 ...</qt>").arg(d->jid.full()));
+	d->busy->start();
+
+	d->type = 0;
+	d->jt = new JT_XSearch(d->pa->client()->rootTask());
+	connect(d->jt, SIGNAL(finished()), SLOT(jt_finished()));
+	d->jt->get(d->jid);
+	d->jt->go(true);
+}
+
+void SearchDlg::doSearchSet()
+{
+	if(d->busy->isActive())
+		return;
+
+	if(!d->pa->checkConnected(this))
+		return;
+
+	d->jt = new JT_XSearch(d->pa->client()->rootTask());
+
+	if ( !d->xdata ) {
+		Form submitForm = d->form;
+
+		// import the changes back into the form.
+		// the QPtrList of QLineEdits should be in the same order
+		QPtrListIterator<QLineEdit> lit(d->le_field);
+		for(Form::Iterator it = submitForm.begin(); it != submitForm.end(); ++it) {
+			FormField &f = *it;
+			QLineEdit *le = lit.current();
+			f.setValue(le->text());
+			++lit;
+		}
+
+		d->jt->set(submitForm);
+	}
+	else {
+		XData form;
+		form.setFields( d->xdata->fields() );
+
+		d->jt->setForm( d->form, form );
+	}
+
+	clear();
+
+	pb_search->setEnabled(false);
+	pb_stop->setEnabled(true);
+	d->gr_form->setEnabled(false);
+	d->busy->start();
+
+	d->type = 1;
+	connect(d->jt, SIGNAL(finished()), SLOT(jt_finished()));
+	d->jt->go(true);
+}
+
+void SearchDlg::jt_finished()
+{
+	d->busy->stop();
+	JT_XSearch *jt = d->jt;
+	d->jt = 0;
+
+	if(d->type == 1) {
+		d->gr_form->setEnabled(true);
+		pb_search->setEnabled(true);
+		pb_stop->setEnabled(false);
+	}
+
+	if(jt->success()) {
+		if(d->type == 0) {
+			d->form = jt->form();
+
+			bool useXData = false;
+			{
+				QDomNode n = queryTag( jt->iq() ).firstChild();
+				for( ; !n.isNull(); n = n.nextSibling()) {
+					QDomElement i = n.toElement();
+					if(i.isNull())
+						continue;
+
+					if( i.attribute( "xmlns" ) == "jabber:x:data" ) {
+						useXData = true;
+
+						XData form;
+						form.fromXml( i );
+
+						//if ( !form.title().isEmpty() )
+						//	setCaption( form.title() );
+
+						QString str = plain2rich( form.instructions() );
+						lb_instructions->setText(str);
+
+						d->xdata = new XDataWidget( d->gr_form );
+						d->xdata->setFields( form.fields() );
+
+						d->xdata->show();
+
+						break;
+					}
+				}
+			}
+
+			if ( !useXData ) {
+				QString str = plain2rich(d->form.instructions());
+				lb_instructions->setText(str);
+
+				for(Form::ConstIterator it = d->form.begin(); it != d->form.end(); ++it) {
+					const FormField &f = *it;
+
+					QLabel *lb = new QLabel(f.fieldName(), d->gr_form);
+					QLineEdit *le = new QLineEdit(d->gr_form);
+					if(f.isSecret())
+						le->setEchoMode(QLineEdit::Password);
+					le->setText(f.value());
+
+					d->lb_field.append(lb);
+					d->le_field.append(le);
+
+					connect(le, SIGNAL(returnPressed()), this, SLOT(doSearchSet()));
+				}
+			}
+
+			d->gr_form->show();
+			pb_search->setEnabled(true);
+			show();
+
+			qApp->processEvents();
+			resize(sizeHint());
+		}
+		else {
+			if ( !d->xdata ) {
+				const QValueList<SearchResult> &list = jt->results();
+				if(list.isEmpty())
+					QMessageBox::information(this, tr("Search Results"), tr("Search returned 0 results."));
+				else {
+					for(QValueList<SearchResult>::ConstIterator it = list.begin(); it != list.end(); ++it) {
+						const SearchResult &r = *it;
+						addEntry(r.jid().full(), r.nick(), r.first(), r.last(), r.email());
+					}
+				}
+			}
+			else {
+				XData form;
+				QDomNode n = queryTag( jt->iq() ).firstChild();
+				for( ; !n.isNull(); n = n.nextSibling()) {
+					QDomElement i = n.toElement();
+					if(i.isNull())
+						continue;
+
+					if( i.attribute( "xmlns" ) == "jabber:x:data" ) {
+						form.fromXml( i );
+						break;
+					}
+				}
+
+				while ( lv_results->columns() )
+					lv_results->removeColumn( 0 );
+
+				QValueList<XData::ReportField>::ConstIterator it = form.report().begin();
+				for ( ; it != form.report().end(); ++it ) {
+					lv_results->addColumn( ( *it ).label );
+				}
+
+				QValueList<XData::ReportItem>::ConstIterator iit = form.reportItems().begin();
+				for ( ; iit != form.reportItems().end(); ++iit ) {
+					QListViewItem *lvi = new QListViewItem(lv_results);
+
+					int i = 0;
+					it = form.report().begin();
+					for ( ; it != form.report().end(); ++it ) {
+						QString name = ( *it ).name;
+						lvi->setText( i++, ( *iit )[name] );
+					}
+				}
+
+				d->xdata_form = form;
+			}
+		}
+	}
+	else {
+		if(d->type == 0) {
+			QMessageBox::critical(this, tr("Error"), tr("Unable to retrieve search form.\nReason: %1").arg(jt->statusString()));
+			close();
+		}
+		else {
+			QMessageBox::critical(this, tr("Error"), tr("Error retrieving search results.\nReason: %1").arg(jt->statusString()));
+		}
+	}
+}
+
+void SearchDlg::clear()
+{
+	lv_results->clear();
+	pb_add->setEnabled(false);
+	pb_info->setEnabled(false);
+}
+
+void SearchDlg::doStop()
+{
+	if(!d->busy->isActive())
+		return;
+
+	delete d->jt;
+	d->jt = 0;
+
+	d->busy->stop();
+	d->gr_form->setEnabled(true);
+	pb_search->setEnabled(true);
+	pb_stop->setEnabled(false);
+}
+
+void SearchDlg::selectionChanged()
+{
+	int d = 0;
+	QListViewItem *lastChild = lv_results->firstChild();
+
+	if(!lastChild) {
+		pb_add->setEnabled(false);
+		pb_info->setEnabled(false);
+		return;
+	}
+
+	if( lastChild->isSelected() ) {
+		pb_add->setEnabled(true);
+		pb_info->setEnabled(true);
+	}
+	d++;
+
+	if ( lastChild ) {
+		while ( lastChild->nextSibling() ) {
+			lastChild = lastChild->nextSibling();
+			if( lastChild->isSelected() ) {
+				pb_add->setEnabled(true);
+				pb_info->setEnabled(true);
+			}
+ 			d++;
+		}
+	}
+}
+
+void SearchDlg::doAdd()
+{
+	QListViewItem *i = lv_results->firstChild();
+	QString name;
+
+	if(!i)
+		return;
+
+	int jid;
+	int nick;
+	if ( !d->xdata ) {
+		jid  = 4;
+		nick = 0;
+	}
+	else {
+		jid = 0;
+		nick = 0;
+
+		int i = 0;
+		QValueList<XData::ReportField>::ConstIterator it = d->xdata_form.report().begin();
+		for ( ; it != d->xdata_form.report().end(); ++it, ++i ) {
+			QString name = ( *it ).name;
+			if ( name == "jid" )
+				jid = i;
+
+			if ( name == "nickname" || name == "nick" )
+				nick = i;
+		}
+	}
+
+	int d = 0;
+
+	if( i->isSelected() ) {
+		name = jidnick(i->text(jid), i->text(nick));
+		add(Jid(i->text(jid)), i->text(nick), QStringList(), true);
+		d++;
+	}
+
+	if( i ) {
+		while( i->nextSibling() ) {
+			i = i->nextSibling();
+			if( i->isSelected() ) {
+				name = jidnick(i->text(jid), i->text(nick));
+				add(Jid(i->text(jid)), i->text(nick), QStringList(), true);
+				d++;
+			}
+		}
+	}
+
+	if( d==1 )
+		QMessageBox::information(this, tr("Add User: Success"), tr("Added %1 to your roster.").arg(name));
+	else
+		QMessageBox::information(this, tr("Add User: Success"), tr("Added %1 users to your roster.").arg(d));
+}
+
+void SearchDlg::doInfo()
+{
+	QListViewItem *i = lv_results->firstChild();
+	QString name;
+
+	if(!i)
+		return;
+
+	int jid;
+	if ( !d->xdata ) {
+		jid  = 4;
+	}
+	else {
+		jid = 0;
+
+		int i = 0;
+		QValueList<XData::ReportField>::ConstIterator it = d->xdata_form.report().begin();
+		for ( ; it != d->xdata_form.report().end(); ++it, ++i ) {
+			QString name = ( *it ).name;
+			if ( name == "jid" )
+				jid = i;
+		}
+	}
+
+	if( i->isSelected() ) {
+		aInfo(Jid(i->text(jid)));
+	}
+
+	if( i ) {
+		while( i->nextSibling() ) {
+			i = i->nextSibling();
+			if( i->isSelected() ) {
+				aInfo(Jid(i->text(jid)));
+			}
+		}
+	}
+}
+
+#include "servicesdlg.moc"
Index: psi/tags/0.10-os2-b1/src/servicesdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/servicesdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/servicesdlg.h	(revision 38)
@@ -0,0 +1,84 @@
+/*
+ * servicesdlg.h - a dialog for browsing Jabber services
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef SERVICESDLG_H
+#define SERVICESDLG_H
+
+#include"ui_search.h"
+#include"xmpp.h"
+
+#include<qdialog.h>
+
+using namespace XMPP;
+
+class PsiAccount;
+
+class RegistrationDlg : public QDialog
+{
+	Q_OBJECT
+public:
+	RegistrationDlg(const Jid &, PsiAccount *);
+	~RegistrationDlg();
+
+//protected:
+//	void closeEvent(QCloseEvent *);
+
+public slots:
+	void done(int);
+
+private slots:
+	void doRegGet();
+	void doRegSet();
+	void jt_finished();
+
+private:
+	class Private;
+	Private *d;
+};
+
+class SearchDlg : public SearchUI
+{
+	Q_OBJECT
+public:
+	SearchDlg(const Jid &, PsiAccount *);
+	~SearchDlg();
+
+signals:
+	void aInfo(const Jid &);
+	void add(const Jid &, const QString &, const QStringList &, bool authReq);
+
+private slots:
+	void doSearchGet();
+	void doSearchSet();
+	void selectionChanged();
+	void jt_finished();
+	void doStop();
+	void doAdd();
+	void doInfo();
+
+private:
+	class Private;
+	Private *d;
+
+	void addEntry(const QString &jid, const QString &nick, const QString &first, const QString &last, const QString &email);
+	void clear();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/showtextdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/showtextdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/showtextdlg.cpp	(revision 38)
@@ -0,0 +1,59 @@
+/*
+ * showtextdlg.cpp - dialog for displaying a text file
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"showtextdlg.h"
+
+#include<qlayout.h>
+#include<qtextedit.h>
+#include<qpushbutton.h>
+#include<qfile.h>
+#include<qtextstream.h>
+
+
+ShowTextDlg::ShowTextDlg(const QString &fname, bool rich, QWidget *parent, const char *name)
+:QDialog(parent, name, FALSE, WDestructiveClose)
+{
+	QString text;
+
+	QFile f(fname);
+	if(f.open(IO_ReadOnly)) {
+		QTextStream t(&f);
+		while(!t.eof())
+			text += t.readLine() + '\n';
+		f.close();
+	}
+
+	QVBoxLayout *vb1 = new QVBoxLayout(this, 8);
+	QTextEdit *te = new QTextEdit(this);
+	te->setReadOnly(TRUE);
+	te->setTextFormat(rich ? QTextEdit::RichText : QTextEdit::PlainText);
+	te->setText(text);
+
+	vb1->addWidget(te);
+
+	QHBoxLayout *hb1 = new QHBoxLayout(vb1);
+	hb1->addStretch(1);
+	QPushButton *pb = new QPushButton(tr("&OK"), this);
+	connect(pb, SIGNAL(clicked()), SLOT(accept()));
+	hb1->addWidget(pb);
+	hb1->addStretch(1);
+
+	resize(560, 384);
+}
Index: psi/tags/0.10-os2-b1/src/showtextdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/showtextdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/showtextdlg.h	(revision 38)
@@ -0,0 +1,33 @@
+/*
+ * showtextdlg.h - dialog for displaying a text file
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_SHOWTEXTDLG_H
+#define CS_SHOWTEXTDLG_H
+
+#include<qdialog.h>
+
+class ShowTextDlg : public QDialog
+{
+	Q_OBJECT
+public:
+        ShowTextDlg(const QString &fname, bool rich=FALSE, QWidget *parent=0, const char *name=0);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/src.pro
===================================================================
--- psi/tags/0.10-os2-b1/src/src.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/src/src.pro	(revision 38)
@@ -0,0 +1,302 @@
+#
+# Psi qmake profile
+#
+
+# Configuration
+TEMPLATE = app
+CONFIG  += qt thread x11
+
+#CONFIG += use_crash
+#DEFINES += GPG_DEBUG
+
+os2:DEFINES += MAP_NDNS_TO_QDNS
+
+# Executable name
+TARGET    = psi
+
+os2:DESTDIR  = ../psios2
+
+debug: {
+	MOC_DIR        = debug/.moc
+	OBJECTS_DIR    = debug/.obj
+	UI_DIR         = debug/.ui
+} else {
+	MOC_DIR        = .moc
+	OBJECTS_DIR    = .obj
+	UI_DIR         = .ui
+}
+
+# qconf
+exists(../conf.pri) {
+	include(../conf.pri)
+
+	# Shared files
+	sharedfiles.path  = $$DATADIR
+	sharedfiles.files = ../README ../COPYING ../iconsets ../sound ../certs
+	INSTALLS += sharedfiles
+
+	# Widgets
+	#widgets.path = $$DATADIR/designer
+	#widgets.files = ../libpsi/psiwidgets/libpsiwidgets.so
+	#INSTALLS += widgets
+
+	# icons and desktop files
+	dt.path=$$PREFIX/share/applications/
+	dt.files = ../psi.desktop 
+	icon1.path=$$PREFIX/share/icons/hicolor/16x16/apps
+	icon1.extra = cp -f ../iconsets/system/default/icon_16.png $(INSTALL_ROOT)$$icon1.path/psi.png
+	icon2.path=$$PREFIX/share/icons/hicolor/32x32/apps
+	icon2.extra = cp -f ../iconsets/system/default/icon_32.png $(INSTALL_ROOT)$$icon2.path/psi.png
+	icon3.path=$$PREFIX/share/icons/hicolor/48x48/apps
+	icon3.extra = cp -f ../iconsets/system/default/icon_48.png $(INSTALL_ROOT)$$icon3.path/psi.png
+	INSTALLS += dt icon1 icon2 icon3
+}
+
+win32: {
+	include(../conf_win32.pri)
+}
+os2: {
+	include(../conf_os2.pri)
+}
+
+# qxml
+!isEmpty(QXML_STATIC) {
+	unix: {
+		INCLUDEPATH += tools/qxml
+		HEADERS += tools/qxml/qxml.h tools/qxml/qdom.h
+		SOURCES += tools/qxml/qxml.cpp tools/qxml/qdom.cpp
+	}
+}
+
+# IPv6 ?
+#DEFINES += NO_NDNS
+
+# include some of cutestuff
+CONFIG += psics
+IRISCS_CPP = ../../cutestuff
+exists(../cutestuff) {
+	IRISCS_CPP = ../cutestuff
+}
+PSICS_CPP = tools
+include(../psics.pri)
+
+# qca
+#DEFINES += QCA_STATIC
+#QCA_PREFIX = ../qca
+#!exists(../qca) {
+#	QCA_PREFIX = ../../qca/src
+#}
+#INCLUDEPATH += $$QCA_PREFIX
+#HEADERS += $$QCA_PREFIX/qca.h $$QCA_PREFIX/qcaprovider.h
+#SOURCES += $$QCA_PREFIX/qca.cpp
+
+# libxmpp
+#CONFIG += xmpp
+#XMPP_BASE = ../../xmpp
+#exists(../xmpp) {
+#	XMPP_BASE = ../xmpp
+#}
+#include($$XMPP_BASE/xmpp.pri)
+
+# include Iris XMPP library
+#CONFIG += iris
+IRIS_BASE = ../../iris
+exists(../iris) {
+	IRIS_BASE = ../iris
+}
+include(../iris.pri)
+
+# include Psi library
+CONFIG += libpsi
+LIBPSI_CPP = ../../libpsi
+exists(../libpsi) {
+	LIBPSI_CPP = ../libpsi
+}
+include(../libpsi.pri)
+
+# Psi sources
+PSI_CPP = .
+
+# Header files
+HEADERS += \
+	$$PSI_CPP/varlist.h \
+	$$PSI_CPP/showtextdlg.h \
+	$$PSI_CPP/profiles.h \
+	$$PSI_CPP/profiledlg.h \
+	$$PSI_CPP/main.h \
+	$$PSI_CPP/psiaccount.h \
+	$$PSI_CPP/psicon.h \
+	$$PSI_CPP/psievent.h \
+	$$PSI_CPP/xmlconsole.h \
+	$$PSI_CPP/contactview.h \
+	$$PSI_CPP/psiiconset.h \
+	$$PSI_CPP/userlist.h \
+	$$PSI_CPP/mainwin.h \
+	$$PSI_CPP/mainwin_p.h \
+	$$PSI_CPP/rtparse.h \
+	$$PSI_CPP/common.h \
+	$$PSI_CPP/proxy.h \
+	$$PSI_CPP/accountdlg.h \
+	$$PSI_CPP/changepwdlg.h \
+	$$PSI_CPP/msgmle.h \
+	$$PSI_CPP/statusdlg.h \
+	$$PSI_CPP/eventdlg.h \
+	$$PSI_CPP/chatdlg.h \
+	$$PSI_CPP/tabdlg.h \
+	$$PSI_CPP/adduserdlg.h \
+	$$PSI_CPP/groupchatdlg.h \
+	$$PSI_CPP/infodlg.h \
+	$$PSI_CPP/eventdb.h \
+	$$PSI_CPP/historydlg.h \
+	$$PSI_CPP/servicesdlg.h \
+	$$PSI_CPP/jltest.h \
+#	$$PSI_CPP/browserdlg.h \
+	$$PSI_CPP/psitoolbar.h \
+	$$PSI_CPP/passphrasedlg.h \
+	$$PSI_CPP/vcardfactory.h \
+	$$PSI_CPP/sslcertdlg.h \
+	$$PSI_CPP/qwextend.h \
+	$$PSI_CPP/tasklist.h \
+	$$PSI_CPP/discodlg.h \
+	$$PSI_CPP/alerticon.h \
+	$$PSI_CPP/psipopup.h \
+	$$PSI_CPP/psiapplication.h \
+	$$PSI_CPP/filetransdlg.h \
+	$$PSI_CPP/avatars.h \
+	$$PSI_CPP/actionlist.h \
+	$$PSI_CPP/psiactionlist.h \
+	$$PSI_CPP/xdata_widget.h \
+	$$PSI_CPP/xmpp_xdata.h
+
+# Source files
+SOURCES += \
+	$$PSI_CPP/varlist.cpp \
+	$$PSI_CPP/showtextdlg.cpp \
+	$$PSI_CPP/psi_profiles.cpp \
+	$$PSI_CPP/profiledlg.cpp \
+	$$PSI_CPP/main.cpp \
+	$$PSI_CPP/psiaccount.cpp \
+	$$PSI_CPP/psicon.cpp \
+	$$PSI_CPP/psievent.cpp \
+	$$PSI_CPP/xmlconsole.cpp \
+	$$PSI_CPP/contactview.cpp \
+	$$PSI_CPP/psiiconset.cpp \
+	$$PSI_CPP/userlist.cpp \
+	$$PSI_CPP/mainwin.cpp \
+	$$PSI_CPP/mainwin_p.cpp \
+	$$PSI_CPP/rtparse.cpp \
+	$$PSI_CPP/common.cpp \
+	$$PSI_CPP/proxy.cpp \
+	$$PSI_CPP/accountdlg.cpp \
+	$$PSI_CPP/changepwdlg.cpp \
+	$$PSI_CPP/msgmle.cpp \
+	$$PSI_CPP/statusdlg.cpp \
+	$$PSI_CPP/eventdlg.cpp \
+	$$PSI_CPP/chatdlg.cpp \
+	$$PSI_CPP/tabdlg.cpp \
+	$$PSI_CPP/adduserdlg.cpp \
+	$$PSI_CPP/groupchatdlg.cpp \
+	$$PSI_CPP/infodlg.cpp \
+	$$PSI_CPP/eventdb.cpp \
+	$$PSI_CPP/historydlg.cpp \
+	$$PSI_CPP/servicesdlg.cpp \
+	$$PSI_CPP/jltest.cpp \
+#	$$PSI_CPP/browserdlg.cpp \
+	$$PSI_CPP/psitoolbar.cpp \
+	$$PSI_CPP/passphrasedlg.cpp \
+	$$PSI_CPP/vcardfactory.cpp \
+	$$PSI_CPP/sslcertdlg.cpp \
+	$$PSI_CPP/qwextend.cpp \
+	$$PSI_CPP/discodlg.cpp \
+	$$PSI_CPP/alerticon.cpp \
+	$$PSI_CPP/psipopup.cpp \
+	$$PSI_CPP/psiapplication.cpp \
+	$$PSI_CPP/filetransdlg.cpp \
+	$$PSI_CPP/avatars.cpp \
+	$$PSI_CPP/actionlist.cpp \
+	$$PSI_CPP/psiactionlist.cpp \
+	$$PSI_CPP/xdata_widget.cpp \
+	$$PSI_CPP/xmpp_xdata.cpp
+mac {
+	contains( DEFINES, HAVE_GROWL ) {
+		HEADERS += $$PSI_CPP/psigrowlnotifier.h 
+		SOURCES += $$PSI_CPP/psigrowlnotifier.cpp 
+	}
+}
+
+# Qt Designer interfaces
+INTERFACES = \
+	$$PSI_CPP/ui_profileopen.ui \
+	$$PSI_CPP/ui_profilemanage.ui \
+	$$PSI_CPP/ui_profilenew.ui \
+	$$PSI_CPP/ui_proxy.ui \
+	$$PSI_CPP/ui_pgpkey.ui \
+	$$PSI_CPP/ui_accountmanage.ui \
+	$$PSI_CPP/ui_accountadd.ui \
+	$$PSI_CPP/ui_accountreg.ui \
+	$$PSI_CPP/ui_accountremove.ui \
+	$$PSI_CPP/ui_accountmodify.ui \
+	$$PSI_CPP/ui_changepw.ui \
+	$$PSI_CPP/ui_addurl.ui \
+	$$PSI_CPP/ui_adduser.ui \
+	$$PSI_CPP/ui_gcjoin.ui \
+	$$PSI_CPP/ui_info.ui \
+	$$PSI_CPP/ui_search.ui \
+	$$PSI_CPP/ui_about.ui \
+#	$$PSI_CPP/ui_browser.ui \
+	$$PSI_CPP/ui_passphrase.ui \
+	$$PSI_CPP/ui_sslcert.ui \
+	$$PSI_CPP/ui_disco.ui \
+	$$PSI_CPP/ui_tip.ui \
+	$$PSI_CPP/ui_filetrans.ui
+
+# options dialog
+include(options/options.pri)
+
+LANG_PATH = ../lang
+
+# Translations
+TRANSLATIONS = \
+	$$LANG_PATH/psi_ar.ts \
+	$$LANG_PATH/psi_ca.ts \
+	$$LANG_PATH/psi_cs.ts \
+	$$LANG_PATH/psi_da.ts \
+	$$LANG_PATH/psi_de.ts \
+	$$LANG_PATH/psi_el.ts \
+	$$LANG_PATH/psi_eo.ts \
+	$$LANG_PATH/psi_es.ts \
+	$$LANG_PATH/psi_fi.ts \
+	$$LANG_PATH/psi_fr.ts \
+	$$LANG_PATH/psi_it.ts \
+	$$LANG_PATH/psi_jp.ts \
+	$$LANG_PATH/psi_mk.ts \
+	$$LANG_PATH/psi_nl.ts \
+	$$LANG_PATH/psi_pl.ts \
+	$$LANG_PATH/psi_pt.ts \
+	$$LANG_PATH/psi_ptbr.ts \
+	$$LANG_PATH/psi_ru.ts \
+	$$LANG_PATH/psi_se.ts \
+	$$LANG_PATH/psi_sk.ts \
+	$$LANG_PATH/psi_sr.ts \
+	$$LANG_PATH/psi_zh.ts
+
+# Platform specifics
+unix:!mac {
+	QMAKE_POST_LINK = rm -f ../psi ; ln -s src/psi ../psi
+}
+win32: {
+	RC_FILE = ../win32/psi_win32.rc
+
+	# buggy MSVC workaround
+	contains(MAKEFILE_GENERATOR, MSVC):     QMAKE_LFLAGS += /FORCE:MULTIPLE
+	contains(MAKEFILE_GENERATOR, MSVC.NET): QMAKE_LFLAGS += /FORCE:MULTIPLE
+}
+os2: {
+	RC_FILE = ../os2/psi_os2.rc
+}
+mac: {
+	QMAKE_LFLAGS += -framework Carbon -framework IOKit
+	QMAKE_INFO_PLIST = ../mac/Info.plist
+	RC_FILE = ../mac/application.icns
+	QMAKE_POST_LINK = cp -R ../COPYING ../README ../certs ../iconsets ../sound $(DESTDIR)../Resources ; echo "APPLpsi " > $(DESTDIR)../PkgInfo
+}
Index: psi/tags/0.10-os2-b1/src/sslcertdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/sslcertdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/sslcertdlg.cpp	(revision 38)
@@ -0,0 +1,106 @@
+#include"sslcertdlg.h"
+
+#include<qtextbrowser.h>
+#include<qdatetime.h>
+#include<qlabel.h>
+#include<qpushbutton.h>
+#include<qdict.h>
+#include<qca.h>
+#include"common.h"
+#include"iconwidget.h"
+
+static void setLabelStatus(QLabel *l, bool ok)
+{
+	if(ok)
+		l->setPaletteForegroundColor(QColor("#2A993B"));
+	else
+		l->setPaletteForegroundColor(QColor("#810000"));
+}
+
+static QString getPropValue(const QCA::CertProperties &list, const QString &var)
+{
+	QString val;
+	for(QCA::CertProperties::ConstIterator it = list.begin(); it != list.end(); ++it) {
+		if(it.key() == var) {
+			if(!val.isEmpty())
+				val += "<br>";
+			val += it.data();
+		}
+	}
+	return val;
+}
+
+static QString makePropEntry(const QString &var, const QString &name, const QCA::CertProperties &list)
+{
+	QString val = getPropValue(list, var);
+	if(val.isEmpty())
+		return "";
+	else
+		return QString("<tr><td><nobr><b>") + name + "</b></nobr></td><td>" + val + "</td></tr>";
+}
+
+QString SSLCertDlg::makePropTable(const QString &heading, const QCA::CertProperties &list)
+{
+	QString str;
+	str += "<tr><td><i>" + heading + "</i><br>";
+	str += "<table>";
+	str += makePropEntry("O", tr("Organization:"), list);
+	str += makePropEntry("OU", tr("Organizational unit:"), list);
+	str += makePropEntry("L", tr("Locality:"), list);
+	str += makePropEntry("ST", tr("State:"), list);
+	str += makePropEntry("C", tr("Country:"), list);
+	str += makePropEntry("CN", tr("Common name:"), list);
+	str += makePropEntry("Email", tr("Email:"), list);
+	str += "</table></td></tr>";
+	return str;
+}
+
+SSLCertDlg::SSLCertDlg(QWidget *parent, const char *name)
+:SSLCertUI(parent, name, true)
+{
+	setCaption(CAP(caption()));
+
+	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
+	pb_close->setDefault(true);
+	pb_close->setFocus();
+}
+
+void SSLCertDlg::setCert(const QCA::Cert &cert, int result)
+{
+	if(cert.isNull())
+		return;
+
+	if(result == QCA::TLS::Valid) {
+		lb_valid->setText(tr("The certificate is valid."));
+		setLabelStatus(lb_valid, true);
+	}
+	else {
+		lb_valid->setText(tr("The certificate is NOT valid!"));
+		setLabelStatus(lb_valid, false);
+	}
+
+	QDateTime now = QDateTime::currentDateTime();
+	QDateTime notBefore = cert.notBefore();
+	QDateTime notAfter = cert.notAfter();
+	lb_notBefore->setText(cert.notBefore().toString());
+	setLabelStatus(lb_notBefore, now > notBefore);
+	lb_notAfter->setText(cert.notAfter().toString());
+	setLabelStatus(lb_notAfter, now < notAfter);
+
+	lb_sn->setText(cert.serialNumber());
+
+	QString str;
+	str += "<table>";
+	str += makePropTable(tr("Subject Details:"), cert.subject());
+	str += makePropTable(tr("Issuer Details:"), cert.issuer());
+	str += "</table>";
+	tb_cert->setText(str);
+}
+
+void SSLCertDlg::showCert(const QCA::Cert &cert, int result)
+{
+	SSLCertDlg *w = new SSLCertDlg(0);
+	w->setCert(cert, result);
+	w->exec();
+	delete w;
+}
Index: psi/tags/0.10-os2-b1/src/sslcertdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/sslcertdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/sslcertdlg.h	(revision 38)
@@ -0,0 +1,22 @@
+#ifndef SSLCERTDLG_H
+#define SSLCERTDLG_H
+
+#include"ui_sslcert.h"
+#include<qvaluelist.h>
+#include<qca.h>
+
+class SSLCertDlg : public SSLCertUI
+{
+	Q_OBJECT
+public:
+	SSLCertDlg(QWidget *parent=0, const char *name=0);
+
+	void setCert(const QCA::Cert &, int result);
+
+	static void showCert(const QCA::Cert &, int result);
+
+private:
+	QString makePropTable(const QString &heading, const QCA::CertProperties &props);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/statusdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/statusdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/statusdlg.cpp	(revision 38)
@@ -0,0 +1,246 @@
+/*
+ * statusdlg.cpp - dialogs for setting and reading status messages
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"statusdlg.h"
+
+#include<qpushbutton.h>
+#include<qlayout.h>
+#include<qlabel.h>
+#include<qcombobox.h>
+#include<qinputdialog.h>
+#include<qcheckbox.h>
+#include"psicon.h"
+#include"psiaccount.h"
+#include"userlist.h"
+#include"common.h"
+#include"msgmle.h"
+
+
+//----------------------------------------------------------------------------
+// StatusShowDlg
+// FIXME: Will no longer be needed once it is out of the groupchat contactview
+//----------------------------------------------------------------------------
+StatusShowDlg::StatusShowDlg(const UserListItem &u)
+:QDialog(0, 0, false, WDestructiveClose)
+{
+	// build the dialog
+	QVBoxLayout *vb = new QVBoxLayout(this, 8);
+	PsiTextView *te = new PsiTextView(this);
+	vb->addWidget(te);
+	QHBoxLayout *hb = new QHBoxLayout(vb);
+	QPushButton *pb = new QPushButton(tr("&Close"), this);
+	connect(pb, SIGNAL(clicked()), SLOT(close()));
+	hb->addStretch(1);
+	hb->addWidget(pb);
+	hb->addStretch(1);
+
+	// set the rest up
+	te->setReadOnly(true);
+	te->setTextFormat(RichText);
+	te->setText(u.makeDesc());
+
+	setCaption(tr("Status for %1").arg(jidnick(u.jid().full(), u.name())));
+	resize(400,240);
+
+	pb->setFocus();
+}
+
+
+//----------------------------------------------------------------------------
+// StatusSetDlg
+//----------------------------------------------------------------------------
+static int combomap[7] = { STATUS_CHAT, STATUS_ONLINE, STATUS_AWAY, STATUS_XA, STATUS_DND, STATUS_INVISIBLE, STATUS_OFFLINE };
+
+class StatusSetDlg::Private
+{
+public:
+	Private() {}
+
+	PsiCon *psi;
+	PsiAccount *pa;
+	Status s;
+	ChatView *te;
+	QComboBox *cb_type, *cb_preset;
+	QCheckBox *save;
+};
+
+StatusSetDlg::StatusSetDlg(PsiCon *psi, const Status &s)
+:QDialog(0, 0, false, WDestructiveClose)
+{
+	d = new Private;
+	d->psi = psi;
+	d->pa = 0;
+	d->psi->dialogRegister(this);
+	d->s = s;
+
+	setCaption(CAP(tr("Set Status: All accounts")));
+	init();
+}
+
+StatusSetDlg::StatusSetDlg(PsiAccount *pa, const Status &s)
+:QDialog(0, 0, false, WDestructiveClose)
+{
+	d = new Private;
+	d->psi = 0;
+	d->pa = pa;
+	d->pa->dialogRegister(this);
+	d->s = s;
+
+	setCaption(CAP(tr("Set Status: %1").arg(d->pa->name())));
+	init();
+}
+
+void StatusSetDlg::init()
+{
+	int type = makeSTATUS(d->s);
+
+	// build the dialog
+	QVBoxLayout *vb = new QVBoxLayout(this, 8);
+	QHBoxLayout *hb1 = new QHBoxLayout(vb);
+
+	QLabel *l;
+	l = new QLabel(tr("Status:"), this);
+	hb1->addWidget(l);
+	d->cb_type = new QComboBox(this);
+	int n;
+	for(n = 0; n < 7; ++n)
+		d->cb_type->insertItem(status2txt(combomap[n]));
+	for(n = 0; n < 7; ++n) {
+		if(type == combomap[n]) {
+			d->cb_type->setCurrentItem(n);
+			break;
+		}
+	}
+	hb1->addWidget(d->cb_type,1);
+	l = new QLabel(tr("Preset:"), this);
+	hb1->addWidget(l);
+	d->cb_preset = new QComboBox(this);
+	d->cb_preset->insertItem(tr("<None>"));
+	QStringList presets=option.sp.varsToStringList();
+	presets.sort();
+	d->cb_preset->insertStringList(presets);
+	connect(d->cb_preset, SIGNAL(highlighted(int)), SLOT(chooseStatusPreset(int)));
+	hb1->addWidget(d->cb_preset,1);
+
+	d->te = new ChatView(this);
+	d->te->setReadOnly(false);
+	d->te->setTextFormat(PlainText);
+	d->te->setMinimumHeight(50);
+	vb->addWidget(d->te);
+	QHBoxLayout *hb = new QHBoxLayout(vb);
+	QPushButton *pb1 = new QPushButton(tr("&Set"), this);
+	QPushButton *pb2 = new QPushButton(tr("&Cancel"), this);
+	d->save = new QCheckBox(this);
+	d->save->setText(tr("Sa&ve as Preset"));
+	d->save->setChecked(false);
+	hb->addWidget(pb1);
+	hb->addStretch(1);
+	hb->addWidget(d->save);
+	hb->addStretch(1);
+	hb->addWidget(pb2);
+
+	// set the rest up
+	d->te->setTextFormat(PlainText);
+	d->te->setText(d->s.status());
+	d->te->selectAll();
+	connect(pb1, SIGNAL(clicked()), SLOT(doButton()));
+	connect(pb2, SIGNAL(clicked()), SLOT(cancel()));
+	d->te->setFocus();
+
+	resize(400,240);
+}
+
+StatusSetDlg::~StatusSetDlg()
+{
+	if(d->psi)
+		d->psi->dialogUnregister(this);
+	else if(d->pa)
+		d->pa->dialogUnregister(this);
+	delete d;
+}
+
+void StatusSetDlg::keyPressEvent(QKeyEvent *e)
+{
+	if(e->key() == Key_Escape)
+		close();
+	else if(e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) )
+		doButton();
+	else
+		e->ignore();
+}
+
+void StatusSetDlg::doButton()
+{
+	// Save preset
+	if (d->save->isChecked()) {
+		QString text;
+		while(1) {
+			// Get preset
+			bool ok = FALSE;
+			text = QInputDialog::getText(
+				CAP(tr("New Status Preset")),
+					tr("Please enter a name for the new status preset:"),
+					QLineEdit::Normal, text, &ok, this);
+			if (!ok)
+				return;
+			
+			// Check preset name
+			if (text.isEmpty()) {
+				QMessageBox::information(this, tr("Error"), 
+					tr("Can't create a blank preset!"));
+			}
+			else if(option.sp.findByKey(text) != option.sp.end()) {
+				QMessageBox::information(this, tr("Error"), 
+					tr("You already have a preset with that name!"));
+			}
+			else 
+				break;
+		}
+		// Store preset
+		option.sp.set(text,d->te->text());
+	} 
+
+	// Set status
+	int type = combomap[d->cb_type->currentItem()];
+	QString str = d->te->text();
+
+	set(makeStatus(type, str));
+	close();
+}
+
+void StatusSetDlg::chooseStatusPreset(int x)
+{
+	if(x < 1)
+		return;
+
+	d->te->setText(option.sp.get(d->cb_preset->text(x)));
+}
+
+void StatusSetDlg::cancel()
+{
+	emit cancelled();
+	close();
+}
+
+void StatusSetDlg::reject()
+{
+	cancel();
+	QDialog::reject();
+}
Index: psi/tags/0.10-os2-b1/src/statusdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/statusdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/statusdlg.h	(revision 38)
@@ -0,0 +1,68 @@
+/*
+ * statusdlg.h - dialogs for setting and reading status messages
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef STATUSDLG_H
+#define STATUSDLG_H
+
+#include<qdialog.h>
+#include"im.h"
+
+using namespace XMPP;
+
+class PsiCon;
+class PsiAccount;
+class UserListItem;
+
+class StatusShowDlg : public QDialog
+{
+	Q_OBJECT
+public:
+	StatusShowDlg(const UserListItem &);
+};
+
+class StatusSetDlg : public QDialog
+{
+	Q_OBJECT
+public:
+	StatusSetDlg(PsiCon *, const Status &);
+	StatusSetDlg(PsiAccount *, const Status &);
+	~StatusSetDlg();
+
+protected:
+	void keyPressEvent(QKeyEvent *);
+
+signals:
+	void set(const Status &);
+	void cancelled();
+
+private slots:
+	void doButton();
+	void cancel();
+	void reject();
+	void chooseStatusPreset(int);
+
+private:
+	class Private;
+	Private *d;
+
+	void init();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tabdlg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tabdlg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tabdlg.cpp	(revision 38)
@@ -0,0 +1,424 @@
+/*
+ * tabdlg.cpp - dialog for handling tabbed chats
+ * Copyright (C) 2005  Kevin Smith
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "tabdlg.h"
+#include "chatdlg.h"
+#include "iconwidget.h"
+#include "iconset.h"
+#include "common.h"
+#include "psicon.h"
+#include <qmenubar.h>
+#include <qcursor.h>
+#include <qdragobject.h>
+#include "psitabwidget.h"
+
+#ifdef Q_WS_WIN
+#include<windows.h>
+#endif
+
+//----------------------------------------------------------------------------
+// TabDlg
+//----------------------------------------------------------------------------
+TabDlg::TabDlg(PsiCon *psiCon)
+{
+	psi=psiCon;
+
+	//this->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
+
+	tabMenu = new QPopupMenu( this );
+	connect( tabMenu, SIGNAL( aboutToShow() ), SLOT( buildTabMenu() ) );
+	
+	tabs = new KTabWidget (this);
+
+	closeCross = new QPushButton(this);
+	//closeCross->setText("x");
+	closeCross->setIconSet(IconsetFactory::icon("psi/closetab").iconSet());
+	closeCross->hide();
+	if (option.usePerTabCloseButton)
+	  tabs->setHoverCloseButton(true);
+	else
+	{
+		tabs->setHoverCloseButton(false);
+		tabs->setCornerWidget( closeCross );
+		closeCross->show();
+	}
+	
+	tabs->setHoverCloseButtonDelayed(false); //people may want this enabled, but it's currently horribly broken.
+	tabs->setTabReorderingEnabled(true);
+	tabs->setCloseIcon(IconsetFactory::icon("psi/closetab").iconSet());
+	connect (tabs, SIGNAL( mouseDoubleClick( QWidget* ) ), SLOT( detachChat( QWidget* ) ) ); 
+	connect (tabs, SIGNAL( testCanDecode(const QDragMoveEvent*, bool&) ), SLOT( tabTestCanDecode(const QDragMoveEvent*, bool&) ) );
+	connect (tabs, SIGNAL( receivedDropEvent( QDropEvent* ) ), SLOT( tabReceivedDropEvent( QDropEvent* ) ) );
+	connect (tabs, SIGNAL( receivedDropEvent( QWidget*, QDropEvent* ) ), SLOT( tabReceivedDropEvent( QWidget*, QDropEvent* ) ) );
+	connect (tabs, SIGNAL( initiateDrag( QWidget* ) ), SLOT( startDrag( QWidget* ) ) );
+	connect (tabs, SIGNAL( closeRequest( QWidget* ) ), SLOT( closeChat( QWidget* ) ) );
+	
+		
+	QVBoxLayout *vert1 = new QVBoxLayout( this, 1);
+	vert1->addWidget(tabs);
+	chats.setAutoDelete( FALSE );
+	X11WM_CLASS("chat");
+	
+	connect( closeCross, SIGNAL( clicked() ), SLOT( closeChat() ) );
+	connect( tabs, SIGNAL( currentChanged( QWidget* ) ), SLOT( tabSelected( QWidget* ) ) );	
+
+	setAcceptDrops(TRUE);
+
+
+
+	
+	setLooks();
+
+	resize(option.sizeTabDlg);
+}
+
+TabDlg::~TabDlg()
+{
+
+}
+
+void TabDlg::resizeEvent(QResizeEvent *e)
+{
+  if(option.keepSizes)
+	option.sizeTabDlg = e->size();
+}
+
+void TabDlg::buildTabMenu()
+{
+	tabMenu->clear();
+	tabMenu->insertItem( tr("Detach Current Tab"), this, SLOT( detachChat() ) );
+	tabMenu->insertItem( tr("Close Current Tab"), this, SLOT( closeChat() ) );
+
+	QPopupMenu* sendTo = new QPopupMenu(tabMenu);
+	for (uint i = 0; i < psi->getTabSets()->count(); ++i)
+	{
+		TabDlg* tabSet= psi->getTabSets()->at(i);
+		sendTo->insertItem( tabSet->getName(), this, SLOT( sendChatTo( tabSet ) ) );
+	}
+	tabMenu->insertItem( tr("Sent Current Tab to"), sendTo);
+}
+
+void TabDlg::sendChatTo(QWidget* chatw, TabDlg* otherTabs)
+{
+	if (otherTabs==this)
+		return;
+	ChatDlg* chat = (ChatDlg*)chatw;
+	closeChat(chat, false);
+	otherTabs->addChat(chat);
+}
+
+void TabDlg::setLooks()
+{
+	//set the widget icon
+#ifndef Q_WS_MAC
+	setIcon(IconsetFactory::icon("psi/start-chat"));
+#endif
+	tabs->setTabPosition(QTabWidget::Top);
+	if (option.putTabsAtBottom)
+		tabs->setTabPosition(QTabWidget::Bottom);
+
+#if QT_VERSION >= 0x030300
+	setWindowOpacity(double(option.chatOpacity)/100);
+#endif
+}
+
+QString TabDlg::getName()
+{
+	return ((ChatDlg*)(tabs->currentPage()))->getDisplayNick();
+}
+
+void TabDlg::tabSelected(QWidget* chat)
+{
+	((ChatDlg*)chat)->activated(); //is this still necessary?
+	updateCaption();
+}
+
+bool TabDlg::managesChat(ChatDlg* chat)
+{
+	if ( chats.contains(chat) )
+			return true;
+	return false;
+}
+
+bool TabDlg::chatOnTop(ChatDlg* chat)
+{
+	if ( tabs->currentPage() == chat )
+		return true;
+	return false;
+}
+
+void TabDlg::addChat(ChatDlg* chat)
+{
+	chats.append(chat);
+	tabs->addTab(chat, chat->getDisplayNick());
+	tabs->setTabIconSet(chat, IconsetFactory::icon("psi/start-chat"));
+
+	tabs->showPage(chat);
+	connect ( chat, SIGNAL( captionChanged( ChatDlg*) ), SLOT( updateTab( ChatDlg* ) ) );
+	connect ( chat, SIGNAL( contactIsComposing(ChatDlg*, bool) ), SLOT( setTabComposing( ChatDlg*, bool) ) );
+	connect ( chat, SIGNAL( unreadMessageUpdate(ChatDlg*, int) ), SLOT( setTabHasMessages(ChatDlg*, int) ) );
+	
+	this->show();
+	updateCaption();
+}
+
+void TabDlg::detachChat()
+{
+	detachChat(tabs->currentPage());
+}
+
+void TabDlg::detachChat(QWidget* chat)
+{
+	//don't detach singleton chats, fix for flyspray #477
+	if (tabs->count()==1)
+		return;
+	
+	TabDlg *newTab = psi->newTabs();
+	sendChatTo(chat, newTab);
+}
+
+void TabDlg::closeChat()
+{
+	ChatDlg* chat = (ChatDlg*)(tabs->currentPage());
+	closeChat(chat);
+}
+
+void TabDlg::closeChat(ChatDlg* chat, bool doclose=true)
+{
+	chat->hide();
+	disconnect ( chat, SIGNAL( captionChanged( ChatDlg*) ), this, SLOT( updateTab( ChatDlg* ) ) );
+	disconnect ( chat, SIGNAL( contactIsComposing(ChatDlg*, bool) ), this, SLOT( setTabComposing( ChatDlg*, bool) ) );
+	disconnect ( chat, SIGNAL( unreadMessageUpdate(ChatDlg*, int) ), this, SLOT( setTabHasMessages(ChatDlg*, int) ) );
+	tabs->removePage(chat);
+	tabIsComposing.erase(chat);
+	tabHasMessages.erase(chat);
+	chats.remove(chat);
+	chat->reparent(0,QPoint());
+	if (doclose)
+		chat->close();
+	if (tabs->count()>0)
+		updateCaption();
+	checkHasChats();
+}
+
+void TabDlg::closeChat(QWidget* chat)
+{
+	closeChat((ChatDlg*)chat);
+}
+
+void TabDlg::selectTab(ChatDlg* chat)
+{
+	tabs->showPage(chat);
+}
+
+void TabDlg::checkHasChats()
+{
+	if (tabs->count()>0)
+		return;
+	closeMe();
+}
+
+void TabDlg::windowActivationChange(bool oldstate)
+{
+	QWidget::windowActivationChange(oldstate);
+
+	// if we're bringing it to the front, get rid of the '*' if necessary
+	if( isActiveWindow() ) { 
+		activated();
+	}
+}
+
+void TabDlg::activated()
+{
+	updateCaption();
+	doFlash(false);
+}
+
+void TabDlg::updateCaption()
+{
+	QString cap = "";
+	uint pending=0;
+	for ( uint i=0; i<tabHasMessages.count(); ++i)
+	{
+		pending+=tabHasMessages.values()[i];
+	}
+	if(pending > 0) {
+		cap += "* ";
+		if(pending > 1)
+			cap += QString("[%1] ").arg(pending);
+	}
+	cap += getName();
+
+	setCaption(cap);
+}
+
+void TabDlg::closeEvent(QCloseEvent* closeEvent)
+{
+	Q_UNUSED(closeEvent);
+	int count=tabs->count();
+	for (int i=0;i<count;++i) {
+		closeChat();
+	}
+}
+
+void TabDlg::closeMe()
+{
+	emit isDying(this);
+	//we do not delete it here, let the PsiCon do that, they create, they destroy.
+}
+
+ChatDlg* TabDlg::getChatPointer(QString fullJid)
+{
+	for (int i=0; i < tabs->count() ; i++)
+	{
+		if (((ChatDlg*)tabs->page(i))->jid().full()==fullJid)
+		{
+			return (ChatDlg*)(tabs->page(i));
+		}
+	}
+	return false;
+}
+
+void TabDlg::updateTab( ChatDlg* chat)
+{
+	QString label, prefix;
+	int num=tabHasMessages[chat];
+	if (num == 0)
+	{
+		prefix="";
+	} 
+	else if (num == 1) 
+	{
+		prefix="* ";
+	}
+	else
+	{
+		prefix=QString("[%1] ").arg(num);
+	}
+
+	label=prefix+chat->getDisplayNick();
+	tabs->setTabLabel( chat, label );
+	//now set text colour based upon whether there are new messages/composing etc
+	if (tabIsComposing[chat])
+		tabs->setTabColor( chat, Qt::darkGreen );
+	else if (tabHasMessages[chat])
+	{
+		tabs->setTabColor( chat, Qt::red );
+		doFlash(true);
+	}
+	else
+		tabs->setTabColor( chat, Qt::black );
+	updateCaption();
+}
+
+void TabDlg::setTabComposing(ChatDlg* chat, bool composing)
+{
+	tabIsComposing[chat]=composing;
+	updateTab(chat);
+}
+
+void TabDlg::setTabHasMessages(ChatDlg* chat, int messages)
+{
+	tabHasMessages[chat]=messages;
+	updateTab(chat);
+}
+
+void TabDlg::nextTab()
+{
+	int page = tabs->currentPageIndex()+1;
+	if ( page >= tabs->count() )
+		page = 0;
+	tabs->setCurrentPage( page );
+}
+
+void TabDlg::previousTab()
+{
+	int page = tabs->currentPageIndex()-1;
+	if ( page < 0 )
+		page = tabs->count() - 1;
+	tabs->setCurrentPage( page );
+}
+
+void TabDlg::tabTestCanDecode(const QDragMoveEvent* e, bool &b){
+	QString jid;
+	QCString type;
+	if ( QTextDrag::canDecode(e) && QTextDrag::decode(e, jid, type) && type=="psichatwindow" )
+	{
+		b=true;
+	}
+	else
+	{
+		b=false;
+	}
+}
+
+void TabDlg::tabReceivedDropEvent(QDropEvent* e){
+    ChatDlg* chat;
+		QString  jid;
+		QCString type;
+    if ( QTextDrag::decode(e, jid, type) && type=="psichatwindow" ) {
+			chat=psi->getChatInTabs(jid);
+			if (chat)
+			{
+				TabDlg *dlg=psi->getManagingTabs(chat);
+				dlg->sendChatTo(chat, this);
+			}
+    } 	
+}
+
+void TabDlg::tabReceivedDropEvent(QWidget* w, QDropEvent* e){
+	Q_UNUSED(w);
+	tabReceivedDropEvent(e);
+}
+
+
+void TabDlg::startDrag(QWidget* w)
+{
+    QDragObject *d = new QTextDrag( ((ChatDlg*)w)->jid().full(), this );
+		((QTextDrag*)d)->setSubtype("psichatwindow");
+    d->dragCopy();
+    // do NOT delete d.
+}
+
+void TabDlg::keyPressEvent(QKeyEvent *e)
+{
+	if (e->key() == Key_Escape)
+	{
+		closeChat();
+	}
+	else if ( e->key() == Key_W && (e->state() & ControlButton) )
+	{
+		closeChat();
+	}
+	else if ( e->key() == Key_PageUp && (e->state() & ControlButton) ||
+			  e->key() == Key_Backtab && (e->state() & ControlButton) )
+	{
+		previousTab();
+	}
+	else if ( e->key() == Key_PageDown && (e->state() & ControlButton) ||
+		      e->key() == Key_Tab && (e->state() & ControlButton) )
+	{
+		nextTab();
+	}
+	else
+		e->ignore();
+	
+}
+
Index: psi/tags/0.10-os2-b1/src/tabdlg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tabdlg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tabdlg.h	(revision 38)
@@ -0,0 +1,106 @@
+/*
+ * tabdlg.h - dialog for handling tabbed chats
+ * Copyright (C) 2005 Kevin Smith
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef TABDLG_H
+#define TABDLG_H
+
+#include <qwidget.h>
+#include <qptrlist.h>
+#include <qtabwidget.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include "im.h"
+#include <qmap.h>
+#include "psitabwidget.h"
+#include "advwidget.h"
+using namespace XMPP;
+
+class ChatDlg;
+class PsiCon;
+class ChatTabs;
+
+class QDragObject;
+class QContextMenuEvent;
+
+/*class PsiAccount;
+class UserListItem;
+class QDropEvent;
+class QDragEnterEvent;*/
+
+class TabDlg : public AdvancedWidget<QWidget>//, public QWidget
+{
+	Q_OBJECT
+public:
+	TabDlg(PsiCon*);
+	~TabDlg();
+	bool managesChat(ChatDlg*);
+	bool chatOnTop(ChatDlg*);
+	QString getName();
+	
+signals:
+	void isDying(TabDlg*);
+protected:
+	void closeEvent( QCloseEvent* );
+	void keyPressEvent(QKeyEvent *);
+	void windowActivationChange(bool);
+	void resizeEvent(QResizeEvent *);	
+protected slots:
+	void detachChat();
+	void detachChat(QWidget*);
+	void closeChat();
+	void closeChat(QWidget*);
+	void buildTabMenu();	
+	void sendChatTo(QWidget*, TabDlg *);
+public slots:
+	void addChat(ChatDlg *chat);
+	void setLooks();
+	void closeChat(ChatDlg*,bool);
+	void selectTab(ChatDlg*);
+	void activated();
+private slots:
+	void tabSelected(QWidget* chat);
+	void checkHasChats();
+	void closeMe();
+	void updateTab(ChatDlg*);
+	void nextTab();
+	void previousTab();
+	void setTabComposing(ChatDlg*, bool);	
+	void setTabHasMessages(ChatDlg*, int);
+	void tabTestCanDecode(const QDragMoveEvent*, bool&);
+	void tabReceivedDropEvent(QDropEvent*);
+	void tabReceivedDropEvent(QWidget*, QDropEvent*);
+	void startDrag(QWidget*);
+	
+public:
+	ChatDlg* getChatPointer(QString fullJid);
+private:
+	void updateCaption();
+	PsiCon *psi;
+	QPtrList<ChatDlg> chats;
+	KTabWidget *tabs;
+	QPushButton *detachButton, *closeButton, *closeCross;
+	QPopupMenu *tabMenu;
+	QMap<ChatDlg*, bool> tabIsComposing;
+	QMap<ChatDlg*, bool> tabHasMessages;
+
+	QSize chatSize;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tasklist.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tasklist.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tasklist.h	(revision 38)
@@ -0,0 +1,75 @@
+/****************************************************************************
+** tasklist.h - A small, but useful Task List
+** Copyright (C) 2003  Michail Pishchagin
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef TASKLIST_H
+#define TASKLIST_H
+
+#include <qobject.h>
+#include <qptrlist.h>
+
+#include "im.h"
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// TaskList -- read some comments inline
+//----------------------------------------------------------------------------
+
+class TaskList : public QObject, public QPtrList<Task>
+{
+	Q_OBJECT
+
+public:
+	TaskList()
+	{
+		setAutoDelete(true);
+	}
+
+	void append(const Task *d)
+	{
+		if ( isEmpty() )
+			emit started();
+
+		connect(d, SIGNAL(destroyed(QObject *)), SLOT(taskDestroyed(QObject *)));
+		QPtrList<Task>::append(d);
+	}
+
+signals:
+	// started() is emitted, when TaskList doesn't have any tasks in it,
+	// and append() is called, indicating, that TaskList contains at least one
+	// running Task
+	void started();
+
+	// finished() is emitted when TaskList contains one Task, and it suddenly
+	// terminates, indicating, that TaskList is empty now
+	void finished();
+
+private slots:
+	void taskDestroyed(QObject *p)
+	{
+		setAutoDelete(false);
+		remove((Task *)p);
+		setAutoDelete(true);
+
+		if ( isEmpty() )
+			emit finished();
+	}
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/advwidget/advwidget.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/advwidget/advwidget.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/advwidget/advwidget.cpp	(revision 38)
@@ -0,0 +1,333 @@
+/*
+ * advwidget.cpp - AdvancedWidget template class
+ * Copyright (C) 2005  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "advwidget.h"
+
+#include <qapplication.h>
+#include <qwidgetlist.h>
+#include <qtimer.h>
+
+#ifdef Q_OS_WIN
+#include <windows.h>
+#include <winuser.h>
+#endif
+
+// TODO: Make use of KDE taskbar flashing support
+// TODO: Use FlashWindowEx instead of FlashWindow!
+
+//----------------------------------------------------------------------------
+// AdvancedWidgetShared
+//----------------------------------------------------------------------------
+
+class AdvancedWidgetShared : public QObject
+{
+	Q_OBJECT
+public:
+	AdvancedWidgetShared();
+	~AdvancedWidgetShared();
+};
+
+AdvancedWidgetShared::AdvancedWidgetShared()
+	: QObject(qApp)
+{
+}
+
+AdvancedWidgetShared::~AdvancedWidgetShared()
+{
+}
+
+static AdvancedWidgetShared *advancedWidgetShared = 0;
+
+//----------------------------------------------------------------------------
+// GAdvancedWidget::Private
+//----------------------------------------------------------------------------
+
+class GAdvancedWidget::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(QWidget *parent);
+
+	static int  stickAt;
+	static bool stickToWindows;
+	static bool stickEnabled;
+
+	QWidget *parentWidget;
+
+	bool flashing();
+	void doFlash(bool on);
+	void posChanging(int *x, int *y, int *width, int *height);
+
+private:
+	QTimer *flashTimer;
+	int flashCount;
+
+private slots:
+	void flashAnimate();
+};
+
+int  GAdvancedWidget::Private::stickAt        = 5;
+bool GAdvancedWidget::Private::stickToWindows = true;
+bool GAdvancedWidget::Private::stickEnabled   = true;
+
+GAdvancedWidget::Private::Private(QWidget *parent)
+	: QObject(parent)
+{
+	if ( !advancedWidgetShared )
+		advancedWidgetShared = new AdvancedWidgetShared();
+
+	parentWidget = parent;
+	flashTimer = 0;
+	flashCount = 0;
+}
+
+void GAdvancedWidget::Private::posChanging(int *x, int *y, int *width, int *height)
+{
+	if ( stickAt <= 0                    ||
+	    !stickEnabled                    ||
+	    !parentWidget->isTopLevel()      ||
+	     parentWidget->isMaximized()     ||
+	    !parentWidget->isUpdatesEnabled() )
+	{
+		return;
+	}
+
+	QWidget *p = parentWidget;
+	if ( p->pos() == QPoint(*x, *y) &&
+	     p->frameSize() == QSize(*width, *height) )
+		return;
+
+	bool resizing = p->frameSize() != QSize(*width, *height);
+
+	QWidget *w;
+	QDesktopWidget *desktop = qApp->desktop();
+	QWidgetList *list;
+
+	if ( stickToWindows )
+		list = QApplication::topLevelWidgets();
+	else
+		list = new QWidgetList();
+	list->append( desktop );
+
+	QWidgetListIt it( *list );
+	for ( ; (w = it.current()); ++it ) {
+		QRect rect;
+		bool dockWidget = false;
+
+		if ( w->isDesktop() )
+			rect = ((QDesktopWidget *)w)->availableGeometry((QWidget *)parent());
+		else {
+			if ( w == p ||
+			     desktop->screenNumber(p) != desktop->screenNumber(w) )
+				continue;
+
+			if ( !w->isVisible() )
+				continue;
+
+			// we want for widget to stick to outer edges of another widget, so
+			// we'll change the rect to what it'll snap
+			rect = QRect(w->frameGeometry().bottomRight(), w->frameGeometry().topLeft());
+			dockWidget = true;
+		}
+
+		if ( *x != p->x() )
+		if ( *x <= rect.left() + stickAt &&
+		     *x >  rect.left() - stickAt ) {
+			if ( !dockWidget ||
+			     (p->frameGeometry().bottom() >= rect.bottom() &&
+			      p->frameGeometry().top() <= rect.top()) ) {
+				*x = rect.left();
+				if ( resizing )
+					*width = p->frameSize().width() + p->x() - *x;
+			}
+		}
+
+		if ( *x + *width >= rect.right() - stickAt &&
+		     *x + *width <= rect.right() + stickAt ) {
+			if ( !dockWidget ||
+			     (p->frameGeometry().bottom() >= rect.bottom() &&
+			      p->frameGeometry().top() <= rect.top()) ) {
+				if ( resizing )
+					*width = rect.right() - *x + 1;
+				else
+					*x = rect.right() - *width + 1;
+			}
+		}
+
+		if ( *y != p->y() )
+		if ( *y <= rect.top() + stickAt &&
+		     *y >  rect.top() - stickAt ) {
+			if ( !dockWidget ||
+			     (p->frameGeometry().right() >= rect.right() &&
+			      p->frameGeometry().left() <= rect.left()) ) {
+				*y = rect.top();
+				if ( resizing )
+					*height = p->frameSize().height() + p->y() - *y;
+			}
+		}
+
+		if ( *y + *height >= rect.bottom() - stickAt &&
+		     *y + *height <= rect.bottom() + stickAt ) {
+			if ( !dockWidget ||
+			     (p->frameGeometry().right() >= rect.right() &&
+			      p->frameGeometry().left() <= rect.left()) ) {
+				if ( resizing )
+					*height = rect.bottom() - *y + 1;
+				else
+					*y = rect.bottom() - *height + 1;
+			}
+		}
+	}
+
+	delete list;
+}
+
+void GAdvancedWidget::Private::doFlash(bool yes)
+{
+#ifdef Q_WS_WIN
+	if ( yes ) {
+		if ( flashTimer )
+			return;
+		flashTimer = new QTimer(this);
+		connect(flashTimer, SIGNAL(timeout()), SLOT(flashAnimate()));
+		flashCount = 0;
+		flashAnimate(); // kick the first one immediately
+		flashTimer->start(500);
+	}
+	else {
+		if ( flashTimer ) {
+			delete flashTimer;
+			flashTimer = 0;
+			// comment this out to fix titlebar repaint on Windows??
+			//FlashWindow(winId(), false);
+		}
+	}
+#else
+	Q_UNUSED(yes)
+#endif
+}
+
+void GAdvancedWidget::Private::flashAnimate()
+{
+#ifdef Q_WS_WIN
+	FlashWindow( parentWidget->winId(), true );
+	if ( ++flashCount == 5 )
+		flashTimer->stop();
+#endif
+}
+
+bool GAdvancedWidget::Private::flashing()
+{
+	bool on = false;
+	if ( flashTimer )
+		on = flashCount & 1;
+
+	return on;
+}
+
+//----------------------------------------------------------------------------
+// GAdvancedWidget
+//----------------------------------------------------------------------------
+
+GAdvancedWidget::GAdvancedWidget(QWidget *parent, const char *name)
+	: QObject(parent, name)
+{
+	d = new Private(parent);
+}
+
+GAdvancedWidget::~GAdvancedWidget()
+{
+	delete d;
+}
+
+#ifdef Q_OS_WIN
+bool GAdvancedWidget::winEvent(MSG *msg)
+{
+	if ( msg->message == WM_WINDOWPOSCHANGING ) {
+		WINDOWPOS *wpos = (WINDOWPOS *)msg->lParam;
+
+		d->posChanging(&wpos->x, &wpos->y, &wpos->cx, &wpos->cy);
+
+		return true;
+	}
+
+	return false;
+}
+#endif
+
+void GAdvancedWidget::preSetCaption()
+{
+#ifdef Q_WS_WIN
+	if ( d->flashing() )
+		FlashWindow( d->parentWidget->winId(), true );
+#endif
+}
+
+void GAdvancedWidget::postSetCaption()
+{
+#ifdef Q_WS_WIN
+	if ( d->flashing() )
+		FlashWindow( d->parentWidget->winId(), true );
+#endif
+}
+
+void GAdvancedWidget::doFlash(bool on)
+{
+	d->doFlash( on );
+}
+
+void GAdvancedWidget::windowActivationChange(bool oldstate)
+{
+	if ( d->parentWidget->isActiveWindow() ) {
+		d->doFlash(false);
+	}
+}
+
+int GAdvancedWidget::stickAt()
+{
+	return Private::stickAt;
+}
+
+void GAdvancedWidget::setStickAt(int val)
+{
+	Private::stickAt = val;
+}
+
+bool GAdvancedWidget::stickToWindows()
+{
+	return Private::stickToWindows;
+}
+
+void GAdvancedWidget::setStickToWindows(bool val)
+{
+	Private::stickToWindows = val;
+}
+
+bool GAdvancedWidget::stickEnabled()
+{
+	return Private::stickEnabled;
+}
+
+void GAdvancedWidget::setStickEnabled(bool val)
+{
+	Private::stickEnabled = val;
+}
+
+#include "advwidget.moc"
Index: psi/tags/0.10-os2-b1/src/tools/advwidget/advwidget.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/advwidget/advwidget.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/advwidget/advwidget.h	(revision 38)
@@ -0,0 +1,108 @@
+/*
+ * advwidget.h - AdvancedWidget template class
+ * Copyright (C) 2005  Michail Pishchagin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ADVWIDGET_H
+#define ADVWIDGET_H
+
+#include <qwidget.h>
+
+class GAdvancedWidget : public QObject
+{
+	Q_OBJECT
+public:
+	GAdvancedWidget(QWidget *parent, const char *name = 0);
+	~GAdvancedWidget();
+
+	static bool stickEnabled();
+	static void setStickEnabled(bool val);
+
+	static int stickAt();
+	static void setStickAt(int val);
+
+	static bool stickToWindows();
+	static void setStickToWindows(bool val);
+
+	void doFlash(bool on);
+
+#ifdef Q_OS_WIN
+	bool winEvent(MSG *msg);
+#endif
+
+	void preSetCaption();
+	void postSetCaption();
+
+	void windowActivationChange(bool oldstate);
+
+public:
+	class Private;
+private:
+	Private *d;
+};
+
+template <class BaseClass>
+class AdvancedWidget : public BaseClass
+{
+private:
+	GAdvancedWidget *gAdvWidget;
+
+public:
+	AdvancedWidget( QWidget *parent = 0, const char *name = 0, Qt::WFlags f = 0 )
+		: BaseClass( parent, name, f )
+	{
+		gAdvWidget = new GAdvancedWidget( this );
+	}
+
+	static bool stickEnabled() { return GAdvancedWidget::stickEnabled(); }
+	static void setStickEnabled( bool val ) { GAdvancedWidget::setStickEnabled( val ); }
+
+	static int stickAt() { return GAdvancedWidget::stickAt(); }
+	static void setStickAt( int val ) { GAdvancedWidget::setStickAt( val ); }
+
+	static bool stickToWindows() { return GAdvancedWidget::stickToWindows(); }
+	static void setStickToWindows( bool val ) { GAdvancedWidget::setStickToWindows( val ); }
+
+	void doFlash( bool on )
+	{
+		gAdvWidget->doFlash( on );
+	}
+
+#ifdef Q_OS_WIN
+	bool winEvent( MSG *msg )
+	{
+		return gAdvWidget->winEvent( msg );
+	}
+#endif
+
+	void setCaption( const QString &c )
+	{
+		gAdvWidget->preSetCaption();
+		BaseClass::setCaption( c );
+		gAdvWidget->postSetCaption();
+	}
+
+protected:
+	void windowActivationChange( bool oldstate )
+	{
+		gAdvWidget->windowActivationChange( oldstate );
+		BaseClass::windowActivationChange( oldstate );
+	}
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/advwidget/advwidget.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/advwidget/advwidget.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/advwidget/advwidget.pri	(revision 38)
@@ -0,0 +1,4 @@
+advwidget {
+	HEADERS += $$ADVWIDGET_CPP/advwidget.h
+	SOURCES += $$ADVWIDGET_CPP/advwidget.cpp
+}
Index: psi/tags/0.10-os2-b1/src/tools/crash/crash.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/crash/crash.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/crash/crash.cpp	(revision 38)
@@ -0,0 +1,32 @@
+/*
+ * crash.c - handle psi's crashes
+ *
+ * Copyright (C) 2004 by Juan F. Codagnone <juam@users.sourceforge.net>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <signal.h>
+#include "crash.h"
+#include "crash_sigsegv.h"
+
+namespace Crash {
+
+void registerSigsegvHandler(QString)
+{
+	signal(SIGSEGV, sigsegv_handler_bt_full_fnc);
+}
+
+}; // namespace Crash
Index: psi/tags/0.10-os2-b1/src/tools/crash/crash.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/crash/crash.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/crash/crash.h	(revision 38)
@@ -0,0 +1,12 @@
+#ifndef CRASH_H
+#define CRASH_H
+
+#include <qstring.h>
+
+namespace Crash {
+
+	void registerSigsegvHandler(QString progname);
+
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/crash/crash.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/crash/crash.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/crash/crash.pri	(revision 38)
@@ -0,0 +1,21 @@
+crash {
+	INCLUDEPATH += $$CRASH_CPP
+	HEADERS     += $$CRASH_CPP/crash.h
+
+	unix {
+		SOURCES     += $$CRASH_CPP/crash_sigsegv.cpp
+		HEADERS     += $$CRASH_CPP/crash_sigsegv.h
+				
+		!isEmpty(KDE) {
+			SOURCES += $$CRASH_CPP/crash_kde.cpp
+			LIBS    += -L$$KDE/lib -lkdecore
+			INCLUDEPATH += $$KDE/include
+		}
+		else {
+			SOURCES +=  $$CRASH_CPP/crash.cpp 
+		}
+	}
+	else {
+		SOURCES += $$CRASH_CPP/crash_dummy.cpp
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/crash/crash_dummy.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/crash/crash_dummy.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/crash/crash_dummy.cpp	(revision 38)
@@ -0,0 +1,9 @@
+#include "crash.h"
+
+namespace Crash {
+
+void registerSigsegvHandler(QString)
+{
+}
+
+}; // namespace Crash
Index: psi/tags/0.10-os2-b1/src/tools/crash/crash_kde.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/crash/crash_kde.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/crash/crash_kde.cpp	(revision 38)
@@ -0,0 +1,12 @@
+#include "crash.h"
+#include <kcrash.h>
+
+namespace Crash {
+
+void registerSigsegvHandler(QString progname)
+{
+	KCrash::setApplicationName(progname);
+	KCrash::setCrashHandler(KCrash::defaultCrashHandler);
+}
+
+}; // namespace Crash
Index: psi/tags/0.10-os2-b1/src/tools/crash/crash_sigsegv.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/crash/crash_sigsegv.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/crash/crash_sigsegv.cpp	(revision 38)
@@ -0,0 +1,248 @@
+/* sigsegv.c -- sigsegv handlers
+ *
+ * Copyright (c) 2003 Juan F. Codagnone <juam@users.sourceforge.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H_
+  #include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <unistd.h>
+
+#ifdef HAVE_PTHREADS_H
+#	include <pthread.h>
+#endif
+
+/*
+ * http://www.gnu.org/manual/glibc-2.2.3/html_chapter/libc_33.html
+ */
+#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1
+#	define HAVE_BACKTRACE
+#	include <execinfo.h>
+#endif
+#include <sys/wait.h>
+
+#include "crash_sigsegv.h"
+
+namespace Crash {
+
+static int (* print)(const char *format, ...) = NULL;
+static int needs_cr = 1;
+
+void *sigsegv_set_print( int (* fnc)(const char *format, ...), int _needs_cr)
+{
+	void *ret;
+
+	ret = &print;
+	print = fnc;
+	needs_cr = _needs_cr;
+
+	return ret;
+}
+
+/**
+ * launchs gdb, and feeds myprint with the backtrace
+ */
+static int dump_pid_son(pid_t pid, const char *binary, int full_bt,
+             int (* myprint)(const char *format, ...))
+{
+	char tmp[]="/tmp/mrbug-crash-XXXXXX";
+	int ret = 0;
+	int fd;
+
+	fd = mkstemp(tmp);
+	if( fd == -1 )
+	{
+		(*myprint)("opening gdb command (tempory) file `%s'%s", tmp,
+			   needs_cr ? "\n" : "");
+		ret = -1;
+	}
+	else
+	{
+		char gdb_cmd[]="bt\nquit";
+		char gdb_cmd_full[]="bt full\nquit";
+		char cmd[128];
+		FILE *fp;
+
+		if( full_bt )
+			write(fd, gdb_cmd_full, strlen(gdb_cmd_full));
+		else
+			write(fd, gdb_cmd, strlen(gdb_cmd));
+		close(fd);
+
+		sprintf(cmd, "gdb -nw -n -batch -x \"%s\" %s %d", tmp, binary,
+			pid);
+		(*myprint)("trying to dump pid: %d (%s)...%s", pid, binary,
+                           needs_cr ? "\n" : "");
+
+		fflush(NULL);
+		fp = popen(cmd, "r");
+		if( fp == NULL )
+		{
+			(*myprint)("err. couldn't exec `%s'%s", cmd,
+				   needs_cr ? "\n" : "");
+			ret = -1;
+		}
+		else
+		{
+			char buff[4096];
+			size_t len;
+
+			while(fgets(buff, sizeof(buff), fp))
+			{
+				len = strlen(buff);
+				if( buff[len-1] == '\n')
+					buff[len-1]=0;
+
+				(*myprint)("%s%s", buff,needs_cr ? "\n" : "");
+			}
+			fclose(fp);
+		}
+		if( remove(tmp) == -1 )
+			(*myprint)("removing `%s` (@;@)%s", tmp,
+				   needs_cr ? "\n" : "");
+	}
+
+	return ret;
+}
+
+static int dump_pid(pid_t pid, const char *binary, int full_bt )
+{
+	pid_t mpid;
+	int (* myprint)(const char *format, ...);
+
+	myprint = print ? (int(*)(const char *format, ...))print : (int(*)(const char *format, ...))printf;
+
+	/*
+	 * clone the process, so we don't make the bt bigger.
+	 */
+	mpid = fork();
+	if( mpid == 0 )
+	{
+		dump_pid_son(pid, binary, full_bt,  myprint);
+		exit(0);
+	}
+	else if( mpid == -1 )
+		(*myprint)("lunching son: `%s' %s", strerror(errno),
+			   needs_cr ? "\n" : "");
+	else
+	{
+		/* father */
+		int status;
+
+		alarm(0);
+		waitpid(0, &status, 0);
+		if( WIFEXITED(status) && WEXITSTATUS(status)==0 )
+			;
+	}
+
+	return 0;
+}
+
+/**
+ * get `pid`'s real path
+ *
+ * \param buff     buffer for the output
+ * \param nbuff    size of the buffer
+ * \param pid      pid processes id to use
+ *
+ * \note this function works only in linux
+ *
+ * \return the buffer
+ */
+static char *get_path_from_pid(char *buff, size_t nbuff, pid_t pid)
+{
+	char proc[256];
+	char *ret = NULL;
+	int n;
+
+	sprintf(proc, "/proc/%d/exe", pid);
+	if( (n=readlink(proc, buff, nbuff)) == -1 )
+		ret = NULL;
+	else
+	{
+		buff[n]=0;
+		ret = buff;
+	}
+
+	return ret;
+}
+
+static void sigsegv_libc_dump( int (* myprint)(const char *format, ...) )
+{
+	void *array[48] = {0};
+	unsigned short i;
+	int n;
+	char **res;
+
+#ifdef HAVE_BACKTRACE
+	(*myprint)("Backtrace:%c", needs_cr ? "\n" : "");
+	n  = backtrace(array, sizeof(array)/(sizeof(*array)));
+	res =  backtrace_symbols(array, n);
+	for (i = 0; i < n; i++)
+		(*myprint)("%s%s", res[i], needs_cr ? "\n" : "");
+
+	(*myprint)("Attempting to generate core file%s",
+		   needs_cr ? "" : "");
+#endif
+}
+
+static void sigsegv_handler_generic(int signal, int full_bt)
+{
+	char binary[2048];
+	int pid = getpid();
+	int (* myprint)(const char *format, ...);
+
+	myprint = print ? print : printf;
+	if( get_path_from_pid(binary, sizeof(binary), pid) == NULL)
+		(*myprint)("pid %d does not seems to exist", pid);
+        else
+	{
+		(*myprint)("Segmentation Violation Detected.%s",
+			   needs_cr ? "\n" : "");
+		dump_pid(pid, binary, full_bt);
+		sigsegv_libc_dump(myprint);
+	}
+
+#ifdef HAVE_PTHREAD_H
+	pthread_kill_other_threads_np();
+#endif
+	fflush(NULL);
+	abort();
+}
+
+void sigsegv_handler_fnc(int signal)
+{
+	sigsegv_handler_generic(signal, 0);
+}
+
+void sigsegv_handler_bt_full_fnc(int signal)
+{
+	sigsegv_handler_generic(signal, 1);
+}
+
+}; // namespace Crash
Index: psi/tags/0.10-os2-b1/src/tools/crash/crash_sigsegv.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/crash/crash_sigsegv.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/crash/crash_sigsegv.h	(revision 38)
@@ -0,0 +1,49 @@
+/*
+ * sigsegv.h -- sigsegv handlers
+ *
+ * Copyright (c) 2003 Juan F. Codagnone <juam@users.sourceforge.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef CRASH_SIGSEGV_H
+#define CRASH_SIGSEGV_H
+
+namespace Crash {
+
+/**
+ * sets the print function (it is used to print the backtrace)
+ * if it isn't set, or it is set to NULL, printf will be used.
+ *
+ * \param  _need_cr    needs to append an \n at the end?
+ */
+void *sigsegv_set_print( int (* fnc)(const char *format, ...), int _needs_cr);
+
+/**
+ * try to print a backtrace of the programa, and dump the core
+ */
+void sigsegv_handler_fnc(int signal);
+
+/**
+ * try to print a verbose backtrace of the program, and dump the core
+ */
+void sigsegv_handler_bt_full_fnc(int signal);
+
+}; // namespace Crash
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch.cpp	(revision 38)
@@ -0,0 +1,289 @@
+/*
+ * dirwatch.cpp - detect changes of directory content
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"dirwatch.h"
+
+#include<qdir.h>
+#include<qfileinfo.h>
+#include<qvaluelist.h>
+#include<qstringlist.h>
+#include<qtimer.h>
+
+static DirWatchPlatform *platform = 0;
+static int platform_ref = 0;
+
+struct file_info
+{
+	QString name;
+	uint size;
+	QDateTime lastModified;
+};
+
+typedef QValueList<file_info> DirInfo;
+
+static DirInfo getDirInfo(const QString &dir)
+{
+	DirInfo info;
+	QDir d(dir);
+	if(!d.exists())
+		return info;
+	QStringList el = d.entryList();
+	for(QStringList::ConstIterator it = el.begin(); it != el.end(); ++it) {
+		if(*it == "." || *it == "..")
+			continue;
+		QFileInfo fi(d.filePath(*it));
+		file_info i;
+		i.name = fi.fileName();
+		i.size = fi.size();
+		i.lastModified = fi.lastModified();
+		info += i;
+	}
+	return info;
+}
+
+class DirWatch::Private
+{
+public:
+	Private() {}
+
+	QString dir;
+
+	// for non-platform watching
+	DirInfo info;
+	QTimer checkTimer;
+	int freq;
+	int id;
+};
+
+DirWatch::DirWatch(const QString &dir, QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->freq = 5000;
+	d->id = -1;
+
+	// try to use platform
+	if(!platform) {
+		DirWatchPlatform *p = new DirWatchPlatform;
+		if(p->init())
+			platform = p;
+		else
+			delete p;
+	}
+	if(platform) {
+		++platform_ref;
+		connect(platform, SIGNAL(dirChanged(int)), SLOT(pf_dirChanged(int)));
+	}
+	else {
+		connect(&d->checkTimer, SIGNAL(timeout()), SLOT(doCheck()));
+	}
+
+	if(!dir.isEmpty())
+		setDir(dir);
+}
+
+DirWatch::~DirWatch()
+{
+	setDir("");
+
+	if(platform) {
+		--platform_ref;
+		if(platform_ref == 0) {
+			delete platform;
+			platform = 0;
+		}
+	}
+	delete d;
+}
+
+bool DirWatch::usingPlatform() const
+{
+	return (platform ? true: false);
+}
+
+QString DirWatch::dir() const
+{
+	return d->dir;
+}
+
+void DirWatch::setDir(const QString &s)
+{
+	if(d->dir == s)
+		return;
+
+	if(!d->dir.isEmpty()) {
+		if(platform) {
+			if(d->id != -1)
+				platform->removeDir(d->id);
+		}
+		else
+			d->checkTimer.stop();
+
+		d->dir = "";
+	}
+
+	if(s.isEmpty())
+		return;
+
+	d->dir = s;
+
+	if(platform) {
+		d->id = platform->addDir(d->dir);
+	}
+	else {
+		d->info = getDirInfo(d->dir);
+		d->checkTimer.start(d->freq);
+	}
+}
+
+void DirWatch::doCheck()
+{
+	DirInfo newInfo = getDirInfo(d->dir);
+
+	bool info_changed = false;
+	QDir dir(d->dir);
+	file_info ni;
+
+	// look for files that are missing or modified
+	for(DirInfo::ConstIterator it = d->info.begin(); it != d->info.end(); ++it) {
+		const file_info &i = *it;
+
+		bool found = false;
+		for(DirInfo::ConstIterator nit = newInfo.begin(); nit != newInfo.end(); ++nit) {
+			const file_info &tmp = *nit;
+			if(i.name == tmp.name) {
+				found = true;
+				ni = tmp;
+				break;
+			}
+		}
+		if(!found) {
+			//printf("%s: not found\n", i.name.latin1());
+			info_changed = true;
+			break;
+		}
+
+		if(i.lastModified != ni.lastModified || i.size != ni.size) {
+			//printf("%s: modified (is: %s, was: %s)\n", i.name.latin1(), ni.lastModified.toString().latin1(), i.lastModified.toString().latin1());
+			info_changed = true;
+			break;
+		}
+		//printf("%s: no change\n", i.name.latin1());
+	}
+
+	// look for files that have been added
+	for(DirInfo::ConstIterator nit = newInfo.begin(); nit != newInfo.end(); ++nit) {
+		const file_info &i = *nit;
+		bool found = false;
+		for(DirInfo::ConstIterator it = d->info.begin(); it != d->info.end(); ++it) {
+			const file_info &tmp = *it;
+			if(i.name == tmp.name) {
+				found = true;
+				break;
+			}
+		}
+		if(!found) {
+			info_changed = true;
+			break;
+		}
+	}
+
+	d->info = newInfo;
+
+	if(info_changed)
+		changed();
+}
+
+void DirWatch::pf_dirChanged(int id)
+{
+	if(d->id != id)
+		return;
+
+	changed();
+}
+
+
+class FileWatch::Private
+{
+public:
+	Private() {}
+
+	QString fname;
+	DirWatch *dw;
+
+	uint lastSize;
+	QDateTime lastModified;
+};
+
+FileWatch::FileWatch(const QString &fname, QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->dw = 0;
+	if(!fname.isEmpty())
+		setFileName(fname);
+}
+
+FileWatch::~FileWatch()
+{
+	delete d;
+}
+
+QString FileWatch::fileName() const
+{
+	return d->fname;
+}
+
+void FileWatch::setFileName(const QString &s)
+{
+	if(d->fname == s)
+		return;
+
+	// remove old watch if necessary
+	if(d->dw) {
+		delete d->dw;
+		d->dw = 0;
+	}
+	d->fname = s;
+
+	if(d->fname.isEmpty())
+		return;
+
+	// setup the watch
+	d->dw = new DirWatch("", this);
+	connect(d->dw, SIGNAL(changed()), SLOT(dirChanged()));
+	QFileInfo fi(d->fname);
+	d->lastSize = fi.size();
+	d->lastModified = fi.lastModified();
+	d->dw->setDir(fi.dirPath());
+}
+
+void FileWatch::dirChanged()
+{
+	bool doChange = false;
+	QFileInfo fi(d->fname);
+	if(fi.size() != d->lastSize || fi.lastModified() != d->lastModified)
+		doChange = true;
+	d->lastSize = fi.size();
+	d->lastModified = fi.lastModified();
+
+	if(doChange)
+		changed();
+}
Index: psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch.h	(revision 38)
@@ -0,0 +1,99 @@
+/*
+ * dirwatch.h - detect changes of directory content
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_DIRWATCH_H
+#define CS_DIRWATCH_H
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include<qobject.h>
+
+class FileWatch : public QObject
+{
+	Q_OBJECT
+public:
+	FileWatch(const QString &fname="", QObject *parent=0);
+	~FileWatch();
+
+	QString fileName() const;
+	void setFileName(const QString &);
+
+signals:
+	void changed();
+
+private slots:
+	void dirChanged();
+
+private:
+	class Private;
+	Private *d;
+};
+
+class DirWatch : public QObject
+{
+	Q_OBJECT
+public:
+	DirWatch(const QString &dir="", QObject *parent=0);
+	~DirWatch();
+
+	QString dir() const;
+	void setDir(const QString &);
+
+	bool usingPlatform() const;
+
+signals:
+	void changed();
+
+private slots:
+	void doCheck();
+	void pf_dirChanged(int);
+
+private:
+	class Private;
+	Private *d;
+};
+
+class DirWatchPlatform : public QObject
+{
+	Q_OBJECT
+public:
+	DirWatchPlatform();
+	~DirWatchPlatform();
+
+	bool init();
+	int addDir(const QString &);
+	void removeDir(int);
+
+signals:
+	void dirChanged(int);
+
+public:
+	class Private;
+private:
+	Private *d;
+
+	friend class Private;
+private slots:
+	void triggerDirChanged(int x) { dirChanged(x); }
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch.pri	(revision 38)
@@ -0,0 +1,17 @@
+dirwatch {
+	HEADERS += $$DIRWATCH_CPP/dirwatch.h
+	SOURCES += $$DIRWATCH_CPP/dirwatch.cpp
+
+	unix:!mac {
+		SOURCES += $$DIRWATCH_CPP/dirwatch_unix.cpp
+	}
+	win32: {
+		SOURCES += $$DIRWATCH_CPP/dirwatch_win.cpp
+	}
+	os2: {
+		SOURCES += $$DIRWATCH_CPP/dirwatch_pm.cpp
+	}
+	mac: {
+		SOURCES += $$DIRWATCH_CPP/dirwatch_mac.cpp
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_mac.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_mac.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_mac.cpp	(revision 38)
@@ -0,0 +1,27 @@
+/*
+ * dirwatch_unix.cpp - detect changes of directory content
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"dirwatch.h"
+
+DirWatchPlatform::DirWatchPlatform():QObject(0) {}
+DirWatchPlatform::~DirWatchPlatform() {}
+bool DirWatchPlatform::init() { return false; }
+int DirWatchPlatform::addDir(const QString &) { return -1; }
+void DirWatchPlatform::removeDir(int) {}
Index: psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_pm.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_pm.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_pm.cpp	(revision 38)
@@ -0,0 +1,37 @@
+/*
+ * dirwatch_pm.cpp - detect changes of directory content (OS/2 PM)
+ * Copyright (C) 2006  Dmitry A. Kuminov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"dirwatch.h"
+
+/// @todo (dmik)
+//  For now, it's a dummy code.
+//  Try to use DosOpenChangeNotify() API, see:
+//      http://os2.in.ru/guru/faq/source/os2prog.004
+//      http://dn2.byte.nsk.su/DN2S/LIB.OS2/fnotify.pas
+//  This may conflict with WPS (that also uses this API). Investigate it and
+//  if it's the case, we can probably subclass WPFileSystem and use the
+//  wpVerifyUpdateAccess to watch for updates. And the final alternative
+//  is a simple polling timer (if this DirWatch API is really critical for Psi).
+
+DirWatchPlatform::DirWatchPlatform():QObject(0) {}
+DirWatchPlatform::~DirWatchPlatform() {}
+bool DirWatchPlatform::init() { return false; }
+int DirWatchPlatform::addDir(const QString &) { return -1; }
+void DirWatchPlatform::removeDir(int) {}
Index: psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_unix.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_unix.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_unix.cpp	(revision 38)
@@ -0,0 +1,342 @@
+/*
+ * dirwatch_unix.cpp - detect changes of directory content
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"dirwatch.h"
+
+// A great many hints taken from KDE's KDirWatch system.  Thank you KDE!
+
+#ifdef HAVE_DNOTIFY
+#include<qtimer.h>
+#include<qsocketnotifier.h>
+#include<qvaluelist.h>
+#include<qptrlist.h>
+#include<qfile.h>
+#include<unistd.h>
+#include<fcntl.h>
+#include<signal.h>
+#include<sys/utsname.h>
+
+#define DNOTIFY_SIGNAL (SIGRTMIN + 8)
+
+class DWEntry
+{
+public:
+	static DWEntry * open(const QString &s)
+	{
+		int fd = ::open(QFile::encodeName(s), O_RDONLY);
+		if(fd == -1)
+			return 0;
+
+		int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT|DN_MODIFY|DN_ATTRIB;
+		if(fcntl(fd, F_SETSIG, DNOTIFY_SIGNAL) == -1 || fcntl(fd, F_NOTIFY, mask) == -1) {
+			close(fd);
+			return 0;
+		}
+		DWEntry *e = new DWEntry(s, fd);
+		return e;
+	}
+
+	~DWEntry()
+	{
+		close(fd);
+	}
+
+	QString dir;
+	int fd;
+	QValueList<int> idList;
+	bool dirty;
+
+private:
+	DWEntry(const QString &_dir, int _fd)
+	{
+		dir = _dir;
+		fd = _fd;
+		dirty = false;
+	}
+};
+
+#endif
+
+class DirWatchPlatform::Private : public QObject
+{
+	Q_OBJECT
+public:
+#ifdef HAVE_DNOTIFY
+	QTimer t;
+	QSocketNotifier *sn;
+	int pipe_notify[2];
+	bool ok;
+	QPtrList<DWEntry> list;
+#endif
+	DirWatchPlatform *par;
+	static Private *self;
+
+	Private(DirWatchPlatform *parent)
+	{
+		par = parent;
+		self = this;
+
+#ifdef HAVE_DNOTIFY
+		list.setAutoDelete(true);
+		ok = false;
+
+		// from KDE
+		bool supports_dnotify = true; // not guilty until proven guilty
+		struct utsname uts;
+		int major, minor, patch;
+		if(uname(&uts) < 0)
+			supports_dnotify = false; // *shrug*
+		else if(sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
+			supports_dnotify = false; // *shrug*
+		else if( major * 1000000 + minor * 1000 + patch < 2004019 ) // <2.4.19
+			supports_dnotify = false;
+
+		if(!supports_dnotify)
+			return;
+
+		if(pipe(pipe_notify) == -1)
+			return;
+
+		if(fcntl(pipe_notify[0], F_SETFL, O_NONBLOCK) == -1)
+			return;
+
+		connect(&t, SIGNAL(timeout()), this, SLOT(slotNotify()));
+
+		sn = new QSocketNotifier(pipe_notify[0], QSocketNotifier::Read);
+		connect(sn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
+
+		struct sigaction act;
+		act.sa_sigaction = dnotify_handler;
+		sigemptyset(&act.sa_mask);
+		act.sa_flags = SA_SIGINFO;
+#ifdef SA_RESTART
+		act.sa_flags |= SA_RESTART;
+#endif
+		sigaction(DNOTIFY_SIGNAL, &act, NULL);
+
+		ok = true;
+#endif
+	}
+
+	~Private()
+	{
+#ifdef HAVE_DNOTIFY
+		if(ok) {
+			delete sn;
+			close(pipe_notify[0]);
+			close(pipe_notify[1]);
+		}
+		list.clear();
+#endif
+
+		self = 0;
+	}
+
+#ifdef HAVE_DNOTIFY
+	static void dnotify_handler(int, siginfo_t *si, void *)
+	{
+		// bad news
+		if(!si)
+			return;
+
+		int fd = si->si_fd;
+		//printf("dnotify_handler: fd=%d\n", fd);
+
+		DWEntry *e = self->findEntryByFd(fd);
+		if(!e)
+			return;
+		e->dirty = true;
+
+		// write a byte into the pipe
+		char c = 1;
+		write(self->pipe_notify[1], &c, 1);
+	}
+#endif
+
+private slots:
+	void slotActivated()
+	{
+#ifdef HAVE_DNOTIFY
+		// clear out the pipe
+		while(1) {
+			char buf[64];
+			int ret = read(pipe_notify[0], buf, 64);
+			if(ret < 64)
+				break;
+		}
+
+		// use a timer to combine multiple dnotify events into one
+		if(!t.isActive())
+			t.start(200, true);
+#endif
+	}
+
+	void slotNotify()
+	{
+#ifdef HAVE_DNOTIFY
+		// see who is dirty
+		QPtrListIterator<DWEntry> it(list);
+		for(DWEntry *e; (e = it.current()); ++it) {
+			if(e->dirty) {
+				e->dirty = false;
+				for(QValueList<int>::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) {
+					par->triggerDirChanged(*idi);
+				}
+			}
+		}
+#endif
+	}
+
+public:
+#ifdef HAVE_DNOTIFY
+	int addItem(const QString &s)
+	{
+		//printf("setting up DNOTIFY for [%s]\n", s.latin1());
+
+		DWEntry *e = findEntryByDir(s);
+		if(!e) {
+			e = DWEntry::open(s);
+			if(!e)
+				return -1;
+			list.append(e);
+		}
+		int id = getUniqueId();
+		e->idList.append(id);
+
+		//printf("success (fd=%d)!\n", e->fd);
+		return id;
+	}
+
+	void removeItem(int id)
+	{
+		DWEntry *e = findEntryById(id);
+		if(!e)
+			return;
+		e->idList.remove(id);
+		if(e->idList.isEmpty())
+			list.removeRef(e);
+	}
+
+	int getUniqueId()
+	{
+		for(int n = 0;; ++n) {
+			QPtrListIterator<DWEntry> it(list);
+			bool found = false;
+			for(DWEntry *e; (e = it.current()); ++it) {
+				for(QValueList<int>::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) {
+					if(*idi == n) {
+						found = true;
+						break;
+					}
+				}
+				if(found)
+					break;
+			}
+			if(!found)
+				return n;
+		}
+	}
+
+	DWEntry * findEntryByDir(const QString &s)
+	{
+		QPtrListIterator<DWEntry> it(list);
+		for(DWEntry *e; (e = it.current()); ++it) {
+			if(e->dir == s)
+				return e;
+		}
+		return 0;
+	}
+
+	DWEntry * findEntryById(int id)
+	{
+		QPtrListIterator<DWEntry> it(list);
+		for(DWEntry *e; (e = it.current()); ++it) {
+			for(QValueList<int>::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) {
+				if(*idi == id)
+					return e;
+			}
+		}
+		return 0;
+	}
+
+	DWEntry * findEntryByFd(int fd)
+	{
+		QPtrListIterator<DWEntry> it(list);
+		for(DWEntry *e; (e = it.current()); ++it) {
+			if(e->fd == fd)
+				return e;
+		}
+		return 0;
+	}
+
+#endif
+};
+
+DirWatchPlatform::Private *DirWatchPlatform::Private::self;
+
+#ifdef HAVE_DNOTIFY
+
+DirWatchPlatform::DirWatchPlatform()
+:QObject(0)
+{
+	d = 0;
+}
+
+DirWatchPlatform::~DirWatchPlatform()
+{
+	delete d;
+}
+
+bool DirWatchPlatform::init()
+{
+	Private *p = new Private(this);
+	if(p->ok) {
+		d = p;
+		//printf("DirWatchPlatform::init() ok!\n");
+		return true;
+	}
+	else {
+		delete p;
+		//printf("DirWatchPlatform::init() fail!\n");
+		return false;
+	}
+}
+
+int DirWatchPlatform::addDir(const QString &s)
+{
+	return d->addItem(s);
+}
+
+void DirWatchPlatform::removeDir(int id)
+{
+	d->removeItem(id);
+}
+
+#else
+
+DirWatchPlatform::DirWatchPlatform():QObject(0) {}
+DirWatchPlatform::~DirWatchPlatform() {}
+bool DirWatchPlatform::init() { return false; }
+int DirWatchPlatform::addDir(const QString &) { return -1; }
+void DirWatchPlatform::removeDir(int) {}
+
+#endif
+
+#include"dirwatch_unix.moc"
Index: psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_win.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_win.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dirwatch/dirwatch_win.cpp	(revision 38)
@@ -0,0 +1,278 @@
+/*
+ * dirwatch_unix.cpp - detect changes of directory content
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"dirwatch.h"
+
+#include<qthread.h>
+#include<qptrlist.h>
+#include<qfile.h>
+#include<qdir.h>
+#include<qtimer.h>
+#include<windows.h>
+
+class DWEvent : public QEvent
+{
+public:
+	DWEvent(int _state)
+	:QEvent(QEvent::User)
+	{
+		state = _state;
+	}
+
+	int state;
+};
+
+class DWThread : public QThread
+{
+public:
+	DWThread(QObject *_par, HANDLE _h) : QThread()
+	{
+		par = _par;
+		h = _h;
+	}
+
+	void run()
+	{
+		while(WaitForSingleObject(h, INFINITE) == WAIT_OBJECT_0) {
+			// send a changed event
+			QThread::postEvent(par, new DWEvent(1));
+			if(!FindNextChangeNotification(h)) {
+				// send an error event
+				QThread::postEvent(par, new DWEvent(0));
+				return;
+			}
+		}
+	}
+
+	QObject *par;
+	HANDLE h;
+};
+
+class DWEntry : public QObject
+{
+	Q_OBJECT
+public:
+	static DWEntry * create(const QString &s)
+	{
+		HANDLE h = FindFirstChangeNotificationA(QFile::encodeName(QDir::convertSeparators(s)), TRUE,
+			FILE_NOTIFY_CHANGE_FILE_NAME |
+			FILE_NOTIFY_CHANGE_DIR_NAME |
+			FILE_NOTIFY_CHANGE_ATTRIBUTES |
+			FILE_NOTIFY_CHANGE_SIZE |
+			FILE_NOTIFY_CHANGE_LAST_WRITE |
+			FILE_NOTIFY_CHANGE_SECURITY);
+
+		if(h == INVALID_HANDLE_VALUE)
+			return 0;
+
+		DWEntry *e = new DWEntry;
+		e->dir = s;
+		e->h = h;
+		e->thread = new DWThread(e, h);
+		e->thread->start();
+		return e;
+	}
+
+	~DWEntry()
+	{
+		if(thread) {
+			FindCloseChangeNotification(h);
+			thread->wait();
+			delete thread;
+		}
+	}
+
+signals:
+	void changed();
+
+protected:
+	bool event(QEvent *e)
+	{
+		if(e->type() == QEvent::User) {
+			DWEvent *de = (DWEvent *)e;
+			// error?
+			if(de->state == 0) {
+				thread->wait();
+				delete thread;
+				FindCloseChangeNotification(h);
+				thread = 0;
+			}
+			else {
+				dirty = true;
+				changed();
+			}
+
+			return true;
+		}
+		return false;
+	}
+
+public:
+	QString dir;
+	QValueList<int> idList;
+	bool dirty;
+
+private:
+	DWEntry()
+	{
+		dirty = false;
+	}
+
+	HANDLE h;
+	DWThread *thread;
+};
+
+class DirWatchPlatform::Private : public QObject
+{
+	Q_OBJECT
+public:
+	Private(DirWatchPlatform *_par)
+	{
+		par = _par;
+		list.setAutoDelete(true);
+		connect(&t, SIGNAL(timeout()), this, SLOT(slotNotify()));
+	}
+
+	~Private()
+	{
+		list.clear();
+	}
+
+	QTimer t;
+	DirWatchPlatform *par;
+	QPtrList<DWEntry> list;
+
+	int addItem(const QString &s)
+	{
+		DWEntry *e = findEntryByDir(s);
+		if(!e) {
+			e = DWEntry::create(s);
+			if(!e)
+				return -1;
+			connect(e, SIGNAL(changed()), SLOT(slotChanged()));
+			list.append(e);
+		}
+		int id = getUniqueId();
+		e->idList.append(id);
+		return id;
+	}
+
+	void removeItem(int id)
+	{
+		DWEntry *e = findEntryById(id);
+		if(!e)
+			return;
+		e->idList.remove(id);
+		if(e->idList.isEmpty())
+			list.removeRef(e);
+	}
+
+	int getUniqueId()
+	{
+		for(int n = 0;; ++n) {
+			QPtrListIterator<DWEntry> it(list);
+			bool found = false;
+			for(DWEntry *e; (e = it.current()); ++it) {
+				for(QValueList<int>::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) {
+					if(*idi == n) {
+						found = true;
+						break;
+					}
+				}
+				if(found)
+					break;
+			}
+			if(!found)
+				return n;
+		}
+	}
+
+	DWEntry * findEntryByDir(const QString &s)
+	{
+		QPtrListIterator<DWEntry> it(list);
+		for(DWEntry *e; (e = it.current()); ++it) {
+			if(e->dir == s)
+				return e;
+		}
+		return 0;
+	}
+
+	DWEntry * findEntryById(int id)
+	{
+		QPtrListIterator<DWEntry> it(list);
+		for(DWEntry *e; (e = it.current()); ++it) {
+			for(QValueList<int>::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) {
+				if(*idi == id)
+					return e;
+			}
+		}
+		return 0;
+	}
+
+private slots:
+	void slotChanged()
+	{
+		// use a timer to combine multiple changed events into one
+		if(!t.isActive())
+			t.start(200, true);
+	}
+
+	void slotNotify()
+	{
+		// see who is dirty
+		QPtrListIterator<DWEntry> it(list);
+		for(DWEntry *e; (e = it.current()); ++it) {
+			if(e->dirty) {
+				e->dirty = false;
+				for(QValueList<int>::ConstIterator idi = e->idList.begin(); idi != e->idList.end(); ++idi) {
+					par->triggerDirChanged(*idi);
+				}
+			}
+		}
+	}
+};
+
+DirWatchPlatform::DirWatchPlatform()
+:QObject(0)
+{
+	d = new Private(this);
+}
+
+DirWatchPlatform::~DirWatchPlatform()
+{
+	delete d;
+}
+
+bool DirWatchPlatform::init()
+{
+	return true;
+}
+
+int DirWatchPlatform::addDir(const QString &s)
+{
+	return d->addItem(s);
+}
+
+void DirWatchPlatform::removeDir(int id)
+{
+	d->removeItem(id);
+}
+
+#include"dirwatch_win.moc"
Index: psi/tags/0.10-os2-b1/src/tools/dwtest.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dwtest.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dwtest.cpp	(revision 38)
@@ -0,0 +1,42 @@
+#include"dwtest.h"
+
+#include<qapplication.h>
+#include<qfile.h>
+
+DwTest::DwTest(const QStringList &dirs)
+{
+	for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
+		DirWatch *dw = new DirWatch(*it, this);
+		connect(dw, SIGNAL(changed()), SLOT(dw_changed()));
+	}
+}
+
+void DwTest::dw_changed()
+{
+	DirWatch *dw = (DirWatch *)sender();
+	printf("[%s] changed\n", dw->dir().latin1());
+}
+
+
+int main(int argc, char **argv)
+{
+	QApplication app(argc, argv);
+
+	if(argc < 2) {
+		printf("usage: dwtest dir1 dir2 ...\n\n");
+		return 0;
+	}
+
+	QStringList dirList;
+	for(int n = 1; n < argc; ++n) {
+		QString dir = QFile::decodeName(argv[n]);
+		dirList += dir;
+	}
+
+	DwTest *test = new DwTest(dirList);
+	app.exec();
+	delete test;
+
+	return 0;
+}
+
Index: psi/tags/0.10-os2-b1/src/tools/dwtest.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dwtest.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dwtest.h	(revision 38)
@@ -0,0 +1,18 @@
+#ifndef DWTEST_H
+#define DWTEST_H
+
+#include<qstringlist.h>
+#include"dirwatch.h"
+
+class DwTest : public QObject
+{
+	Q_OBJECT
+public:
+	DwTest(const QStringList &);
+
+private slots:
+	void dw_changed();
+};
+
+#endif
+
Index: psi/tags/0.10-os2-b1/src/tools/dwtest.pro
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/dwtest.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/dwtest.pro	(revision 38)
@@ -0,0 +1,19 @@
+TEMPLATE = app
+TARGET   = dwtest
+CONFIG  += qt thread console
+
+MOC_DIR        = .moc
+OBJECTS_DIR    = .obj
+UI_DIR         = .ui
+
+DEFINES += HAVE_DNOTIFY
+
+CONFIG += dirwatch
+DIRWATCH_CPP = dirwatch
+include($$DIRWATCH_CPP/dirwatch.pri)
+
+INCLUDEPATH += dirwatch
+
+HEADERS += dwtest.h
+SOURCES += dwtest.cpp
+
Index: psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccel.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccel.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccel.pri	(revision 38)
@@ -0,0 +1,16 @@
+globalaccel {
+	HEADERS += $$GLOBALACCEL_CPP/globalaccelman.h
+
+	unix:!mac {
+		SOURCES += $$GLOBALACCEL_CPP/globalaccelman_x11.cpp
+	}
+	win32: {
+		SOURCES += $$GLOBALACCEL_CPP/globalaccelman_win.cpp
+	}
+	os2: {
+		SOURCES += $$GLOBALACCEL_CPP/globalaccelman_pm.cpp
+	}
+	mac: {
+		SOURCES += $$GLOBALACCEL_CPP/globalaccelman_mac.cpp
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman.h	(revision 38)
@@ -0,0 +1,50 @@
+/*
+ * globalaccelman.h - manager for global hotkeys
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_GLOBALACCELMAN_H
+#define CS_GLOBALACCELMAN_H
+
+#include<qobject.h>
+
+class QKeySequence;
+
+class GlobalAccelManager : public QObject
+{
+	Q_OBJECT
+public:
+	GlobalAccelManager();
+	~GlobalAccelManager();
+
+	int setAccel(const QKeySequence &);
+	void removeAccel(int);
+
+signals:
+	void activated(int);
+
+private slots:
+	void triggerActivated(int x) { activated(x); }
+
+private:
+	class Private;
+	friend class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_mac.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_mac.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_mac.cpp	(revision 38)
@@ -0,0 +1,372 @@
+/*
+ * globalaccelman_mac.cpp - manager for global hotkeys
+ * Copyright (C) 2003  Eric Smith
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+// TODO:
+//  - don't invoke hotkey if there is a modal dialog?
+//  - do multi-mapping, like the x11 version
+
+#include"globalaccelman.h"
+
+#include<qkeysequence.h>
+#include<qwidget.h>
+#include<qptrlist.h>
+
+#include <Carbon/Carbon.h>
+
+typedef struct {
+	short kchrID;
+        Str255 KCHRname;
+	short transtable[256];
+} Ascii2KeyCodeTable;
+
+
+
+	/* InitAscii2KeyCodeTable initializes the ascii to key code
+	look up table using the currently active KCHR resource. This
+	routine calls GetResource so it cannot be called at interrupt time.*/
+OSStatus InitAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable);
+
+	/* ValidateAscii2KeyCodeTable verifies that the ascii to key code
+	lookup table is synchronized with the current KCHR resource.  If
+	it is not synchronized, then the table is re-built. This routine calls
+	GetResource so it cannot be called at interrupt time.*/
+OSStatus ValidateAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable,Boolean *wasChanged);
+
+	/* AsciiToKeyCode looks up the ascii character in the key
+	code look up table and returns the virtual key code for that
+	letter.  If there is no virtual key code for that letter, then
+	the value -1 will be returned. */
+short AsciiToKeyCode(Ascii2KeyCodeTable *ttable, short asciiCode);
+
+enum {
+	kTableCountOffset = 256+2,
+	kFirstTableOffset = 256+4,
+	kTableSize = 128
+};
+
+OSStatus InitAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable) {
+	unsigned char *theCurrentKCHR, *ithKeyTable;
+	short count, i, j, resID;
+	Handle theKCHRRsrc;
+	ResType rType;
+	/* set up our table to all minus ones */
+	for (i=0;i<256; i++) ttable->transtable[i] = -1;
+	/* find the current kchr resource ID */
+	ttable->kchrID = (short) GetScriptVariable(smCurrentScript, smScriptKeys);
+	/* get the current KCHR resource */
+	theKCHRRsrc = GetResource('KCHR', ttable->kchrID);
+	if (theKCHRRsrc == NULL) return resNotFound;
+	GetResInfo(theKCHRRsrc,&resID,&rType,ttable->KCHRname);
+	/* dereference the resource */
+	theCurrentKCHR = (unsigned char *)  (*theKCHRRsrc);
+	/* get the count from the resource */
+	count = * (short *) (theCurrentKCHR + kTableCountOffset);
+	/* build inverse table by merging all key tables */
+	for (i=0; i<count; i++) {
+		ithKeyTable = theCurrentKCHR + kFirstTableOffset + (i * kTableSize);
+		for (j=0; j<kTableSize; j++) {
+			if ( ttable->transtable[ ithKeyTable[j] ] == -1)
+				ttable->transtable[ ithKeyTable[j] ] = j;
+		}
+	}
+	/* all done */
+	return noErr;
+}
+
+
+// Should probably call this at some point, in case the user has switched keyboard
+// layouts while we were running.
+OSStatus ValidateAscii2KeyCodeTable(Ascii2KeyCodeTable *ttable,Boolean *wasChanged) {
+	short theID;
+	theID = (short) GetScriptVariable(smCurrentScript, smScriptKeys);
+	if (theID != ttable->kchrID)
+	{
+		*wasChanged=true;
+		return InitAscii2KeyCodeTable(ttable);
+	}
+	else
+	{
+		*wasChanged=false;
+		return noErr;
+	}
+}
+
+short AsciiToKeyCode(Ascii2KeyCodeTable *ttable, short asciiCode) {
+	if (asciiCode >= 0 && asciiCode <= 255)
+		return ttable->transtable[asciiCode];
+	else return false;
+}
+
+
+// Not used.
+char KeyCodeToAscii(short virtualKeyCode) {
+	unsigned long state;
+	long keyTrans;
+	char charCode;
+	Ptr kchr;
+	state = 0;
+	kchr = (Ptr) GetScriptVariable(smCurrentScript, smKCHRCache);
+	keyTrans = KeyTranslate(kchr, virtualKeyCode, &state);
+	charCode = keyTrans;
+	if (!charCode) charCode = (keyTrans>>16);
+	return charCode;
+}
+
+
+EventHotKeyID hotKeyID = {
+	'QtHK',
+	0
+};
+
+
+class MacHotKey
+{
+public:
+	MacHotKey(EventHotKeyRef _ref, int _id)
+	{
+		hot_key_ref = _ref;
+		id = _id;
+	}
+
+	~MacHotKey()
+	{
+		UnregisterEventHotKey(hot_key_ref);
+	}
+
+	EventHotKeyRef hot_key_ref;
+	int id;         // GlobalAccelManager unique ID for this hotkey
+};
+
+
+// The following table is from Apple sample-code.
+// Apple's headers don't appear to define any constants for the virtual key 
+// codes of special keys, but these constants are somewhat documented in the chart at
+// <http://developer.apple.com/techpubs/mac/Text/Text-571.html#MARKER-9-18>
+//
+// The constants on the chartappear to be the same values as are used in Apple's iGetKeys
+// sample.
+// <http://developer.apple.com/samplecode/Sample_Code/Human_Interface_Toolbox/Mac_OS_High_Level_Toolbox/iGetKeys.htm>.
+//
+// See also <http://www.mactech.com/articles/mactech/Vol.04/04.12/Macinkeys/>.
+//
+struct QtKeyMap
+{
+	int qt_key;
+	int mac_key;
+} qt_key_map[] = {
+	{ Qt::Key_Escape,     0x35 },
+	{ Qt::Key_Tab,        0x30 },
+	{ Qt::Key_Backtab,    0 },
+	{ Qt::Key_Backspace,  0x33 },
+	{ Qt::Key_Return,     0x24 },
+	{ Qt::Key_Enter,      0x4c },    // Return & Enter are different on the Mac
+	{ Qt::Key_Insert,     0 },
+	{ Qt::Key_Delete,     0x75 },
+	{ Qt::Key_Pause,      0 },
+	{ Qt::Key_Print,      0 },
+	{ Qt::Key_SysReq,     0 },
+	{ Qt::Key_Clear,      0x47 },
+	{ Qt::Key_Home,       0x73 },
+	{ Qt::Key_End,        0x77 },
+	{ Qt::Key_Left,       0x7b },
+	{ Qt::Key_Up,         0x7e },
+	{ Qt::Key_Right,      0x7c },
+	{ Qt::Key_Down,       0x7d },
+	{ Qt::Key_Prior,      0x74 },  // Page Up
+	{ Qt::Key_Next,       0x79 },   // Page Down
+	{ Qt::Key_Shift,      0x38 },
+	{ Qt::Key_Control,    0x3b },
+	{ Qt::Key_Meta,       0x37 },    // Command
+	{ Qt::Key_Alt,        0x3a },    // Option
+	{ Qt::Key_CapsLock,   57 },
+	{ Qt::Key_NumLock,    0 },
+	{ Qt::Key_ScrollLock, 0 },
+	{ Qt::Key_F1,         0x7a },
+	{ Qt::Key_F2,         0x78 },
+	{ Qt::Key_F3,         0x63 },
+	{ Qt::Key_F4,         0x76 },
+	{ Qt::Key_F5,         0x60 },
+	{ Qt::Key_F6,         0x61 },
+	{ Qt::Key_F7,         0x62 },
+	{ Qt::Key_F8,         0x64 },
+	{ Qt::Key_F9,         0x65 },
+	{ Qt::Key_F10,        0x6d },
+	{ Qt::Key_F11,        0x67 },
+	{ Qt::Key_F12,        0x6f },
+	{ Qt::Key_F13,        0x69 },
+	{ Qt::Key_F14,        0x6b },
+	{ Qt::Key_F15,        0x71 },
+	{ Qt::Key_F16,        0 },
+	{ Qt::Key_F17,        0 },
+	{ Qt::Key_F18,        0 },
+	{ Qt::Key_F19,        0 },
+	{ Qt::Key_F20,        0 },
+	{ Qt::Key_F21,        0 },
+	{ Qt::Key_F22,        0 },
+	{ Qt::Key_F23,        0 },
+	{ Qt::Key_F24,        0 },
+	{ Qt::Key_F25,        0 },
+	{ Qt::Key_F26,        0 },
+	{ Qt::Key_F27,        0 },
+	{ Qt::Key_F28,        0 },
+	{ Qt::Key_F29,        0 },
+	{ Qt::Key_F30,        0 },
+	{ Qt::Key_F31,        0 },
+	{ Qt::Key_F32,        0 },
+	{ Qt::Key_F33,        0 },
+	{ Qt::Key_F34,        0 },
+	{ Qt::Key_F35,        0 },
+	{ Qt::Key_Super_L,    0 },
+	{ Qt::Key_Super_R,    0 },
+	{ Qt::Key_Menu,       0 },
+	{ Qt::Key_Hyper_L,    0 },
+	{ Qt::Key_Hyper_R,    0 },
+	{ Qt::Key_Help,       0x72 },
+	{ Qt::Key_Direction_L,0 },
+	{ Qt::Key_Direction_R,0 },
+
+	{ Qt::Key_unknown,    0 }
+};
+
+
+class GlobalAccelManager::Private
+{
+public:
+	Private(GlobalAccelManager *par)
+	{
+		id_at = 1;
+		list.setAutoDelete(true);
+
+		InitAscii2KeyCodeTable(&key_codes);
+		hot_key_function = NewEventHandlerUPP(HotKeyHandler);
+		EventTypeSpec type;
+		type.eventClass = kEventClassKeyboard;
+		type.eventKind = kEventHotKeyPressed;
+		InstallApplicationEventHandler(hot_key_function, 1, &type, par, NULL);
+	}
+
+	~Private() {
+	}
+
+private:
+	static pascal OSStatus HotKeyHandler(EventHandlerCallRef /*nextHandler*/, EventRef theEvent, void *userData);
+
+public:
+	bool convertKeySequence(const QKeySequence &ks, UInt32 *_mod, UInt32 *_key)
+{
+	int code = ks[0];
+
+	UInt32 mod = 0;
+	if(code & Qt::META)
+		mod |= cmdKey;
+	if(code & Qt::SHIFT)
+		mod |= shiftKey;
+	if(code & Qt::CTRL)
+		mod |= controlKey;
+	if(code & Qt::ALT)
+		mod |= optionKey;
+
+	code &= 0xffff;
+	UInt32 key = 0;
+	for(int n = 0; qt_key_map[n].qt_key != Qt::Key_unknown; ++n) {
+		if(qt_key_map[n].qt_key == code) {
+			key = qt_key_map[n].mac_key;
+			break;
+		}
+	}
+	if (key == 0) {
+		key = AsciiToKeyCode(&key_codes, code & 0xffff);
+	}
+
+	if(_mod)
+		*_mod = mod;
+	if(_key)
+		*_key = key;
+
+	return true;
+}
+
+
+	int insertHotKey(int key, int mod)
+	{
+		EventHotKeyID hotKeyID;
+		hotKeyID.signature = 'QtHK';
+		hotKeyID.id = id_at++;
+		EventHotKeyRef hotKeyRef = 0;
+
+		OSStatus foo = RegisterEventHotKey(key, mod, hotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef);
+		if (foo != 0) {
+			fprintf(stderr, "RegisterEventHotKey(%x,%x) returned %ld\n", key, mod, foo);
+			return 0;
+		}
+
+		MacHotKey *hk = new MacHotKey(hotKeyRef, hotKeyID.id);
+		list.append(hk);
+		return hk->id;
+	}
+
+	
+	int id_at;
+	QPtrList<MacHotKey> list;
+	EventHandlerUPP hot_key_function;
+	Ascii2KeyCodeTable key_codes;
+};
+
+
+// Callback function invoked when the user hits a hot-key.
+pascal OSStatus GlobalAccelManager::Private::HotKeyHandler(EventHandlerCallRef /*nextHandler*/, EventRef theEvent, void *userData)
+{
+	EventHotKeyID hkID;
+	GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(EventHotKeyID), NULL, &hkID);
+	reinterpret_cast<GlobalAccelManager*>(userData)->triggerActivated(hkID.id);
+	return noErr;
+}
+
+
+GlobalAccelManager::GlobalAccelManager()
+{
+	d = new Private(this);
+}
+
+GlobalAccelManager::~GlobalAccelManager()
+{
+	delete d;
+}
+
+int GlobalAccelManager::setAccel(const QKeySequence &ks)
+{
+	
+	UInt32 mod, key;
+	if(!d->convertKeySequence(ks, &mod, &key))
+		return 0;
+
+	return d->insertHotKey(key, mod);
+}
+
+void GlobalAccelManager::removeAccel(int id)
+{
+	QPtrListIterator<MacHotKey> it(d->list);
+	for(MacHotKey *hk = 0; (hk = it.current()); ++it) {
+		if(hk->id == id) {
+			d->list.removeRef(hk);
+			return;
+		}
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_pm.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_pm.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_pm.cpp	(revision 38)
@@ -0,0 +1,55 @@
+/*
+ * globalaccelman_win.cpp - manager for global hotkeys (OS/2 PM)
+ * Copyright (C) 2006  Dmitry A. Kuminov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"globalaccelman.h"
+
+/// @todo (dmik)
+//  For now, it's a dummy class. One of the ways to implement its functionality
+//  is to use the keyboard hook. 
+
+class GlobalAccelManager::Private
+{
+public:
+	Private(GlobalAccelManager *par)
+	{
+		manager = par;
+	}
+
+	GlobalAccelManager *manager;
+};
+
+GlobalAccelManager::GlobalAccelManager()
+{
+	d = new Private(this);
+}
+
+GlobalAccelManager::~GlobalAccelManager()
+{
+	delete d;
+}
+
+int GlobalAccelManager::setAccel(const QKeySequence &ks)
+{
+	return 0;
+}
+
+void GlobalAccelManager::removeAccel(int id)
+{
+}
Index: psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_win.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_win.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_win.cpp	(revision 38)
@@ -0,0 +1,282 @@
+/*
+ * globalaccelman_win.cpp - manager for global hotkeys
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+// TODO:
+//  - don't invoke hotkey if there is a modal dialog?
+//  - do multi-mapping, like the x11 version
+
+#include"globalaccelman.h"
+
+#include<qkeysequence.h>
+#include<qwidget.h>
+#include<qptrlist.h>
+
+#include<windows.h>
+
+// We'll start our hotkeys at offset 0x1000 in the Windows hotkey table (no real reason)
+#define HOTKEY_START 0x1000
+
+struct QT_VK_KEYMAP
+{
+	int key;
+	UINT vk;
+} qt_vk_table[] = {
+	{ Qt::Key_Escape,     VK_ESCAPE },
+	{ Qt::Key_Tab,        VK_TAB },
+	{ Qt::Key_Backtab,    0 },
+	{ Qt::Key_Backspace,  VK_BACK },
+	{ Qt::Key_Return,     VK_RETURN },
+	{ Qt::Key_Enter,      VK_RETURN },
+	{ Qt::Key_Insert,     VK_INSERT },
+	{ Qt::Key_Delete,     VK_DELETE },
+	{ Qt::Key_Pause,      VK_PAUSE },
+	{ Qt::Key_Print,      VK_PRINT },
+	{ Qt::Key_SysReq,     0 },
+	{ Qt::Key_Clear,      VK_CLEAR },
+	{ Qt::Key_Home,       VK_HOME },
+	{ Qt::Key_End,        VK_END },
+	{ Qt::Key_Left,       VK_LEFT },
+	{ Qt::Key_Up,         VK_UP },
+	{ Qt::Key_Right,      VK_RIGHT },
+	{ Qt::Key_Down,       VK_DOWN },
+	{ Qt::Key_Prior,      VK_PRIOR },
+	{ Qt::Key_Next,       VK_NEXT },
+	{ Qt::Key_Shift,      VK_SHIFT },
+	{ Qt::Key_Control,    VK_CONTROL },
+	{ Qt::Key_Meta,       VK_LWIN },
+	{ Qt::Key_Alt,        VK_MENU },
+	{ Qt::Key_CapsLock,   VK_CAPITAL },
+	{ Qt::Key_NumLock,    VK_NUMLOCK },
+	{ Qt::Key_ScrollLock, VK_SCROLL },
+	{ Qt::Key_F1,         VK_F1 },
+	{ Qt::Key_F2,         VK_F2 },
+	{ Qt::Key_F3,         VK_F3 },
+	{ Qt::Key_F4,         VK_F4 },
+	{ Qt::Key_F5,         VK_F5 },
+	{ Qt::Key_F6,         VK_F6 },
+	{ Qt::Key_F7,         VK_F7 },
+	{ Qt::Key_F8,         VK_F8 },
+	{ Qt::Key_F9,         VK_F9 },
+	{ Qt::Key_F10,        VK_F10 },
+	{ Qt::Key_F11,        VK_F11 },
+	{ Qt::Key_F12,        VK_F12 },
+	{ Qt::Key_F13,        VK_F13 },
+	{ Qt::Key_F14,        VK_F14 },
+	{ Qt::Key_F15,        VK_F15 },
+	{ Qt::Key_F16,        VK_F16 },
+	{ Qt::Key_F17,        VK_F17 },
+	{ Qt::Key_F18,        VK_F18 },
+	{ Qt::Key_F19,        VK_F19 },
+	{ Qt::Key_F20,        VK_F20 },
+	{ Qt::Key_F21,        VK_F21 },
+	{ Qt::Key_F22,        VK_F22 },
+	{ Qt::Key_F23,        VK_F23 },
+	{ Qt::Key_F24,        VK_F24 },
+	{ Qt::Key_F25,        0 },
+	{ Qt::Key_F26,        0 },
+	{ Qt::Key_F27,        0 },
+	{ Qt::Key_F28,        0 },
+	{ Qt::Key_F29,        0 },
+	{ Qt::Key_F30,        0 },
+	{ Qt::Key_F31,        0 },
+	{ Qt::Key_F32,        0 },
+	{ Qt::Key_F33,        0 },
+	{ Qt::Key_F34,        0 },
+	{ Qt::Key_F35,        0 },
+	{ Qt::Key_Super_L,    0 },
+	{ Qt::Key_Super_R,    0 },
+	{ Qt::Key_Menu,       0 },
+	{ Qt::Key_Hyper_L,    0 },
+	{ Qt::Key_Hyper_R,    0 },
+	{ Qt::Key_Help,       0 },
+	{ Qt::Key_Direction_L,0 },
+	{ Qt::Key_Direction_R,0 },
+
+	{ Qt::Key_unknown,    0 },
+};
+
+static bool convertKeySequence(const QKeySequence &ks, UINT *_mod, UINT *_key)
+{
+	int code = ks;
+
+	UINT mod = 0;
+	if(code & Qt::META)
+		mod |= MOD_WIN;
+	if(code & Qt::SHIFT)
+		mod |= MOD_SHIFT;
+	if(code & Qt::CTRL)
+		mod |= MOD_CONTROL;
+	if(code & Qt::ALT)
+		mod |= MOD_ALT;
+
+	UINT key = 0;
+	code &= 0xffff;
+	if(code >= 0x20 && code <= 0x7f)
+		key = code;
+	else {
+		for(int n = 0; qt_vk_table[n].key != Qt::Key_unknown; ++n) {
+			if(qt_vk_table[n].key == code) {
+				key = qt_vk_table[n].vk;
+				break;
+			}
+		}
+		if(!key)
+			return false;
+	}
+
+	if(_mod)
+		*_mod = mod;
+	if(_key)
+		*_key = key;
+
+	return true;
+}
+
+class WindowsHotKey
+{
+public:
+	WindowsHotKey(HWND _w, int _win_key_id, int _id)
+	{
+		w = _w;
+		win_key_id = _win_key_id;
+		id = _id;
+	}
+
+	~WindowsHotKey()
+	{
+		UnregisterHotKey(w, win_key_id);
+	}
+
+	HWND w;
+	int win_key_id; // Windows hotkey slot number
+	int id;         // GlobalAccelManager unique ID for this hotkey
+};
+
+class GlobalAccelManager::Private
+{
+public:
+	Private(GlobalAccelManager *par)
+	{
+		manager = par;
+		id_at = 1;
+		hidden = new HiddenWidget(this);
+		list.setAutoDelete(true);
+	}
+
+	~Private()
+	{
+		delete hidden;
+	}
+
+	int getFreeSlot() const
+	{
+		QPtrListIterator<WindowsHotKey> it(list);
+		// find first non-match
+		int n = HOTKEY_START;
+		for(WindowsHotKey *hk = 0; (hk = it.current()); ++it) {
+			if(hk->win_key_id != n)
+				break;
+			++n;
+		}
+		return n;
+	}
+
+	int insertHotKey(int win_key_id)
+	{
+		WindowsHotKey *hk = new WindowsHotKey(hidden->winId(), win_key_id, id_at++);
+		if(win_key_id >= (int)list.count())
+			list.append(hk);
+		else
+			list.insert(win_key_id - HOTKEY_START, hk);
+		return hk->id;
+	}
+
+	void hotkeyActivated(int win_key_id)
+	{
+		QPtrListIterator<WindowsHotKey> it(list);
+		for(WindowsHotKey *hk = 0; (hk = it.current()); ++it) {
+			if(hk->win_key_id == win_key_id)
+				manager->triggerActivated(hk->id);
+		}
+	}
+
+	class HiddenWidget : public QWidget
+	{
+	public:
+		HiddenWidget(Private *_p)
+		{
+			p = _p;
+		}
+
+	protected:
+		bool winEvent(MSG *m)
+		{
+			m->message &= 0xffff;
+			if(m->message == WM_HOTKEY) {
+				p->hotkeyActivated(m->wParam);
+				return true;
+			}
+        		return QWidget::winEvent(m);
+		}
+
+	private:
+		Private *p;
+	};
+
+	GlobalAccelManager *manager;
+	int id_at;
+	HiddenWidget *hidden;
+	QPtrList<WindowsHotKey> list;
+};
+
+GlobalAccelManager::GlobalAccelManager()
+{
+	d = new Private(this);
+}
+
+GlobalAccelManager::~GlobalAccelManager()
+{
+	delete d;
+}
+
+int GlobalAccelManager::setAccel(const QKeySequence &ks)
+{
+	UINT mod, key;
+	if(!convertKeySequence(ks, &mod, &key))
+		return 0;
+
+	int win_key_id = d->getFreeSlot();
+
+	if(!RegisterHotKey(d->hidden->winId(), win_key_id, mod, key))
+		return 0;
+
+	return d->insertHotKey(win_key_id);
+}
+
+void GlobalAccelManager::removeAccel(int id)
+{
+	QPtrListIterator<WindowsHotKey> it(d->list);
+	for(WindowsHotKey *hk = 0; (hk = it.current()); ++it) {
+		if(hk->id == id) {
+			d->list.removeRef(hk);
+			return;
+		}
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_x11.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_x11.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/globalaccel/globalaccelman_x11.cpp	(revision 38)
@@ -0,0 +1,403 @@
+/*
+ * globalaccelman_x11.cpp - manager for global hotkeys
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"globalaccelman.h"
+
+#include<qkeysequence.h>
+#include<qwidget.h>
+#include<qapplication.h>
+
+#include<X11/X.h>
+#include<X11/Xlib.h>
+#include<X11/keysym.h>
+
+#ifdef KeyPress
+// defined by X11 headers
+const int XKeyPress = KeyPress;
+const int XKeyRelease = KeyRelease;
+#undef KeyPress
+#endif
+
+struct QT_XK_KEYGROUP
+{
+	char num;
+	int sym[3];
+};
+
+struct QT_XK_KEYMAP
+{
+	int key;
+	QT_XK_KEYGROUP xk;
+} qt_xk_table[] = {
+	{ Qt::Key_Escape,     {1, { XK_Escape }}},
+	{ Qt::Key_Tab,        {2, { XK_Tab, XK_KP_Tab }}},
+	{ Qt::Key_Backtab,    {1, { XK_ISO_Left_Tab }}},
+	{ Qt::Key_Backspace,  {1, { XK_BackSpace }}},
+	{ Qt::Key_Return,     {1, { XK_Return }}},
+	{ Qt::Key_Enter,      {1, { XK_KP_Enter }}},
+	{ Qt::Key_Insert,     {2, { XK_Insert, XK_KP_Insert }}},
+	{ Qt::Key_Delete,     {3, { XK_Delete, XK_KP_Delete, XK_Clear }}},
+	{ Qt::Key_Pause,      {1, { XK_Pause }}},
+	{ Qt::Key_Print,      {1, { XK_Print }}},
+	{ Qt::Key_SysReq,     {1, { XK_Sys_Req }}},
+	{ Qt::Key_Clear,      {1, { XK_KP_Begin }}},
+	{ Qt::Key_Home,       {2, { XK_Home, XK_KP_Home }}},
+	{ Qt::Key_End,        {2, { XK_End, XK_KP_End }}},
+	{ Qt::Key_Left,       {2, { XK_Left, XK_KP_Left }}},
+	{ Qt::Key_Up,         {2, { XK_Up, XK_KP_Up }}},
+	{ Qt::Key_Right,      {2, { XK_Right, XK_KP_Right }}},
+	{ Qt::Key_Down,       {2, { XK_Down, XK_KP_Down }}},
+	{ Qt::Key_Prior,      {2, { XK_Prior, XK_KP_Prior }}},
+	{ Qt::Key_Next,       {2, { XK_Next, XK_KP_Next }}},
+	{ Qt::Key_Shift,      {3, { XK_Shift_L, XK_Shift_R, XK_Shift_Lock  }}},
+	{ Qt::Key_Control,    {2, { XK_Control_L, XK_Control_R }}},
+	{ Qt::Key_Meta,       {2, { XK_Meta_L, XK_Meta_R }}},
+	{ Qt::Key_Alt,        {2, { XK_Alt_L, XK_Alt_R }}},
+	{ Qt::Key_CapsLock,   {1, { XK_Caps_Lock }}},
+	{ Qt::Key_NumLock,    {1, { XK_Num_Lock }}},
+	{ Qt::Key_ScrollLock, {1, { XK_Scroll_Lock }}},
+	{ Qt::Key_Space,      {2, { XK_space, XK_KP_Space }}},
+	{ Qt::Key_Equal,      {2, { XK_equal, XK_KP_Equal }}},
+	{ Qt::Key_Asterisk,   {2, { XK_asterisk, XK_KP_Multiply }}},
+	{ Qt::Key_Plus,       {2, { XK_plus, XK_KP_Add }}},
+	{ Qt::Key_Comma,      {2, { XK_comma, XK_KP_Separator }}},
+	{ Qt::Key_Minus,      {2, { XK_minus, XK_KP_Subtract }}},
+	{ Qt::Key_Period,     {2, { XK_period, XK_KP_Decimal }}},
+	{ Qt::Key_Slash,      {2, { XK_slash, XK_KP_Divide }}},
+	{ Qt::Key_F1,         {1, { XK_F1 }}},
+	{ Qt::Key_F2,         {1, { XK_F2 }}},
+	{ Qt::Key_F3,         {1, { XK_F3 }}},
+	{ Qt::Key_F4,         {1, { XK_F4 }}},
+	{ Qt::Key_F5,         {1, { XK_F5 }}},
+	{ Qt::Key_F6,         {1, { XK_F6 }}},
+	{ Qt::Key_F7,         {1, { XK_F7 }}},
+	{ Qt::Key_F8,         {1, { XK_F8 }}},
+	{ Qt::Key_F9,         {1, { XK_F9 }}},
+	{ Qt::Key_F10,        {1, { XK_F10 }}},
+	{ Qt::Key_F11,        {1, { XK_F11 }}},
+	{ Qt::Key_F12,        {1, { XK_F12 }}},
+	{ Qt::Key_F13,        {1, { XK_F13 }}},
+	{ Qt::Key_F14,        {1, { XK_F14 }}},
+	{ Qt::Key_F15,        {1, { XK_F15 }}},
+	{ Qt::Key_F16,        {1, { XK_F16 }}},
+	{ Qt::Key_F17,        {1, { XK_F17 }}},
+	{ Qt::Key_F18,        {1, { XK_F18 }}},
+	{ Qt::Key_F19,        {1, { XK_F19 }}},
+	{ Qt::Key_F20,        {1, { XK_F20 }}},
+	{ Qt::Key_F21,        {1, { XK_F21 }}},
+	{ Qt::Key_F22,        {1, { XK_F22 }}},
+	{ Qt::Key_F23,        {1, { XK_F23 }}},
+	{ Qt::Key_F24,        {1, { XK_F24 }}},
+	{ Qt::Key_F25,        {1, { XK_F25 }}},
+	{ Qt::Key_F26,        {1, { XK_F26 }}},
+	{ Qt::Key_F27,        {1, { XK_F27 }}},
+	{ Qt::Key_F28,        {1, { XK_F28 }}},
+	{ Qt::Key_F29,        {1, { XK_F29 }}},
+	{ Qt::Key_F30,        {1, { XK_F30 }}},
+	{ Qt::Key_F31,        {1, { XK_F31 }}},
+	{ Qt::Key_F32,        {1, { XK_F32 }}},
+	{ Qt::Key_F33,        {1, { XK_F33 }}},
+	{ Qt::Key_F34,        {1, { XK_F34 }}},
+	{ Qt::Key_F35,        {1, { XK_F35 }}},
+	{ Qt::Key_Super_L,    {1, { XK_Super_L }}},
+	{ Qt::Key_Super_R,    {1, { XK_Super_R }}},
+	{ Qt::Key_Menu,       {1, { XK_Menu }}},
+	{ Qt::Key_Hyper_L,    {1, { XK_Hyper_L }}},
+	{ Qt::Key_Hyper_R,    {1, { XK_Hyper_R }}},
+	{ Qt::Key_Help,       {1, { XK_Help }}},
+	{ Qt::Key_Direction_L,{0, { 0 }}},
+	{ Qt::Key_Direction_R,{0, { 0 }}},
+
+	{ Qt::Key_unknown,    {0, { 0 }}},
+};
+
+static long alt_mask = 0;
+static long meta_mask = 0;
+static bool haveMods = false;
+
+// adapted from qapplication_x11.cpp
+static void ensureModifiers()
+{
+	if(haveMods)
+		return;
+
+	Display *appDpy = qt_xdisplay();
+	XModifierKeymap *map = XGetModifierMapping(appDpy);
+	if(map) {
+		int i, maskIndex = 0, mapIndex = 0;
+		for (maskIndex = 0; maskIndex < 8; maskIndex++) {
+			for (i = 0; i < map->max_keypermod; i++) {
+				if (map->modifiermap[mapIndex]) {
+					KeySym sym = XKeycodeToKeysym(appDpy, map->modifiermap[mapIndex], 0);
+					if ( alt_mask == 0 && ( sym == XK_Alt_L || sym == XK_Alt_R ) ) {
+						alt_mask = 1 << maskIndex;
+					}
+					if ( meta_mask == 0 && (sym == XK_Meta_L || sym == XK_Meta_R ) ) {
+						meta_mask = 1 << maskIndex;
+					}
+				}
+				mapIndex++;
+			}
+		}
+
+		XFreeModifiermap(map);
+	}
+	else {
+		// assume defaults
+		alt_mask = Mod1Mask;
+		meta_mask = Mod4Mask;
+	}
+
+	haveMods = true;
+}
+
+static bool convertKeySequence(const QKeySequence &ks, unsigned int *_mod, QT_XK_KEYGROUP *_kg)
+{
+	int code = ks;
+	ensureModifiers();
+
+	unsigned int mod = 0;
+	if(code & Qt::META)
+		mod |= meta_mask;
+	if(code & Qt::SHIFT)
+		mod |= ShiftMask;
+	if(code & Qt::CTRL)
+		mod |= ControlMask;
+	if(code & Qt::ALT)
+		mod |= alt_mask;
+
+	QT_XK_KEYGROUP kg;
+	kg.num = 0;
+	code &= 0xffff;
+	// see if it is in our table
+	bool found = false;
+	for(int n = 0; qt_xk_table[n].key != Qt::Key_unknown; ++n) {
+		if(qt_xk_table[n].key == code) {
+			kg = qt_xk_table[n].xk;
+			found = true;
+			break;
+		}
+	}
+	// not found?
+	if(!found) {
+		// try latin1
+		if(code >= 0x20 && code <= 0x7f) {
+			kg.num = 1;
+			kg.sym[0] = code;
+		}
+	}
+	if(!kg.num)
+		return false;
+
+	if(_mod)
+		*_mod = mod;
+	if(_kg)
+		*_kg = kg;
+
+	return true;
+}
+
+class X11HotKey
+{
+public:
+	X11HotKey() {}
+	~X11HotKey()
+	{
+		XUngrabKey(dsp, code, mod, grab);
+	}
+
+	static bool failed;
+	static int XGrabErrorHandler(Display *, XErrorEvent *)
+	{
+		failed = true;
+        	return 0;
+	}
+
+	static X11HotKey * bind(int keysym, unsigned int mod)
+	{
+		Display *dsp = qt_xdisplay();
+		Window grab = qt_xrootwin();
+		int code = XKeysymToKeycode(dsp, keysym);
+
+		// don't grab keys with empty code (because it means just the modifier key)
+		if ( keysym && !code )
+			return 0;
+
+		failed = false;
+		XErrorHandler savedErrorHandler = XSetErrorHandler(XGrabErrorHandler);
+		XGrabKey(dsp, code, mod, grab, False, GrabModeAsync, GrabModeAsync);
+		XSync(dsp, False);
+		XSetErrorHandler(savedErrorHandler);
+		if(failed)
+			return 0;
+		X11HotKey *k = new X11HotKey;
+		k->dsp = dsp;
+		k->grab = grab;
+		k->code = code;
+		k->mod = mod;
+		return k;
+	}
+
+	Display *dsp;     // X11 display
+	Window grab;      // Window (root window, in our case)
+	int code;         // Keycode
+	unsigned int mod; // modifiers (shift/alt/etc)
+};
+
+bool X11HotKey::failed;
+
+class X11HotKeyGroup
+{
+public:
+	X11HotKeyGroup()
+	{
+		list.setAutoDelete(true);
+	}
+
+	~X11HotKeyGroup()
+	{
+	}
+
+	static X11HotKeyGroup * bind(const QT_XK_KEYGROUP &kg, unsigned int mod, int qkey, int id)
+	{
+		QPtrList<X11HotKey> list;
+		for(int n = 0; n < kg.num; ++n) {
+			X11HotKey *k = X11HotKey::bind(kg.sym[n], mod);
+			if(k)
+				list.append(k);
+		}
+		if(list.isEmpty())
+			return 0;
+		X11HotKeyGroup *h = new X11HotKeyGroup;
+		h->list = list;
+		h->qkey = qkey;
+		h->id = id;
+		return h;
+	}
+
+	QPtrList<X11HotKey> list; // Associated X11HotKeys
+	int qkey;                 // Qt keycode (with modifiers)
+	int id;                   // GlobalAccelManager unique ID for this hotkey
+};
+
+class GlobalAccelManager::Private
+{
+public:
+	Private(GlobalAccelManager *_man)
+	{
+		man = _man;
+		id_at = 1;
+		list.setAutoDelete(true);
+		f = new Filter(this);
+		qApp->installEventFilter(f);
+	}
+
+	~Private()
+	{
+		qApp->removeEventFilter(f);
+		delete f;
+	}
+
+	bool isThisYours(int qkey)
+	{
+		QPtrListIterator<X11HotKeyGroup> it(list);
+		for(X11HotKeyGroup *k; (k = it.current()); ++it) {
+			if(k->qkey == qkey) {
+				man->triggerActivated(k->id);
+				return true;
+			}
+		}
+		return false;
+	}
+
+	class Filter : public QObject
+	{
+	public:
+		Filter(Private *_p)
+		{
+			p = _p;
+		}
+
+	protected:
+		bool eventFilter(QObject *o, QEvent *e)
+		{
+			if(e->type() == QEvent::KeyPress) {
+				QKeyEvent *k = (QKeyEvent *)e;
+				int qkey = k->key();
+				if(k->state() & ShiftButton)
+					qkey |= Qt::SHIFT;
+				if(k->state() & ControlButton)
+					qkey |= Qt::CTRL;
+				if(k->state() & AltButton)
+					qkey |= Qt::ALT;
+				if(k->state() & MetaButton)
+					qkey |= Qt::META;
+
+				if(p->isThisYours(qkey))
+					return true;
+			}
+			return QObject::eventFilter(o, e);
+		}
+
+	private:
+		Private *p;
+	};
+
+	GlobalAccelManager *man;
+	int id_at;
+	Filter *f;
+	QPtrList<X11HotKeyGroup> list;
+};
+
+GlobalAccelManager::GlobalAccelManager()
+{
+	d = new Private(this);
+}
+
+GlobalAccelManager::~GlobalAccelManager()
+{
+	delete d;
+}
+
+int GlobalAccelManager::setAccel(const QKeySequence &ks)
+{
+	QT_XK_KEYGROUP kg;
+	unsigned int mod;
+	if(!convertKeySequence(ks, &mod, &kg))
+		return 0;
+
+	X11HotKeyGroup *h = X11HotKeyGroup::bind(kg, mod, ks, d->id_at++);
+	if(!h)
+		return 0;
+	d->list.append(h);
+
+	return h->id;
+}
+
+void GlobalAccelManager::removeAccel(int id)
+{
+	QPtrListIterator<X11HotKeyGroup> it(d->list);
+	for(X11HotKeyGroup *hk = 0; (hk = it.current()); ++it) {
+		if(hk->id == id) {
+			d->list.removeRef(hk);
+			return;
+		}
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/growlnotifier/ChangeLog
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/growlnotifier/ChangeLog	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/growlnotifier/ChangeLog	(revision 38)
@@ -0,0 +1,9 @@
+2005-05-05, Remko Troncon <remko@psi-im.org>
+	* Created a more extensive example
+	* Added timeout signal (new in Growl 0.7)
+
+2005-04-15, Remko Troncon <remko@psi-im.org>
+	* Implemented the click notification
+	
+2005-04-11, Remko Troncon <remko@psi-im.org>
+	* Start
Index: psi/tags/0.10-os2-b1/src/tools/growlnotifier/growlnotifier.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/growlnotifier/growlnotifier.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/growlnotifier/growlnotifier.cpp	(revision 38)
@@ -0,0 +1,331 @@
+/*
+ * growlnotifier.cpp - A simple Qt interface to Growl
+ *
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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.
+ *
+ * You can also redistribute and/or modify this program under the
+ * terms of the Psi License, specified in the accompanied COPYING
+ * file, as published by the Psi Project; either dated January 1st,
+ * 2005, 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+/*
+ * TODO:
+ * - Write a destructor, which CFReleases all datastructures
+ * - Make the signal/slot work when Growl is fixed
+ **/ 
+
+extern "C" {
+	#include <CoreFoundation/CoreFoundation.h>
+	#include <Growl/Growl.h>
+}
+
+#include <qstringlist.h>
+#include <qpixmap.h>
+#include <qbuffer.h>
+
+//#include <sys/types.h>
+//#include <unistd.h>
+
+#include "growlnotifier.h"
+
+//------------------------------------------------------------------------------ 
+
+/*!
+ * A class for emitting a clicked signal to the interested party.
+ */
+class GrowlNotifierSignaler : public QObject
+{
+	Q_OBJECT
+
+public: 
+	GrowlNotifierSignaler() { };
+	void emitNotificationClicked(void* context) { emit notificationClicked(context); }
+	void emitNotificationTimeout(void* context) { emit notificationTimedOut(context); }
+
+signals:
+	void notificationClicked(void*);
+	void notificationTimedOut(void*);
+};
+
+//------------------------------------------------------------------------------ 
+
+/*!
+ * Converts a QString to a CoreFoundation string, preserving Unicode.
+ *
+ * \param s the string to be converted.
+ * \return a reference to a CoreFoundation string.
+ */
+static CFStringRef qString2CFString(const QString& s)
+{
+	if (s.isNull())
+		return 0;
+	
+	ushort* buffer = new ushort[s.length()];
+	for (unsigned int i = 0; i < s.length(); ++i)
+		buffer[i] = s[i].unicode();
+	CFStringRef result = CFStringCreateWithBytes ( NULL, 
+		(UInt8*) buffer, 
+		s.length()*sizeof(ushort),
+		kCFStringEncodingUnicode, false);
+
+	delete buffer;
+	return result;
+}
+
+//------------------------------------------------------------------------------ 
+
+/*!
+ * Retrieves the values from the context.
+ *
+ * \param context the context
+ * \param receiver the receiving object which will be signaled when the
+ *	notification is clicked. May be NULL.
+ * \param clicked_slot the slot to be signaled when the notification is clicked.
+ * \param timeout_slot the slot to be signaled when the notification isn't clicked.
+ * \param context the context which will be passed back to the slot
+ *	May be NULL.
+ */
+void getContext( CFPropertyListRef context, GrowlNotifierSignaler** signaler, const QObject** receiver, const char** clicked_slot, const char** timeout_slot, void** qcontext/*, pid_t* pid*/)
+{
+	CFDataRef data;
+
+	if (signaler) {
+		data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 0);
+		CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) signaler);
+	}
+
+	if (receiver){
+		data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 1);
+		CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) receiver);
+	}
+	
+	if (clicked_slot) {
+		data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 2);
+		CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) clicked_slot);
+	}
+	
+	if (timeout_slot) {
+		data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 3);
+		CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) timeout_slot);
+	}
+	
+	if (qcontext) {
+		data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 4);
+		CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) qcontext);
+	}
+
+	//if (pid) {
+	//	data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) context, 5);
+	//	CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8*) pid);
+	//}
+}
+
+//------------------------------------------------------------------------------ 
+
+/*!
+ * Creates a context for a notification, which will be sent back by Growl
+ * when a notification is clicked.
+ *
+ * \param receiver the receiving object which will be signaled when the
+ *	notification is clicked. May be NULL.
+ * \param clicked_slot the slot to be signaled when the notification is clicked.
+ * \param timeout_slot the slot to be signaled when the notification isn't clicked.
+ * \param context the context which will be passed back to the slot
+ *	May be NULL.
+ * \return the context
+ */
+CFPropertyListRef createContext( GrowlNotifierSignaler* signaler, const QObject* receiver, const char* clicked_slot, const char* timeout_slot, void* qcontext /*, pid_t pid*/)
+{
+	CFDataRef context[5];
+
+	context[0] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &signaler, sizeof(GrowlNotifierSignaler*));
+	context[1] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &receiver, sizeof(const QObject *));
+	context[2] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &clicked_slot, sizeof(const char*));
+	context[3] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &timeout_slot, sizeof(const char*));
+	context[4] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &qcontext, sizeof(void*));
+	//context[5] = CFDataCreate(kCFAllocatorDefault, (const UInt8*) &pid, sizeof(pid_t));
+
+	CFArrayRef array = CFArrayCreate( kCFAllocatorDefault, 
+                (const void **)context, 5, &kCFTypeArrayCallBacks );
+
+	// Cleaning up
+	CFRelease(context[0]);
+	CFRelease(context[1]);
+	CFRelease(context[2]);
+	CFRelease(context[3]);
+	CFRelease(context[4]);
+	//CFRelease(context[5]);
+
+	return array;
+}
+
+//------------------------------------------------------------------------------ 
+
+/*!
+ * The callback function, used by Growl to notify that a notification was
+ * clicked.
+ * \param context the context of the notification
+ */
+void notification_clicked(CFPropertyListRef context)
+{
+	GrowlNotifierSignaler* signaler;
+	const QObject* receiver;
+	const char* slot;
+	void* qcontext;
+	//pid_t pid;
+
+	getContext(context, &signaler, &receiver, &slot, 0, &qcontext/*, &pid*/);
+	
+	//if (pid == getpid()) {
+	QObject::connect(signaler,SIGNAL(notificationClicked(void*)),receiver,slot);
+	signaler->emitNotificationClicked(qcontext);
+	QObject::disconnect(signaler,SIGNAL(notificationClicked(void*)),receiver,slot);
+	//}
+}
+
+//------------------------------------------------------------------------------ 
+
+/*!
+ * The callback function, used by Growl to notify that a notification has
+ * timed out.
+ * \param context the context of the notification
+ */
+void notification_timeout(CFPropertyListRef context)
+{
+	GrowlNotifierSignaler* signaler;
+	const QObject* receiver;
+	const char* slot;
+	void* qcontext;
+	//pid_t pid;
+
+	getContext(context, &signaler, &receiver, 0, &slot, &qcontext /*, &pid*/);
+	
+	//if (pid == getpid()) {
+	QObject::connect(signaler,SIGNAL(notificationTimedOut(void*)),receiver,slot);
+	signaler->emitNotificationTimeout(qcontext);
+	QObject::disconnect(signaler,SIGNAL(notificationTimedOut(void*)),receiver,slot);
+	//}
+}
+
+//------------------------------------------------------------------------------ 
+
+/*!
+ * Constructs a GrowlNotifier.
+ *
+ * \param notifications the list names of all notifications that can be sent
+ *	by this notifier.
+ * \param default_notifications the list of names of the notifications that
+ *  should be enabled by default.
+ * \param app the name of the application under which the notifier should 
+ *	register with growl.
+ */
+GrowlNotifier::GrowlNotifier(
+	const QStringList& notifications, const QStringList& default_notifications,
+	const QString& app)
+{
+	// Initialize signaler
+	signaler_ = new GrowlNotifierSignaler();
+
+	// All Notifications
+	QStringList::ConstIterator it;
+	CFMutableArrayRef allNotifications = CFArrayCreateMutable(
+		kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+	for ( it = notifications.begin(); it != notifications.end(); ++it ) 
+		CFArrayAppendValue(allNotifications, qString2CFString(*it));
+
+	// Default Notifications
+	CFMutableArrayRef defaultNotifications = CFArrayCreateMutable(
+		kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+	for ( it = default_notifications.begin(); it != default_notifications.end(); ++it ) 
+		CFArrayAppendValue(defaultNotifications, qString2CFString(*it));
+	
+	// Initialize delegate
+	InitGrowlDelegate(&delegate_);
+	if (!app.isEmpty())
+		delegate_.applicationName = qString2CFString(app);
+	CFTypeRef keys[] = { GROWL_NOTIFICATIONS_ALL, GROWL_NOTIFICATIONS_DEFAULT };
+	CFTypeRef values[] = { allNotifications, defaultNotifications };
+	delegate_.registrationDictionary = CFDictionaryCreate(
+		kCFAllocatorDefault, keys, values, 2, 
+		&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+	delegate_.growlNotificationWasClicked = &notification_clicked;
+	delegate_.growlNotificationTimedOut = &notification_timeout;
+
+	// Register with Growl
+	Growl_SetDelegate(&delegate_);
+}
+	
+
+/*!
+ * Sends a notification to Growl.
+ *
+ * \param name the registered name of the notification.
+ * \param title the title for the notification.
+ * \param description the description of the notification.
+ * \param icon the icon of the notification.
+ * \param sticky whether the notification should be sticky (i.e. require a 
+ *	click to discard.
+ * \param receiver the receiving object which will be signaled when the
+ *	notification is clicked. May be NULL.
+ * \param slot the slot to be signaled when the notification is clicked.
+ * \param context the context which will be passed back to the slot
+ *	May be NULL.
+ */
+void GrowlNotifier::notify(const QString& name, const QString& title, 
+	const QString& description, const QPixmap& p, bool sticky, 
+	const QObject* receiver, 
+	const char* clicked_slot, const char* timeout_slot, 
+	void* qcontext)
+{
+	// Convert the image if necessary
+	CFDataRef icon = 0;
+	if (!p.isNull()) {
+		QByteArray img_data;
+		QBuffer buffer(img_data);
+		buffer.open(IO_WriteOnly);
+		p.save(&buffer, "PNG");
+		icon = CFDataCreate( NULL, (UInt8*) img_data.data(), img_data.size());
+	}
+
+	// Convert strings
+	CFStringRef cf_title = qString2CFString(title);
+	CFStringRef cf_description = qString2CFString(description);
+	CFStringRef cf_name = qString2CFString(name);
+
+	// Do notification
+	CFPropertyListRef context = createContext(signaler_, receiver, clicked_slot, timeout_slot, qcontext/*, getpid()*/);
+	Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext(
+		cf_title, cf_description, cf_name, icon, 0, sticky, context);
+	
+	// Release intermediary datastructures
+	CFRelease(context);
+	if (icon) 
+		CFRelease(icon);
+	if (cf_title) 
+		CFRelease(cf_title);
+	if (cf_description) 
+		CFRelease(cf_description);
+	if (cf_name) 
+		CFRelease(cf_name);
+}
+
+//----------------------------------------------------------------------------- 
+
+#include "growlnotifier.moc"
Index: psi/tags/0.10-os2-b1/src/tools/growlnotifier/growlnotifier.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/growlnotifier/growlnotifier.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/growlnotifier/growlnotifier.h	(revision 38)
@@ -0,0 +1,63 @@
+/*
+ * growlnotifier.h - A simple Qt interface to Growl
+ *
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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.
+ *
+ * You can also redistribute and/or modify this program under the
+ * terms of the Psi License, specified in the accompanied COPYING
+ * file, as published by the Psi Project; either dated January 1st,
+ * 2005, 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef GROWLNOTIFIER_H
+#define GROWLNOTIFIER_H
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Growl/Growl.h>
+
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+class GrowlNotifierSignaler;
+
+/*!
+ * A simple interface to Growl.
+ */
+class GrowlNotifier
+{
+public:
+	GrowlNotifier(
+		const QStringList& notifications, 
+		const QStringList& default_notifications,
+		const QString& app_name = "");
+	
+	void notify(const QString& name, const QString& title, 
+		const QString& description, const QPixmap& icon = QPixmap(), 
+		bool sticky = false, const QObject* receiver = 0, 
+		const char* clicked_slot = 0, const char* timeout_slot = 0,
+		void* context = 0);
+
+private:
+	struct Growl_Delegate delegate_;
+	
+	GrowlNotifierSignaler* signaler_;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/growlnotifier/growlnotifier.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/growlnotifier/growlnotifier.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/growlnotifier/growlnotifier.pri	(revision 38)
@@ -0,0 +1,5 @@
+growlnotifier {
+	HEADERS += $$GROWLNOTIFIER_CPP/growlnotifier.h
+	SOURCES += $$GROWLNOTIFIER_CPP/growlnotifier.cpp
+	QMAKE_LFLAGS += -framework Growl -framework CoreFoundation
+}
Index: psi/tags/0.10-os2-b1/src/tools/growlnotifier/growltest.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/growlnotifier/growltest.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/growlnotifier/growltest.cpp	(revision 38)
@@ -0,0 +1,126 @@
+/*
+ * growltest.cpp: A test program for the GrowlNotifier class
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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.
+ *
+ * You can also redistribute and/or modify this program under the
+ * terms of the Psi License, specified in the accompanied COPYING
+ * file, as published by the Psi Project; either dated January 1st,
+ * 2005, 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "growlnotifier.h"
+
+#include <qstringlist.h>
+#include <qapplication.h>
+#include <qmessagebox.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qvbox.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+
+class GrowlTestWidget : public QWidget
+{
+	Q_OBJECT
+
+public:
+    GrowlTestWidget( QWidget *parent=0, const char *name=0 );
+
+public slots:
+	void do_notification1();
+	void do_notification2();
+	void notification_clicked();
+
+private:
+	QLineEdit *text, *title;
+	QCheckBox* sticky;
+	GrowlNotifier* growlNotifier;
+};
+
+
+GrowlTestWidget::GrowlTestWidget( QWidget *parent, const char *name ) : QWidget(parent, name)
+{
+	// Initialize widgets
+	QGridLayout *layout = new QGridLayout(this,4,2);
+
+	layout->addWidget(new QLabel("Title",this),0,0);
+	title = new QLineEdit(this);
+	title->setText("My Text");
+	layout->addWidget(title,0,1);
+
+	layout->addWidget(new QLabel("Text",this),1,0);
+	text = new QLineEdit(this);
+	text->setText("My Description");
+	layout->addWidget(text,1,1);
+
+	//layout->addWidget(new QLabel("Sticky",this),2,0);
+	//sticky = new QCheckBox(this);
+	//sticky->setTristate();
+	//layout->addWidget(sticky,2,1);
+	
+    QPushButton *notification1 = new QPushButton( "Notification 1", this );
+	connect(notification1, SIGNAL(clicked()), SLOT(do_notification1()));
+	layout->addWidget(notification1,3,0);
+
+	QPushButton *notification2 = new QPushButton( "Notification 2", this );
+	connect(notification2, SIGNAL(clicked()), SLOT(do_notification2()));
+	layout->addWidget(notification2,3,1);
+	
+	// Initialize GrowlNotifier
+	QStringList nots, defaults;
+	nots << "Notification 1" << "Notification 2";
+	defaults << "Notification 1";
+	growlNotifier = new GrowlNotifier(nots, defaults, "GrowlNotifierTest");
+}
+
+int main( int argc, char **argv )
+{
+    QApplication a( argc, argv );
+    GrowlTestWidget w;
+    a.setMainWidget( &w );
+    w.show();
+    return a.exec();
+}
+	
+void GrowlTestWidget::do_notification1()
+{
+	//if (sticky->state() != QButton::NoChange) {
+		growlNotifier->notify("Notification 1", title->text(), text->text(), QPixmap(), sticky->isChecked(), this, SLOT(notification_clicked()));
+	//}
+	//else {
+	//	growlNotifier->notify("Notification 1", title->text(), text->text(), QPixmap());
+	//}
+}
+
+void GrowlTestWidget::do_notification2()
+{
+	//if (sticky->state() != QButton::NoChange) {
+		growlNotifier->notify("Notification 2", title->text(), text->text(), QPixmap(), sticky->isChecked(), this, SLOT(notification_clicked()));
+	//}
+	//else {
+	//	growlNotifier->notify("Notification 2", title->text(), text->text(), QPixmap());
+	//}
+}
+
+void GrowlTestWidget::notification_clicked()
+{
+	QMessageBox::information(0, "Information", "Notification was clicked\n");
+}
+
+#include "growltest.moc"
Index: psi/tags/0.10-os2-b1/src/tools/growlnotifier/growltest.pro
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/growlnotifier/growltest.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/growlnotifier/growltest.pro	(revision 38)
@@ -0,0 +1,6 @@
+TEMPLATE = app
+
+HEADERS += growlnotifier.h
+SOURCES += growlnotifier.cpp growltest.cpp
+
+QMAKE_LFLAGS += -framework Growl -framework CoreFoundation
Index: psi/tags/0.10-os2-b1/src/tools/idle/idle.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/idle.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/idle.cpp	(revision 38)
@@ -0,0 +1,140 @@
+/*
+ * idle.cpp - detect desktop idle time
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"idle.h"
+
+#include<qcursor.h>
+#include<qdatetime.h>
+#include<qtimer.h>
+
+static IdlePlatform *platform = 0;
+static int platform_ref = 0;
+
+class Idle::Private
+{
+public:
+	Private() {}
+
+	QPoint lastMousePos;
+	QDateTime idleSince;
+
+	bool active;
+	int idleTime;
+	QDateTime startTime;
+	QTimer checkTimer;
+};
+
+Idle::Idle()
+{
+	d = new Private;
+	d->active = false;
+	d->idleTime = 0;
+
+	// try to use platform idle
+	if(!platform) {
+		IdlePlatform *p = new IdlePlatform;
+		if(p->init())
+			platform = p;
+		else
+			delete p;
+	}
+	if(platform)
+		++platform_ref;
+
+	connect(&d->checkTimer, SIGNAL(timeout()), SLOT(doCheck()));
+}
+
+Idle::~Idle()
+{
+	if(platform) {
+		--platform_ref;
+		if(platform_ref == 0) {
+			delete platform;
+			platform = 0;
+		}
+	}
+	delete d;
+}
+
+bool Idle::isActive() const
+{
+	return d->active;
+}
+
+bool Idle::usingPlatform() const
+{
+	return (platform ? true: false);
+}
+
+void Idle::start()
+{
+	d->startTime = QDateTime::currentDateTime();
+
+	if(!platform) {
+		// generic idle
+		d->lastMousePos = QCursor::pos();
+		d->idleSince = QDateTime::currentDateTime();
+	}
+
+	// poll every second (use a lower value if you need more accuracy)
+	d->checkTimer.start(1000);
+}
+
+void Idle::stop()
+{
+	d->checkTimer.stop();
+}
+
+void Idle::doCheck()
+{
+	int i;
+	if(platform)
+		i = platform->secondsIdle();
+	else {
+		QPoint curMousePos = QCursor::pos();
+		QDateTime curDateTime = QDateTime::currentDateTime();
+		if(d->lastMousePos != curMousePos) {
+			d->lastMousePos = curMousePos;
+			d->idleSince = curDateTime;
+		}
+		i = d->idleSince.secsTo(curDateTime);
+	}
+
+	// set 'beginIdle' to the beginning of the idle time (by backtracking 'i' seconds from now)
+	QDateTime beginIdle = QDateTime::currentDateTime().addSecs(-i);
+
+	// set 't' to hold the number of seconds between 'beginIdle' and 'startTime'
+	int t = beginIdle.secsTo(d->startTime);
+
+	// beginIdle later than (or equal to) startTime?
+	if(t <= 0) {
+		// scoot ourselves up to the new idle start
+		d->startTime = beginIdle;
+	}
+	// beginIdle earlier than startTime?
+	else if(t > 0) {
+		// do nothing
+	}
+
+	// how long have we been idle?
+	int idleTime = d->startTime.secsTo(QDateTime::currentDateTime());
+
+	secondsIdle(idleTime);
+}
Index: psi/tags/0.10-os2-b1/src/tools/idle/idle.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/idle.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/idle.h	(revision 38)
@@ -0,0 +1,65 @@
+/*
+ * idle.h - detect desktop idle time
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef IDLE_H
+#define IDLE_H
+
+#include<qobject.h>
+
+class IdlePlatform;
+
+class Idle : public QObject
+{
+	Q_OBJECT
+public:
+	Idle();
+	~Idle();
+
+	bool isActive() const;
+	bool usingPlatform() const;
+	void start();
+	void stop();
+
+signals:
+	void secondsIdle(int);
+
+private slots:
+	void doCheck();
+
+private:
+	class Private;
+	Private *d;
+};
+
+class IdlePlatform
+{
+public:
+	IdlePlatform();
+	~IdlePlatform();
+
+	bool init();
+	int secondsIdle();
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/idle/idle.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/idle.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/idle.pri	(revision 38)
@@ -0,0 +1,17 @@
+idle {
+	HEADERS += $$IDLE_CPP/idle.h
+	SOURCES += $$IDLE_CPP/idle.cpp
+
+	unix:!mac {
+		SOURCES += $$IDLE_CPP/idle_x11.cpp
+	}
+	win32: {
+		SOURCES += $$IDLE_CPP/idle_win.cpp
+	}
+	os2: {
+		SOURCES += $$IDLE_CPP/idle_pm.cpp
+	}
+	mac: {
+		SOURCES += $$IDLE_CPP/idle_mac.cpp
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/idle/idle_mac.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/idle_mac.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/idle_mac.cpp	(revision 38)
@@ -0,0 +1,160 @@
+/*
+ * idle_mac.cpp - detect desktop idle time
+ * Copyright (C) 2003  Tarkvara Design Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"idle.h"
+#include <Carbon/Carbon.h>
+
+
+// Why does Apple have to make this so complicated?
+static OSStatus LoadFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr) {
+	OSStatus  err;
+	FSRef   frameworksFolderRef;
+	CFURLRef baseURL;
+	CFURLRef bundleURL;
+ 
+	if ( bundlePtr == nil ) return( -1 );
+ 
+	*bundlePtr = nil;
+ 
+	baseURL = nil;
+	bundleURL = nil;
+ 
+	err = FSFindFolder(kOnAppropriateDisk, kFrameworksFolderType, true, &frameworksFolderRef);
+	if (err == noErr) {
+		baseURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &frameworksFolderRef);
+		if (baseURL == nil) {
+			err = coreFoundationUnknownErr;
+		}
+	}
+	if (err == noErr) {
+		bundleURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, framework, false);
+		if (bundleURL == nil) {
+			err = coreFoundationUnknownErr;
+		}
+	}
+	if (err == noErr) {
+		*bundlePtr = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL);
+		if (*bundlePtr == nil) {
+			err = coreFoundationUnknownErr;
+		}
+	}
+	if (err == noErr) {
+		if ( ! CFBundleLoadExecutable( *bundlePtr ) ) {
+			err = coreFoundationUnknownErr;
+		}
+	}
+
+	// Clean up.
+	if (err != noErr && *bundlePtr != nil) {
+		CFRelease(*bundlePtr);
+		*bundlePtr = nil;
+	}
+	if (bundleURL != nil) {
+		CFRelease(bundleURL);
+	} 
+	if (baseURL != nil) {
+		CFRelease(baseURL);
+	} 
+
+	return err;
+}
+
+
+class IdlePlatform::Private {
+public:
+	EventLoopTimerRef mTimerRef;
+	int mSecondsIdle;
+
+	Private() : mTimerRef(0), mSecondsIdle(0) {}
+
+	static pascal void IdleTimerAction(EventLoopTimerRef, EventLoopIdleTimerMessage inState, void* inUserData);
+
+};
+
+
+pascal void IdlePlatform::Private::IdleTimerAction(EventLoopTimerRef, EventLoopIdleTimerMessage inState, void* inUserData) {
+	switch (inState) {
+		case kEventLoopIdleTimerStarted:
+		case kEventLoopIdleTimerStopped:
+    		// Get invoked with this constant at the start of the idle period,
+			// or whenever user activity cancels the idle.
+		   ((IdlePlatform::Private*)inUserData)->mSecondsIdle = 0;
+			break;
+		case kEventLoopIdleTimerIdling:
+			// Called every time the timer fires (i.e. every second).
+		   ((IdlePlatform::Private*)inUserData)->mSecondsIdle++;
+			break;
+	}
+}
+
+
+IdlePlatform::IdlePlatform() {
+	d = new Private();
+}
+
+IdlePlatform::~IdlePlatform() {
+	RemoveEventLoopTimer(d->mTimerRef);
+	delete d;
+}
+
+
+// Typedef for the function we're getting back from CFBundleGetFunctionPointerForName.
+typedef OSStatus (*InstallEventLoopIdleTimerPtr)(EventLoopRef inEventLoop,
+																 EventTimerInterval   inFireDelay,
+																 EventTimerInterval   inInterval,
+																 EventLoopIdleTimerUPP    inTimerProc,
+																 void *               inTimerData,
+																 EventLoopTimerRef *  outTimer);
+
+
+bool IdlePlatform::init() {
+	// May already be init'ed.
+	if (d->mTimerRef) {
+		return true;
+	}
+
+	// According to the docs, InstallEventLoopIdleTimer is new in 10.2.
+	// According to the headers, it has been around since 10.0.
+	// One of them is lying.  We'll play it safe and weak-link the function.
+
+	// Load the "Carbon.framework" bundle.
+	CFBundleRef carbonBundle;
+	if (LoadFrameworkBundle( CFSTR("Carbon.framework"), &carbonBundle ) != noErr) {
+		return false;
+	}
+ 
+	// Load the Mach-O function pointers for the routine we will be using.
+	InstallEventLoopIdleTimerPtr myInstallEventLoopIdleTimer = (InstallEventLoopIdleTimerPtr)CFBundleGetFunctionPointerForName(carbonBundle, CFSTR("InstallEventLoopIdleTimer"));
+	if (myInstallEventLoopIdleTimer == 0) {
+		return false;
+	}
+
+	EventLoopIdleTimerUPP timerUPP = NewEventLoopIdleTimerUPP(Private::IdleTimerAction);
+	if ((*myInstallEventLoopIdleTimer)(GetMainEventLoop(), kEventDurationSecond, kEventDurationSecond, timerUPP, 0, &d->mTimerRef)) {
+		return true;
+	}
+
+	return false;
+}
+
+
+int IdlePlatform::secondsIdle() {
+	return d->mSecondsIdle;
+}
Index: psi/tags/0.10-os2-b1/src/tools/idle/idle_pm.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/idle_pm.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/idle_pm.cpp	(revision 38)
@@ -0,0 +1,51 @@
+/*
+ * idle_pm.cpp - detect desktop idle time (OS/2 PM)
+ * Copyright (C) 2006  Dmitry A. Kuminov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"idle.h"
+
+/// @todo (dmik)
+//  For now, it's a dummy class. One of the ways to implement its functionality
+//  is to use keyboard and mouse hooks.
+
+class IdlePlatform::Private
+{
+};
+
+IdlePlatform::IdlePlatform()
+{
+	d = new Private;
+}
+
+IdlePlatform::~IdlePlatform()
+{
+	delete d;
+}
+
+bool IdlePlatform::init()
+{
+	/// @todo (dmik)
+	return false;
+}
+
+int IdlePlatform::secondsIdle()
+{
+	/// @todo (dmik)
+	return 0;
+}
Index: psi/tags/0.10-os2-b1/src/tools/idle/idle_win.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/idle_win.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/idle_win.cpp	(revision 38)
@@ -0,0 +1,106 @@
+/*
+ * idle_win.cpp - detect desktop idle time
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"idle.h"
+
+#include<qlibrary.h>
+#include<windows.h>
+
+typedef struct tagLASTINPUTINFO {
+	UINT cbSize;
+	DWORD dwTime;
+} LASTINPUTINFO, *PLASTINPUTINFO;
+
+class IdlePlatform::Private
+{
+public:
+	Private()
+	{
+		GetLastInputInfo = 0;
+		IdleUIGetLastInputTime = 0;
+		lib = 0;
+	}
+
+	BOOL (__stdcall * GetLastInputInfo)(PLASTINPUTINFO);
+	DWORD (__stdcall * IdleUIGetLastInputTime)(void);
+	QLibrary *lib;
+};
+
+IdlePlatform::IdlePlatform()
+{
+	d = new Private;
+}
+
+IdlePlatform::~IdlePlatform()
+{
+	delete d->lib;
+	delete d;
+}
+
+bool IdlePlatform::init()
+{
+	if(d->lib)
+		return true;
+	void *p;
+
+	// try to find the built-in Windows 2000 function
+	d->lib = new QLibrary("user32");
+	if(d->lib->load() && (p = d->lib->resolve("GetLastInputInfo"))) {
+		d->GetLastInputInfo = (BOOL (__stdcall *)(PLASTINPUTINFO))p;
+		return true;
+	}
+	else {
+		delete d->lib;
+		d->lib = 0;
+	}
+
+	// fall back on idleui
+	d->lib = new QLibrary("idleui");
+	if(d->lib->load() && (p = d->lib->resolve("IdleUIGetLastInputTime"))) {
+		d->IdleUIGetLastInputTime = (DWORD (__stdcall *)(void))p;
+		return true;
+	}
+	else {
+		delete d->lib;
+		d->lib = 0;
+	}
+
+	return false;
+}
+
+int IdlePlatform::secondsIdle()
+{
+	int i;
+	if(d->GetLastInputInfo) {
+		LASTINPUTINFO li;
+		li.cbSize = sizeof(LASTINPUTINFO);
+		bool ok = d->GetLastInputInfo(&li);
+		if(!ok)
+			return 0;
+		i = li.dwTime;
+	}
+	else if(d->IdleUIGetLastInputTime) {
+		i = d->IdleUIGetLastInputTime();
+	}
+	else
+		return 0;
+
+	return (GetTickCount() - i) / 1000;
+}
Index: psi/tags/0.10-os2-b1/src/tools/idle/idle_x11.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/idle_x11.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/idle_x11.cpp	(revision 38)
@@ -0,0 +1,96 @@
+/*
+ * idle_x11.cpp - detect desktop idle time
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"idle.h"
+
+#ifndef HAVE_XSS
+
+IdlePlatform::IdlePlatform() {}
+IdlePlatform::~IdlePlatform() {}
+bool IdlePlatform::init() { return false; }
+int IdlePlatform::secondsIdle() { return 0; }
+
+#else
+
+#include<qapplication.h>
+
+#include<X11/Xlib.h>
+#include<X11/Xutil.h>
+#include<X11/extensions/scrnsaver.h>
+
+static XErrorHandler old_handler = 0;
+extern "C" int xerrhandler(Display* dpy, XErrorEvent* err)
+{
+	if(err->error_code == BadDrawable)
+		return 0;
+
+	return (*old_handler)(dpy, err);
+}
+
+class IdlePlatform::Private
+{
+public:
+	Private() {}
+
+	XScreenSaverInfo *ss_info;
+};
+
+IdlePlatform::IdlePlatform()
+{
+	d = new Private;
+	d->ss_info = 0;
+}
+
+IdlePlatform::~IdlePlatform()
+{
+	if(d->ss_info)
+		XFree(d->ss_info);
+	if(old_handler) {
+		XSetErrorHandler(old_handler);
+		old_handler = 0;
+	}
+	delete d;
+}
+
+bool IdlePlatform::init()
+{
+	if(d->ss_info)
+		return true;
+
+	old_handler = XSetErrorHandler(xerrhandler);
+
+	int event_base, error_base;
+	if(XScreenSaverQueryExtension(QApplication::desktop()->screen()->x11Display(), &event_base, &error_base)) {
+		d->ss_info = XScreenSaverAllocInfo();
+		return true;
+	}
+	return false;
+}
+
+int IdlePlatform::secondsIdle()
+{
+	if(!d->ss_info)
+		return 0;
+	if(!XScreenSaverQueryInfo(QApplication::desktop()->screen()->x11Display(), qt_xrootwin(), d->ss_info))
+		return 0;
+	return d->ss_info->idle / 1000;
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/idle/win32/Makefile
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/win32/Makefile	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/win32/Makefile	(revision 38)
@@ -0,0 +1,9 @@
+SOURCES = idleui.cpp
+DLL_NAME = idleui.dll
+DEF_NAME = idleui.def
+
+all: $(DLL_NAME)
+
+$(DLL_NAME): $(SOURCES)
+ cl /nologo /MD $(SOURCES) /link /def:$(DEF_NAME) user32.lib kernel32.lib /dll /out:$(DLL_NAME)
+
Index: psi/tags/0.10-os2-b1/src/tools/idle/win32/idleui.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/win32/idleui.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/win32/idleui.cpp	(revision 38)
@@ -0,0 +1,175 @@
+////////////////////////////////////////////////////////////////
+// 2000 Microsoft Systems Journal.
+// If this program works, it was written by Paul DiLascia.
+// If not, I don't know who wrote it.
+// This program compiles with Visual C++ 6.0 on Windows 98
+//
+// See IdleUI.h
+//
+
+#include<windows.h>
+#include<winuser.h>
+#include<assert.h>
+
+#define DLLEXPORT __declspec(dllexport)
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+////////////////
+// The following global data is SHARED among all instances of the DLL
+// (processes); i.e., these are system-wide globals.
+//
+#pragma data_seg (".IdleUI")  // you must define as SHARED in .def
+
+HHOOK g_keyboardHook = NULL;      // one instance for all processes
+HHOOK g_mouseHook = NULL;    // one instance for all processes
+DWORD g_lastInputTick = 0;  // tick time of last input event
+
+/**
+  Last mouse position.
+*/
+POINT g_lastMousePos;
+
+#pragma data_seg ()
+
+/**
+  Flag indicating whether the DLL's owning process is the loading DLL.
+*/
+bool g_isHandleOwner = false;
+
+//
+// Public interface
+//
+
+//////////////////
+// Initialize DLL: install kbd/mouse hooks.
+//
+DLLEXPORT BOOL IdleUIInit()
+{
+  return TRUE;
+}
+
+//////////////////
+// Terminate DLL: remove hooks.
+//
+DLLEXPORT void IdleUITerm()
+{
+}
+
+/////////////////
+// Get tick count of last keyboard or mouse event
+//
+DLLEXPORT DWORD IdleUIGetLastInputTime()
+{
+  return g_lastInputTick;
+}
+
+//
+// Internals
+//
+
+/////////////////
+// Keyboard hook: record tick count
+//
+LRESULT CALLBACK keyboardHookCallback(int code, WPARAM wParam, LPARAM lParam)
+{
+	if (code == HC_ACTION)
+  {
+    g_lastInputTick = GetTickCount();
+	}
+  return ::CallNextHookEx(g_keyboardHook, code, wParam, lParam);
+}
+
+/////////////////
+// Mouse hook: record tick count
+//
+LRESULT CALLBACK mouseHookCallback(int code, WPARAM wParam, LPARAM lParam)
+{
+	if (code == HC_ACTION)
+  {
+    // Update timestamp if event indicates mouse action
+    bool change = false;
+    if (wParam == WM_MOUSEMOVE && lParam != 0)
+    {
+      PMOUSEHOOKSTRUCT mhs = (PMOUSEHOOKSTRUCT) lParam;
+      if (mhs->pt.x != g_lastMousePos.x ||
+        mhs->pt.y != g_lastMousePos.y)
+      {
+        change = true;
+        g_lastMousePos = mhs->pt;
+      }
+    }
+    else
+    {
+      change = true;
+    }
+    if (change)
+    {
+      g_lastInputTick = GetTickCount();
+    }
+	}
+  return ::CallNextHookEx(g_mouseHook, code, wParam, lParam);
+}
+
+void initialize(HINSTANCE module)
+{
+  if (g_keyboardHook == 0)
+  {
+    g_keyboardHook = SetWindowsHookEx(WH_KEYBOARD, keyboardHookCallback, module, 0);
+    g_mouseHook = SetWindowsHookEx(WH_MOUSE, mouseHookCallback, module, 0);
+    g_lastInputTick = GetTickCount();
+    g_isHandleOwner = true;
+  }
+  assert(g_keyboardHook);
+  assert(g_mouseHook);
+}
+
+void shutdown()
+{
+  // Only handle-owning process may unhook
+  if (g_isHandleOwner)
+  {
+    if (g_keyboardHook != 0)
+    {
+      UnhookWindowsHookEx(g_keyboardHook);
+      g_keyboardHook = 0;
+    }
+    if (g_mouseHook != 0)
+    {
+      UnhookWindowsHookEx(g_mouseHook);
+      g_mouseHook = 0;
+    }
+  }
+}
+
+//
+// DLL entry point
+//
+
+BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved)
+{
+  switch (reason)
+  {
+    case DLL_PROCESS_ATTACH:
+    {
+      initialize(module);
+      break;
+    }
+    case DLL_PROCESS_DETACH:
+    {
+      shutdown();
+      break;
+    }
+    case DLL_THREAD_ATTACH:
+    case DLL_THREAD_DETACH:
+    {
+      // Ignore
+      break;
+    }
+  }
+  return TRUE;
+}
Index: psi/tags/0.10-os2-b1/src/tools/idle/win32/idleui.def
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/win32/idleui.def	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/win32/idleui.def	(revision 38)
@@ -0,0 +1,7 @@
+LIBRARY      "IdleUI"
+DESCRIPTION  'IdleUI DLL by Paul DiLascia'
+SECTIONS     .IdleUI READ WRITE SHARED
+
+EXPORTS
+IdleUIGetLastInputTime
+
Index: psi/tags/0.10-os2-b1/src/tools/idle/win32/idleui.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/idle/win32/idleui.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/idle/win32/idleui.h	(revision 38)
@@ -0,0 +1,23 @@
+////////////////////////////////////////////////////////////////
+// 2000 Microsoft Systems Journal. 
+// If this program works, it was written by Paul DiLascia.
+// If not, I don't know who wrote it.
+// This program compiles with Visual C++ 6.0 on Windows 98
+//
+#define DLLIMPORT __declspec(dllimport)
+
+// IdleUI is a DLL that lets you tell when the user interface has been idle
+// for a specified amount of time. The DLL works by installing windows keyboard
+// and mouse hooks. The DLL records the tick count whenever input is received.
+//
+// To use, you must
+// - call IdleUIInit when your app starts up
+// - call IdleUITerm when your app terminates
+// - call IdleUIGetLastInputTime to get the time, and compare this with
+//   the current GetTickCount();
+//
+// See TestIdleUI.cpp for an example of how to use IdleUI
+// 
+DLLIMPORT BOOL IdleUIInit();
+DLLIMPORT void IdleUITerm();
+DLLIMPORT DWORD IdleUIGetLastInputTime();
Index: psi/tags/0.10-os2-b1/src/tools/mac_dock/docktest.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/mac_dock/docktest.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/mac_dock/docktest.cpp	(revision 38)
@@ -0,0 +1,81 @@
+/*
+ * docktest.cpp: A test program for the dock class
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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.
+ *
+ * You can also redistribute and/or modify this program under the
+ * terms of the Psi License, specified in the accompanied COPYING
+ * file, as published by the Psi Project; either dated January 1st,
+ * 2005, 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <qapplication.h>
+#include <qmessagebox.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qvbox.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+
+#include "mac_dock.h"
+
+class DockTestWidget : public QWidget
+{
+	Q_OBJECT
+
+public:
+    DockTestWidget( QWidget *parent=0, const char *name=0 );
+
+public slots:
+	void do_overlay();
+
+private:
+	QLineEdit *text;
+};
+
+
+DockTestWidget::DockTestWidget( QWidget *parent, const char *name ) : QWidget(parent, name)
+{
+	// Initialize widgets
+	QGridLayout *layout = new QGridLayout(this,4,2);
+
+	layout->addWidget(new QLabel("Text",this),0,0);
+	text = new QLineEdit(this);
+	text->setText("1");
+	layout->addWidget(text,0,1);
+
+    QPushButton *overlay = new QPushButton( "Overlay", this );
+	connect(overlay, SIGNAL(clicked()), SLOT(do_overlay()));
+	layout->addWidget(overlay,1,0);
+}
+
+int main( int argc, char **argv )
+{
+    QApplication a( argc, argv );
+    DockTestWidget w;
+    a.setMainWidget( &w );
+    w.show();
+    return a.exec();
+}
+	
+void DockTestWidget::do_overlay()
+{
+	MacDock::overlay(text->text());
+}
+
+#include "docktest.moc"
Index: psi/tags/0.10-os2-b1/src/tools/mac_dock/docktest.pro
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/mac_dock/docktest.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/mac_dock/docktest.pro	(revision 38)
@@ -0,0 +1,6 @@
+TEMPLATE = app
+
+HEADERS += mac_dock.h
+SOURCES += mac_dock.cpp docktest.cpp
+
+QMAKE_LFLAGS += -framework Carbon
Index: psi/tags/0.10-os2-b1/src/tools/mac_dock/mac_dock.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/mac_dock/mac_dock.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/mac_dock/mac_dock.cpp	(revision 38)
@@ -0,0 +1,109 @@
+#include "mac_dock.h"
+
+#include "ApplicationServices/ApplicationServices.h"
+#include "Carbon/Carbon.h"
+
+#define DOCK_FONT_NAME "LucidaGrande-Bold"
+#define DOCK_FONT_SIZE 24
+	
+static NMRec bounceRec;
+
+/*!
+ * Converts a QString to a CoreFoundation string, preserving Unicode.
+ *
+ * \param s the string to be converted.
+ * \return a reference to a CoreFoundation string.
+ */
+/*static CFStringRef qString2CFString(const QString& s)
+{
+	if (s.isNull())
+		return 0;
+	
+	ushort* buffer = new ushort[s.length()];
+	for (unsigned int i = 0; i < s.length(); ++i)
+		buffer[i] = s[i].unicode();
+	CFStringRef result = CFStringCreateWithBytes ( NULL, 
+		(UInt8*) buffer, 
+		s.length()*sizeof(ushort),
+		kCFStringEncodingUnicode, false);
+
+	delete buffer;
+	return result;
+}*/
+
+void MacDock::startBounce()
+{
+	if (!isBouncing) {
+		bounceRec.qType = nmType;
+		bounceRec.nmMark = 1;
+		NMInstall(&bounceRec);
+		isBouncing = true;
+	}
+}
+
+void MacDock::stopBounce()
+{
+	if(isBouncing) {
+		NMRemove(&bounceRec);
+		isBouncing = false;
+	}
+}
+
+void MacDock::overlay(const QString& text)
+{
+	if (text.isEmpty()) {
+		overlayed = false;
+		RestoreApplicationDockTileImage();
+		return;
+	}
+
+	// Create the context
+	CGContextRef context = BeginCGContextForApplicationDockTile();
+
+	if (!overlayed) {
+		overlayed = true;
+
+		// Add some subtle drop down shadow
+		// FIXME: Disabled because 10.2 doesn't support it
+		//CGSize s = { 2.0, -4.0 };
+		//CGContextSetShadow(context,s,5.0);
+	}
+
+	// Draw a circle
+	CGContextBeginPath(context);
+	CGContextAddArc(context, 95.0, 95.0, 25.0, 0.0, 2 * M_PI, true);
+	CGContextClosePath(context);
+	CGContextSetRGBFillColor(context, 1, 0.0, 0.0, 1);
+	CGContextFillPath(context);
+
+	// Set the clipping path to the same circle
+	CGContextBeginPath(context);
+	CGContextAddArc(context, 95.0, 95.0, 25.0, 0.0, 2 * M_PI, true);
+	CGContextClip(context);
+
+	// Remove drop shadow
+	// FIXME: Disabled because 10.2 doesn't support it
+	//CGSize s = { 0.0, -0.0 };
+	//CGContextSetShadowWithColor(context, s, 0, NULL);
+
+	// Select the appropriate font
+	CGContextSelectFont(context,DOCK_FONT_NAME, DOCK_FONT_SIZE, kCGEncodingMacRoman);
+	CGContextSetRGBFillColor(context, 1, 1, 1, 1);
+
+	// Draw the text invisible
+	CGPoint begin = CGContextGetTextPosition(context);
+	CGContextSetTextDrawingMode(context, kCGTextInvisible);	
+	CGContextShowTextAtPoint(context, begin.x, begin.y, text.latin1(), text.length());
+	CGPoint end = CGContextGetTextPosition(context);
+
+	// Draw the text
+	CGContextSetTextDrawingMode(context, kCGTextFill);	
+	CGContextShowTextAtPoint(context, 95 - (end.x - begin.x)/2, 95 - 8, text.latin1(), text.length());
+	
+	// Cleanup
+	CGContextFlush(context);
+	EndCGContextForApplicationDockTile(context);
+}
+
+bool MacDock::isBouncing = false;
+bool MacDock::overlayed = false;
Index: psi/tags/0.10-os2-b1/src/tools/mac_dock/mac_dock.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/mac_dock/mac_dock.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/mac_dock/mac_dock.h	(revision 38)
@@ -0,0 +1,18 @@
+#ifndef MACDOCK_H
+#define MACDOCK_H
+
+#include <qstring.h>
+
+class MacDock
+{
+public:
+	static void startBounce();
+	static void stopBounce();
+	static void overlay(const QString& text = QString::null);
+
+private:
+	static bool isBouncing;
+	static bool overlayed;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/mac_dock/mac_dock.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/mac_dock/mac_dock.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/mac_dock/mac_dock.pri	(revision 38)
@@ -0,0 +1,7 @@
+mac_dock {
+	mac: {
+		HEADERS += $$MACDOCK_CPP/mac_dock.h
+		SOURCES += $$MACDOCK_CPP/mac_dock.cpp
+		QMAKE_LFLAGS += -framework Carbon
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gnupg.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gnupg.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gnupg.cpp	(revision 38)
@@ -0,0 +1,484 @@
+/*
+ * gnupg.cpp - OpenPGP interface to GnuPG
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"gnupg.h"
+
+#include<qptrlist.h>
+#include<qstringlist.h>
+#include<qapplication.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qfileinfo.h>
+#include"gpgop.h"
+#include"dirwatch.h"
+
+#ifdef Q_WS_WIN
+#include<windows.h>
+
+static QString find_reg_gpgProgram()
+{
+	HKEY root;
+	root = HKEY_CURRENT_USER;
+
+	HKEY hkey;
+	const char *path = "Software\\GNU\\GnuPG";
+	if(RegOpenKeyExA(HKEY_CURRENT_USER, path, 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) {
+		if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, path, 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS)
+			return QString::null;
+	}
+
+	char szValue[256];
+	DWORD dwLen = 256;
+	if(RegQueryValueExA(hkey, "gpgProgram", NULL, NULL, (LPBYTE)szValue, &dwLen) != ERROR_SUCCESS) {
+		RegCloseKey(hkey);
+		return QString::null;
+	}
+	RegCloseKey(hkey);
+
+	return QString::fromLatin1(szValue);
+}
+#endif
+
+using namespace OpenPGP;
+
+//----------------------------------------------------------------------------
+// GnuPG
+//----------------------------------------------------------------------------
+class GnuPG::Item
+{
+public:
+	Item() {}
+
+	QGuardedPtr<Request> r;
+	Request *r_ng;
+
+	int op;
+	QByteArray data;
+	QString verSig, keyID;
+	QStringList keys;
+	QString str;
+};
+
+class GnuPG::Private
+{
+public:
+	Private(const QString &bin) : gpg(bin) {}
+
+	bool available, initialized;
+	QString name;
+	GpgOp gpg;
+
+	// item list
+	QPtrList<Item> list;
+
+	KeyList secretKeys, publicKeys;
+	QString secretRing, publicRing;
+	FileWatch secretWatch, publicWatch;
+	bool secretDirty, publicDirty;
+};
+
+GnuPG::GnuPG(QObject *parent)
+:Engine(parent)
+{
+	QString bin = "gpg";
+#ifdef Q_WS_WIN
+	QString s = find_reg_gpgProgram();
+	if(!s.isNull())
+		bin = s;
+#endif
+
+#ifdef Q_OS_MAC
+	// mac-gpg
+	QFileInfo fi("/usr/local/bin/gpg");
+	if(fi.exists())
+		bin = fi.filePath();
+#endif
+
+#ifdef GPG_DEBUG
+	printf("gpg bin=[%s]\n", bin.latin1());
+#endif
+	d = new Private(bin);
+	d->available = false;
+	d->initialized = false;
+	d->name = "GNU Privacy Guard (GPG)";
+
+	connect(&d->gpg, SIGNAL(finished(bool)), SLOT(gpg_finished(bool)));
+	connect(&d->gpg, SIGNAL(needPassphrase()), SLOT(gpg_needPassphrase()));
+
+	connect(&d->secretWatch, SIGNAL(changed()), SLOT(fileChanged()));
+	connect(&d->publicWatch, SIGNAL(changed()), SLOT(fileChanged()));
+}
+
+GnuPG::~GnuPG()
+{
+	d->gpg.stop();
+
+	// unhook all requests (send errors)
+	QPtrListIterator<Item> it(d->list);
+	Item *i;
+	while((i = it.current())) {
+		if(i->op == Sign)
+			signFinished(i->r, false, "");
+		else if(i->op == Verify)
+			verifyFinished(i->r, VerifyError, "", QDateTime());
+		unhook(i);
+	}
+
+	delete d;
+}
+
+void GnuPG::setTryAgent(bool b)
+{
+	d->gpg.setTryAgent(b);
+}
+
+bool GnuPG::checkAvailability()
+{
+	if(d->available)
+		return true;
+
+	d->gpg.doCheck();
+
+	// loop here until we are done executing
+	while(d->gpg.isActive())
+		qApp->processEvents();
+
+	return d->available;
+}
+
+QString GnuPG::id() const
+{
+	return "gpg";
+}
+
+QString GnuPG::name() const
+{
+	return d->name;
+}
+
+void GnuPG::init()
+{
+	if(d->initialized)
+		return;
+
+	d->available = true;
+
+	// first step: get secret keys
+	d->gpg.doSecretKeys();
+}
+
+KeyList GnuPG::secretKeys() const
+{
+	return d->secretKeys;
+}
+
+KeyList GnuPG::publicKeys() const
+{
+	return d->publicKeys;
+}
+
+void GnuPG::encrypt(Request *r, const QByteArray &in, const QStringList &keys)
+{
+	if(!d->initialized)
+		return;
+
+	Item *i = new Item;
+	i->r = r;
+	i->r_ng = r;
+	i->op = Encrypt;
+	i->data = in;
+	i->keys = keys;
+	hook(i);
+
+	if(d->list.count() == 1)
+		tryNext();
+}
+
+void GnuPG::decrypt(Request *r, const QString &in)
+{
+	if(!d->initialized)
+		return;
+
+	Item *i = new Item;
+	i->r = r;
+	i->r_ng = r;
+	i->op = Decrypt;
+	i->str = in;
+	hook(i);
+
+	if(d->list.count() == 1)
+		tryNext();
+}
+
+void GnuPG::sign(Request *r, const QByteArray &in, const QString &keyID)
+{
+	if(!d->initialized)
+		return;
+
+	Item *i = new Item;
+	i->r = r;
+	i->r_ng = r;
+	i->op = Sign;
+	i->data = in;
+	i->keyID = keyID;
+	hook(i);
+
+	if(d->list.count() == 1)
+		tryNext();
+}
+
+void GnuPG::verify(Request *r, const QByteArray &in, const QString &sig)
+{
+	if(!d->initialized)
+		return;
+
+	Item *i = new Item;
+	i->r = r;
+	i->r_ng = r;
+	i->op = Verify;
+	i->data = in;
+	i->verSig = sig;
+	hook(i);
+
+	if(d->list.count() == 1)
+		tryNext();
+}
+
+void GnuPG::submitPassphrase(Request *, const QString &pp)
+{
+	d->gpg.submitPassphrase(pp);
+}
+
+void GnuPG::tryNext()
+{
+	QTimer::singleShot(0, this, SLOT(doNext()));
+}
+
+void GnuPG::doNext()
+{
+	// busy?  don't do anything then
+	if(d->gpg.isActive())
+		return;
+	if(d->list.isEmpty()) {
+		handleDirtyRings();
+		return;
+	}
+
+	Item *i = d->list.getFirst();
+	if(i->op == Encrypt)
+		d->gpg.doEncrypt(i->data, i->keys);
+	else if(i->op == Decrypt)
+		d->gpg.doDecrypt(i->str);
+	else if(i->op == Sign)
+		d->gpg.doSign(i->data, i->keyID);
+	else if(i->op == Verify)
+		d->gpg.doVerify(i->data, i->verSig);
+}
+
+void GnuPG::doInitFinished(bool b, const QString &err)
+{
+#ifdef GPG_DEBUG
+	printf("GnuPG initialization complete\n");
+	printf(" Secret Keyring: [%s]\n", d->secretWatch.fileName().latin1());
+	printf(" Public Keyring: [%s]\n", d->publicWatch.fileName().latin1());
+#endif
+	initFinished(b, err);
+}
+
+void GnuPG::gpg_finished(bool b)
+{
+	GpgOp *gpg = (GpgOp *)sender();
+	int op = gpg->op();
+
+	if(op == GpgOp::Check)
+		d->available = b;
+	else if(op == GpgOp::SecretKeyringFile) {
+		if(!gpg->keyringFile().isEmpty())
+			d->secretWatch.setFileName(gpg->keyringFile());
+
+		if(!d->initialized) {
+			// get public keys
+			d->gpg.doPublicKeys();
+		}
+		else
+			keysUpdated();
+
+		handleDirtyRings();
+	}
+	else if(op == GpgOp::PublicKeyringFile) {
+		if(!gpg->keyringFile().isEmpty())
+			d->publicWatch.setFileName(gpg->keyringFile());
+
+		if(!d->initialized) {
+			// init finished
+			d->initialized = true;
+			doInitFinished(true, "");
+		}
+		else
+			keysUpdated();
+
+		handleDirtyRings();
+	}
+	else if(op == GpgOp::SecretKeys) {
+		if(!b) {
+			doInitFinished(false, tr("Unable to retrieve secret key list."));
+			return;
+		}
+
+		d->secretKeys = gpg->keys();
+		d->secretDirty = false;
+		// We may need to do a second request to obtain the keyring
+		// filename, but only do this once.
+		if(!d->initialized && gpg->keyringFile().isEmpty()) {
+			d->gpg.doSecretKeyringFile();
+			return;
+		}
+		d->secretWatch.setFileName(gpg->keyringFile());
+
+		if(!d->initialized) {
+			// get public keys
+			d->gpg.doPublicKeys();
+		}
+		else
+			keysUpdated();
+
+		handleDirtyRings();
+	}
+	else if(op == GpgOp::PublicKeys) {
+		if(!b) {
+			doInitFinished(false, tr("Unable to retrieve public key list."));
+			return;
+		}
+
+		d->publicKeys = gpg->keys();
+		d->publicDirty = false;
+		// We may need to do a second request to obtain the keyring
+		// filename, but only do this once.
+		if(!d->initialized && gpg->keyringFile().isEmpty()) {
+			d->gpg.doPublicKeyringFile();
+			return;
+		}
+		d->publicWatch.setFileName(gpg->keyringFile());
+
+		if(!d->initialized) {
+			// init finished
+			d->initialized = true;
+			doInitFinished(true, "");
+		}
+		else
+			keysUpdated();
+
+		handleDirtyRings();
+	}
+	else {
+		Request *r = unhookFirst();
+		setBadPassphrase(r, gpg->badPassphrase());
+
+		if(op == GpgOp::Encrypt)
+			encryptFinished(r, b, gpg->encrypted());
+		else if(op == GpgOp::Decrypt)
+			decryptFinished(r, b, gpg->decrypted());
+		else if(op == GpgOp::Sign)
+			signFinished(r, b, gpg->signature());
+		else if(op == GpgOp::Verify)
+			verifyFinished(r, gpg->verifyResult(), gpg->keyID(), gpg->timestamp());
+
+		tryNext();
+	}
+}
+
+void GnuPG::gpg_needPassphrase()
+{
+	needPassphrase(d->list.getFirst()->r);
+}
+
+void GnuPG::req_destroyed()
+{
+	Request *r = (Request *)sender();
+	Item *i = find(r);
+	if(i) {
+		bool first = (r == d->list.getFirst()->r_ng) ? true: false;
+		unhook(i);
+		if(first) {
+			d->gpg.stop();
+			tryNext();
+		}
+	}
+}
+
+GnuPG::Item *GnuPG::find(Request *r) const
+{
+	QPtrListIterator<Item> it(d->list);
+	for(Item *i; (i = it.current()); ++it) {
+		if((Request *)i->r_ng == r)
+			return i;
+	}
+	return 0;
+}
+
+void GnuPG::hook(Item *i)
+{
+	connect(i->r, SIGNAL(destroyed()), this, SLOT(req_destroyed()));
+	d->list.append(i);
+}
+
+Request *GnuPG::unhook(Item *i)
+{
+	Request *r = i->r;
+	if(r)
+		disconnect(r, SIGNAL(destroyed()), this, SLOT(req_destroyed()));
+	d->list.removeRef(i);
+	delete i;
+	return r;
+}
+
+Request *GnuPG::unhookFirst()
+{
+	return unhook(d->list.getFirst());
+}
+
+void GnuPG::fileChanged()
+{
+	FileWatch *w = (FileWatch *)sender();
+	if(w == &d->secretWatch) {
+		d->secretDirty = true;
+#ifdef GPG_DEBUG
+		printf("Secret keys updated!\n");
+#endif
+	}
+	else if(w == &d->publicWatch) {
+		d->publicDirty = true;
+#ifdef GPG_DEBUG
+		printf("Public keys updated!\n");
+#endif
+	}
+	handleDirtyRings();
+}
+
+void GnuPG::handleDirtyRings()
+{
+	if(!d->initialized || d->gpg.isActive())
+		return;
+
+	if(d->secretDirty)
+		d->gpg.doSecretKeys();
+	else if(d->publicDirty)
+		d->gpg.doPublicKeys();
+}
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gnupg.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gnupg.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gnupg.h	(revision 38)
@@ -0,0 +1,75 @@
+/*
+ * gnupg.h - OpenPGP interface to GnuPG
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef GNUPG_H
+#define GNUPG_H
+
+#include"openpgp.h"
+
+class GnuPG : public OpenPGP::Engine
+{
+	Q_OBJECT
+public:
+	GnuPG(QObject *parent=0);
+	~GnuPG();
+
+	void setTryAgent(bool);
+
+	bool checkAvailability();
+	QString id() const;
+	QString name() const;
+
+	void init();
+	OpenPGP::KeyList secretKeys() const;
+	OpenPGP::KeyList publicKeys() const;
+
+protected:
+	void encrypt(OpenPGP::Request *, const QByteArray &in, const QStringList &keys);
+	void decrypt(OpenPGP::Request *, const QString &in);
+	void sign(OpenPGP::Request *, const QByteArray &in, const QString &keyID);
+	void verify(OpenPGP::Request *, const QByteArray &in, const QString &sig);
+	void submitPassphrase(OpenPGP::Request *, const QString &);
+
+private slots:
+	void gpg_finished(bool);
+	void gpg_needPassphrase();
+
+	void req_destroyed();
+	void doNext();
+	void fileChanged();
+
+public:
+	class Item;
+private:
+	class Private;
+	Private *d;
+
+	void tryNext();
+
+	Item *find(OpenPGP::Request *) const;
+	void hook(Item *);
+	OpenPGP::Request *unhook(Item *);
+	OpenPGP::Request *unhookFirst();
+
+	void handleDirtyRings();
+	void doInitFinished(bool, const QString &);
+};
+ 
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgop.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgop.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgop.cpp	(revision 38)
@@ -0,0 +1,680 @@
+#include"gpgop.h"
+
+#include<qstringlist.h>
+#include<qtimer.h>
+#include<qdatetime.h>
+#include<stdlib.h>
+#include"gpgproc/gpgproc.h"
+
+static QByteArray stringToArray(const QString &str)
+{
+	QCString cs = str.utf8();
+	QByteArray a(cs.length());
+	memcpy(a.data(), cs.data(), a.size());
+	return a;
+}
+
+class GpgOp::Private
+{
+public:
+	Private() {}
+
+	QString bin;
+	int op;
+	GPGProc *proc;
+	bool tryAgent;
+
+	QByteArray outbuf, errbuf;
+	bool didPassphrase;
+	QString sigKeyID;
+	QDateTime sigTS;
+	int verType;
+
+	OpenPGP::KeyList keys;
+	QString keyring;
+	bool badpp;
+
+	QString enc;
+	QByteArray dec;
+};
+
+GpgOp::GpgOp(const QString &bin, QObject *parent)
+:QObject(parent)
+{
+	d = new Private;
+	d->bin = bin;
+	d->proc = 0;
+	d->op = -1;
+	d->tryAgent = true;
+}
+
+GpgOp::~GpgOp()
+{
+	reset();
+	delete d;
+}
+
+void GpgOp::reset()
+{
+	if(d->proc) {
+		d->proc->disconnect(this);
+		d->proc->deleteLater();
+		d->proc = 0;
+	}
+
+	d->sigKeyID = "";
+	d->sigTS = QDateTime();
+	d->didPassphrase = false;
+	d->outbuf.resize(0);
+	d->errbuf.resize(0);
+	d->keys.clear();
+	d->keyring = "";
+	d->verType = OpenPGP::VerifyError;
+	d->badpp = false;
+	d->enc = "";
+	d->dec.resize(0);
+}
+
+void GpgOp::stop()
+{
+	reset();
+}
+
+bool GpgOp::isActive() const
+{
+	return (d->proc ? true: false);
+}
+
+int GpgOp::op() const
+{
+	return d->op;
+}
+
+const OpenPGP::KeyList & GpgOp::keys() const
+{
+	return d->keys;
+}
+
+const QString & GpgOp::keyringFile() const
+{
+	return d->keyring;
+}
+
+const QString & GpgOp::keyID() const
+{
+	return d->sigKeyID;
+}
+
+const QDateTime & GpgOp::timestamp() const
+{
+	return d->sigTS;
+}
+
+int GpgOp::verifyResult() const
+{
+	return d->verType;
+}
+
+bool GpgOp::badPassphrase() const
+{
+	return d->badpp;
+}
+
+const QString & GpgOp::encrypted() const
+{
+	return d->enc;
+}
+
+const QByteArray & GpgOp::decrypted() const
+{
+	return d->dec;
+}
+
+const QString & GpgOp::signature() const
+{
+	return d->enc;
+}
+
+void GpgOp::setTryAgent(bool b)
+{
+	d->tryAgent = b;
+}
+
+void GpgOp::doCheck()
+{
+	reset();
+
+	d->op = Check;
+	QStringList args;
+	args += "--version";
+	if(!launchGPG(args, false)) {
+		QTimer::singleShot(0, this, SLOT(doFail()));
+		return;
+	}
+}
+
+void GpgOp::doSecretKeyringFile()
+{
+	reset();
+
+	d->op = SecretKeyringFile;
+	QStringList args;
+	args += "--list-secret-keys";
+	if(!launchGPG(args, false)) {
+		QTimer::singleShot(0, this, SLOT(doFail()));
+		return;
+	}
+}
+
+void GpgOp::doPublicKeyringFile()
+{
+	reset();
+
+	d->op = PublicKeyringFile;
+	QStringList args;
+	args += "--list-public-keys";
+	if(!launchGPG(args, false)) {
+		QTimer::singleShot(0, this, SLOT(doFail()));
+		return;
+	}
+}
+
+void GpgOp::doSecretKeys()
+{
+	reset();
+
+	d->op = SecretKeys;
+	QStringList args;
+	args += "--fixed-list-mode";
+	args += "--with-colons";
+	args += "--list-secret-keys";
+	if(!launchGPG(args, false)) {
+		QTimer::singleShot(0, this, SLOT(doFail()));
+		return;
+	}
+}
+
+void GpgOp::doPublicKeys()
+{
+	reset();
+
+	d->op = PublicKeys;
+	QStringList args;
+	args += "--fixed-list-mode";
+	args += "--with-colons";
+	args += "--list-public-keys";
+	if(!launchGPG(args, false)) {
+		QTimer::singleShot(0, this, SLOT(doFail()));
+		return;
+	}
+}
+
+void GpgOp::doEncrypt(const QByteArray &in, const QStringList &keys)
+{
+	reset();
+
+	d->op = Encrypt;
+	QStringList args;
+	args += "--armor";
+	args += "--always-trust";
+	args += "--encrypt";
+
+	// recipients
+	for(QStringList::ConstIterator it = keys.begin(); it != keys.end(); ++it) {
+		args += "--recipient";
+		args += QString("0x") + *it;
+	}
+
+	if(!launchGPG(args)) {
+		QTimer::singleShot(0, this, SLOT(doFail()));
+		return;
+	}
+
+	if(!in.isEmpty())
+		d->proc->writeToStdin(in);
+	else
+		d->proc->closeStdin();
+}
+
+void GpgOp::doDecrypt(const QString &in)
+{
+	reset();
+
+	d->op = Decrypt;
+	QStringList args;
+	if(!d->tryAgent)
+		args += "--no-use-agent";
+	args += "--armor";
+	args += "--decrypt";
+	if(!launchGPG(args)) {
+		QTimer::singleShot(0, this, SLOT(doFail()));
+		return;
+	}
+
+	if(!in.isEmpty())
+		d->proc->writeToStdin(stringToArray(fixOutgoingLines(in)));
+	else
+		d->proc->closeStdin();
+}
+
+void GpgOp::doSign(const QByteArray &in, const QString &keyID)
+{
+	reset();
+
+	d->op = Sign;
+	QStringList args;
+	if(!d->tryAgent)
+		args += "--no-use-agent";
+	args += "--armor";
+	args += "--default-key";
+	args += QString("0x") + keyID;
+	args += "--detach-sign";
+	if(!launchGPG(args)) {
+		QTimer::singleShot(0, this, SLOT(doFail()));
+		return;
+	}
+
+	if(!in.isEmpty())
+		d->proc->writeToStdin(in);
+	else
+		d->proc->closeStdin();
+}
+
+void GpgOp::doVerify(const QByteArray &in, const QString &sig)
+{
+	reset();
+
+	d->op = Verify;
+	d->verType = OpenPGP::VerifyError;
+	QStringList args;
+	args += "--armor";
+	args += "--verify";
+	args += "-";
+	args += "-&?";
+
+	if(!launchGPG(args)) {
+		QTimer::singleShot(0, this, SLOT(doFail()));
+		return;
+	}
+
+	// sig goes into stdin
+	if(!sig.isEmpty())
+		d->proc->writeToStdin(stringToArray(fixOutgoingLines(sig)));
+	else
+		d->proc->closeStdin();
+
+	// data goes into aux
+	d->proc->writeToAux(in);
+	d->proc->closeAux();
+}
+
+void GpgOp::submitPassphrase(const QString &pp)
+{
+	QCString cs = pp.local8Bit() + '\n';
+	QByteArray a(cs.length());
+	memcpy(a.data(), cs.data(), a.size());
+	d->proc->writeToCommand(a);
+}
+
+void GpgOp::doFail()
+{
+	finished(false);
+}
+
+bool GpgOp::launchGPG(const QStringList &args, bool useExtra)
+{
+	d->proc = new GPGProc;
+	connect(d->proc, SIGNAL(readyReadStdout()), SLOT(proc_readyReadStdout()));
+	connect(d->proc, SIGNAL(readyReadStderr()), SLOT(proc_readyReadStderr()));
+	connect(d->proc, SIGNAL(processExited()), SLOT(proc_processExited()));
+	connect(d->proc, SIGNAL(wroteToStdin()), SLOT(proc_wroteToStdin()));
+	connect(d->proc, SIGNAL(statusLine(const QString &)), SLOT(proc_statusLine(const QString &)));
+
+	if(!d->proc->start(d->bin, args, useExtra)) {
+		d->proc->disconnect(this);
+		d->proc->deleteLater();
+		d->proc = 0;
+		return false;
+	}
+
+	return true;
+}
+
+void GpgOp::proc_readyReadStdout()
+{
+	QByteArray block = d->proc->readStdout();
+	int oldsize = d->outbuf.size();
+	d->outbuf.resize(oldsize + block.size());
+	memcpy(d->outbuf.data() + oldsize, block.data(), block.size());
+}
+
+void GpgOp::proc_readyReadStderr()
+{
+	QByteArray block = d->proc->readStderr();
+	int oldsize = d->errbuf.size();
+	d->errbuf.resize(oldsize + block.size());
+	memcpy(d->errbuf.data() + oldsize, block.data(), block.size());
+}
+
+void GpgOp::proc_wroteToStdin()
+{
+	d->proc->closeStdin();
+}
+
+void GpgOp::proc_statusLine(const QString &str)
+{
+#ifdef GPG_DEBUG
+	printf("{%s}\n", str.latin1());
+#endif
+	QString s, rest;
+	int n = str.find(' ');
+	if(n == -1) {
+		s = str;
+		rest = "";
+	}
+	else {
+		s = str.mid(0, n);
+		rest = str.mid(n+1);
+	}
+
+	if(s == "NEED_PASSPHRASE") {
+		if(!(d->tryAgent && getenv("GPG_AGENT_INFO"))) {
+			if(!d->didPassphrase) {
+				d->didPassphrase = true;
+				needPassphrase();
+			}
+			else {
+				QByteArray a(1);
+				a[0] = '\n';
+				d->proc->writeToCommand(a);
+			}
+		}
+	}
+	else if(s == "BAD_PASSPHRASE") {
+		d->badpp = true;
+	}
+	else if(s == "GOOD_PASSPHRASE") {
+		d->badpp = false;
+	}
+	else if(s == "GOODSIG") {
+		QString keyID;
+		int n = rest.find(' ');
+		if(n == -1)
+			keyID = rest;
+		else
+			keyID = rest.mid(0, n);
+		d->sigKeyID = keyID;
+		d->verType = OpenPGP::VerifyGood;
+	}
+	else if(s == "BADSIG") {
+		QString keyID;
+		int n = rest.find(' ');
+		if(n == -1)
+			keyID = rest;
+		else
+			keyID = rest.mid(0, n);
+		d->sigKeyID = keyID;
+		d->verType = OpenPGP::VerifyBad;
+	}
+	else if(s == "ERRSIG") {
+		QStringList list = QStringList::split(' ', rest, false);
+		d->sigKeyID = list[0];
+		d->sigTS.setTime_t(list[4].toInt());
+		d->verType = OpenPGP::VerifyNoKey;
+	}
+	else if(s == "VALIDSIG") {
+		QStringList list = QStringList::split(' ', rest, false);
+		d->sigTS.setTime_t(list[2].toInt());
+	}
+}
+
+void GpgOp::proc_processExited()
+{
+	if(!d->proc)
+		return;
+
+#ifdef GPG_DEBUG
+	printf("GPG Finished: ");
+#endif
+
+	bool clean = true;
+	int exitStatus = -1;
+	if(!d->proc->normalExit()) {
+		clean = false;
+#ifdef GPG_DEBUG
+		printf("bad exit.. crash?\n");
+#endif
+	}
+	else {
+		exitStatus = d->proc->exitStatus();
+#ifdef GPG_DEBUG
+		printf("exitStatus=%d\n", exitStatus);
+#endif
+	}
+
+	d->proc->disconnect(this);
+	d->proc->deleteLater();
+	d->proc = 0;
+
+	processResult(clean, exitStatus, d->outbuf, d->errbuf);
+}
+
+void GpgOp::processResult(bool clean, int code, const QByteArray &out, const QByteArray &err)
+{
+	// put stdout and stderr into QStrings
+	QCString cs;
+	cs.resize(out.size()+1);
+	memcpy(cs.data(), out.data(), out.size());
+	QString outstr = QString::fromLatin1(cs);
+	cs.resize(err.size()+1);
+	memcpy(cs.data(), err.data(), err.size());
+	QString errstr = QString::fromLatin1(cs);
+
+#ifdef GPG_DEBUG
+	printf("stdout: [%s]\n", outstr.latin1());
+	printf("stderr: [%s]\n", errstr.latin1());
+#endif
+
+	if(d->op == Check) {
+		if(!clean || code != 0) {
+			doFail();
+			return;
+		}
+		finished(true);
+	}
+	else if(d->op == SecretKeyringFile || d->op == PublicKeyringFile) {
+		if(!clean || code != 0) {
+			doFail();
+			return;
+		}
+		if(!findKeyringFilename(fixIncomingLines(outstr), &d->keyring)) {
+			doFail();
+			return;
+		}
+		finished(true);
+	}
+	else if(d->op == SecretKeys || d->op == PublicKeys) {
+		if(!clean || code != 0) {
+			doFail();
+			return;
+		}
+		if(!stringToKeyList(fixIncomingLines(outstr), &d->keys, &d->keyring)) {
+			doFail();
+			return;
+		}
+		finished(true);
+	}
+	else if(d->op == Encrypt) {
+		if(!clean || code != 0) {
+			doFail();
+			return;
+		}
+		d->enc = fixIncomingLines(outstr);
+		finished(true);
+	}
+	else if(d->op == Decrypt) {
+		if(!clean || code != 0) {
+			doFail();
+			return;
+		}
+		d->dec = out;
+		finished(true);
+	}
+	else if(d->op == Sign) {
+		if(!clean || code != 0) {
+			doFail();
+			return;
+		}
+		d->enc = fixIncomingLines(outstr);
+		finished(true);
+	}
+	else if(d->op == Verify) {
+		if(!clean || code != 0) {
+			doFail();
+			return;
+		}
+		finished(true);
+	}
+}
+
+QString GpgOp::fixIncomingLines(const QString &str)
+{
+#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
+	QString out;
+	for(int n = 0; n < (int)str.length(); ++n) {
+		if(str.at(n) == '\r' && (n + 1) < (int)str.length() && str.at(n+1) == '\n') {
+			out += '\n';
+			++n;
+		}
+		else
+			out += str.at(n);
+	}
+	return out;
+#else
+	return str;
+#endif
+}
+
+QString GpgOp::fixOutgoingLines(const QString &str)
+{
+#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
+	QString out;
+	for(int n = 0; n < (int)str.length(); ++n) {
+		if(str.at(n) == '\n' && (n == 0 || str.at(n-1) != '\r'))
+			out += "\r\n";
+		else
+			out += str.at(n);
+	}
+	return out;
+#else
+	return str;
+#endif
+}
+
+bool GpgOp::stringToKeyList(const QString &outstr, OpenPGP::KeyList *_keylist, QString *_keyring)
+{
+	OpenPGP::KeyList keyList;
+	QStringList lines = QStringList::split('\n', outstr, true);
+
+	if(lines.count() < 1)
+		return false;
+
+	QStringList::ConstIterator it = lines.begin();
+
+	// first line is keyring file
+	QString keyring = *(it++);
+
+	// if the second line isn't a divider, we are dealing
+	// with a new version of gnupg that doesn't give us
+	// the keyring file on gpg --list-keys --with-colons
+	if(it == lines.end() || (*it).at(0) != '-') {
+		// first line wasn't the keyring name...
+		keyring = "";
+		// ...so read the first line again
+		it--;
+	}
+	else {
+		// this was the divider line - skip it
+		it++;
+	}
+
+	OpenPGP::Key *k = 0;
+	for(; it != lines.end(); ++it) {
+		QStringList f = QStringList::split(':', *it, true);
+		QString type = f[0];
+
+		if(type == "pub") {
+			if(k) {
+				keyList += (*k);
+				delete k;
+				k = 0;
+			}
+			k = new OpenPGP::Key;
+
+			k->setKeyID(f[4]);
+		}
+		else if(type == "sec") {
+			if(k) {
+				keyList += (*k);
+				delete k;
+				k = 0;
+			}
+			k = new OpenPGP::Key;
+
+			k->setKeyID(f[4]);
+		}
+		else if(type == "uid") {
+			if(!k)
+				continue;
+			// ignore a uid if we already have one
+			if(!k->userID().isEmpty())
+				continue;
+			QString s = f[9];
+			QCString uid;
+			// convert the "backslash" C-string syntax
+			for(int n = 0; n < (int)s.length(); ++n) {
+				if(s.at(n) == '\\' && n + 1 < (int)s.length()) {
+					++n;
+					unsigned char c = (unsigned char)s.at(n).latin1();
+					if(c == '\\')
+						uid += '\\';
+					else if(c == 'x' && n + 2 < (int)s.length()) {
+						++n;
+						QString hex = s.mid(n, 2);
+						bool ok;
+						uint val = hex.toInt(&ok, 16);
+						uid += (unsigned char)val;
+						++n; // only skip one, the for-loop will skip the next
+					}
+				}
+				else {
+					uid += (unsigned char)s.at(n).latin1();
+				}
+			}
+			k->setUserID(QString::fromUtf8(uid));
+		}
+	}
+	if(k) {
+		keyList += (*k);
+		delete k;
+		k = 0;
+	}
+
+	if(_keylist)
+		*_keylist = keyList;
+	if(_keyring)
+		*_keyring = keyring;
+
+	return true;
+}
+
+bool GpgOp::findKeyringFilename(const QString &outstr, QString *_keyring)
+{
+	QStringList lines = QStringList::split('\n', outstr, true);
+	if(lines.count() < 1)
+		return false;
+
+	*_keyring = lines[0];
+	return true;
+}
+
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgop.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgop.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgop.h	(revision 38)
@@ -0,0 +1,68 @@
+#ifndef CS_GPGOP_H
+#define CS_GPGOP_H
+
+#include"openpgp.h"
+
+class GpgOp : public QObject
+{
+	Q_OBJECT
+public:
+	enum { Check = 0, SecretKeyringFile, PublicKeyringFile, SecretKeys, PublicKeys, Encrypt, Decrypt, Sign, Verify };
+	GpgOp(const QString &bin, QObject *parent=0);
+	~GpgOp();
+
+	bool isActive() const;
+	int op() const;
+	const OpenPGP::KeyList & keys() const;
+	const QString & keyringFile() const;
+	const QString & keyID() const;
+	const QDateTime & timestamp() const;
+	int verifyResult() const;
+	bool badPassphrase() const;
+	const QString & encrypted() const;
+	const QByteArray & decrypted() const;
+	const QString & signature() const;
+
+	void doCheck();
+	void doSecretKeyringFile();
+	void doPublicKeyringFile();
+	void doSecretKeys();
+	void doPublicKeys();
+	void doEncrypt(const QByteArray &in, const QStringList &keys);
+	void doDecrypt(const QString &in);
+	void doSign(const QByteArray &in, const QString &keyID);
+	void doVerify(const QByteArray &in, const QString &sig);
+	void stop();
+
+	void submitPassphrase(const QString &);
+	void setTryAgent(bool);
+
+signals:
+	void finished(bool);
+	void needPassphrase();
+
+private slots:
+	void proc_readyReadStdout();
+	void proc_readyReadStderr();
+	void proc_wroteToStdin();
+	void proc_statusLine(const QString &);
+	void proc_processExited();
+
+	void doFail();
+
+private:
+	class Private;
+	Private *d;
+
+	void reset();
+
+	bool launchGPG(const QStringList &, bool useExtra=true);
+	void processResult(bool clean, int code, const QByteArray &out, const QByteArray &err);
+
+	QString fixIncomingLines(const QString &);
+	QString fixOutgoingLines(const QString &);
+	bool stringToKeyList(const QString &, OpenPGP::KeyList *, QString *);
+	bool findKeyringFilename(const QString &, QString *);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/gpgproc.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/gpgproc.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/gpgproc.cpp	(revision 38)
@@ -0,0 +1,404 @@
+/*
+  TODO:
+    maybe have a queue for aux/command ??
+*/
+
+#include"gpgproc.h"
+
+#include<qsocketnotifier.h>
+#include<qtimer.h>
+#include"safedelete.h"
+#include"qpipe.h"
+
+#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
+#include<qprocess.h>
+#ifdef Q_WS_WIN
+#include<windows.h>
+#endif
+#else
+#include<unistd.h>
+#include"sprocess.h"
+#endif
+
+//----------------------------------------------------------------------------
+// GPGProc
+//----------------------------------------------------------------------------
+class GPGProc::Private
+{
+public:
+	Private() {}
+
+	QByteArray statusbuf;
+	SafeDelete sd;
+
+#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
+	QProcess *proc;
+#else
+	SProcess *proc;
+#endif
+	QPipe pipeAux, pipeCommand, pipeStatus;
+
+	QTimer *t;
+	QSocketNotifier *snStatus;
+};
+
+GPGProc::GPGProc()
+:QObject(0)
+{
+	d = new Private;
+	d->proc = 0;
+
+	d->snStatus = 0;
+	d->t = 0;
+}
+
+GPGProc::~GPGProc()
+{
+	closePipes();
+
+	if(d->proc) {
+		d->proc->disconnect(this);
+		if(d->proc->isRunning())
+			d->proc->kill();
+		d->sd.deleteLater(d->proc);
+	}
+
+	delete d;
+}
+
+void GPGProc::closePipes()
+{
+	d->pipeAux.close();
+	d->pipeCommand.close();
+	d->pipeStatus.close();
+
+	delete d->snStatus;
+	d->snStatus = 0;
+	delete d->t;
+	d->t = 0;
+}
+
+bool GPGProc::isRunning() const
+{
+	return d->proc->isRunning();
+}
+
+bool GPGProc::normalExit() const
+{
+	return d->proc->normalExit();
+}
+
+int GPGProc::exitStatus() const
+{
+	return d->proc->exitStatus();
+}
+
+bool GPGProc::start(const QString &bin, const QStringList &args, bool useExtra)
+{
+	if(d->proc) {
+		closePipes();
+		d->proc->disconnect(this);
+		d->sd.deleteLater(d->proc);
+		d->proc = 0;
+	}
+	d->statusbuf.resize(0);
+
+	if(!d->pipeAux.open()) {
+#ifdef GPG_DEBUG
+		printf("Error creating pipeAux\n");
+#endif
+		closePipes();
+		return false;
+	}
+	if(!d->pipeCommand.open()) {
+#ifdef GPG_DEBUG
+		printf("Error creating pipeCommand\n");
+#endif
+		closePipes();
+		return false;
+	}
+	if(!d->pipeStatus.open()) {
+#ifdef GPG_DEBUG
+		printf("Error creating pipeStatus\n");
+#endif
+		closePipes();
+		return false;
+	}
+
+
+#ifdef Q_WS_WIN
+	if(!d->pipeAux.writeEnd().winDupHandle()) {
+#ifdef GPG_DEBUG
+		printf("Error dup'ing pipeAux\n");
+#endif
+		closePipes();
+		return false;
+	}
+	if(!d->pipeCommand.writeEnd().winDupHandle()) {
+#ifdef GPG_DEBUG
+		printf("Error dup'ing pipeCommand\n");
+#endif
+		closePipes();
+		return false;
+	}
+	if(!d->pipeStatus.readEnd().winDupHandle()) {
+#ifdef GPG_DEBUG
+		printf("Error dup'ing pipeStatus\n");
+#endif
+		closePipes();
+		return false;
+	}
+
+	// timer
+	d->t = new QTimer;
+	connect(d->t, SIGNAL(timeout()), SLOT(timeout()));
+	d->t->start(100);
+#else
+	// status socket notifier
+	d->pipeStatus.readEnd().setBlock(false);
+	d->snStatus = new QSocketNotifier(d->pipeStatus.readEnd().id(), QSocketNotifier::Read);
+	connect(d->snStatus, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+#endif
+
+	/*{
+		// test: write some data into the pipe
+		QCString cs = "Foobar";
+		d->pipeStatus.write(cs);
+		QByteArray buf = d->pipeStatus.readAll();
+		QCString cs2 = buf.data();
+		if(cs != cs2)
+			printf("Pipe test failed.\n");
+		else
+			printf("Pipe test succeeded.\n");
+	}*/
+
+#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
+	d->proc = new QProcess;
+#else
+	d->proc = new SProcess;
+#endif
+
+#ifdef GPG_DEBUG
+	printf("Pipe setup complete.\n");
+#endif
+
+	QStringList fullargs;
+	fullargs += bin;
+	fullargs += "--no-tty";
+
+	if(useExtra) {
+		fullargs += "--enable-special-filenames";
+
+		fullargs += "--status-fd";
+		fullargs += d->pipeStatus.writeEnd().toString();
+
+		fullargs += "--command-fd";
+		fullargs += d->pipeCommand.readEnd().toString();
+	}
+
+	for(QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) {
+		const QString &a = (*it);
+		if(a == "-&?")
+			fullargs += (QString("-&") + d->pipeAux.readEnd().toString());
+		else
+			fullargs += a;
+	}
+
+	// show full command
+#ifdef GPG_DEBUG
+	printf("GPGProc: ");
+	for(QStringList::ConstIterator it2 = fullargs.begin(); it2 != fullargs.end(); ++it2) {
+		printf("%s ", (*it2).latin1());
+	}
+	printf("\n");
+#endif
+
+	d->proc->setArguments(fullargs);
+
+#if !defined(Q_WS_WIN) && !defined(Q_OS_OS2)
+	QValueList<int> plist;
+	plist += d->pipeAux.writeEnd().id();
+	plist += d->pipeCommand.writeEnd().id();
+	plist += d->pipeStatus.readEnd().id();
+	d->proc->setClosePipeList(plist);
+#endif
+	/// @todo (dmik)
+	//  On Unix, it is done when SProcess is destructed. On OS/2 as well as on
+	//  Win32, SProcess is not used, but we use the same QPipe implelentation
+	//  on OS/2 as Unix does, so we need to use the similar technuque to close
+	//  them... Need to investigate this file (i.e. usage) in detail.
+
+	connect(d->proc, SIGNAL(readyReadStdout()), SLOT(proc_readyReadStdout()));
+	connect(d->proc, SIGNAL(readyReadStderr()), SLOT(proc_readyReadStderr()));
+	connect(d->proc, SIGNAL(wroteToStdin()), SLOT(proc_wroteToStdin()));
+	connect(d->proc, SIGNAL(processExited()), SLOT(proc_processExited()));
+
+	bool ok = d->proc->start();
+
+	// we don't care about these
+	d->pipeAux.closeReadEnd();
+	d->pipeCommand.closeReadEnd();
+	d->pipeStatus.closeWriteEnd();
+
+	if(!ok) {
+		closePipes();
+		d->proc->disconnect(this);
+		d->sd.deleteLater(d->proc);
+		d->proc = 0;
+		return false;
+	}
+
+	return true;
+}
+
+void GPGProc::writeToStdin(const QByteArray &buf)
+{
+	d->proc->writeToStdin(buf);
+}
+
+void GPGProc::writeToAux(const QByteArray &buf)
+{
+	d->pipeAux.write(buf);
+	wroteToAux();
+}
+
+void GPGProc::writeToCommand(const QByteArray &buf)
+{
+	d->pipeCommand.write(buf);
+	wroteToCommand();
+}
+
+void GPGProc::closeStdin()
+{
+	d->proc->closeStdin();
+}
+
+void GPGProc::closeAux()
+{
+	d->pipeAux.close();
+}
+
+void GPGProc::closeCommand()
+{
+	d->pipeCommand.close();
+}
+
+QByteArray GPGProc::readStdout()
+{
+	return d->proc->readStdout();
+}
+
+QByteArray GPGProc::readStderr()
+{
+	return d->proc->readStderr();
+}
+
+void GPGProc::proc_readyReadStdout()
+{
+	SafeDeleteLock s(&d->sd);
+	readyReadStdout();
+}
+
+void GPGProc::proc_readyReadStderr()
+{
+	SafeDeleteLock s(&d->sd);
+	readyReadStderr();
+}
+
+void GPGProc::proc_wroteToStdin()
+{
+	SafeDeleteLock s(&d->sd);
+	wroteToStdin();
+}
+
+void GPGProc::proc_processExited()
+{
+	SafeDeleteLock s(&d->sd);
+
+#ifdef Q_WS_WIN
+	timeout();
+	delete d->t;
+	d->t = 0;
+#else
+	// maybe there is some status left??
+	QByteArray buf = d->pipeStatus.readAll();
+	if(!buf.isEmpty())
+		processStatusData(buf);
+#endif
+	processExited();
+}
+
+#ifdef Q_WS_WIN
+void GPGProc::timeout()
+{
+	QByteArray buf = d->pipeStatus.readAll();
+	if(!buf.isEmpty())
+		processStatusData(buf);
+}
+
+void GPGProc::sn_activated(int)
+{
+}
+#else
+void GPGProc::timeout()
+{
+}
+
+void GPGProc::sn_activated(int)
+{
+	bool done;
+	QByteArray buf = d->pipeStatus.readAll(&done);
+	if(done) {
+		d->pipeStatus.close();
+		delete d->snStatus;
+		d->snStatus = 0;
+	}
+	if(!buf.isEmpty())
+		processStatusData(buf);
+}
+#endif
+
+void GPGProc::processStatusData(const QByteArray &buf)
+{
+	// append to the statusbuf
+	int oldsize = d->statusbuf.size();
+	d->statusbuf.resize(oldsize + buf.size());
+	memcpy(d->statusbuf.data() + oldsize, buf.data(), buf.size());
+
+	// extract all lines
+	QStringList list;
+	while(1) {
+		int n = 0;
+		int len = d->statusbuf.size();
+		char *p = (char *)d->statusbuf.data();
+		for(; n < len; ++n) {
+			if(p[n] == '\n')
+				break;
+		}
+		// no full line
+		if(n >= len)
+			break;
+		// extract the string from statusbuf
+		++n;
+		QCString cs;
+		cs.resize(n+1);
+		memcpy(cs.data(), p, n);
+		int newsize = len - n;
+		memmove(p, p + n, newsize);
+		d->statusbuf.resize(newsize);
+
+		// convert to string without newline
+		QString str = QString::fromUtf8(cs);
+		str.truncate(str.length()-1);
+		// ensure it has a proper header
+		if(str.left(9) != "[GNUPG:] ")
+			continue;
+		// take it off
+		str = str.mid(9);
+		// add to the list
+		list += str;
+	}
+
+	for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
+		statusLine(*it);
+}
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/gpgproc.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/gpgproc.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/gpgproc.h	(revision 38)
@@ -0,0 +1,55 @@
+#ifndef GPGPROC_H
+#define GPGPROC_H
+
+#include<qobject.h>
+
+class GPGProc : public QObject
+{
+	Q_OBJECT
+public:
+	GPGProc();
+	~GPGProc();
+
+	bool start(const QString &bin, const QStringList &args, bool useExtra=true);
+
+	void writeToStdin(const QByteArray &);
+	void writeToAux(const QByteArray &);
+	void writeToCommand(const QByteArray &);
+
+	void closeStdin();
+	void closeAux();
+	void closeCommand();
+
+	QByteArray readStdout();
+	QByteArray readStderr();
+
+	bool isRunning() const;
+	bool normalExit() const;
+	int exitStatus() const;
+
+signals:
+	void readyReadStdout();
+	void readyReadStderr();
+	void processExited();
+	void wroteToStdin();
+	void wroteToAux();
+	void wroteToCommand();
+	void statusLine(const QString &);
+
+private slots:
+	void proc_readyReadStdout();
+	void proc_readyReadStderr();
+	void proc_wroteToStdin();
+	void proc_processExited();
+	void timeout();
+	void sn_activated(int);
+
+private:
+	class Private;
+	Private *d;
+
+	void closePipes();
+	void processStatusData(const QByteArray &);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/gpgproc.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/gpgproc.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/gpgproc.pri	(revision 38)
@@ -0,0 +1,22 @@
+# gpgproc module
+
+gpgproc {
+	unix: {
+		HEADERS += \
+			$$GPGPROC_CPP/jprocess.h \
+			$$GPGPROC_CPP/sprocess.h
+		SOURCES += \
+			$$GPGPROC_CPP/jprocess.cpp \
+			$$GPGPROC_CPP/jprocess_unix.cpp \
+			$$GPGPROC_CPP/sprocess.cpp
+	}
+
+	HEADERS += \
+		$$GPGPROC_CPP/qpipe.h \
+		$$GPGPROC_CPP/gpgproc.h
+
+	SOURCES += \
+		$$GPGPROC_CPP/qpipe.cpp \
+		$$GPGPROC_CPP/gpgproc.cpp
+}
+
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/jprocess.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/jprocess.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/jprocess.cpp	(revision 38)
@@ -0,0 +1,796 @@
+/****************************************************************************
+** $Id: jprocess.cpp,v 1.4 2003/04/23 10:48:33 justin Exp $
+**
+** Implementation of QProcess class
+**
+** Created : 20000905
+**
+** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "jprocess.h"
+
+#ifndef QT_NO_PROCESS
+
+#include "qapplication.h"
+
+
+//#define QT_QPROCESS_DEBUG
+
+
+/*!
+    \class QProcess qprocess.h
+
+    \brief The QProcess class is used to start external programs and
+    to communicate with them.
+
+    \ingroup io
+    \ingroup misc
+    \mainclass
+
+    You can write to the started program's standard input, and can
+    read the program's standard output and standard error. You can
+    pass command line arguments to the program either in the
+    constructor or with setArguments() or addArgument(). The program's
+    working directory can be set with setWorkingDirectory(). If you
+    need to set up environment variables pass them to the start() or
+    launch() functions (see below). The processExited() signal is
+    emitted if the program exits. The program's exit status is
+    available from exitStatus(), although you could simply call
+    normalExit() to see if the program terminated normally.
+
+    There are two different ways to start a process. If you just want
+    to run a program, optionally passing data to its standard input at
+    the beginning, use one of the launch() functions. If you want full
+    control of the program's standard input (especially if you don't
+    know all the data you want to send to standard input at the
+    beginning), use the start() function.
+
+    If you use start() you can write to the program's standard input
+    using writeToStdin() and you can close the standard input with
+    closeStdin(). The wroteToStdin() signal is emitted if the data
+    sent to standard input has been written. You can read from the
+    program's standard output using readStdout() or readLineStdout().
+    These functions return an empty QByteArray if there is no data to
+    read. The readyReadStdout() signal is emitted when there is data
+    available to be read from standard output. Standard error has a
+    set of functions that correspond to the standard output functions,
+    i.e. readStderr(), readLineStderr() and readyReadStderr().
+
+    If you use one of the launch() functions the data you pass will be
+    sent to the program's standard input which will be closed once all
+    the data has been written. You should \e not use writeToStdin() or
+    closeStdin() if you use launch(). If you need to send data to the
+    program's standard input after it has started running use start()
+    instead of launch().
+
+    Both start() and launch() can accept a string list of strings each
+    of which has the format, key=value, where the keys are the names
+    of environment variables.
+
+    You can test to see if a program is running with isRunning(). The
+    program's process identifier is available from
+    processIdentifier(). If you want to terminate a running program
+    use tryTerminate(), but note that the program may ignore this. If
+    you \e really want to terminate the program, without it having any
+    chance to clean up, you can use kill().
+
+    As an example, suppose we want to start the \c uic command (a Qt
+    command line tool used with \e{Qt Designer}) and perform some
+    operations on the output (the \c uic outputs the code it generates
+    to standard output by default). Suppose further that we want to
+    run the program on the file "small_dialog.ui" with the command
+    line options "-tr i18n". On the command line we would write:
+    \code
+    uic -tr i18n small_dialog.ui
+    \endcode
+
+    \quotefile process/process.cpp
+
+    A code snippet for this with the QProcess class might look like
+    this:
+
+    \skipto UicManager::UicManager()
+    \printline UicManager::UicManager()
+    \printline {
+    \skipto proc = new QProcess( this );
+    \printline proc = new QProcess( this );
+    \skipto proc->addArgument( "uic" );
+    \printuntil this, SLOT(readFromStdout()) );
+    \skipto if ( !proc->start() ) {
+    \printuntil // error handling
+    \skipto }
+    \printline }
+    \printline }
+
+    \skipto void UicManager::readFromStdout()
+    \printuntil // Bear in mind that the data might be output in chunks.
+    \skipto }
+    \printline }
+
+    Although you may need quotes for a file named on the command line
+    (e.g. if it contains spaces) you shouldn't use extra quotes for
+    arguments passed to addArgument() or setArguments().
+
+    The readyReadStdout() signal is emitted when there is new data on
+    standard output. This happens asynchronously: you don't know if
+    more data will arrive later.
+
+    In the above example you could connect the processExited() signal
+    to the slot UicManager::readFromStdout() instead. If you do so,
+    you will be certain that all the data is available when the slot
+    is called. On the other hand, you must wait until the process has
+    finished before doing any processing.
+
+    Note that if you are expecting a lot of output from the process,
+    you may hit platform-dependent limits to the pipe buffer size. The
+    solution is to make sure you connect to the output, e.g. the
+    readyReadStdout() and readyReadStderr() signals and read the data
+    as soon as it becomes available.
+
+    Note: Under Windows there are certain problems starting 16-bit applications
+    and capture their output. Microsoft recommends to use an intermediate
+    application to start 16-bit applications. See Knowledge Base article \link
+    http://support.microsoft.com/default.aspx?scid=KB;en-us;q150956 Q150956
+    \endlink for details on this and example code for an intermediate
+    application.
+
+    \sa QSocket
+*/
+
+/*!
+    \enum QProcess::Communication
+
+    This enum type defines the communication channels connected to the
+    process.
+
+    \value Stdin  Data can be written to the process's standard input.
+
+    \value Stdout  Data can be read from the process's standard
+    output.
+
+    \value Stderr  Data can be read from the process's standard error.
+
+    \value DupStderr  Both the process's standard error output \e and
+    its standard output are written to its standard output. (Like
+    Unix's dup2().) This means that nothing is sent to the standard
+    error output. This is especially useful if your application
+    requires that the output on standard output and on standard error
+    must be read in the same order that they are produced. This is a
+    flag, so to activate it you must pass \c{Stdout|Stderr|DupStderr},
+    or \c{Stdin|Stdout|Stderr|DupStderr} if you want to provide input,
+    to the setCommunication() call.
+
+    \sa setCommunication() communication()
+*/
+
+/*!
+    Constructs a QProcess object. The \a parent and \a name parameters
+    are passed to the QObject constructor.
+
+    \sa setArguments() addArgument() start()
+*/
+JProcess::JProcess( QObject *parent, const char *name )
+    : QObject( parent, name ), ioRedirection( FALSE ), notifyOnExit( FALSE ),
+    wroteToStdinConnected( FALSE ),
+    readStdoutCalled( FALSE ), readStderrCalled( FALSE ),
+    comms( Stdin|Stdout|Stderr )
+{
+    init();
+}
+
+/*!
+    Constructs a QProcess with \a arg0 as the command to be executed.
+    The \a parent and \a name parameters are passed to the QObject
+    constructor.
+
+    The process is not started. You must call start() or launch() to
+    start the process.
+
+    \sa setArguments() addArgument() start()
+*/
+JProcess::JProcess( const QString& arg0, QObject *parent, const char *name )
+    : QObject( parent, name ), ioRedirection( FALSE ), notifyOnExit( FALSE ),
+    wroteToStdinConnected( FALSE ),
+    readStdoutCalled( FALSE ), readStderrCalled( FALSE ),
+    comms( Stdin|Stdout|Stderr )
+{
+    init();
+    addArgument( arg0 );
+}
+
+/*!
+    Constructs a QProcess with \a args as the arguments of the
+    process. The first element in the list is the command to be
+    executed. The other elements in the list are the arguments to this
+    command. The \a parent and \a name parameters are passed to the
+    QObject constructor.
+
+    The process is not started. You must call start() or launch() to
+    start the process.
+
+    \sa setArguments() addArgument() start()
+*/
+JProcess::JProcess( const QStringList& args, QObject *parent, const char *name )
+    : QObject( parent, name ), ioRedirection( FALSE ), notifyOnExit( FALSE ),
+    wroteToStdinConnected( FALSE ),
+    readStdoutCalled( FALSE ), readStderrCalled( FALSE ),
+    comms( Stdin|Stdout|Stderr )
+{
+    init();
+    setArguments( args );
+}
+
+
+/*!
+    Returns the list of arguments that are set for the process.
+    Arguments can be specified with the constructor or with the
+    functions setArguments() and addArgument().
+
+    Note that if you want to iterate over the list, you should iterate
+    over a copy, e.g.
+    \code
+    QStringList list = myProcess.arguments();
+    QStringList::Iterator it = list.begin();
+    while( it != list.end() ) {
+	myProcessing( *it );
+	++it;
+    }
+    \endcode
+
+    \sa setArguments() addArgument()
+*/
+QStringList JProcess::arguments() const
+{
+    return _arguments;
+}
+
+/*!
+    Clears the list of arguments that are set for the process.
+
+    \sa setArguments() addArgument()
+*/
+void JProcess::clearArguments()
+{
+    _arguments.clear();
+}
+
+/*!
+    Sets \a args as the arguments for the process. The first element
+    in the list is the command to be executed. The other elements in
+    the list are the arguments to the command. Any previous arguments
+    are deleted.
+
+    QProcess does not perform argument substitutions; for example, if you
+    specify "*" or "$DISPLAY", these values are passed to the process
+    literally. If you want to have the same behavior as the shell
+    provides, you must do the substitutions yourself; i.e. instead of
+    specifying a "*" you must specify the list of all the filenames in
+    the current directory, and instead of "$DISPLAY" you must specify
+    the value of the environment variable \c DISPLAY.
+
+    Note for Windows users. The standard Windows shells, e.g. \c
+    command.com and \c cmd.exe, do not perform file globbing, i.e.
+    they do not convert a "*" on the command line into a list of files
+    in the current directory. For this reason most Windows
+    applications implement their own file globbing, and as a result of
+    this, specifying an argument of "*" for a Windows application is
+    likely to result in the application performing a file glob and
+    ending up with a list of filenames.
+
+    \sa arguments() addArgument()
+*/
+void JProcess::setArguments( const QStringList& args )
+{
+    _arguments = args;
+}
+
+/*!
+    Adds \a arg to the end of the list of arguments.
+
+    The first element in the list of arguments is the command to be
+    executed; the following elements are the command's arguments.
+
+    \sa arguments() setArguments()
+*/
+void JProcess::addArgument( const QString& arg )
+{
+    _arguments.append( arg );
+}
+
+#ifndef QT_NO_DIR
+/*!
+    Returns the working directory that was set with
+    setWorkingDirectory(), or the current directory if none has been
+    explicitly set.
+
+    \sa setWorkingDirectory() QDir::current()
+*/
+QDir JProcess::workingDirectory() const
+{
+    return workingDir;
+}
+
+/*!
+    Sets \a dir as the working directory for processes. This does not
+    affect running processes; only processes that are started
+    afterwards are affected.
+
+    Setting the working directory is especially useful for processes
+    that try to access files with relative paths.
+
+    \sa workingDirectory() start()
+*/
+void JProcess::setWorkingDirectory( const QDir& dir )
+{
+    workingDir = dir;
+}
+#endif //QT_NO_DIR
+
+/*!
+    Returns the communication required with the process, i.e. some
+    combination of the \c Communication flags.
+
+    \sa setCommunication()
+*/
+int JProcess::communication() const
+{
+    return comms;
+}
+
+/*!
+    Sets \a commFlags as the communication required with the process.
+
+    \a commFlags is a bitwise OR of the flags defined by the \c
+    Communication enum.
+
+    The default is \c{Stdin|Stdout|Stderr}.
+
+    \sa communication()
+*/
+void JProcess::setCommunication( int commFlags )
+{
+    comms = commFlags;
+}
+
+/*!
+    Returns TRUE if the process has exited normally; otherwise returns
+    FALSE. This implies that this function returns FALSE if the
+    process is still running.
+
+    \sa isRunning() exitStatus() processExited()
+*/
+bool JProcess::normalExit() const
+{
+    // isRunning() has the side effect that it determines the exit status!
+    if ( isRunning() )
+	return FALSE;
+    else
+	return exitNormal;
+}
+
+/*!
+    Returns the exit status of the process or 0 if the process is
+    still running. This function returns immediately and does not wait
+    until the process is finished.
+
+    If normalExit() is FALSE (e.g. if the program was killed or
+    crashed), this function returns 0, so you should check the return
+    value of normalExit() before relying on this value.
+
+    \sa normalExit() processExited()
+*/
+int JProcess::exitStatus() const
+{
+    // isRunning() has the side effect that it determines the exit status!
+    if ( isRunning() )
+	return 0;
+    else
+	return exitStat;
+}
+
+
+/*!
+    Reads the data that the process has written to standard output.
+    When new data is written to standard output, the class emits the
+    signal readyReadStdout().
+
+    If there is no data to read, this function returns a QByteArray of
+    size 0: it does not wait until there is something to read.
+
+    \sa readyReadStdout() readLineStdout() readStderr() writeToStdin()
+*/
+QByteArray JProcess::readStdout()
+{
+    if ( readStdoutCalled ) {
+	return QByteArray();
+    }
+    readStdoutCalled = TRUE;
+
+    QByteArray buf = bufStdout()->copy();
+    consumeBufStdout( -1 ); // consume everything
+
+    readStdoutCalled = FALSE;
+    return buf;
+}
+
+/*!
+    Reads the data that the process has written to standard error.
+    When new data is written to standard error, the class emits the
+    signal readyReadStderr().
+
+    If there is no data to read, this function returns a QByteArray of
+    size 0: it does not wait until there is something to read.
+
+    \sa readyReadStderr() readLineStderr() readStdout() writeToStdin()
+*/
+QByteArray JProcess::readStderr()
+{
+    if ( readStderrCalled ) {
+	return QByteArray();
+    }
+    readStderrCalled = TRUE;
+
+    QByteArray buf = bufStderr()->copy();
+    consumeBufStderr( -1 ); // consume everything
+
+    readStderrCalled = FALSE;
+    return buf;
+}
+
+/*!
+    Reads a line of text from standard output, excluding any trailing
+    newline or carriage return characters, and returns it. Returns
+    QString::null if canReadLineStdout() returns FALSE.
+
+    \sa canReadLineStdout() readyReadStdout() readStdout() readLineStderr()
+*/
+QString JProcess::readLineStdout()
+{
+    QByteArray a;
+    QString s;
+    if ( scanNewline( TRUE, &a ) ) {
+	if ( a.isEmpty() )
+	    s = "";
+	else
+	    s = QString( a );
+    } else if ( canReadLineStdout() ) {
+	s = QString( *bufStdout() );
+	consumeBufStdout( -1 ); // consume everything
+    }
+    return s;
+}
+
+/*!
+    Reads a line of text from standard error, excluding any trailing
+    newline or carriage return characters and returns it. Returns
+    QString::null if canReadLineStderr() returns FALSE.
+
+    \sa canReadLineStderr() readyReadStderr() readStderr() readLineStdout()
+*/
+QString JProcess::readLineStderr()
+{
+    QByteArray a;
+    QString s;
+    if ( scanNewline( FALSE, &a ) ) {
+	if ( a.isEmpty() )
+	    s = "";
+	else
+	    s = QString( a );
+    } else if ( canReadLineStderr() ) {
+	s = QString( *bufStderr() );
+	consumeBufStderr( -1 ); // consume everything
+    }
+    return s;
+}
+
+/*
+  This private function scans for any occurrence of \n or \r\n in the
+  buffer \e buf. It stores the text in the byte array \a store if it is
+  non-null.
+*/
+bool JProcess::scanNewline( bool stdOut, QByteArray *store )
+{
+    QByteArray *buf;
+    if ( stdOut )
+	buf = bufStdout();
+    else
+	buf = bufStderr();
+    uint n = buf->size();
+    uint i;
+    for ( i=0; i<n; i++ ) {
+	if ( buf->at(i) == '\n' ) {
+	    break;
+	}
+    }
+    if ( i >= n )
+	return FALSE;
+
+    if ( store ) {
+	uint lineLength = i;
+	if ( lineLength>0 && buf->at(lineLength-1) == '\r' )
+	    lineLength--; // (if there are two \r, let one stay)
+	store->resize( lineLength );
+	memcpy( store->data(), buf->data(), lineLength );
+	if ( stdOut )
+	    consumeBufStdout( i+1 );
+	else
+	    consumeBufStderr( i+1 );
+    }
+    return TRUE;
+}
+
+/*!
+    \fn void QProcess::launchFinished()
+
+    This signal is emitted when the process was started with launch().
+    If the start was successful, this signal is emitted after all the
+    data has been written to standard input. If the start failed, then
+    this signal is emitted immediately.
+
+    This signal is especially useful if you want to know when you can
+    safely delete the QProcess object when you are not intrested in
+    reading from standard output or standard error.
+
+    \sa launch() QObject::deleteLater()
+*/
+
+/*!
+    Runs the process and writes the data \a buf to the process's
+    standard input. If all the data is written to standard input,
+    standard input is closed. The command is searched for in the path
+    for executable programs; you can also use an absolute path in the
+    command itself.
+
+    If \a env is null, then the process is started with the same
+    environment as the starting process. If \a env is non-null, then
+    the values in the string list are interpreted as environment
+    setttings of the form \c {key=value} and the process is started
+    with these environment settings. For convenience, there is a small
+    exception to this rule under Unix: if \a env does not contain any
+    settings for the environment variable \c LD_LIBRARY_PATH, then
+    this variable is inherited from the starting process.
+
+    Returns TRUE if the process could be started; otherwise returns
+    FALSE.
+
+    Note that you should not use the slots writeToStdin() and
+    closeStdin() on processes started with launch(), since the result
+    is not well-defined. If you need these slots, use start() instead.
+
+    The process may or may not read the \a buf data sent to its
+    standard input.
+
+    You can call this function even when a process that was started
+    with this instance is still running. Be aware that if you do this
+    the standard input of the process that was launched first will be
+    closed, with any pending data being deleted, and the process will
+    be left to run out of your control. Similarly, if the process
+    could not be started the standard input will be closed and the
+    pending data deleted. (On operating systems that have zombie
+    processes, Qt will also wait() on the old process.)
+
+    The object emits the signal launchFinished() when this function
+    call is finished. If the start was successful, this signal is
+    emitted after all the data has been written to standard input. If
+    the start failed, then this signal is emitted immediately.
+
+    \sa start() launchFinished();
+*/
+bool JProcess::launch( const QByteArray& buf, QStringList *env )
+{
+    if ( start( env ) ) {
+	if ( !buf.isEmpty() ) {
+	    connect( this, SIGNAL(wroteToStdin()),
+		    this, SLOT(closeStdinLaunch()) );
+	    writeToStdin( buf );
+	} else {
+	    closeStdin();
+	    emit launchFinished();
+	}
+	return TRUE;
+    } else {
+	emit launchFinished();
+	return FALSE;
+    }
+}
+
+/*!
+    \overload
+
+    The data \a buf is written to standard input with writeToStdin()
+    using the QString::local8Bit() representation of the strings.
+*/
+bool JProcess::launch( const QString& buf, QStringList *env )
+{
+    if ( start( env ) ) {
+	if ( !buf.isEmpty() ) {
+	    connect( this, SIGNAL(wroteToStdin()),
+		    this, SLOT(closeStdinLaunch()) );
+	    writeToStdin( buf );
+	} else {
+	    closeStdin();
+	    emit launchFinished();
+	}
+	return TRUE;
+    } else {
+	emit launchFinished();
+	return FALSE;
+    }
+}
+
+/*
+  This private slot is used by the launch() functions to close standard input.
+*/
+void JProcess::closeStdinLaunch()
+{
+    disconnect( this, SIGNAL(wroteToStdin()),
+	    this, SLOT(closeStdinLaunch()) );
+    closeStdin();
+    emit launchFinished();
+}
+
+
+/*!
+    \fn void QProcess::readyReadStdout()
+
+    This signal is emitted when the process has written data to
+    standard output. You can read the data with readStdout().
+
+    Note that this signal is only emitted when there is new data and
+    not when there is old, but unread data. In the slot connected to
+    this signal, you should always read everything that is available
+    at that moment to make sure that you don't lose any data.
+
+    \sa readStdout() readLineStdout() readyReadStderr()
+*/
+
+/*!
+    \fn void QProcess::readyReadStderr()
+
+    This signal is emitted when the process has written data to
+    standard error. You can read the data with readStderr().
+
+    Note that this signal is only emitted when there is new data and
+    not when there is old, but unread data. In the slot connected to
+    this signal, you should always read everything that is available
+    at that moment to make sure that you don't lose any data.
+
+    \sa readStderr() readLineStderr() readyReadStdout()
+*/
+
+/*!
+    \fn void QProcess::processExited()
+
+    This signal is emitted when the process has exited.
+
+    \sa isRunning() normalExit() exitStatus() start() launch()
+*/
+
+/*!
+    \fn void QProcess::wroteToStdin()
+
+    This signal is emitted if the data sent to standard input (via
+    writeToStdin()) was actually written to the process. This does not
+    imply that the process really read the data, since this class only
+    detects when it was able to write the data to the operating
+    system. But it is now safe to close standard input without losing
+    pending data.
+
+    \sa writeToStdin() closeStdin()
+*/
+
+
+/*!
+    \overload
+
+    The string \a buf is handled as text using the
+    QString::local8Bit() representation.
+*/
+void JProcess::writeToStdin( const QString& buf )
+{
+    QByteArray tmp = buf.local8Bit();
+    tmp.resize( buf.length() );
+    writeToStdin( tmp );
+}
+
+
+/*
+ * Under Windows the implementation is not so nice: it is not that easy to
+ * detect when one of the signals should be emitted; therefore there are some
+ * timers that query the information.
+ * To keep it a little efficient, use the timers only when they are needed.
+ * They are needed, if you are interested in the signals. So use
+ * connectNotify() and disconnectNotify() to keep track of your interest.
+ */
+/*!  \reimp
+*/
+void JProcess::connectNotify( const char * signal )
+{
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcess::connectNotify(): signal %s has been connected", signal );
+#endif
+    if ( !ioRedirection )
+	if ( qstrcmp( signal, SIGNAL(readyReadStdout()) )==0 ||
+		qstrcmp( signal, SIGNAL(readyReadStderr()) )==0
+	   ) {
+#if defined(QT_QPROCESS_DEBUG)
+	    qDebug( "QProcess::connectNotify(): set ioRedirection to TRUE" );
+#endif
+	    setIoRedirection( TRUE );
+	    return;
+	}
+    if ( !notifyOnExit && qstrcmp( signal, SIGNAL(processExited()) )==0 ) {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::connectNotify(): set notifyOnExit to TRUE" );
+#endif
+	setNotifyOnExit( TRUE );
+	return;
+    }
+    if ( !wroteToStdinConnected && qstrcmp( signal, SIGNAL(wroteToStdin()) )==0 ) {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::connectNotify(): set wroteToStdinConnected to TRUE" );
+#endif
+	setWroteStdinConnected( TRUE );
+	return;
+    }
+}
+
+/*!  \reimp
+*/
+void JProcess::disconnectNotify( const char * )
+{
+    if ( ioRedirection &&
+	    receivers( SIGNAL(readyReadStdout()) ) ==0 &&
+	    receivers( SIGNAL(readyReadStderr()) ) ==0
+	    ) {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::disconnectNotify(): set ioRedirection to FALSE" );
+#endif
+	setIoRedirection( FALSE );
+    }
+    if ( notifyOnExit && receivers( SIGNAL(processExited()) ) == 0 ) {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::disconnectNotify(): set notifyOnExit to FALSE" );
+#endif
+	setNotifyOnExit( FALSE );
+    }
+    if ( wroteToStdinConnected && receivers( SIGNAL(wroteToStdin()) ) == 0 ) {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::disconnectNotify(): set wroteToStdinConnected to FALSE" );
+#endif
+	setWroteStdinConnected( FALSE );
+    }
+}
+
+#endif // QT_NO_PROCESS
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/jprocess.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/jprocess.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/jprocess.h	(revision 38)
@@ -0,0 +1,179 @@
+/****************************************************************************
+** $Id: jprocess.h,v 1.5 2003/06/05 02:56:56 justin Exp $
+**
+** Implementation of QProcess class
+**
+** Created : 20000905
+**
+** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef JPROCESS_H
+#define JPROCESS_H
+
+#ifndef QT_H
+#include "qobject.h"
+#include "qstringlist.h"
+#include "qdir.h"
+#endif // QT_H
+
+#ifndef QT_NO_PROCESS
+
+class JProcessPrivate;
+
+
+class Q_EXPORT JProcess : public QObject
+{
+    Q_OBJECT
+public:
+    JProcess( QObject *parent=0, const char *name=0 );
+    JProcess( const QString& arg0, QObject *parent=0, const char *name=0 );
+    JProcess( const QStringList& args, QObject *parent=0, const char *name=0 );
+    ~JProcess();
+
+    // set and get the arguments and working directory
+    QStringList arguments() const;
+    void clearArguments();
+    virtual void setArguments( const QStringList& args );
+    virtual void addArgument( const QString& arg );
+#ifndef QT_NO_DIR
+    QDir workingDirectory() const;
+    virtual void setWorkingDirectory( const QDir& dir );
+#endif
+
+    // set and get the comms wanted
+    enum Communication { Stdin=0x01, Stdout=0x02, Stderr=0x04, DupStderr=0x08 };
+    void setCommunication( int c );
+    int communication() const;
+
+    // start the execution
+    virtual bool start( QStringList *env=0 );
+    virtual bool launch( const QString& buf, QStringList *env=0  );
+    virtual bool launch( const QByteArray& buf, QStringList *env=0  );
+
+    // inquire the status
+    bool isRunning() const;
+    bool normalExit() const;
+    int exitStatus() const;
+
+    // reading
+    virtual QByteArray readStdout();
+    virtual QByteArray readStderr();
+    bool canReadLineStdout() const;
+    bool canReadLineStderr() const;
+    virtual QString readLineStdout();
+    virtual QString readLineStderr();
+
+    // get platform dependent process information
+#if defined(Q_OS_WIN32)
+    typedef void* PID;
+#else
+    typedef Q_LONG PID;
+    void setChildStartingHandler(void (*childStarting)(JProcess *));
+#endif
+    PID processIdentifier();
+
+    void flushStdin();
+
+signals:
+    void readyReadStdout();
+    void readyReadStderr();
+    void processExited();
+    void wroteToStdin();
+    void launchFinished();
+
+public slots:
+    // end the execution
+    void tryTerminate() const;
+    void kill() const;
+
+    // input
+    virtual void writeToStdin( const QByteArray& buf );
+    virtual void writeToStdin( const QString& buf );
+    virtual void closeStdin();
+
+protected: // ### or private?
+    void connectNotify( const char * signal );
+    void disconnectNotify( const char * signal );
+private:
+    void setIoRedirection( bool value );
+    void setNotifyOnExit( bool value );
+    void setWroteStdinConnected( bool value );
+
+    void init();
+    void reset();
+#if defined(Q_OS_WIN32)
+    uint readStddev( HANDLE dev, char *buf, uint bytes );
+#endif
+    bool scanNewline( bool stdOut, QByteArray *store );
+
+    QByteArray* bufStdout();
+    QByteArray* bufStderr();
+    void consumeBufStdout( int consume );
+    void consumeBufStderr( int consume );
+
+private slots:
+    void socketRead( int fd );
+    void socketWrite( int fd );
+    void timeout();
+    void closeStdinLaunch();
+
+private:
+    JProcessPrivate *d;
+#ifndef QT_NO_DIR
+    QDir        workingDir;
+#endif
+    QStringList _arguments;
+
+    int  exitStat; // exit status
+    bool exitNormal; // normal exit?
+    bool ioRedirection; // automatically set be (dis)connectNotify
+    bool notifyOnExit; // automatically set be (dis)connectNotify
+    bool wroteToStdinConnected; // automatically set be (dis)connectNotify
+
+    bool readStdoutCalled;
+    bool readStderrCalled;
+    int comms;
+
+    friend class JProcessPrivate;
+#if defined(Q_OS_UNIX)
+    friend class JProcessManager;
+    friend class JProc;
+#endif
+
+#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
+    JProcess( const JProcess & );
+    JProcess &operator=( const JProcess & );
+#endif
+};
+
+#endif // QT_NO_PROCESS
+
+#endif // QPROCESS_H
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/jprocess_unix.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/jprocess_unix.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/jprocess_unix.cpp	(revision 38)
@@ -0,0 +1,1482 @@
+/****************************************************************************
+** $Id: jprocess_unix.cpp,v 1.13 2004/06/07 07:30:04 justin Exp $
+**
+** Implementation of QProcess class for Unix
+**
+** Created : 20000905
+**
+** Copyright (C) 1992-2003 Trolltech AS.  All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qplatformdefs.h"
+
+// Solaris redefines connect -> __xnet_connect with _XOPEN_SOURCE_EXTENDED.
+#if defined(connect)
+#undef connect
+#endif
+
+//#include "qprocess.h"
+#include "jprocess.h"
+
+#ifndef QT_NO_PROCESS
+
+#include "qapplication.h"
+#include "qptrqueue.h"
+#include "qptrlist.h"
+#include "qsocketnotifier.h"
+#include "qtimer.h"
+#include "qcleanuphandler.h"
+#include "qregexp.h"
+#include "qguardedptr.h"
+
+// table to hold callbacks -- Justin
+struct JP_CALLBACK
+{
+	void (*childStarting)(JProcess *);
+	JProcess *p;
+};
+
+static QPtrList<JP_CALLBACK> jp_callback_list;
+
+static JP_CALLBACK *findCallback(JProcess *p)
+{
+	QPtrListIterator<JP_CALLBACK> it(jp_callback_list);
+	for(JP_CALLBACK *cb; (cb = it.current()); ++it) {
+		if(cb->p == p)
+			return cb;
+	}
+	return 0;
+}
+
+static void setCallback(void (*childStarting)(JProcess *), JProcess *p)
+{
+	JP_CALLBACK *cb = findCallback(p);
+	if(childStarting) {
+		if(!cb) {
+			cb = new JP_CALLBACK;
+			cb->p = p;
+			jp_callback_list.append(cb);
+		}
+		cb->childStarting = childStarting;
+	}
+	else {
+		if(cb) {
+			jp_callback_list.removeRef(cb);
+			delete cb;
+		}
+	}
+}
+
+static void callCallback(JProcess *p)
+{
+	JP_CALLBACK *cb = findCallback(p);
+	if(cb)
+		cb->childStarting(p);
+}
+
+
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef __MIPSEL__
+# ifndef SOCK_DGRAM
+#  define SOCK_DGRAM 1
+# endif
+# ifndef SOCK_STREAM
+#  define SOCK_STREAM 2
+# endif
+#endif
+
+//#define QT_QPROCESS_DEBUG
+
+// Don't need this -- Justin
+/*
+#ifdef Q_C_CALLBACKS
+extern "C" {
+#endif // Q_C_CALLBACKS
+
+    QT_SIGNAL_RETTYPE qt_C_sigchldHnd(QT_SIGNAL_ARGS);
+
+#ifdef Q_C_CALLBACKS
+}
+#endif // Q_C_CALLBACKS
+*/
+
+class JProc;
+class JProcessManager;
+class JProcessPrivate
+{
+public:
+    JProcessPrivate();
+    ~JProcessPrivate();
+
+    void closeOpenSocketsForChild();
+    void newProc( pid_t pid, JProcess *process );
+
+    QByteArray bufStdout;
+    QByteArray bufStderr;
+
+    QPtrQueue<QByteArray> stdinBuf;
+
+    QSocketNotifier *notifierStdin;
+    QSocketNotifier *notifierStdout;
+    QSocketNotifier *notifierStderr;
+
+    ssize_t stdinBufRead;
+    JProc *proc;
+
+    bool exitValuesCalculated;
+    bool socketReadCalled;
+
+    static JProcessManager *procManager;
+};
+
+/***********************************************************************
+ *
+ * QProc
+ *
+ **********************************************************************/
+/*
+  The class QProcess does not necessarily map exactly to the running
+  child processes: if the process is finished, the QProcess class may still be
+  there; furthermore a user can use QProcess to start more than one process.
+
+  The helper-class QProc has the semantics that one instance of this class maps
+  directly to a running child process.
+*/
+class JProc
+{
+public:
+    JProc( pid_t p, JProcess *proc=0 ) : pid(p), process(proc)
+    {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProc: Constructor for pid %d and QProcess %p", pid, process );
+#endif
+	socketStdin = 0;
+	socketStdout = 0;
+	socketStderr = 0;
+    }
+    ~JProc()
+    {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProc: Destructor for pid %d and QProcess %p", pid, process );
+#endif
+	if ( process != 0 ) {
+	    if ( process->d->notifierStdin )
+		process->d->notifierStdin->setEnabled( FALSE );
+	    if ( process->d->notifierStdout )
+		process->d->notifierStdout->setEnabled( FALSE );
+	    if ( process->d->notifierStderr )
+		process->d->notifierStderr->setEnabled( FALSE );
+	    process->d->proc = 0;
+	}
+	if( socketStdin != 0 )
+	    ::close( socketStdin );
+	// ### close these sockets even on parent exit or is it better only on
+	// sigchld (but what do I have to do with them on exit then)?
+	if( socketStdout != 0 )
+	    ::close( socketStdout );
+	if( socketStderr != 0 )
+	    ::close( socketStderr );
+    }
+
+    pid_t pid;
+    int socketStdin;
+    int socketStdout;
+    int socketStderr;
+    JProcess *process;
+};
+
+/***********************************************************************
+ *
+ * QProcessManager
+ *
+ **********************************************************************/
+class JProcessManager : public QObject
+{
+    Q_OBJECT
+
+public:
+    JProcessManager();
+    ~JProcessManager();
+
+    void append( JProc *p );
+    void remove( JProc *p );
+
+    void cleanup();
+
+public slots:
+    void removeMe();
+    void sigchldHnd( int );
+
+public:
+    struct sigaction oldactChld;
+    struct sigaction oldactPipe;
+    QPtrList<JProc> *procList;
+    int sigchldFd[2];
+
+private slots:
+    void cleanupProcesses();
+
+private:
+    QSocketNotifier *sn;
+    QTimer *pollTimer;
+};
+
+static QCleanupHandler<JProcessManager> qprocess_cleanup_procmanager;
+
+#ifdef Q_OS_QNX6
+#define BAILOUT close(tmpSocket);close(socketFD[1]);return -1;
+int qnx6SocketPairReplacement (int socketFD[2]) {
+    int tmpSocket;
+    tmpSocket = socket (AF_INET, SOCK_STREAM, 0);
+    if (tmpSocket == -1)
+	return -1;
+    socketFD[1] = socket(AF_INET, SOCK_STREAM, 0);
+    if (socketFD[1] == -1) { BAILOUT };
+
+    sockaddr_in ipAddr;
+    memset(&ipAddr, 0, sizeof(ipAddr));
+    ipAddr.sin_family = AF_INET;
+    ipAddr.sin_addr.s_addr = INADDR_ANY;
+
+    int socketOptions = 1;
+    setsockopt(tmpSocket, SOL_SOCKET, SO_REUSEADDR, &socketOptions, sizeof(int));
+
+    bool found = FALSE;
+    for (int socketIP = 2000; (socketIP < 2500) && !(found); socketIP++) {
+	ipAddr.sin_port = htons(socketIP);
+	if (bind(tmpSocket, (struct sockaddr *)&ipAddr, sizeof(ipAddr)))
+	    found = TRUE;
+    }
+
+    if (listen(tmpSocket, 5)) { BAILOUT };
+
+    // Select non-blocking mode
+    int originalFlags = fcntl(socketFD[1], F_GETFL, 0);
+    fcntl(socketFD[1], F_SETFL, originalFlags | O_NONBLOCK);
+
+    // Request connection
+    if (connect(socketFD[1], (struct sockaddr*)&ipAddr, sizeof(ipAddr)))
+	if (errno != EINPROGRESS) { BAILOUT };
+
+    // Accept connection
+    socketFD[0] = accept(tmpSocket, (struct sockaddr *)NULL, (size_t *)NULL);
+    if(socketFD[0] == -1) { BAILOUT };
+
+    // We're done
+    close(tmpSocket);
+
+    // Restore original flags , ie return to blocking
+    fcntl(socketFD[1], F_SETFL, originalFlags);
+    return 0;
+}
+#undef BAILOUT
+#endif
+
+JProcessManager::JProcessManager() : sn(0)
+{
+    procList = new QPtrList<JProc>;
+    procList->setAutoDelete( TRUE );
+
+    // really lame workaround -- Justin
+    pollTimer = new QTimer(this);
+    connect(pollTimer, SIGNAL(timeout()), SLOT(cleanupProcesses()));
+
+    // The SIGCHLD handler writes to a socket to tell the manager that
+    // something happened. This is done to get the processing in sync with the
+    // event reporting.
+#ifndef Q_OS_QNX6
+    if ( ::socketpair( AF_UNIX, SOCK_STREAM, 0, sigchldFd ) ) {
+#else
+    if ( qnx6SocketPairReplacement (sigchldFd) ) {
+#endif
+	sigchldFd[0] = 0;
+	sigchldFd[1] = 0;
+    } else {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcessManager: install socket notifier (%d)", sigchldFd[1] );
+#endif
+	sn = new QSocketNotifier( sigchldFd[1],
+		QSocketNotifier::Read, this );
+	connect( sn, SIGNAL(activated(int)),
+		this, SLOT(sigchldHnd(int)) );
+	sn->setEnabled( TRUE );
+    }
+
+    // install a SIGCHLD handler and ignore SIGPIPE
+    struct sigaction act;
+
+    // NO SIGCHLD -- Justin
+/*
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcessManager: install a SIGCHLD handler" );
+#endif
+    act.sa_handler = qt_C_sigchldHnd;
+    sigemptyset( &(act.sa_mask) );
+    sigaddset( &(act.sa_mask), SIGCHLD );
+    act.sa_flags = SA_NOCLDSTOP;
+#if defined(SA_RESTART)
+    act.sa_flags |= SA_RESTART;
+#endif
+    if ( sigaction( SIGCHLD, &act, &oldactChld ) != 0 )
+	qWarning( "Error installing SIGCHLD handler" );
+*/
+
+    // obtain current handler
+    if ( sigaction( SIGPIPE, NULL, &act ) == 0 ) {
+     // already ignoring?
+     if(act.sa_handler == QT_SIGNAL_IGNORE) {
+       // do nothing
+     }
+     else {
+#if defined(QT_QPROCESS_DEBUG)
+      qDebug( "QProcessManager: install a SIGPIPE handler (SIG_IGN)" );
+#endif
+      act.sa_handler = QT_SIGNAL_IGNORE;
+      sigemptyset( &(act.sa_mask) );
+      sigaddset( &(act.sa_mask), SIGPIPE );
+      act.sa_flags = 0;
+      if ( sigaction( SIGPIPE, &act, &oldactPipe ) != 0 )
+	  qWarning( "Error installing SIGPIPE handler" );
+     }
+    }
+
+    pollTimer->start(100);
+}
+
+JProcessManager::~JProcessManager()
+{
+    delete procList;
+
+    if ( sigchldFd[0] != 0 )
+	::close( sigchldFd[0] );
+    if ( sigchldFd[1] != 0 )
+	::close( sigchldFd[1] );
+
+// NO SIGCHLD -- Justin
+/*
+    // restore SIGCHLD handler
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcessManager: restore old sigchild handler" );
+#endif
+    if ( sigaction( SIGCHLD, &oldactChld, 0 ) != 0 )
+	qWarning( "Error restoring SIGCHLD handler" );
+*/
+
+// NO SIGPIPE restore (we might trash the one from QProcess)
+/*
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcessManager: restore old sigpipe handler" );
+#endif
+    if ( sigaction( SIGPIPE, &oldactPipe, 0 ) != 0 )
+	qWarning( "Error restoring SIGPIPE handler" );
+*/
+}
+
+void JProcessManager::append( JProc *p )
+{
+    procList->append( p );
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcessManager: append process (procList.count(): %d)", procList->count() );
+#endif
+}
+
+void JProcessManager::remove( JProc *p )
+{
+    procList->remove( p );
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcessManager: remove process (procList.count(): %d)", procList->count() );
+#endif
+    cleanup();
+}
+
+void JProcessManager::cleanup()
+{
+    if ( procList->count() == 0 ) {
+	QTimer::singleShot( 0, this, SLOT(removeMe()) );
+    }
+}
+
+void JProcessManager::removeMe()
+{
+    if ( procList->count() == 0 ) {
+	qprocess_cleanup_procmanager.remove( &JProcessPrivate::procManager );
+	JProcessPrivate::procManager = 0;
+	delete this;
+    }
+}
+
+void JProcessManager::sigchldHnd( int fd )
+{
+    // Disable the socket notifier to make sure that this function is not
+    // called recursively -- this can happen, if you enter the event loop in
+    // the slot connected to the processExited() signal (e.g. by showing a
+    // modal dialog) and there are more than one process which exited in the
+    // meantime.
+    if ( sn )
+	sn->setEnabled( FALSE );
+
+    char tmp;
+    ::read( fd, &tmp, sizeof(tmp) );
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcessManager::sigchldHnd()" );
+#endif
+    JProc *proc;
+    JProcess *process;
+    bool removeProc;
+    proc = procList->first();
+    while ( proc != 0 ) {
+	removeProc = FALSE;
+	process = proc->process;
+	if ( process != 0 ) {
+	    if ( !process->isRunning() ) {
+#if defined(QT_QPROCESS_DEBUG)
+		qDebug( "QProcessManager::sigchldHnd() (PID: %d): process exited (QProcess available)", proc->pid );
+#endif
+		/*
+		  Apparently, there is not consistency among different
+		  operating systems on how to use FIONREAD.
+
+		  FreeBSD, Linux and Solaris all expect the 3rd
+		  argument to ioctl() to be an int, which is normally
+		  32-bit even on 64-bit machines.
+
+		  IRIX, on the other hand, expects a size_t, which is
+		  64-bit on 64-bit machines.
+
+		  So, the solution is to use size_t initialized to
+		  zero to make sure all bits are set to zero,
+		  preventing underflow with the FreeBSD/Linux/Solaris
+		  ioctls.
+		*/
+		size_t nbytes = 0;
+		// read pending data
+		if ( ::ioctl(proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {
+#if defined(QT_QPROCESS_DEBUG)
+		    qDebug( "QProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stdout", proc->pid, nbytes );
+#endif
+		    process->socketRead( proc->socketStdout );
+		}
+		nbytes = 0;
+		if ( ::ioctl(proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 ) {
+#if defined(QT_QPROCESS_DEBUG)
+		    qDebug( "QProcessManager::sigchldHnd() (PID: %d): reading %d bytes of pending data on stderr", proc->pid, nbytes );
+#endif
+		    process->socketRead( proc->socketStderr );
+		}
+
+		if ( process->notifyOnExit )
+		    emit process->processExited();
+
+		removeProc = TRUE;
+	    }
+	} else {
+	    int status;
+	    if ( ::waitpid( proc->pid, &status, WNOHANG ) == proc->pid ) {
+#if defined(QT_QPROCESS_DEBUG)
+		qDebug( "QProcessManager::sigchldHnd() (PID: %d): process exited (QProcess not available)", proc->pid );
+#endif
+		removeProc = TRUE;
+	    }
+	}
+	if ( removeProc ) {
+	    JProc *oldproc = proc;
+	    proc = procList->next();
+	    remove( oldproc );
+	} else {
+	    proc = procList->next();
+	}
+    }
+    if ( sn )
+	sn->setEnabled( TRUE );
+}
+
+void JProcessManager::cleanupProcesses()
+{
+    if ( JProcessPrivate::procManager == 0 )
+	return;
+    if ( JProcessPrivate::procManager->sigchldFd[0] == 0 )
+	return;
+
+    char a = 1;
+    ::write( JProcessPrivate::procManager->sigchldFd[0], &a, sizeof(a) );
+}
+
+#include "jprocess_unix.moc"
+
+
+/***********************************************************************
+ *
+ * QProcessPrivate
+ *
+ **********************************************************************/
+JProcessManager *JProcessPrivate::procManager = 0;
+
+JProcessPrivate::JProcessPrivate()
+{
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcessPrivate: Constructor" );
+#endif
+    stdinBufRead = 0;
+
+    notifierStdin = 0;
+    notifierStdout = 0;
+    notifierStderr = 0;
+
+    exitValuesCalculated = FALSE;
+    socketReadCalled = FALSE;
+
+    proc = 0;
+}
+
+JProcessPrivate::~JProcessPrivate()
+{
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcessPrivate: Destructor" );
+#endif
+
+    if ( proc != 0 ) {
+	if ( proc->socketStdin != 0 ) {
+	    ::close( proc->socketStdin );
+	    proc->socketStdin = 0;
+	}
+	proc->process = 0;
+    }
+
+    while ( !stdinBuf.isEmpty() ) {
+	delete stdinBuf.dequeue();
+    }
+    delete notifierStdin;
+    delete notifierStdout;
+    delete notifierStderr;
+}
+
+/*
+  Closes all open sockets in the child process that are not needed by the child
+  process. Otherwise one child may have an open socket on standard input, etc.
+  of another child.
+*/
+void JProcessPrivate::closeOpenSocketsForChild()
+{
+    if ( procManager != 0 ) {
+	if ( procManager->sigchldFd[0] != 0 )
+	    ::close( procManager->sigchldFd[0] );
+	if ( procManager->sigchldFd[1] != 0 )
+	    ::close( procManager->sigchldFd[1] );
+
+	// close also the sockets from other QProcess instances
+	for ( JProc *p=procManager->procList->first(); p!=0; p=procManager->procList->next() ) {
+	    ::close( p->socketStdin );
+	    ::close( p->socketStdout );
+	    ::close( p->socketStderr );
+	}
+    }
+}
+
+void JProcessPrivate::newProc( pid_t pid, JProcess *process )
+{
+    proc = new JProc( pid, process );
+    if ( procManager == 0 ) {
+	procManager = new JProcessManager;
+	qprocess_cleanup_procmanager.add( &procManager );
+    }
+    // the QProcessManager takes care of deleting the QProc instances
+    procManager->append( proc );
+}
+
+/***********************************************************************
+ *
+ * sigchld handler callback
+ *
+ **********************************************************************/
+// Don't need this -- Justin
+/*QT_SIGNAL_RETTYPE qt_C_sigchldHnd( QT_SIGNAL_ARGS )
+{
+    if ( JProcessPrivate::procManager == 0 )
+	return;
+    if ( JProcessPrivate::procManager->sigchldFd[0] == 0 )
+	return;
+
+    char a = 1;
+    ::write( JProcessPrivate::procManager->sigchldFd[0], &a, sizeof(a) );
+}*/
+
+/***********************************************************************
+ *
+ * QProcess
+ *
+ **********************************************************************/
+/*
+  This private class does basic initialization.
+*/
+void JProcess::init()
+{
+    d = new JProcessPrivate();
+    exitStat = 0;
+    exitNormal = FALSE;
+}
+
+/*
+  This private class resets the process variables, etc. so that it can be used
+  for another process to start.
+*/
+void JProcess::reset()
+{
+    delete d;
+    d = new JProcessPrivate();
+    exitStat = 0;
+    exitNormal = FALSE;
+    d->bufStdout.resize( 0 );
+    d->bufStderr.resize( 0 );
+}
+
+QByteArray* JProcess::bufStdout()
+{
+    if ( d->proc && d->proc->socketStdout ) {
+	/*
+	  Apparently, there is not consistency among different
+	  operating systems on how to use FIONREAD.
+
+	  FreeBSD, Linux and Solaris all expect the 3rd argument to
+	  ioctl() to be an int, which is normally 32-bit even on
+	  64-bit machines.
+
+	  IRIX, on the other hand, expects a size_t, which is 64-bit
+	  on 64-bit machines.
+
+	  So, the solution is to use size_t initialized to zero to
+	  make sure all bits are set to zero, preventing underflow
+	  with the FreeBSD/Linux/Solaris ioctls.
+	*/
+	size_t nbytes = 0;
+	if ( ::ioctl(d->proc->socketStdout, FIONREAD, (char*)&nbytes)==0 && nbytes>0 )
+	    socketRead( d->proc->socketStdout );
+    }
+    return &d->bufStdout;
+}
+
+QByteArray* JProcess::bufStderr()
+{
+    if ( d->proc && d->proc->socketStderr ) {
+	/*
+	  Apparently, there is not consistency among different
+	  operating systems on how to use FIONREAD.
+
+	  FreeBSD, Linux and Solaris all expect the 3rd argument to
+	  ioctl() to be an int, which is normally 32-bit even on
+	  64-bit machines.
+
+	  IRIX, on the other hand, expects a size_t, which is 64-bit
+	  on 64-bit machines.
+
+	  So, the solution is to use size_t initialized to zero to
+	  make sure all bits are set to zero, preventing underflow
+	  with the FreeBSD/Linux/Solaris ioctls.
+	*/
+	size_t nbytes = 0;
+	if ( ::ioctl(d->proc->socketStderr, FIONREAD, (char*)&nbytes)==0 && nbytes>0 )
+	    socketRead( d->proc->socketStderr );
+    }
+    return &d->bufStderr;
+}
+
+void JProcess::consumeBufStdout( int consume )
+{
+    uint n = d->bufStdout.size();
+    if ( consume==-1 || (uint)consume >= n ) {
+	d->bufStdout.resize( 0 );
+    } else {
+	QByteArray tmp( n - consume );
+	memcpy( tmp.data(), d->bufStdout.data()+consume, n-consume );
+	d->bufStdout = tmp;
+    }
+}
+
+void JProcess::consumeBufStderr( int consume )
+{
+    uint n = d->bufStderr.size();
+    if ( consume==-1 || (uint)consume >= n ) {
+	d->bufStderr.resize( 0 );
+    } else {
+	QByteArray tmp( n - consume );
+	memcpy( tmp.data(), d->bufStderr.data()+consume, n-consume );
+	d->bufStderr = tmp;
+    }
+}
+
+/*!
+    Destroys the instance.
+
+    If the process is running, it is <b>not</b> terminated! The
+    standard input, standard output and standard error of the process
+    are closed.
+
+    You can connect the destroyed() signal to the kill() slot, if you
+    want the process to be terminated automatically when the instance
+    is destroyed.
+
+    \sa tryTerminate() kill()
+*/
+JProcess::~JProcess()
+{
+    setCallback(0, this);
+    delete d;
+}
+
+/*!
+    Tries to run a process for the command and arguments that were
+    specified with setArguments(), addArgument() or that were
+    specified in the constructor. The command is searched for in the
+    path for executable programs; you can also use an absolute path in
+    the command itself.
+
+    If \a env is null, then the process is started with the same
+    environment as the starting process. If \a env is non-null, then
+    the values in the stringlist are interpreted as environment
+    setttings of the form \c {key=value} and the process is started in
+    these environment settings. For convenience, there is a small
+    exception to this rule: under Unix, if \a env does not contain any
+    settings for the environment variable \c LD_LIBRARY_PATH, then
+    this variable is inherited from the starting process; under
+    Windows the same applies for the environment variable \c PATH.
+
+    Returns TRUE if the process could be started; otherwise returns
+    FALSE.
+
+    You can write data to the process's standard input with
+    writeToStdin(). You can close standard input with closeStdin() and
+    you can terminate the process with tryTerminate(), or with kill().
+
+    You can call this function even if you've used this instance to
+    create a another process which is still running. In such cases,
+    QProcess closes the old process's standard input and deletes
+    pending data, i.e., you lose all control over the old process, but
+    the old process is not terminated. This applies also if the
+    process could not be started. (On operating systems that have
+    zombie processes, Qt will also wait() on the old process.)
+
+    \sa launch() closeStdin()
+*/
+bool JProcess::start( QStringList *env )
+{
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcess::start()" );
+#endif
+    reset();
+
+    int sStdin[2];
+    int sStdout[2];
+    int sStderr[2];
+
+    // open sockets for piping
+#ifndef Q_OS_QNX6
+    if ( (comms & Stdin) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdin ) == -1 ) {
+#else
+    if ( (comms & Stdin) && qnx6SocketPairReplacement(sStdin) == -1 ) {
+#endif
+	return FALSE;
+    }
+#ifndef Q_OS_QNX6
+    if ( (comms & Stderr) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStderr ) == -1 ) {
+#else
+    if ( (comms & Stderr) && qnx6SocketPairReplacement(sStderr) == -1 ) {
+#endif
+	return FALSE;
+    }
+#ifndef Q_OS_QNX6
+    if ( (comms & Stdout) && ::socketpair( AF_UNIX, SOCK_STREAM, 0, sStdout ) == -1 ) {
+#else
+    if ( (comms & Stdout) && qnx6SocketPairReplacement(sStdout) == -1 ) {
+#endif
+	return FALSE;
+    }
+
+    // the following pipe is only used to determine if the process could be
+    // started
+    int fd[2];
+    if ( pipe( fd ) < 0 ) {
+	// non critical error, go on
+	fd[0] = 0;
+	fd[1] = 0;
+    }
+
+    // construct the arguments for exec
+    QCString *arglistQ = new QCString[ _arguments.count() + 1 ];
+    const char** arglist = new const char*[ _arguments.count() + 1 ];
+    int i = 0;
+    for ( QStringList::Iterator it = _arguments.begin(); it != _arguments.end(); ++it ) {
+	arglistQ[i] = (*it).local8Bit();
+	arglist[i] = arglistQ[i];
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::start(): arg %d = %s", i, arglist[i] );
+#endif
+	i++;
+    }
+    arglist[i] = 0;
+
+    // Must make sure signal handlers are installed before exec'ing
+    // in case the process exits quickly.
+    if ( d->procManager == 0 ) {
+	d->procManager = new JProcessManager;
+	qprocess_cleanup_procmanager.add( &d->procManager );
+    }
+
+    // fork and exec
+    QApplication::flushX();
+    pid_t pid = fork();
+    if ( pid == 0 ) {
+        callCallback(this);
+	// child
+	d->closeOpenSocketsForChild();
+	if ( comms & Stdin ) {
+	    ::close( sStdin[1] );
+	    ::dup2( sStdin[0], STDIN_FILENO );
+	}
+	if ( comms & Stdout ) {
+	    ::close( sStdout[0] );
+	    ::dup2( sStdout[1], STDOUT_FILENO );
+	}
+	if ( comms & Stderr ) {
+	    ::close( sStderr[0] );
+	    ::dup2( sStderr[1], STDERR_FILENO );
+	}
+	if ( comms & DupStderr ) {
+	    ::dup2( STDOUT_FILENO, STDERR_FILENO );
+	}
+#ifndef QT_NO_DIR
+	::chdir( workingDir.absPath().latin1() );
+#endif
+	if ( fd[0] )
+	    ::close( fd[0] );
+	if ( fd[1] )
+	    ::fcntl( fd[1], F_SETFD, FD_CLOEXEC ); // close on exec shows sucess
+
+	if ( env == 0 ) { // inherit environment and start process
+	    QString command = _arguments[0];
+#if defined(Q_OS_MACX) //look in a bundle
+	    const QString mac_bundle_suffix = ".app/Contents/MacOS/";
+	    if(!QFile::exists(command) && QFile::exists(command + mac_bundle_suffix)) {
+		QString exec = command;
+		int lslash = command.findRev('/');
+		if(lslash != -1)
+		    exec = command.mid(lslash+1);
+		QFileInfo fileInfo( command + mac_bundle_suffix + exec );
+		if ( fileInfo.isExecutable() )
+		    command = fileInfo.absFilePath().local8Bit();
+	    }
+#endif
+#ifndef Q_OS_QNX4
+	    ::execvp( command, (char*const*)arglist ); // ### cast not nice
+#else
+	    ::execvp( command, (char const*const*)arglist ); // ### cast not nice
+#endif
+	} else { // start process with environment settins as specified in env
+	    // construct the environment for exec
+	    int numEntries = env->count();
+#if defined(Q_OS_MACX)
+	    QString ld_library_path("DYLD_LIBRARY_PATH");
+#else
+	    QString ld_library_path("LD_LIBRARY_PATH");
+#endif
+	    bool setLibraryPath =
+		env->grep( QRegExp( "^" + ld_library_path + "=" ) ).empty() &&
+		getenv( ld_library_path ) != 0;
+	    if ( setLibraryPath )
+		numEntries++;
+	    QCString *envlistQ = new QCString[ numEntries + 1 ];
+	    const char** envlist = new const char*[ numEntries + 1 ];
+	    int i = 0;
+	    if ( setLibraryPath ) {
+		envlistQ[i] = QString( ld_library_path + "=%1" ).arg( getenv( ld_library_path ) ).local8Bit();
+		envlist[i] = envlistQ[i];
+		i++;
+	    }
+	    for ( QStringList::Iterator it = env->begin(); it != env->end(); ++it ) {
+		envlistQ[i] = (*it).local8Bit();
+		envlist[i] = envlistQ[i];
+		i++;
+	    }
+	    envlist[i] = 0;
+
+	    // look for the executable in the search path
+	    if ( _arguments.count()>0 && getenv("PATH")!=0 ) {
+		QString command = _arguments[0];
+		if ( !command.contains( '/' ) ) {
+		    QStringList pathList = QStringList::split( ':', getenv( "PATH" ) );
+		    for (QStringList::Iterator it = pathList.begin(); it != pathList.end(); ++it ) {
+			QString dir = *it;
+#if defined(Q_OS_MACX) //look in a bundle
+			if(!QFile::exists(dir + "/" + command) && QFile::exists(dir + "/" + command + ".app"))
+			    dir += "/" + command + ".app/Contents/MacOS";
+#endif
+#ifndef QT_NO_DIR
+			QFileInfo fileInfo( dir, command );
+#else
+			QFileInfo fileInfo( dir + "/" + command );
+#endif
+			if ( fileInfo.isExecutable() ) {
+#if defined(Q_OS_MACX)
+			    arglistQ[0] = fileInfo.absFilePath().local8Bit();
+#else
+			    arglistQ[0] = fileInfo.filePath().local8Bit();
+#endif
+			    arglist[0] = arglistQ[0];
+			    break;
+			}
+		    }
+		}
+	    }
+#if defined(Q_OS_MACX)
+	    if(!QFile::exists(arglist[0])) {
+		QString command = arglist[0];
+		const QString mac_bundle_suffix = ".app/Contents/MacOS/";
+		if(QFile::exists(command + mac_bundle_suffix)) {
+		    QString exec = command;
+		    int lslash = command.findRev('/');
+		    if(lslash != -1)
+			exec = command.mid(lslash+1);
+		    QFileInfo fileInfo( command + mac_bundle_suffix + exec );
+		    if ( fileInfo.isExecutable() ) {
+			arglistQ[0] = fileInfo.absFilePath().local8Bit();
+			arglist[0] = arglistQ[0];
+		    }
+		}
+	    }
+#endif
+#ifndef Q_OS_QNX4
+	    ::execve( arglist[0], (char*const*)arglist, (char*const*)envlist ); // ### casts not nice
+#else
+	    ::execve( arglist[0], (char const*const*)arglist,(char const*const*)envlist ); // ### casts not nice
+#endif
+	}
+	if ( fd[1] ) {
+	    char buf = 0;
+	    ::write( fd[1], &buf, 1 );
+	    ::close( fd[1] );
+	}
+	::_exit( -1 );
+    } else if ( pid == -1 ) {
+	// error forking
+	goto error;
+    }
+
+    // test if exec was successful
+    if ( fd[1] )
+	::close( fd[1] );
+    if ( fd[0] ) {
+	char buf;
+	for ( ;; ) {
+	    int n = ::read( fd[0], &buf, 1 );
+	    if ( n==1 ) {
+		// socket was not closed => error
+		::waitpid( pid, 0, WNOHANG );
+		d->proc = 0;
+		goto error;
+	    } else if ( n==-1 ) {
+		if ( errno==EAGAIN || errno==EINTR )
+		    // try it again
+		    continue;
+	    }
+	    break;
+	}
+	::close( fd[0] );
+    }
+
+    d->newProc( pid, this );
+
+    if ( comms & Stdin ) {
+	::close( sStdin[0] );
+	d->proc->socketStdin = sStdin[1];
+
+	// Select non-blocking mode
+	int originalFlags = fcntl(d->proc->socketStdin, F_GETFL, 0);
+	fcntl(d->proc->socketStdin, F_SETFL, originalFlags | O_NONBLOCK);
+
+	d->notifierStdin = new QSocketNotifier( sStdin[1], QSocketNotifier::Write );
+	connect( d->notifierStdin, SIGNAL(activated(int)),
+		this, SLOT(socketWrite(int)) );
+	// setup notifiers for the sockets
+	if ( !d->stdinBuf.isEmpty() ) {
+	    d->notifierStdin->setEnabled( TRUE );
+	}
+    }
+    if ( comms & Stdout ) {
+	::close( sStdout[1] );
+	d->proc->socketStdout = sStdout[0];
+	d->notifierStdout = new QSocketNotifier( sStdout[0], QSocketNotifier::Read );
+	connect( d->notifierStdout, SIGNAL(activated(int)),
+		this, SLOT(socketRead(int)) );
+	if ( ioRedirection )
+	    d->notifierStdout->setEnabled( TRUE );
+    }
+    if ( comms & Stderr ) {
+	::close( sStderr[1] );
+	d->proc->socketStderr = sStderr[0];
+	d->notifierStderr = new QSocketNotifier( sStderr[0], QSocketNotifier::Read );
+	connect( d->notifierStderr, SIGNAL(activated(int)),
+		this, SLOT(socketRead(int)) );
+	if ( ioRedirection )
+	    d->notifierStderr->setEnabled( TRUE );
+    }
+
+    // cleanup and return
+    delete[] arglistQ;
+    delete[] arglist;
+    return TRUE;
+
+error:
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcess::start(): error starting process" );
+#endif
+    if ( d->procManager )
+	d->procManager->cleanup();
+    if ( comms & Stdin ) {
+	::close( sStdin[1] );
+	::close( sStdin[0] );
+    }
+    if ( comms & Stdout ) {
+	::close( sStdout[0] );
+	::close( sStdout[1] );
+    }
+    if ( comms & Stderr ) {
+	::close( sStderr[0] );
+	::close( sStderr[1] );
+    }
+    ::close( fd[0] );
+    ::close( fd[1] );
+    delete[] arglistQ;
+    delete[] arglist;
+    return FALSE;
+}
+
+
+/*!
+    Asks the process to terminate. Processes can ignore this if they
+    wish. If you want to be certain that the process really
+    terminates, you can use kill() instead.
+
+    The slot returns immediately: it does not wait until the process
+    has finished. When the process terminates, the processExited()
+    signal is emitted.
+
+    \sa kill() processExited()
+*/
+void JProcess::tryTerminate() const
+{
+    if ( d->proc != 0 )
+	::kill( d->proc->pid, SIGTERM );
+}
+
+/*!
+    Terminates the process. This is not a safe way to end a process
+    since the process will not be able to do any cleanup.
+    tryTerminate() is safer, but processes can ignore a
+    tryTerminate().
+
+    The nice way to end a process and to be sure that it is finished,
+    is to do something like this:
+    \code
+	process->tryTerminate();
+	QTimer::singleShot( 5000, process, SLOT( kill() ) );
+    \endcode
+
+    This tries to terminate the process the nice way. If the process
+    is still running after 5 seconds, it terminates the process the
+    hard way. The timeout should be chosen depending on the time the
+    process needs to do all its cleanup: use a higher value if the
+    process is likely to do a lot of computation or I/O on cleanup.
+
+    The slot returns immediately: it does not wait until the process
+    has finished. When the process terminates, the processExited()
+    signal is emitted.
+
+    \sa tryTerminate() processExited()
+*/
+void JProcess::kill() const
+{
+    if ( d->proc != 0 )
+	::kill( d->proc->pid, SIGKILL );
+}
+
+/*!
+    Returns TRUE if the process is running; otherwise returns FALSE.
+
+    \sa normalExit() exitStatus() processExited()
+*/
+bool JProcess::isRunning() const
+{
+    if ( d->exitValuesCalculated ) {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::isRunning(): FALSE (already computed)" );
+#endif
+	return FALSE;
+    }
+    if ( d->proc == 0 )
+	return FALSE;
+    int status;
+    if ( ::waitpid( d->proc->pid, &status, WNOHANG ) == d->proc->pid )
+    {
+	// compute the exit values
+	JProcess *that = (JProcess*)this; // mutable
+	that->exitNormal = WIFEXITED( status ) != 0;
+	if ( exitNormal ) {
+	    that->exitStat = (char)WEXITSTATUS( status );
+	}
+	d->exitValuesCalculated = TRUE;
+
+	// On heavy processing, the socket notifier for the sigchild might not
+	// have found time to fire yet.
+	if ( d->procManager ) {
+	    fd_set fds;
+	    struct timeval tv;
+	    FD_ZERO( &fds );
+	    FD_SET( d->procManager->sigchldFd[1], &fds );
+	    tv.tv_sec = 0;
+	    tv.tv_usec = 0;
+	    while ( ::select( d->procManager->sigchldFd[1]+1, &fds, 0, 0, &tv ) > 0 )
+		d->procManager->sigchldHnd( d->procManager->sigchldFd[1] );
+	}
+
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::isRunning() (PID: %d): FALSE", d->proc->pid );
+#endif
+	return FALSE;
+    }
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcess::isRunning() (PID: %d): TRUE", d->proc->pid );
+#endif
+    return TRUE;
+}
+
+/*!
+    Returns TRUE if it's possible to read an entire line of text from
+    standard output at this time; otherwise returns FALSE.
+
+    \sa readLineStdout() canReadLineStderr()
+*/
+bool JProcess::canReadLineStdout() const
+{
+    if ( !d->proc || !d->proc->socketStdout )
+	return !d->bufStdout.isEmpty();
+
+    JProcess *that = (JProcess*)this;
+    return that->scanNewline( TRUE, 0 );
+}
+
+/*!
+    Returns TRUE if it's possible to read an entire line of text from
+    standard error at this time; otherwise returns FALSE.
+
+    \sa readLineStderr() canReadLineStdout()
+*/
+bool JProcess::canReadLineStderr() const
+{
+    if ( !d->proc || !d->proc->socketStderr )
+	return !d->bufStderr.isEmpty();
+
+    JProcess *that = (JProcess*)this;
+    return that->scanNewline( FALSE, 0 );
+}
+
+/*!
+    Writes the data \a buf to the process's standard input. The
+    process may or may not read this data.
+
+    This function returns immediately; the QProcess class might write
+    the data at a later point (you must enter the event loop for this
+    to occur). When all the data is written to the process, the signal
+    wroteToStdin() is emitted. This does not mean that the process
+    actually read the data, since this class only detects when it was
+    able to write the data to the operating system.
+
+    \sa wroteToStdin() closeStdin() readStdout() readStderr()
+*/
+void JProcess::writeToStdin( const QByteArray& buf )
+{
+#if defined(QT_QPROCESS_DEBUG)
+//    qDebug( "QProcess::writeToStdin(): write to stdin (%d)", d->socketStdin );
+#endif
+    d->stdinBuf.enqueue( new QByteArray(buf) );
+    if ( d->notifierStdin != 0 )
+	d->notifierStdin->setEnabled( TRUE );
+}
+
+
+/*!
+    Closes the process's standard input.
+
+    This function also deletes any pending data that has not been
+    written to standard input.
+
+    \sa wroteToStdin()
+*/
+void JProcess::closeStdin()
+{
+    if ( d->proc == 0 )
+	return;
+    if ( d->proc->socketStdin !=0 ) {
+	while ( !d->stdinBuf.isEmpty() ) {
+	    delete d->stdinBuf.dequeue();
+	}
+	delete d->notifierStdin;
+	d->notifierStdin = 0;
+	if ( ::close( d->proc->socketStdin ) != 0 ) {
+	    qWarning( "Could not close stdin of child process" );
+	}
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::closeStdin(): stdin (%d) closed", d->proc->socketStdin );
+#endif
+	d->proc->socketStdin = 0;
+    }
+}
+
+
+/*
+  This private slot is called when the process has outputted data to either
+  standard output or standard error.
+*/
+void JProcess::socketRead( int fd )
+{
+    if ( d->socketReadCalled ) {
+	// the slots that are connected to the readyRead...() signals might
+	// trigger a recursive call of socketRead(). Avoid this since you get a
+	// blocking read otherwise.
+	return;
+    }
+
+#if defined(QT_QPROCESS_DEBUG)
+    qDebug( "QProcess::socketRead(): %d", fd );
+#endif
+    if ( fd == 0 )
+	return;
+    const int bufsize = 4096;
+    QByteArray *buffer = 0;
+    uint oldSize;
+    int n;
+    if ( fd == d->proc->socketStdout ) {
+	buffer = &d->bufStdout;
+    } else if ( fd == d->proc->socketStderr ) {
+	buffer = &d->bufStderr;
+    } else {
+	// this case should never happen, but just to be safe
+	return;
+    }
+
+    // try to read data first (if it fails, the filedescriptor was closed)
+    oldSize = buffer->size();
+    buffer->resize( oldSize + bufsize );
+    n = ::read( fd, buffer->data()+oldSize, bufsize );
+    if ( n > 0 )
+	buffer->resize( oldSize + n );
+    else
+	buffer->resize( oldSize );
+    // eof or error?
+    if ( n == 0 || n == -1 ) {
+	if ( fd == d->proc->socketStdout ) {
+#if defined(QT_QPROCESS_DEBUG)
+	    qDebug( "QProcess::socketRead(): stdout (%d) closed", fd );
+#endif
+	    d->notifierStdout->setEnabled( FALSE );
+	    delete d->notifierStdout;
+	    d->notifierStdout = 0;
+	    ::close( d->proc->socketStdout );
+	    d->proc->socketStdout = 0;
+	    return;
+	} else if ( fd == d->proc->socketStderr ) {
+#if defined(QT_QPROCESS_DEBUG)
+	    qDebug( "QProcess::socketRead(): stderr (%d) closed", fd );
+#endif
+	    d->notifierStderr->setEnabled( FALSE );
+	    delete d->notifierStderr;
+	    d->notifierStderr = 0;
+	    ::close( d->proc->socketStderr );
+	    d->proc->socketStderr = 0;
+	    return;
+	}
+    }
+
+    fd_set fds;
+    struct timeval tv;
+    FD_ZERO( &fds );
+    FD_SET( fd, &fds );
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+    while ( ::select( fd+1, &fds, 0, 0, &tv ) > 0 ) {
+	// prepare for the next round
+	FD_ZERO( &fds );
+	FD_SET( fd, &fds );
+	// read data
+	oldSize = buffer->size();
+	buffer->resize( oldSize + bufsize );
+	n = ::read( fd, buffer->data()+oldSize, bufsize );
+	if ( n > 0 ) {
+	    buffer->resize( oldSize + n );
+	} else {
+	    buffer->resize( oldSize );
+	    break;
+	}
+    }
+
+    d->socketReadCalled = TRUE;
+    if ( fd == d->proc->socketStdout ) {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::socketRead(): %d bytes read from stdout (%d)",
+		buffer->size()-oldSize, fd );
+#endif
+	emit readyReadStdout();
+    } else if ( fd == d->proc->socketStderr ) {
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::socketRead(): %d bytes read from stderr (%d)",
+		buffer->size()-oldSize, fd );
+#endif
+	emit readyReadStderr();
+    }
+    d->socketReadCalled = FALSE;
+}
+
+
+/*
+  This private slot is called when the process tries to read data from standard
+  input.
+*/
+void JProcess::socketWrite( int fd )
+{
+    while ( fd == d->proc->socketStdin && d->proc->socketStdin != 0 ) {
+	if ( d->stdinBuf.isEmpty() ) {
+	    d->notifierStdin->setEnabled( FALSE );
+	    return;
+	}
+	ssize_t ret = ::write( fd,
+		d->stdinBuf.head()->data() + d->stdinBufRead,
+		d->stdinBuf.head()->size() - d->stdinBufRead );
+#if defined(QT_QPROCESS_DEBUG)
+	qDebug( "QProcess::socketWrite(): wrote %d bytes to stdin (%d)", ret, fd );
+#endif
+	if ( ret == -1 )
+	    return;
+	d->stdinBufRead += ret;
+	if ( d->stdinBufRead == (ssize_t)d->stdinBuf.head()->size() ) {
+	    d->stdinBufRead = 0;
+	    delete d->stdinBuf.dequeue();
+	    if ( wroteToStdinConnected && d->stdinBuf.isEmpty() )
+		emit wroteToStdin();
+	}
+    }
+}
+
+/*!
+  \internal
+  Flushes standard input. This is useful if you want to use QProcess in a
+  synchronous manner.
+
+  This function should probably go into the public API.
+*/
+void JProcess::flushStdin()
+{
+    socketWrite( d->proc->socketStdin );
+}
+
+/*
+  This private slot is only used under Windows (but moc does not know about #if
+  defined()).
+*/
+void JProcess::timeout()
+{
+}
+
+
+/*
+  This private function is used by connectNotify() and disconnectNotify() to
+  change the value of ioRedirection (and related behaviour)
+*/
+void JProcess::setIoRedirection( bool value )
+{
+    ioRedirection = value;
+    if ( ioRedirection ) {
+	if ( d->notifierStdout )
+	    d->notifierStdout->setEnabled( TRUE );
+	if ( d->notifierStderr )
+	    d->notifierStderr->setEnabled( TRUE );
+    } else {
+	if ( d->notifierStdout )
+	    d->notifierStdout->setEnabled( FALSE );
+	if ( d->notifierStderr )
+	    d->notifierStderr->setEnabled( FALSE );
+    }
+}
+
+/*
+  This private function is used by connectNotify() and
+  disconnectNotify() to change the value of notifyOnExit (and related
+  behaviour)
+*/
+void JProcess::setNotifyOnExit( bool value )
+{
+    notifyOnExit = value;
+}
+
+/*
+  This private function is used by connectNotify() and disconnectNotify() to
+  change the value of wroteToStdinConnected (and related behaviour)
+*/
+void JProcess::setWroteStdinConnected( bool value )
+{
+    wroteToStdinConnected = value;
+}
+
+/*! \enum QProcess::PID
+  \internal
+*/
+/*!
+    Returns platform dependent information about the process. This can
+    be used together with platform specific system calls.
+
+    Under Unix the return value is the PID of the process, or -1 if no
+    process is belongs to this object.
+
+    Under Windows it is a pointer to the \c PROCESS_INFORMATION
+    struct, or 0 if no process is belongs to this object.
+
+    Use of this function's return value is likely to be non-portable.
+*/
+JProcess::PID JProcess::processIdentifier()
+{
+    if ( d->proc == 0 )
+	return -1;
+    return d->proc->pid;
+}
+
+void JProcess::setChildStartingHandler(void (*childStarting)(JProcess *))
+{
+	setCallback(childStarting, this);
+}
+
+#endif // QT_NO_PROCESS
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/qpipe.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/qpipe.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/qpipe.cpp	(revision 38)
@@ -0,0 +1,248 @@
+#include"qpipe.h"
+
+#ifdef Q_WS_WIN
+
+static uint readFromHandle(HANDLE h, char *buf, uint size)
+{
+	if(size > 0) {
+		ulong r;
+		if(ReadFile(h, buf, size, &r, 0))
+			return r;
+	}
+	return 0;
+}
+
+#else
+
+#include<unistd.h>
+#include<fcntl.h>
+#include<errno.h>
+
+#endif
+
+//----------------------------------------------------------------------------
+// QPipeEnd
+//----------------------------------------------------------------------------
+QPipeEnd::QPipeEnd()
+{
+	reset();
+}
+
+QPipeEnd::~QPipeEnd()
+{
+	close();
+}
+
+void QPipeEnd::reset()
+{
+#ifdef Q_WS_WIN
+	p = 0;
+#else
+	p = -1;
+#endif
+}
+
+bool QPipeEnd::isOpen() const
+{
+#ifdef Q_WS_WIN
+	return (p ? true: false);
+#else
+	return (p != -1 ? true: false);
+#endif
+}
+
+const QPipeId & QPipeEnd::id() const
+{
+	return p;
+}
+
+void QPipeEnd::setId(const QPipeId &x)
+{
+	p = x;
+}
+
+void QPipeEnd::close()
+{
+	if(!isOpen())
+		return;
+#ifdef Q_WS_WIN
+	CloseHandle(p);
+#else
+	::close(p);
+#endif
+	reset();
+}
+
+void QPipeEnd::release()
+{
+	reset();
+}
+
+QString QPipeEnd::toString() const
+{
+#ifdef Q_WS_WIN
+	DWORD dw;
+	memcpy(&dw, &p, sizeof(DWORD));
+	return QString::number(dw);
+#else
+	return QString::number(p);
+#endif
+}
+
+int QPipeEnd::write(const QByteArray &buf)
+{
+#ifdef Q_WS_WIN
+	DWORD written;
+	if(!WriteFile(p, buf.data(), buf.size(), &written, 0))
+		return -1;
+	return written;
+#else
+	return (::write(p, buf.data(), buf.size()));
+#endif
+}
+
+QByteArray QPipeEnd::readAll(bool *done)
+{
+#ifdef Q_WS_WIN
+	if(done)
+		*done = false;
+	unsigned long i, r;
+	char dummy;
+	if(!PeekNamedPipe(p, &dummy, 1, &r, &i, 0))
+		return QByteArray();
+	if(i <= 0)
+		return QByteArray();
+
+	QByteArray buf;
+	buf.resize(i);
+	int size = readFromHandle(p, buf.data(), i);
+	if(size == 0)
+		return QByteArray();
+	return buf;
+#else
+	if(done)
+		*done = false;
+	QByteArray buf;
+	while(1) {
+		char block[1024];
+		int n = read(p, block, 1024);
+		if(n < 0) {
+			if(errno == EAGAIN) {
+				break;
+			}
+			else {
+				if(done)
+					*done = true;
+				break;
+			}
+		}
+		else if(n == 0) {
+			if(done)
+				*done = true;
+			break;
+		}
+
+		int oldsize = buf.size();
+		buf.resize(oldsize + n);
+		memcpy(buf.data() + oldsize, block, n);
+	}
+
+	return buf;
+#endif
+}
+
+#ifdef Q_WS_WIN
+bool QPipeEnd::winDupHandle()
+{
+	HANDLE h;
+	if(!DuplicateHandle(GetCurrentProcess(), p, GetCurrentProcess(), &h, 0, FALSE, DUPLICATE_SAME_ACCESS))
+		return false;
+	CloseHandle(p);
+	p = h;
+	return true;
+}
+
+#else
+
+bool QPipeEnd::setBlock(bool b)
+{
+	int flags = fcntl(p, F_GETFL);
+	if(!b)
+		flags |= O_NONBLOCK;
+	else
+		flags &= ~O_NONBLOCK;
+	if(fcntl(p, F_SETFL, flags) == -1)
+		return false;
+	return true;
+}
+#endif
+
+
+//----------------------------------------------------------------------------
+// QPipe
+//----------------------------------------------------------------------------
+QPipe::QPipe()
+{
+}
+
+QPipe::~QPipe()
+{
+	close();
+}
+
+void QPipe::closeReadEnd()
+{
+	i.close();
+}
+
+void QPipe::closeWriteEnd()
+{
+	o.close();
+}
+
+int QPipe::write(const QByteArray &buf)
+{
+	return o.write(buf);
+}
+
+QByteArray QPipe::readAll(bool *done)
+{
+	return i.readAll(done);
+}
+
+bool QPipe::open()
+{
+	close();
+
+#ifdef Q_WS_WIN
+	SECURITY_ATTRIBUTES secAttr;
+	memset(&secAttr, 0, sizeof secAttr);
+	secAttr.nLength = sizeof secAttr;
+	secAttr.bInheritHandle = TRUE;
+
+	HANDLE r, w;
+	if(!CreatePipe(&r, &w, &secAttr, 0))
+		return false;
+	i.setId(r);
+	o.setId(w);
+#else
+	int p[2];
+	if(pipe(p) == -1)
+		return false;
+	i.setId(p[0]);
+	o.setId(p[1]);
+#endif
+	return true;
+}
+
+void QPipe::close()
+{
+	i.close();
+	o.close();
+}
+
+void QPipe::release()
+{
+	i.release();
+	o.release();
+}
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/qpipe.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/qpipe.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/qpipe.h	(revision 38)
@@ -0,0 +1,68 @@
+#ifndef QPIPE_H
+#define QPIPE_H
+
+#include<qstring.h>
+#include<qcstring.h>
+
+#ifdef Q_WS_WIN
+#include<windows.h>
+typedef HANDLE QPipeId;
+#else
+typedef int QPipeId;
+#endif
+
+class QPipeEnd
+{
+public:
+	enum { Read, Write };
+	QPipeEnd();
+	~QPipeEnd();
+
+	bool isOpen() const;
+	const QPipeId & id() const;
+	void setId(const QPipeId &);
+
+	void close();
+	void release();
+
+	QString toString() const;
+
+	int write(const QByteArray &);
+	QByteArray readAll(bool *done=0);
+
+#ifdef Q_WS_WIN
+	bool winDupHandle();
+#else
+	bool setBlock(bool b);
+#endif
+
+private:
+	QPipeId p;
+
+	void reset();
+};
+
+class QPipe
+{
+public:
+	QPipe();
+	~QPipe();
+
+	void closeReadEnd();
+	void closeWriteEnd();
+
+	int write(const QByteArray &);
+	QByteArray readAll(bool *done=0);
+
+	QPipeEnd & readEnd() { return i; }
+	QPipeEnd & writeEnd() { return o; }
+
+	bool open();
+	void close();
+	void release();
+
+private:
+	QPipeEnd i, o;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/sprocess.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/sprocess.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/sprocess.cpp	(revision 38)
@@ -0,0 +1,31 @@
+#include"sprocess.h"
+
+#include<unistd.h>
+
+//----------------------------------------------------------------------------
+// SProcess
+//----------------------------------------------------------------------------
+SProcess::SProcess()
+:JProcess()
+{
+	setChildStartingHandler(childStarting);
+}
+
+SProcess::~SProcess()
+{
+}
+
+void SProcess::setClosePipeList(const QValueList<int> &list)
+{
+	pipeList = list;
+}
+
+void SProcess::childStarting(JProcess *p)
+{
+	SProcess *that = (SProcess *)p;
+	// close all pipes
+	for(QValueList<int>::ConstIterator it = that->pipeList.begin(); it != that->pipeList.end(); ++it) {
+		int x = *it;
+		close(x);
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/sprocess.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/sprocess.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/gpgproc/sprocess.h	(revision 38)
@@ -0,0 +1,21 @@
+#ifndef SPROCESS_H
+#define SPROCESS_H
+
+#include<qvaluelist.h>
+#include"jprocess.h"
+
+class SProcess : public JProcess
+{
+	Q_OBJECT
+public:
+	SProcess();
+	~SProcess();
+
+	void setClosePipeList(const QValueList<int> &);
+
+private:
+	QValueList<int> pipeList;
+	static void childStarting(JProcess *);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/openpgp.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/openpgp.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/openpgp.cpp	(revision 38)
@@ -0,0 +1,348 @@
+/*
+ * openpgp.cpp - OpenPGP base
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"openpgp.h"
+
+#include<qptrlist.h>
+#include<qstringlist.h>
+
+using namespace OpenPGP;
+
+
+//----------------------------------------------------------------------------
+// Key
+//----------------------------------------------------------------------------
+Key::Key()
+{
+}
+
+Key::~Key()
+{
+}
+
+const QString & Key::keyID() const
+{
+	return v_keyID;
+}
+
+const QString & Key::userID() const
+{
+	return v_userID;
+}
+
+void Key::setKeyID(const QString &s)
+{
+	v_keyID = s;
+}
+
+void Key::setUserID(const QString &s)
+{
+	v_userID = s;
+}
+
+
+//----------------------------------------------------------------------------
+// Request
+//----------------------------------------------------------------------------
+class Request::Private
+{
+public:
+	Private() {}
+
+	Engine *eng;
+	int type;
+
+	QString enc, sig, keyID;
+	QDateTime ts;
+	int verifyResult;
+	QByteArray dec;
+	bool badpp;
+};
+
+Request::Request(Engine *eng)
+:QObject(0)
+{
+	d = new Private;
+	d->eng = eng;
+	d->type = None;
+	d->badpp = false;
+}
+
+Request::~Request()
+{
+	delete d;
+}
+
+int Request::type() const
+{
+	return d->type;
+}
+
+void Request::encrypt(const QByteArray &in, const QStringList &keys)
+{
+	d->type = Encrypt;
+	d->eng->encrypt(this, in, keys);
+}
+
+void Request::decrypt(const QString &in)
+{
+	d->type = Decrypt;
+	d->eng->decrypt(this, in);
+}
+
+void Request::sign(const QByteArray &in, const QString &keyID)
+{
+	d->type = Sign;
+	d->eng->sign(this, in, keyID);
+}
+
+void Request::verify(const QByteArray &in, const QString &sig)
+{
+	d->type = Verify;
+	d->eng->verify(this, in, sig);
+}
+
+void Request::submitPassphrase(const QString &str)
+{
+	d->eng->submitPassphrase(this, str);
+}
+
+void Request::op_encryptFinished(bool b, const QString &str)
+{
+	if(b)
+		d->enc = str;
+	finished(b);
+}
+
+void Request::op_decryptFinished(bool b, const QByteArray &out)
+{
+	if(b)
+		d->dec = out.copy();
+	finished(b);
+}
+
+void Request::op_signFinished(bool b, const QString &str)
+{
+	if(b)
+		d->sig = str;
+	finished(b);
+}
+
+void Request::op_verifyFinished(int x, const QString &str, const QDateTime &ts)
+{
+	bool b;
+	if(x != VerifyError) {
+		d->keyID = str;
+		d->ts = ts;
+		d->verifyResult = x;
+		b = true;
+	}
+	else
+		b = false;
+
+	finished(b);
+}
+
+void Request::op_needPassphrase()
+{
+	needPassphrase();
+}
+
+QString Request::encrypted() const
+{
+	return d->enc;
+}
+
+QByteArray Request::decrypted() const
+{
+	return d->dec;
+}
+
+QString Request::signature() const
+{
+	return d->sig;
+}
+
+QString Request::keyID() const
+{
+	return d->keyID;
+}
+
+QDateTime Request::timestamp() const
+{
+	return d->ts;
+}
+
+int Request::verifyResult() const
+{
+	return d->verifyResult;
+}
+
+void Request::op_setBadPassphrase(bool b)
+{
+	d->badpp = b;
+}
+
+bool Request::badPassphrase() const
+{
+	return d->badpp;
+}
+
+
+//----------------------------------------------------------------------------
+// Engine
+//----------------------------------------------------------------------------
+Engine::Engine(QObject *parent)
+:QObject(parent)
+{
+}
+
+Engine::~Engine()
+{
+}
+
+void Engine::encryptFinished(Request *r, bool b, const QString &str)
+{
+	r->op_encryptFinished(b, str);
+}
+
+void Engine::decryptFinished(Request *r, bool b, const QByteArray &out)
+{
+	r->op_decryptFinished(b, out);
+}
+
+void Engine::signFinished(Request *r, bool b, const QString &str)
+{
+	r->op_signFinished(b, str);
+}
+
+void Engine::verifyFinished(Request *r, int x, const QString &str, const QDateTime &ts)
+{
+	r->op_verifyFinished(x, str, ts);
+}
+
+void Engine::needPassphrase(Request *r)
+{
+	r->op_needPassphrase();
+}
+
+void Engine::setBadPassphrase(Request *r, bool b)
+{
+	r->op_setBadPassphrase(b);
+}
+
+
+//----------------------------------------------------------------------------
+// Misc
+//----------------------------------------------------------------------------
+#include"gnupg.h"
+static QPtrList<Engine> createEngineList()
+{
+	QPtrList<Engine> list;
+	list.append(new GnuPG);
+	return list;
+}
+
+QValueList<EngineItem> OpenPGP::getAllEngines()
+{
+	QValueList<EngineItem> list;
+	QPtrList<Engine> el = createEngineList();
+	QPtrListIterator<Engine> it(el);
+	for(Engine *e; (e = it.current()); ++it)
+		list += qMakePair(e->id(), e->name());
+	el.setAutoDelete(true);
+	el.clear();
+	return list;
+}
+
+QValueList<EngineItem> OpenPGP::getAvailableEngines()
+{
+	QValueList<EngineItem> list;
+	QPtrList<Engine> el = createEngineList();
+	QPtrListIterator<Engine> it(el);
+	for(Engine *e; (e = it.current()); ++it) {
+		if(e->checkAvailability())
+			list += qMakePair(e->id(), e->name());
+	}
+	el.setAutoDelete(true);
+	el.clear();
+	return list;
+}
+
+Engine * OpenPGP::createEngine(const QString &id)
+{
+	if(id == "gpg")
+		return (new GnuPG);
+	else
+		return 0;
+}
+
+QString OpenPGP::stripHeaderFooter(const QString &str)
+{
+	QString s;
+	if(str.at(0) != '-')
+		return str;
+	QStringList lines = QStringList::split('\n', str, true);
+	QStringList::ConstIterator it = lines.begin();
+	// skip the first line
+	++it;
+	if(it == lines.end())
+		return str;
+
+	// skip the header
+	for(; it != lines.end(); ++it) {
+		if((*it).isEmpty())
+			break;
+	}
+	if(it == lines.end())
+		return str;
+	++it;
+	if(it == lines.end())
+		return str;
+
+	bool first = true;
+	for(; it != lines.end(); ++it) {
+		if((*it).at(0) == '-')
+			break;
+		if(!first)
+			s += '\n';
+		s += (*it);
+		first = false;
+	}
+
+	return s;
+}
+
+QString OpenPGP::addHeaderFooter(const QString &str, int type)
+{
+	QString stype;
+	if(type == 0)
+		stype = "MESSAGE";
+	else
+		stype = "SIGNATURE";
+
+	QString s;
+	s += QString("-----BEGIN PGP %1-----\n").arg(stype);
+	s += "Version: PGP\n";
+	s += "\n";
+	s += str + '\n';
+	s += QString("-----END PGP %1-----\n").arg(stype);
+	return s;
+}
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/openpgp.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/openpgp.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/openpgp.h	(revision 38)
@@ -0,0 +1,140 @@
+/*
+ * openpgp.h - OpenPGP base
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_OPENPGP_H
+#define CS_OPENPGP_H
+
+#include<qobject.h>
+#include<qstring.h>
+#include<qvaluelist.h>
+#include<qdatetime.h>
+#include<qpair.h>
+
+namespace OpenPGP
+{
+	enum { None, Encrypt, Decrypt, Sign, Verify };
+	enum { VerifyGood, VerifyBad, VerifyNoKey, VerifyError };
+	class Engine;
+
+	class Key
+	{
+	public:
+		Key();
+		~Key();
+
+		const QString & keyID() const;
+		const QString & userID() const;
+		void setKeyID(const QString &);
+		void setUserID(const QString &);
+
+	private:
+		QString v_keyID;
+		QString v_userID;
+	};
+
+	typedef QValueList<Key> KeyList;
+
+	class Request : public QObject
+	{
+		Q_OBJECT
+	public:
+		Request(Engine *);
+		~Request();
+
+		int type() const;
+
+		void encrypt(const QByteArray &in, const QStringList &keys);
+		void decrypt(const QString &in);
+		void sign(const QByteArray &in, const QString &keyID);
+		void verify(const QByteArray &in, const QString &sig);
+
+		void submitPassphrase(const QString &);
+
+		QString encrypted() const;
+		QByteArray decrypted() const;
+		QString signature() const;
+		QString keyID() const;
+		QDateTime timestamp() const;
+		int verifyResult() const;
+
+		bool badPassphrase() const;
+
+	signals:
+		void finished(bool);
+		void needPassphrase();
+
+	private:
+		class Private;
+		Private *d;
+
+		friend class Engine;
+		void op_encryptFinished(bool, const QString &);
+		void op_decryptFinished(bool, const QByteArray &);
+		void op_signFinished(bool, const QString &);
+		void op_verifyFinished(int, const QString &, const QDateTime &);
+		void op_needPassphrase();
+		void op_setBadPassphrase(bool);
+	};
+
+	class Engine : public QObject
+	{
+		Q_OBJECT
+	public:
+		Engine(QObject *parent=0);
+		virtual ~Engine()=0;
+
+		virtual bool checkAvailability()=0;
+		virtual QString id() const=0;
+		virtual QString name() const=0;
+
+		virtual void init()=0;
+		virtual KeyList secretKeys() const=0;
+		virtual KeyList publicKeys() const=0;
+
+	signals:
+		void initFinished(bool, const QString &);
+		void keysUpdated();
+
+	protected:
+		friend class Request;
+		virtual void encrypt(Request *, const QByteArray &, const QStringList &keys)=0;
+		virtual void decrypt(Request *, const QString &)=0;
+		virtual void sign(Request *, const QByteArray &, const QString &keyID)=0;
+		virtual void verify(Request *, const QByteArray &, const QString &sig)=0;
+		virtual void submitPassphrase(Request *, const QString &)=0;
+		void encryptFinished(Request *, bool, const QString &);
+		void decryptFinished(Request *, bool, const QByteArray &);
+		void signFinished(Request *, bool, const QString &);
+		void verifyFinished(Request *, int, const QString &, const QDateTime &);
+		void needPassphrase(Request *);
+		void setBadPassphrase(Request *, bool);
+	};
+
+	typedef QPair<QString,QString> EngineItem;
+
+	QValueList<EngineItem> getAllEngines();
+	QValueList<EngineItem> getAvailableEngines();
+	Engine * createEngine(const QString &);
+
+	QString stripHeaderFooter(const QString &);
+	QString addHeaderFooter(const QString &, int);
+}
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/openpgp/openpgp.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/openpgp/openpgp.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/openpgp/openpgp.pri	(revision 38)
@@ -0,0 +1,16 @@
+openpgp {
+	CONFIG += gpgproc
+	GPGPROC_CPP = $$OPENPGP_CPP/gpgproc
+	include($$GPGPROC_CPP/gpgproc.pri)
+
+	HEADERS += \
+		$$OPENPGP_CPP/openpgp.h \
+		$$OPENPGP_CPP/gpgop.h \
+		$$OPENPGP_CPP/gnupg.h
+
+	SOURCES += \
+		$$OPENPGP_CPP/openpgp.cpp \
+		$$OPENPGP_CPP/gpgop.cpp \
+		$$OPENPGP_CPP/gnupg.cpp
+}
+
Index: psi/tags/0.10-os2-b1/src/tools/pgptest.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/pgptest.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/pgptest.cpp	(revision 38)
@@ -0,0 +1,296 @@
+#include"pgptest.h"
+
+#include<qlayout.h>
+#include<qmessagebox.h>
+#include<qinputdialog.h>
+#include<qapplication.h>
+#include<qguardedptr.h>
+
+static QByteArray stringToArray(const QString &s)
+{
+	QByteArray a;
+	QCString cs = s.utf8();
+	a.resize(cs.length());
+	memcpy(a.data(), cs.data(), a.size());
+	return a;
+}
+
+QString plain2rich(const QString &plain, bool nobr=false);
+
+QString plain2rich(const QString &plain, bool nobr)
+{
+	QString rich;
+	int col = 0;
+
+	for(int i = 0; i < (int)plain.length(); ++i) {
+		if(nobr && col == 0)
+			rich += "<nobr>";
+
+		if(plain[i] == '\n') {
+			if(nobr)
+				rich += "</nobr>";
+			rich += "<br>";
+			col = 0;
+			continue;
+		}
+		else if(plain[i] == '\t') {
+			rich += QChar::nbsp;
+			while(col % 4) {
+				rich += QChar::nbsp;
+				++col;
+			}
+		}
+		else if(plain[i].isSpace()) {
+			if(i > 0 && plain[i-1] == ' ')
+				rich += QChar::nbsp;
+			else
+				rich += ' ';
+		}
+		else if(plain[i] == '<')
+			rich += "&lt;";
+		else if(plain[i] == '>')
+			rich += "&gt;";
+		else if(plain[i] == '\"')
+			rich += "&quot;";
+		else if(plain[i] == '\'')
+			rich += "&apos;";
+		else if(plain[i] == '&')
+			rich += "&amp;";
+		else
+			rich += plain[i];
+		++col;
+	}
+
+	if(col > 0 && nobr)
+		rich += "</nobr>";
+
+	return rich;
+}
+
+//----------------------------------------------------------------------------
+// View
+//----------------------------------------------------------------------------
+View::View(OpenPGP::Engine *_pgp, QWidget *parent)
+:QDialog(parent, 0, false, WDestructiveClose)
+{
+	pix_key = new QPixmap("key.png");
+	pgp = _pgp;
+	connect(pgp, SIGNAL(initFinished(bool, const QString &)), SLOT(pgp_initFinished(bool, const QString &)));
+
+	QVBoxLayout *vb = new QVBoxLayout(this, 4);
+	lv = new QListView(this);
+	vb->addWidget(lv);
+	QHBoxLayout *hb = new QHBoxLayout(vb);
+	te = new QTextEdit(this);
+	hb->addWidget(te);
+	te2 = new QTextEdit(this);
+	hb->addWidget(te2);
+	hb = new QHBoxLayout(vb);
+	hb->addStretch(1);
+
+	pb_encrypt = new QPushButton("&Encrypt", this);
+	connect(pb_encrypt, SIGNAL(clicked()), SLOT(encrypt()));
+	hb->addWidget(pb_encrypt);
+
+	pb_decrypt = new QPushButton("&Decrypt", this);
+	connect(pb_decrypt, SIGNAL(clicked()), SLOT(decrypt()));
+	hb->addWidget(pb_decrypt);
+
+	pb_sign = new QPushButton("&Sign", this);
+	connect(pb_sign, SIGNAL(clicked()), SLOT(sign()));
+	hb->addWidget(pb_sign);
+
+	pb_verify = new QPushButton("&Verify", this);
+	connect(pb_verify, SIGNAL(clicked()), SLOT(verify()));
+	hb->addWidget(pb_verify);
+
+	QPushButton *pb = new QPushButton("&Close", this);
+	connect(pb, SIGNAL(clicked()), SLOT(reject()));
+	hb->addWidget(pb);
+
+	lv->addColumn("Key ID");
+	lv->addColumn("User ID");
+	lv->setAllColumnsShowFocus(true);
+	lv->setResizeMode(QListView::LastColumn);
+	resize(600,400);
+
+	lv->setSelectionMode(QListView::Extended);
+
+	disableButtons();
+	pgp->init();
+}
+
+View::~View()
+{
+	delete pgp;
+	delete pix_key;
+}
+
+void View::enableButtons()
+{
+	pb_encrypt->setEnabled(true);
+	pb_decrypt->setEnabled(true);
+	pb_sign->setEnabled(true);
+	pb_verify->setEnabled(true);
+}
+
+void View::disableButtons()
+{
+	pb_encrypt->setEnabled(false);
+	pb_decrypt->setEnabled(false);
+	pb_sign->setEnabled(false);
+	pb_verify->setEnabled(false);
+}
+
+void View::pgp_initFinished(bool ok, const QString &str)
+{
+	if(!ok) {
+		QMessageBox::information(this, tr("Error"), tr("Unable to load %1.\nReason: %2").arg(pgp->name()).arg(str));
+		close();
+	}
+	else {
+		enableButtons();
+		connect(pgp, SIGNAL(keysUpdated()), SLOT(pgp_keysUpdated()));
+		loadKeys();
+	}
+}
+
+void View::encrypt()
+{
+	QStringList recipients;
+	for(QListViewItem *i = lv->firstChild(); i; i = i->nextSibling()) {
+		if(i->isSelected())
+			recipients += i->text(0);
+	}
+	if(recipients.isEmpty()) {
+		QMessageBox::information(this, "Encrypt", "You must select a public key to encrypt with.");
+		return;
+	}
+
+	OpenPGP::Request *r = new OpenPGP::Request(pgp);
+	connect(r, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
+	disableButtons();
+	r->encrypt(stringToArray(te->text()), recipients);
+}
+
+void View::decrypt()
+{
+	OpenPGP::Request *r = new OpenPGP::Request(pgp);
+	connect(r, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
+	connect(r, SIGNAL(needPassphrase()), SLOT(pgp_needPassphrase()));
+	disableButtons();
+	r->decrypt(te->text());
+}
+
+void View::sign()
+{
+	OpenPGP::Request *r = new OpenPGP::Request(pgp);
+	connect(r, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
+	connect(r, SIGNAL(needPassphrase()), SLOT(pgp_needPassphrase()));
+	disableButtons();
+	QString defkey = pgp->secretKeys().first().keyID();
+	r->sign(stringToArray(te->text()), defkey);
+}
+
+void View::verify()
+{
+	OpenPGP::Request *r = new OpenPGP::Request(pgp);
+	connect(r, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
+	disableButtons();
+	r->verify(stringToArray(te->text()), te2->text());
+}
+
+void View::pgp_finished(bool ok)
+{
+	OpenPGP::Request *r = (OpenPGP::Request *)sender();
+	if(r->type() == OpenPGP::Encrypt) {
+		if(!ok)
+			QMessageBox::information(this, tr("Encrypt"), tr("Encountered an error while encrypting."));
+		else
+			te2->setText(r->encrypted());
+	}
+	else if(r->type() == OpenPGP::Decrypt) {
+		if(!ok)
+			QMessageBox::information(this, tr("Encrypt"), tr("Encountered an error while decrypting."));
+		else {
+			QByteArray dec = r->decrypted();
+			te2->setText(QString::fromUtf8(dec.data(), dec.size()));
+		}
+	}
+	else if(r->type() == OpenPGP::Sign) {
+		if(!ok)
+			QMessageBox::information(this, tr("Sign"), tr("Encountered an error while signing."));
+		else
+			te2->setText(r->signature());
+	}
+	else if(r->type() == OpenPGP::Verify) {
+		int x = r->verifyResult();
+		QString keyID = r->keyID();
+		if(x == OpenPGP::VerifyGood)
+			QMessageBox::information(this, "Verify", tr("<nobr>GOOD signature from <b>%1</b>.</nobr>").arg(keyID));
+		else if(x == OpenPGP::VerifyBad)
+			QMessageBox::information(this, "Verify", tr("<nobr>BAD signature from <b>%1</b>.</nobr>").arg(keyID));
+		else if(x == OpenPGP::VerifyNoKey)
+			QMessageBox::information(this, "Verify", tr("<nobr>Cannot verify without key <b>%1</b>.</nobr>").arg(keyID));
+		else
+			QMessageBox::information(this, tr("Verify"), tr("Encountered an error while verifying."));
+	}
+
+	r->deleteLater();
+	enableButtons();
+}
+
+void View::pgp_needPassphrase()
+{
+	QGuardedPtr<OpenPGP::Request> r = (OpenPGP::Request *)sender();
+	bool ok;
+	QString pp = QInputDialog::getText("Passphrase", "Please enter your passphrase:", QLineEdit::Password, QString::null, &ok, this);
+	if(r) {
+		if(ok)
+			r->submitPassphrase(pp);
+		else {
+			r->deleteLater();
+			enableButtons();
+		}
+	}
+}
+
+void View::pgp_keysUpdated()
+{
+	loadKeys();
+}
+
+void View::loadKeys()
+{
+	// display the keys
+	OpenPGP::KeyList pubkeys = pgp->publicKeys();
+	lv->clear();
+	for(OpenPGP::KeyList::ConstIterator it = pubkeys.begin(); it != pubkeys.end(); ++it) {
+		const OpenPGP::Key &key = *it;
+		QListViewItem *lvi = new QListViewItem(lv);
+		lvi->setPixmap(0, *pix_key);
+		lvi->setText(0, key.keyID());
+		lvi->setText(1, key.userID());
+	}
+}
+
+
+//----------------------------------------------------------------------------
+// main
+//----------------------------------------------------------------------------
+int main(int argc, char **argv)
+{
+	QApplication app(argc, argv);
+
+	OpenPGP::Engine *pgp = OpenPGP::createEngine("gpg");
+	if(!pgp->checkAvailability()) {
+		QMessageBox::information(0, QObject::tr("Error"), QObject::tr("%1 is not available.").arg(pgp->name()));
+		return 0;
+	}
+
+	View *v = new View(pgp, 0);
+	app.setMainWidget(v);
+	v->show();
+	app.exec();
+	return 0;
+}
Index: psi/tags/0.10-os2-b1/src/tools/pgptest.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/pgptest.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/pgptest.h	(revision 38)
@@ -0,0 +1,42 @@
+#ifndef PGPTEST_H
+#define PGPTEST_H
+
+#include<qdialog.h>
+#include<qlabel.h>
+#include<qlistview.h>
+#include<qtextedit.h>
+#include<qpushbutton.h>
+#include"openpgp.h"
+
+class View : public QDialog
+{
+	Q_OBJECT
+public:
+	View(OpenPGP::Engine *, QWidget *parent=0);
+	~View();
+
+private slots:
+	void pgp_initFinished(bool ok, const QString &str);
+
+	void encrypt();
+	void decrypt();
+	void sign();
+	void verify();
+	void pgp_finished(bool);
+	void pgp_needPassphrase();
+	void pgp_keysUpdated();
+
+private:
+	QTextEdit *te, *te2;
+	QListView *lv;
+	OpenPGP::Engine *pgp;
+	QPixmap *pix_key;
+	QPushButton *pb_encrypt, *pb_decrypt, *pb_sign, *pb_verify;
+
+	void enableButtons();
+	void disableButtons();
+
+	void loadKeys();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/pgptest.pro
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/pgptest.pro	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/pgptest.pro	(revision 38)
@@ -0,0 +1,26 @@
+TEMPLATE = app
+TARGET   = pgptest
+CONFIG  += qt thread
+
+MOC_DIR        = .moc
+OBJECTS_DIR    = .obj
+UI_DIR         = .ui
+
+DEFINES += GPG_DEBUG HAVE_DNOTIFY
+
+CONFIG += dirwatch
+DIRWATCH_CPP = dirwatch
+include($$DIRWATCH_CPP/dirwatch.pri)
+
+CONFIG += openpgp
+OPENPGP_CPP = openpgp
+include($$OPENPGP_CPP/openpgp.pri)
+
+INCLUDEPATH += dirwatch openpgp
+
+HEADERS += \
+	pgptest.h
+
+SOURCES += \
+	pgptest.cpp
+
Index: psi/tags/0.10-os2-b1/src/tools/qxml/qdom.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/qxml/qdom.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/qxml/qdom.cpp	(revision 38)
@@ -0,0 +1,7044 @@
+/****************************************************************************
+** $Id: qdom.cpp,v 1.1 2004/02/29 00:05:06 justin Exp $
+**
+** Implementation of QDomDocument and related classes.
+**
+** Created : 000518
+**
+** Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+**
+** This file is part of the xml module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition licenses may use this
+** file in accordance with the Qt Commercial License Agreement provided
+** with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qdom.h"
+
+#ifndef QT_NO_DOM
+
+#include "qxml.h"
+#include "qptrlist.h"
+#include "qdict.h"
+#include "qtextstream.h"
+#include "qtextcodec.h"
+#include "qiodevice.h"
+#include "qregexp.h"
+#include "qbuffer.h"
+
+namespace PsiXml {
+
+/*
+  ### old todo comments -- I don't know if they still apply...
+
+  If the document dies, remove all pointers to it from children
+  which can not be deleted at this time.
+
+  If a node dies and has direct children which can not be deleted,
+  then remove the pointer to the parent.
+
+  createElement and friends create double reference counts.
+*/
+
+/*
+  Reference counting:
+
+  Some simple rules:
+  1) If an intern object returns a pointer to another intern object
+     then the reference count of the returned object is not increased.
+  2) If an extern object is created and gets a pointer to some intern
+     object, then the extern object increases the intern objects reference count.
+  3) If an extern object is deleted, then it decreases the reference count
+     on its associated intern object and deletes it if nobody else hold references
+     on the intern object.
+*/
+
+
+/*
+  Helper to split a qualified name in the prefix and local name.
+*/
+static void qt_split_namespace( QString& prefix, QString& name, const QString& qName, bool hasURI )
+{
+    int i = qName.find( ':' );
+    if ( i == -1 ) {
+	if ( hasURI )
+	    prefix = "";
+	else
+	    prefix = QString::null;
+	name = qName;
+    } else {
+	prefix = qName.left( i );
+	name = qName.mid( i + 1 );
+    }
+}
+
+/*
+  Counter for the QDomNodeListPrivate timestamps.
+*/
+static volatile long qt_nodeListTime = 0;
+
+/**************************************************************
+ *
+ * Private class declerations
+ *
+ **************************************************************/
+
+class QDomImplementationPrivate : public QShared
+{
+public:
+    QDomImplementationPrivate();
+    ~QDomImplementationPrivate();
+
+    QDomImplementationPrivate* clone();
+
+};
+
+class QDomNodePrivate : public QShared
+{
+public:
+    QDomNodePrivate( QDomDocumentPrivate*, QDomNodePrivate* parent = 0 );
+    QDomNodePrivate( QDomNodePrivate* n, bool deep );
+    virtual ~QDomNodePrivate();
+
+    QString nodeName() const { return name; }
+    QString nodeValue() const { return value; }
+    virtual void setNodeValue( const QString& v ) { value = v; }
+
+    QDomDocumentPrivate* ownerDocument();
+    void setOwnerDocument( QDomDocumentPrivate* doc );
+
+    virtual QDomNamedNodeMapPrivate* attributes();
+    virtual bool hasAttributes() { return FALSE; }
+    virtual QDomNodePrivate* insertBefore( QDomNodePrivate* newChild, QDomNodePrivate* refChild );
+    virtual QDomNodePrivate* insertAfter( QDomNodePrivate* newChild, QDomNodePrivate* refChild );
+    virtual QDomNodePrivate* replaceChild( QDomNodePrivate* newChild, QDomNodePrivate* oldChild );
+    virtual QDomNodePrivate* removeChild( QDomNodePrivate* oldChild );
+    virtual QDomNodePrivate* appendChild( QDomNodePrivate* newChild );
+
+    QDomNodePrivate* namedItem( const QString& name );
+
+    virtual QDomNodePrivate* cloneNode( bool deep = TRUE );
+    virtual void normalize();
+    virtual void clear();
+
+    QDomNodePrivate* parent() { return hasParent ? ownerNode : 0; }
+    void setParent( QDomNodePrivate *p ) { ownerNode = p; hasParent = TRUE; }
+    void setNoParent() {
+	ownerNode = hasParent ? (QDomNodePrivate*)ownerDocument() : 0;
+	hasParent = FALSE;
+    }
+
+    // Dynamic cast
+    virtual bool isAttr() { return FALSE; }
+    virtual bool isCDATASection() { return FALSE; }
+    virtual bool isDocumentFragment() { return FALSE; }
+    virtual bool isDocument() { return FALSE; }
+    virtual bool isDocumentType() { return FALSE; }
+    virtual bool isElement() { return FALSE; }
+    virtual bool isEntityReference() { return FALSE; }
+    virtual bool isText() { return FALSE; }
+    virtual bool isEntity() { return FALSE; }
+    virtual bool isNotation() { return FALSE; }
+    virtual bool isProcessingInstruction() { return FALSE; }
+    virtual bool isCharacterData() { return FALSE; }
+    virtual bool isComment() { return FALSE; }
+    virtual QDomNode::NodeType nodeType() const { return QDomNode::BaseNode; }
+
+    virtual void save( QTextStream&, int, int ) const;
+
+    // Variables
+    QDomNodePrivate* prev;
+    QDomNodePrivate* next;
+    QDomNodePrivate* ownerNode; // either the node's parent or the node's owner document
+    QDomNodePrivate* first;
+    QDomNodePrivate* last;
+
+    QString name; // this is the local name if prefix != null
+    QString value;
+    QString prefix; // set this only for ElementNode and AttributeNode
+    QString namespaceURI; // set this only for ElementNode and AttributeNode
+    bool createdWithDom1Interface;
+    bool hasParent;
+};
+
+class QDomNodeListPrivate : public QShared
+{
+public:
+    QDomNodeListPrivate( QDomNodePrivate* );
+    QDomNodeListPrivate( QDomNodePrivate*, const QString&  );
+    QDomNodeListPrivate( QDomNodePrivate*, const QString&, const QString&  );
+    virtual ~QDomNodeListPrivate();
+
+    virtual bool operator== ( const QDomNodeListPrivate& ) const;
+    virtual bool operator!= ( const QDomNodeListPrivate& ) const;
+
+    void createList();
+    virtual QDomNodePrivate* item( int index );
+    virtual uint length() const;
+
+    QDomNodePrivate* node_impl;
+    QString tagname;
+    QString nsURI;
+    QPtrList<QDomNodePrivate> list;
+    long timestamp;
+};
+
+class QDomNamedNodeMapPrivate : public QShared
+{
+public:
+    QDomNamedNodeMapPrivate( QDomNodePrivate* );
+    ~QDomNamedNodeMapPrivate();
+
+    QDomNodePrivate* namedItem( const QString& name ) const;
+    QDomNodePrivate* namedItemNS( const QString& nsURI, const QString& localName ) const;
+    QDomNodePrivate* setNamedItem( QDomNodePrivate* arg );
+    QDomNodePrivate* setNamedItemNS( QDomNodePrivate* arg );
+    QDomNodePrivate* removeNamedItem( const QString& name );
+    QDomNodePrivate* item( int index ) const;
+    uint length() const;
+    bool contains( const QString& name ) const;
+    bool containsNS( const QString& nsURI, const QString & localName ) const;
+
+    /**
+     * Remove all children from the map.
+     */
+    void clearMap();
+    bool isReadOnly() { return readonly; }
+    void setReadOnly( bool r ) { readonly = r; }
+    bool isAppendToParent() { return appendToParent; }
+    /**
+     * If TRUE, then the node will redirect insert/remove calls
+     * to its parent by calling QDomNodePrivate::appendChild or removeChild.
+     * In addition the map wont increase or decrease the reference count
+     * of the nodes it contains.
+     *
+     * By default this value is FALSE and the map will handle reference counting
+     * by itself.
+     */
+    void setAppendToParent( bool b ) { appendToParent = b; }
+
+    /**
+     * Creates a copy of the map. It is a deep copy
+     * that means that all children are cloned.
+     */
+    QDomNamedNodeMapPrivate* clone( QDomNodePrivate* parent );
+
+    // Variables
+    QDict<QDomNodePrivate> map;
+    QDomNodePrivate* parent;
+    bool readonly;
+    bool appendToParent;
+};
+
+class QDomDocumentTypePrivate : public QDomNodePrivate
+{
+public:
+    QDomDocumentTypePrivate( QDomDocumentPrivate*, QDomNodePrivate* parent = 0 );
+    QDomDocumentTypePrivate( QDomDocumentTypePrivate* n, bool deep );
+    ~QDomDocumentTypePrivate();
+    void init();
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    QDomNodePrivate* insertBefore( QDomNodePrivate* newChild, QDomNodePrivate* refChild );
+    QDomNodePrivate* insertAfter( QDomNodePrivate* newChild, QDomNodePrivate* refChild );
+    QDomNodePrivate* replaceChild( QDomNodePrivate* newChild, QDomNodePrivate* oldChild );
+    QDomNodePrivate* removeChild( QDomNodePrivate* oldChild );
+    QDomNodePrivate* appendChild( QDomNodePrivate* newChild );
+
+    bool isDocumentType() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::DocumentTypeNode; }
+
+    void save( QTextStream& s, int, int ) const;
+
+    // Variables
+    QDomNamedNodeMapPrivate* entities;
+    QDomNamedNodeMapPrivate* notations;
+    QString publicId;
+    QString systemId;
+    QString internalSubset;
+};
+
+class QDomDocumentFragmentPrivate : public QDomNodePrivate
+{
+public:
+    QDomDocumentFragmentPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent = 0 );
+    QDomDocumentFragmentPrivate( QDomNodePrivate* n, bool deep );
+    ~QDomDocumentFragmentPrivate();
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isDocumentFragment() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::DocumentFragmentNode; }
+};
+
+class QDomCharacterDataPrivate : public QDomNodePrivate
+{
+public:
+    QDomCharacterDataPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& data );
+    QDomCharacterDataPrivate( QDomCharacterDataPrivate* n, bool deep );
+    ~QDomCharacterDataPrivate();
+
+    uint dataLength() const;
+    QString substringData( unsigned long offset, unsigned long count ) const;
+    void appendData( const QString& arg );
+    void insertData( unsigned long offset, const QString& arg );
+    void deleteData( unsigned long offset, unsigned long count );
+    void replaceData( unsigned long offset, unsigned long count, const QString& arg );
+
+    // Reimplemented from QDomNodePrivate
+    bool isCharacterData() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::CharacterDataNode; }
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+
+};
+
+class QDomTextPrivate : public QDomCharacterDataPrivate
+{
+public:
+    QDomTextPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& value );
+    QDomTextPrivate( QDomTextPrivate* n, bool deep );
+    ~QDomTextPrivate();
+
+    QDomTextPrivate* splitText( int offset );
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isText() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::TextNode; }
+    void save( QTextStream& s, int, int ) const;
+
+};
+
+class QDomAttrPrivate : public QDomNodePrivate
+{
+public:
+    QDomAttrPrivate( QDomDocumentPrivate*, QDomNodePrivate*, const QString& name );
+    QDomAttrPrivate( QDomDocumentPrivate*, QDomNodePrivate*, const QString& nsURI, const QString& qName );
+    QDomAttrPrivate( QDomAttrPrivate* n, bool deep );
+    ~QDomAttrPrivate();
+
+    bool specified() const;
+
+    // Reimplemented from QDomNodePrivate
+    void setNodeValue( const QString& v );
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isAttr() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::AttributeNode; }
+    void save( QTextStream& s, int, int ) const;
+
+    // Variables
+    bool m_specified;
+};
+
+class QDomElementPrivate : public QDomNodePrivate
+{
+public:
+    QDomElementPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& name );
+    QDomElementPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& nsURI, const QString& qName );
+    QDomElementPrivate( QDomElementPrivate* n, bool deep );
+    ~QDomElementPrivate();
+
+    QString attribute( const QString& name,  const QString& defValue ) const;
+    QString attributeNS( const QString& nsURI, const QString& localName, const QString& defValue ) const;
+    void setAttribute( const QString& name, const QString& value );
+    void setAttributeNS( const QString& nsURI, const QString& qName, const QString& newValue );
+    void removeAttribute( const QString& name );
+    QDomAttrPrivate* attributeNode( const QString& name);
+    QDomAttrPrivate* attributeNodeNS( const QString& nsURI, const QString& localName );
+    QDomAttrPrivate* setAttributeNode( QDomAttrPrivate* newAttr );
+    QDomAttrPrivate* setAttributeNodeNS( QDomAttrPrivate* newAttr );
+    QDomAttrPrivate* removeAttributeNode( QDomAttrPrivate* oldAttr );
+    bool hasAttribute( const QString& name );
+    bool hasAttributeNS( const QString& nsURI, const QString& localName );
+
+    QString text();
+
+    // Reimplemented from QDomNodePrivate
+    QDomNamedNodeMapPrivate* attributes() { return m_attr; }
+    bool hasAttributes() { return ( m_attr->length() > 0 ); }
+    bool isElement() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::ElementNode; }
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    void save( QTextStream& s, int, int ) const;
+
+    // Variables
+    QDomNamedNodeMapPrivate* m_attr;
+};
+
+
+class QDomCommentPrivate : public QDomCharacterDataPrivate
+{
+public:
+    QDomCommentPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& value );
+    QDomCommentPrivate( QDomCommentPrivate* n, bool deep );
+    ~QDomCommentPrivate();
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isComment() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::CommentNode; }
+    void save( QTextStream& s, int, int ) const;
+
+};
+
+class QDomCDATASectionPrivate : public QDomTextPrivate
+{
+public:
+    QDomCDATASectionPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& value );
+    QDomCDATASectionPrivate( QDomCDATASectionPrivate* n, bool deep );
+    ~QDomCDATASectionPrivate();
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isCDATASection() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::CDATASectionNode; }
+    void save( QTextStream& s, int, int ) const;
+
+};
+
+class QDomNotationPrivate : public QDomNodePrivate
+{
+public:
+    QDomNotationPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& name,
+			  const QString& pub, const QString& sys );
+    QDomNotationPrivate( QDomNotationPrivate* n, bool deep );
+    ~QDomNotationPrivate();
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isNotation() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::NotationNode; }
+    void save( QTextStream& s, int, int ) const;
+
+    // Variables
+    QString m_sys;
+    QString m_pub;
+};
+
+class QDomEntityPrivate : public QDomNodePrivate
+{
+public:
+    QDomEntityPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& name,
+			const QString& pub, const QString& sys, const QString& notation );
+    QDomEntityPrivate( QDomEntityPrivate* n, bool deep );
+    ~QDomEntityPrivate();
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isEntity() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::EntityNode; }
+    void save( QTextStream& s, int, int ) const;
+
+    // Variables
+    QString m_sys;
+    QString m_pub;
+    QString m_notationName;
+};
+
+class QDomEntityReferencePrivate : public QDomNodePrivate
+{
+public:
+    QDomEntityReferencePrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& name );
+    QDomEntityReferencePrivate( QDomNodePrivate* n, bool deep );
+    ~QDomEntityReferencePrivate();
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isEntityReference() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::EntityReferenceNode; }
+    void save( QTextStream& s, int, int ) const;
+};
+
+class QDomProcessingInstructionPrivate : public QDomNodePrivate
+{
+public:
+    QDomProcessingInstructionPrivate( QDomDocumentPrivate*, QDomNodePrivate* parent, const QString& target,
+				       const QString& data);
+    QDomProcessingInstructionPrivate( QDomProcessingInstructionPrivate* n, bool deep );
+    ~QDomProcessingInstructionPrivate();
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isProcessingInstruction() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::ProcessingInstructionNode; }
+    void save( QTextStream& s, int, int ) const;
+};
+
+class QDomDocumentPrivate : public QDomNodePrivate
+{
+public:
+    QDomDocumentPrivate();
+    QDomDocumentPrivate( const QString& name );
+    QDomDocumentPrivate( QDomDocumentTypePrivate* dt );
+    QDomDocumentPrivate( QDomDocumentPrivate* n, bool deep );
+    ~QDomDocumentPrivate();
+
+    bool setContent( QXmlInputSource *source, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn );
+    bool setContent( QXmlInputSource *source, QXmlReader *reader, QString *errorMsg, int *errorLine, int *errorColumn );
+
+    // Attributes
+    QDomDocumentTypePrivate* doctype() { return type; };
+    QDomImplementationPrivate* implementation() { return impl; };
+    QDomElementPrivate* documentElement();
+
+    // Factories
+    QDomElementPrivate* createElement( const QString& tagName );
+    QDomElementPrivate*	createElementNS( const QString& nsURI, const QString& qName );
+    QDomDocumentFragmentPrivate* createDocumentFragment();
+    QDomTextPrivate* createTextNode( const QString& data );
+    QDomCommentPrivate* createComment( const QString& data );
+    QDomCDATASectionPrivate* createCDATASection( const QString& data );
+    QDomProcessingInstructionPrivate* createProcessingInstruction( const QString& target, const QString& data );
+    QDomAttrPrivate* createAttribute( const QString& name );
+    QDomAttrPrivate* createAttributeNS( const QString& nsURI, const QString& qName );
+    QDomEntityReferencePrivate* createEntityReference( const QString& name );
+
+    QDomNodePrivate* importNode( const QDomNodePrivate* importedNode, bool deep );
+
+    // Reimplemented from QDomNodePrivate
+    QDomNodePrivate* cloneNode( bool deep = TRUE );
+    bool isDocument() { return TRUE; }
+    QDomNode::NodeType nodeType() const { return QDomNode::DocumentNode; }
+    void clear();
+    void save( QTextStream&, int, int ) const;
+
+    // Variables
+    QDomImplementationPrivate* impl;
+    QDomDocumentTypePrivate* type;
+};
+
+/**************************************************************
+ *
+ * QDomHandler
+ *
+ **************************************************************/
+
+class QDomHandler : public QXmlDefaultHandler
+{
+public:
+    QDomHandler( QDomDocumentPrivate* d, bool namespaceProcessing );
+    ~QDomHandler();
+
+    // content handler
+    bool endDocument();
+    bool startElement( const QString& nsURI, const QString& localName, const QString& qName, const QXmlAttributes& atts );
+    bool endElement( const QString& nsURI, const QString& localName, const QString& qName );
+    bool characters( const QString& ch );
+    bool processingInstruction( const QString& target, const QString& data );
+    bool skippedEntity( const QString& name );
+
+    // error handler
+    bool fatalError( const QXmlParseException& exception );
+
+    // lexical handler
+    bool startCDATA();
+    bool endCDATA();
+    bool startEntity( const QString & );
+    bool endEntity( const QString & );
+    bool startDTD( const QString& name, const QString& publicId, const QString& systemId );
+    bool comment( const QString& ch );
+
+    // decl handler
+    bool externalEntityDecl( const QString &name, const QString &publicId, const QString &systemId ) ;
+
+    // DTD handler
+    bool notationDecl( const QString & name, const QString & publicId, const QString & systemId );
+    bool unparsedEntityDecl( const QString &name, const QString &publicId, const QString &systemId, const QString &notationName ) ;
+
+    QString errorMsg;
+    int errorLine;
+    int errorColumn;
+
+private:
+    QDomDocumentPrivate *doc;
+    QDomNodePrivate *node;
+    QString entityName;
+    bool cdata;
+    bool nsProcessing;
+};
+
+/**************************************************************
+ *
+ * QDomImplementationPrivate
+ *
+ **************************************************************/
+
+QDomImplementationPrivate::QDomImplementationPrivate()
+{
+}
+
+QDomImplementationPrivate::~QDomImplementationPrivate()
+{
+}
+
+QDomImplementationPrivate* QDomImplementationPrivate::clone()
+{
+    QDomImplementationPrivate* p = new QDomImplementationPrivate;
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+/**************************************************************
+ *
+ * QDomImplementation
+ *
+ **************************************************************/
+
+/*!
+    \class QDomImplementation qdom.h
+    \reentrant
+    \brief The QDomImplementation class provides information about the
+    features of the DOM implementation.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    This class describes the features that are supported by the DOM
+    implementation. Currently the XML subset of DOM Level 1 and DOM
+    Level 2 Core are supported.
+
+    Normally you will use the function QDomDocument::implementation()
+    to get the implementation object.
+
+    You can create a new document type with createDocumentType() and a
+    new document with createDocument().
+
+    For further information about the Document Object Model see \link
+    http://www.w3.org/TR/REC-DOM-Level-1/\endlink and \link
+    http://www.w3.org/TR/DOM-Level-2-Core/\endlink. For a more general
+    introduction of the DOM implementation see the QDomDocument
+    documentation.
+
+    \sa hasFeature()
+*/
+
+/*!
+    Constructs a QDomImplementation object.
+*/
+QDomImplementation::QDomImplementation()
+{
+    impl = 0;
+}
+
+/*!
+    Constructs a copy of \a x.
+*/
+QDomImplementation::QDomImplementation( const QDomImplementation& x )
+{
+    impl = x.impl;
+    if ( impl )
+	impl->ref();
+}
+
+QDomImplementation::QDomImplementation( QDomImplementationPrivate* p )
+{
+    // We want to be co-owners, so increase the reference count
+    impl = p;
+    if (impl)
+    	impl->ref();
+}
+
+/*!
+    Assigns \a x to this DOM implementation.
+*/
+QDomImplementation& QDomImplementation::operator= ( const QDomImplementation& x )
+{
+    if ( x.impl )
+	x.impl->ref(); // avoid x=x
+    if ( impl && impl->deref() )
+	delete impl;
+    impl = x.impl;
+
+    return *this;
+}
+
+/*!
+    Returns TRUE if \a x and this DOM implementation object were
+    created from the same QDomDocument; otherwise returns FALSE.
+*/
+bool QDomImplementation::operator==( const QDomImplementation& x ) const
+{
+    return ( impl == x.impl );
+}
+
+/*!
+    Returns TRUE if \a x and this DOM implementation object were
+    created from different QDomDocuments; otherwise returns FALSE.
+*/
+bool QDomImplementation::operator!=( const QDomImplementation& x ) const
+{
+    return ( impl != x.impl );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomImplementation::~QDomImplementation()
+{
+    if ( impl && impl->deref() )
+	delete impl;
+}
+
+/*!
+    The function returns TRUE if QDom implements the requested \a
+    version of a \a feature; otherwise returns FALSE.
+
+    The currently supported features and their versions:
+    \table
+    \header \i Feature \i Version
+    \row \i XML \i 1.0
+    \endtable
+*/
+bool QDomImplementation::hasFeature( const QString& feature, const QString& version )
+{
+    if ( feature == "XML" ) {
+	if ( version.isEmpty() || version == "1.0" ) {
+	    return TRUE;
+	}
+    }
+    // ### add DOM level 2 features
+    return FALSE;
+}
+
+/*!
+    Creates a document type node for the name \a qName.
+
+    \a publicId specifies the public identifier of the external
+    subset. If you specify QString::null as the \a publicId, this
+    means that the document type has no public identifier.
+
+    \a systemId specifies the system identifier of the external
+    subset. If you specify QString::null as the \a systemId, this
+    means that the document type has no system identifier.
+
+    Since you cannot have a public identifier without a system
+    identifier, the public identifier is set to QString::null if there
+    is no system identifier.
+
+    DOM level 2 does not support any other document type declaration
+    features.
+
+    The only way you can use a document type that was created this
+    way, is in combination with the createDocument() function to
+    create a QDomDocument with this document type.
+
+    \sa createDocument();
+*/
+QDomDocumentType QDomImplementation::createDocumentType( const QString& qName, const QString& publicId, const QString& systemId )
+{
+    QDomDocumentTypePrivate *dt = new QDomDocumentTypePrivate( 0 );
+    dt->name = qName;
+    if ( systemId.isNull() ) {
+	dt->publicId = QString::null;
+	dt->systemId = QString::null;
+    } else {
+	dt->publicId = publicId;
+	dt->systemId = systemId;
+    }
+    return QDomDocumentType( dt );
+}
+
+/*!
+    Creates a DOM document with the document type \a doctype. This
+    function also adds a root element node with the qualified name \a
+    qName and the namespace URI \a nsURI.
+*/
+QDomDocument QDomImplementation::createDocument( const QString& nsURI, const QString& qName, const QDomDocumentType& doctype )
+{
+    QDomDocument doc( doctype );
+    QDomElement root = doc.createElementNS( nsURI, qName );
+    doc.appendChild( root );
+    return doc;
+}
+
+/*!
+    Returns FALSE if the object was created by
+    QDomDocument::implementation(); otherwise returns TRUE.
+*/
+bool QDomImplementation::isNull()
+{
+    return ( impl == 0 );
+}
+
+/**************************************************************
+ *
+ * QDomNodeListPrivate
+ *
+ **************************************************************/
+
+QDomNodeListPrivate::QDomNodeListPrivate( QDomNodePrivate* n_impl )
+{
+    node_impl = n_impl;
+    if ( node_impl )
+	node_impl->ref();
+    timestamp = -1;
+}
+
+QDomNodeListPrivate::QDomNodeListPrivate( QDomNodePrivate* n_impl, const QString& name )
+{
+    node_impl = n_impl;
+    if ( node_impl )
+	node_impl->ref();
+    tagname = name;
+    timestamp = -1;
+}
+
+QDomNodeListPrivate::QDomNodeListPrivate( QDomNodePrivate* n_impl, const QString& _nsURI, const QString& localName )
+{
+    node_impl = n_impl;
+    if ( node_impl )
+	node_impl->ref();
+    tagname = localName;
+    nsURI = _nsURI;
+    timestamp = -1;
+}
+
+QDomNodeListPrivate::~QDomNodeListPrivate()
+{
+    if ( node_impl && node_impl->deref() )
+	delete node_impl;
+}
+
+bool QDomNodeListPrivate::operator== ( const QDomNodeListPrivate& other ) const
+{
+    return ( node_impl == other.node_impl ) && ( tagname == other.tagname ) ;
+}
+
+bool QDomNodeListPrivate::operator!= ( const QDomNodeListPrivate& other ) const
+{
+    return ( node_impl != other.node_impl ) || ( tagname != other.tagname ) ;
+}
+
+void QDomNodeListPrivate::createList()
+{
+    if ( !node_impl )
+	return;
+    timestamp = qt_nodeListTime;
+    QDomNodePrivate* p = node_impl->first;
+
+    list.clear();
+    if ( tagname.isNull() ) {
+	while ( p ) {
+	    list.append( p );
+	    p = p->next;
+	}
+    } else if ( nsURI.isNull() ) {
+	while ( p && p != node_impl ) {
+	    if ( p->isElement() && p->nodeName() == tagname ) {
+		list.append( p );
+	    }
+	    if ( p->first )
+		p = p->first;
+	    else if ( p->next )
+		p = p->next;
+	    else {
+		p = p->parent();
+		while ( p && p != node_impl && !p->next )
+		    p = p->parent();
+		if ( p && p != node_impl )
+		    p = p->next;
+	    }
+	}
+    } else {
+	while ( p && p != node_impl ) {
+	    if ( p->isElement() && p->name==tagname && p->namespaceURI==nsURI ) {
+		list.append( p );
+	    }
+	    if ( p->first )
+		p = p->first;
+	    else if ( p->next )
+		p = p->next;
+	    else {
+		p = p->parent();
+		while ( p && p != node_impl && !p->next )
+		    p = p->parent();
+		if ( p && p != node_impl )
+		    p = p->next;
+	    }
+	}
+    }
+}
+
+QDomNodePrivate* QDomNodeListPrivate::item( int index )
+{
+    if ( !node_impl )
+	return 0;
+    if ( timestamp < qt_nodeListTime )
+	createList();
+    return list.at( index );
+}
+
+uint QDomNodeListPrivate::length() const
+{
+    if ( !node_impl )
+	return 0;
+    if ( timestamp < qt_nodeListTime ) {
+	QDomNodeListPrivate *that = (QDomNodeListPrivate*)this;
+	that->createList();
+    }
+    return list.count();
+}
+
+/**************************************************************
+ *
+ * QDomNodeList
+ *
+ **************************************************************/
+
+/*!
+    \class QDomNodeList qdom.h
+    \reentrant
+    \brief The QDomNodeList class is a list of QDomNode objects.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    Lists can be obtained by QDomDocument::elementsByTagName() and
+    QDomNode::childNodes(). The Document Object Model (DOM) requires
+    these lists to be "live": whenever you change the underlying
+    document, the contents of the list will get updated.
+
+    You can get a particular node from the list with item(). The
+    number of items in the list is returned by count() (and by
+    length()).
+
+    For further information about the Document Object Model see \link
+    http://www.w3.org/TR/REC-DOM-Level-1/\endlink and \link
+    http://www.w3.org/TR/DOM-Level-2-Core/\endlink. For a more general
+    introduction of the DOM implementation see the QDomDocument
+    documentation.
+
+    \sa QDomNode::childNodes() QDomDocument::elementsByTagName()
+*/
+
+/*!
+    Creates an empty node list.
+*/
+QDomNodeList::QDomNodeList()
+{
+    impl = 0;
+}
+
+QDomNodeList::QDomNodeList( QDomNodeListPrivate* p )
+{
+    impl = p;
+}
+
+/*!
+    Constructs a copy of \a n.
+*/
+QDomNodeList::QDomNodeList( const QDomNodeList& n )
+{
+    impl = n.impl;
+    if ( impl )
+	impl->ref();
+}
+
+/*!
+    Assigns \a n to this node list.
+*/
+QDomNodeList& QDomNodeList::operator= ( const QDomNodeList& n )
+{
+    if ( n.impl )
+	n.impl->ref();
+    if ( impl && impl->deref() )
+	delete impl;
+    impl = n.impl;
+
+    return *this;
+}
+
+/*!
+    Returns TRUE if the node list \a n and this node list are equal;
+    otherwise returns FALSE.
+*/
+bool QDomNodeList::operator== ( const QDomNodeList& n ) const
+{
+    if ( impl == n.impl )
+	return TRUE;
+    if ( !impl || !n.impl )
+	return FALSE;
+    return (*impl == *n.impl);
+}
+
+/*!
+    Returns TRUE the node list \a n and this node list are not equal;
+    otherwise returns FALSE.
+*/
+bool QDomNodeList::operator!= ( const QDomNodeList& n ) const
+{
+    return !operator==(n);
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomNodeList::~QDomNodeList()
+{
+    if ( impl && impl->deref() )
+	delete impl;
+}
+
+/*!
+    Returns the node at position \a index.
+
+    If \a index is negative or if \a index >= length() then a null
+    node is returned (i.e. a node for which QDomNode::isNull() returns
+    TRUE).
+
+    \sa count()
+*/
+QDomNode QDomNodeList::item( int index ) const
+{
+    if ( !impl )
+	return QDomNode();
+
+    return QDomNode( impl->item( index ) );
+}
+
+/*!
+    Returns the number of nodes in the list.
+
+    This function is the same as count().
+*/
+uint QDomNodeList::length() const
+{
+    if ( !impl )
+	return 0;
+    return impl->length();
+}
+
+/*!
+    \fn uint QDomNodeList::count() const
+
+    Returns the number of nodes in the list.
+
+    This function is the same as length().
+*/
+
+
+/**************************************************************
+ *
+ * QDomNodePrivate
+ *
+ **************************************************************/
+
+inline void QDomNodePrivate::setOwnerDocument( QDomDocumentPrivate* doc )
+{
+    ownerNode = doc;
+    hasParent = FALSE;
+}
+
+QDomNodePrivate::QDomNodePrivate( QDomDocumentPrivate* doc, QDomNodePrivate *par )
+{
+    if ( par )
+	setParent( par );
+    else
+	setOwnerDocument( doc );
+    prev = 0;
+    next = 0;
+    first = 0;
+    last = 0;
+    createdWithDom1Interface = TRUE;
+}
+
+QDomNodePrivate::QDomNodePrivate( QDomNodePrivate* n, bool deep )
+{
+    setOwnerDocument( n->ownerDocument() );
+    prev = 0;
+    next = 0;
+    first = 0;
+    last = 0;
+
+    name = n->name;
+    value = n->value;
+    prefix = n->prefix;
+    namespaceURI = n->namespaceURI;
+    createdWithDom1Interface = n->createdWithDom1Interface;
+
+    if ( !deep )
+	return;
+
+    for ( QDomNodePrivate* x = n->first; x; x = x->next )
+	appendChild( x->cloneNode( TRUE ) );
+}
+
+QDomNodePrivate::~QDomNodePrivate()
+{
+    QDomNodePrivate* p = first;
+    QDomNodePrivate* n;
+
+    while ( p ) {
+	n = p->next;
+	if ( p->deref() )
+	    delete p;
+	else
+	    p->setNoParent();
+	p = n;
+    }
+
+    first = 0;
+    last = 0;
+}
+
+void QDomNodePrivate::clear()
+{
+    QDomNodePrivate* p = first;
+    QDomNodePrivate* n;
+
+    while ( p ) {
+	n = p->next;
+	if ( p->deref() )
+	    delete p;
+	p = n;
+    }
+
+    first = 0;
+    last = 0;
+}
+
+QDomNodePrivate* QDomNodePrivate::namedItem( const QString& n )
+{
+    QDomNodePrivate* p = first;
+    while ( p ) {
+	if ( p->nodeName() == n )
+	    return p;
+	p = p->next;
+    }
+
+    return 0;
+}
+
+QDomNamedNodeMapPrivate* QDomNodePrivate::attributes()
+{
+    return 0;
+}
+
+QDomNodePrivate* QDomNodePrivate::insertBefore( QDomNodePrivate* newChild, QDomNodePrivate* refChild )
+{
+    // Error check
+    if ( !newChild )
+	return 0;
+
+    // Error check
+    if ( newChild == refChild )
+	return 0;
+
+    // Error check
+    if ( refChild && refChild->parent() != this )
+	return 0;
+
+    // "mark lists as dirty"
+    qt_nodeListTime++;
+
+    // Special handling for inserting a fragment. We just insert
+    // all elements of the fragment instead of the fragment itself.
+    if ( newChild->isDocumentFragment() ) {
+	// Fragment is empty ?
+	if ( newChild->first == 0 )
+	    return newChild;
+
+	// New parent
+	QDomNodePrivate* n = newChild->first;
+	while ( n )  {
+	    n->setParent( this );
+	    n = n->next;
+	}
+
+	// Insert at the beginning ?
+	if ( !refChild || refChild->prev == 0 ) {
+	    if ( first )
+		first->prev = newChild->last;
+	    newChild->last->next = first;
+	    if ( !last )
+		last = newChild->last;
+	    first = newChild->first;
+	} else {
+	    // Insert in the middle
+	    newChild->last->next = refChild;
+	    newChild->first->prev = refChild->prev;
+	    refChild->prev->next = newChild->first;
+	    refChild->prev = newChild->last;
+	}
+
+	// No need to increase the reference since QDomDocumentFragment
+	// does not decrease the reference.
+
+	// Remove the nodes from the fragment
+	newChild->first = 0;
+	newChild->last = 0;
+	return newChild;
+    }
+
+    // No more errors can occur now, so we take
+    // ownership of the node.
+    newChild->ref();
+
+    if ( newChild->parent() )
+	newChild->parent()->removeChild( newChild );
+
+    newChild->setParent( this );
+
+    if ( !refChild ) {
+	if ( first )
+	    first->prev = newChild;
+	newChild->next = first;
+	if ( !last )
+	    last = newChild;
+	first = newChild;
+	return newChild;
+    }
+
+    if ( refChild->prev == 0 ) {
+	if ( first )
+	    first->prev = newChild;
+	newChild->next = first;
+	if ( !last )
+	    last = newChild;
+	first = newChild;
+	return newChild;
+    }
+
+    newChild->next = refChild;
+    newChild->prev = refChild->prev;
+    refChild->prev->next = newChild;
+    refChild->prev = newChild;
+
+    return newChild;
+}
+
+QDomNodePrivate* QDomNodePrivate::insertAfter( QDomNodePrivate* newChild, QDomNodePrivate* refChild )
+{
+    // Error check
+    if ( !newChild )
+	return 0;
+
+    // Error check
+    if ( newChild == refChild )
+	return 0;
+
+    // Error check
+    if ( refChild && refChild->parent() != this )
+	return 0;
+
+    // "mark lists as dirty"
+    qt_nodeListTime++;
+
+    // Special handling for inserting a fragment. We just insert
+    // all elements of the fragment instead of the fragment itself.
+    if ( newChild->isDocumentFragment() ) {
+	// Fragment is empty ?
+	if ( newChild->first == 0 )
+	    return newChild;
+
+	// New parent
+	QDomNodePrivate* n = newChild->first;
+	while ( n ) {
+	    n->setParent( this );
+	    n = n->next;
+	}
+
+	// Insert at the end
+	if ( !refChild || refChild->next == 0 ) {
+	    if ( last )
+		last->next = newChild->first;
+	    newChild->first->prev = last;
+	    if ( !first )
+		first = newChild->first;
+	    last = newChild->last;
+	} else { // Insert in the middle
+	    newChild->first->prev = refChild;
+	    newChild->last->next = refChild->next;
+	    refChild->next->prev = newChild->last;
+	    refChild->next = newChild->first;
+	}
+
+	// No need to increase the reference since QDomDocumentFragment
+	// does not decrease the reference.
+
+	// Remove the nodes from the fragment
+	newChild->first = 0;
+	newChild->last = 0;
+	return newChild;
+    }
+
+    // Release new node from its current parent
+    if ( newChild->parent() )
+	newChild->parent()->removeChild( newChild );
+
+    // No more errors can occur now, so we take
+    // ownership of the node
+    newChild->ref();
+
+    newChild->setParent( this );
+
+    // Insert at the end
+    if ( !refChild ) {
+	if ( last )
+	    last->next = newChild;
+	newChild->prev = last;
+	if ( !first )
+	    first = newChild;
+	last = newChild;
+	return newChild;
+    }
+
+    if ( refChild->next == 0 ) {
+	if ( last )
+	    last->next = newChild;
+	newChild->prev = last;
+	if ( !first )
+	    first = newChild;
+	last = newChild;
+	return newChild;
+    }
+
+    newChild->prev = refChild;
+    newChild->next = refChild->next;
+    refChild->next->prev = newChild;
+    refChild->next = newChild;
+
+    return newChild;
+}
+
+QDomNodePrivate* QDomNodePrivate::replaceChild( QDomNodePrivate* newChild, QDomNodePrivate* oldChild )
+{
+    if ( oldChild->parent() != this )
+	return 0;
+    if ( !newChild || !oldChild )
+	return 0;
+    if ( newChild == oldChild )
+	return 0;
+
+    // mark lists as dirty
+    qt_nodeListTime++;
+
+    // Special handling for inserting a fragment. We just insert
+    // all elements of the fragment instead of the fragment itself.
+    if ( newChild->isDocumentFragment() ) {
+	// Fragment is empty ?
+	if ( newChild->first == 0 )
+	    return newChild;
+
+	// New parent
+	QDomNodePrivate* n = newChild->first;
+	while ( n ) {
+	    n->setParent( this );
+	    n = n->next;
+	}
+
+
+	if ( oldChild->next )
+	    oldChild->next->prev = newChild->last;
+	if ( oldChild->prev )
+	    oldChild->prev->next = newChild->first;
+
+	newChild->last->next = oldChild->next;
+	newChild->first->prev = oldChild->prev;
+
+	if ( first == oldChild )
+	    first = newChild->first;
+	if ( last == oldChild )
+	    last = newChild->last;
+
+	oldChild->setNoParent();
+	oldChild->next = 0;
+	oldChild->prev = 0;
+
+	// No need to increase the reference since QDomDocumentFragment
+	// does not decrease the reference.
+
+	// Remove the nodes from the fragment
+	newChild->first = 0;
+	newChild->last = 0;
+
+	// We are no longer interested in the old node
+	if ( oldChild ) oldChild->deref();
+
+	return oldChild;
+    }
+
+    // No more errors can occur now, so we take
+    // ownership of the node
+    newChild->ref();
+
+    // Release new node from its current parent
+    if ( newChild->parent() )
+	newChild->parent()->removeChild( newChild );
+
+    newChild->setParent( this );
+
+    if ( oldChild->next )
+	oldChild->next->prev = newChild;
+    if ( oldChild->prev )
+	oldChild->prev->next = newChild;
+
+    newChild->next = oldChild->next;
+    newChild->prev = oldChild->prev;
+
+    if ( first == oldChild )
+	first = newChild;
+    if ( last == oldChild )
+	last = newChild;
+
+    oldChild->setNoParent();
+    oldChild->next = 0;
+    oldChild->prev = 0;
+
+    // We are no longer interested in the old node
+    if ( oldChild ) oldChild->deref();
+
+    return oldChild;
+}
+
+QDomNodePrivate* QDomNodePrivate::removeChild( QDomNodePrivate* oldChild )
+{
+    // Error check
+    if ( oldChild->parent() != this )
+	return 0;
+
+    // "mark lists as dirty"
+    qt_nodeListTime++;
+
+    // Perhaps oldChild was just created with "createElement" or that. In this case
+    // its parent is QDomDocument but it is not part of the documents child list.
+    if ( oldChild->next == 0 && oldChild->prev == 0 && first != oldChild )
+	return 0;
+
+    if ( oldChild->next )
+	oldChild->next->prev = oldChild->prev;
+    if ( oldChild->prev )
+	oldChild->prev->next = oldChild->next;
+
+    if ( last == oldChild )
+	last = oldChild->prev;
+    if ( first == oldChild )
+	first = oldChild->next;
+
+    oldChild->setNoParent();
+    oldChild->next = 0;
+    oldChild->prev = 0;
+
+    // We are no longer interested in the old node
+    if ( oldChild ) oldChild->deref();
+
+    return oldChild;
+}
+
+QDomNodePrivate* QDomNodePrivate::appendChild( QDomNodePrivate* newChild )
+{
+    // No reference manipulation needed. Done in insertAfter.
+    return insertAfter( newChild, 0 );
+}
+
+QDomDocumentPrivate* QDomNodePrivate::ownerDocument()
+{
+    QDomNodePrivate* p = this;
+    while ( p && !p->isDocument() ) {
+	if ( !p->hasParent )
+	    return (QDomDocumentPrivate*)p->ownerNode;
+	p = p->parent();
+    }
+
+    return (QDomDocumentPrivate*)p;
+}
+
+QDomNodePrivate* QDomNodePrivate::cloneNode( bool deep )
+{
+    QDomNodePrivate* p = new QDomNodePrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+static void qNormalizeNode( QDomNodePrivate* n )
+{
+    QDomNodePrivate* p = n->first;
+    QDomTextPrivate* t = 0;
+
+    while ( p ) {
+	if ( p->isText() ) {
+	    if ( t ) {
+		QDomNodePrivate* tmp = p->next;
+		t->appendData( p->nodeValue() );
+		n->removeChild( p );
+		p = tmp;
+	    } else {
+		t = (QDomTextPrivate*)p;
+		p = p->next;
+	    }
+	} else {
+	    p = p->next;
+	    t = 0;
+	}
+    }
+}
+void QDomNodePrivate::normalize()
+{
+    // ### This one has moved from QDomElementPrivate to this position. It is
+    // not tested.
+    qNormalizeNode( this );
+}
+
+void QDomNodePrivate::save( QTextStream& s, int depth, int indent ) const
+{
+    const QDomNodePrivate* n = first;
+    while ( n ) {
+	n->save( s, depth, indent );
+	n = n->next;
+    }
+}
+
+/**************************************************************
+ *
+ * QDomNode
+ *
+ **************************************************************/
+
+#define IMPL ((QDomNodePrivate*)impl)
+
+/*!
+    \class QDomNode qdom.h
+    \reentrant
+    \brief The QDomNode class is the base class for all the nodes in a DOM tree.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    Many functions in the DOM return a QDomNode.
+
+    You can find out the type of a node using isAttr(),
+    isCDATASection(), isDocumentFragment(), isDocument(),
+    isDocumentType(), isElement(), isEntityReference(), isText(),
+    isEntity(), isNotation(), isProcessingInstruction(),
+    isCharacterData() and isComment().
+
+    A QDomNode can be converted into one of its subclasses using
+    toAttr(), toCDATASection(), toDocumentFragment(), toDocument(),
+    toDocumentType(), toElement(), toEntityReference(), toText(),
+    toEntity(), toNotation(), toProcessingInstruction(),
+    toCharacterData() or toComment(). You can convert a node to a null
+    node with clear().
+
+    Copies of the QDomNode class share their data using explicit
+    sharing. This means that modifying one node will change all
+    copies. This is especially useful in combination with functions
+    which return a QDomNode, e.g. firstChild(). You can make an
+    independent (deep) copy of the node with cloneNode().
+
+    Nodes are inserted with insertBefore(), insertAfter() or
+    appendChild(). You can replace one node with another using
+    replaceChild() and remove a node with removeChild().
+
+    To traverse nodes use firstChild() to get a node's first child (if
+    any), and nextSibling() to traverse. QDomNode also provides
+    lastChild(), previousSibling() and parentNode(). To find the first
+    child node with a particular node name use namedItem().
+
+    To find out if a node has children use hasChildNodes() and to get
+    a list of all of a node's children use childNodes().
+
+    The node's name and value (the meaning of which varies depending
+    on its type) is returned by nodeName() and nodeValue()
+    respectively. The node's type is returned by nodeType(). The
+    node's value can be set with setNodeValue().
+
+    The document to which the node belongs is returned by
+    ownerDocument().
+
+    Adjacent QDomText nodes can be merged into a single node with
+    normalize().
+
+    \l QDomElement nodes have attributes which can be retrieved with
+    attributes().
+
+    QDomElement and QDomAttr nodes can have namespaces which can be
+    retrieved with namespaceURI(). Their local name is retrieved with
+    localName(), and their prefix with prefix(). The prefix can be set
+    with setPrefix().
+
+    You can write the XML representation of the node to a text stream
+    with save().
+
+    The following example looks for the first element in an XML document and
+    prints the names of all the elements that are its direct children.
+    \code
+    QDomDocument d;
+    d.setContent( someXML );
+    QDomNode n = d.firstChild();
+    while ( !n.isNull() ) {
+	if ( n.isElement() ) {
+	    QDomElement e = n.toElement();
+	    cout << "Element name: " << e.tagName() << endl;
+	    break;
+	}
+	n = n.nextSibling();
+    }
+    \endcode
+
+    For further information about the Document Object Model see \link
+    http://www.w3.org/TR/REC-DOM-Level-1/\endlink and \link
+    http://www.w3.org/TR/DOM-Level-2-Core/\endlink. For a more general
+    introduction of the DOM implementation see the QDomDocument
+    documentation.
+*/
+
+/*!
+    Constructs a \link isNull() null\endlink node.
+*/
+QDomNode::QDomNode()
+{
+    impl = 0;
+}
+
+/*!
+    Constructs a copy of \a n.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomNode::QDomNode( const QDomNode& n )
+{
+    impl = n.impl;
+    if ( impl ) impl->ref();
+}
+
+/*!  \internal
+  Constructs a new node for the data \a n.
+*/
+QDomNode::QDomNode( QDomNodePrivate* n )
+{
+    impl = n;
+    if ( impl ) impl->ref();
+}
+
+/*!
+    Assigns a copy of \a n to this DOM node.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomNode& QDomNode::operator= ( const QDomNode& n )
+{
+    if ( n.impl ) n.impl->ref();
+    if ( impl && impl->deref() ) delete impl;
+    impl = n.impl;
+
+    return *this;
+}
+
+/*!
+    Returns TRUE if \a n and this DOM node are equal; otherwise
+    returns FALSE.
+*/
+bool QDomNode::operator== ( const QDomNode& n ) const
+{
+    return ( impl == n.impl );
+}
+
+/*!
+    Returns TRUE if \a n and this DOM node are not equal; otherwise
+    returns FALSE.
+*/
+bool QDomNode::operator!= ( const QDomNode& n ) const
+{
+    return ( impl != n.impl );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomNode::~QDomNode()
+{
+    if ( impl && impl->deref() ) delete impl;
+}
+
+/*!
+    Returns the name of the node.
+
+    The meaning of the name depends on the subclass:
+    \table
+    \header \i Name \i Meaning
+    \row \i QDomAttr \i The name of the attribute
+    \row \i QDomCDATASection \i The string "#cdata-section"
+    \row \i QDomComment \i The string "#comment"
+    \row \i QDomDocument \i The string "#document"
+    \row \i QDomDocumentFragment \i The string "#document-fragment"
+    \row \i QDomDocumentType \i The name of the document type
+    \row \i QDomElement \i The tag name
+    \row \i QDomEntity \i The name of the entity
+    \row \i QDomEntityReference \i The name of the referenced entity
+    \row \i QDomNotation \i The name of the notation
+    \row \i QDomProcessingInstruction \i The target of the processing instruction
+    \row \i QDomText \i The string "#text"
+    \endtable
+
+    \sa nodeValue()
+*/
+QString QDomNode::nodeName() const
+{
+    if ( !impl )
+	return QString::null;
+
+    if ( !IMPL->prefix.isEmpty() )
+	return IMPL->prefix + ":" + IMPL->name;
+    return IMPL->name;
+}
+
+/*!
+    Returns the value of the node.
+
+    The meaning of the value depends on the subclass:
+    \table
+    \header \i Name \i Meaning
+    \row \i QDomAttr \i The attribute value
+    \row \i QDomCDATASection \i The content of the CDATA section
+    \row \i QDomComment \i The comment
+    \row \i QDomProcessingInstruction \i The data of the processing intruction
+    \row \i QDomText \i The text
+    \endtable
+
+    All the other subclasses do not have a node value and will return
+    QString::null.
+
+    \sa setNodeValue() nodeName()
+*/
+QString QDomNode::nodeValue() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->value;
+}
+
+/*!
+    Sets the node's value to \a v.
+
+    \sa nodeValue()
+*/
+void QDomNode::setNodeValue( const QString& v )
+{
+    if ( !impl )
+	return;
+    IMPL->setNodeValue( v );
+}
+
+/*!
+    \enum QDomNode::NodeType
+
+    This enum defines the type of the node:
+    \value ElementNode
+    \value AttributeNode
+    \value TextNode
+    \value CDATASectionNode
+    \value EntityReferenceNode
+    \value EntityNode
+    \value ProcessingInstructionNode
+    \value CommentNode
+    \value DocumentNode
+    \value DocumentTypeNode
+    \value DocumentFragmentNode
+    \value NotationNode
+    \value BaseNode  A QDomNode object, i.e. not a QDomNode subclass.
+    \value CharacterDataNode
+*/
+
+/*!
+    Returns the type of the node.
+
+    \sa toAttr(), toCDATASection(), toDocumentFragment(),
+    toDocument() toDocumentType(), toElement(), toEntityReference(),
+    toText(), toEntity() toNotation(), toProcessingInstruction(),
+    toCharacterData(), toComment()
+*/
+QDomNode::NodeType QDomNode::nodeType() const
+{
+    if ( !impl )
+	return QDomNode::BaseNode;
+    return IMPL->nodeType();
+}
+
+/*!
+    Returns the parent node. If this node has no parent, a null node
+    is returned (i.e. a node for which isNull() returns TRUE).
+*/
+QDomNode QDomNode::parentNode() const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->parent() );
+}
+
+/*!
+    Returns a list of all direct child nodes.
+
+    Most often you will call this function on a QDomElement object.
+
+    For example, if the XML document looks like this:
+    \code
+    <body>
+    <h1>Heading</h1>
+    <p>Hello <b>you</b></p>
+    </body>
+    \endcode
+    Then the list of child nodes for the "body"-element will contain
+    the node created by the &lt;h1&gt; tag and the node created by the
+    &lt;p&gt; tag.
+
+    The nodes in the list are not copied; so changing the nodes in the
+    list will also change the children of this node.
+
+    \sa firstChild() lastChild()
+*/
+QDomNodeList QDomNode::childNodes() const
+{
+    if ( !impl )
+	return QDomNodeList();
+    return QDomNodeList( new QDomNodeListPrivate( impl ) );
+}
+
+/*!
+    Returns the first child of the node. If there is no child node, a
+    \link isNull() null node\endlink is returned. Changing the
+    returned node will also change the node in the document tree.
+
+    \sa lastChild() childNodes()
+*/
+QDomNode QDomNode::firstChild() const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->first );
+}
+
+/*!
+    Returns the last child of the node. If there is no child node, a
+    \link isNull() null node\endlink is returned. Changing the
+    returned node will also change the node in the document tree.
+
+    \sa firstChild() childNodes()
+*/
+QDomNode QDomNode::lastChild() const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->last );
+}
+
+/*!
+    Returns the previous sibling in the document tree. Changing the
+    returned node will also change the node in the document tree.
+
+    For example, if you have XML like this:
+    \code
+    <h1>Heading</h1>
+    <p>The text...</p>
+    <h2>Next heading</h2>
+    \endcode
+    and this QDomNode represents the &lt;p&gt; tag, previousSibling()
+    will return the node representing the &lt;h1&gt; tag.
+
+    \sa nextSibling()
+*/
+QDomNode QDomNode::previousSibling() const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->prev );
+}
+
+/*!
+    Returns the next sibling in the document tree. Changing the
+    returned node will also change the node in the document tree.
+
+    If you have XML like this:
+    \code
+    <h1>Heading</h1>
+    <p>The text...</p>
+    <h2>Next heading</h2>
+    \endcode
+    and this QDomNode represents the &lt;p&gt; tag, nextSibling() will
+    return the node representing the &lt;h2&gt; tag.
+
+    \sa previousSibling()
+*/
+QDomNode QDomNode::nextSibling() const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->next );
+}
+
+/*!
+    Returns a named node map of all attributes. Attributes are only
+    provided for \l{QDomElement}s.
+
+    Changing the attributes in the map will also change the attributes
+    of this QDomNode.
+*/
+QDomNamedNodeMap QDomNode::attributes() const
+{
+    if ( !impl )
+	return QDomNamedNodeMap();
+
+    return QDomNamedNodeMap( impl->attributes() );
+}
+
+/*!
+    Returns the document to which this node belongs.
+*/
+QDomDocument QDomNode::ownerDocument() const
+{
+    if ( !impl )
+	return QDomDocument();
+    return QDomDocument( IMPL->ownerDocument() );
+}
+
+/*!
+    Creates a deep (not shallow) copy of the QDomNode.
+
+    If \a deep is TRUE, then the cloning is done recursively which
+    means that all the node's children are deep copied too. If \a deep
+    is FALSE only the node itself is copied and the copy will have no
+    child nodes.
+*/
+QDomNode QDomNode::cloneNode( bool deep ) const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->cloneNode( deep ) );
+}
+
+/*!
+    Calling normalize() on an element converts all its children into a
+    standard form. This means that adjacent QDomText objects will be
+    merged into a single text object (QDomCDATASection nodes are not
+    merged).
+*/
+void QDomNode::normalize()
+{
+    if ( !impl )
+	return;
+    IMPL->normalize();
+}
+
+/*!
+    Returns TRUE if the DOM implementation implements the feature \a
+    feature and this feature is supported by this node in the version
+    \a version; otherwise returns FALSE.
+
+    \sa QDomImplementation::hasFeature()
+*/
+bool QDomNode::isSupported( const QString& feature, const QString& version ) const
+{
+    QDomImplementation i;
+    return i.hasFeature( feature, version );
+}
+
+/*!
+    Returns the namespace URI of this node or QString::null if the
+    node has no namespace URI.
+
+    Only nodes of type \link QDomNode::NodeType ElementNode\endlink or
+    \link QDomNode::NodeType AttributeNode\endlink can have
+    namespaces. A namespace URI must be specified at creation time and
+    cannot be changed later.
+
+    \sa prefix() localName() QDomDocument::createElementNS()
+    QDomDocument::createAttributeNS()
+*/
+QString QDomNode::namespaceURI() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->namespaceURI;
+}
+
+/*!
+    Returns the namespace prefix of the node or QString::null if the
+    node has no namespace prefix.
+
+    Only nodes of type \link QDomNode::NodeType ElementNode\endlink or
+    \link QDomNode::NodeType AttributeNode\endlink can have
+    namespaces. A namespace prefix must be specified at creation time.
+    If a node was created with a namespace prefix, you can change it
+    later with setPrefix().
+
+    If you create an element or attribute with
+    QDomDocument::createElement() or QDomDocument::createAttribute(),
+    the prefix will be QString::null. If you use
+    QDomDocument::createElementNS() or
+    QDomDocument::createAttributeNS() instead, the prefix will not be
+    QString::null; but it might be an empty string if the name does
+    not have a prefix.
+
+    \sa setPrefix() localName() namespaceURI()
+    QDomDocument::createElementNS() QDomDocument::createAttributeNS()
+*/
+QString QDomNode::prefix() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->prefix;
+}
+
+/*!
+    If the node has a namespace prefix, this function changes the
+    namespace prefix of the node to \a pre. Otherwise this function
+    does nothing.
+
+    Only nodes of type \link QDomNode::NodeType ElementNode\endlink or
+    \link QDomNode::NodeType AttributeNode\endlink can have
+    namespaces. A namespace prefix must have be specified at creation
+    time; it is not possible to add a namespace prefix afterwards.
+
+    \sa prefix() localName() namespaceURI()
+    QDomDocument::createElementNS() QDomDocument::createAttributeNS()
+*/
+void QDomNode::setPrefix( const QString& pre )
+{
+    if ( !impl || IMPL->prefix.isNull() )
+	return;
+    if ( isAttr() || isElement() )
+	IMPL->prefix = pre;
+}
+
+/*!
+    If the node uses namespaces, this function returns the local name
+    of the node; otherwise it returns QString::null.
+
+    Only nodes of type \link QDomNode::NodeType ElementNode\endlink or
+    \link QDomNode::NodeType AttributeNode\endlink can have
+    namespaces. A namespace must have been specified at creation time;
+    it is not possible to add a namespace afterwards.
+
+    \sa prefix() namespaceURI() QDomDocument::createElementNS()
+    QDomDocument::createAttributeNS()
+*/
+QString QDomNode::localName() const
+{
+    if ( !impl || IMPL->createdWithDom1Interface )
+	return QString::null;
+    return IMPL->name;
+}
+
+/*!
+    Returns TRUE if the node has attributes; otherwise returns FALSE.
+
+    \sa attributes()
+*/
+bool QDomNode::hasAttributes() const
+{
+    if ( !impl )
+	return FALSE;
+    return IMPL->hasAttributes();
+}
+
+/*!
+    Inserts the node \a newChild before the child node \a refChild.
+    \a refChild must be a direct child of this node. If \a refChild is
+    \link isNull() null\endlink then \a newChild is inserted as the
+    node's first child.
+
+    If \a newChild is the child of another node, it is reparented to
+    this node. If \a newChild is a child of this node, then its
+    position in the list of children is changed.
+
+    If \a newChild is a QDomDocumentFragment, then the children of the
+    fragment are removed from the fragment and inserted before \a
+    refChild.
+
+    Returns a new reference to \a newChild on success or a \link
+    isNull() null node\endlink on failure.
+
+    \sa insertAfter() replaceChild() removeChild() appendChild()
+*/
+QDomNode QDomNode::insertBefore( const QDomNode& newChild, const QDomNode& refChild )
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->insertBefore( newChild.impl, refChild.impl ) );
+}
+
+/*!
+    Inserts the node \a newChild after the child node \a refChild. \a
+    refChild must be a direct child of this node. If \a refChild is
+    \link isNull() null\endlink then \a newChild is appended as this
+    node's last child.
+
+    If \a newChild is the child of another node, it is reparented to
+    this node. If \a newChild is a child of this node, then its
+    position in the list of children is changed.
+
+    If \a newChild is a QDomDocumentFragment, then the children of the
+    fragment are removed from the fragment and inserted after \a
+    refChild.
+
+    Returns a new reference to \a newChild on success or a \link
+    isNull() null node\endlink on failure.
+
+    \sa insertBefore() replaceChild() removeChild() appendChild()
+*/
+QDomNode QDomNode::insertAfter( const QDomNode& newChild, const QDomNode& refChild )
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->insertAfter( newChild.impl, refChild.impl ) );
+}
+
+/*!
+    Replaces \a oldChild with \a newChild. \a oldChild must be a
+    direct child of this node.
+
+    If \a newChild is the child of another node, it is reparented to
+    this node. If \a newChild is a child of this node, then its
+    position in the list of children is changed.
+
+    If \a newChild is a QDomDocumentFragment, then \a oldChild is
+    replaced by all of the children of the fragment.
+
+    Returns a new reference to \a oldChild on success or a \link
+    isNull() null node\endlink an failure.
+
+    \sa insertBefore() insertAfter() removeChild() appendChild()
+*/
+QDomNode QDomNode::replaceChild( const QDomNode& newChild, const QDomNode& oldChild )
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->replaceChild( newChild.impl, oldChild.impl ) );
+}
+
+/*!
+    Removes \a oldChild from the list of children. \a oldChild must be
+    a direct child of this node.
+
+    Returns a new reference to \a oldChild on success or a \link
+    isNull() null node\endlink on failure.
+
+    \sa insertBefore() insertAfter() replaceChild() appendChild()
+*/
+QDomNode QDomNode::removeChild( const QDomNode& oldChild )
+{
+    if ( !impl )
+	return QDomNode();
+
+    if ( oldChild.isNull() )
+	return QDomNode();
+
+    return QDomNode( IMPL->removeChild( oldChild.impl ) );
+}
+
+/*!
+    Appends \a newChild as the node's last child.
+
+    If \a newChild is the child of another node, it is reparented to
+    this node. If \a newChild is a child of this node, then its
+    position in the list of children is changed.
+
+    If \a newChild is a QDomDocumentFragment, then the children of the
+    fragment are removed from the fragment and appended.
+
+    Returns a new reference to \a newChild.
+
+    \sa insertBefore() insertAfter() replaceChild() removeChild()
+*/
+QDomNode QDomNode::appendChild( const QDomNode& newChild )
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->appendChild( newChild.impl ) );
+}
+
+/*!
+    Returns TRUE if the node has one or more children; otherwise
+    returns FALSE.
+*/
+bool QDomNode::hasChildNodes() const
+{
+    if ( !impl )
+	return FALSE;
+    return IMPL->first != 0;
+}
+
+/*!
+    Returns TRUE if this node is null (i.e. if it has no type or
+    contents); otherwise returns FALSE.
+*/
+bool QDomNode::isNull() const
+{
+    return ( impl == 0 );
+}
+
+/*!
+    Converts the node into a null node; if it was not a null node
+    before, its type and contents are deleted.
+
+    \sa isNull()
+*/
+void QDomNode::clear()
+{
+    if ( impl && impl->deref() ) delete impl;
+    impl = 0;
+}
+
+/*!
+    Returns the first direct child node for which nodeName() equals \a
+    name.
+
+    If no such direct child exists, a \link isNull() null node\endlink
+    is returned.
+
+    \sa nodeName()
+*/
+QDomNode QDomNode::namedItem( const QString& name ) const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( impl->namedItem( name ) );
+}
+
+/*!
+    Writes the XML representation of the node and all its children to
+    the stream \a str. This function uses \a indent as the amount of
+    space to indent the node.
+*/
+void QDomNode::save( QTextStream& str, int indent ) const
+{
+    if ( impl )
+	IMPL->save( str, 1, indent );
+}
+
+/*!
+    \relates QDomNode
+
+    Writes the XML representation of the node \a node and all its
+    children to the stream \a str.
+*/
+QTextStream& operator<<( QTextStream& str, const QDomNode& node )
+{
+    node.save( str, 1 );
+
+    return str;
+}
+
+/*!
+    Returns TRUE if the node is an attribute; otherwise returns FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomAttribute; you can get the QDomAttribute with
+    toAttribute().
+
+    \sa toAttr()
+*/
+bool QDomNode::isAttr() const
+{
+    if(impl)
+	return impl->isAttr();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is a CDATA section; otherwise returns
+    FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomCDATASection; you can get the QDomCDATASection with
+    toCDATASection().
+
+    \sa toCDATASection()
+*/
+bool QDomNode::isCDATASection() const
+{
+    if(impl)
+	return impl->isCDATASection();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is a document fragment; otherwise returns
+    FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomDocumentFragment; you can get the QDomDocumentFragment
+    with toDocumentFragment().
+
+    \sa toDocumentFragment()
+*/
+bool QDomNode::isDocumentFragment() const
+{
+    if(impl)
+	return impl->isDocumentFragment();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is a document; otherwise returns FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomDocument; you can get the QDomDocument with toDocument().
+
+    \sa toDocument()
+*/
+bool QDomNode::isDocument() const
+{
+    if(impl)
+	return impl->isDocument();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is a document type; otherwise returns
+    FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomDocumentType; you can get the QDomDocumentType with
+    toDocumentType().
+
+    \sa toDocumentType()
+*/
+bool QDomNode::isDocumentType() const
+{
+    if(impl)
+	return impl->isDocumentType();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is an element; otherwise returns FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomElement; you can get the QDomElement with toElement().
+
+    \sa toElement()
+*/
+bool QDomNode::isElement() const
+{
+    if(impl)
+	return impl->isElement();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is an entity reference; otherwise returns
+    FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomEntityReference; you can get the QDomEntityReference with
+    toEntityReference().
+
+    \sa toEntityReference()
+*/
+bool QDomNode::isEntityReference() const
+{
+    if(impl)
+	return impl->isEntityReference();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is a text node; otherwise returns FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomText; you can get the QDomText with toText().
+
+    \sa toText()
+*/
+bool QDomNode::isText() const
+{
+    if(impl)
+	return impl->isText();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is an entity; otherwise returns FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomEntity; you can get the QDomEntity with toEntity().
+
+    \sa toEntity()
+*/
+bool QDomNode::isEntity() const
+{
+    if(impl)
+	return impl->isEntity();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is a notation; otherwise returns FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomNotation; you can get the QDomNotation with toNotation().
+
+    \sa toNotation()
+*/
+bool QDomNode::isNotation() const
+{
+    if(impl)
+	return impl->isNotation();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is a processing instruction; otherwise
+    returns FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomProcessingInstruction; you can get the
+    QProcessingInstruction with toProcessingInstruction().
+
+    \sa toProcessingInstruction()
+*/
+bool QDomNode::isProcessingInstruction() const
+{
+    if(impl)
+	return impl->isProcessingInstruction();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is a character data node; otherwise
+    returns FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomCharacterData; you can get the QDomCharacterData with
+    toCharacterData().
+
+    \sa toCharacterData()
+*/
+bool QDomNode::isCharacterData() const
+{
+    if(impl)
+	return impl->isCharacterData();
+    return FALSE;
+}
+
+/*!
+    Returns TRUE if the node is a comment; otherwise returns FALSE.
+
+    If this function returns TRUE, it does not imply that this object
+    is a QDomComment; you can get the QDomComment with toComment().
+
+    \sa toComment()
+*/
+bool QDomNode::isComment() const
+{
+     if(impl)
+	return impl->isComment();
+    return FALSE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomNamedNodeMapPrivate
+ *
+ **************************************************************/
+
+QDomNamedNodeMapPrivate::QDomNamedNodeMapPrivate( QDomNodePrivate* n )
+{
+    readonly = FALSE;
+    parent = n;
+    appendToParent = FALSE;
+}
+
+QDomNamedNodeMapPrivate::~QDomNamedNodeMapPrivate()
+{
+    clearMap();
+}
+
+QDomNamedNodeMapPrivate* QDomNamedNodeMapPrivate::clone( QDomNodePrivate* p )
+{
+    QDomNamedNodeMapPrivate* m = new QDomNamedNodeMapPrivate( p );
+    m->readonly = readonly;
+    m->appendToParent = appendToParent;
+
+    QDictIterator<QDomNodePrivate> it ( map );
+    for ( ; it.current(); ++it )
+	m->setNamedItem( it.current()->cloneNode() );
+
+    // we are no longer interested in ownership
+    m->deref();
+    return m;
+}
+
+void QDomNamedNodeMapPrivate::clearMap()
+{
+    // Dereference all of our children if we took references
+    if ( !appendToParent ) {
+	QDictIterator<QDomNodePrivate> it( map );
+	for ( ; it.current(); ++it )
+	    if ( it.current()->deref() )
+		delete it.current();
+    }
+
+    map.clear();
+}
+
+QDomNodePrivate* QDomNamedNodeMapPrivate::namedItem( const QString& name ) const
+{
+    QDomNodePrivate* p = map[ name ];
+    return p;
+}
+
+QDomNodePrivate* QDomNamedNodeMapPrivate::namedItemNS( const QString& nsURI, const QString& localName ) const
+{
+    QDictIterator<QDomNodePrivate> it( map );
+    QDomNodePrivate *n = it.current();
+    while ( n ) {
+	if ( !n->prefix.isNull() ) {
+	    // node has a namespace
+	    if ( n->namespaceURI==nsURI && n->name==localName ) {
+		return n;
+	    }
+	}
+	++it;
+	n = it.current();
+    }
+    return 0;
+}
+
+QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItem( QDomNodePrivate* arg )
+{
+    if ( readonly || !arg )
+	return 0;
+
+    if ( appendToParent )
+	return parent->appendChild( arg );
+
+    QDomNodePrivate *n = map[ arg->nodeName() ];
+    // We take a reference
+    arg->ref();
+    map.insert( arg->nodeName(), arg );
+    return n;
+}
+
+QDomNodePrivate* QDomNamedNodeMapPrivate::setNamedItemNS( QDomNodePrivate* arg )
+{
+    if ( readonly || !arg )
+	return 0;
+
+    if ( appendToParent )
+	return parent->appendChild( arg );
+
+    if ( !arg->prefix.isNull() ) {
+	// node has a namespace
+	QDomNodePrivate *n = namedItemNS( arg->namespaceURI, arg->name );
+	// We take a reference
+	arg->ref();
+	map.insert( arg->nodeName(), arg );
+	return n;
+    } else {
+	// ### check the following code if it is ok
+	return setNamedItem( arg );
+    }
+}
+
+QDomNodePrivate* QDomNamedNodeMapPrivate::removeNamedItem( const QString& name )
+{
+    if ( readonly )
+	return 0;
+
+    QDomNodePrivate* p = namedItem( name );
+    if ( p == 0 )
+	return 0;
+    if ( appendToParent )
+	return parent->removeChild( p );
+
+    map.remove( p->nodeName() );
+    // We took a reference, so we have to free one here
+    p->deref();
+    return p;
+}
+
+QDomNodePrivate* QDomNamedNodeMapPrivate::item( int index ) const
+{
+    if ( (uint)index >= length() )
+	return 0;
+
+    QDictIterator<QDomNodePrivate> it( map );
+    for ( int i = 0; i < index; ++i, ++it )
+	;
+    return it.current();
+}
+
+uint QDomNamedNodeMapPrivate::length() const
+{
+    return map.count();
+}
+
+bool QDomNamedNodeMapPrivate::contains( const QString& name ) const
+{
+    return ( map[ name ] != 0 );
+}
+
+bool QDomNamedNodeMapPrivate::containsNS( const QString& nsURI, const QString & localName ) const
+{
+    return ( namedItemNS( nsURI, localName ) != 0 );
+}
+
+/**************************************************************
+ *
+ * QDomNamedNodeMap
+ *
+ **************************************************************/
+
+#define IMPL ((QDomNamedNodeMapPrivate*)impl)
+
+/*!
+    \class QDomNamedNodeMap qdom.h
+    \reentrant
+    \brief The QDomNamedNodeMap class contains a collection of nodes
+    that can be accessed by name.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    Note that QDomNamedNodeMap does not inherit from QDomNodeList.
+    QDomNamedNodeMaps do not provide any specific node ordering.
+    Although nodes in a QDomNamedNodeMap may be accessed by an ordinal
+    index, this is simply to allow a convenient enumeration of the
+    contents of a QDomNamedNodeMap, and does not imply that the DOM
+    specifies an ordering of the nodes.
+
+    The QDomNamedNodeMap is used in three places:
+    \list 1
+    \i QDomDocumentType::entities() returns a map of all entities
+	described in the DTD.
+    \i QDomDocumentType::notations() returns a map of all notations
+	described in the DTD.
+    \i QDomNode::attributes() returns a map of all attributes of an
+	element.
+    \endlist
+
+    Items in the map are identified by the name which QDomNode::name()
+    returns. Nodes are retrieved using namedItem(), namedItemNS() or
+    item(). New nodes are inserted with setNamedItem() or
+    setNamedItemNS() and removed with removeNamedItem() or
+    removeNamedItemNS(). Use contains() to see if an item with the
+    given name is in the named node map. The number of items is
+    returned by length().
+
+    Terminology: in this class we use "item" and "node"
+    interchangeably.
+*/
+
+/*!
+    Constructs an empty named node map.
+*/
+QDomNamedNodeMap::QDomNamedNodeMap()
+{
+    impl = 0;
+}
+
+/*!
+    Constructs a copy of \a n.
+*/
+QDomNamedNodeMap::QDomNamedNodeMap( const QDomNamedNodeMap& n )
+{
+    impl = n.impl;
+    if ( impl )
+	impl->ref();
+}
+
+QDomNamedNodeMap::QDomNamedNodeMap( QDomNamedNodeMapPrivate* n )
+{
+    impl = n;
+    if ( impl )
+	impl->ref();
+}
+
+/*!
+    Assigns \a n to this named node map.
+*/
+QDomNamedNodeMap& QDomNamedNodeMap::operator= ( const QDomNamedNodeMap& n )
+{
+    if ( impl && impl->deref() )
+	delete impl;
+    impl = n.impl;
+    if ( impl )
+	impl->ref();
+
+    return *this;
+}
+
+/*!
+    Returns TRUE if \a n and this named node map are equal; otherwise
+    returns FALSE.
+*/
+bool QDomNamedNodeMap::operator== ( const QDomNamedNodeMap& n ) const
+{
+    return ( impl == n.impl );
+}
+
+/*!
+    Returns TRUE if \a n and this named node map are not equal;
+    otherwise returns FALSE.
+*/
+bool QDomNamedNodeMap::operator!= ( const QDomNamedNodeMap& n ) const
+{
+    return ( impl != n.impl );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomNamedNodeMap::~QDomNamedNodeMap()
+{
+    if ( impl && impl->deref() )
+	delete impl;
+}
+
+/*!
+    Returns the node called \a name.
+
+    If the named node map does not contain such a node, a \link
+    QDomNode::isNull() null node\endlink is returned. A node's name is
+    the name returned by QDomNode::nodeName().
+
+    \sa setNamedItem() namedItemNS()
+*/
+QDomNode QDomNamedNodeMap::namedItem( const QString& name ) const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->namedItem( name ) );
+}
+
+/*!
+    Inserts the node \a newNode into the named node map. The name used
+    by the map is the node name of \a newNode as returned by
+    QDomNode::nodeName().
+
+    If the new node replaces an existing node, i.e. the map contains a
+    node with the same name, the replaced node is returned.
+
+    \sa namedItem() removeNamedItem() setNamedItemNS()
+*/
+QDomNode QDomNamedNodeMap::setNamedItem( const QDomNode& newNode )
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->setNamedItem( (QDomNodePrivate*)newNode.impl ) );
+}
+
+/*!
+    Removes the node called \a name from the map.
+
+    The function returns the removed node or a \link
+    QDomNode::isNull() null node\endlink if the map did not contain a
+    node called \a name.
+
+    \sa setNamedItem() namedItem() removeNamedItemNS()
+*/
+QDomNode QDomNamedNodeMap::removeNamedItem( const QString& name )
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->removeNamedItem( name ) );
+}
+
+/*!
+    Retrieves the node at position \a index.
+
+    This can be used to iterate over the map. Note that the nodes in
+    the map are ordered arbitrarily.
+
+    \sa length()
+*/
+QDomNode QDomNamedNodeMap::item( int index ) const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->item( index ) );
+}
+
+/*!
+    Returns the node associated with the local name \a localName and
+    the namespace URI \a nsURI.
+
+    If the map does not contain such a node, a \link
+    QDomNode::isNull() null node\endlink is returned.
+
+    \sa setNamedItemNS() namedItem()
+*/
+QDomNode QDomNamedNodeMap::namedItemNS( const QString& nsURI, const QString& localName ) const
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->namedItemNS( nsURI, localName ) );
+}
+
+/*!
+    Inserts the node \a newNode in the map. If a node with the same
+    namespace URI and the same local name already exists in the map,
+    it is replaced by \a newNode. If the new node replaces an existing
+    node, the replaced node is returned.
+
+    \sa namedItemNS() removeNamedItemNS() setNamedItem()
+*/
+QDomNode QDomNamedNodeMap::setNamedItemNS( const QDomNode& newNode )
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->setNamedItemNS( (QDomNodePrivate*)newNode.impl ) );
+}
+
+/*!
+    Removes the node with the local name \a localName and the
+    namespace URI \a nsURI from the map.
+
+    The function returns the removed node or a \link
+    QDomNode::isNull() null node\endlink if the map did not contain a
+    node with the local name \a localName and the namespace URI \a
+    nsURI.
+
+    \sa setNamedItemNS() namedItemNS() removeNamedItem()
+*/
+QDomNode QDomNamedNodeMap::removeNamedItemNS( const QString& nsURI, const QString& localName )
+{
+    if ( !impl )
+	return QDomNode();
+    QDomNodePrivate *n = IMPL->namedItemNS( nsURI, localName );
+    if ( !n )
+	return QDomNode();
+    return QDomNode( IMPL->removeNamedItem( n->name ) );
+}
+
+/*!
+    Returns the number of nodes in the map.
+
+    \sa item()
+*/
+uint QDomNamedNodeMap::length() const
+{
+    if ( !impl )
+	return 0;
+    return IMPL->length();
+}
+
+/*!
+    \fn uint QDomNamedNodeMap::count() const
+
+    Returns the number of nodes in the map.
+
+    This function is the same as length().
+*/
+
+/*!
+    Returns TRUE if the map contains a node called \a name; otherwise
+    returns FALSE.
+*/
+bool QDomNamedNodeMap::contains( const QString& name ) const
+{
+    if ( !impl )
+	return FALSE;
+    return IMPL->contains( name );
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomDocumentTypePrivate
+ *
+ **************************************************************/
+
+QDomDocumentTypePrivate::QDomDocumentTypePrivate( QDomDocumentPrivate* doc, QDomNodePrivate* parent )
+    : QDomNodePrivate( doc, parent )
+{
+    init();
+}
+
+QDomDocumentTypePrivate::QDomDocumentTypePrivate( QDomDocumentTypePrivate* n, bool deep )
+    : QDomNodePrivate( n, deep )
+{
+    init();
+    // Refill the maps with our new children
+    QDomNodePrivate* p = first;
+    while ( p ) {
+	if ( p->isEntity() )
+	    // Dont use normal insert function since we would create infinite recursion
+	    entities->map.insert( p->nodeName(), p );
+	if ( p->isNotation() )
+	    // Dont use normal insert function since we would create infinite recursion
+	    notations->map.insert( p->nodeName(), p );
+    }
+}
+
+QDomDocumentTypePrivate::~QDomDocumentTypePrivate()
+{
+    if ( entities->deref() )
+	delete entities;
+    if ( notations->deref() )
+	delete notations;
+}
+
+void QDomDocumentTypePrivate::init()
+{
+    entities = new QDomNamedNodeMapPrivate( this );
+    notations = new QDomNamedNodeMapPrivate( this );
+    publicId = QString::null;
+    systemId = QString::null;
+    internalSubset = QString::null;
+
+    entities->setAppendToParent( TRUE );
+    notations->setAppendToParent( TRUE );
+}
+
+QDomNodePrivate* QDomDocumentTypePrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomDocumentTypePrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+QDomNodePrivate* QDomDocumentTypePrivate::insertBefore( QDomNodePrivate* newChild, QDomNodePrivate* refChild )
+{
+    // Call the origianl implementation
+    QDomNodePrivate* p = QDomNodePrivate::insertBefore( newChild, refChild );
+    // Update the maps
+    if ( p && p->isEntity() )
+	entities->map.insert( p->nodeName(), p );
+    else if ( p && p->isNotation() )
+	notations->map.insert( p->nodeName(), p );
+
+    return p;
+}
+
+QDomNodePrivate* QDomDocumentTypePrivate::insertAfter( QDomNodePrivate* newChild, QDomNodePrivate* refChild )
+{
+    // Call the origianl implementation
+    QDomNodePrivate* p = QDomNodePrivate::insertAfter( newChild, refChild );
+    // Update the maps
+    if ( p && p->isEntity() )
+	entities->map.insert( p->nodeName(), p );
+    else if ( p && p->isNotation() )
+	notations->map.insert( p->nodeName(), p );
+
+    return p;
+}
+
+QDomNodePrivate* QDomDocumentTypePrivate::replaceChild( QDomNodePrivate* newChild, QDomNodePrivate* oldChild )
+{
+    // Call the origianl implementation
+    QDomNodePrivate* p = QDomNodePrivate::replaceChild( newChild, oldChild );
+    // Update the maps
+    if ( p ) {
+	if ( oldChild && oldChild->isEntity() )
+	    entities->map.remove( oldChild->nodeName() );
+	else if ( oldChild && oldChild->isNotation() )
+	    notations->map.remove( oldChild->nodeName() );
+
+	if ( p->isEntity() )
+	    entities->map.insert( p->nodeName(), p );
+	else if ( p->isNotation() )
+	    notations->map.insert( p->nodeName(), p );
+    }
+
+    return p;
+}
+
+QDomNodePrivate* QDomDocumentTypePrivate::removeChild( QDomNodePrivate* oldChild )
+{
+    // Call the origianl implementation
+    QDomNodePrivate* p = QDomNodePrivate::removeChild(  oldChild );
+    // Update the maps
+    if ( p && p->isEntity() )
+	entities->map.remove( p->nodeName() );
+    else if ( p && p->isNotation() )
+	notations->map.remove( p ->nodeName() );
+
+    return p;
+}
+
+QDomNodePrivate* QDomDocumentTypePrivate::appendChild( QDomNodePrivate* newChild )
+{
+    return insertAfter( newChild, 0 );
+}
+
+void QDomDocumentTypePrivate::save( QTextStream& s, int, int indent ) const
+{
+    if ( name.isEmpty() )
+	return;
+
+    s << "<!DOCTYPE " << name;
+
+    if ( !publicId.isNull() ) {
+	s << " PUBLIC \"" << publicId << "\"";
+	if ( !systemId.isNull() )
+	    s << " \"" << systemId << "\"";
+    } else if ( !systemId.isNull() ) {
+	s << " SYSTEM \"" << systemId << "\"";
+    }
+
+    if ( entities->length()>0 || notations->length()>0 ) {
+	s << " [ " << endl;
+
+	QDictIterator<QDomNodePrivate> it2( notations->map );
+	for ( ; it2.current(); ++it2 )
+	    it2.current()->save( s, 0, indent );
+
+	QDictIterator<QDomNodePrivate> it( entities->map );
+	for ( ; it.current(); ++it )
+	    it.current()->save( s, 0, indent );
+
+	s << " ]";
+    }
+
+    s << ">" << endl;
+}
+
+/**************************************************************
+ *
+ * QDomDocumentType
+ *
+ **************************************************************/
+
+#define IMPL ((QDomDocumentTypePrivate*)impl)
+
+/*!
+    \class QDomDocumentType qdom.h
+    \reentrant
+    \brief The QDomDocumentType class is the representation of the DTD
+    in the document tree.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    The QDomDocumentType class allows read-only access to some of the
+    data structures in the DTD: it can return a map of all entities()
+    and notations(). In addition the function name() returns the name
+    of the document type as specified in the &lt;!DOCTYPE name&gt;
+    tag. This class also provides the publicId(), systemId() and
+    internalSubset() functions.
+
+    \sa QDomDocument
+*/
+
+/*!
+    Creates an empty QDomDocumentType object.
+*/
+QDomDocumentType::QDomDocumentType() : QDomNode()
+{
+}
+
+/*!
+    Constructs a copy of \a n.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomDocumentType::QDomDocumentType( const QDomDocumentType& n )
+    : QDomNode( n )
+{
+}
+
+QDomDocumentType::QDomDocumentType( QDomDocumentTypePrivate* n )
+    : QDomNode( n )
+{
+}
+
+/*!
+    Assigns \a n to this document type.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomDocumentType& QDomDocumentType::operator= ( const QDomDocumentType& n )
+{
+    return (QDomDocumentType&) QDomNode::operator=( n );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomDocumentType::~QDomDocumentType()
+{
+}
+
+/*!
+    Returns the name of the document type as specified in the
+    &lt;!DOCTYPE name&gt; tag.
+
+    \sa nodeName()
+*/
+QString QDomDocumentType::name() const
+{
+    if ( !impl )
+	return QString::null;
+
+    return IMPL->nodeName();
+}
+
+/*!
+    Returns a map of all entities described in the DTD.
+*/
+QDomNamedNodeMap QDomDocumentType::entities() const
+{
+    if ( !impl )
+	return QDomNamedNodeMap();
+    return QDomNamedNodeMap( IMPL->entities );
+}
+
+/*!
+    Returns a map of all notations described in the DTD.
+*/
+QDomNamedNodeMap QDomDocumentType::notations() const
+{
+    if ( !impl )
+	return QDomNamedNodeMap();
+    return QDomNamedNodeMap( IMPL->notations );
+}
+
+/*!
+    Returns the public identifier of the external DTD subset or
+    QString::null if there is no public identifier.
+
+    \sa systemId() internalSubset() QDomImplementation::createDocumentType()
+*/
+QString QDomDocumentType::publicId() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->publicId;
+}
+
+/*!
+    Returns the system identifier of the external DTD subset or
+    QString::null if there is no system identifier.
+
+    \sa publicId() internalSubset() QDomImplementation::createDocumentType()
+*/
+QString QDomDocumentType::systemId() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->systemId;
+}
+
+/*!
+    Returns the internal subset of the document type or QString::null
+    if there is no internal subset.
+
+    \sa publicId() systemId()
+*/
+QString QDomDocumentType::internalSubset() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->internalSubset;
+}
+
+/*!
+    Returns \c DocumentTypeNode.
+
+    \sa isDocumentType() QDomNode::toDocumentType()
+*/
+QDomNode::NodeType QDomDocumentType::nodeType() const
+{
+    return DocumentTypeNode;
+}
+
+/*!
+    This function overloads QDomNode::isDocumentType().
+
+    \sa nodeType() QDomNode::toDocumentType()
+*/
+bool QDomDocumentType::isDocumentType() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomDocumentFragmentPrivate
+ *
+ **************************************************************/
+
+QDomDocumentFragmentPrivate::QDomDocumentFragmentPrivate( QDomDocumentPrivate* doc, QDomNodePrivate* parent )
+    : QDomNodePrivate( doc, parent )
+{
+    name = "#document-fragment";
+}
+
+QDomDocumentFragmentPrivate::QDomDocumentFragmentPrivate( QDomNodePrivate* n, bool deep )
+    : QDomNodePrivate( n, deep )
+{
+}
+
+QDomDocumentFragmentPrivate::~QDomDocumentFragmentPrivate()
+{
+}
+
+QDomNodePrivate* QDomDocumentFragmentPrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomDocumentFragmentPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+/**************************************************************
+ *
+ * QDomDocumentFragment
+ *
+ **************************************************************/
+
+#define IMPL ((QDomDocumentFragmentPrivate*)impl)
+
+/*!
+    \class QDomDocumentFragment qdom.h
+    \reentrant
+    \brief The QDomDocumentFragment class is a tree of QDomNodes which is not usually a complete QDomDocument.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    If you want to do complex tree operations it is useful to have a
+    lightweight class to store nodes and their relations.
+    QDomDocumentFragment stores a subtree of a document which does not
+    necessarily represent a well-formed XML document.
+
+    QDomDocumentFragment is also useful if you want to group several
+    nodes in a list and insert them all together as children of some
+    node. In these cases QDomDocumentFragment can be used as a
+    temporary container for this list of children.
+
+    The most important feature of QDomDocumentFragment is that it is
+    treated in a special way by QDomNode::insertAfter(),
+    QDomNode::insertBefore(), QDomNode::replaceChild() and
+    QDomNode::appendChild(): instead of inserting the fragment itself, all
+    the fragment's children are inserted.
+*/
+
+/*!
+    Constructs an empty document fragment.
+*/
+QDomDocumentFragment::QDomDocumentFragment()
+{
+}
+
+QDomDocumentFragment::QDomDocumentFragment( QDomDocumentFragmentPrivate* n )
+    : QDomNode( n )
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomDocumentFragment::QDomDocumentFragment( const QDomDocumentFragment& x )
+    : QDomNode( x )
+{
+}
+
+/*!
+    Assigns \a x to this DOM document fragment.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomDocumentFragment& QDomDocumentFragment::operator= ( const QDomDocumentFragment& x )
+{
+    return (QDomDocumentFragment&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomDocumentFragment::~QDomDocumentFragment()
+{
+}
+
+/*!
+    Returns \c DocumentFragment.
+
+    \sa isDocumentFragment() QDomNode::toDocumentFragment()
+*/
+QDomNode::NodeType QDomDocumentFragment::nodeType() const
+{
+    return QDomNode::DocumentFragmentNode;
+}
+
+/*!
+    This function reimplements QDomNode::isDocumentFragment().
+
+    \sa nodeType() QDomNode::toDocumentFragment()
+*/
+bool QDomDocumentFragment::isDocumentFragment() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomCharacterDataPrivate
+ *
+ **************************************************************/
+
+QDomCharacterDataPrivate::QDomCharacterDataPrivate( QDomDocumentPrivate* d, QDomNodePrivate* p,
+						      const QString& data )
+    : QDomNodePrivate( d, p )
+{
+    value = data;
+
+    name = "#character-data";
+}
+
+QDomCharacterDataPrivate::QDomCharacterDataPrivate( QDomCharacterDataPrivate* n, bool deep )
+    : QDomNodePrivate( n, deep )
+{
+}
+
+QDomCharacterDataPrivate::~QDomCharacterDataPrivate()
+{
+}
+
+QDomNodePrivate* QDomCharacterDataPrivate::cloneNode( bool deep )
+{
+    QDomNodePrivate* p = new QDomCharacterDataPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+uint QDomCharacterDataPrivate::dataLength() const
+{
+    return value.length();
+}
+
+QString QDomCharacterDataPrivate::substringData( unsigned long offset, unsigned long n ) const
+{
+    return value.mid( offset, n );
+}
+
+void QDomCharacterDataPrivate::insertData( unsigned long offset, const QString& arg )
+{
+    value.insert( offset, arg );
+}
+
+void QDomCharacterDataPrivate::deleteData( unsigned long offset, unsigned long n )
+{
+    value.remove( offset, n );
+}
+
+void QDomCharacterDataPrivate::replaceData( unsigned long offset, unsigned long n, const QString& arg )
+{
+    value.replace( offset, n, arg );
+}
+
+void QDomCharacterDataPrivate::appendData( const QString& arg )
+{
+    value += arg;
+}
+
+/**************************************************************
+ *
+ * QDomCharacterData
+ *
+ **************************************************************/
+
+#define IMPL ((QDomCharacterDataPrivate*)impl)
+
+/*!
+    \class QDomCharacterData qdom.h
+    \reentrant
+    \brief The QDomCharacterData class represents a generic string in the DOM.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    Character data as used in XML specifies a generic data string.
+    More specialized versions of this class are QDomText, QDomComment
+    and QDomCDATASection.
+
+    The data string is set with setData() and retrieved with data().
+    You can retrieve a portion of the data string using
+    substringData(). Extra data can be appended with appendData(), or
+    inserted with insertData(). Portions of the data string can be
+    deleted with deleteData() or replaced with replaceData(). The
+    length of the data string is returned by length().
+
+    The node type of the node containing this character data is
+    returned by nodeType().
+
+    \sa QDomText QDomComment QDomCDATASection
+*/
+
+/*!
+    Constructs an empty character data object.
+*/
+QDomCharacterData::QDomCharacterData()
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomCharacterData::QDomCharacterData( const QDomCharacterData& x )
+    : QDomNode( x )
+{
+}
+
+QDomCharacterData::QDomCharacterData( QDomCharacterDataPrivate* n )
+    : QDomNode( n )
+{
+}
+
+/*!
+    Assigns \a x to this character data.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomCharacterData& QDomCharacterData::operator= ( const QDomCharacterData& x )
+{
+    return (QDomCharacterData&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomCharacterData::~QDomCharacterData()
+{
+}
+
+/*!
+    Returns the string stored in this object.
+
+    If the node is a \link isNull() null node\endlink, it will return
+    QString::null.
+*/
+QString QDomCharacterData::data() const
+{
+    if ( !impl )
+	return QString::null;
+    return impl->nodeValue();
+}
+
+/*!
+    Sets this object's string to \a v.
+*/
+void QDomCharacterData::setData( const QString& v )
+{
+    if ( impl )
+	impl->setNodeValue( v );
+}
+
+/*!
+    Returns the length of the stored string.
+*/
+uint QDomCharacterData::length() const
+{
+    if ( impl )
+	return IMPL->dataLength();
+    return 0;
+}
+
+/*!
+    Returns the substring of length \a count from position \a offset.
+*/
+QString QDomCharacterData::substringData( unsigned long offset, unsigned long count )
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->substringData( offset, count );
+}
+
+/*!
+    Appends the string \a arg to the stored string.
+*/
+void QDomCharacterData::appendData( const QString& arg )
+{
+    if ( impl )
+	IMPL->appendData( arg );
+}
+
+/*!
+    Inserts the string \a arg into the stored string at position \a offset.
+*/
+void QDomCharacterData::insertData( unsigned long offset, const QString& arg )
+{
+    if ( impl )
+	IMPL->insertData( offset, arg );
+}
+
+/*!
+    Deletes a substring of length \a count from position \a offset.
+*/
+void QDomCharacterData::deleteData( unsigned long offset, unsigned long count )
+{
+    if ( impl )
+	IMPL->deleteData( offset, count );
+}
+
+/*!
+    Replaces the substring of length \a count starting at position \a
+    offset with the string \a arg.
+*/
+void QDomCharacterData::replaceData( unsigned long offset, unsigned long count, const QString& arg )
+{
+    if ( impl )
+	IMPL->replaceData( offset, count, arg );
+}
+
+/*!
+    Returns the type of node this object refers to (i.e. \c TextNode,
+    \c CDATASectionNode, \c CommentNode or \c CharacterDataNode). For
+    a \link isNull() null node\endlink \c CharacterDataNode is
+    returned.
+*/
+QDomNode::NodeType QDomCharacterData::nodeType() const
+{
+    if( !impl )
+	return CharacterDataNode;
+    return QDomNode::nodeType();
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomCharacterData::isCharacterData() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomAttrPrivate
+ *
+ **************************************************************/
+
+QDomAttrPrivate::QDomAttrPrivate( QDomDocumentPrivate* d, QDomNodePrivate* parent, const QString& name_ )
+    : QDomNodePrivate( d, parent )
+{
+    name = name_;
+    m_specified = FALSE;
+}
+
+QDomAttrPrivate::QDomAttrPrivate( QDomDocumentPrivate* d, QDomNodePrivate* p, const QString& nsURI, const QString& qName )
+    : QDomNodePrivate( d, p )
+{
+    qt_split_namespace( prefix, name, qName, !nsURI.isNull() );
+    namespaceURI = nsURI;
+    createdWithDom1Interface = FALSE;
+    m_specified = FALSE;
+}
+
+QDomAttrPrivate::QDomAttrPrivate( QDomAttrPrivate* n, bool deep )
+    : QDomNodePrivate( n, deep )
+{
+    m_specified = n->specified();
+}
+
+QDomAttrPrivate::~QDomAttrPrivate()
+{
+}
+
+void QDomAttrPrivate::setNodeValue( const QString& v )
+{
+    value = v;
+    QDomTextPrivate *t = new QDomTextPrivate( 0, this, v );
+    // keep the refcount balanced: appendChild() does a ref() anyway.
+    t->deref();
+    if ( first ) {
+	delete removeChild( first );
+    }
+    appendChild( t );
+}
+
+QDomNodePrivate* QDomAttrPrivate::cloneNode( bool deep )
+{
+    QDomNodePrivate* p = new QDomAttrPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+bool QDomAttrPrivate::specified() const
+{
+    return m_specified;
+}
+
+/*
+  Encode an attribute value upon saving.
+*/
+static QString encodeAttr( const QString& str )
+{
+    QString tmp( str );
+    uint len = tmp.length();
+    uint i = 0;
+    while ( i < len ) {
+	if ( tmp[(int)i] == '<' ) {
+	    tmp.replace( i, 1, "&lt;" );
+	    len += 3;
+	    i += 4;
+	} else if ( tmp[(int)i] == '"' ) {
+	    tmp.replace( i, 1, "&quot;" );
+	    len += 5;
+	    i += 6;
+	} else if ( tmp[(int)i] == '&' ) {
+	    tmp.replace( i, 1, "&amp;" );
+	    len += 4;
+	    i += 5;
+	} else if ( tmp[(int)i] == '>' && i>=2 && tmp[(int)i-1]==']' && tmp[(int)i-2]==']' ) {
+	    tmp.replace( i, 1, "&gt;" );
+	    len += 3;
+	    i += 4;
+	} else {
+	    ++i;
+	}
+    }
+
+    return tmp;
+}
+
+void QDomAttrPrivate::save( QTextStream& s, int, int ) const
+{
+    if ( namespaceURI.isNull() ) {
+	s << name << "=\"" << encodeAttr( value ) << "\"";
+    } else {
+	// ### optimize this (see comment of QDomElementPrivate::save()
+	s << prefix << ":" << name << "=\"" << encodeAttr( value ) << "\""
+	    << " xmlns:" << prefix << "=\"" << encodeAttr( namespaceURI ) << "\"";
+    }
+}
+
+/**************************************************************
+ *
+ * QDomAttr
+ *
+ **************************************************************/
+
+#define IMPL ((QDomAttrPrivate*)impl)
+
+/*!
+    \class QDomAttr qdom.h
+    \reentrant
+    \brief The QDomAttr class represents one attribute of a QDomElement.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    For example, the following piece of XML produces an element with
+    no children, but two attributes:
+
+    \code
+    <link href="http://www.trolltech.com" color="red" />
+    \endcode
+
+    You can access the attributes of an element with code like this:
+
+    \code
+    QDomElement e = //...
+    //...
+    QDomAttr a = e.attributeNode( "href" );
+    cout << a.value() << endl;                // prints "http://www.trolltech.com"
+    a.setValue( "http://doc.trolltech.com" ); // change the node's attribute
+    QDomAttr a2 = e.attributeNode( "href" );
+    cout << a2.value() << endl;               // prints "http://doc.trolltech.com"
+    \endcode
+
+    This example also shows that changing an attribute received from
+    an element changes the attribute of the element. If you do not
+    want to change the value of the element's attribute you must
+    use cloneNode() to get an independent copy of the attribute.
+
+    QDomAttr can return the name() and value() of an attribute. An
+    attribute's value is set with setValue(). If specified() returns
+    TRUE the value was either set in the document or set with
+    setValue(); otherwise the value hasn't been set. The node this
+    attribute is attached to (if any) is returned by ownerElement().
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+
+/*!
+    Constructs an empty attribute.
+*/
+QDomAttr::QDomAttr()
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomAttr::QDomAttr( const QDomAttr& x )
+    : QDomNode( x )
+{
+}
+
+QDomAttr::QDomAttr( QDomAttrPrivate* n )
+    : QDomNode( n )
+{
+}
+
+/*!
+    Assigns \a x to this DOM attribute.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomAttr& QDomAttr::operator= ( const QDomAttr& x )
+{
+    return (QDomAttr&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomAttr::~QDomAttr()
+{
+}
+
+/*!
+    Returns the attribute's name.
+*/
+QString QDomAttr::name() const
+{
+    if ( !impl )
+	return QString::null;
+    return impl->nodeName();
+}
+
+/*!
+    Returns TRUE if the attribute has either been expicitly specified
+    in the XML document or was set by the user with setValue().
+    Returns FALSE if the value hasn't been specified or set.
+
+    \sa setValue()
+*/
+bool QDomAttr::specified() const
+{
+    if ( !impl )
+	return FALSE;
+    return IMPL->specified();
+}
+
+/*!
+    Returns the element node this attribute is attached to or a \link
+    QDomNode::isNull() null node\endlink if this attribute is not
+    attached to any element.
+*/
+QDomElement QDomAttr::ownerElement() const
+{
+    if ( !impl && !impl->parent()->isElement() )
+	return QDomElement();
+    return QDomElement( (QDomElementPrivate*)(impl->parent()) );
+}
+
+/*!
+    Returns the value of the attribute or QString::null if the
+    attribute has not been specified.
+
+    \sa specified() setValue()
+*/
+QString QDomAttr::value() const
+{
+    if ( !impl )
+	return QString::null;
+    return impl->nodeValue();
+}
+
+/*!
+    Sets the attribute's value to \a v.
+
+    \sa value()
+*/
+void QDomAttr::setValue( const QString& v )
+{
+    if ( !impl )
+	return;
+    impl->setNodeValue( v );
+    IMPL->m_specified = TRUE;
+}
+
+/*!
+    Returns \link QDomNode::NodeType AttributeNode\endlink.
+*/
+QDomNode::NodeType QDomAttr::nodeType() const
+{
+    return AttributeNode;
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomAttr::isAttr() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomElementPrivate
+ *
+ **************************************************************/
+
+QDomElementPrivate::QDomElementPrivate( QDomDocumentPrivate* d, QDomNodePrivate* p,
+					  const QString& tagname )
+    : QDomNodePrivate( d, p )
+{
+    name = tagname;
+    m_attr = new QDomNamedNodeMapPrivate( this );
+}
+
+QDomElementPrivate::QDomElementPrivate( QDomDocumentPrivate* d, QDomNodePrivate* p,
+	const QString& nsURI, const QString& qName )
+    : QDomNodePrivate( d, p )
+{
+    qt_split_namespace( prefix, name, qName, !nsURI.isNull() );
+    namespaceURI = nsURI;
+    createdWithDom1Interface = FALSE;
+    m_attr = new QDomNamedNodeMapPrivate( this );
+}
+
+QDomElementPrivate::QDomElementPrivate( QDomElementPrivate* n, bool deep ) :
+    QDomNodePrivate( n, deep )
+{
+    m_attr = n->m_attr->clone( this );
+    // Reference is down to 0, so we set it to 1 here.
+    m_attr->ref();
+}
+
+QDomElementPrivate::~QDomElementPrivate()
+{
+    if ( m_attr->deref() )
+	delete m_attr;
+}
+
+QDomNodePrivate* QDomElementPrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomElementPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+QString QDomElementPrivate::attribute( const QString& name_, const QString& defValue ) const
+{
+    QDomNodePrivate* n = m_attr->namedItem( name_ );
+    if ( !n )
+	return defValue;
+
+    return n->nodeValue();
+}
+
+QString QDomElementPrivate::attributeNS( const QString& nsURI, const QString& localName, const QString& defValue ) const
+{
+    QDomNodePrivate* n = m_attr->namedItemNS( nsURI, localName );
+    if ( !n )
+	return defValue;
+
+    return n->nodeValue();
+}
+
+void QDomElementPrivate::setAttribute( const QString& aname, const QString& newValue )
+{
+    QDomNodePrivate* n = m_attr->namedItem( aname );
+    if ( !n ) {
+	n = new QDomAttrPrivate( ownerDocument(), this, aname );
+	n->setNodeValue( newValue );
+
+	// Referencing is done by the map, so we set the reference counter back
+	// to 0 here. This is ok since we created the QDomAttrPrivate.
+	n->deref();
+	m_attr->setNamedItem( n );
+    } else {
+	n->setNodeValue( newValue );
+    }
+}
+
+void QDomElementPrivate::setAttributeNS( const QString& nsURI, const QString& qName, const QString& newValue )
+{
+    QString prefix, localName;
+    qt_split_namespace( prefix, localName, qName, TRUE );
+    QDomNodePrivate* n = m_attr->namedItemNS( nsURI, localName );
+    if ( !n ) {
+	n = new QDomAttrPrivate( ownerDocument(), this, nsURI, qName );
+	n->setNodeValue( newValue );
+
+	// Referencing is done by the map, so we set the reference counter back
+	// to 0 here. This is ok since we created the QDomAttrPrivate.
+	n->deref();
+	m_attr->setNamedItem( n );
+    } else {
+	n->setNodeValue( newValue );
+	n->prefix = prefix;
+    }
+}
+
+void QDomElementPrivate::removeAttribute( const QString& aname )
+{
+    QDomNodePrivate* p = m_attr->removeNamedItem( aname );
+    if ( p && p->count == 0 )
+	delete p;
+}
+
+QDomAttrPrivate* QDomElementPrivate::attributeNode( const QString& aname )
+{
+    return (QDomAttrPrivate*)m_attr->namedItem( aname );
+}
+
+QDomAttrPrivate* QDomElementPrivate::attributeNodeNS( const QString& nsURI, const QString& localName )
+{
+    return (QDomAttrPrivate*)m_attr->namedItemNS( nsURI, localName );
+}
+
+QDomAttrPrivate* QDomElementPrivate::setAttributeNode( QDomAttrPrivate* newAttr )
+{
+    QDomNodePrivate* n = m_attr->namedItem( newAttr->nodeName() );
+
+    // Referencing is done by the maps
+    m_attr->setNamedItem( newAttr );
+
+    return (QDomAttrPrivate*)n;
+}
+
+QDomAttrPrivate* QDomElementPrivate::setAttributeNodeNS( QDomAttrPrivate* newAttr )
+{
+    QDomNodePrivate* n = 0;
+    if ( !newAttr->prefix.isNull() )
+	n = m_attr->namedItemNS( newAttr->namespaceURI, newAttr->name );
+
+    // Referencing is done by the maps
+    m_attr->setNamedItem( newAttr );
+
+    return (QDomAttrPrivate*)n;
+}
+
+QDomAttrPrivate* QDomElementPrivate::removeAttributeNode( QDomAttrPrivate* oldAttr )
+{
+    return (QDomAttrPrivate*)m_attr->removeNamedItem( oldAttr->nodeName() );
+}
+
+bool QDomElementPrivate::hasAttribute( const QString& aname )
+{
+    return m_attr->contains( aname );
+}
+
+bool QDomElementPrivate::hasAttributeNS( const QString& nsURI, const QString& localName )
+{
+    return m_attr->containsNS( nsURI, localName );
+}
+
+QString QDomElementPrivate::text()
+{
+    QString t( "" );
+
+    QDomNodePrivate* p = first;
+    while ( p ) {
+	if ( p->isText() || p->isCDATASection() )
+	    t += p->nodeValue();
+	else if ( p->isElement() )
+	    t += ((QDomElementPrivate*)p)->text();
+	p = p->next;
+    }
+
+    return t;
+}
+
+void QDomElementPrivate::save( QTextStream& s, int depth, int indent ) const
+{
+    if ( !( prev && prev->isText() ) )
+	for ( int i = 0; i < depth*indent; ++i )
+	    s << " ";
+
+    QString qName( name );
+    QString nsDecl( "" );
+    if ( !namespaceURI.isNull() ) {
+	// ### optimize this, so that you only declare namespaces that are not
+	// yet declared -- we loose default namespace mappings, so maybe we
+	// should rather store the information that we get from
+	// startPrefixMapping()/endPrefixMapping() and use them (you have to
+	// take care if the DOM tree is modified, though)
+	if ( prefix.isEmpty() ) {
+	    nsDecl = " xmlns";
+	} else {
+	    qName = prefix + ":" + name;
+	    nsDecl = " xmlns:" + prefix;
+	}
+	nsDecl += "=\"" + encodeAttr( namespaceURI ) + "\"";
+    }
+    s << "<" << qName << nsDecl;
+
+    if ( !m_attr->map.isEmpty() ) {
+	s << " ";
+	QDictIterator<QDomNodePrivate> it( m_attr->map );
+	for ( ; it.current(); ++it ) {
+	    it.current()->save( s, 0, indent );
+	    s << " ";
+	}
+    }
+
+    if ( last ) {
+	// has child nodes
+	if ( first->isText() )
+	    s << ">";
+	else
+	    s << ">" << endl;
+	QDomNodePrivate::save( s, depth + 1, indent);
+	if ( !last->isText() )
+	    for( int i = 0; i < depth*indent; ++i )
+		s << " ";
+
+	s << "</" << qName << ">";
+    } else {
+	s << "/>";
+    }
+    if ( !( next && next->isText() ) )
+	s << endl;
+}
+
+/**************************************************************
+ *
+ * QDomElement
+ *
+ **************************************************************/
+
+#define IMPL ((QDomElementPrivate*)impl)
+
+/*!
+    \class QDomElement qdom.h
+    \reentrant
+    \brief The QDomElement class represents one element in the DOM tree.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    Elements have a tagName() and zero or more attributes associated
+    with them. The tag name can be changed with setTagName().
+
+    Element attributes are represented by QDomAttr objects that can
+    be queried using the attribute() and attributeNode() functions.
+    You can set attributes with the setAttribute() and
+    setAttributeNode() functions. Attributes can be removed with
+    removeAttribute(). There are namespace-aware equivalents to these
+    functions, i.e. setAttributeNS(), setAttributeNodeNS() and
+    removeAttributeNS().
+
+    If you want to access the text of a node use text(), e.g.
+    \code
+    QDomElement e = //...
+    //...
+    QString s = e.text()
+    \endcode
+    The text() function operates recursively to find the text (since
+    not all elements contain text). If you want to find all the text
+    in all of a node's children, iterate over the children looking for
+    QDomText nodes, e.g.
+    \code
+    QString text;
+    QDomElement element = doc.documentElement();
+    for( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() )
+    {
+	QDomText t = n.toText();
+	if ( !t.isNull() )
+	    text += t.data();
+    }
+    \endcode
+    Note that we attempt to convert each node to a text node and use
+    text() rather than using firstChild().toText().data() or
+    n.toText().data() directly on the node, because the node may not
+    be a text element.
+
+    You can get a list of all the decendents of an element which have
+    a specified tag name with elementsByTagName() or
+    elementsByTagNameNS().
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+/*!
+    Constructs an empty element. Use the QDomDocument::createElement()
+    function to construct elements with content.
+*/
+QDomElement::QDomElement()
+    : QDomNode()
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomElement::QDomElement( const QDomElement& x )
+    : QDomNode( x )
+{
+}
+
+QDomElement::QDomElement( QDomElementPrivate* n )
+    : QDomNode( n )
+{
+}
+
+/*!
+    Assigns \a x to this DOM element.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomElement& QDomElement::operator= ( const QDomElement& x )
+{
+    return (QDomElement&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomElement::~QDomElement()
+{
+}
+
+/*!
+    Returns \c ElementNode.
+*/
+QDomNode::NodeType QDomElement::nodeType() const
+{
+    return ElementNode;
+}
+
+/*!
+    Sets this element's tag name to \a name.
+
+    \sa tagName()
+*/
+void QDomElement::setTagName( const QString& name )
+{
+    if ( impl )
+	impl->name = name;
+}
+
+/*!
+    Returns the tag name of this element. For an XML element like this:
+    \code
+    <img src="myimg.png">
+    \endcode
+    the tagname would return "img".
+
+    \sa setTagName()
+*/
+QString QDomElement::tagName() const
+{
+    if ( !impl )
+	return QString::null;
+    return impl->nodeName();
+}
+
+/*!
+    Returns the attribute called \a name. If the attribute does not
+    exist \a defValue is returned.
+
+    \sa setAttribute() attributeNode() setAttributeNode() attributeNS()
+*/
+QString QDomElement::attribute( const QString& name,  const QString& defValue ) const
+{
+    if ( !impl )
+	return defValue;
+    return IMPL->attribute( name, defValue );
+}
+
+/*!
+    Adds an attribute called \a name with value \a value. If an
+    attribute with the same name exists, its value is replaced by \a
+    value.
+
+    \sa attribute() setAttributeNode() setAttributeNS()
+*/
+void QDomElement::setAttribute( const QString& name, const QString& value )
+{
+    if ( !impl )
+	return;
+    IMPL->setAttribute( name, value );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttribute( const QString& name, int value )
+{
+    // ### 4.0: inline
+    setAttribute( name, long(value) );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttribute( const QString& name, uint value )
+{
+    // ### 4.0: inline
+    setAttribute( name, ulong(value) );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttribute( const QString& name, long value )
+{
+    if ( !impl )
+	return;
+    QString x;
+    x.setNum( value );
+    IMPL->setAttribute( name, x );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttribute( const QString& name, ulong value )
+{
+    if ( !impl )
+	return;
+    QString x;
+    x.setNum( value );
+    IMPL->setAttribute( name, x );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttribute( const QString& name, double value )
+{
+    if ( !impl )
+	return;
+    QString x;
+    x.setNum( value );
+    IMPL->setAttribute( name, x );
+}
+
+/*!
+    Removes the attribute called name \a name from this element.
+
+    \sa setAttribute() attribute() removeAttributeNS()
+*/
+void QDomElement::removeAttribute( const QString& name )
+{
+    if ( !impl )
+	return;
+    IMPL->removeAttribute( name );
+}
+
+/*!
+    Returns the QDomAttr object that corresponds to the attribute
+    called \a name. If no such attribute exists a \link
+    QDomNode::isNull() null attribute\endlink is returned.
+
+    \sa setAttributeNode() attribute() setAttribute() attributeNodeNS()
+*/
+QDomAttr QDomElement::attributeNode( const QString& name)
+{
+    if ( !impl )
+	return QDomAttr();
+    return QDomAttr( IMPL->attributeNode( name ) );
+}
+
+/*!
+    Adds the attribute \a newAttr to this element.
+
+    If the element has another attribute that has the same name as \a
+    newAttr, this function replaces that attribute and returns it;
+    otherwise the function returns a \link QDomNode::isNull() null
+    attribute\endlink.
+
+    \sa attributeNode() setAttribute() setAttributeNodeNS()
+*/
+QDomAttr QDomElement::setAttributeNode( const QDomAttr& newAttr )
+{
+    if ( !impl )
+	return QDomAttr();
+    return QDomAttr( IMPL->setAttributeNode( ((QDomAttrPrivate*)newAttr.impl) ) );
+}
+
+/*!
+    Removes the attribute \a oldAttr from the element and returns it.
+
+    \sa attributeNode() setAttributeNode()
+*/
+QDomAttr QDomElement::removeAttributeNode( const QDomAttr& oldAttr )
+{
+    if ( !impl )
+	return QDomAttr(); // ### should this return oldAttr?
+    return QDomAttr( IMPL->removeAttributeNode( ((QDomAttrPrivate*)oldAttr.impl) ) );
+}
+
+/*!
+    Returns a QDomNodeList containing all descendent elements of this
+    element that are called \a tagname. The order they are in the node
+    list is the order they are encountered in a preorder traversal of
+    the element tree.
+
+    \sa elementsByTagNameNS() QDomDocument::elementsByTagName()
+*/
+QDomNodeList QDomElement::elementsByTagName( const QString& tagname ) const
+{
+    return QDomNodeList( new QDomNodeListPrivate( impl, tagname ) );
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomElement::isElement() const
+{
+    return TRUE;
+}
+
+/*!
+    Returns a QDomNamedNodeMap containing all this element's attributes.
+
+    \sa attribute() setAttribute() attributeNode() setAttributeNode()
+*/
+QDomNamedNodeMap QDomElement::attributes() const
+{
+    if ( !impl )
+	return QDomNamedNodeMap();
+    return QDomNamedNodeMap( IMPL->attributes() );
+}
+
+/*!
+    Returns TRUE if this element has an attribute called \a name;
+    otherwise returns FALSE.
+*/
+bool QDomElement::hasAttribute( const QString& name ) const
+{
+    if ( !impl )
+	return FALSE;
+    return IMPL->hasAttribute( name );
+}
+
+/*!
+    Returns the attribute with the local name \a localName and the
+    namespace URI \a nsURI. If the attribute does not exist \a
+    defValue is returned.
+
+    \sa setAttributeNS() attributeNodeNS() setAttributeNodeNS() attribute()
+*/
+QString QDomElement::attributeNS( const QString nsURI, const QString& localName, const QString& defValue ) const
+{
+    if ( !impl )
+	return defValue;
+    return IMPL->attributeNS( nsURI, localName, defValue );
+}
+
+/*!
+    Adds an attribute with the qualified name \a qName and the
+    namespace URI \a nsURI with the value \a value. If an attribute
+    with the same local name and namespace URI exists, its prefix is
+    replaced by the prefix of \a qName and its value is repaced by \a
+    value.
+
+    Although \a qName is the qualified name, the local name is used to
+    decide if an existing attribute's value should be replaced.
+
+    \sa attributeNS() setAttributeNodeNS() setAttribute()
+*/
+void QDomElement::setAttributeNS( const QString nsURI, const QString& qName, const QString& value )
+{
+    if ( !impl )
+	return;
+    IMPL->setAttributeNS( nsURI, qName, value );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttributeNS( const QString nsURI, const QString& qName, int value )
+{
+    // ### 4.0: inline
+    setAttributeNS( nsURI, qName, long(value) );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttributeNS( const QString nsURI, const QString& qName, uint value )
+{
+    // ### 4.0: inline
+    setAttributeNS( nsURI, qName, ulong(value) );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttributeNS( const QString nsURI, const QString& qName, long value )
+{
+    if ( !impl )
+	return;
+    QString x;
+    x.setNum( value );
+    IMPL->setAttributeNS( nsURI, qName, x );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttributeNS( const QString nsURI, const QString& qName, ulong value )
+{
+    if ( !impl )
+	return;
+    QString x;
+    x.setNum( value );
+    IMPL->setAttributeNS( nsURI, qName, x );
+}
+
+/*!
+    \overload
+*/
+void QDomElement::setAttributeNS( const QString nsURI, const QString& qName, double value )
+{
+    if ( !impl )
+	return;
+    QString x;
+    x.setNum( value );
+    IMPL->setAttributeNS( nsURI, qName, x );
+}
+
+/*!
+    Removes the attribute with the local name \a localName and the
+    namespace URI \a nsURI from this element.
+
+    \sa setAttributeNS() attributeNS() removeAttribute()
+*/
+void QDomElement::removeAttributeNS( const QString& nsURI, const QString& localName )
+{
+    if ( !impl )
+	return;
+    QDomNodePrivate *n = IMPL->attributeNodeNS( nsURI, localName );
+    if ( !n )
+	return;
+    IMPL->removeAttribute( n->nodeName() );
+}
+
+/*!
+    Returns the QDomAttr object that corresponds to the attribute with
+    the local name \a localName and the namespace URI \a nsURI. If no
+    such attribute exists a \link QDomNode::isNull() null
+    attribute\endlink is returned.
+
+    \sa setAttributeNode() attribute() setAttribute() attributeNodeNS()
+*/
+QDomAttr QDomElement::attributeNodeNS( const QString& nsURI, const QString& localName )
+{
+    if ( !impl )
+	return QDomAttr();
+    return QDomAttr( IMPL->attributeNodeNS( nsURI, localName ) );
+}
+
+/*!
+    Adds the attribute \a newAttr to this element.
+
+    If the element has another attribute that has the same local name
+    and namespace URI as \a newAttr, this function replaces that
+    attribute and returns it; otherwise the function returns a \link
+    QDomNode::isNull() null attribute\endlink.
+
+    \sa attributeNodeNS() setAttributeNS() setAttributeNode()
+*/
+QDomAttr QDomElement::setAttributeNodeNS( const QDomAttr& newAttr )
+{
+    if ( !impl )
+	return QDomAttr();
+    return QDomAttr( IMPL->setAttributeNodeNS( ((QDomAttrPrivate*)newAttr.impl) ) );
+}
+
+/*!
+    Returns a QDomNodeList containing all the descendent elements of
+    this element with the local name \a localName and the namespace
+    URI \a nsURI. The order they are in the node list is the order
+    they are encountered in a preorder traversal of the element tree.
+
+    \sa elementsByTagName() QDomDocument::elementsByTagNameNS()
+*/
+QDomNodeList QDomElement::elementsByTagNameNS( const QString& nsURI, const QString& localName ) const
+{
+    return QDomNodeList( new QDomNodeListPrivate( impl, nsURI, localName ) );
+}
+
+/*!
+    Returns TRUE if this element has an attribute with the local name
+    \a localName and the namespace URI \a nsURI; otherwise returns
+    FALSE.
+*/
+bool QDomElement::hasAttributeNS( const QString& nsURI, const QString& localName ) const
+{
+    if ( !impl )
+	return FALSE;
+    return IMPL->hasAttributeNS( nsURI, localName );
+}
+
+/*!
+    Returns the element's text or QString::null.
+
+    Example:
+    \code
+    <h1>Hello <b>Qt</b> <![CDATA[<xml is cool>]]></h1>
+    \endcode
+
+    The function text() of the QDomElement for the &lt;h1&gt; tag,
+    will return "Hello Qt &lt;xml is cool&gt;".
+
+    Comments are ignored by this function. It only evaluates QDomText
+    and QDomCDATASection objects.
+*/
+QString QDomElement::text() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->text();
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomTextPrivate
+ *
+ **************************************************************/
+
+QDomTextPrivate::QDomTextPrivate( QDomDocumentPrivate* d, QDomNodePrivate* parent, const QString& value )
+    : QDomCharacterDataPrivate( d, parent, value )
+{
+    name = "#text";
+}
+
+QDomTextPrivate::QDomTextPrivate( QDomTextPrivate* n, bool deep )
+    : QDomCharacterDataPrivate( n, deep )
+{
+}
+
+QDomTextPrivate::~QDomTextPrivate()
+{
+}
+
+QDomNodePrivate* QDomTextPrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomTextPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+QDomTextPrivate* QDomTextPrivate::splitText( int offset )
+{
+    if ( !parent() ) {
+	qWarning( "QDomText::splitText  The node has no parent. So I can not split" );
+	return 0;
+    }
+
+    QDomTextPrivate* t = new QDomTextPrivate( ownerDocument(), 0, value.mid( offset ) );
+    value.truncate( offset );
+
+    parent()->insertAfter( t, this );
+
+    return t;
+}
+
+void QDomTextPrivate::save( QTextStream& s, int, int ) const
+{
+    s << encodeAttr( value );
+}
+
+/**************************************************************
+ *
+ * QDomText
+ *
+ **************************************************************/
+
+#define IMPL ((QDomTextPrivate*)impl)
+
+/*!
+    \class QDomText qdom.h
+    \reentrant
+    \brief The QDomText class represents text data in the parsed XML document.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    You can split the text in a QDomText object over two QDomText
+    objecs with splitText().
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+/*!
+    Constructs an empty QDomText object.
+
+    To construct a QDomText with content, use QDomDocument::createTextNode().
+*/
+QDomText::QDomText()
+    : QDomCharacterData()
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomText::QDomText( const QDomText& x )
+    : QDomCharacterData( x )
+{
+}
+
+QDomText::QDomText( QDomTextPrivate* n )
+    : QDomCharacterData( n )
+{
+}
+
+/*!
+    Assigns \a x to this DOM text.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomText& QDomText::operator= ( const QDomText& x )
+{
+    return (QDomText&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomText::~QDomText()
+{
+}
+
+/*!
+    Returns \c TextNode.
+*/
+QDomNode::NodeType QDomText::nodeType() const
+{
+    return TextNode;
+}
+
+/*!
+    Splits this DOM text object into two QDomText objects. This object
+    keeps its first \a offset characters and the second (newly
+    created) object is inserted into the document tree after this
+    object with the remaining characters.
+
+    The function returns the newly created object.
+
+    \sa QDomNode::normalize()
+*/
+QDomText QDomText::splitText( int offset )
+{
+    if ( !impl )
+	return QDomText();
+    return QDomText( IMPL->splitText( offset ) );
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomText::isText() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomCommentPrivate
+ *
+ **************************************************************/
+
+QDomCommentPrivate::QDomCommentPrivate( QDomDocumentPrivate* d, QDomNodePrivate* parent, const QString& value )
+    : QDomCharacterDataPrivate( d, parent, value )
+{
+    name = "#comment";
+}
+
+QDomCommentPrivate::QDomCommentPrivate( QDomCommentPrivate* n, bool deep )
+    : QDomCharacterDataPrivate( n, deep )
+{
+}
+
+QDomCommentPrivate::~QDomCommentPrivate()
+{
+}
+
+QDomNodePrivate* QDomCommentPrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomCommentPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+void QDomCommentPrivate::save( QTextStream& s, int, int ) const
+{
+    s << "<!--" << value << "-->";
+}
+
+/**************************************************************
+ *
+ * QDomComment
+ *
+ **************************************************************/
+
+#define IMPL ((QDomCommentPrivate*)impl)
+
+/*!
+    \class QDomComment qdom.h
+    \reentrant
+    \brief The QDomComment class represents an XML comment.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    A comment in the parsed XML such as this:
+    \code
+    <!-- this is a comment -->
+    \endcode
+    is represented by QDomComment objects in the parsed Dom tree.
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+/*!
+    Constructs an empty comment. To construct a comment with content,
+    use the QDomDocument::createComment() function.
+*/
+QDomComment::QDomComment()
+    : QDomCharacterData()
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomComment::QDomComment( const QDomComment& x )
+    : QDomCharacterData( x )
+{
+}
+
+QDomComment::QDomComment( QDomCommentPrivate* n )
+    : QDomCharacterData( n )
+{
+}
+
+/*!
+    Assigns \a x to this DOM comment.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomComment& QDomComment::operator= ( const QDomComment& x )
+{
+    return (QDomComment&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomComment::~QDomComment()
+{
+}
+
+/*!
+    Returns \c CommentNode.
+*/
+QDomNode::NodeType QDomComment::nodeType() const
+{
+    return CommentNode;
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomComment::isComment() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomCDATASectionPrivate
+ *
+ **************************************************************/
+
+QDomCDATASectionPrivate::QDomCDATASectionPrivate( QDomDocumentPrivate* d, QDomNodePrivate* parent,
+						    const QString& value )
+    : QDomTextPrivate( d, parent, value )
+{
+    name = "#cdata-section";
+}
+
+QDomCDATASectionPrivate::QDomCDATASectionPrivate( QDomCDATASectionPrivate* n, bool deep )
+    : QDomTextPrivate( n, deep )
+{
+}
+
+QDomCDATASectionPrivate::~QDomCDATASectionPrivate()
+{
+}
+
+QDomNodePrivate* QDomCDATASectionPrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomCDATASectionPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+void QDomCDATASectionPrivate::save( QTextStream& s, int, int ) const
+{
+    // ### How do we escape "]]>" ?
+    // "]]>" is not allowed; so there should be none in value anyway
+    s << "<![CDATA[" << value << "]]>";
+}
+
+/**************************************************************
+ *
+ * QDomCDATASection
+ *
+ **************************************************************/
+
+#define IMPL ((QDomCDATASectionPrivate*)impl)
+
+/*!
+    \class QDomCDATASection qdom.h
+    \reentrant
+    \brief The QDomCDATASection class represents an XML CDATA section.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    CDATA sections are used to escape blocks of text containing
+    characters that would otherwise be regarded as markup. The only
+    delimiter that is recognized in a CDATA section is the "]]&gt;"
+    string that terminates the CDATA section. CDATA sections cannot be
+    nested. Their primary purpose is for including material such as
+    XML fragments, without needing to escape all the delimiters.
+
+    Adjacent QDomCDATASection nodes are not merged by the
+    QDomNode::normalize() function.
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+/*!
+    Constructs an empty CDATA section. To create a CDATA section with
+    content, use the QDomDocument::createCDATASection() function.
+*/
+QDomCDATASection::QDomCDATASection()
+    : QDomText()
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomCDATASection::QDomCDATASection( const QDomCDATASection& x )
+    : QDomText( x )
+{
+}
+
+QDomCDATASection::QDomCDATASection( QDomCDATASectionPrivate* n )
+    : QDomText( n )
+{
+}
+
+/*!
+    Assigns \a x to this CDATA section.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomCDATASection& QDomCDATASection::operator= ( const QDomCDATASection& x )
+{
+    return (QDomCDATASection&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomCDATASection::~QDomCDATASection()
+{
+}
+
+/*!
+    Returns \c CDATASection.
+*/
+QDomNode::NodeType QDomCDATASection::nodeType() const
+{
+    return CDATASectionNode;
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomCDATASection::isCDATASection() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomNotationPrivate
+ *
+ **************************************************************/
+
+QDomNotationPrivate::QDomNotationPrivate( QDomDocumentPrivate* d, QDomNodePrivate* parent,
+					    const QString& aname,
+					    const QString& pub, const QString& sys )
+    : QDomNodePrivate( d, parent )
+{
+    name = aname;
+    m_pub = pub;
+    m_sys = sys;
+}
+
+QDomNotationPrivate::QDomNotationPrivate( QDomNotationPrivate* n, bool deep )
+    : QDomNodePrivate( n, deep )
+{
+    m_sys = n->m_sys;
+    m_pub = n->m_pub;
+}
+
+QDomNotationPrivate::~QDomNotationPrivate()
+{
+}
+
+QDomNodePrivate* QDomNotationPrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomNotationPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+void QDomNotationPrivate::save( QTextStream& s, int, int ) const
+{
+    s << "<!NOTATION " << name << " ";
+    if ( !m_pub.isNull() )  {
+	s << "PUBLIC \"" << m_pub << "\"";
+	if ( !m_sys.isNull() )
+	    s << " \"" << m_sys << "\"";
+    }  else {
+	s << "SYSTEM \"" << m_sys << "\"";
+    }
+    s << ">" << endl;
+}
+
+/**************************************************************
+ *
+ * QDomNotation
+ *
+ **************************************************************/
+
+#define IMPL ((QDomNotationPrivate*)impl)
+
+/*!
+    \class QDomNotation qdom.h
+    \reentrant
+    \brief The QDomNotation class represents an XML notation.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    A notation either declares, by name, the format of an unparsed
+    entity (see section 4.7 of the XML 1.0 specification), or is used
+    for formal declaration of processing instruction targets (see
+    section 2.6 of the XML 1.0 specification).
+
+    DOM does not support editing notation nodes; they are therefore
+    read-only.
+
+    A notation node does not have any parent.
+
+    You can retrieve the publicId() and systemId() from a notation
+    node.
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+
+/*!
+    Constructor.
+*/
+QDomNotation::QDomNotation()
+    : QDomNode()
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomNotation::QDomNotation( const QDomNotation& x )
+    : QDomNode( x )
+{
+}
+
+QDomNotation::QDomNotation( QDomNotationPrivate* n )
+    : QDomNode( n )
+{
+}
+
+/*!
+    Assigns \a x to this DOM notation.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomNotation& QDomNotation::operator= ( const QDomNotation& x )
+{
+    return (QDomNotation&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomNotation::~QDomNotation()
+{
+}
+
+/*!
+    Returns \c NotationNode.
+*/
+QDomNode::NodeType QDomNotation::nodeType() const
+{
+    return NotationNode;
+}
+
+/*!
+    Returns the public identifier of this notation.
+*/
+QString QDomNotation::publicId() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->m_pub;
+}
+
+/*!
+    Returns the system identifier of this notation.
+*/
+QString QDomNotation::systemId() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->m_sys;
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomNotation::isNotation() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomEntityPrivate
+ *
+ **************************************************************/
+
+QDomEntityPrivate::QDomEntityPrivate( QDomDocumentPrivate* d, QDomNodePrivate* parent,
+					const QString& aname,
+					const QString& pub, const QString& sys, const QString& notation )
+    : QDomNodePrivate( d, parent )
+{
+    name = aname;
+    m_pub = pub;
+    m_sys = sys;
+    m_notationName = notation;
+}
+
+QDomEntityPrivate::QDomEntityPrivate( QDomEntityPrivate* n, bool deep )
+    : QDomNodePrivate( n, deep )
+{
+    m_sys = n->m_sys;
+    m_pub = n->m_pub;
+    m_notationName = n->m_notationName;
+}
+
+QDomEntityPrivate::~QDomEntityPrivate()
+{
+}
+
+QDomNodePrivate* QDomEntityPrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomEntityPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+/*
+  Encode an entity value upon saving.
+*/
+static QCString encodeEntity( const QCString& str )
+{
+    QCString tmp( str );
+    uint len = tmp.length();
+    uint i = 0;
+    const char* d = tmp.data();
+    while ( i < len ) {
+	if ( d[i] == '%' ){
+	    tmp.replace( i, 1, "&#60;" );
+	    d = tmp.data();
+	    len += 4;
+	    i += 5;
+	}
+	else if ( d[i] == '"' ) {
+	    tmp.replace( i, 1, "&#34;" );
+	    d = tmp.data();
+	    len += 4;
+	    i += 5;
+	} else if ( d[i] == '&' && i + 1 < len && d[i+1] == '#' ) {
+	    // Dont encode &lt; or &quot; or &custom;.
+	    // Only encode character references
+	    tmp.replace( i, 1, "&#38;" );
+	    d = tmp.data();
+	    len += 4;
+	    i += 5;
+	} else {
+	    ++i;
+	}
+    }
+
+    return tmp;
+}
+
+void QDomEntityPrivate::save( QTextStream& s, int, int ) const
+{
+    if ( m_sys.isNull() && m_pub.isNull() ) {
+	s << "<!ENTITY " << name << " \"" << encodeEntity( value.utf8() ) << "\">" << endl;
+    } else {
+	s << "<!ENTITY " << name << " ";
+	if ( m_pub.isNull() ) {
+	    s << "SYSTEM \"" << m_sys << "\"";
+	} else {
+	    s << "PUBLIC \"" << m_pub << "\" \"" << m_sys << "\"";
+	}
+	if (! m_notationName.isNull() ) {
+	    s << " NDATA " << m_notationName;
+	}
+	s << ">" << endl;
+    }
+}
+
+/**************************************************************
+ *
+ * QDomEntity
+ *
+ **************************************************************/
+
+#define IMPL ((QDomEntityPrivate*)impl)
+
+/*!
+    \class QDomEntity qdom.h
+    \reentrant
+    \brief The QDomEntity class represents an XML entity.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    This class represents an entity in an XML document, either parsed
+    or unparsed. Note that this models the entity itself not the
+    entity declaration.
+
+    DOM does not support editing entity nodes; if a user wants to make
+    changes to the contents of an entity, every related
+    QDomEntityReference node must be replaced in the DOM tree by a
+    clone of the entity's contents, and then the desired changes must
+    be made to each of the clones instead. All the descendents of an
+    entity node are read-only.
+
+    An entity node does not have any parent.
+
+    You can access the entity's publicId(), systemId() and
+    notationName() when available.
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+
+/*!
+    Constructs an empty entity.
+*/
+QDomEntity::QDomEntity()
+    : QDomNode()
+{
+}
+
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomEntity::QDomEntity( const QDomEntity& x )
+    : QDomNode( x )
+{
+}
+
+QDomEntity::QDomEntity( QDomEntityPrivate* n )
+    : QDomNode( n )
+{
+}
+
+/*!
+    Assigns \a x to this DOM entity.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomEntity& QDomEntity::operator= ( const QDomEntity& x )
+{
+    return (QDomEntity&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomEntity::~QDomEntity()
+{
+}
+
+/*!
+    Returns \c EntityNode.
+*/
+QDomNode::NodeType QDomEntity::nodeType() const
+{
+    return EntityNode;
+}
+
+/*!
+    Returns the public identifier associated with this entity. If the
+    public identifier was not specified QString::null is returned.
+*/
+QString QDomEntity::publicId() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->m_pub;
+}
+
+/*!
+    Returns the system identifier associated with this entity. If the
+    system identifier was not specified QString::null is returned.
+*/
+QString QDomEntity::systemId() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->m_sys;
+}
+
+/*!
+    For unparsed entities this function returns the name of the
+    notation for the entity. For parsed entities this function returns
+    QString::null.
+*/
+QString QDomEntity::notationName() const
+{
+    if ( !impl )
+	return QString::null;
+    return IMPL->m_notationName;
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomEntity::isEntity() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomEntityReferencePrivate
+ *
+ **************************************************************/
+
+QDomEntityReferencePrivate::QDomEntityReferencePrivate( QDomDocumentPrivate* d, QDomNodePrivate* parent, const QString& aname )
+    : QDomNodePrivate( d, parent )
+{
+    name = aname;
+}
+
+QDomEntityReferencePrivate::QDomEntityReferencePrivate( QDomNodePrivate* n, bool deep )
+    : QDomNodePrivate( n, deep )
+{
+}
+
+QDomEntityReferencePrivate::~QDomEntityReferencePrivate()
+{
+}
+
+QDomNodePrivate* QDomEntityReferencePrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomEntityReferencePrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+void QDomEntityReferencePrivate::save( QTextStream& s, int, int ) const
+{
+    s << "&" << name << ";";
+}
+
+/**************************************************************
+ *
+ * QDomEntityReference
+ *
+ **************************************************************/
+
+#define IMPL ((QDomEntityReferencePrivate*)impl)
+
+/*!
+    \class QDomEntityReference qdom.h
+    \reentrant
+    \brief The QDomEntityReference class represents an XML entity reference.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    A QDomEntityReference object may be inserted into the DOM tree
+    when an entity reference is in the source document, or when the
+    user wishes to insert an entity reference.
+
+    Note that character references and references to predefined
+    entities are expanded by the XML processor so that characters are
+    represented by their Unicode equivalent rather than by an entity
+    reference.
+
+    Moreover, the XML processor may completely expand references to
+    entities while building the DOM tree, instead of providing
+    QDomEntityReference objects.
+
+    If it does provide such objects, then for a given entity reference
+    node, it may be that there is no entity node representing the
+    referenced entity; but if such an entity exists, then the child
+    list of the entity reference node is the same as that of the
+    entity  node. As with the entity node, all descendents of the
+    entity reference are read-only.
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+/*!
+    Constructs an empty entity reference. Use
+    QDomDocument::createEntityReference() to create a entity reference
+    with content.
+*/
+QDomEntityReference::QDomEntityReference()
+    : QDomNode()
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomEntityReference::QDomEntityReference( const QDomEntityReference& x )
+    : QDomNode( x )
+{
+}
+
+QDomEntityReference::QDomEntityReference( QDomEntityReferencePrivate* n )
+    : QDomNode( n )
+{
+}
+
+/*!
+    Assigns \a x to this entity reference.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomEntityReference& QDomEntityReference::operator= ( const QDomEntityReference& x )
+{
+    return (QDomEntityReference&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomEntityReference::~QDomEntityReference()
+{
+}
+
+/*!
+    Returns \c EntityReference.
+*/
+QDomNode::NodeType QDomEntityReference::nodeType() const
+{
+    return EntityReferenceNode;
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomEntityReference::isEntityReference() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomProcessingInstructionPrivate
+ *
+ **************************************************************/
+
+QDomProcessingInstructionPrivate::QDomProcessingInstructionPrivate( QDomDocumentPrivate* d,
+	QDomNodePrivate* parent, const QString& target, const QString& data )
+    : QDomNodePrivate( d, parent )
+{
+    name = target;
+    value = data;
+}
+
+QDomProcessingInstructionPrivate::QDomProcessingInstructionPrivate( QDomProcessingInstructionPrivate* n, bool deep )
+    : QDomNodePrivate( n, deep )
+{
+}
+
+QDomProcessingInstructionPrivate::~QDomProcessingInstructionPrivate()
+{
+}
+
+QDomNodePrivate* QDomProcessingInstructionPrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomProcessingInstructionPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+void QDomProcessingInstructionPrivate::save( QTextStream& s, int, int ) const
+{
+    s << "<?" << name << " " << value << "?>" << endl;
+}
+
+/**************************************************************
+ *
+ * QDomProcessingInstruction
+ *
+ **************************************************************/
+
+#define IMPL ((QDomProcessingInstructionPrivate*)impl)
+
+/*!
+    \class QDomProcessingInstruction qdom.h
+    \reentrant
+    \brief The QDomProcessingInstruction class represents an XML processing
+    instruction.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    Processing instructions are used in XML to keep processor-specific
+    information in the text of the document.
+
+    The content of the processing instruction is retrieved with data()
+    and set with setData(). The processing instruction's target is
+    retrieved with target().
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+/*!
+    Constructs an empty processing instruction. Use
+    QDomDocument::createProcessingInstruction() to create a processing
+    instruction with content.
+*/
+QDomProcessingInstruction::QDomProcessingInstruction()
+    : QDomNode()
+{
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomProcessingInstruction::QDomProcessingInstruction( const QDomProcessingInstruction& x )
+    : QDomNode( x )
+{
+}
+
+QDomProcessingInstruction::QDomProcessingInstruction( QDomProcessingInstructionPrivate* n )
+    : QDomNode( n )
+{
+}
+
+/*!
+    Assigns \a x to this processing instruction.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomProcessingInstruction& QDomProcessingInstruction::operator= ( const QDomProcessingInstruction& x )
+{
+    return (QDomProcessingInstruction&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomProcessingInstruction::~QDomProcessingInstruction()
+{
+}
+
+/*!
+    Returns \c ProcessingInstructionNode.
+*/
+QDomNode::NodeType QDomProcessingInstruction::nodeType() const
+{
+    return ProcessingInstructionNode;
+}
+
+/*!
+    Returns the target of this processing instruction.
+
+    \sa data()
+*/
+QString QDomProcessingInstruction::target() const
+{
+    if ( !impl )
+	return QString::null;
+    return impl->nodeName();
+}
+
+/*!
+    Returns the content of this processing instruction.
+
+    \sa setData() target()
+*/
+QString QDomProcessingInstruction::data() const
+{
+    if ( !impl )
+	return QString::null;
+    return impl->nodeValue();
+}
+
+/*!
+    Sets the data contained in the processing instruction to \a d.
+
+    \sa data()
+*/
+void QDomProcessingInstruction::setData( const QString& d )
+{
+    if ( !impl )
+	return;
+    impl->setNodeValue( d );
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomProcessingInstruction::isProcessingInstruction() const
+{
+    return TRUE;
+}
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * QDomDocumentPrivate
+ *
+ **************************************************************/
+
+QDomDocumentPrivate::QDomDocumentPrivate()
+    : QDomNodePrivate( 0 )
+{
+    impl = new QDomImplementationPrivate();
+    type = new QDomDocumentTypePrivate( this, this );
+
+    name = "#document";
+}
+
+QDomDocumentPrivate::QDomDocumentPrivate( const QString& aname )
+    : QDomNodePrivate( 0 )
+{
+    impl = new QDomImplementationPrivate();
+    type = new QDomDocumentTypePrivate( this, this );
+    type->name = aname;
+
+    name = "#document";
+}
+
+QDomDocumentPrivate::QDomDocumentPrivate( QDomDocumentTypePrivate* dt )
+    : QDomNodePrivate( 0 )
+{
+    impl = new QDomImplementationPrivate();
+    if ( dt != 0 ) {
+	type = dt;
+	type->ref();
+    } else {
+	type = new QDomDocumentTypePrivate( this, this );
+    }
+
+    name = "#document";
+}
+
+QDomDocumentPrivate::QDomDocumentPrivate( QDomDocumentPrivate* n, bool deep )
+    : QDomNodePrivate( n, deep )
+{
+    impl = n->impl->clone();
+    // Reference count is down to 0, so we set it to 1 here.
+    impl->ref();
+    type = (QDomDocumentTypePrivate*)n->type->cloneNode();
+    type->setParent( this );
+    // Reference count is down to 0, so we set it to 1 here.
+    type->ref();
+}
+
+QDomDocumentPrivate::~QDomDocumentPrivate()
+{
+    if ( impl->deref() ) delete impl;
+    if ( type->deref() ) delete type;
+}
+
+void QDomDocumentPrivate::clear()
+{
+    if ( impl->deref() ) delete impl;
+    if ( type->deref() ) delete type;
+    impl = 0;
+    type = 0;
+    QDomNodePrivate::clear();
+}
+
+bool QDomDocumentPrivate::setContent( QXmlInputSource *source, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn )
+{
+    QXmlSimpleReader reader;
+    if ( namespaceProcessing ) {
+	reader.setFeature( "http://xml.org/sax/features/namespaces", TRUE );
+	reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", FALSE );
+    } else {
+	reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE );
+	reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE );
+    }
+    reader.setFeature( "http://trolltech.com/xml/features/report-whitespace-only-CharData", FALSE );
+
+    return setContent( source, &reader, errorMsg, errorLine, errorColumn );
+}
+
+bool QDomDocumentPrivate::setContent( QXmlInputSource *source, QXmlReader *reader, QString *errorMsg, int *errorLine, int *errorColumn )
+{
+    clear();
+    impl = new QDomImplementationPrivate;
+    type = new QDomDocumentTypePrivate( this, this );
+
+    bool namespaceProcessing = reader->feature( "http://xml.org/sax/features/namespaces" )
+	&& !reader->feature( "http://xml.org/sax/features/namespace-prefixes" );
+
+    QDomHandler hnd( this, namespaceProcessing );
+    reader->setContentHandler( &hnd );
+    reader->setErrorHandler( &hnd );
+    reader->setLexicalHandler( &hnd );
+    reader->setDeclHandler( &hnd );
+    reader->setDTDHandler( &hnd );
+
+    if ( !reader->parse( source ) ) {
+	if ( errorMsg )
+	    *errorMsg = hnd.errorMsg;
+	if ( errorLine )
+	    *errorLine = hnd.errorLine;
+	if ( errorColumn )
+	    *errorColumn = hnd.errorColumn;
+	return FALSE;
+    }
+
+    return TRUE;
+}
+
+QDomNodePrivate* QDomDocumentPrivate::cloneNode( bool deep)
+{
+    QDomNodePrivate* p = new QDomDocumentPrivate( this, deep );
+    // We are not interested in this node
+    p->deref();
+    return p;
+}
+
+QDomElementPrivate* QDomDocumentPrivate::documentElement()
+{
+    QDomNodePrivate* p = first;
+    while ( p && !p->isElement() )
+	p = p->next;
+
+    return (QDomElementPrivate*)p;
+}
+
+QDomElementPrivate* QDomDocumentPrivate::createElement( const QString& tagName )
+{
+    QDomElementPrivate* e = new QDomElementPrivate( this, 0, tagName );
+    e->deref();
+    return e;
+}
+
+QDomElementPrivate* QDomDocumentPrivate::createElementNS( const QString& nsURI, const QString& qName )
+{
+    QDomElementPrivate* e = new QDomElementPrivate( this, 0, nsURI, qName );
+    e->deref();
+    return e;
+}
+
+QDomDocumentFragmentPrivate* QDomDocumentPrivate::createDocumentFragment()
+{
+    QDomDocumentFragmentPrivate* f = new QDomDocumentFragmentPrivate( this, (QDomNodePrivate*)0 );
+    f->deref();
+    return f;
+}
+
+QDomTextPrivate* QDomDocumentPrivate::createTextNode( const QString& data )
+{
+    QDomTextPrivate* t = new QDomTextPrivate( this, 0, data );
+    t->deref();
+    return t;
+}
+
+QDomCommentPrivate* QDomDocumentPrivate::createComment( const QString& data )
+{
+    QDomCommentPrivate* c = new QDomCommentPrivate( this, 0, data );
+    c->deref();
+    return c;
+}
+
+QDomCDATASectionPrivate* QDomDocumentPrivate::createCDATASection( const QString& data )
+{
+    QDomCDATASectionPrivate* c = new QDomCDATASectionPrivate( this, 0, data );
+    c->deref();
+    return c;
+}
+
+QDomProcessingInstructionPrivate* QDomDocumentPrivate::createProcessingInstruction( const QString& target, const QString& data )
+{
+    QDomProcessingInstructionPrivate* p = new QDomProcessingInstructionPrivate( this, 0, target, data );
+    p->deref();
+    return p;
+}
+
+QDomAttrPrivate* QDomDocumentPrivate::createAttribute( const QString& aname )
+{
+    QDomAttrPrivate* a = new QDomAttrPrivate( this, 0, aname );
+    a->deref();
+    return a;
+}
+
+QDomAttrPrivate* QDomDocumentPrivate::createAttributeNS( const QString& nsURI, const QString& qName )
+{
+    QDomAttrPrivate* a = new QDomAttrPrivate( this, 0, nsURI, qName );
+    a->deref();
+    return a;
+}
+
+QDomEntityReferencePrivate* QDomDocumentPrivate::createEntityReference( const QString& aname )
+{
+    QDomEntityReferencePrivate* e = new QDomEntityReferencePrivate( this, 0, aname );
+    e->deref();
+    return e;
+}
+
+QDomNodePrivate* QDomDocumentPrivate::importNode( const QDomNodePrivate* importedNode, bool deep )
+{
+    QDomNodePrivate *node = 0;
+    switch ( importedNode->nodeType() ) {
+	case QDomNode::AttributeNode:
+	    node = new QDomAttrPrivate( (QDomAttrPrivate*)importedNode, TRUE );
+	    break;
+	case QDomNode::DocumentFragmentNode:
+	    node = new QDomDocumentFragmentPrivate( (QDomDocumentFragmentPrivate*)importedNode, deep );
+	    break;
+	case QDomNode::ElementNode:
+	    node = new QDomElementPrivate( (QDomElementPrivate*)importedNode, deep );
+	    break;
+	case QDomNode::EntityNode:
+	    node = new QDomEntityPrivate( (QDomEntityPrivate*)importedNode, deep );
+	    break;
+	case QDomNode::EntityReferenceNode:
+	    node = new QDomEntityReferencePrivate( (QDomEntityReferencePrivate*)importedNode, FALSE );
+	    break;
+	case QDomNode::NotationNode:
+	    node = new QDomNotationPrivate( (QDomNotationPrivate*)importedNode, deep );
+	    break;
+	case QDomNode::ProcessingInstructionNode:
+	    node = new QDomProcessingInstructionPrivate( (QDomProcessingInstructionPrivate*)importedNode, deep );
+	    break;
+	case QDomNode::TextNode:
+	    node = new QDomTextPrivate( (QDomTextPrivate*)importedNode, deep );
+	    break;
+	case QDomNode::CDATASectionNode:
+	    node = new QDomCDATASectionPrivate( (QDomCDATASectionPrivate*)importedNode, deep );
+	    break;
+	case QDomNode::CommentNode:
+	    node = new QDomCommentPrivate( (QDomCommentPrivate*)importedNode, deep );
+	    break;
+	default:
+	    break;
+    }
+    if ( node ) {
+	node->setOwnerDocument( this );
+	// The QDomNode constructor increases the refcount, so deref() first to
+	// keep refcount balanced.
+	node->deref();
+    }
+    return node;
+}
+
+void QDomDocumentPrivate::save( QTextStream& s, int, int indent ) const
+{
+    bool doc = FALSE;
+
+    QDomNodePrivate* n = first;
+    if ( n && n->isProcessingInstruction() && n->nodeName()=="xml" ) {
+	// we have an XML declaration
+	QString data = n->nodeValue();
+	QRegExp encoding( QString::fromLatin1("encoding\\s*=\\s*((\"([^\"]*)\")|('([^']*)'))") );
+	encoding.search( data );
+	QString enc = encoding.cap(3);
+	if ( enc.isEmpty() ) {
+	    enc = encoding.cap(5);
+	}
+	if ( enc.isEmpty() ) {
+	    s.setEncoding( QTextStream::UnicodeUTF8 );
+	} else {
+	    s.setCodec( QTextCodec::codecForName( enc ) );
+	}
+    } else {
+	s.setEncoding( QTextStream::UnicodeUTF8 );
+    }
+    while ( n ) {
+	if ( !doc && !(n->isProcessingInstruction()&&n->nodeName()=="xml") ) {
+	    // save doctype after XML declaration
+	    type->save( s, 0, indent );
+	    doc = TRUE;
+	}
+	n->save( s, 0, indent );
+	n = n->next;
+    }
+}
+
+/**************************************************************
+ *
+ * QDomDocument
+ *
+ **************************************************************/
+
+#define IMPL ((QDomDocumentPrivate*)impl)
+
+/*!
+    \class QDomDocument qdom.h
+    \reentrant
+    \brief The QDomDocument class represents an XML document.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    The QDomDocument class represents the entire XML document.
+    Conceptually, it is the root of the document tree, and provides
+    the primary access to the document's data.
+
+    Since elements, text nodes, comments, processing instructions,
+    etc., cannot exist outside the context of a document, the document
+    class also contains the factory functions needed to create these
+    objects. The node objects created have an ownerDocument() function
+    which associates them with the document within whose context they
+    were created. The DOM classes that will be used most often are
+    QDomNode, QDomDocument, QDomElement and QDomText.
+
+    The parsed XML is represented internally by a tree of objects that
+    can be accessed using the various QDom classes. All QDom classes
+    only \e reference objects in the internal tree. The internal
+    objects in the DOM tree will get deleted once the last QDom
+    object referencing them and the QDomDocument itself are deleted.
+
+    Creation of elements, text nodes, etc. is done using the various
+    factory functions provided in this class. Using the default
+    constructors of the QDom classes will only result in empty
+    objects that cannot be manipulated or inserted into the Document.
+
+    The QDomDocument class has several functions for creating document
+    data, for example, createElement(), createTextNode(),
+    createComment(), createCDATASection(),
+    createProcessingInstruction(), createAttribute() and
+    createEntityReference(). Some of these functions have versions
+    that support namespaces, i.e. createElementNS() and
+    createAttributeNS(). The createDocumentFragment() function is used
+    to hold parts of the document; this is useful for manipulating for
+    complex documents.
+
+    The entire content of the document is set with setContent(). This
+    function parses the string it is passed as an XML document and
+    creates the DOM tree that represents the document. The root
+    element is available using documentElement(). The textual
+    representation of the document can be obtained using toString().
+
+    It is possible to insert a node from another document into the
+    document using importNode().
+
+    You can obtain a list of all the elements that have a particular
+    tag with elementsByTagName() or with elementsByTagNameNS().
+
+    The QDom classes are typically used as follows:
+    \code
+    QDomDocument doc( "mydocument" );
+    QFile file( "mydocument.xml" );
+    if ( !file.open( IO_ReadOnly ) )
+	return;
+    if ( !doc.setContent( &file ) ) {
+	file.close();
+	return;
+    }
+    file.close();
+
+    // print out the element names of all elements that are direct children
+    // of the outermost element.
+    QDomElement docElem = doc.documentElement();
+
+    QDomNode n = docElem.firstChild();
+    while( !n.isNull() ) {
+	QDomElement e = n.toElement(); // try to convert the node to an element.
+	if( !e.isNull() ) {
+	    cout << e.tagName() << endl; // the node really is an element.
+	}
+	n = n.nextSibling();
+    }
+
+    // Here we append a new element to the end of the document
+    QDomElement elem = doc.createElement( "img" );
+    elem.setAttribute( "src", "myimage.png" );
+    docElem.appendChild( elem );
+    \endcode
+
+    Once \c doc and \c elem go out of scope, the whole internal tree
+    representing the XML document is deleted.
+
+    To create a document using DOM use code like this:
+    \code
+    QDomDocument doc( "MyML" );
+    QDomElement root = doc.createElement( "MyML" );
+    doc.appendChild( root );
+
+    QDomElement tag = doc.createElement( "Greeting" );
+    root.appendChild( tag );
+
+    QDomText t = doc.createTextNode( "Hello World" );
+    tag.appendChild( t );
+
+    QString xml = doc.toString();
+    \endcode
+
+    For further information about the Document Object Model see
+    \link http://www.w3.org/TR/REC-DOM-Level-1/\endlink and
+    \link http://www.w3.org/TR/DOM-Level-2-Core/\endlink.
+    For a more general introduction of the DOM implementation see the
+    QDomDocument documentation.
+*/
+
+
+/*!
+    Constructs an empty document.
+*/
+QDomDocument::QDomDocument()
+{
+    impl = new QDomDocumentPrivate;
+}
+
+/*!
+    Creates a document and sets the name of the document type to \a
+    name.
+*/
+QDomDocument::QDomDocument( const QString& name )
+{
+    // We take over ownership
+    impl = new QDomDocumentPrivate( name );
+}
+
+/*!
+    Creates a document with the document type \a doctype.
+
+    \sa QDomImplementation::createDocumentType()
+*/
+QDomDocument::QDomDocument( const QDomDocumentType& doctype )
+{
+    impl = new QDomDocumentPrivate( (QDomDocumentTypePrivate*)(doctype.impl) );
+}
+
+/*!
+    Constructs a copy of \a x.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomDocument::QDomDocument( const QDomDocument& x )
+    : QDomNode( x )
+{
+}
+
+QDomDocument::QDomDocument( QDomDocumentPrivate* x )
+    : QDomNode( x )
+{
+}
+
+/*!
+    Assigns \a x to this DOM document.
+
+    The data of the copy is shared (shallow copy): modifying one node
+    will also change the other. If you want to make a deep copy, use
+    cloneNode().
+*/
+QDomDocument& QDomDocument::operator= ( const QDomDocument& x )
+{
+    return (QDomDocument&) QDomNode::operator=( x );
+}
+
+/*!
+    Destroys the object and frees its resources.
+*/
+QDomDocument::~QDomDocument()
+{
+}
+
+/*!
+    \overload
+
+    This function reads the XML document from the string \a text.
+    Since \a text is already a Unicode string, no encoding detection
+    is done.
+*/
+bool QDomDocument::setContent( const QString& text, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn )
+{
+    if ( !impl )
+	impl = new QDomDocumentPrivate;
+    QXmlInputSource source;
+    source.setData( text );
+    return IMPL->setContent( &source, namespaceProcessing, errorMsg, errorLine, errorColumn );
+}
+
+/*!
+    This function parses the XML document from the byte array \a
+    buffer and sets it as the content of the document. It tries to
+    detect the encoding of the document as required by the XML
+    specification.
+
+    If \a namespaceProcessing is TRUE, the parser recognizes
+    namespaces in the XML file and sets the prefix name, local name
+    and namespace URI to appropriate values. If \a namespaceProcessing
+    is FALSE, the parser does no namespace processing when it reads
+    the XML file.
+
+    If a parse error occurs, the function returns FALSE; otherwise it
+    returns TRUE. If a parse error occurs and \a errorMsg, \a
+    errorLine and \a errorColumn are not 0, the error message is
+    placed in \a *errorMsg, the line number \a *errorLine and the
+    column number in \a *errorColumn.
+
+    If \a namespaceProcessing is TRUE, the function QDomNode::prefix()
+    returns a string for all elements and attributes. It returns an
+    empty string if the element or attribute has no prefix.
+
+    If \a namespaceProcessing is FALSE, the functions
+    QDomNode::prefix(), QDomNode::localName() and
+    QDomNode::namespaceURI() return QString::null.
+
+    \sa QDomNode::namespaceURI() QDomNode::localName()
+    QDomNode::prefix() QString::isNull() QString::isEmpty()
+*/
+bool QDomDocument::setContent( const QByteArray& buffer, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn )
+{
+    if ( !impl )
+	impl = new QDomDocumentPrivate;
+    QBuffer buf( buffer );
+    QXmlInputSource source( &buf );
+    return IMPL->setContent( &source, namespaceProcessing, errorMsg, errorLine, errorColumn );
+}
+
+/*!
+    \overload
+
+    This function reads the XML document from the C string \a buffer.
+
+    \warning This function does not try to detect the encoding:
+    instead it assumes that the C string is UTF-8 encoded.
+*/
+bool QDomDocument::setContent( const QCString& buffer, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn )
+{
+    return setContent( QString::fromUtf8( buffer, buffer.length() ), namespaceProcessing, errorMsg, errorLine, errorColumn );
+}
+
+/*!
+    \overload
+
+    This function reads the XML document from the IO device \a dev.
+*/
+bool QDomDocument::setContent( QIODevice* dev, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn )
+{
+    if ( !impl )
+	impl = new QDomDocumentPrivate;
+    QXmlInputSource source( dev );
+    return IMPL->setContent( &source, namespaceProcessing, errorMsg, errorLine, errorColumn );
+}
+
+/*!
+    \overload
+
+    This function reads the XML document from the string \a text.
+    Since \a text is already a Unicode string, no encoding detection
+    is performed.
+
+    No namespace processing is performed either.
+*/
+bool QDomDocument::setContent( const QString& text, QString *errorMsg, int *errorLine, int *errorColumn )
+{
+    return setContent( text, FALSE, errorMsg, errorLine, errorColumn );
+}
+
+/*!
+    \overload
+
+    This function reads the XML document from the byte array \a
+    buffer.
+
+    No namespace processing is performed.
+*/
+bool QDomDocument::setContent( const QByteArray& buffer, QString *errorMsg, int *errorLine, int *errorColumn  )
+{
+    return setContent( buffer, FALSE, errorMsg, errorLine, errorColumn );
+}
+
+/*!
+    \overload
+
+    This function reads the XML document from the C string \a buffer.
+
+    No namespace processing is performed.
+
+    \warning This function does not try to detect the encoding:
+    instead it assumes that the C string is UTF-8 encoded.
+*/
+bool QDomDocument::setContent( const QCString& buffer, QString *errorMsg, int *errorLine, int *errorColumn  )
+{
+    return setContent( buffer, FALSE, errorMsg, errorLine, errorColumn );
+}
+
+/*!
+    \overload
+
+    This function reads the XML document from the IO device \a dev.
+
+    No namespace processing is performed.
+*/
+bool QDomDocument::setContent( QIODevice* dev, QString *errorMsg, int *errorLine, int *errorColumn  )
+{
+    return setContent( dev, FALSE, errorMsg, errorLine, errorColumn );
+}
+
+/*!
+    \overload
+
+    This function reads the XML document from the QXmlInputSource \a source and
+    parses it with the QXmlReader \a reader.
+
+    This function doesn't change the features of the \a reader. If you want to
+    use certain features for parsing you can use this function to set up the
+    reader appropriate.
+
+    \sa QXmlSimpleReader
+*/
+bool QDomDocument::setContent( QXmlInputSource *source, QXmlReader *reader, QString *errorMsg, int *errorLine, int *errorColumn  )
+{
+    if ( !impl )
+	impl = new QDomDocumentPrivate;
+    return IMPL->setContent( source, reader, errorMsg, errorLine, errorColumn );
+}
+
+/*!
+    Converts the parsed document back to its textual representation.
+
+    \sa toCString()
+*/
+QString QDomDocument::toString() const
+{
+    QString str;
+    QTextStream s( str, IO_WriteOnly );
+    save( s, 1 );
+
+    return str;
+}
+
+/*!
+    \overload
+
+    This function uses \a indent as the amount of space to indent
+    subelements.
+*/
+QString QDomDocument::toString( int indent ) const
+{
+    QString str;
+    QTextStream s( str, IO_WriteOnly );
+    save( s, indent );
+
+    return str;
+}
+
+/*!
+    Converts the parsed document back to its textual representation
+    and returns a QCString for that is encoded in UTF-8.
+
+    \sa toString()
+*/
+QCString QDomDocument::toCString() const
+{
+    // ### if there is an encoding specified in the xml declaration, this
+    // encoding declaration should be changed to utf8
+    return toString().utf8();
+}
+
+/*!
+    \overload
+
+    This function uses \a indent as the amount of space to indent
+    subelements.
+*/
+QCString QDomDocument::toCString( int indent ) const
+{
+    // ### if there is an encoding specified in the xml declaration, this
+    // encoding declaration should be changed to utf8
+    return toString( indent ).utf8();
+}
+
+
+/*!
+    Returns the document type of this document.
+*/
+QDomDocumentType QDomDocument::doctype() const
+{
+    if ( !impl )
+	return QDomDocumentType();
+    return QDomDocumentType( IMPL->doctype() );
+}
+
+/*!
+    Returns a QDomImplementation object.
+*/
+QDomImplementation QDomDocument::implementation() const
+{
+    if ( !impl )
+	return QDomImplementation();
+    return QDomImplementation( IMPL->implementation() );
+}
+
+/*!
+    Returns the root element of the document.
+*/
+QDomElement QDomDocument::documentElement() const
+{
+    if ( !impl )
+	return QDomElement();
+    return QDomElement( IMPL->documentElement() );
+}
+
+/*!
+    Creates a new element called \a tagName that can be inserted into
+    the DOM tree, e.g. using QDomNode::appendChild().
+
+    \sa createElementNS() QDomNode::appendChild() QDomNode::insertBefore()
+    QDomNode::insertAfter()
+*/
+QDomElement QDomDocument::createElement( const QString& tagName )
+{
+    if ( !impl )
+	return QDomElement();
+    return QDomElement( IMPL->createElement( tagName ) );
+}
+
+/*!
+    Creates a new document fragment, that can be used to hold parts of
+    the document, e.g. when doing complex manipulations of the
+    document tree.
+*/
+QDomDocumentFragment QDomDocument::createDocumentFragment()
+{
+    if ( !impl )
+	return QDomDocumentFragment();
+    return QDomDocumentFragment( IMPL->createDocumentFragment() );
+}
+
+/*!
+    Creates a text node for the string \a value that can be inserted
+    into the document tree, e.g. using QDomNode::appendChild().
+
+    \sa QDomNode::appendChild() QDomNode::insertBefore() QDomNode::insertAfter()
+*/
+QDomText QDomDocument::createTextNode( const QString& value )
+{
+    if ( !impl )
+	return QDomText();
+    return QDomText( IMPL->createTextNode( value ) );
+}
+
+/*!
+    Creates a new comment for the string \a value that can be inserted
+    into the document, e.g. using QDomNode::appendChild().
+
+    \sa QDomNode::appendChild() QDomNode::insertBefore() QDomNode::insertAfter()
+*/
+QDomComment QDomDocument::createComment( const QString& value )
+{
+    if ( !impl )
+	return QDomComment();
+    return QDomComment( IMPL->createComment( value ) );
+}
+
+/*!
+    Creates a new CDATA section for the string \a value that can be
+    inserted into the document, e.g. using QDomNode::appendChild().
+
+    \sa QDomNode::appendChild() QDomNode::insertBefore() QDomNode::insertAfter()
+*/
+QDomCDATASection QDomDocument::createCDATASection( const QString& value )
+{
+    if ( !impl )
+	return QDomCDATASection();
+    return QDomCDATASection( IMPL->createCDATASection( value ) );
+}
+
+/*!
+    Creates a new processing instruction that can be inserted into the
+    document, e.g. using QDomNode::appendChild(). This function sets
+    the target for the processing instruction to \a target and the
+    data to \a data.
+
+    \sa QDomNode::appendChild() QDomNode::insertBefore() QDomNode::insertAfter()
+*/
+QDomProcessingInstruction QDomDocument::createProcessingInstruction( const QString& target,
+								     const QString& data )
+{
+    if ( !impl )
+	return QDomProcessingInstruction();
+    return QDomProcessingInstruction( IMPL->createProcessingInstruction( target, data ) );
+}
+
+
+/*!
+    Creates a new attribute called \a name that can be inserted into
+    an element, e.g. using QDomElement::setAttributeNode().
+
+    \sa createAttributeNS()
+*/
+QDomAttr QDomDocument::createAttribute( const QString& name )
+{
+    if ( !impl )
+	return QDomAttr();
+    return QDomAttr( IMPL->createAttribute( name ) );
+}
+
+/*!
+    Creates a new entity reference called \a name that can be inserted
+    into the document, e.g. using QDomNode::appendChild().
+
+    \sa QDomNode::appendChild() QDomNode::insertBefore() QDomNode::insertAfter()
+*/
+QDomEntityReference QDomDocument::createEntityReference( const QString& name )
+{
+    if ( !impl )
+	return QDomEntityReference();
+    return QDomEntityReference( IMPL->createEntityReference( name ) );
+}
+
+/*!
+    Returns a QDomNodeList, that contains all the elements in the
+    document with the name \a tagname. The order of the node list is
+    the order they are encountered in a preorder traversal of the
+    element tree.
+
+    \sa elementsByTagNameNS() QDomElement::elementsByTagName()
+*/
+QDomNodeList QDomDocument::elementsByTagName( const QString& tagname ) const
+{
+    return QDomNodeList( new QDomNodeListPrivate( impl, tagname ) );
+}
+
+/*!
+    Imports the node \a importedNode from another document to this
+    document. \a importedNode remains in the original document; this
+    function creates a copy that can be used within this document.
+
+    This function returns the imported node that belongs to this
+    document. The returned node has no parent. It is not possible to
+    import QDomDocument and QDomDocumentType nodes. In those cases
+    this function returns a \link QDomNode::isNull() null node\endlink.
+
+    If \a deep is TRUE, this function imports not only the node \a
+    importedNode but its whole subtree; if it is FALSE, only the \a
+    importedNode is imported. The argument \a deep has no effect on
+    QDomAttr and QDomEntityReference nodes, since the descendents of
+    QDomAttr nodes are always imported and those of
+    QDomEntityReference nodes are never imported.
+
+    The behavior of this function is slightly different depending on
+    the node types:
+    \table
+    \header \i Node Type \i Behaviour
+    \row \i QDomAttr
+	 \i The owner element is set to 0 and the specified flag is
+	    set to TRUE in the generated attribute. The whole subtree
+	    of \a importedNode is always imported for attribute nodes:
+	    \a deep has no effect.
+    \row \i QDomDocument
+	 \i Document nodes cannot be imported.
+    \row \i QDomDocumentFragment
+	 \i If \a deep is TRUE, this function imports the whole
+	    document fragment; otherwise it only generates an empty
+	    document fragment.
+    \row \i QDomDocumentType
+	 \i Document type nodes cannot be imported.
+    \row \i QDomElement
+	 \i Attributes for which QDomAttr::specified() is TRUE are
+	    also imported, other attributes are not imported. If \a
+	    deep is TRUE, this function also imports the subtree of \a
+	    importedNode; otherwise it imports only the element node
+	    (and some attributes, see above).
+    \row \i QDomEntity
+	 \i Entity nodes can be imported, but at the moment there is
+	    no way to use them since the document type is read-only in
+	    DOM level 2.
+    \row \i QDomEntityReference
+	 \i Descendents of entity reference nodes are never imported:
+	    \a deep has no effect.
+    \row \i QDomNotation
+	 \i Notation nodes can be imported, but at the moment there is
+	    no way to use them since the document type is read-only in
+	    DOM level 2.
+    \row \i QDomProcessingInstruction
+	 \i The target and value of the processing instruction is
+	    copied to the new node.
+    \row \i QDomText
+	 \i The text is copied to the new node.
+    \row \i QDomCDATASection
+	 \i The text is copied to the new node.
+    \row \i QDomComment
+	 \i The text is copied to the new node.
+    \endtable
+
+    \sa QDomElement::setAttribute() QDomNode::insertBefore()
+	QDomNode::insertAfter() QDomNode::replaceChild() QDomNode::removeChild()
+	QDomNode::appendChild()
+*/
+QDomNode QDomDocument::importNode( const QDomNode& importedNode, bool deep )
+{
+    if ( !impl )
+	return QDomNode();
+    return QDomNode( IMPL->importNode( importedNode.impl, deep ) );
+}
+
+/*!
+    Creates a new element with namespace support that can be inserted
+    into the DOM tree. The name of the element is \a qName and the
+    namespace URI is \a nsURI. This function also sets
+    QDomNode::prefix() and QDomNode::localName() to appropriate values
+    (depending on \a qName).
+
+    \sa createElement()
+*/
+QDomElement QDomDocument::createElementNS( const QString& nsURI, const QString& qName )
+{
+    if ( !impl )
+	return QDomElement();
+    return QDomElement( IMPL->createElementNS( nsURI, qName ) );
+}
+
+/*!
+    Creates a new attribute with namespace support that can be
+    inserted into an element. The name of the attribute is \a qName
+    and the namespace URI is \a nsURI. This function also sets
+    QDomNode::prefix() and QDomNode::localName() to appropriate values
+    (depending on \a qName).
+
+    \sa createAttribute()
+*/
+QDomAttr QDomDocument::createAttributeNS( const QString& nsURI, const QString& qName )
+{
+    if ( !impl )
+	return QDomAttr();
+    return QDomAttr( IMPL->createAttributeNS( nsURI, qName ) );
+}
+
+/*!
+    Returns a QDomNodeList that contains all the elements in the
+    document with the local name \a localName and a namespace URI of
+    \a nsURI. The order of the node list is the order they are
+    encountered in a preorder traversal of the element tree.
+
+    \sa elementsByTagName() QDomElement::elementsByTagNameNS()
+*/
+QDomNodeList QDomDocument::elementsByTagNameNS( const QString& nsURI, const QString& localName )
+{
+    return QDomNodeList( new QDomNodeListPrivate( impl, nsURI, localName ) );
+}
+
+/*!
+    Returns the element whose ID is equal to \a elementId. If no
+    element with the ID was found, this function returns a \link
+    QDomNode::isNull() null element\endlink.
+
+    Since the QDomClasses do not know which attributes are element
+    IDs, this function returns always a \link QDomNode::isNull() null
+    element\endlink. This may change in a future version.
+*/
+QDomElement QDomDocument::elementById( const QString& /*elementId*/ )
+{
+    return QDomElement();
+}
+
+/*!
+    Returns \c DocumentNode.
+*/
+QDomNode::NodeType QDomDocument::nodeType() const
+{
+    return DocumentNode;
+}
+
+/*!
+    Returns TRUE.
+*/
+bool QDomDocument::isDocument() const
+{
+    return TRUE;
+}
+
+
+#undef IMPL
+
+/**************************************************************
+ *
+ * Node casting functions
+ *
+ **************************************************************/
+
+/*!
+    Converts a QDomNode into a QDomAttr. If the node is not an
+    attribute, the returned object will be \link QDomNode::isNull()
+    null\endlink.
+
+    \sa isAttr()
+*/
+QDomAttr QDomNode::toAttr()
+{
+    if ( impl && impl->isAttr() )
+	return QDomAttr( ((QDomAttrPrivate*)impl) );
+    return QDomAttr();
+}
+
+/*!
+    Converts a QDomNode into a QDomCDATASection. If the node is not a
+    CDATA section, the returned object will be \link
+    QDomNode::isNull() null\endlink.
+
+    \sa isCDATASection()
+*/
+QDomCDATASection QDomNode::toCDATASection()
+{
+    if ( impl && impl->isCDATASection() )
+	return QDomCDATASection( ((QDomCDATASectionPrivate*)impl) );
+    return QDomCDATASection();
+}
+
+/*!
+    Converts a QDomNode into a QDomDocumentFragment. If the node is
+    not a document fragment the returned object will be \link
+    QDomNode::isNull() null\endlink.
+
+    \sa isDocumentFragment()
+*/
+QDomDocumentFragment QDomNode::toDocumentFragment()
+{
+    if ( impl && impl->isDocumentFragment() )
+	return QDomDocumentFragment( ((QDomDocumentFragmentPrivate*)impl) );
+    return QDomDocumentFragment();
+}
+
+/*!
+    Converts a QDomNode into a QDomDocument. If the node is not a
+    document the returned object will be \link QDomNode::isNull()
+    null\endlink.
+
+    \sa isDocument()
+*/
+QDomDocument QDomNode::toDocument()
+{
+    if ( impl && impl->isDocument() )
+	return QDomDocument( ((QDomDocumentPrivate*)impl) );
+    return QDomDocument();
+}
+
+/*!
+    Converts a QDomNode into a QDomDocumentType. If the node is not a
+    document type the returned object will be \link QDomNode::isNull()
+    null\endlink.
+
+    \sa isDocumentType()
+*/
+QDomDocumentType QDomNode::toDocumentType()
+{
+    if ( impl && impl->isDocumentType() )
+	return QDomDocumentType( ((QDomDocumentTypePrivate*)impl) );
+    return QDomDocumentType();
+}
+
+/*!
+    Converts a QDomNode into a QDomElement. If the node is not an
+    element the returned object will be \link QDomNode::isNull()
+    null\endlink.
+
+    \sa isElement()
+*/
+QDomElement QDomNode::toElement()
+{
+    if ( impl && impl->isElement() )
+	return QDomElement( ((QDomElementPrivate*)impl) );
+    return QDomElement();
+}
+
+/*!
+    Converts a QDomNode into a QDomEntityReference. If the node is not
+    an entity reference, the returned object will be \link
+    QDomNode::isNull() null\endlink.
+
+    \sa isEntityReference()
+*/
+QDomEntityReference QDomNode::toEntityReference()
+{
+    if ( impl && impl->isEntityReference() )
+	return QDomEntityReference( ((QDomEntityReferencePrivate*)impl) );
+    return QDomEntityReference();
+}
+
+/*!
+    Converts a QDomNode into a QDomText. If the node is not a text,
+    the returned object will be \link QDomNode::isNull() null\endlink.
+
+    \sa isText()
+*/
+QDomText QDomNode::toText()
+{
+    if ( impl && impl->isText() )
+	return QDomText( ((QDomTextPrivate*)impl) );
+    return QDomText();
+}
+
+/*!
+    Converts a QDomNode into a QDomEntity. If the node is not an
+    entity the returned object will be \link QDomNode::isNull()
+    null\endlink.
+
+    \sa isEntity()
+*/
+QDomEntity QDomNode::toEntity()
+{
+    if ( impl && impl->isEntity() )
+	return QDomEntity( ((QDomEntityPrivate*)impl) );
+    return QDomEntity();
+}
+
+/*!
+    Converts a QDomNode into a QDomNotation. If the node is not a
+    notation the returned object will be \link QDomNode::isNull()
+    null\endlink.
+
+    \sa isNotation()
+*/
+QDomNotation QDomNode::toNotation()
+{
+    if ( impl && impl->isNotation() )
+	return QDomNotation( ((QDomNotationPrivate*)impl) );
+    return QDomNotation();
+}
+
+/*!
+    Converts a QDomNode into a QDomProcessingInstruction. If the node
+    is not a processing instruction the returned object will be \link
+    QDomNode::isNull() null\endlink.
+
+    \sa isProcessingInstruction()
+*/
+QDomProcessingInstruction QDomNode::toProcessingInstruction()
+{
+    if ( impl && impl->isProcessingInstruction() )
+	return QDomProcessingInstruction( ((QDomProcessingInstructionPrivate*)impl) );
+    return QDomProcessingInstruction();
+}
+
+/*!
+    Converts a QDomNode into a QDomCharacterData. If the node is not a
+    character data node the returned object will be \link
+    QDomNode::isNull() null\endlink.
+
+    \sa isCharacterData()
+*/
+QDomCharacterData QDomNode::toCharacterData()
+{
+    if ( impl && impl->isCharacterData() )
+	return QDomCharacterData( ((QDomCharacterDataPrivate*)impl) );
+    return QDomCharacterData();
+}
+
+/*!
+    Converts a QDomNode into a QDomComment. If the node is not a
+    comment the returned object will be \link QDomNode::isNull()
+    null\endlink.
+
+    \sa isComment()
+*/
+QDomComment QDomNode::toComment()
+{
+    if ( impl && impl->isComment() )
+	return QDomComment( ((QDomCommentPrivate*)impl) );
+    return QDomComment();
+}
+
+/**************************************************************
+ *
+ * QDomHandler
+ *
+ **************************************************************/
+
+QDomHandler::QDomHandler( QDomDocumentPrivate* adoc, bool namespaceProcessing )
+{
+    doc = adoc;
+    node = doc;
+    cdata = FALSE;
+    nsProcessing = namespaceProcessing;
+}
+
+QDomHandler::~QDomHandler()
+{
+}
+
+bool QDomHandler::endDocument()
+{
+    // ### is this really necessary? (rms)
+    if ( node != doc )
+	return FALSE;
+    return TRUE;
+}
+
+bool QDomHandler::startDTD( const QString& name, const QString& publicId, const QString& systemId )
+{
+    doc->doctype()->name = name;
+    doc->doctype()->publicId = publicId;
+    doc->doctype()->systemId = systemId;
+    return TRUE;
+}
+
+bool QDomHandler::startElement( const QString& nsURI, const QString&, const QString& qName, const QXmlAttributes& atts )
+{
+    // tag name
+    QDomNodePrivate* n;
+    if ( nsProcessing ) {
+	n = doc->createElementNS( nsURI, qName );
+    } else {
+	n = doc->createElement( qName );
+    }
+    node->appendChild( n );
+    node = n;
+
+    // attributes
+    for ( int i=0; i<atts.length(); i++ )
+    {
+	if ( nsProcessing ) {
+	    ((QDomElementPrivate*)node)->setAttributeNS( atts.uri(i), atts.qName(i), atts.value(i) );
+	} else {
+	    ((QDomElementPrivate*)node)->setAttribute( atts.qName(i), atts.value(i) );
+	}
+    }
+
+    return TRUE;
+}
+
+bool QDomHandler::endElement( const QString&, const QString&, const QString& )
+{
+    if ( node == doc )
+	return FALSE;
+    node = node->parent();
+
+    return TRUE;
+}
+
+bool QDomHandler::characters( const QString&  ch )
+{
+    // No text as child of some document
+    if ( node == doc )
+	return FALSE;
+
+    if ( cdata ) {
+	node->appendChild( doc->createCDATASection( ch ) );
+    } else if ( !entityName.isEmpty() ) {
+	QDomEntityPrivate* e = new QDomEntityPrivate( doc, 0, entityName,
+		QString::null, QString::null, QString::null );
+	e->value = ch;
+	doc->doctype()->appendChild( e );
+	node->appendChild( doc->createEntityReference( entityName ) );
+    } else {
+	node->appendChild( doc->createTextNode( ch ) );
+    }
+
+    return TRUE;
+}
+
+bool QDomHandler::processingInstruction( const QString& target, const QString& data )
+{
+    node->appendChild( doc->createProcessingInstruction( target, data ) );
+    return TRUE;
+}
+
+bool QDomHandler::skippedEntity( const QString& name )
+{
+    node->appendChild( doc->createEntityReference( name ) );
+    return TRUE;
+}
+
+bool QDomHandler::fatalError( const QXmlParseException& exception )
+{
+    errorMsg = exception.message();
+    errorLine =  exception.lineNumber();
+    errorColumn =  exception.columnNumber();
+    return QXmlDefaultHandler::fatalError( exception );
+}
+
+bool QDomHandler::startCDATA()
+{
+    cdata = TRUE;
+    return TRUE;
+}
+
+bool QDomHandler::endCDATA()
+{
+    cdata = FALSE;
+    return TRUE;
+}
+
+bool QDomHandler::startEntity( const QString &name )
+{
+    entityName = name;
+    return TRUE;
+}
+
+bool QDomHandler::endEntity( const QString & )
+{
+    entityName = QString::null;
+    return TRUE;
+}
+
+bool QDomHandler::comment( const QString& ch )
+{
+    node->appendChild( doc->createComment( ch ) );
+    return TRUE;
+}
+
+bool QDomHandler::unparsedEntityDecl( const QString &name, const QString &publicId, const QString &systemId, const QString &notationName )
+{
+    QDomEntityPrivate* e = new QDomEntityPrivate( doc, 0, name,
+	    publicId, systemId, notationName );
+    doc->doctype()->appendChild( e );
+    return TRUE;
+}
+
+bool QDomHandler::externalEntityDecl( const QString &name, const QString &publicId, const QString &systemId )
+{
+    return unparsedEntityDecl( name, publicId, systemId, QString::null );
+}
+
+bool QDomHandler::notationDecl( const QString & name, const QString & publicId, const QString & systemId )
+{
+    QDomNotationPrivate* n = new QDomNotationPrivate( doc, 0, name, publicId, systemId );
+    doc->doctype()->appendChild( n );
+    return TRUE;
+}
+
+} // namespace PsiXml
+
+#endif //QT_NO_DOM
Index: psi/tags/0.10-os2-b1/src/tools/qxml/qdom.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/qxml/qdom.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/qxml/qdom.h	(revision 38)
@@ -0,0 +1,680 @@
+/****************************************************************************
+** $Id: qdom.h,v 1.1 2004/02/29 00:05:06 justin Exp $
+**
+** Definition of QDomDocument and related classes.
+**
+** Created : 000518
+**
+** Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+**
+** This file is part of the xml module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition licenses may use this
+** file in accordance with the Qt Commercial License Agreement provided
+** with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QDOM_H
+#define QDOM_H
+
+#ifndef QT_H
+#include "qstring.h"
+#endif // QT_H
+
+#if !defined(QT_MODULE_XML) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_XML )
+#define QM_EXPORT_DOM
+#else
+#define QM_EXPORT_DOM Q_EXPORT
+#endif
+
+#ifndef QT_NO_DOM
+
+class QIODevice;
+class QTextStream;
+
+namespace PsiXml {
+
+class QXmlInputSource;
+class QXmlReader;
+
+class QDomDocumentPrivate;
+class QDomDocumentTypePrivate;
+class QDomDocumentFragmentPrivate;
+class QDomNodePrivate;
+class QDomNodeListPrivate;
+class QDomImplementationPrivate;
+class QDomElementPrivate;
+class QDomNotationPrivate;
+class QDomEntityPrivate;
+class QDomEntityReferencePrivate;
+class QDomProcessingInstructionPrivate;
+class QDomAttrPrivate;
+class QDomCharacterDataPrivate;
+class QDomTextPrivate;
+class QDomCommentPrivate;
+class QDomCDATASectionPrivate;
+class QDomNamedNodeMapPrivate;
+class QDomImplementationPrivate;
+
+class QDomNodeList;
+class QDomElement;
+class QDomText;
+class QDomComment;
+class QDomCDATASection;
+class QDomProcessingInstruction;
+class QDomAttr;
+class QDomEntityReference;
+class QDomDocument;
+class QDomNamedNodeMap;
+class QDomDocument;
+class QDomDocumentFragment;
+class QDomDocumentType;
+class QDomImplementation;
+class QDomNode;
+class QDomEntity;
+class QDomNotation;
+class QDomCharacterData;
+
+class QM_EXPORT_DOM QDomImplementation
+{
+public:
+    QDomImplementation();
+    QDomImplementation( const QDomImplementation& );
+    virtual ~QDomImplementation();
+    QDomImplementation& operator= ( const QDomImplementation& );
+    bool operator== ( const QDomImplementation& ) const;
+    bool operator!= ( const QDomImplementation& ) const;
+
+    // functions
+    virtual bool hasFeature( const QString& feature, const QString& version );
+    virtual QDomDocumentType createDocumentType( const QString& qName, const QString& publicId, const QString& systemId );
+    virtual QDomDocument createDocument( const QString& nsURI, const QString& qName, const QDomDocumentType& doctype );
+
+    // Qt extension
+    bool isNull();
+
+private:
+    QDomImplementationPrivate* impl;
+    QDomImplementation( QDomImplementationPrivate* );
+
+    friend class QDomDocument;
+};
+
+class QM_EXPORT_DOM QDomNode
+{
+public:
+    enum NodeType {
+	ElementNode               = 1,
+	AttributeNode             = 2,
+	TextNode                  = 3,
+	CDATASectionNode          = 4,
+	EntityReferenceNode       = 5,
+	EntityNode                = 6,
+	ProcessingInstructionNode = 7,
+	CommentNode               = 8,
+	DocumentNode              = 9,
+	DocumentTypeNode          = 10,
+	DocumentFragmentNode      = 11,
+	NotationNode              = 12,
+	BaseNode                  = 21,// this is not in the standard
+	CharacterDataNode         = 22 // this is not in the standard
+    };
+
+    QDomNode();
+    QDomNode( const QDomNode& );
+    QDomNode& operator= ( const QDomNode& );
+    bool operator== ( const QDomNode& ) const;
+    bool operator!= ( const QDomNode& ) const;
+    virtual ~QDomNode();
+
+    // DOM functions
+    virtual QDomNode insertBefore( const QDomNode& newChild, const QDomNode& refChild );
+    virtual QDomNode insertAfter( const QDomNode& newChild, const QDomNode& refChild );
+    virtual QDomNode replaceChild( const QDomNode& newChild, const QDomNode& oldChild );
+    virtual QDomNode removeChild( const QDomNode& oldChild );
+    virtual QDomNode appendChild( const QDomNode& newChild );
+    virtual bool hasChildNodes() const;
+    virtual QDomNode cloneNode( bool deep = TRUE ) const;
+    virtual void normalize();
+    virtual bool isSupported( const QString& feature, const QString& version ) const;
+
+    // DOM read only attributes
+    virtual QString nodeName() const;
+    virtual QDomNode::NodeType nodeType() const;
+    virtual QDomNode         parentNode() const;
+    virtual QDomNodeList     childNodes() const;
+    virtual QDomNode         firstChild() const;
+    virtual QDomNode         lastChild() const;
+    virtual QDomNode         previousSibling() const;
+    virtual QDomNode         nextSibling() const;
+    virtual QDomNamedNodeMap attributes() const;
+    virtual QDomDocument     ownerDocument() const;
+    virtual QString namespaceURI() const;
+    virtual QString localName() const;
+    virtual bool hasAttributes() const;
+
+    // DOM attributes
+    virtual QString nodeValue() const;
+    virtual void setNodeValue( const QString& );
+    virtual QString prefix() const;
+    virtual void setPrefix( const QString& pre );
+
+    // Qt extensions
+    virtual bool isAttr() const;
+    virtual bool isCDATASection() const;
+    virtual bool isDocumentFragment() const;
+    virtual bool isDocument() const;
+    virtual bool isDocumentType() const;
+    virtual bool isElement() const;
+    virtual bool isEntityReference() const;
+    virtual bool isText() const;
+    virtual bool isEntity() const;
+    virtual bool isNotation() const;
+    virtual bool isProcessingInstruction() const;
+    virtual bool isCharacterData() const;
+    virtual bool isComment() const;
+
+    /**
+     * Shortcut to avoid dealing with QDomNodeList
+     * all the time.
+     */
+    QDomNode namedItem( const QString& name ) const;
+
+    bool isNull() const;
+    void clear();
+
+    QDomAttr toAttr();
+    QDomCDATASection toCDATASection();
+    QDomDocumentFragment toDocumentFragment();
+    QDomDocument toDocument();
+    QDomDocumentType toDocumentType();
+    QDomElement toElement();
+    QDomEntityReference toEntityReference();
+    QDomText toText();
+    QDomEntity toEntity();
+    QDomNotation toNotation();
+    QDomProcessingInstruction toProcessingInstruction();
+    QDomCharacterData toCharacterData();
+    QDomComment toComment();
+
+    void save( QTextStream&, int ) const;
+
+protected:
+    QDomNodePrivate* impl;
+    QDomNode( QDomNodePrivate* );
+
+private:
+    friend class QDomDocument;
+    friend class QDomDocumentType;
+    friend class QDomNodeList;
+    friend class QDomNamedNodeMap;
+};
+
+class QM_EXPORT_DOM QDomNodeList
+{
+public:
+    QDomNodeList();
+    QDomNodeList( const QDomNodeList& );
+    QDomNodeList& operator= ( const QDomNodeList& );
+    bool operator== ( const QDomNodeList& ) const;
+    bool operator!= ( const QDomNodeList& ) const;
+    virtual ~QDomNodeList();
+
+    // DOM functions
+    virtual QDomNode item( int index ) const;
+
+    // DOM read only attributes
+    virtual uint length() const;
+    uint count() const { return length(); } // Qt API consitancy
+
+private:
+    QDomNodeListPrivate* impl;
+    QDomNodeList( QDomNodeListPrivate* );
+
+    friend class QDomNode;
+    friend class QDomElement;
+    friend class QDomDocument;
+};
+
+class QM_EXPORT_DOM QDomDocumentType : public QDomNode
+{
+public:
+    QDomDocumentType();
+    QDomDocumentType( const QDomDocumentType& x );
+    QDomDocumentType& operator= ( const QDomDocumentType& );
+    ~QDomDocumentType();
+
+    // DOM read only attributes
+    virtual QString name() const;
+    virtual QDomNamedNodeMap entities() const;
+    virtual QDomNamedNodeMap notations() const;
+    virtual QString publicId() const;
+    virtual QString systemId() const;
+    virtual QString internalSubset() const;
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isDocumentType() const;
+
+private:
+    QDomDocumentType( QDomDocumentTypePrivate* );
+
+    friend class QDomImplementation;
+    friend class QDomDocument;
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomDocument : public QDomNode
+{
+public:
+    QDomDocument();
+    Q_EXPLICIT QDomDocument( const QString& name );
+    Q_EXPLICIT QDomDocument( const QDomDocumentType& doctype );
+    QDomDocument( const QDomDocument& x );
+    QDomDocument& operator= ( const QDomDocument& );
+    ~QDomDocument();
+
+    // DOM functions
+    QDomElement createElement( const QString& tagName );
+    QDomDocumentFragment createDocumentFragment();
+    QDomText createTextNode( const QString& data );
+    QDomComment createComment( const QString& data );
+    QDomCDATASection createCDATASection( const QString& data );
+    QDomProcessingInstruction createProcessingInstruction( const QString& target, const QString& data );
+    QDomAttr createAttribute( const QString& name );
+    QDomEntityReference createEntityReference( const QString& name );
+    QDomNodeList elementsByTagName( const QString& tagname ) const;
+    QDomNode importNode( const QDomNode& importedNode, bool deep );
+    QDomElement createElementNS( const QString& nsURI, const QString& qName );
+    QDomAttr createAttributeNS( const QString& nsURI, const QString& qName );
+    QDomNodeList elementsByTagNameNS( const QString& nsURI, const QString& localName );
+    QDomElement elementById( const QString& elementId );
+
+    // DOM read only attributes
+    QDomDocumentType doctype() const;
+    QDomImplementation implementation() const;
+    QDomElement documentElement() const;
+
+    // Qt extensions
+    bool setContent( const QCString& text, bool namespaceProcessing, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0  );
+    bool setContent( const QByteArray& text, bool namespaceProcessing, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0  );
+    bool setContent( const QString& text, bool namespaceProcessing, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0  );
+    bool setContent( QIODevice* dev, bool namespaceProcessing, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0  );
+    bool setContent( const QCString& text, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0 );
+    bool setContent( const QByteArray& text, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0  );
+    bool setContent( const QString& text, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0  );
+    bool setContent( QIODevice* dev, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0  );
+
+    bool setContent( QXmlInputSource *source, QXmlReader *reader, QString *errorMsg=0, int *errorLine=0, int *errorColumn=0  );
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isDocument() const;
+
+    // Qt extensions
+    QString toString() const; // ### Qt 4: merge the two overloads
+    QString toString( int ) const;
+    QCString toCString() const; // ### Qt 4: merge the two overloads
+    QCString toCString( int ) const;
+
+private:
+    QDomDocument( QDomDocumentPrivate* );
+
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomNamedNodeMap
+{
+public:
+    QDomNamedNodeMap();
+    QDomNamedNodeMap( const QDomNamedNodeMap& );
+    QDomNamedNodeMap& operator= ( const QDomNamedNodeMap& );
+    bool operator== ( const QDomNamedNodeMap& ) const;
+    bool operator!= ( const QDomNamedNodeMap& ) const;
+    ~QDomNamedNodeMap();
+
+    // DOM functions
+    QDomNode namedItem( const QString& name ) const;
+    QDomNode setNamedItem( const QDomNode& newNode );
+    QDomNode removeNamedItem( const QString& name );
+    QDomNode item( int index ) const;
+    QDomNode namedItemNS( const QString& nsURI, const QString& localName ) const;
+    QDomNode setNamedItemNS( const QDomNode& newNode );
+    QDomNode removeNamedItemNS( const QString& nsURI, const QString& localName );
+
+    // DOM read only attributes
+    uint length() const;
+    uint count() const { return length(); } // Qt API consitancy
+
+    // Qt extension
+    bool contains( const QString& name ) const;
+
+private:
+    QDomNamedNodeMapPrivate* impl;
+    QDomNamedNodeMap( QDomNamedNodeMapPrivate* );
+
+    friend class QDomNode;
+    friend class QDomDocumentType;
+    friend class QDomElement;
+};
+
+class QM_EXPORT_DOM QDomDocumentFragment : public QDomNode
+{
+public:
+    QDomDocumentFragment();
+    QDomDocumentFragment( const QDomDocumentFragment& x );
+    QDomDocumentFragment& operator= ( const QDomDocumentFragment& );
+    ~QDomDocumentFragment();
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isDocumentFragment() const;
+
+private:
+    QDomDocumentFragment( QDomDocumentFragmentPrivate* );
+
+    friend class QDomDocument;
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomCharacterData : public QDomNode
+{
+public:
+    QDomCharacterData();
+    QDomCharacterData( const QDomCharacterData& x );
+    QDomCharacterData& operator= ( const QDomCharacterData& );
+    ~QDomCharacterData();
+
+    // DOM functions
+    virtual QString substringData( unsigned long offset, unsigned long count );
+    virtual void appendData( const QString& arg );
+    virtual void insertData( unsigned long offset, const QString& arg );
+    virtual void deleteData( unsigned long offset, unsigned long count );
+    virtual void replaceData( unsigned long offset, unsigned long count, const QString& arg );
+
+    // DOM read only attributes
+    virtual uint length() const;
+
+    // DOM attributes
+    virtual QString data() const;
+    virtual void setData( const QString& );
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isCharacterData() const;
+
+private:
+    QDomCharacterData( QDomCharacterDataPrivate* );
+
+    friend class QDomDocument;
+    friend class QDomText;
+    friend class QDomComment;
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomAttr : public QDomNode
+{
+public:
+    QDomAttr();
+    QDomAttr( const QDomAttr& x );
+    QDomAttr& operator= ( const QDomAttr& );
+    ~QDomAttr();
+
+    // DOM read only attributes
+    virtual QString name() const;
+    virtual bool specified() const;
+    virtual QDomElement ownerElement() const;
+
+    // DOM attributes
+    virtual QString value() const;
+    virtual void setValue( const QString& );
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isAttr() const;
+
+private:
+    QDomAttr( QDomAttrPrivate* );
+
+    friend class QDomDocument;
+    friend class QDomElement;
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomElement : public QDomNode
+{
+public:
+    QDomElement();
+    QDomElement( const QDomElement& x );
+    QDomElement& operator= ( const QDomElement& );
+    ~QDomElement();
+
+    // DOM functions
+    QString attribute( const QString& name, const QString& defValue = QString::null ) const;
+    void setAttribute( const QString& name, const QString& value );
+    void setAttribute( const QString& name, int value );
+    void setAttribute( const QString& name, uint value );
+    void setAttribute( const QString& name, long value );
+    void setAttribute( const QString& name, ulong value );
+    void setAttribute( const QString& name, double value );
+    void removeAttribute( const QString& name );
+    QDomAttr attributeNode( const QString& name);
+    QDomAttr setAttributeNode( const QDomAttr& newAttr );
+    QDomAttr removeAttributeNode( const QDomAttr& oldAttr );
+    virtual QDomNodeList elementsByTagName( const QString& tagname ) const;
+    bool hasAttribute( const QString& name ) const;
+
+    QString attributeNS( const QString nsURI, const QString& localName, const QString& defValue ) const;
+    void setAttributeNS( const QString nsURI, const QString& qName, const QString& value );
+    void setAttributeNS( const QString nsURI, const QString& qName, int value );
+    void setAttributeNS( const QString nsURI, const QString& qName, uint value );
+    void setAttributeNS( const QString nsURI, const QString& qName, long value );
+    void setAttributeNS( const QString nsURI, const QString& qName, ulong value );
+    void setAttributeNS( const QString nsURI, const QString& qName, double value );
+    void removeAttributeNS( const QString& nsURI, const QString& localName );
+    QDomAttr attributeNodeNS( const QString& nsURI, const QString& localName );
+    QDomAttr setAttributeNodeNS( const QDomAttr& newAttr );
+    virtual QDomNodeList elementsByTagNameNS( const QString& nsURI, const QString& localName ) const;
+    bool hasAttributeNS( const QString& nsURI, const QString& localName ) const;
+
+    // DOM read only attributes
+    QString tagName() const;
+    void setTagName( const QString& name ); // Qt extension
+
+    // Reimplemented from QDomNode
+    QDomNamedNodeMap attributes() const;
+    QDomNode::NodeType nodeType() const;
+    bool isElement() const;
+
+    QString text() const;
+
+private:
+    QDomElement( QDomElementPrivate* );
+
+    friend class QDomDocument;
+    friend class QDomNode;
+    friend class QDomAttr;
+};
+
+class QM_EXPORT_DOM QDomText : public QDomCharacterData
+{
+public:
+    QDomText();
+    QDomText( const QDomText& x );
+    QDomText& operator= ( const QDomText& );
+    ~QDomText();
+
+    // DOM functions
+    QDomText splitText( int offset );
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isText() const;
+
+private:
+    QDomText( QDomTextPrivate* );
+
+    friend class QDomCDATASection;
+    friend class QDomDocument;
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomComment : public QDomCharacterData
+{
+public:
+    QDomComment();
+    QDomComment( const QDomComment& x );
+    QDomComment& operator= ( const QDomComment& );
+    ~QDomComment();
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isComment() const;
+
+private:
+    QDomComment( QDomCommentPrivate* );
+
+    friend class QDomDocument;
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomCDATASection : public QDomText
+{
+public:
+    QDomCDATASection();
+    QDomCDATASection( const QDomCDATASection& x );
+    QDomCDATASection& operator= ( const QDomCDATASection& );
+    ~QDomCDATASection();
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isCDATASection() const;
+
+private:
+    QDomCDATASection( QDomCDATASectionPrivate* );
+
+    friend class QDomDocument;
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomNotation : public QDomNode
+{
+public:
+    QDomNotation();
+    QDomNotation( const QDomNotation& x );
+    QDomNotation& operator= ( const QDomNotation& );
+    ~QDomNotation();
+
+    // DOM read only attributes
+    QString publicId() const;
+    QString systemId() const;
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isNotation() const;
+
+private:
+    QDomNotation( QDomNotationPrivate* );
+
+    friend class QDomDocument;
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomEntity : public QDomNode
+{
+public:
+    QDomEntity();
+    QDomEntity( const QDomEntity& x );
+    QDomEntity& operator= ( const QDomEntity& );
+    ~QDomEntity();
+
+    // DOM read only attributes
+    virtual QString publicId() const;
+    virtual QString systemId() const;
+    virtual QString notationName() const;
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isEntity() const;
+
+private:
+    QDomEntity( QDomEntityPrivate* );
+
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomEntityReference : public QDomNode
+{
+public:
+    QDomEntityReference();
+    QDomEntityReference( const QDomEntityReference& x );
+    QDomEntityReference& operator= ( const QDomEntityReference& );
+    ~QDomEntityReference();
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isEntityReference() const;
+
+private:
+    QDomEntityReference( QDomEntityReferencePrivate* );
+
+    friend class QDomDocument;
+    friend class QDomNode;
+};
+
+class QM_EXPORT_DOM QDomProcessingInstruction : public QDomNode
+{
+public:
+    QDomProcessingInstruction();
+    QDomProcessingInstruction( const QDomProcessingInstruction& x );
+    QDomProcessingInstruction& operator= ( const QDomProcessingInstruction& );
+    ~QDomProcessingInstruction();
+
+    // DOM read only attributes
+    virtual QString target() const;
+
+    // DOM attributes
+    virtual QString data() const;
+    virtual void setData( const QString& d );
+
+    // Reimplemented from QDomNode
+    QDomNode::NodeType nodeType() const;
+    bool isProcessingInstruction() const;
+
+private:
+    QDomProcessingInstruction( QDomProcessingInstructionPrivate* );
+
+    friend class QDomDocument;
+    friend class QDomNode;
+};
+
+
+QM_EXPORT_DOM QTextStream& operator<<( QTextStream&, const QDomNode& );
+
+} // namespace PsiXml
+
+using namespace PsiXml;
+
+#endif //QT_NO_DOM
+#endif // QDOM_H
Index: psi/tags/0.10-os2-b1/src/tools/qxml/qxml.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/qxml/qxml.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/qxml/qxml.cpp	(revision 38)
@@ -0,0 +1,7583 @@
+/****************************************************************************
+** $Id: qxml.cpp,v 1.1 2004/02/29 00:05:06 justin Exp $
+**
+** Implementation of QXmlSimpleReader and related classes.
+**
+** Created : 000518
+**
+** Copyright (C) 1992-2004 Trolltech AS.  All rights reserved.
+**
+** This file is part of the xml module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition licenses may use this
+** file in accordance with the Qt Commercial License Agreement provided
+** with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qxml.h"
+#include "qtextcodec.h"
+#include "qbuffer.h"
+#include "qregexp.h"
+#include "qptrstack.h"
+#include "qmap.h"
+#include "qvaluestack.h"
+
+// needed for QT_TRANSLATE_NOOP:
+#include "qobject.h"
+
+#ifndef QT_NO_XML
+
+namespace PsiXml {
+
+//#define QT_QXML_DEBUG
+
+// Error strings for the XML reader
+#define XMLERR_OK                         QT_TRANSLATE_NOOP( "QXml", "no error occurred" )
+#define XMLERR_ERRORBYCONSUMER            QT_TRANSLATE_NOOP( "QXml", "error triggered by consumer" )
+#define XMLERR_UNEXPECTEDEOF              QT_TRANSLATE_NOOP( "QXml", "unexpected end of file" )
+#define XMLERR_MORETHANONEDOCTYPE         QT_TRANSLATE_NOOP( "QXml", "more than one document type definition" )
+#define XMLERR_ERRORPARSINGELEMENT        QT_TRANSLATE_NOOP( "QXml", "error occurred while parsing element" )
+#define XMLERR_TAGMISMATCH                QT_TRANSLATE_NOOP( "QXml", "tag mismatch" )
+#define XMLERR_ERRORPARSINGCONTENT        QT_TRANSLATE_NOOP( "QXml", "error occurred while parsing content" )
+#define XMLERR_UNEXPECTEDCHARACTER        QT_TRANSLATE_NOOP( "QXml", "unexpected character" )
+#define XMLERR_INVALIDNAMEFORPI           QT_TRANSLATE_NOOP( "QXml", "invalid name for processing instruction" )
+#define XMLERR_VERSIONEXPECTED            QT_TRANSLATE_NOOP( "QXml", "version expected while reading the XML declaration" )
+#define XMLERR_WRONGVALUEFORSDECL         QT_TRANSLATE_NOOP( "QXml", "wrong value for standalone declaration" )
+#define XMLERR_EDECLORSDDECLEXPECTED      QT_TRANSLATE_NOOP( "QXml", "encoding declaration or standalone declaration expected while reading the XML declaration" )
+#define XMLERR_SDDECLEXPECTED             QT_TRANSLATE_NOOP( "QXml", "standalone declaration expected while reading the XML declaration" )
+#define XMLERR_ERRORPARSINGDOCTYPE        QT_TRANSLATE_NOOP( "QXml", "error occurred while parsing document type definition" )
+#define XMLERR_LETTEREXPECTED             QT_TRANSLATE_NOOP( "QXml", "letter is expected" )
+#define XMLERR_ERRORPARSINGCOMMENT        QT_TRANSLATE_NOOP( "QXml", "error occurred while parsing comment" )
+#define XMLERR_ERRORPARSINGREFERENCE      QT_TRANSLATE_NOOP( "QXml", "error occurred while parsing reference" )
+#define XMLERR_INTERNALGENERALENTITYINDTD QT_TRANSLATE_NOOP( "QXml", "internal general entity reference not allowed in DTD" )
+#define XMLERR_EXTERNALGENERALENTITYINAV  QT_TRANSLATE_NOOP( "QXml", "external parsed general entity reference not allowed in attribute value" )
+#define XMLERR_EXTERNALGENERALENTITYINDTD QT_TRANSLATE_NOOP( "QXml", "external parsed general entity reference not allowed in DTD" )
+#define XMLERR_UNPARSEDENTITYREFERENCE    QT_TRANSLATE_NOOP( "QXml", "unparsed entity reference in wrong context" )
+#define XMLERR_RECURSIVEENTITIES          QT_TRANSLATE_NOOP( "QXml", "recursive entities" )
+#define XMLERR_ERRORINTEXTDECL            QT_TRANSLATE_NOOP( "QXml", "error in the text declaration of an external entity" )
+
+// the constants for the lookup table
+static const signed char cltWS      =  0; // white space
+static const signed char cltPer     =  1; // %
+static const signed char cltAmp     =  2; // &
+static const signed char cltGt      =  3; // >
+static const signed char cltLt      =  4; // <
+static const signed char cltSlash   =  5; // /
+static const signed char cltQm      =  6; // ?
+static const signed char cltEm      =  7; // !
+static const signed char cltDash    =  8; // -
+static const signed char cltCB      =  9; // ]
+static const signed char cltOB      = 10; // [
+static const signed char cltEq      = 11; // =
+static const signed char cltDq      = 12; // "
+static const signed char cltSq      = 13; // '
+static const signed char cltUnknown = 14;
+
+// character lookup table
+static const signed char charLookupTable[256]={
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x00 - 0x07
+    cltUnknown, // 0x08
+    cltWS,      // 0x09 \t
+    cltWS,      // 0x0A \n
+    cltUnknown, // 0x0B
+    cltUnknown, // 0x0C
+    cltWS,      // 0x0D \r
+    cltUnknown, // 0x0E
+    cltUnknown, // 0x0F
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x17 - 0x16
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x18 - 0x1F
+    cltWS,      // 0x20 Space
+    cltEm,      // 0x21 !
+    cltDq,      // 0x22 "
+    cltUnknown, // 0x23
+    cltUnknown, // 0x24
+    cltPer,     // 0x25 %
+    cltAmp,     // 0x26 &
+    cltSq,      // 0x27 '
+    cltUnknown, // 0x28
+    cltUnknown, // 0x29
+    cltUnknown, // 0x2A
+    cltUnknown, // 0x2B
+    cltUnknown, // 0x2C
+    cltDash,    // 0x2D -
+    cltUnknown, // 0x2E
+    cltSlash,   // 0x2F /
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x30 - 0x37
+    cltUnknown, // 0x38
+    cltUnknown, // 0x39
+    cltUnknown, // 0x3A
+    cltUnknown, // 0x3B
+    cltLt,      // 0x3C <
+    cltEq,      // 0x3D =
+    cltGt,      // 0x3E >
+    cltQm,      // 0x3F ?
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x40 - 0x47
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x48 - 0x4F
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x50 - 0x57
+    cltUnknown, // 0x58
+    cltUnknown, // 0x59
+    cltUnknown, // 0x5A
+    cltOB,      // 0x5B [
+    cltUnknown, // 0x5C
+    cltCB,      // 0x5D ]
+    cltUnknown, // 0x5E
+    cltUnknown, // 0x5F
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x60 - 0x67
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x68 - 0x6F
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x70 - 0x77
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x78 - 0x7F
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x80 - 0x87
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x88 - 0x8F
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x90 - 0x97
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0x98 - 0x9F
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xA0 - 0xA7
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xA8 - 0xAF
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xB0 - 0xB7
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xB8 - 0xBF
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xC0 - 0xC7
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xC8 - 0xCF
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xD0 - 0xD7
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xD8 - 0xDF
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xE0 - 0xE7
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xE8 - 0xEF
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, // 0xF0 - 0xF7
+    cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown, cltUnknown  // 0xF8 - 0xFF
+};
+
+
+//
+// local helper functions
+//
+
+/*
+  This function strips the TextDecl [77] ("<?xml ...?>") from the string \a
+  str. The stripped version is stored in \a str. If this function finds an
+  invalid TextDecl, it returns FALSE, otherwise TRUE.
+
+  This function is used for external entities since those can include an
+  TextDecl that must be stripped before inserting the entity.
+*/
+static bool stripTextDecl( QString& str )
+{
+    QString textDeclStart( "<?xml" );
+    if ( str.startsWith( textDeclStart ) ) {
+	QRegExp textDecl(QString::fromLatin1(
+	    "^<\\?xml\\s+"
+	    "(version\\s*=\\s*((['\"])[-a-zA-Z0-9_.:]+\\3))?"
+	    "\\s*"
+	    "(encoding\\s*=\\s*((['\"])[A-Za-z][-a-zA-Z0-9_.]*\\6))?"
+	    "\\s*\\?>"
+	));
+	QString strTmp = str.replace( textDecl, "" );
+	if ( strTmp.length() != str.length() )
+	    return FALSE; // external entity has wrong TextDecl
+	str = strTmp;
+    }
+    return TRUE;
+}
+
+
+class QXmlAttributesPrivate
+{
+};
+class QXmlInputSourcePrivate
+{
+};
+class QXmlParseExceptionPrivate
+{
+};
+class QXmlLocatorPrivate
+{
+};
+class QXmlDefaultHandlerPrivate
+{
+};
+
+/*!
+    \class QXmlParseException qxml.h
+    \reentrant
+    \brief The QXmlParseException class is used to report errors with
+    the QXmlErrorHandler interface.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    The XML subsystem constructs an instance of this class when it
+    detects an error. You can retrieve the place where the error
+    occurred using systemId(), publicId(), lineNumber() and
+    columnNumber(), along with the error message().
+
+    \sa QXmlErrorHandler QXmlReader
+*/
+
+/*!
+    \fn QXmlParseException::QXmlParseException( const QString& name, int c, int l, const QString& p, const QString& s )
+
+    Constructs a parse exception with the error string \a name for
+    column \a c and line \a l for the public identifier \a p and the
+    system identifier \a s.
+*/
+
+/*!
+    Returns the error message.
+*/
+QString QXmlParseException::message() const
+{
+    return msg;
+}
+/*!
+    Returns the column number where the error occurred.
+*/
+int QXmlParseException::columnNumber() const
+{
+    return column;
+}
+/*!
+    Returns the line number where the error occurred.
+*/
+int QXmlParseException::lineNumber() const
+{
+    return line;
+}
+/*!
+    Returns the public identifier where the error occurred.
+*/
+QString QXmlParseException::publicId() const
+{
+    return pub;
+}
+/*!
+    Returns the system identifier where the error occurred.
+*/
+QString QXmlParseException::systemId() const
+{
+    return sys;
+}
+
+
+/*!
+    \class QXmlLocator qxml.h
+    \reentrant
+    \brief The QXmlLocator class provides the XML handler classes with
+    information about the parsing position within a file.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    The reader reports a QXmlLocator to the content handler before it
+    starts to parse the document. This is done with the
+    QXmlContentHandler::setDocumentLocator() function. The handler
+    classes can now use this locator to get the position (lineNumber()
+    and columnNumber()) that the reader has reached.
+*/
+
+/*!
+    Constructor.
+*/
+QXmlLocator::QXmlLocator()
+{
+}
+
+/*!
+    Destructor.
+*/
+QXmlLocator::~QXmlLocator()
+{
+}
+
+/*!
+    \fn int QXmlLocator::columnNumber()
+
+    Returns the column number (starting at 1) or -1 if there is no
+    column number available.
+*/
+
+/*!
+    \fn int QXmlLocator::lineNumber()
+
+    Returns the line number (starting at 1) or -1 if there is no line
+    number available.
+*/
+
+class QXmlSimpleReaderLocator : public QXmlLocator
+{
+public:
+    QXmlSimpleReaderLocator( QXmlSimpleReader* parent )
+    {
+	reader = parent;
+    }
+    ~QXmlSimpleReaderLocator()
+    {
+    }
+
+    int columnNumber()
+    {
+	return ( reader->columnNr == -1 ? -1 : reader->columnNr + 1 );
+    }
+    int lineNumber()
+    {
+	return ( reader->lineNr == -1 ? -1 : reader->lineNr + 1 );
+    }
+//    QString getPublicId()
+//    QString getSystemId()
+
+private:
+    QXmlSimpleReader *reader;
+};
+
+/*********************************************
+ *
+ * QXmlNamespaceSupport
+ *
+ *********************************************/
+
+class QXmlNamespaceSupportPrivate
+{
+public:
+    QXmlNamespaceSupportPrivate()
+    {
+	ns = new QMap<QString, QString>;
+	ns->insert( "xml", "http://www.w3.org/XML/1998/namespace" ); // the XML namespace
+    }
+
+    ~QXmlNamespaceSupportPrivate()
+    {
+	nsStack.setAutoDelete( TRUE );
+	nsStack.clear();
+	delete ns;
+    }
+
+    QPtrStack<QMap<QString, QString> > nsStack;
+    QMap<QString, QString> *ns;
+};
+
+/*!
+    \class QXmlNamespaceSupport qxml.h
+    \reentrant
+    \brief The QXmlNamespaceSupport class is a helper class for XML
+    readers which want to include namespace support.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    You can set the prefix for the current namespace with setPrefix(),
+    and get the list of current prefixes (or those for a given URI)
+    with prefixes(). The namespace URI is available from uri(). Use
+    pushContext() to start a new namespace context, and popContext()
+    to return to the previous namespace context. Use splitName() or
+    processName() to split a name into its prefix and local name.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+*/
+
+/*!
+    Constructs a QXmlNamespaceSupport.
+*/
+QXmlNamespaceSupport::QXmlNamespaceSupport()
+{
+    d = new QXmlNamespaceSupportPrivate;
+}
+
+/*!
+    Destroys a QXmlNamespaceSupport.
+*/
+QXmlNamespaceSupport::~QXmlNamespaceSupport()
+{
+    delete d;
+}
+
+/*!
+    This function declares a prefix \a pre in the current namespace
+    context to be the namespace URI \a uri. The prefix remains in
+    force until this context is popped, unless it is shadowed in a
+    descendant context.
+
+    Note that there is an asymmetry in this library. prefix() does not
+    return the default "" prefix, even if you have declared one; to
+    check for a default prefix, you must look it up explicitly using
+    uri(). This asymmetry exists to make it easier to look up prefixes
+    for attribute names, where the default prefix is not allowed.
+*/
+void QXmlNamespaceSupport::setPrefix( const QString& pre, const QString& uri )
+{
+    if( pre.isNull() ) {
+	d->ns->insert( "", uri );
+    } else {
+	d->ns->insert( pre, uri );
+    }
+}
+
+/*!
+    Returns one of the prefixes mapped to the namespace URI \a uri.
+
+    If more than one prefix is currently mapped to the same URI, this
+    function makes an arbitrary selection; if you want all of the
+    prefixes, use prefixes() instead.
+
+    Note: to check for a default prefix, use the uri() function with
+    an argument of "".
+*/
+QString QXmlNamespaceSupport::prefix( const QString& uri ) const
+{
+    QMap<QString, QString>::ConstIterator itc, it = d->ns->begin();
+    while ( (itc=it) != d->ns->end() ) {
+	++it;
+	if ( itc.data() == uri && !itc.key().isEmpty() )
+	    return itc.key();
+    }
+    return "";
+}
+
+/*!
+    Looks up the prefix \a prefix in the current context and returns
+    the currently-mapped namespace URI. Use the empty string ("") for
+    the default namespace.
+*/
+QString QXmlNamespaceSupport::uri( const QString& prefix ) const
+{
+    const QString& returi = (*d->ns)[ prefix ];
+    return returi;
+}
+
+/*!
+    Splits the name \a qname at the ':' and returns the prefix in \a
+    prefix and the local name in \a localname.
+
+    \sa processName()
+*/
+void QXmlNamespaceSupport::splitName( const QString& qname,
+	QString& prefix, QString& localname ) const
+{
+    uint pos;
+    // search the ':'
+    for( pos=0; pos<qname.length(); pos++ ) {
+	if ( qname.at(pos) == ':' )
+	    break;
+    }
+    // and split
+    prefix = qname.left( pos );
+    localname = qname.mid( pos+1 );
+}
+
+/*!
+    Processes a raw XML 1.0 name in the current context by removing
+    the prefix and looking it up among the prefixes currently
+    declared.
+
+    \a qname is the raw XML 1.0 name to be processed. \a isAttribute
+    is TRUE if the name is an attribute name.
+
+    This function stores the namespace URI in \a nsuri (which will be
+    set to QString::null if the raw name has an undeclared prefix),
+    and stores the local name (without prefix) in \a localname (which
+    will be set to QString::null if no namespace is in use).
+
+    Note that attribute names are processed differently than element
+    names: an unprefixed element name gets the default namespace (if
+    any), while an unprefixed element name does not.
+*/
+void QXmlNamespaceSupport::processName( const QString& qname,
+	bool isAttribute,
+	QString& nsuri, QString& localname ) const
+{
+    uint pos = qname.find( ':' );
+    if ( pos < qname.length() ) {
+	// there was a ':'
+	nsuri = uri( qname.left( pos ) );
+	localname = qname.mid( pos+1 );
+    } else {
+	// there was no ':'
+	if ( isAttribute ) {
+	    nsuri = QString::null; // attributes don't take default namespace
+	} else {
+	    nsuri = uri( "" ); // get default namespace
+	}
+	localname = qname;
+    }
+}
+
+/*!
+    Returns a list of all the prefixes currently declared.
+
+    If there is a default prefix, this function does not return it in
+    the list; check for the default prefix using uri() with an
+    argument of "".
+
+    Note that if you want to iterate over the list, you should iterate
+    over a copy, e.g.
+    \code
+    QStringList list = myXmlNamespaceSupport.prefixes();
+    QStringList::Iterator it = list.begin();
+    while( it != list.end() ) {
+	myProcessing( *it );
+	++it;
+    }
+    \endcode
+*/
+QStringList QXmlNamespaceSupport::prefixes() const
+{
+    QStringList list;
+
+    QMap<QString, QString>::ConstIterator itc, it = d->ns->begin();
+    while ( (itc=it) != d->ns->end() ) {
+	++it;
+	if ( !itc.key().isEmpty() )
+	    list.append( itc.key() );
+    }
+    return list;
+}
+
+/*!
+    \overload
+
+    Returns a list of all prefixes currently declared for the
+    namespace URI \a uri.
+
+    The "xml:" prefix is included. If you only want one prefix that is
+    mapped to the namespace URI, and you don't care which one you get,
+    use the prefix() function instead.
+
+    Note: the empty (default) prefix is never included in this list;
+    to check for the presence of a default namespace, use uri() with
+    an argument of "".
+
+    Note that if you want to iterate over the list, you should iterate
+    over a copy, e.g.
+    \code
+    QStringList list = myXmlNamespaceSupport.prefixes( "" );
+    QStringList::Iterator it = list.begin();
+    while( it != list.end() ) {
+	myProcessing( *it );
+	++it;
+    }
+    \endcode
+*/
+QStringList QXmlNamespaceSupport::prefixes( const QString& uri ) const
+{
+    QStringList list;
+
+    QMap<QString, QString>::ConstIterator itc, it = d->ns->begin();
+    while ( (itc=it) != d->ns->end() ) {
+	++it;
+	if ( itc.data() == uri && !itc.key().isEmpty() )
+	    list.append( itc.key() );
+    }
+    return list;
+}
+
+/*!
+    Starts a new namespace context.
+
+    Normally, you should push a new context at the beginning of each
+    XML element: the new context automatically inherits the
+    declarations of its parent context, and it also keeps track of
+    which declarations were made within this context.
+
+    \sa popContext()
+*/
+void QXmlNamespaceSupport::pushContext()
+{
+    d->nsStack.push( new QMap<QString, QString>(*d->ns) );
+}
+
+/*!
+    Reverts to the previous namespace context.
+
+    Normally, you should pop the context at the end of each XML
+    element. After popping the context, all namespace prefix mappings
+    that were previously in force are restored.
+
+    \sa pushContext()
+*/
+void QXmlNamespaceSupport::popContext()
+{
+    delete d->ns;
+    if( !d->nsStack.isEmpty() )
+	d->ns = d->nsStack.pop();
+}
+
+/*!
+    Resets this namespace support object ready for reuse.
+*/
+void QXmlNamespaceSupport::reset()
+{
+    delete d;
+    d = new QXmlNamespaceSupportPrivate;
+}
+
+
+
+/*********************************************
+ *
+ * QXmlAttributes
+ *
+ *********************************************/
+
+/*!
+    \class QXmlAttributes qxml.h
+    \reentrant
+    \brief The QXmlAttributes class provides XML attributes.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    If attributes are reported by QXmlContentHandler::startElement()
+    this class is used to pass the attribute values.
+
+    Use index() to locate the position of an attribute in the list,
+    count() to retrieve the number of attributes, and clear() to
+    remove the attributes. New attributes can be added with append().
+    Use type() to get an attribute's type and value() to get its
+    value. The attribute's name is available from localName() or
+    qName(), and its namespace URI from uri().
+
+*/
+
+/*!
+    \fn QXmlAttributes::QXmlAttributes()
+
+    Constructs an empty attribute list.
+*/
+
+/*!
+    \fn QXmlAttributes::~QXmlAttributes()
+
+    Destroys the attributes object.
+*/
+
+/*!
+    Looks up the index of an attribute by the qualified name \a qName.
+
+    Returns the index of the attribute or -1 if it wasn't found.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+*/
+int QXmlAttributes::index( const QString& qName ) const
+{
+    return qnameList.findIndex( qName );
+}
+
+/*!
+    \overload
+
+    Looks up the index of an attribute by a namespace name.
+
+    \a uri specifies the namespace URI, or an empty string if the name
+    has no namespace URI. \a localPart specifies the attribute's local
+    name.
+
+    Returns the index of the attribute, or -1 if it wasn't found.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+*/
+int QXmlAttributes::index( const QString& uri, const QString& localPart ) const
+{
+    QString uriTmp;
+    if ( uri.isEmpty() )
+	uriTmp = QString::null;
+    else
+	uriTmp = uri;
+    uint count = (uint)uriList.count(); // ### size_t/int cast
+    for ( uint i=0; i<count; i++ ) {
+	if ( uriList[i] == uriTmp && localnameList[i] == localPart )
+	    return i;
+    }
+    return -1;
+}
+
+/*!
+    Returns the number of attributes in the list.
+
+    \sa count()
+*/
+int QXmlAttributes::length() const
+{
+    return (int)valueList.count();
+}
+
+/*!
+    \fn int QXmlAttributes::count() const
+
+    Returns the number of attributes in the list. This function is
+    equivalent to length().
+*/
+
+/*!
+    Looks up an attribute's local name for the attribute at position
+    \a index. If no namespace processing is done, the local name is
+    QString::null.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+*/
+QString QXmlAttributes::localName( int index ) const
+{
+    return localnameList[index];
+}
+
+/*!
+    Looks up an attribute's XML 1.0 qualified name for the attribute
+    at position \a index.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+*/
+QString QXmlAttributes::qName( int index ) const
+{
+    return qnameList[index];
+}
+
+/*!
+    Looks up an attribute's namespace URI for the attribute at
+    position \a index. If no namespace processing is done or if the
+    attribute has no namespace, the namespace URI is QString::null.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+*/
+QString QXmlAttributes::uri( int index ) const
+{
+    return uriList[index];
+}
+
+/*!
+    Looks up an attribute's type for the attribute at position \a
+    index.
+
+    Currently only "CDATA" is returned.
+*/
+QString QXmlAttributes::type( int ) const
+{
+    return "CDATA";
+}
+
+/*!
+    \overload
+
+    Looks up an attribute's type for the qualified name \a qName.
+
+    Currently only "CDATA" is returned.
+*/
+QString QXmlAttributes::type( const QString& ) const
+{
+    return "CDATA";
+}
+
+/*!
+    \overload
+
+    Looks up an attribute's type by namespace name.
+
+    \a uri specifies the namespace URI and \a localName specifies the
+    local name. If the name has no namespace URI, use an empty string
+    for \a uri.
+
+    Currently only "CDATA" is returned.
+*/
+QString QXmlAttributes::type( const QString&, const QString& ) const
+{
+    return "CDATA";
+}
+
+/*!
+    Looks up an attribute's value for the attribute at position \a
+    index.
+*/
+QString QXmlAttributes::value( int index ) const
+{
+    return valueList[index];
+}
+
+/*!
+    \overload
+
+    Looks up an attribute's value for the qualified name \a qName.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+*/
+QString QXmlAttributes::value( const QString& qName ) const
+{
+    int i = index( qName );
+    if ( i == -1 )
+	return QString::null;
+    return valueList[ i ];
+}
+
+/*!
+    \overload
+
+    Looks up an attribute's value by namespace name.
+
+    \a uri specifies the namespace URI, or an empty string if the name
+    has no namespace URI. \a localName specifies the attribute's local
+    name.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+*/
+QString QXmlAttributes::value( const QString& uri, const QString& localName ) const
+{
+    int i = index( uri, localName );
+    if ( i == -1 )
+	return QString::null;
+    return valueList[ i ];
+}
+
+/*!
+    Clears the list of attributes.
+
+    \sa append()
+*/
+void QXmlAttributes::clear()
+{
+    qnameList.clear();
+    uriList.clear();
+    localnameList.clear();
+    valueList.clear();
+}
+
+/*!
+    Appends a new attribute entry to the list of attributes. The
+    qualified name of the attribute is \a qName, the namespace URI is
+    \a uri and the local name is \a localPart. The value of the
+    attribute is \a value.
+
+    \sa qName() uri() localName() value()
+*/
+void QXmlAttributes::append( const QString &qName, const QString &uri, const QString &localPart, const QString &value )
+{
+    qnameList.append( qName );
+    uriList.append( uri );
+    localnameList.append( localPart);
+    valueList.append( value );
+}
+
+
+/*********************************************
+ *
+ * QXmlInputSource
+ *
+ *********************************************/
+
+/*!
+    \class QXmlInputSource qxml.h
+    \reentrant
+    \brief The QXmlInputSource class provides the input data for the
+    QXmlReader subclasses.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    All subclasses of QXmlReader read the input XML document from this
+    class.
+
+    This class recognizes the encoding of the data by reading the
+    encoding declaration in the XML file if it finds one, and reading
+    the data using the corresponding encoding. If it does not find an
+    encoding declaration, then it assumes that the data is either in
+    UTF-8 or UTF-16, depending on whether it can find a byte-order
+    mark.
+
+    There are two ways to populate the input source with data: you can
+    construct it with a QIODevice* so that the input source reads the
+    data from that device. Or you can set the data explicitly with one
+    of the setData() functions.
+
+    Usually you either construct a QXmlInputSource that works on a
+    QIODevice* or you construct an empty QXmlInputSource and set the
+    data with setData(). There are only rare occasions where you would
+    want to mix both methods.
+
+    The QXmlReader subclasses use the next() function to read the
+    input character by character. If you want to start from the
+    beginning again, use reset().
+
+    The functions data() and fetchData() are useful if you want to do
+    something with the data other than parsing, e.g. displaying the
+    raw XML file. The benefit of using the QXmlInputClass in such
+    cases is that it tries to use the correct encoding.
+
+    \sa QXmlReader QXmlSimpleReader
+*/
+
+// the following two are guaranteed not to be a character
+const QChar QXmlInputSource::EndOfData = QChar((ushort)0xfffe);
+const QChar QXmlInputSource::EndOfDocument = QChar((ushort)0xffff);
+
+/*
+    Common part of the constructors.
+*/
+void QXmlInputSource::init()
+{
+    inputDevice = 0;
+    inputStream = 0;
+
+    setData( QString::null );
+    encMapper = 0;
+}
+
+/*!
+    Constructs an input source which contains no data.
+
+    \sa setData()
+*/
+QXmlInputSource::QXmlInputSource()
+{
+    init();
+}
+
+/*!
+    Constructs an input source and gets the data from device \a dev.
+    If \a dev is not open, it is opened in read-only mode. If \a dev
+    is 0 or it is not possible to read from the device, the input
+    source will contain no data.
+
+    \sa setData() fetchData() QIODevice
+*/
+QXmlInputSource::QXmlInputSource( QIODevice *dev )
+{
+    init();
+    inputDevice = dev;
+    fetchData();
+}
+
+/*! \obsolete
+  Constructs an input source and gets the data from the text stream \a stream.
+*/
+QXmlInputSource::QXmlInputSource( QTextStream& stream )
+{
+    init();
+    inputStream = &stream;
+    fetchData();
+}
+
+/*! \obsolete
+  Constructs an input source and gets the data from the file \a file. If the
+  file cannot be read the input source is empty.
+*/
+QXmlInputSource::QXmlInputSource( QFile& file )
+{
+    init();
+    inputDevice = &file;
+    fetchData();
+}
+
+/*!
+    Destructor.
+*/
+QXmlInputSource::~QXmlInputSource()
+{
+    delete encMapper;
+}
+
+/*!
+    Returns the next character of the input source. If this function
+    reaches the end of available data, it returns
+    QXmlInputSource::EndOfData. If you call next() after that, it
+    tries to fetch more data by calling fetchData(). If the
+    fetchData() call results in new data, this function returns the
+    first character of that data; otherwise it returns
+    QXmlInputSource::EndOfDocument.
+
+    \sa reset() fetchData() QXmlSimpleReader::parse() QXmlSimpleReader::parseContinue()
+*/
+QChar QXmlInputSource::next()
+{
+    if ( pos >= length ) {
+	if ( nextReturnedEndOfData ) {
+	    nextReturnedEndOfData = FALSE;
+	    fetchData();
+	    if ( pos >= length ) {
+		return EndOfDocument;
+	    }
+	    return next();
+	}
+	nextReturnedEndOfData = TRUE;
+	return EndOfData;
+    }
+    return unicode[pos++];
+}
+
+/*!
+    This function sets the position used by next() to the beginning of
+    the data returned by data(). This is useful if you want to use the
+    input source for more than one parse.
+
+    \sa next()
+*/
+void QXmlInputSource::reset()
+{
+    nextReturnedEndOfData = FALSE;
+    pos = 0;
+}
+
+/*!
+    Returns the data the input source contains or QString::null if the
+    input source does not contain any data.
+
+    \sa setData() QXmlInputSource() fetchData()
+*/
+QString QXmlInputSource::data()
+{
+    return str;
+}
+
+/*!
+    Sets the data of the input source to \a dat.
+
+    If the input source already contains data, this function deletes
+    that data first.
+
+    \sa data()
+*/
+void QXmlInputSource::setData( const QString& dat )
+{
+    str = dat;
+    unicode = dat.unicode();
+    pos = 0;
+    length = str.length();
+    nextReturnedEndOfData = FALSE;
+}
+
+/*!
+    \overload
+
+    The data \a dat is passed through the correct text-codec, before
+    it is set.
+*/
+void QXmlInputSource::setData( const QByteArray& dat )
+{
+    setData( fromRawData( dat ) );
+}
+
+/*!
+    This function reads more data from the device that was set during
+    construction. If the input source already contained data, this
+    function deletes that data first.
+
+    This object contains no data after a call to this function if the
+    object was constructed without a device to read data from or if
+    this function was not able to get more data from the device.
+
+    There are two occasions where a fetch is done implicitly by
+    another function call: during construction (so that the object
+    starts out with some initial data where available), and during a
+    call to next() (if the data had run out).
+
+    You don't normally need to use this function if you use next().
+
+    \sa data() next() QXmlInputSource()
+*/
+void QXmlInputSource::fetchData()
+{
+    QByteArray rawData;
+
+    if ( inputDevice != 0 ) {
+	if ( inputDevice->isOpen() || inputDevice->open( IO_ReadOnly )  )
+	    rawData = inputDevice->readAll();
+    } else if ( inputStream != 0 ) {
+	if ( inputStream->device()->isDirectAccess() ) {
+	    rawData = inputStream->device()->readAll();
+	} else {
+	    int nread = 0;
+	    const int bufsize = 512;
+	    while ( !inputStream->device()->atEnd() ) {
+		rawData.resize( nread + bufsize );
+		nread += inputStream->device()->readBlock( rawData.data()+nread, bufsize );
+	    }
+	    rawData.resize( nread );
+	}
+    }
+    setData( fromRawData( rawData ) );
+}
+
+/*!
+    This function reads the XML file from \a data and tries to
+    recognize the encoding. It converts the raw data \a data into a
+    QString and returns it. It tries its best to get the correct
+    encoding for the XML file.
+
+    If \a beginning is TRUE, this function assumes that the data
+    starts at the beginning of a new XML document and looks for an
+    encoding declaration. If \a beginning is FALSE, it converts the
+    raw data using the encoding determined from prior calls.
+*/
+QString QXmlInputSource::fromRawData( const QByteArray &data, bool beginning )
+{
+    if ( data.size() == 0 )
+	return QString::null;
+    if ( beginning ) {
+	delete encMapper;
+	encMapper = 0;
+    }
+    if ( encMapper == 0 ) {
+	QTextCodec *codec = 0;
+	// look for byte order mark and read the first 5 characters
+	if ( data.size() >= 2 &&
+		( ((uchar)data.at(0)==(uchar)0xfe &&
+		   (uchar)data.at(1)==(uchar)0xff ) ||
+		  ((uchar)data.at(0)==(uchar)0xff &&
+		   (uchar)data.at(1)==(uchar)0xfe ) )) {
+	    codec = QTextCodec::codecForMib( 1000 ); // UTF-16
+	} else {
+	    codec = QTextCodec::codecForMib( 106 ); // UTF-8
+	}
+	if ( !codec )
+	    return QString::null;
+
+	encMapper = codec->makeDecoder();
+	QString input = encMapper->toUnicode( data.data(), data.size() );
+	// ### unexpected EOF? (for incremental parsing)
+	// starts the document with an XML declaration?
+	if ( input.find("<?xml") == 0 ) {
+	    // try to find out if there is an encoding
+	    int endPos = input.find( ">" );
+	    int pos = input.find( "encoding" );
+	    if ( pos < endPos && pos != -1 ) {
+		QString encoding;
+		do {
+		    pos++;
+		    if ( pos > endPos ) {
+			return input;
+		    }
+		} while( input[pos] != '"' && input[pos] != '\'' );
+		pos++;
+		while( input[pos] != '"' && input[pos] != '\'' ) {
+		    encoding += input[pos];
+		    pos++;
+		    if ( pos > endPos ) {
+			return input;
+		    }
+		}
+
+		codec = QTextCodec::codecForName( encoding );
+		if ( codec == 0 ) {
+		    return input;
+		}
+		delete encMapper;
+		encMapper = codec->makeDecoder();
+		return encMapper->toUnicode( data.data(), data.size() );
+	    }
+	}
+	return input;
+    }
+    return encMapper->toUnicode( data.data(), data.size() );
+}
+
+
+/*********************************************
+ *
+ * QXmlDefaultHandler
+ *
+ *********************************************/
+
+/*!
+    \class QXmlContentHandler qxml.h
+    \reentrant
+    \brief The QXmlContentHandler class provides an interface to
+    report the logical content of XML data.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    If the application needs to be informed of basic parsing events,
+    it can implement this interface and activate it using
+    QXmlReader::setContentHandler(). The reader can then report basic
+    document-related events like the start and end of elements and
+    character data through this interface.
+
+    The order of events in this interface is very important, and
+    mirrors the order of information in the document itself. For
+    example, all of an element's content (character data, processing
+    instructions, and sub-elements) appears, in order, between the
+    startElement() event and the corresponding endElement() event.
+
+    The class QXmlDefaultHandler provides a default implementation for
+    this interface; subclassing from the QXmlDefaultHandler class is
+    very convenient if you only want to be informed of some parsing
+    events.
+
+    The startDocument() function is called at the start of the
+    document, and endDocument() is called at the end. Before parsing
+    begins setDocumentLocator() is called. For each element
+    startElement() is called, with endElement() being called at the
+    end of each element. The characters() function is called with
+    chunks of character data; ignorableWhitespace() is called with
+    chunks of whitespace and processingInstruction() is called with
+    processing instructions. If an entity is skipped skippedEntity()
+    is called. At the beginning of prefix-URI scopes
+    startPrefixMapping() is called.
+
+    See also the \link xml.html#sax2Intro Introduction to SAX2\endlink.
+
+    \sa QXmlDTDHandler QXmlDeclHandler QXmlEntityResolver QXmlErrorHandler
+    QXmlLexicalHandler
+*/
+
+/*!
+    \fn void QXmlContentHandler::setDocumentLocator( QXmlLocator* locator )
+
+    The reader calls this function before it starts parsing the
+    document. The argument \a locator is a pointer to a QXmlLocator
+    which allows the application to get the parsing position within
+    the document.
+
+    Do not destroy the \a locator; it is destroyed when the reader is
+    destroyed. (Do not use the \a locator after the reader is
+    destroyed).
+*/
+
+/*!
+    \fn bool QXmlContentHandler::startDocument()
+
+    The reader calls this function when it starts parsing the
+    document. The reader calls this function just once, after the call
+    to setDocumentLocator(), and before any other functions in this
+    class or in the QXmlDTDHandler class are called.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    \sa endDocument()
+*/
+
+/*!
+    \fn bool QXmlContentHandler::endDocument()
+
+    The reader calls this function after it has finished parsing. It
+    is called just once, and is the last handler function called. It
+    is called after the reader has read all input or has abandoned
+    parsing because of a fatal error.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    \sa startDocument()
+*/
+
+/*!
+    \fn bool QXmlContentHandler::startPrefixMapping( const QString& prefix, const QString& uri )
+
+    The reader calls this function to signal the begin of a prefix-URI
+    namespace mapping scope. This information is not necessary for
+    normal namespace processing since the reader automatically
+    replaces prefixes for element and attribute names.
+
+    Note that startPrefixMapping() and endPrefixMapping() calls are
+    not guaranteed to be properly nested relative to each other: all
+    startPrefixMapping() events occur before the corresponding
+    startElement() event, and all endPrefixMapping() events occur
+    after the corresponding endElement() event, but their order is not
+    otherwise guaranteed.
+
+    The argument \a prefix is the namespace prefix being declared and
+    the argument \a uri is the namespace URI the prefix is mapped to.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+
+    \sa endPrefixMapping()
+*/
+
+/*!
+    \fn bool QXmlContentHandler::endPrefixMapping( const QString& prefix )
+
+    The reader calls this function to signal the end of a prefix
+    mapping for the prefix \a prefix.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+
+    \sa startPrefixMapping()
+*/
+
+/*!
+    \fn bool QXmlContentHandler::startElement( const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts )
+
+    The reader calls this function when it has parsed a start element
+    tag.
+
+    There is a corresponding endElement() call when the corresponding
+    end element tag is read. The startElement() and endElement() calls
+    are always nested correctly. Empty element tags (e.g. \c{<x/>})
+    cause a startElement() call to be immediately followed by an
+    endElement() call.
+
+    The attribute list provided only contains attributes with explicit
+    values. The attribute list contains attributes used for namespace
+    declaration (i.e. attributes starting with xmlns) only if the
+    namespace-prefix property of the reader is TRUE.
+
+    The argument \a namespaceURI is the namespace URI, or
+    QString::null if the element has no namespace URI or if no
+    namespace processing is done. \a localName is the local name
+    (without prefix), or QString::null if no namespace processing is
+    done, \a qName is the qualified name (with prefix) and \a atts are
+    the attributes attached to the element. If there are no
+    attributes, \a atts is an empty attributes object.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+
+    \sa endElement()
+*/
+
+/*!
+    \fn bool QXmlContentHandler::endElement( const QString& namespaceURI, const QString& localName, const QString& qName )
+
+    The reader calls this function when it has parsed an end element
+    tag with the qualified name \a qName, the local name \a localName
+    and the namespace URI \a namespaceURI.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    See also the \link xml.html#sax2Namespaces namespace description\endlink.
+
+    \sa startElement()
+*/
+
+/*!
+    \fn bool QXmlContentHandler::characters( const QString& ch )
+
+    The reader calls this function when it has parsed a chunk of
+    character data (either normal character data or character data
+    inside a CDATA section; if you need to distinguish between those
+    two types you must use QXmlLexicalHandler::startCDATA() and
+    QXmlLexicalHandler::endCDATA()). The character data is reported in
+    \a ch.
+
+    Some readers report whitespace in element content using the
+    ignorableWhitespace() function rather than using this one.
+
+    A reader may report the character data of an element in more than
+    one chunk; e.g. a reader might want to report "a\<b" in three
+    characters() events ("a ", "\<" and " b").
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn bool QXmlContentHandler::ignorableWhitespace( const QString& ch )
+
+    Some readers may use this function to report each chunk of
+    whitespace in element content. The whitespace is reported in \a ch.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn bool QXmlContentHandler::processingInstruction( const QString& target, const QString& data )
+
+    The reader calls this function when it has parsed a processing
+    instruction.
+
+    \a target is the target name of the processing instruction and \a
+    data is the data in the processing instruction.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn bool QXmlContentHandler::skippedEntity( const QString& name )
+
+    Some readers may skip entities if they have not seen the
+    declarations (e.g. because they are in an external DTD). If they
+    do so they report that they skipped the entity called \a name by
+    calling this function.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn QString QXmlContentHandler::errorString()
+
+    The reader calls this function to get an error string, e.g. if any
+    of the handler functions returns FALSE.
+*/
+
+
+/*!
+    \class QXmlErrorHandler qxml.h
+    \reentrant
+    \brief The QXmlErrorHandler class provides an interface to report
+    errors in XML data.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    If you want your application to report errors to the user or to
+    perform customized error handling, you should subclass this class.
+
+    You can set the error handler with QXmlReader::setErrorHandler().
+
+    Errors can be reported using warning(), error() and fatalError(),
+    with the error text being reported with errorString().
+
+    See also the \link xml.html#sax2Intro Introduction to SAX2\endlink.
+
+    \sa QXmlDTDHandler QXmlDeclHandler QXmlContentHandler QXmlEntityResolver
+    QXmlLexicalHandler
+*/
+
+/*!
+    \fn bool QXmlErrorHandler::warning( const QXmlParseException& exception )
+
+    A reader might use this function to report a warning. Warnings are
+    conditions that are not errors or fatal errors as defined by the
+    XML 1.0 specification. Details of the warning are stored in \a
+    exception.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn bool QXmlErrorHandler::error( const QXmlParseException& exception )
+
+    A reader might use this function to report a recoverable error. A
+    recoverable error corresponds to the definiton of "error" in
+    section 1.2 of the XML 1.0 specification. Details of the error are
+    stored in \a exception.
+
+    The reader must continue to provide normal parsing events after
+    invoking this function.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn bool QXmlErrorHandler::fatalError( const QXmlParseException& exception )
+
+    A reader must use this function to report a non-recoverable error.
+    Details of the error are stored in \a exception.
+
+    If this function returns TRUE the reader might try to go on
+    parsing and reporting further errors; but no regular parsing
+    events are reported.
+*/
+
+/*!
+    \fn QString QXmlErrorHandler::errorString()
+
+    The reader calls this function to get an error string if any of
+    the handler functions returns FALSE.
+*/
+
+
+/*!
+    \class QXmlDTDHandler qxml.h
+    \reentrant
+    \brief The QXmlDTDHandler class provides an interface to report
+    DTD content of XML data.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    If an application needs information about notations and unparsed
+    entities, it can implement this interface and register an instance
+    with QXmlReader::setDTDHandler().
+
+    Note that this interface includes only those DTD events that the
+    XML recommendation requires processors to report, i.e. notation
+    and unparsed entity declarations using notationDecl() and
+    unparsedEntityDecl() respectively.
+
+    See also the \link xml.html#sax2Intro Introduction to SAX2\endlink.
+
+    \sa QXmlDeclHandler QXmlContentHandler QXmlEntityResolver QXmlErrorHandler
+    QXmlLexicalHandler
+*/
+
+/*!
+    \fn bool QXmlDTDHandler::notationDecl( const QString& name, const QString& publicId, const QString& systemId )
+
+    The reader calls this function when it has parsed a notation
+    declaration.
+
+    The argument \a name is the notation name, \a publicId is the
+    notation's public identifier and \a systemId is the notation's
+    system identifier.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn bool QXmlDTDHandler::unparsedEntityDecl( const QString& name, const QString& publicId, const QString& systemId, const QString& notationName )
+
+    The reader calls this function when it finds an unparsed entity
+    declaration.
+
+    The argument \a name is the unparsed entity's name, \a publicId is
+    the entity's public identifier, \a systemId is the entity's system
+    identifier and \a notationName is the name of the associated
+    notation.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn QString QXmlDTDHandler::errorString()
+
+    The reader calls this function to get an error string if any of
+    the handler functions returns FALSE.
+*/
+
+
+/*!
+    \class QXmlEntityResolver qxml.h
+    \reentrant
+    \brief The QXmlEntityResolver class provides an interface to
+    resolve external entities contained in XML data.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    If an application needs to implement customized handling for
+    external entities, it must implement this interface, i.e.
+    resolveEntity(), and register it with
+    QXmlReader::setEntityResolver().
+
+    See also the \link xml.html#sax2Intro Introduction to SAX2\endlink.
+
+    \sa QXmlDTDHandler QXmlDeclHandler QXmlContentHandler QXmlErrorHandler
+    QXmlLexicalHandler
+*/
+
+/*!
+    \fn bool QXmlEntityResolver::resolveEntity( const QString& publicId, const QString& systemId, QXmlInputSource*& ret )
+
+    The reader calls this function before it opens any external
+    entity, except the top-level document entity. The application may
+    request the reader to resolve the entity itself (\a ret is 0) or
+    to use an entirely different input source (\a ret points to the
+    input source).
+
+    The reader deletes the input source \a ret when it no longer needs
+    it, so you should allocate it on the heap with \c new.
+
+    The argument \a publicId is the public identifier of the external
+    entity, \a systemId is the system identifier of the external
+    entity and \a ret is the return value of this function. If \a ret
+    is 0 the reader should resolve the entity itself, if it is
+    non-zero it must point to an input source which the reader uses
+    instead.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn QString QXmlEntityResolver::errorString()
+
+    The reader calls this function to get an error string if any of
+    the handler functions returns FALSE.
+*/
+
+
+/*!
+    \class QXmlLexicalHandler qxml.h
+    \reentrant
+    \brief The QXmlLexicalHandler class provides an interface to
+    report the lexical content of XML data.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    The events in the lexical handler apply to the entire document,
+    not just to the document element, and all lexical handler events
+    appear between the content handler's startDocument and endDocument
+    events.
+
+    You can set the lexical handler with
+    QXmlReader::setLexicalHandler().
+
+    This interface's design is based on the the SAX2 extension
+    LexicalHandler.
+
+    The interface provides the startDTD(), endDTD(), startEntity(),
+    endEntity(), startCDATA(), endCDATA() and comment() functions.
+
+    See also the \link xml.html#sax2Intro Introduction to SAX2\endlink.
+
+    \sa QXmlDTDHandler QXmlDeclHandler QXmlContentHandler QXmlEntityResolver
+    QXmlErrorHandler
+*/
+
+/*!
+    \fn bool QXmlLexicalHandler::startDTD( const QString& name, const QString& publicId, const QString& systemId )
+
+    The reader calls this function to report the start of a DTD
+    declaration, if any. It reports the name of the document type in
+    \a name, the public identifier in \a publicId and the system
+    identifier in \a systemId.
+
+    If the public identifier is missing, \a publicId is set to
+    QString::null. If the system identifier is missing, \a systemId is
+    set to QString::null. Note that it is not valid XML to have a
+    public identifier but no system identifier; in such cases a parse
+    error will occur.
+
+    All declarations reported through QXmlDTDHandler or
+    QXmlDeclHandler appear between the startDTD() and endDTD() calls.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    \sa endDTD()
+*/
+
+/*!
+    \fn bool QXmlLexicalHandler::endDTD()
+
+    The reader calls this function to report the end of a DTD
+    declaration, if any.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    \sa startDTD()
+*/
+
+/*!
+    \fn bool QXmlLexicalHandler::startEntity( const QString& name )
+
+    The reader calls this function to report the start of an entity
+    called \a name.
+
+    Note that if the entity is unknown, the reader reports it through
+    QXmlContentHandler::skippedEntity() and not through this
+    function.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    \sa endEntity() QXmlSimpleReader::setFeature()
+*/
+
+/*!
+    \fn bool QXmlLexicalHandler::endEntity( const QString& name )
+
+    The reader calls this function to report the end of an entity
+    called \a name.
+
+    For every startEntity() call, there is a corresponding endEntity()
+    call. The calls to startEntity() and endEntity() are properly
+    nested.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    \sa startEntity() QXmlContentHandler::skippedEntity() QXmlSimpleReader::setFeature()
+*/
+
+/*!
+    \fn bool QXmlLexicalHandler::startCDATA()
+
+    The reader calls this function to report the start of a CDATA
+    section. The content of the CDATA section is reported through the
+    QXmlContentHandler::characters() function. This function is
+    intended only to report the boundary.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+
+    \sa endCDATA()
+*/
+
+/*!
+    \fn bool QXmlLexicalHandler::endCDATA()
+
+    The reader calls this function to report the end of a CDATA
+    section.
+
+    If this function returns FALSE the reader stops parsing and reports
+    an error. The reader uses the function errorString() to get the error
+    message.
+
+    \sa startCDATA() QXmlContentHandler::characters()
+*/
+
+/*!
+    \fn bool QXmlLexicalHandler::comment( const QString& ch )
+
+    The reader calls this function to report an XML comment anywhere
+    in the document. It reports the text of the comment in \a ch.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn QString QXmlLexicalHandler::errorString()
+
+    The reader calls this function to get an error string if any of
+    the handler functions returns FALSE.
+*/
+
+
+/*!
+    \class QXmlDeclHandler qxml.h
+    \reentrant
+    \brief The QXmlDeclHandler class provides an interface to report declaration
+    content of XML data.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    You can set the declaration handler with
+    QXmlReader::setDeclHandler().
+
+    This interface is based on the SAX2 extension DeclHandler.
+
+    The interface provides attributeDecl(), internalEntityDecl() and
+    externalEntityDecl() functions.
+
+    See also the \link xml.html#sax2Intro Introduction to SAX2\endlink.
+
+    \sa QXmlDTDHandler QXmlContentHandler QXmlEntityResolver QXmlErrorHandler
+    QXmlLexicalHandler
+*/
+
+/*!
+    \fn bool QXmlDeclHandler::attributeDecl( const QString& eName, const QString& aName, const QString& type, const QString& valueDefault, const QString& value )
+
+    The reader calls this function to report an attribute type
+    declaration. Only the effective (first) declaration for an
+    attribute is reported.
+
+    The reader passes the name of the associated element in \a eName
+    and the name of the attribute in \a aName. It passes a string that
+    represents the attribute type in \a type and a string that
+    represents the attribute default in \a valueDefault. This string
+    is one of "#IMPLIED", "#REQUIRED", "#FIXED" or QString::null (if
+    none of the others applies). The reader passes the attribute's
+    default value in \a value. If no default value is specified in the
+    XML file, \a value is QString::null.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn bool QXmlDeclHandler::internalEntityDecl( const QString& name, const QString& value )
+
+    The reader calls this function to report an internal entity
+    declaration. Only the effective (first) declaration is reported.
+
+    The reader passes the name of the entity in \a name and the value
+    of the entity in \a value.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn bool QXmlDeclHandler::externalEntityDecl( const QString& name, const QString& publicId, const QString& systemId )
+
+    The reader calls this function to report a parsed external entity
+    declaration. Only the effective (first) declaration for each
+    entity is reported.
+
+    The reader passes the name of the entity in \a name, the public
+    identifier in \a publicId and the system identifier in \a
+    systemId. If there is no public identifier specified, it passes
+    QString::null in \a publicId.
+
+    If this function returns FALSE the reader stops parsing and
+    reports an error. The reader uses the function errorString() to
+    get the error message.
+*/
+
+/*!
+    \fn QString QXmlDeclHandler::errorString()
+
+    The reader calls this function to get an error string if any of
+    the handler functions returns FALSE.
+*/
+
+
+/*!
+    \class QXmlDefaultHandler qxml.h
+    \reentrant
+    \brief The QXmlDefaultHandler class provides a default implementation of all
+    the XML handler classes.
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    Very often we are only interested in parts of the things that the
+    reader reports. This class implements a default behaviour for the
+    handler classes (i.e. most of the time do nothing). Usually this
+    is the class you subclass for implementing your own customized
+    handler.
+
+    See also the \link xml.html#sax2Intro Introduction to SAX2\endlink.
+
+    \sa QXmlDTDHandler QXmlDeclHandler QXmlContentHandler QXmlEntityResolver
+    QXmlErrorHandler QXmlLexicalHandler
+*/
+
+/*!
+    \fn QXmlDefaultHandler::QXmlDefaultHandler()
+
+    Constructor.
+*/
+/*!
+    \fn QXmlDefaultHandler::~QXmlDefaultHandler()
+
+    Destructor.
+*/
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+void QXmlDefaultHandler::setDocumentLocator( QXmlLocator* )
+{
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::startDocument()
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::endDocument()
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::startPrefixMapping( const QString&, const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::endPrefixMapping( const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::startElement( const QString&, const QString&,
+	const QString&, const QXmlAttributes& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::endElement( const QString&, const QString&,
+	const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::characters( const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::ignorableWhitespace( const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::processingInstruction( const QString&,
+	const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::skippedEntity( const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::warning( const QXmlParseException& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::error( const QXmlParseException& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::fatalError( const QXmlParseException& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::notationDecl( const QString&, const QString&,
+	const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::unparsedEntityDecl( const QString&, const QString&,
+	const QString&, const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Sets \a ret to 0, so that the reader uses the system identifier
+    provided in the XML document.
+*/
+bool QXmlDefaultHandler::resolveEntity( const QString&, const QString&,
+	QXmlInputSource*& ret )
+{
+    ret = 0;
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Returns the default error string.
+*/
+QString QXmlDefaultHandler::errorString()
+{
+    return QString( XMLERR_ERRORBYCONSUMER );
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::startDTD( const QString&, const QString&, const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::endDTD()
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::startEntity( const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::endEntity( const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::startCDATA()
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::endCDATA()
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::comment( const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::attributeDecl( const QString&, const QString&, const QString&, const QString&, const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::internalEntityDecl( const QString&, const QString& )
+{
+    return TRUE;
+}
+
+/*!
+    \reimp
+
+    Does nothing.
+*/
+bool QXmlDefaultHandler::externalEntityDecl( const QString&, const QString&, const QString& )
+{
+    return TRUE;
+}
+
+
+/*********************************************
+ *
+ * QXmlSimpleReaderPrivate
+ *
+ *********************************************/
+
+class QXmlSimpleReaderPrivate
+{
+private:
+    // functions
+    QXmlSimpleReaderPrivate()
+    {
+	parseStack = 0;
+    }
+
+    ~QXmlSimpleReaderPrivate()
+    {
+	delete parseStack;
+    }
+
+    void initIncrementalParsing()
+    {
+	delete parseStack;
+	parseStack = new QPtrStack<ParseState>;
+	parseStack->setAutoDelete( TRUE );
+    }
+
+    // used to determine if elements are correctly nested
+    QValueStack<QString> tags;
+
+    // used for entity declarations
+    struct ExternParameterEntity
+    {
+	ExternParameterEntity( ) {}
+	ExternParameterEntity( const QString &p, const QString &s )
+	    : publicId(p), systemId(s) {}
+	QString publicId;
+	QString systemId;
+
+	Q_DUMMY_COMPARISON_OPERATOR(ExternParameterEntity)
+    };
+    struct ExternEntity
+    {
+	ExternEntity( ) {}
+	ExternEntity( const QString &p, const QString &s, const QString &n )
+	    : publicId(p), systemId(s), notation(n) {}
+	QString publicId;
+	QString systemId;
+	QString notation;
+	Q_DUMMY_COMPARISON_OPERATOR(ExternEntity)
+    };
+    QMap<QString,ExternParameterEntity> externParameterEntities;
+    QMap<QString,QString> parameterEntities;
+    QMap<QString,ExternEntity> externEntities;
+    QMap<QString,QString> entities;
+
+    // used for parsing of entity references
+    QValueStack<QString> xmlRef;
+    QValueStack<QString> xmlRefName;
+
+    // used for standalone declaration
+    enum Standalone { Yes, No, Unknown };
+
+    QString doctype; // only used for the doctype
+    QString xmlVersion; // only used to store the version information
+    QString encoding; // only used to store the encoding
+    Standalone standalone; // used to store the value of the standalone declaration
+
+    QString publicId; // used by parseExternalID() to store the public ID
+    QString systemId; // used by parseExternalID() to store the system ID
+    QString attDeclEName; // use by parseAttlistDecl()
+    QString attDeclAName; // use by parseAttlistDecl()
+
+    // flags for some features support
+    bool useNamespaces;
+    bool useNamespacePrefixes;
+    bool reportWhitespaceCharData;
+    bool reportEntities;
+
+    // used to build the attribute list
+    QXmlAttributes attList;
+
+    // used in QXmlSimpleReader::parseContent() to decide whether character
+    // data was read
+    bool contentCharDataRead;
+
+    // helper classes
+    QXmlLocator *locator;
+    QXmlNamespaceSupport namespaceSupport;
+
+    // error string
+    QString error;
+
+    // arguments for parse functions (this is needed to allow incremental
+    // parsing)
+    bool parsePI_xmldecl;
+    bool parseName_useRef;
+    bool parseReference_charDataRead;
+    QXmlSimpleReader::EntityRecognitionContext parseReference_context;
+    bool parseExternalID_allowPublicID;
+    QXmlSimpleReader::EntityRecognitionContext parsePEReference_context;
+    QString parseString_s;
+
+    // for incremental parsing
+    struct ParseState {
+	typedef bool (QXmlSimpleReader::*ParseFunction) ();
+	ParseFunction function;
+	int state;
+    };
+    QPtrStack<ParseState> *parseStack;
+
+    // used in parseProlog()
+    bool xmldecl_possible;
+    bool doctype_read;
+
+    // used in parseDoctype()
+    bool startDTDwasReported;
+
+    // used in parseString()
+    signed char Done;
+
+
+    // friend declarations
+    friend class QXmlSimpleReader;
+};
+
+
+/*********************************************
+ *
+ * QXmlSimpleReader
+ *
+ *********************************************/
+
+/*!
+    \class QXmlReader qxml.h
+    \reentrant
+    \brief The QXmlReader class provides an interface for XML readers (i.e.
+    parsers).
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+
+    This abstract class provides an interface for all of Qt's XML
+    readers. Currently there is only one implementation of a reader
+    included in Qt's XML module: QXmlSimpleReader. In future releases
+    there might be more readers with different properties available
+    (e.g. a validating parser).
+
+    The design of the XML classes follows the \link
+    http://www.saxproject.org/ SAX2 Java interface\endlink, with
+    the names adapted to fit Qt naming conventions. It should be very
+    easy for anybody who has worked with SAX2 to get started with the
+    Qt XML classes.
+
+    All readers use the class QXmlInputSource to read the input
+    document. Since you are normally interested in particular content
+    in the XML document, the reader reports the content through
+    special handler classes (QXmlDTDHandler, QXmlDeclHandler,
+    QXmlContentHandler, QXmlEntityResolver, QXmlErrorHandler and
+    QXmlLexicalHandler), which you must subclass, if you want to
+    process the contents.
+
+    Since the handler classes only describe interfaces you must
+    implement all the functions. We provide the QXmlDefaultHandler
+    class to make this easier: it implements a default behaviour (do
+    nothing) for all functions, so you can subclass it and just
+    implement the functions you are interested in.
+
+    Features and properties of the reader can be set with setFeature()
+    and setProperty() respectively. You can set the reader to use your
+    own subclasses with setEntityResolver(), setDTDHandler(),
+    setContentHandler(), setErrorHandler(), setLexicalHandler() and
+    setDeclHandler(). The parse itself is started with a call to
+    parse().
+
+    \sa QXmlSimpleReader
+*/
+
+/*!
+    \fn bool QXmlReader::feature( const QString& name, bool *ok ) const
+
+    If the reader has the feature called \a name, the feature's value
+    is returned. If no such feature exists the return value is
+    undefined.
+
+    If \a ok is not 0: \a *ok  is set to TRUE if the reader has the
+    feature called \a name; otherwise \a *ok is set to FALSE.
+
+    \sa setFeature() hasFeature()
+*/
+
+/*!
+    \fn void QXmlReader::setFeature( const QString& name, bool value )
+
+    Sets the feature called \a name to the given \a value. If the
+    reader doesn't have the feature nothing happens.
+
+    \sa feature() hasFeature()
+*/
+
+/*!
+    \fn bool QXmlReader::hasFeature( const QString& name ) const
+
+    Returns \c TRUE if the reader has the feature called \a name;
+    otherwise returns FALSE.
+
+    \sa feature() setFeature()
+*/
+
+/*!
+    \fn void* QXmlReader::property( const QString& name, bool *ok ) const
+
+    If the reader has the property \a name, this function returns the
+    value of the property; otherwise the return value is undefined.
+
+    If \a ok is not 0: if the reader has the \a name property \a *ok
+    is set to TRUE; otherwise \a *ok is set to FALSE.
+
+    \sa setProperty() hasProperty()
+*/
+
+/*!
+    \fn void QXmlReader::setProperty( const QString& name, void* value )
+
+    Sets the property \a name to \a value. If the reader doesn't have
+    the property nothing happens.
+
+    \sa property() hasProperty()
+*/
+
+/*!
+    \fn bool QXmlReader::hasProperty( const QString& name ) const
+
+    Returns TRUE if the reader has the property \a name; otherwise
+    returns FALSE.
+
+    \sa property() setProperty()
+*/
+
+/*!
+    \fn void QXmlReader::setEntityResolver( QXmlEntityResolver* handler )
+
+    Sets the entity resolver to \a handler.
+
+    \sa entityResolver()
+*/
+
+/*!
+    \fn QXmlEntityResolver* QXmlReader::entityResolver() const
+
+    Returns the entity resolver or 0 if none was set.
+
+    \sa setEntityResolver()
+*/
+
+/*!
+    \fn void QXmlReader::setDTDHandler( QXmlDTDHandler* handler )
+
+    Sets the DTD handler to \a handler.
+
+    \sa DTDHandler()
+*/
+
+/*!
+    \fn QXmlDTDHandler* QXmlReader::DTDHandler() const
+
+    Returns the DTD handler or 0 if none was set.
+
+    \sa setDTDHandler()
+*/
+
+/*!
+    \fn void QXmlReader::setContentHandler( QXmlContentHandler* handler )
+
+    Sets the content handler to \a handler.
+
+    \sa contentHandler()
+*/
+
+/*!
+    \fn QXmlContentHandler* QXmlReader::contentHandler() const
+
+    Returns the content handler or 0 if none was set.
+
+    \sa setContentHandler()
+*/
+
+/*!
+    \fn void QXmlReader::setErrorHandler( QXmlErrorHandler* handler )
+
+    Sets the error handler to \a handler. Clears the error handler if
+    \a handler is 0.
+
+    \sa errorHandler()
+*/
+
+/*!
+    \fn QXmlErrorHandler* QXmlReader::errorHandler() const
+
+    Returns the error handler or 0 if none is set.
+
+    \sa setErrorHandler()
+*/
+
+/*!
+    \fn void QXmlReader::setLexicalHandler( QXmlLexicalHandler* handler )
+
+    Sets the lexical handler to \a handler.
+
+    \sa lexicalHandler()
+*/
+
+/*!
+    \fn QXmlLexicalHandler* QXmlReader::lexicalHandler() const
+
+    Returns the lexical handler or 0 if none was set.
+
+    \sa setLexicalHandler()
+*/
+
+/*!
+    \fn void QXmlReader::setDeclHandler( QXmlDeclHandler* handler )
+
+    Sets the declaration handler to \a handler.
+
+    \sa declHandler()
+*/
+
+/*!
+    \fn QXmlDeclHandler* QXmlReader::declHandler() const
+
+    Returns the declaration handler or 0 if none was set.
+
+    \sa setDeclHandler()
+*/
+
+/*!
+  \fn bool QXmlReader::parse( const QXmlInputSource& input )
+
+  \obsolete
+*/
+
+/*!
+    \fn bool QXmlReader::parse( const QXmlInputSource* input )
+
+    Reads an XML document from \a input and parses it. Returns TRUE if
+    the parsing was successful; otherwise returns FALSE.
+*/
+
+
+/*!
+    \class QXmlSimpleReader qxml.h
+    \reentrant
+    \brief The QXmlSimpleReader class provides an implementation of a
+    simple XML reader (parser).
+\if defined(commercial)
+    It is part of the <a href="commercialeditions.html">Qt Enterprise Edition</a>.
+\endif
+
+    \module XML
+    \ingroup xml-tools
+    \mainclass
+
+    This XML reader is sufficient for simple parsing tasks. The reader:
+    \list
+    \i provides a well-formed parser;
+    \i does not parse any external entities;
+    \i can do namespace processing.
+    \endlist
+
+    Documents are parsed with a call to parse().
+
+*/
+
+
+/*!
+    Constructs a simple XML reader with the following feature settings:
+    \table
+    \header \i Feature \i Setting
+    \row \i \e http://xml.org/sax/features/namespaces \i TRUE
+    \row \i \e http://xml.org/sax/features/namespace-prefixes \i FALSE
+    \row \i \e http://trolltech.com/xml/features/report-whitespace-only-CharData
+	 \i TRUE
+    \row \i \e http://trolltech.com/xml/features/report-start-end-entity \i FALSE
+    \endtable
+
+    More information about features can be found in the \link
+    xml.html#sax2Features Qt SAX2 overview. \endlink
+
+    \sa setFeature()
+*/
+QXmlSimpleReader::QXmlSimpleReader()
+{
+    d = new QXmlSimpleReaderPrivate();
+    d->locator = new QXmlSimpleReaderLocator( this );
+
+    entityRes  = 0;
+    dtdHnd     = 0;
+    contentHnd = 0;
+    errorHnd   = 0;
+    lexicalHnd = 0;
+    declHnd    = 0;
+
+    // default feature settings
+    d->useNamespaces = TRUE;
+    d->useNamespacePrefixes = FALSE;
+    d->reportWhitespaceCharData = TRUE;
+    d->reportEntities = FALSE;
+}
+
+/*!
+    Destroys the simple XML reader.
+*/
+QXmlSimpleReader::~QXmlSimpleReader()
+{
+    delete d->locator;
+    delete d;
+}
+
+/*!
+    \reimp
+*/
+bool QXmlSimpleReader::feature( const QString& name, bool *ok ) const
+{
+    if ( ok != 0 )
+	*ok = TRUE;
+    if        ( name == "http://xml.org/sax/features/namespaces" ) {
+	return d->useNamespaces;
+    } else if ( name == "http://xml.org/sax/features/namespace-prefixes" ) {
+	return d->useNamespacePrefixes;
+    } else if ( name == "http://trolltech.com/xml/features/report-whitespace-only-CharData" ) {
+	return d->reportWhitespaceCharData;
+    } else if ( name == "http://trolltech.com/xml/features/report-start-end-entity" ) {
+	return d->reportEntities;
+    } else {
+	qWarning( "Unknown feature %s", name.latin1() );
+	if ( ok != 0 )
+	    *ok = FALSE;
+    }
+    return FALSE;
+}
+
+/*!
+    Sets the state of the feature \a name to \a value:
+
+    If the feature is not recognized, it is ignored.
+
+    The following features are supported:
+    \table
+    \header \i Feature \i Notes
+    \row \i \e http://xml.org/sax/features/namespaces
+	 \i If this feature is TRUE, namespace processing is
+	    performed.
+    \row \i \e http://xml.org/sax/features/namespace-prefixes
+	 \i If this feature is TRUE, the the original prefixed names
+	    and attributes used for namespace declarations are
+	    reported.
+    \row \i \e http://trolltech.com/xml/features/report-whitespace-only-CharData
+	 \i If this feature is TRUE, CharData that consist of
+	    whitespace only (and no other characters) are not reported
+	    via QXmlContentHandler::characters().
+    \row \i \e http://trolltech.com/xml/features/report-start-end-entity
+	 \i If this feature is TRUE, the parser reports
+	    QXmlContentHandler::startEntity() and
+	    QXmlContentHandler::endEntity() events. So character data
+	    might be reported in chunks. If this feature is FALSE, the
+	    parser does not report those events, but rather silently
+	    substitutes the entities and reports the character data in
+	    one chunk.
+    \endtable
+
+    \quotefile xml/tagreader-with-features/tagreader.cpp
+    \skipto reader
+    \printline reader
+    \skipto setFeature
+    \printline setFeature
+    \printline TRUE
+
+    (Code taken from xml/tagreader-with-features/tagreader.cpp)
+
+    \sa feature() hasFeature()
+*/
+void QXmlSimpleReader::setFeature( const QString& name, bool value )
+{
+    if        ( name == "http://xml.org/sax/features/namespaces" ) {
+	d->useNamespaces = value;
+    } else if ( name == "http://xml.org/sax/features/namespace-prefixes" ) {
+	d->useNamespacePrefixes = value;
+    } else if ( name == "http://trolltech.com/xml/features/report-whitespace-only-CharData" ) {
+	d->reportWhitespaceCharData = value;
+    } else if ( name == "http://trolltech.com/xml/features/report-start-end-entity" ) {
+	d->reportEntities = value;
+    } else {
+	qWarning( "Unknown feature %s", name.latin1() );
+    }
+}
+
+/*! \reimp
+*/
+bool QXmlSimpleReader::hasFeature( const QString& name ) const
+{
+    if ( name == "http://xml.org/sax/features/namespaces"
+	    || name == "http://xml.org/sax/features/namespace-prefixes"
+	    || name == "http://trolltech.com/xml/features/report-whitespace-only-CharData"
+	    || name == "http://trolltech.com/xml/features/report-start-end-entity" ) {
+	return TRUE;
+    } else {
+	return FALSE;
+    }
+}
+
+/*! \reimp
+*/
+void* QXmlSimpleReader::property( const QString&, bool *ok ) const
+{
+    if ( ok != 0 )
+	*ok = FALSE;
+    return 0;
+}
+
+/*! \reimp
+*/
+void QXmlSimpleReader::setProperty( const QString&, void* )
+{
+}
+
+/*!
+    \reimp
+*/
+bool QXmlSimpleReader::hasProperty( const QString& ) const
+{
+    return FALSE;
+}
+
+/*!
+    \reimp
+*/
+void QXmlSimpleReader::setEntityResolver( QXmlEntityResolver* handler )
+{ entityRes = handler; }
+
+/*!
+    \reimp
+*/
+QXmlEntityResolver* QXmlSimpleReader::entityResolver() const
+{ return entityRes; }
+
+/*!
+    \reimp
+*/
+void QXmlSimpleReader::setDTDHandler( QXmlDTDHandler* handler )
+{ dtdHnd = handler; }
+
+/*!
+    \reimp
+*/
+QXmlDTDHandler* QXmlSimpleReader::DTDHandler() const
+{ return dtdHnd; }
+
+/*!
+    \reimp
+*/
+void QXmlSimpleReader::setContentHandler( QXmlContentHandler* handler )
+{ contentHnd = handler; }
+
+/*!
+    \reimp
+*/
+QXmlContentHandler* QXmlSimpleReader::contentHandler() const
+{ return contentHnd; }
+
+/*!
+    \reimp
+*/
+void QXmlSimpleReader::setErrorHandler( QXmlErrorHandler* handler )
+{ errorHnd = handler; }
+
+/*!
+    \reimp
+*/
+QXmlErrorHandler* QXmlSimpleReader::errorHandler() const
+{ return errorHnd; }
+
+/*!
+    \reimp
+*/
+void QXmlSimpleReader::setLexicalHandler( QXmlLexicalHandler* handler )
+{ lexicalHnd = handler; }
+
+/*!
+    \reimp
+*/
+QXmlLexicalHandler* QXmlSimpleReader::lexicalHandler() const
+{ return lexicalHnd; }
+
+/*!
+    \reimp
+*/
+void QXmlSimpleReader::setDeclHandler( QXmlDeclHandler* handler )
+{ declHnd = handler; }
+
+/*!
+    \reimp
+*/
+QXmlDeclHandler* QXmlSimpleReader::declHandler() const
+{ return declHnd; }
+
+
+
+/*!
+    \reimp
+*/
+bool QXmlSimpleReader::parse( const QXmlInputSource& input )
+{
+    return parse( &input, FALSE );
+}
+
+/*!
+    \reimp
+*/
+bool QXmlSimpleReader::parse( const QXmlInputSource* input )
+{
+    return parse( input, FALSE );
+}
+
+/*!
+    Reads an XML document from \a input and parses it. Returns FALSE
+    if the parsing detects an error; otherwise returns TRUE.
+
+    If \a incremental is TRUE, the parser does not return FALSE when
+    it reaches the end of the \a input without reaching the end of the
+    XML file. Instead it stores the state of the parser so that
+    parsing can be continued at a later stage when more data is
+    available. You can use the function parseContinue() to continue
+    with parsing. This class stores a pointer to the input source \a
+    input and the parseContinue() function tries to read from that
+    input souce. This means that you should not delete the input
+    source \a input until you've finished your calls to
+    parseContinue(). If you call this function with \a incremental
+    TRUE whilst an incremental parse is in progress a new parsing
+    session will be started and the previous session lost.
+
+    If \a incremental is FALSE, this function behaves like the normal
+    parse function, i.e. it returns FALSE when the end of input is
+    reached without reaching the end of the XML file and the parsing
+    cannot be continued.
+
+    \sa parseContinue() QSocket
+*/
+bool QXmlSimpleReader::parse( const QXmlInputSource *input, bool incremental )
+{
+    init( input );
+    if ( incremental ) {
+	d->initIncrementalParsing();
+    } else {
+	delete d->parseStack;
+	d->parseStack = 0;
+    }
+    // call the handler
+    if ( contentHnd ) {
+	contentHnd->setDocumentLocator( d->locator );
+	if ( !contentHnd->startDocument() ) {
+	    reportParseError( contentHnd->errorString() );
+	    d->tags.clear();
+	    return FALSE;
+	}
+    }
+    return parseBeginOrContinue( 0, incremental );
+}
+
+/*!
+    Continues incremental parsing; this function reads the input from
+    the QXmlInputSource that was specified with the last parse()
+    command. To use this function, you \e must have called parse()
+    with the incremental argument set to TRUE.
+
+    Returns FALSE if a parsing error occurs; otherwise returns TRUE.
+
+    If the input source returns an empty string for the function
+    QXmlInputSource::data(), then this means that the end of the XML
+    file has been reached; this is quite important, especially if you
+    want to use the reader to parse more than one XML file.
+
+    The case of the end of the XML file being reached without having
+    finished parsing is not considered to be an error: you can
+    continue parsing at a later stage by calling this function again
+    when there is more data available to parse.
+
+    This function assumes that the end of the XML document is reached
+    if the QXmlInputSource::next() function returns
+    QXmlInputSource::EndOfDocument. If the parser has not finished
+    parsing when it encounters this symbol, it is an error and FALSE
+    is returned.
+
+    \sa parse() QXmlInputSource::next()
+*/
+bool QXmlSimpleReader::parseContinue()
+{
+    if ( d->parseStack == 0 )
+	return FALSE;
+    if ( d->parseStack->isEmpty() )
+	return FALSE;
+    initData();
+    int state = state = d->parseStack->top()->state;
+    d->parseStack->remove();
+    return parseBeginOrContinue( state, TRUE );
+}
+
+/*
+  Common part of parse() and parseContinue()
+*/
+bool QXmlSimpleReader::parseBeginOrContinue( int state, bool incremental )
+{
+    bool atEndOrig = atEnd();
+
+    if ( state==0 ) {
+	if ( !parseProlog() ) {
+	    if ( incremental && d->error.isNull() ) {
+		pushParseState( 0, 0 );
+		return TRUE;
+	    } else {
+		d->tags.clear();
+		return FALSE;
+	    }
+	}
+	state = 1;
+    }
+    if ( state==1 ) {
+	if ( !parseElement() ) {
+	    if ( incremental && d->error.isNull() ) {
+		pushParseState( 0, 1 );
+		return TRUE;
+	    } else {
+		d->tags.clear();
+		return FALSE;
+	    }
+	}
+	state = 2;
+    }
+    // parse Misc*
+    while ( !atEnd() ) {
+	if ( !parseMisc() ) {
+	    if ( incremental && d->error.isNull() ) {
+		pushParseState( 0, 2 );
+		return TRUE;
+	    } else {
+		d->tags.clear();
+		return FALSE;
+	    }
+	}
+    }
+    if ( !atEndOrig && incremental ) {
+	// we parsed something at all, so be prepared to come back later
+	pushParseState( 0, 2 );
+	return TRUE;
+    }
+    // is stack empty?
+    if ( !d->tags.isEmpty() && !d->error.isNull() ) {
+	reportParseError( XMLERR_UNEXPECTEDEOF );
+	d->tags.clear();
+	return FALSE;
+    }
+    // call the handler
+    if ( contentHnd ) {
+	delete d->parseStack;
+	d->parseStack = 0;
+	if ( !contentHnd->endDocument() ) {
+	    reportParseError( contentHnd->errorString() );
+	    return FALSE;
+	}
+    }
+    return TRUE;
+}
+
+//
+// The following private parse functions have another semantics for the return
+// value: They return TRUE iff parsing has finished successfully (i.e. the end
+// of the XML file must be reached!). If one of these functions return FALSE,
+// there is only an error when d->error.isNULL() is also FALSE.
+//
+
+/*
+  For the incremental parsing, it is very important that the parse...()
+  functions have a certain structure. Since it might be hard to understand how
+  they work, here is a description of the layout of these functions:
+
+    bool QXmlSimpleReader::parse...()
+    {
+(1)	const signed char Init             = 0;
+	...
+
+(2)	const signed char Inp...           = 0;
+	...
+
+(3)	static signed char table[3][2] = {
+	...
+	};
+	signed char state;
+	signed char input;
+
+(4)	if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+(4a)	...
+	} else {
+(4b)	...
+	}
+
+	for ( ; ; ) {
+(5)	    switch ( state ) {
+	    ...
+	    }
+
+(6)
+(6a)	    if ( atEnd() ) {
+		unexpectedEof( &QXmlSimpleReader::parseNmtoken, state );
+		return FALSE;
+	    }
+(6b)	    if ( is_NameChar(c) ) {
+	    ...
+	    }
+(7)	    state = table[state][input];
+
+(8)	    switch ( state ) {
+	    ...
+	    }
+	}
+    }
+
+  Explanation:
+  ad 1: constants for the states (used in the transition table)
+  ad 2: constants for the input (used in the transition table)
+  ad 3: the transition table for the state machine
+  ad 4: test if we are in a parseContinue() step
+	a) if no, do inititalizations
+	b) if yes, restore the state and call parse functions recursively
+  ad 5: Do some actions according to the state; from the logical execution
+	order, this code belongs after 8 (see there for an explanation)
+  ad 6: Check the character that is at the actual "cursor" position:
+	a) If we reached the EOF, report either error or push the state (in the
+	   case of incremental parsing).
+	b) Otherwise, set the input character constant for the transition
+	   table.
+  ad 7: Get the new state according to the input that was read.
+  ad 8: Do some actions according to the state. The last line in every case
+	statement reads new data (i.e. it move the cursor). This can also be
+	done by calling another parse...() funtion. If you need processing for
+	this state after that, you have to put it into the switch statement 5.
+	This ensures that you have a well defined re-entry point, when you ran
+	out of data.
+*/
+
+/*
+  Parses the prolog [22].
+*/
+bool QXmlSimpleReader::parseProlog()
+{
+    const signed char Init             = 0;
+    const signed char EatWS            = 1; // eat white spaces
+    const signed char Lt               = 2; // '<' read
+    const signed char Em               = 3; // '!' read
+    const signed char DocType          = 4; // read doctype
+    const signed char Comment          = 5; // read comment
+    const signed char CommentR         = 6; // same as Comment, but already reported
+    const signed char PInstr           = 7; // read PI
+    const signed char PInstrR          = 8; // same as PInstr, but already reported
+    const signed char Done             = 9;
+
+    const signed char InpWs            = 0;
+    const signed char InpLt            = 1; // <
+    const signed char InpQm            = 2; // ?
+    const signed char InpEm            = 3; // !
+    const signed char InpD             = 4; // D
+    const signed char InpDash          = 5; // -
+    const signed char InpUnknown       = 6;
+
+    static const signed char table[9][7] = {
+     /*  InpWs   InpLt  InpQm  InpEm  InpD      InpDash  InpUnknown */
+	{ EatWS,  Lt,    -1,    -1,    -1,       -1,       -1      }, // Init
+	{ -1,     Lt,    -1,    -1,    -1,       -1,       -1      }, // EatWS
+	{ -1,     -1,    PInstr,Em,    Done,     -1,       Done    }, // Lt
+	{ -1,     -1,    -1,    -1,    DocType,  Comment,  -1      }, // Em
+	{ EatWS,  Lt,    -1,    -1,    -1,       -1,       -1      }, // DocType
+	{ EatWS,  Lt,    -1,    -1,    -1,       -1,       -1      }, // Comment
+	{ EatWS,  Lt,    -1,    -1,    -1,       -1,       -1      }, // CommentR
+	{ EatWS,  Lt,    -1,    -1,    -1,       -1,       -1      }, // PInstr
+	{ EatWS,  Lt,    -1,    -1,    -1,       -1,       -1      }  // PInstrR
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	d->xmldecl_possible = TRUE;
+	d->doctype_read = FALSE;
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseProlog (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseProlog, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case DocType:
+		if ( d->doctype_read ) {
+		    reportParseError( XMLERR_MORETHANONEDOCTYPE );
+		    return FALSE;
+		} else {
+		    d->doctype_read = FALSE;
+		}
+		break;
+	    case Comment:
+		if ( lexicalHnd ) {
+		    if ( !lexicalHnd->comment( string() ) ) {
+			reportParseError( lexicalHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		state = CommentR;
+		break;
+	    case PInstr:
+		// call the handler
+		if ( contentHnd ) {
+		    if ( d->xmldecl_possible && !d->xmlVersion.isEmpty() ) {
+			QString value( "version = '" );
+			value += d->xmlVersion;
+			value += "'";
+			if ( !d->encoding.isEmpty() ) {
+			    value += " encoding = '";
+			    value += d->encoding;
+			    value += "'";
+			}
+			if ( d->standalone == QXmlSimpleReaderPrivate::Yes ) {
+			    value += " standalone = 'yes'";
+			} else if ( d->standalone == QXmlSimpleReaderPrivate::No ) {
+			    value += " standalone = 'no'";
+			}
+			if ( !contentHnd->processingInstruction( "xml", value ) ) {
+			    reportParseError( contentHnd->errorString() );
+			    return FALSE;
+			}
+		    } else {
+			if ( !contentHnd->processingInstruction( name(), string() ) ) {
+			    reportParseError( contentHnd->errorString() );
+			    return FALSE;
+			}
+		    }
+		}
+		// XML declaration only on first position possible
+		d->xmldecl_possible = FALSE;
+		state = PInstrR;
+		break;
+	    case Done:
+		return TRUE;
+	    case -1:
+		reportParseError( XMLERR_ERRORPARSINGELEMENT );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseProlog, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == '<' ) {
+	    input = InpLt;
+	} else if ( c == '?' ) {
+	    input = InpQm;
+	} else if ( c == '!' ) {
+	    input = InpEm;
+	} else if ( c == 'D' ) {
+	    input = InpD;
+	} else if ( c == '-' ) {
+	    input = InpDash;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case EatWS:
+		// XML declaration only on first position possible
+		d->xmldecl_possible = FALSE;
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseProlog, state );
+		    return FALSE;
+		}
+		break;
+	    case Lt:
+		next();
+		break;
+	    case Em:
+		// XML declaration only on first position possible
+		d->xmldecl_possible = FALSE;
+		next();
+		break;
+	    case DocType:
+		if ( !parseDoctype() ) {
+		    parseFailed( &QXmlSimpleReader::parseProlog, state );
+		    return FALSE;
+		}
+		break;
+	    case Comment:
+	    case CommentR:
+		if ( !parseComment() ) {
+		    parseFailed( &QXmlSimpleReader::parseProlog, state );
+		    return FALSE;
+		}
+		break;
+	    case PInstr:
+	    case PInstrR:
+		d->parsePI_xmldecl = d->xmldecl_possible;
+		if ( !parsePI() ) {
+		    parseFailed( &QXmlSimpleReader::parseProlog, state );
+		    return FALSE;
+		}
+		break;
+	}
+    }
+}
+
+/*
+  Parse an element [39].
+
+  Precondition: the opening '<' is already read.
+*/
+bool QXmlSimpleReader::parseElement()
+{
+    const signed char Init             =  0;
+    const signed char ReadName         =  1;
+    const signed char Ws1              =  2;
+    const signed char STagEnd          =  3;
+    const signed char STagEnd2         =  4;
+    const signed char ETagBegin        =  5;
+    const signed char ETagBegin2       =  6;
+    const signed char Ws2              =  7;
+    const signed char EmptyTag         =  8;
+    const signed char Attrib           =  9;
+    const signed char AttribPro        = 10; // like Attrib, but processAttribute was already called
+    const signed char Ws3              = 11;
+    const signed char Done             = 12;
+
+    const signed char InpWs            = 0; // whitespace
+    const signed char InpNameBe        = 1; // is_NameBeginning()
+    const signed char InpGt            = 2; // >
+    const signed char InpSlash         = 3; // /
+    const signed char InpUnknown       = 4;
+
+    static const signed char table[12][5] = {
+     /*  InpWs      InpNameBe    InpGt        InpSlash     InpUnknown */
+	{ -1,        ReadName,    -1,          -1,          -1        }, // Init
+	{ Ws1,       Attrib,      STagEnd,     EmptyTag,    -1        }, // ReadName
+	{ -1,        Attrib,      STagEnd,     EmptyTag,    -1        }, // Ws1
+	{ STagEnd2,  STagEnd2,    STagEnd2,    STagEnd2,    STagEnd2  }, // STagEnd
+	{ -1,        -1,          -1,          ETagBegin,   -1        }, // STagEnd2
+	{ -1,        ETagBegin2,  -1,          -1,          -1        }, // ETagBegin
+	{ Ws2,       -1,          Done,        -1,          -1        }, // ETagBegin2
+	{ -1,        -1,          Done,        -1,          -1        }, // Ws2
+	{ -1,        -1,          Done,        -1,          -1        }, // EmptyTag
+	{ Ws3,       Attrib,      STagEnd,     EmptyTag,    -1        }, // Attrib
+	{ Ws3,       Attrib,      STagEnd,     EmptyTag,    -1        }, // AttribPro
+	{ -1,        Attrib,      STagEnd,     EmptyTag,    -1        }  // Ws3
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseElement (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseElement, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case ReadName:
+		// store it on the stack
+		d->tags.push( name() );
+		// empty the attributes
+		d->attList.clear();
+		if ( d->useNamespaces ) {
+		    d->namespaceSupport.pushContext();
+		}
+		break;
+	    case ETagBegin2:
+		if ( !processElementETagBegin2() )
+		    return FALSE;
+		break;
+	    case Attrib:
+		if ( !processElementAttribute() )
+		    return FALSE;
+		state = AttribPro;
+		break;
+	    case Done:
+		return TRUE;
+	    case -1:
+		reportParseError( XMLERR_ERRORPARSINGELEMENT );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseElement, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( is_NameBeginning(c) ) {
+	    input = InpNameBe;
+	} else if ( c == '>' ) {
+	    input = InpGt;
+	} else if ( c == '/' ) {
+	    input = InpSlash;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case ReadName:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseElement, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws1:
+	    case Ws2:
+	    case Ws3:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseElement, state );
+		    return FALSE;
+		}
+		break;
+	    case STagEnd:
+		// call the handler
+		if ( contentHnd ) {
+		    if ( d->useNamespaces ) {
+			QString uri, lname;
+			d->namespaceSupport.processName( d->tags.top(), FALSE, uri, lname );
+			if ( !contentHnd->startElement( uri, lname, d->tags.top(), d->attList ) ) {
+			    reportParseError( contentHnd->errorString() );
+			    return FALSE;
+			}
+		    } else {
+			if ( !contentHnd->startElement( QString::null, QString::null, d->tags.top(), d->attList ) ) {
+			    reportParseError( contentHnd->errorString() );
+			    return FALSE;
+			}
+		    }
+		}
+		next();
+		break;
+	    case STagEnd2:
+		if ( !parseContent() ) {
+		    parseFailed( &QXmlSimpleReader::parseElement, state );
+		    return FALSE;
+		}
+		break;
+	    case ETagBegin:
+		next();
+		break;
+	    case ETagBegin2:
+		// get the name of the tag
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseElement, state );
+		    return FALSE;
+		}
+		break;
+	    case EmptyTag:
+		if  ( d->tags.isEmpty() ) {
+		    reportParseError( XMLERR_TAGMISMATCH );
+		    return FALSE;
+		}
+		if ( !processElementEmptyTag() )
+		    return FALSE;
+		next();
+		break;
+	    case Attrib:
+	    case AttribPro:
+		// get name and value of attribute
+		if ( !parseAttribute() ) {
+		    parseFailed( &QXmlSimpleReader::parseElement, state );
+		    return FALSE;
+		}
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+/*
+  Helper to break down the size of the code in the case statement.
+  Return FALSE on error, otherwise TRUE.
+*/
+bool QXmlSimpleReader::processElementEmptyTag()
+{
+    QString uri, lname;
+    // pop the stack and call the handler
+    if ( contentHnd ) {
+	if ( d->useNamespaces ) {
+	    // report startElement first...
+	    d->namespaceSupport.processName( d->tags.top(), FALSE, uri, lname );
+	    if ( !contentHnd->startElement( uri, lname, d->tags.top(), d->attList ) ) {
+		reportParseError( contentHnd->errorString() );
+		return FALSE;
+	    }
+	    // ... followed by endElement...
+	    if ( !contentHnd->endElement( uri, lname, d->tags.pop() ) ) {
+		reportParseError( contentHnd->errorString() );
+		return FALSE;
+	    }
+	    // ... followed by endPrefixMapping
+	    QStringList prefixesBefore, prefixesAfter;
+	    if ( contentHnd ) {
+		prefixesBefore = d->namespaceSupport.prefixes();
+	    }
+	    d->namespaceSupport.popContext();
+	    // call the handler for prefix mapping
+	    prefixesAfter = d->namespaceSupport.prefixes();
+	    for ( QStringList::Iterator it = prefixesBefore.begin(); it != prefixesBefore.end(); ++it ) {
+		if ( prefixesAfter.contains(*it) == 0 ) {
+		    if ( !contentHnd->endPrefixMapping( *it ) ) {
+			reportParseError( contentHnd->errorString() );
+			return FALSE;
+		    }
+		}
+	    }
+	} else {
+	    // report startElement first...
+	    if ( !contentHnd->startElement( QString::null, QString::null, d->tags.top(), d->attList ) ) {
+		reportParseError( contentHnd->errorString() );
+		return FALSE;
+	    }
+	    // ... followed by endElement
+	    if ( !contentHnd->endElement( QString::null, QString::null, d->tags.pop() ) ) {
+		reportParseError( contentHnd->errorString() );
+		return FALSE;
+	    }
+	}
+    } else {
+	d->tags.pop_back();
+	d->namespaceSupport.popContext();
+    }
+    return TRUE;
+}
+/*
+  Helper to break down the size of the code in the case statement.
+  Return FALSE on error, otherwise TRUE.
+*/
+bool QXmlSimpleReader::processElementETagBegin2()
+{
+    // pop the stack and compare it with the name
+    if ( d->tags.pop() != name() ) {
+	reportParseError( XMLERR_TAGMISMATCH );
+	return FALSE;
+    }
+    // call the handler
+    if ( contentHnd ) {
+	if ( d->useNamespaces ) {
+	    QString uri, lname;
+	    d->namespaceSupport.processName( name(), FALSE, uri, lname );
+	    if ( !contentHnd->endElement( uri, lname, name() ) ) {
+		reportParseError( contentHnd->errorString() );
+		return FALSE;
+	    }
+	} else {
+	    if ( !contentHnd->endElement( QString::null, QString::null, name() ) ) {
+		reportParseError( contentHnd->errorString() );
+		return FALSE;
+	    }
+	}
+    }
+    if ( d->useNamespaces ) {
+	QStringList prefixesBefore, prefixesAfter;
+	if ( contentHnd ) {
+	    prefixesBefore = d->namespaceSupport.prefixes();
+	}
+	d->namespaceSupport.popContext();
+	// call the handler for prefix mapping
+	if ( contentHnd ) {
+	    prefixesAfter = d->namespaceSupport.prefixes();
+	    for ( QStringList::Iterator it = prefixesBefore.begin(); it != prefixesBefore.end(); ++it ) {
+		if ( prefixesAfter.contains(*it) == 0 ) {
+		    if ( !contentHnd->endPrefixMapping( *it ) ) {
+			reportParseError( contentHnd->errorString() );
+			return FALSE;
+		    }
+		}
+	    }
+	}
+    }
+    return TRUE;
+}
+/*
+  Helper to break down the size of the code in the case statement.
+  Return FALSE on error, otherwise TRUE.
+*/
+bool QXmlSimpleReader::processElementAttribute()
+{
+    QString uri, lname, prefix;
+    // add the attribute to the list
+    if ( d->useNamespaces ) {
+	// is it a namespace declaration?
+	d->namespaceSupport.splitName( name(), prefix, lname );
+	if ( prefix == "xmlns" ) {
+	    // namespace declaration
+	    d->namespaceSupport.setPrefix( lname, string() );
+	    if ( d->useNamespacePrefixes ) {
+		// according to http://www.w3.org/2000/xmlns/, the "prefix"
+		// xmlns maps to the namespace name
+		// http://www.w3.org/2000/xmlns/
+		d->attList.append( name(), "http://www.w3.org/2000/xmlns/", lname, string() );
+	    }
+	    // call the handler for prefix mapping
+	    if ( contentHnd ) {
+		if ( !contentHnd->startPrefixMapping( lname, string() ) ) {
+		    reportParseError( contentHnd->errorString() );
+		    return FALSE;
+		}
+	    }
+	} else {
+	    // no namespace delcaration
+	    d->namespaceSupport.processName( name(), TRUE, uri, lname );
+	    d->attList.append( name(), uri, lname, string() );
+	}
+    } else {
+	// no namespace support
+	d->attList.append( name(), QString::null, QString::null, string() );
+    }
+    return TRUE;
+}
+
+/*
+  Parse a content [43].
+
+  A content is only used between tags. If a end tag is found the < is already
+  read and the head stand on the '/' of the end tag '</name>'.
+*/
+bool QXmlSimpleReader::parseContent()
+{
+    const signed char Init             =  0;
+    const signed char ChD              =  1; // CharData
+    const signed char ChD1             =  2; // CharData help state
+    const signed char ChD2             =  3; // CharData help state
+    const signed char Ref              =  4; // Reference
+    const signed char Lt               =  5; // '<' read
+    const signed char PInstr           =  6; // PI
+    const signed char PInstrR          =  7; // same as PInstr, but already reported
+    const signed char Elem             =  8; // Element
+    const signed char Em               =  9; // '!' read
+    const signed char Com              = 10; // Comment
+    const signed char ComR             = 11; // same as Com, but already reported
+    const signed char CDS              = 12; // CDSect
+    const signed char CDS1             = 13; // read a CDSect
+    const signed char CDS2             = 14; // read a CDSect (help state)
+    const signed char CDS3             = 15; // read a CDSect (help state)
+    const signed char Done             = 16; // finished reading content
+
+    const signed char InpLt            = 0; // <
+    const signed char InpGt            = 1; // >
+    const signed char InpSlash         = 2; // /
+    const signed char InpQMark         = 3; // ?
+    const signed char InpEMark         = 4; // !
+    const signed char InpAmp           = 5; // &
+    const signed char InpDash          = 6; // -
+    const signed char InpOpenB         = 7; // [
+    const signed char InpCloseB        = 8; // ]
+    const signed char InpUnknown       = 9;
+
+    static const signed char mapCLT2FSMChar[] = {
+	InpUnknown, // white space
+	InpUnknown, // %
+	InpAmp,     // &
+	InpGt,      // >
+	InpLt,      // <
+	InpSlash,   // /
+	InpQMark,   // ?
+	InpEMark,   // !
+	InpDash,    // -
+	InpCloseB,  // ]
+	InpOpenB,   // [
+	InpUnknown, // =
+	InpUnknown, // "
+	InpUnknown, // '
+	InpUnknown  // unknown
+    };
+
+    static const signed char table[16][10] = {
+     /*  InpLt  InpGt  InpSlash  InpQMark  InpEMark  InpAmp  InpDash  InpOpenB  InpCloseB  InpUnknown */
+	{ Lt,    ChD,   ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD1,      ChD  }, // Init
+	{ Lt,    ChD,   ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD1,      ChD  }, // ChD
+	{ Lt,    ChD,   ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD2,      ChD  }, // ChD1
+	{ Lt,    -1,    ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD2,      ChD  }, // ChD2
+	{ Lt,    ChD,   ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD,       ChD  }, // Ref (same as Init)
+	{ -1,    -1,    Done,     PInstr,   Em,       -1,     -1,      -1,       -1,        Elem }, // Lt
+	{ Lt,    ChD,   ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD,       ChD  }, // PInstr (same as Init)
+	{ Lt,    ChD,   ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD,       ChD  }, // PInstrR
+	{ Lt,    ChD,   ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD,       ChD  }, // Elem (same as Init)
+	{ -1,    -1,    -1,       -1,       -1,       -1,     Com,     CDS,      -1,        -1   }, // Em
+	{ Lt,    ChD,   ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD,       ChD  }, // Com (same as Init)
+	{ Lt,    ChD,   ChD,      ChD,      ChD,      Ref,    ChD,     ChD,      ChD,       ChD  }, // ComR
+	{ CDS1,  CDS1,  CDS1,     CDS1,     CDS1,     CDS1,   CDS1,    CDS1,     CDS2,      CDS1 }, // CDS
+	{ CDS1,  CDS1,  CDS1,     CDS1,     CDS1,     CDS1,   CDS1,    CDS1,     CDS2,      CDS1 }, // CDS1
+	{ CDS1,  CDS1,  CDS1,     CDS1,     CDS1,     CDS1,   CDS1,    CDS1,     CDS3,      CDS1 }, // CDS2
+	{ CDS1,  Init,  CDS1,     CDS1,     CDS1,     CDS1,   CDS1,    CDS1,     CDS3,      CDS1 }  // CDS3
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	d->contentCharDataRead = FALSE;
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseContent (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseContent, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Ref:
+		if ( !d->contentCharDataRead)
+		    d->contentCharDataRead = d->parseReference_charDataRead;
+		break;
+	    case PInstr:
+		if ( contentHnd ) {
+		    if ( !contentHnd->processingInstruction(name(),string()) ) {
+			reportParseError( contentHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		state = PInstrR;
+		break;
+	    case Com:
+		if ( lexicalHnd ) {
+		    if ( !lexicalHnd->comment( string() ) ) {
+			reportParseError( lexicalHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		state = ComR;
+		break;
+	    case CDS:
+		stringClear();
+		break;
+	    case CDS2:
+		if ( !atEnd() && c != ']' )
+		    stringAddC( ']' );
+		break;
+	    case CDS3:
+		// test if this skipping was legal
+		if ( !atEnd() ) {
+		    if ( c == '>' ) {
+			// the end of the CDSect
+			if ( lexicalHnd ) {
+			    if ( !lexicalHnd->startCDATA() ) {
+				reportParseError( lexicalHnd->errorString() );
+				return FALSE;
+			    }
+			}
+			if ( contentHnd ) {
+			    if ( !contentHnd->characters( string() ) ) {
+				reportParseError( contentHnd->errorString() );
+				return FALSE;
+			    }
+			}
+			if ( lexicalHnd ) {
+			    if ( !lexicalHnd->endCDATA() ) {
+				reportParseError( lexicalHnd->errorString() );
+				return FALSE;
+			    }
+			}
+		    } else if (c == ']') {
+			// three or more ']'
+			stringAddC( ']' );
+		    } else {
+			// after ']]' comes another character
+			stringAddC( ']' );
+			stringAddC( ']' );
+		    }
+		}
+		break;
+	    case Done:
+		// call the handler for CharData
+		if ( contentHnd ) {
+		    if ( d->contentCharDataRead ) {
+			if ( d->reportWhitespaceCharData || !string().simplifyWhiteSpace().isEmpty() ) {
+			    if ( !contentHnd->characters( string() ) ) {
+				reportParseError( contentHnd->errorString() );
+				return FALSE;
+			    }
+			}
+		    }
+		}
+		// Done
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_ERRORPARSINGCONTENT );
+		return FALSE;
+	}
+
+	// get input (use lookup-table instead of nested ifs for performance
+	// reasons)
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseContent, state );
+	    return FALSE;
+	}
+	if ( c.row() ) {
+	    input = InpUnknown;
+	} else {
+	    input = mapCLT2FSMChar[ charLookupTable[ c.cell() ] ];
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Init:
+		// skip the ending '>' of a CDATASection
+		next();
+		break;
+	    case ChD:
+		// on first call: clear string
+		if ( !d->contentCharDataRead ) {
+		    d->contentCharDataRead = TRUE;
+		    stringClear();
+		}
+		stringAddC();
+		if ( d->reportEntities ) {
+		    if ( !reportEndEntities() )
+			return FALSE;
+		}
+		next();
+		break;
+	    case ChD1:
+		// on first call: clear string
+		if ( !d->contentCharDataRead ) {
+		    d->contentCharDataRead = TRUE;
+		    stringClear();
+		}
+		stringAddC();
+		if ( d->reportEntities ) {
+		    if ( !reportEndEntities() )
+			return FALSE;
+		}
+		next();
+		break;
+	    case ChD2:
+		stringAddC();
+		if ( d->reportEntities ) {
+		    if ( !reportEndEntities() )
+			return FALSE;
+		}
+		next();
+		break;
+	    case Ref:
+		if ( !d->contentCharDataRead) {
+		    // reference may be CharData; so clear string to be safe
+		    stringClear();
+		    d->parseReference_context = InContent;
+		    if ( !parseReference() ) {
+			parseFailed( &QXmlSimpleReader::parseContent, state );
+			return FALSE;
+		    }
+		} else {
+		    if ( d->reportEntities ) {
+			// report character data in chunks
+			if ( contentHnd ) {
+			    if ( d->reportWhitespaceCharData || !string().simplifyWhiteSpace().isEmpty() ) {
+				if ( !contentHnd->characters( string() ) ) {
+				    reportParseError( contentHnd->errorString() );
+				    return FALSE;
+				}
+			    }
+			}
+			stringClear();
+		    }
+		    d->parseReference_context = InContent;
+		    if ( !parseReference() ) {
+			parseFailed( &QXmlSimpleReader::parseContent, state );
+			return FALSE;
+		    }
+		}
+		break;
+	    case Lt:
+		// call the handler for CharData
+		if ( contentHnd ) {
+		    if ( d->contentCharDataRead ) {
+			if ( d->reportWhitespaceCharData || !string().simplifyWhiteSpace().isEmpty() ) {
+			    if ( !contentHnd->characters( string() ) ) {
+				reportParseError( contentHnd->errorString() );
+				return FALSE;
+			    }
+			}
+		    }
+		}
+		d->contentCharDataRead = FALSE;
+		next();
+		break;
+	    case PInstr:
+	    case PInstrR:
+		d->parsePI_xmldecl = FALSE;
+		if ( !parsePI() ) {
+		    parseFailed( &QXmlSimpleReader::parseContent, state );
+		    return FALSE;
+		}
+		break;
+	    case Elem:
+		if ( !parseElement() ) {
+		    parseFailed( &QXmlSimpleReader::parseContent, state );
+		    return FALSE;
+		}
+		break;
+	    case Em:
+		next();
+		break;
+	    case Com:
+	    case ComR:
+		if ( !parseComment() ) {
+		    parseFailed( &QXmlSimpleReader::parseContent, state );
+		    return FALSE;
+		}
+		break;
+	    case CDS:
+		d->parseString_s = "[CDATA[";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseContent, state );
+		    return FALSE;
+		}
+		break;
+	    case CDS1:
+		stringAddC();
+		next();
+		break;
+	    case CDS2:
+		// skip ']'
+		next();
+		break;
+	    case CDS3:
+		// skip ']'...
+		next();
+		break;
+	}
+    }
+}
+bool QXmlSimpleReader::reportEndEntities()
+{
+    int count = (int)d->xmlRef.count();
+    while ( count != 0 && d->xmlRef.top().isEmpty() ) {
+	if ( contentHnd ) {
+	    if ( d->reportWhitespaceCharData || !string().simplifyWhiteSpace().isEmpty() ) {
+		if ( !contentHnd->characters( string() ) ) {
+		    reportParseError( contentHnd->errorString() );
+		    return FALSE;
+		}
+	    }
+	}
+	stringClear();
+	if ( lexicalHnd ) {
+	    if ( !lexicalHnd->endEntity(d->xmlRefName.top()) ) {
+		reportParseError( lexicalHnd->errorString() );
+		return FALSE;
+	    }
+	}
+	d->xmlRef.pop_back();
+	d->xmlRefName.pop_back();
+	count--;
+    }
+    return TRUE;
+}
+
+/*
+  Parse Misc [27].
+*/
+bool QXmlSimpleReader::parseMisc()
+{
+    const signed char Init             = 0;
+    const signed char Lt               = 1; // '<' was read
+    const signed char Comment          = 2; // read comment
+    const signed char eatWS            = 3; // eat whitespaces
+    const signed char PInstr           = 4; // read PI
+    const signed char Comment2         = 5; // read comment
+
+    const signed char InpWs            = 0; // S
+    const signed char InpLt            = 1; // <
+    const signed char InpQm            = 2; // ?
+    const signed char InpEm            = 3; // !
+    const signed char InpUnknown       = 4;
+
+    static const signed char table[3][5] = {
+     /*  InpWs   InpLt  InpQm  InpEm     InpUnknown */
+	{ eatWS,  Lt,    -1,    -1,       -1        }, // Init
+	{ -1,     -1,    PInstr,Comment,  -1        }, // Lt
+	{ -1,     -1,    -1,    -1,       Comment2  }  // Comment
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseMisc (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseMisc, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case eatWS:
+		return TRUE;
+	    case PInstr:
+		if ( contentHnd ) {
+		    if ( !contentHnd->processingInstruction(name(),string()) ) {
+			reportParseError( contentHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		return TRUE;
+	    case Comment2:
+		if ( lexicalHnd ) {
+		    if ( !lexicalHnd->comment( string() ) ) {
+			reportParseError( lexicalHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseMisc, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == '<' ) {
+	    input = InpLt;
+	} else if ( c == '?' ) {
+	    input = InpQm;
+	} else if ( c == '!' ) {
+	    input = InpEm;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case eatWS:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseMisc, state );
+		    return FALSE;
+		}
+		break;
+	    case Lt:
+		next();
+		break;
+	    case PInstr:
+		d->parsePI_xmldecl = FALSE;
+		if ( !parsePI() ) {
+		    parseFailed( &QXmlSimpleReader::parseMisc, state );
+		    return FALSE;
+		}
+		break;
+	    case Comment:
+		next();
+		break;
+	    case Comment2:
+		if ( !parseComment() ) {
+		    parseFailed( &QXmlSimpleReader::parseMisc, state );
+		    return FALSE;
+		}
+		break;
+	}
+    }
+}
+
+/*
+  Parse a processing instruction [16].
+
+  If xmldec is TRUE, it tries to parse a PI or a XML declaration [23].
+
+  Precondition: the beginning '<' of the PI is already read and the head stand
+  on the '?' of '<?'.
+
+  If this funktion was successful, the head-position is on the first
+  character after the PI.
+*/
+bool QXmlSimpleReader::parsePI()
+{
+    const signed char Init             =  0;
+    const signed char QmI              =  1; // ? was read
+    const signed char Name             =  2; // read Name
+    const signed char XMLDecl          =  3; // read XMLDecl
+    const signed char Ws1              =  4; // eat ws after "xml" of XMLDecl
+    const signed char PInstr           =  5; // read PI
+    const signed char Ws2              =  6; // eat ws after Name of PI
+    const signed char Version          =  7; // read versionInfo
+    const signed char Ws3              =  8; // eat ws after versionInfo
+    const signed char EorSD            =  9; // read EDecl or SDDecl
+    const signed char Ws4              = 10; // eat ws after EDecl or SDDecl
+    const signed char SD               = 11; // read SDDecl
+    const signed char Ws5              = 12; // eat ws after SDDecl
+    const signed char ADone            = 13; // almost done
+    const signed char Char             = 14; // Char was read
+    const signed char Qm               = 15; // Qm was read
+    const signed char Done             = 16; // finished reading content
+
+    const signed char InpWs            = 0; // whitespace
+    const signed char InpNameBe        = 1; // is_nameBeginning()
+    const signed char InpGt            = 2; // >
+    const signed char InpQm            = 3; // ?
+    const signed char InpUnknown       = 4;
+
+    static const signed char table[16][5] = {
+     /*  InpWs,  InpNameBe  InpGt  InpQm   InpUnknown  */
+	{ -1,     -1,        -1,    QmI,    -1     }, // Init
+	{ -1,     Name,      -1,    -1,     -1     }, // QmI
+	{ -1,     -1,        -1,    -1,     -1     }, // Name (this state is left not through input)
+	{ Ws1,    -1,        -1,    -1,     -1     }, // XMLDecl
+	{ -1,     Version,   -1,    -1,     -1     }, // Ws1
+	{ Ws2,    -1,        -1,    Qm,     -1     }, // PInstr
+	{ Char,   Char,      Char,  Qm,     Char   }, // Ws2
+	{ Ws3,    -1,        -1,    ADone,  -1     }, // Version
+	{ -1,     EorSD,     -1,    ADone,  -1     }, // Ws3
+	{ Ws4,    -1,        -1,    ADone,  -1     }, // EorSD
+	{ -1,     SD,        -1,    ADone,  -1     }, // Ws4
+	{ Ws5,    -1,        -1,    ADone,  -1     }, // SD
+	{ -1,     -1,        -1,    ADone,  -1     }, // Ws5
+	{ -1,     -1,        Done,  -1,     -1     }, // ADone
+	{ Char,   Char,      Char,  Qm,     Char   }, // Char
+	{ Char,   Char,      Done,  Qm,     Char   }, // Qm
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parsePI (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parsePI, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Name:
+		// test what name was read and determine the next state
+		// (not very beautiful, I admit)
+		if ( name().lower() == "xml" ) {
+		    if ( d->parsePI_xmldecl && name()=="xml" ) {
+			state = XMLDecl;
+		    } else {
+			reportParseError( XMLERR_INVALIDNAMEFORPI );
+			return FALSE;
+		    }
+		} else {
+		    state = PInstr;
+		    stringClear();
+		}
+		break;
+	    case Version:
+		// get version (syntax like an attribute)
+		if ( name() != "version" ) {
+		    reportParseError( XMLERR_VERSIONEXPECTED );
+		    return FALSE;
+		}
+		d->xmlVersion = string();
+		break;
+	    case EorSD:
+		// get the EDecl or SDDecl (syntax like an attribute)
+		if        ( name() == "standalone" ) {
+		    if ( string()=="yes" ) {
+			d->standalone = QXmlSimpleReaderPrivate::Yes;
+		    } else if ( string()=="no" ) {
+			d->standalone = QXmlSimpleReaderPrivate::No;
+		    } else {
+			reportParseError( XMLERR_WRONGVALUEFORSDECL );
+			return FALSE;
+		    }
+		} else if ( name() == "encoding" ) {
+		    d->encoding = string();
+		} else {
+		    reportParseError( XMLERR_EDECLORSDDECLEXPECTED );
+		    return FALSE;
+		}
+		break;
+	    case SD:
+		if ( name() != "standalone" ) {
+		    reportParseError( XMLERR_SDDECLEXPECTED );
+		    return FALSE;
+		}
+		if ( string()=="yes" ) {
+		    d->standalone = QXmlSimpleReaderPrivate::Yes;
+		} else if ( string()=="no" ) {
+		    d->standalone = QXmlSimpleReaderPrivate::No;
+		} else {
+		    reportParseError( XMLERR_WRONGVALUEFORSDECL );
+		    return FALSE;
+		}
+		break;
+	    case Qm:
+		// test if the skipping was legal
+		if ( !atEnd() && c != '>' )
+		    stringAddC( '?' );
+		break;
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parsePI, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( is_NameBeginning(c) ) {
+	    input = InpNameBe;
+	} else if ( c == '>' ) {
+	    input = InpGt;
+	} else if ( c == '?' ) {
+	    input = InpQm;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case QmI:
+		next();
+		break;
+	    case Name:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parsePI, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws1:
+	    case Ws2:
+	    case Ws3:
+	    case Ws4:
+	    case Ws5:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parsePI, state );
+		    return FALSE;
+		}
+		break;
+	    case Version:
+		if ( !parseAttribute() ) {
+		    parseFailed( &QXmlSimpleReader::parsePI, state );
+		    return FALSE;
+		}
+		break;
+	    case EorSD:
+		if ( !parseAttribute() ) {
+		    parseFailed( &QXmlSimpleReader::parsePI, state );
+		    return FALSE;
+		}
+		break;
+	    case SD:
+		// get the SDDecl (syntax like an attribute)
+		if ( d->standalone != QXmlSimpleReaderPrivate::Unknown ) {
+		    // already parsed the standalone declaration
+		    reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		    return FALSE;
+		}
+		if ( !parseAttribute() ) {
+		    parseFailed( &QXmlSimpleReader::parsePI, state );
+		    return FALSE;
+		}
+		break;
+	    case ADone:
+		next();
+		break;
+	    case Char:
+		stringAddC();
+		next();
+		break;
+	    case Qm:
+		// skip the '?'
+		next();
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a document type definition (doctypedecl [28]).
+
+  Precondition: the beginning '<!' of the doctype is already read the head
+  stands on the 'D' of '<!DOCTYPE'.
+
+  If this funktion was successful, the head-position is on the first
+  character after the document type definition.
+*/
+bool QXmlSimpleReader::parseDoctype()
+{
+    const signed char Init             =  0;
+    const signed char Doctype          =  1; // read the doctype
+    const signed char Ws1              =  2; // eat_ws
+    const signed char Doctype2         =  3; // read the doctype, part 2
+    const signed char Ws2              =  4; // eat_ws
+    const signed char Sys              =  5; // read SYSTEM or PUBLIC
+    const signed char Ws3              =  6; // eat_ws
+    const signed char MP               =  7; // markupdecl or PEReference
+    const signed char MPR              =  8; // same as MP, but already reported
+    const signed char PER              =  9; // PERReference
+    const signed char Mup              = 10; // markupdecl
+    const signed char Ws4              = 11; // eat_ws
+    const signed char MPE              = 12; // end of markupdecl or PEReference
+    const signed char Done             = 13;
+
+    const signed char InpWs            = 0;
+    const signed char InpD             = 1; // 'D'
+    const signed char InpS             = 2; // 'S' or 'P'
+    const signed char InpOB            = 3; // [
+    const signed char InpCB            = 4; // ]
+    const signed char InpPer           = 5; // %
+    const signed char InpGt            = 6; // >
+    const signed char InpUnknown       = 7;
+
+    static const signed char table[13][8] = {
+     /*  InpWs,  InpD       InpS       InpOB  InpCB  InpPer InpGt  InpUnknown */
+	{ -1,     Doctype,   -1,        -1,    -1,    -1,    -1,    -1        }, // Init
+	{ Ws1,    -1,        -1,        -1,    -1,    -1,    -1,    -1        }, // Doctype
+	{ -1,     Doctype2,  Doctype2,  -1,    -1,    -1,    -1,    Doctype2  }, // Ws1
+	{ Ws2,    -1,        Sys,       MP,    -1,    -1,    Done,  -1        }, // Doctype2
+	{ -1,     -1,        Sys,       MP,    -1,    -1,    Done,  -1        }, // Ws2
+	{ Ws3,    -1,        -1,        MP,    -1,    -1,    Done,  -1        }, // Sys
+	{ -1,     -1,        -1,        MP,    -1,    -1,    Done,  -1        }, // Ws3
+	{ -1,     -1,        -1,        -1,    MPE,   PER,   -1,    Mup       }, // MP
+	{ -1,     -1,        -1,        -1,    MPE,   PER,   -1,    Mup       }, // MPR
+	{ Ws4,    -1,        -1,        -1,    MPE,   PER,   -1,    Mup       }, // PER
+	{ Ws4,    -1,        -1,        -1,    MPE,   PER,   -1,    Mup       }, // Mup
+	{ -1,     -1,        -1,        -1,    MPE,   PER,   -1,    Mup       }, // Ws4
+	{ -1,     -1,        -1,        -1,    -1,    -1,    Done,  -1        }  // MPE
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	d->startDTDwasReported = FALSE;
+	d->systemId = QString::null;
+	d->publicId = QString::null;
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseDoctype (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseDoctype, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Doctype2:
+		d->doctype = name();
+		break;
+	    case MP:
+		if ( !d->startDTDwasReported && lexicalHnd  ) {
+		    d->startDTDwasReported = TRUE;
+		    if ( !lexicalHnd->startDTD( d->doctype, d->publicId, d->systemId ) ) {
+			reportParseError( lexicalHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		state = MPR;
+		break;
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_ERRORPARSINGDOCTYPE );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseDoctype, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == 'D' ) {
+	    input = InpD;
+	} else if ( c == 'S' ) {
+	    input = InpS;
+	} else if ( c == 'P' ) {
+	    input = InpS;
+	} else if ( c == '[' ) {
+	    input = InpOB;
+	} else if ( c == ']' ) {
+	    input = InpCB;
+	} else if ( c == '%' ) {
+	    input = InpPer;
+	} else if ( c == '>' ) {
+	    input = InpGt;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Doctype:
+		d->parseString_s = "DOCTYPE";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseDoctype, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws1:
+	    case Ws2:
+	    case Ws3:
+	    case Ws4:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseDoctype, state );
+		    return FALSE;
+		}
+		break;
+	    case Doctype2:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseDoctype, state );
+		    return FALSE;
+		}
+		break;
+	    case Sys:
+		d->parseExternalID_allowPublicID = FALSE;
+		if ( !parseExternalID() ) {
+		    parseFailed( &QXmlSimpleReader::parseDoctype, state );
+		    return FALSE;
+		}
+		break;
+	    case MP:
+	    case MPR:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseDoctype, state );
+		    return FALSE;
+		}
+		break;
+	    case PER:
+		d->parsePEReference_context = InDTD;
+		if ( !parsePEReference() ) {
+		    parseFailed( &QXmlSimpleReader::parseDoctype, state );
+		    return FALSE;
+		}
+		break;
+	    case Mup:
+		if ( !parseMarkupdecl() ) {
+		    parseFailed( &QXmlSimpleReader::parseDoctype, state );
+		    return FALSE;
+		}
+		break;
+	    case MPE:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseDoctype, state );
+		    return FALSE;
+		}
+		break;
+	    case Done:
+		if ( lexicalHnd ) {
+		    if ( !d->startDTDwasReported ) {
+			d->startDTDwasReported = TRUE;
+			if ( !lexicalHnd->startDTD( d->doctype, d->publicId, d->systemId ) ) {
+			    reportParseError( lexicalHnd->errorString() );
+			    return FALSE;
+			}
+		    }
+		    if ( !lexicalHnd->endDTD() ) {
+			reportParseError( lexicalHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a ExternalID [75].
+
+  If allowPublicID is TRUE parse ExternalID [75] or PublicID [83].
+*/
+bool QXmlSimpleReader::parseExternalID()
+{
+    const signed char Init             =  0;
+    const signed char Sys              =  1; // parse 'SYSTEM'
+    const signed char SysWS            =  2; // parse the whitespace after 'SYSTEM'
+    const signed char SysSQ            =  3; // parse SystemLiteral with '
+    const signed char SysSQ2           =  4; // parse SystemLiteral with '
+    const signed char SysDQ            =  5; // parse SystemLiteral with "
+    const signed char SysDQ2           =  6; // parse SystemLiteral with "
+    const signed char Pub              =  7; // parse 'PUBLIC'
+    const signed char PubWS            =  8; // parse the whitespace after 'PUBLIC'
+    const signed char PubSQ            =  9; // parse PubidLiteral with '
+    const signed char PubSQ2           = 10; // parse PubidLiteral with '
+    const signed char PubDQ            = 11; // parse PubidLiteral with "
+    const signed char PubDQ2           = 12; // parse PubidLiteral with "
+    const signed char PubE             = 13; // finished parsing the PubidLiteral
+    const signed char PubWS2           = 14; // parse the whitespace after the PubidLiteral
+    const signed char PDone            = 15; // done if allowPublicID is TRUE
+    const signed char Done             = 16;
+
+    const signed char InpSQ            = 0; // '
+    const signed char InpDQ            = 1; // "
+    const signed char InpS             = 2; // S
+    const signed char InpP             = 3; // P
+    const signed char InpWs            = 4; // white space
+    const signed char InpUnknown       = 5;
+
+    static const signed char table[15][6] = {
+     /*  InpSQ    InpDQ    InpS     InpP     InpWs     InpUnknown */
+	{ -1,      -1,      Sys,     Pub,     -1,       -1      }, // Init
+	{ -1,      -1,      -1,      -1,      SysWS,    -1      }, // Sys
+	{ SysSQ,   SysDQ,   -1,      -1,      -1,       -1      }, // SysWS
+	{ Done,    SysSQ2,  SysSQ2,  SysSQ2,  SysSQ2,   SysSQ2  }, // SysSQ
+	{ Done,    SysSQ2,  SysSQ2,  SysSQ2,  SysSQ2,   SysSQ2  }, // SysSQ2
+	{ SysDQ2,  Done,    SysDQ2,  SysDQ2,  SysDQ2,   SysDQ2  }, // SysDQ
+	{ SysDQ2,  Done,    SysDQ2,  SysDQ2,  SysDQ2,   SysDQ2  }, // SysDQ2
+	{ -1,      -1,      -1,      -1,      PubWS,    -1      }, // Pub
+	{ PubSQ,   PubDQ,   -1,      -1,      -1,       -1      }, // PubWS
+	{ PubE,    -1,      PubSQ2,  PubSQ2,  PubSQ2,   PubSQ2  }, // PubSQ
+	{ PubE,    -1,      PubSQ2,  PubSQ2,  PubSQ2,   PubSQ2  }, // PubSQ2
+	{ -1,      PubE,    PubDQ2,  PubDQ2,  PubDQ2,   PubDQ2  }, // PubDQ
+	{ -1,      PubE,    PubDQ2,  PubDQ2,  PubDQ2,   PubDQ2  }, // PubDQ2
+	{ PDone,   PDone,   PDone,   PDone,   PubWS2,   PDone   }, // PubE
+	{ SysSQ,   SysDQ,   PDone,   PDone,   PDone,    PDone   }  // PubWS2
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	d->systemId = QString::null;
+	d->publicId = QString::null;
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseExternalID (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseExternalID, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case PDone:
+		if ( d->parseExternalID_allowPublicID ) {
+		    d->publicId = string();
+		    return TRUE;
+		} else {
+		    reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		    return FALSE;
+		}
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseExternalID, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == '\'' ) {
+	    input = InpSQ;
+	} else if ( c == '"' ) {
+	    input = InpDQ;
+	} else if ( c == 'S' ) {
+	    input = InpS;
+	} else if ( c == 'P' ) {
+	    input = InpP;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Sys:
+		d->parseString_s = "SYSTEM";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseExternalID, state );
+		    return FALSE;
+		}
+		break;
+	    case SysWS:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseExternalID, state );
+		    return FALSE;
+		}
+		break;
+	    case SysSQ:
+	    case SysDQ:
+		stringClear();
+		next();
+		break;
+	    case SysSQ2:
+	    case SysDQ2:
+		stringAddC();
+		next();
+		break;
+	    case Pub:
+		d->parseString_s = "PUBLIC";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseExternalID, state );
+		    return FALSE;
+		}
+		break;
+	    case PubWS:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseExternalID, state );
+		    return FALSE;
+		}
+		break;
+	    case PubSQ:
+	    case PubDQ:
+		stringClear();
+		next();
+		break;
+	    case PubSQ2:
+	    case PubDQ2:
+		stringAddC();
+		next();
+		break;
+	    case PubE:
+		next();
+		break;
+	    case PubWS2:
+		d->publicId = string();
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseExternalID, state );
+		    return FALSE;
+		}
+		break;
+	    case Done:
+		d->systemId = string();
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a markupdecl [29].
+*/
+bool QXmlSimpleReader::parseMarkupdecl()
+{
+    const signed char Init             = 0;
+    const signed char Lt               = 1; // < was read
+    const signed char Em               = 2; // ! was read
+    const signed char CE               = 3; // E was read
+    const signed char Qm               = 4; // ? was read
+    const signed char Dash             = 5; // - was read
+    const signed char CA               = 6; // A was read
+    const signed char CEL              = 7; // EL was read
+    const signed char CEN              = 8; // EN was read
+    const signed char CN               = 9; // N was read
+    const signed char Done             = 10;
+
+    const signed char InpLt            = 0; // <
+    const signed char InpQm            = 1; // ?
+    const signed char InpEm            = 2; // !
+    const signed char InpDash          = 3; // -
+    const signed char InpA             = 4; // A
+    const signed char InpE             = 5; // E
+    const signed char InpL             = 6; // L
+    const signed char InpN             = 7; // N
+    const signed char InpUnknown       = 8;
+
+    static const signed char table[4][9] = {
+     /*  InpLt  InpQm  InpEm  InpDash  InpA   InpE   InpL   InpN   InpUnknown */
+	{ Lt,    -1,    -1,    -1,      -1,    -1,    -1,    -1,    -1     }, // Init
+	{ -1,    Qm,    Em,    -1,      -1,    -1,    -1,    -1,    -1     }, // Lt
+	{ -1,    -1,    -1,    Dash,    CA,    CE,    -1,    CN,    -1     }, // Em
+	{ -1,    -1,    -1,    -1,      -1,    -1,    CEL,   CEN,   -1     }  // CE
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseMarkupdecl (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseMarkupdecl, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Qm:
+		if ( contentHnd ) {
+		    if ( !contentHnd->processingInstruction(name(),string()) ) {
+			reportParseError( contentHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		return TRUE;
+	    case Dash:
+		if ( lexicalHnd ) {
+		    if ( !lexicalHnd->comment( string() ) ) {
+			reportParseError( lexicalHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		return TRUE;
+	    case CA:
+		return TRUE;
+	    case CEL:
+		return TRUE;
+	    case CEN:
+		return TRUE;
+	    case CN:
+		return TRUE;
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_LETTEREXPECTED );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseMarkupdecl, state );
+	    return FALSE;
+	}
+	if        ( c == '<' ) {
+	    input = InpLt;
+	} else if ( c == '?' ) {
+	    input = InpQm;
+	} else if ( c == '!' ) {
+	    input = InpEm;
+	} else if ( c == '-' ) {
+	    input = InpDash;
+	} else if ( c == 'A' ) {
+	    input = InpA;
+	} else if ( c == 'E' ) {
+	    input = InpE;
+	} else if ( c == 'L' ) {
+	    input = InpL;
+	} else if ( c == 'N' ) {
+	    input = InpN;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Lt:
+		next();
+		break;
+	    case Em:
+		next();
+		break;
+	    case CE:
+		next();
+		break;
+	    case Qm:
+		d->parsePI_xmldecl = FALSE;
+		if ( !parsePI() ) {
+		    parseFailed( &QXmlSimpleReader::parseMarkupdecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Dash:
+		if ( !parseComment() ) {
+		    parseFailed( &QXmlSimpleReader::parseMarkupdecl, state );
+		    return FALSE;
+		}
+		break;
+	    case CA:
+		if ( !parseAttlistDecl() ) {
+		    parseFailed( &QXmlSimpleReader::parseMarkupdecl, state );
+		    return FALSE;
+		}
+		break;
+	    case CEL:
+		if ( !parseElementDecl() ) {
+		    parseFailed( &QXmlSimpleReader::parseMarkupdecl, state );
+		    return FALSE;
+		}
+		break;
+	    case CEN:
+		if ( !parseEntityDecl() ) {
+		    parseFailed( &QXmlSimpleReader::parseMarkupdecl, state );
+		    return FALSE;
+		}
+		break;
+	    case CN:
+		if ( !parseNotationDecl() ) {
+		    parseFailed( &QXmlSimpleReader::parseMarkupdecl, state );
+		    return FALSE;
+		}
+		break;
+	}
+    }
+}
+
+/*
+  Parse a PEReference [69]
+*/
+bool QXmlSimpleReader::parsePEReference()
+{
+    const signed char Init             = 0;
+    const signed char Next             = 1;
+    const signed char Name             = 2;
+    const signed char NameR            = 3; // same as Name, but already reported
+    const signed char Done             = 4;
+
+    const signed char InpSemi          = 0; // ;
+    const signed char InpPer           = 1; // %
+    const signed char InpUnknown       = 2;
+
+    static const signed char table[4][3] = {
+     /*  InpSemi  InpPer  InpUnknown */
+	{ -1,      Next,   -1    }, // Init
+	{ -1,      -1,     Name  }, // Next
+	{ Done,    -1,     -1    }, // Name
+	{ Done,    -1,     -1    }  // NameR
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parsePEReference (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parsePEReference, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Name:
+		{
+		    bool skipIt = TRUE;
+		    QString xmlRefString;
+
+		    QMap<QString,QString>::Iterator it;
+		    it = d->parameterEntities.find( ref() );
+		    if ( it != d->parameterEntities.end() ) {
+			skipIt = FALSE;
+			xmlRefString = it.data();
+		    } else if ( entityRes ) {
+			QMap<QString,QXmlSimpleReaderPrivate::ExternParameterEntity>::Iterator it2;
+			it2 = d->externParameterEntities.find( ref() );
+			QXmlInputSource *ret = 0;
+			if ( it2 != d->externParameterEntities.end() ) {
+			    if ( !entityRes->resolveEntity( it2.data().publicId, it2.data().systemId, ret ) ) {
+				delete ret;
+				reportParseError( entityRes->errorString() );
+				return FALSE;
+			    }
+			    if ( ret ) {
+				xmlRefString = ret->data();
+				delete ret;
+				if ( !stripTextDecl( xmlRefString ) ) {
+				    reportParseError( XMLERR_ERRORINTEXTDECL );
+				    return FALSE;
+				}
+				skipIt = FALSE;
+			    }
+			}
+		    }
+
+		    if ( skipIt ) {
+			if ( contentHnd ) {
+			    if ( !contentHnd->skippedEntity( QString("%") + ref() ) ) {
+				reportParseError( contentHnd->errorString() );
+				return FALSE;
+			    }
+			}
+		    } else {
+			if ( d->parsePEReference_context == InEntityValue ) {
+			    // Included in literal
+			    if ( !insertXmlRef( xmlRefString, ref(), TRUE ) )
+				return FALSE;
+			} else if ( d->parsePEReference_context == InDTD ) {
+			    // Included as PE
+			    if ( !insertXmlRef( QString(" ")+xmlRefString+QString(" "), ref(), FALSE ) )
+				return FALSE;
+			}
+		    }
+		}
+		state = NameR;
+		break;
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_LETTEREXPECTED );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parsePEReference, state );
+	    return FALSE;
+	}
+	if        ( c == ';' ) {
+	    input = InpSemi;
+	} else if ( c == '%' ) {
+	    input = InpPer;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Next:
+		next();
+		break;
+	    case Name:
+	    case NameR:
+		d->parseName_useRef = TRUE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parsePEReference, state );
+		    return FALSE;
+		}
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a AttlistDecl [52].
+
+  Precondition: the beginning '<!' is already read and the head
+  stands on the 'A' of '<!ATTLIST'
+*/
+bool QXmlSimpleReader::parseAttlistDecl()
+{
+    const signed char Init             =  0;
+    const signed char Attlist          =  1; // parse the string "ATTLIST"
+    const signed char Ws               =  2; // whitespace read
+    const signed char Name             =  3; // parse name
+    const signed char Ws1              =  4; // whitespace read
+    const signed char Attdef           =  5; // parse the AttDef
+    const signed char Ws2              =  6; // whitespace read
+    const signed char Atttype          =  7; // parse the AttType
+    const signed char Ws3              =  8; // whitespace read
+    const signed char DDecH            =  9; // DefaultDecl with #
+    const signed char DefReq           = 10; // parse the string "REQUIRED"
+    const signed char DefImp           = 11; // parse the string "IMPLIED"
+    const signed char DefFix           = 12; // parse the string "FIXED"
+    const signed char Attval           = 13; // parse the AttValue
+    const signed char Ws4              = 14; // whitespace read
+    const signed char Done             = 15;
+
+    const signed char InpWs            = 0; // white space
+    const signed char InpGt            = 1; // >
+    const signed char InpHash          = 2; // #
+    const signed char InpA             = 3; // A
+    const signed char InpI             = 4; // I
+    const signed char InpF             = 5; // F
+    const signed char InpR             = 6; // R
+    const signed char InpUnknown       = 7;
+
+    static const signed char table[15][8] = {
+     /*  InpWs    InpGt    InpHash  InpA      InpI     InpF     InpR     InpUnknown */
+	{ -1,      -1,      -1,      Attlist,  -1,      -1,      -1,      -1      }, // Init
+	{ Ws,      -1,      -1,      -1,       -1,      -1,      -1,      -1      }, // Attlist
+	{ -1,      -1,      -1,      Name,     Name,    Name,    Name,    Name    }, // Ws
+	{ Ws1,     Done,    Attdef,  Attdef,   Attdef,  Attdef,  Attdef,  Attdef  }, // Name
+	{ -1,      Done,    Attdef,  Attdef,   Attdef,  Attdef,  Attdef,  Attdef  }, // Ws1
+	{ Ws2,     -1,      -1,      -1,       -1,      -1,      -1,      -1      }, // Attdef
+	{ -1,      Atttype, Atttype, Atttype,  Atttype, Atttype, Atttype, Atttype }, // Ws2
+	{ Ws3,     -1,      -1,      -1,       -1,      -1,      -1,      -1      }, // Attype
+	{ -1,      Attval,  DDecH,   Attval,   Attval,  Attval,  Attval,  Attval  }, // Ws3
+	{ -1,      -1,      -1,      -1,       DefImp,  DefFix,  DefReq,  -1      }, // DDecH
+	{ Ws4,     Ws4,     -1,      -1,       -1,      -1,      -1,      -1      }, // DefReq
+	{ Ws4,     Ws4,     -1,      -1,       -1,      -1,      -1,      -1      }, // DefImp
+	{ Ws3,     -1,      -1,      -1,       -1,      -1,      -1,      -1      }, // DefFix
+	{ Ws4,     Ws4,     -1,      -1,       -1,      -1,      -1,      -1      }, // Attval
+	{ -1,      Done,    Attdef,  Attdef,   Attdef,  Attdef,  Attdef,  Attdef  }  // Ws4
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseAttlistDecl (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Name:
+		d->attDeclEName = name();
+		break;
+	    case Attdef:
+		d->attDeclAName = name();
+		break;
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_LETTEREXPECTED );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseAttlistDecl, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == '>' ) {
+	    input = InpGt;
+	} else if ( c == '#' ) {
+	    input = InpHash;
+	} else if ( c == 'A' ) {
+	    input = InpA;
+	} else if ( c == 'I' ) {
+	    input = InpI;
+	} else if ( c == 'F' ) {
+	    input = InpF;
+	} else if ( c == 'R' ) {
+	    input = InpR;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Attlist:
+		d->parseString_s = "ATTLIST";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws:
+	    case Ws1:
+	    case Ws2:
+	    case Ws3:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Name:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Attdef:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Atttype:
+		if ( !parseAttType() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case DDecH:
+		next();
+		break;
+	    case DefReq:
+		d->parseString_s = "REQUIRED";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case DefImp:
+		d->parseString_s = "IMPLIED";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case DefFix:
+		d->parseString_s = "FIXED";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Attval:
+		if ( !parseAttValue() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws4:
+		if ( declHnd ) {
+		    // ### not all values are computed yet...
+		    if ( !declHnd->attributeDecl( d->attDeclEName, d->attDeclAName, "", "", "" ) ) {
+			reportParseError( declHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttlistDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a AttType [54]
+*/
+bool QXmlSimpleReader::parseAttType()
+{
+    const signed char Init             =  0;
+    const signed char ST               =  1; // StringType
+    const signed char TTI              =  2; // TokenizedType starting with 'I'
+    const signed char TTI2             =  3; // TokenizedType helpstate
+    const signed char TTI3             =  4; // TokenizedType helpstate
+    const signed char TTE              =  5; // TokenizedType starting with 'E'
+    const signed char TTEY             =  6; // TokenizedType starting with 'ENTITY'
+    const signed char TTEI             =  7; // TokenizedType starting with 'ENTITI'
+    const signed char N                =  8; // N read (TokenizedType or Notation)
+    const signed char TTNM             =  9; // TokenizedType starting with 'NM'
+    const signed char TTNM2            = 10; // TokenizedType helpstate
+    const signed char NO               = 11; // Notation
+    const signed char NO2              = 12; // Notation helpstate
+    const signed char NO3              = 13; // Notation helpstate
+    const signed char NOName           = 14; // Notation, read name
+    const signed char NO4              = 15; // Notation helpstate
+    const signed char EN               = 16; // Enumeration
+    const signed char ENNmt            = 17; // Enumeration, read Nmtoken
+    const signed char EN2              = 18; // Enumeration helpstate
+    const signed char ADone            = 19; // almost done (make next and accept)
+    const signed char Done             = 20;
+
+    const signed char InpWs            =  0; // whitespace
+    const signed char InpOp            =  1; // (
+    const signed char InpCp            =  2; // )
+    const signed char InpPipe          =  3; // |
+    const signed char InpC             =  4; // C
+    const signed char InpE             =  5; // E
+    const signed char InpI             =  6; // I
+    const signed char InpM             =  7; // M
+    const signed char InpN             =  8; // N
+    const signed char InpO             =  9; // O
+    const signed char InpR             = 10; // R
+    const signed char InpS             = 11; // S
+    const signed char InpY             = 12; // Y
+    const signed char InpUnknown       = 13;
+
+    static const signed char table[19][14] = {
+     /*  InpWs    InpOp    InpCp    InpPipe  InpC     InpE     InpI     InpM     InpN     InpO     InpR     InpS     InpY     InpUnknown */
+	{ -1,      EN,      -1,      -1,      ST,      TTE,     TTI,     -1,      N,       -1,      -1,      -1,      -1,      -1     }, // Init
+	{ Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done   }, // ST
+	{ Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    TTI2,    Done,    Done,    Done   }, // TTI
+	{ Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    TTI3,    Done,    Done   }, // TTI2
+	{ Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done   }, // TTI3
+	{ -1,      -1,      -1,      -1,      -1,      -1,      TTEI,    -1,      -1,      -1,      -1,      -1,      TTEY,    -1     }, // TTE
+	{ Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done   }, // TTEY
+	{ Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done   }, // TTEI
+	{ -1,      -1,      -1,      -1,      -1,      -1,      -1,      TTNM,    -1,      NO,      -1,      -1,      -1,      -1     }, // N
+	{ Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    TTNM2,   Done,    Done   }, // TTNM
+	{ Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done,    Done   }, // TTNM2
+	{ NO2,     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1     }, // NO
+	{ -1,      NO3,     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1     }, // NO2
+	{ NOName,  NOName,  NOName,  NOName,  NOName,  NOName,  NOName,  NOName,  NOName,  NOName,  NOName,  NOName,  NOName,  NOName }, // NO3
+	{ NO4,     -1,      ADone,   NO3,     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1     }, // NOName
+	{ -1,      -1,      ADone,   NO3,     -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1     }, // NO4
+	{ -1,      -1,      ENNmt,   -1,      ENNmt,   ENNmt,   ENNmt,   ENNmt,   ENNmt,   ENNmt,   ENNmt,   ENNmt,   ENNmt,   ENNmt  }, // EN
+	{ EN2,     -1,      ADone,   EN,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1     }, // ENNmt
+	{ -1,      -1,      ADone,   EN,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1     }  // EN2
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseAttType (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseAttType, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case ADone:
+		return TRUE;
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_LETTEREXPECTED );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseAttType, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == '(' ) {
+	    input = InpOp;
+	} else if ( c == ')' ) {
+	    input = InpCp;
+	} else if ( c == '|' ) {
+	    input = InpPipe;
+	} else if ( c == 'C' ) {
+	    input = InpC;
+	} else if ( c == 'E' ) {
+	    input = InpE;
+	} else if ( c == 'I' ) {
+	    input = InpI;
+	} else if ( c == 'M' ) {
+	    input = InpM;
+	} else if ( c == 'N' ) {
+	    input = InpN;
+	} else if ( c == 'O' ) {
+	    input = InpO;
+	} else if ( c == 'R' ) {
+	    input = InpR;
+	} else if ( c == 'S' ) {
+	    input = InpS;
+	} else if ( c == 'Y' ) {
+	    input = InpY;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case ST:
+		d->parseString_s = "CDATA";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case TTI:
+		d->parseString_s = "ID";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case TTI2:
+		d->parseString_s = "REF";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case TTI3:
+		next(); // S
+		break;
+	    case TTE:
+		d->parseString_s = "ENTIT";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case TTEY:
+		next(); // Y
+		break;
+	    case TTEI:
+		d->parseString_s = "IES";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case N:
+		next(); // N
+		break;
+	    case TTNM:
+		d->parseString_s = "MTOKEN";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case TTNM2:
+		next(); // S
+		break;
+	    case NO:
+		d->parseString_s = "OTATION";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case NO2:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case NO3:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case NOName:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case NO4:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case EN:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case ENNmt:
+		if ( !parseNmtoken() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case EN2:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttType, state );
+		    return FALSE;
+		}
+		break;
+	    case ADone:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a AttValue [10]
+
+  Precondition: the head stands on the beginning " or '
+
+  If this function was successful, the head stands on the first
+  character after the closing " or ' and the value of the attribute
+  is in string().
+*/
+bool QXmlSimpleReader::parseAttValue()
+{
+    const signed char Init             = 0;
+    const signed char Dq               = 1; // double quotes were read
+    const signed char DqRef            = 2; // read references in double quotes
+    const signed char DqC              = 3; // signed character read in double quotes
+    const signed char Sq               = 4; // single quotes were read
+    const signed char SqRef            = 5; // read references in single quotes
+    const signed char SqC              = 6; // signed character read in single quotes
+    const signed char Done             = 7;
+
+    const signed char InpDq            = 0; // "
+    const signed char InpSq            = 1; // '
+    const signed char InpAmp           = 2; // &
+    const signed char InpLt            = 3; // <
+    const signed char InpUnknown       = 4;
+
+    static const signed char table[7][5] = {
+     /*  InpDq  InpSq  InpAmp  InpLt InpUnknown */
+	{ Dq,    Sq,    -1,     -1,   -1    }, // Init
+	{ Done,  DqC,   DqRef,  -1,   DqC   }, // Dq
+	{ Done,  DqC,   DqRef,  -1,   DqC   }, // DqRef
+	{ Done,  DqC,   DqRef,  -1,   DqC   }, // DqC
+	{ SqC,   Done,  SqRef,  -1,   SqC   }, // Sq
+	{ SqC,   Done,  SqRef,  -1,   SqC   }, // SqRef
+	{ SqC,   Done,  SqRef,  -1,   SqC   }  // SqRef
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseAttValue (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseAttValue, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseAttValue, state );
+	    return FALSE;
+	}
+	if        ( c == '"' ) {
+	    input = InpDq;
+	} else if ( c == '\'' ) {
+	    input = InpSq;
+	} else if ( c == '&' ) {
+	    input = InpAmp;
+	} else if ( c == '<' ) {
+	    input = InpLt;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Dq:
+	    case Sq:
+		stringClear();
+		next();
+		break;
+	    case DqRef:
+	    case SqRef:
+		d->parseReference_context = InAttributeValue;
+		if ( !parseReference() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttValue, state );
+		    return FALSE;
+		}
+		break;
+	    case DqC:
+	    case SqC:
+		stringAddC();
+		next();
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a elementdecl [45].
+
+  Precondition: the beginning '<!E' is already read and the head
+  stands on the 'L' of '<!ELEMENT'
+*/
+bool QXmlSimpleReader::parseElementDecl()
+{
+    const signed char Init             =  0;
+    const signed char Elem             =  1; // parse the beginning string
+    const signed char Ws1              =  2; // whitespace required
+    const signed char Nam              =  3; // parse Name
+    const signed char Ws2              =  4; // whitespace required
+    const signed char Empty            =  5; // read EMPTY
+    const signed char Any              =  6; // read ANY
+    const signed char Cont             =  7; // read contentspec (except ANY or EMPTY)
+    const signed char Mix              =  8; // read Mixed
+    const signed char Mix2             =  9; //
+    const signed char Mix3             = 10; //
+    const signed char MixN1            = 11; //
+    const signed char MixN2            = 12; //
+    const signed char MixN3            = 13; //
+    const signed char MixN4            = 14; //
+    const signed char Cp               = 15; // parse cp
+    const signed char Cp2              = 16; //
+    const signed char WsD              = 17; // eat whitespace before Done
+    const signed char Done             = 18;
+
+    const signed char InpWs            =  0;
+    const signed char InpGt            =  1; // >
+    const signed char InpPipe          =  2; // |
+    const signed char InpOp            =  3; // (
+    const signed char InpCp            =  4; // )
+    const signed char InpHash          =  5; // #
+    const signed char InpQm            =  6; // ?
+    const signed char InpAst           =  7; // *
+    const signed char InpPlus          =  8; // +
+    const signed char InpA             =  9; // A
+    const signed char InpE             = 10; // E
+    const signed char InpL             = 11; // L
+    const signed char InpUnknown       = 12;
+
+    static const signed char table[18][13] = {
+     /*  InpWs   InpGt  InpPipe  InpOp  InpCp   InpHash  InpQm  InpAst  InpPlus  InpA    InpE    InpL    InpUnknown */
+	{ -1,     -1,    -1,      -1,    -1,     -1,      -1,    -1,     -1,      -1,     -1,     Elem,   -1     }, // Init
+	{ Ws1,    -1,    -1,      -1,    -1,     -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }, // Elem
+	{ -1,     -1,    -1,      -1,    -1,     -1,      -1,    -1,     -1,      Nam,    Nam,    Nam,    Nam    }, // Ws1
+	{ Ws2,    -1,    -1,      -1,    -1,     -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }, // Nam
+	{ -1,     -1,    -1,      Cont,  -1,     -1,      -1,    -1,     -1,      Any,    Empty,  -1,     -1     }, // Ws2
+	{ WsD,    Done,  -1,      -1,    -1,     -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }, // Empty
+	{ WsD,    Done,  -1,      -1,    -1,     -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }, // Any
+	{ -1,     -1,    -1,      Cp,    Cp,     Mix,     -1,    -1,     -1,      Cp,     Cp,     Cp,     Cp     }, // Cont
+	{ Mix2,   -1,    MixN1,   -1,    Mix3,   -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }, // Mix
+	{ -1,     -1,    MixN1,   -1,    Mix3,   -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }, // Mix2
+	{ WsD,    Done,  -1,      -1,    -1,     -1,      -1,    WsD,    -1,      -1,     -1,     -1,     -1     }, // Mix3
+	{ -1,     -1,    -1,      -1,    -1,     -1,      -1,    -1,     -1,      MixN2,  MixN2,  MixN2,  MixN2  }, // MixN1
+	{ MixN3,  -1,    MixN1,   -1,    MixN4,  -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }, // MixN2
+	{ -1,     -1,    MixN1,   -1,    MixN4,  -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }, // MixN3
+	{ -1,     -1,    -1,      -1,    -1,     -1,      -1,    WsD,    -1,      -1,     -1,     -1,     -1     }, // MixN4
+	{ WsD,    Done,  -1,      -1,    -1,     -1,      Cp2,   Cp2,    Cp2,     -1,     -1,     -1,     -1     }, // Cp
+	{ WsD,    Done,  -1,      -1,    -1,     -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }, // Cp2
+	{ -1,     Done,  -1,      -1,    -1,     -1,      -1,    -1,     -1,      -1,     -1,     -1,     -1     }  // WsD
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseElementDecl (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Done:
+		return TRUE;
+	    case -1:
+		reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseElementDecl, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == '>' ) {
+	    input = InpGt;
+	} else if ( c == '|' ) {
+	    input = InpPipe;
+	} else if ( c == '(' ) {
+	    input = InpOp;
+	} else if ( c == ')' ) {
+	    input = InpCp;
+	} else if ( c == '#' ) {
+	    input = InpHash;
+	} else if ( c == '?' ) {
+	    input = InpQm;
+	} else if ( c == '*' ) {
+	    input = InpAst;
+	} else if ( c == '+' ) {
+	    input = InpPlus;
+	} else if ( c == 'A' ) {
+	    input = InpA;
+	} else if ( c == 'E' ) {
+	    input = InpE;
+	} else if ( c == 'L' ) {
+	    input = InpL;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Elem:
+		d->parseString_s = "LEMENT";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws1:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Nam:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws2:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Empty:
+		d->parseString_s = "EMPTY";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Any:
+		d->parseString_s = "ANY";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Cont:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Mix:
+		d->parseString_s = "#PCDATA";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Mix2:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Mix3:
+		next();
+		break;
+	    case MixN1:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case MixN2:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case MixN3:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case MixN4:
+		next();
+		break;
+	    case Cp:
+		if ( !parseChoiceSeq() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Cp2:
+		next();
+		break;
+	    case WsD:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseElementDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a NotationDecl [82].
+
+  Precondition: the beginning '<!' is already read and the head
+  stands on the 'N' of '<!NOTATION'
+*/
+bool QXmlSimpleReader::parseNotationDecl()
+{
+    const signed char Init             = 0;
+    const signed char Not              = 1; // read NOTATION
+    const signed char Ws1              = 2; // eat whitespaces
+    const signed char Nam              = 3; // read Name
+    const signed char Ws2              = 4; // eat whitespaces
+    const signed char ExtID            = 5; // parse ExternalID
+    const signed char ExtIDR           = 6; // same as ExtID, but already reported
+    const signed char Ws3              = 7; // eat whitespaces
+    const signed char Done             = 8;
+
+    const signed char InpWs            = 0;
+    const signed char InpGt            = 1; // >
+    const signed char InpN             = 2; // N
+    const signed char InpUnknown       = 3;
+
+    static const signed char table[8][4] = {
+     /*  InpWs   InpGt  InpN    InpUnknown */
+	{ -1,     -1,    Not,    -1     }, // Init
+	{ Ws1,    -1,    -1,     -1     }, // Not
+	{ -1,     -1,    Nam,    Nam    }, // Ws1
+	{ Ws2,    Done,  -1,     -1     }, // Nam
+	{ -1,     Done,  ExtID,  ExtID  }, // Ws2
+	{ Ws3,    Done,  -1,     -1     }, // ExtID
+	{ Ws3,    Done,  -1,     -1     }, // ExtIDR
+	{ -1,     Done,  -1,     -1     }  // Ws3
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseNotationDecl (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseNotationDecl, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case ExtID:
+		// call the handler
+		if ( dtdHnd ) {
+		    if ( !dtdHnd->notationDecl( name(), d->publicId, d->systemId ) ) {
+			reportParseError( dtdHnd->errorString() );
+			return FALSE;
+		    }
+		}
+		state = ExtIDR;
+		break;
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseNotationDecl, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == '>' ) {
+	    input = InpGt;
+	} else if ( c == 'N' ) {
+	    input = InpN;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Not:
+		d->parseString_s = "NOTATION";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseNotationDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws1:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseNotationDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Nam:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseNotationDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws2:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseNotationDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case ExtID:
+	    case ExtIDR:
+		d->parseExternalID_allowPublicID = TRUE;
+		if ( !parseExternalID() ) {
+		    parseFailed( &QXmlSimpleReader::parseNotationDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws3:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseNotationDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse choice [49] or seq [50].
+
+  Precondition: the beginning '('S? is already read and the head
+  stands on the first non-whitespace character after it.
+*/
+bool QXmlSimpleReader::parseChoiceSeq()
+{
+    const signed char Init             = 0;
+    const signed char Ws1              = 1; // eat whitespace
+    const signed char CS               = 2; // choice or set
+    const signed char Ws2              = 3; // eat whitespace
+    const signed char More             = 4; // more cp to read
+    const signed char Name             = 5; // read name
+    const signed char Done             = 6; //
+
+    const signed char InpWs            = 0; // S
+    const signed char InpOp            = 1; // (
+    const signed char InpCp            = 2; // )
+    const signed char InpQm            = 3; // ?
+    const signed char InpAst           = 4; // *
+    const signed char InpPlus          = 5; // +
+    const signed char InpPipe          = 6; // |
+    const signed char InpComm          = 7; // ,
+    const signed char InpUnknown       = 8;
+
+    static const signed char table[6][9] = {
+     /*  InpWs   InpOp  InpCp  InpQm  InpAst  InpPlus  InpPipe  InpComm  InpUnknown */
+	{ -1,     Ws1,   -1,    -1,    -1,     -1,      -1,      -1,      Name  }, // Init
+	{ -1,     CS,    -1,    -1,    -1,     -1,      -1,      -1,      CS    }, // Ws1
+	{ Ws2,    -1,    Done,  Ws2,   Ws2,    Ws2,     More,    More,    -1    }, // CS
+	{ -1,     -1,    Done,  -1,    -1,     -1,      More,    More,    -1    }, // Ws2
+	{ -1,     Ws1,   -1,    -1,    -1,     -1,      -1,      -1,      Name  }, // More (same as Init)
+	{ Ws2,    -1,    Done,  Ws2,   Ws2,    Ws2,     More,    More,    -1    }  // Name (same as CS)
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseChoiceSeq (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseChoiceSeq, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseChoiceSeq, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == '(' ) {
+	    input = InpOp;
+	} else if ( c == ')' ) {
+	    input = InpCp;
+	} else if ( c == '?' ) {
+	    input = InpQm;
+	} else if ( c == '*' ) {
+	    input = InpAst;
+	} else if ( c == '+' ) {
+	    input = InpPlus;
+	} else if ( c == '|' ) {
+	    input = InpPipe;
+	} else if ( c == ',' ) {
+	    input = InpComm;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Ws1:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseChoiceSeq, state );
+		    return FALSE;
+		}
+		break;
+	    case CS:
+		if ( !parseChoiceSeq() ) {
+		    parseFailed( &QXmlSimpleReader::parseChoiceSeq, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws2:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseChoiceSeq, state );
+		    return FALSE;
+		}
+		break;
+	    case More:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseChoiceSeq, state );
+		    return FALSE;
+		}
+		break;
+	    case Name:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseChoiceSeq, state );
+		    return FALSE;
+		}
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a EntityDecl [70].
+
+  Precondition: the beginning '<!E' is already read and the head
+  stand on the 'N' of '<!ENTITY'
+*/
+bool QXmlSimpleReader::parseEntityDecl()
+{
+    const signed char Init             =  0;
+    const signed char Ent              =  1; // parse "ENTITY"
+    const signed char Ws1              =  2; // white space read
+    const signed char Name             =  3; // parse name
+    const signed char Ws2              =  4; // white space read
+    const signed char EValue           =  5; // parse entity value
+    const signed char EValueR          =  6; // same as EValue, but already reported
+    const signed char ExtID            =  7; // parse ExternalID
+    const signed char Ws3              =  8; // white space read
+    const signed char Ndata            =  9; // parse "NDATA"
+    const signed char Ws4              = 10; // white space read
+    const signed char NNam             = 11; // parse name
+    const signed char NNamR            = 12; // same as NNam, but already reported
+    const signed char PEDec            = 13; // parse PEDecl
+    const signed char Ws6              = 14; // white space read
+    const signed char PENam            = 15; // parse name
+    const signed char Ws7              = 16; // white space read
+    const signed char PEVal            = 17; // parse entity value
+    const signed char PEValR           = 18; // same as PEVal, but already reported
+    const signed char PEEID            = 19; // parse ExternalID
+    const signed char PEEIDR           = 20; // same as PEEID, but already reported
+    const signed char WsE              = 21; // white space read
+    const signed char Done             = 22;
+    const signed char EDDone           = 23; // done, but also report an external, unparsed entity decl
+
+    const signed char InpWs            = 0; // white space
+    const signed char InpPer           = 1; // %
+    const signed char InpQuot          = 2; // " or '
+    const signed char InpGt            = 3; // >
+    const signed char InpN             = 4; // N
+    const signed char InpUnknown       = 5;
+
+    static const signed char table[22][6] = {
+     /*  InpWs  InpPer  InpQuot  InpGt  InpN    InpUnknown */
+	{ -1,    -1,     -1,      -1,    Ent,    -1      }, // Init
+	{ Ws1,   -1,     -1,      -1,    -1,     -1      }, // Ent
+	{ -1,    PEDec,  -1,      -1,    Name,   Name    }, // Ws1
+	{ Ws2,   -1,     -1,      -1,    -1,     -1      }, // Name
+	{ -1,    -1,     EValue,  -1,    -1,     ExtID   }, // Ws2
+	{ WsE,   -1,     -1,      Done,  -1,     -1      }, // EValue
+	{ WsE,   -1,     -1,      Done,  -1,     -1      }, // EValueR
+	{ Ws3,   -1,     -1,      EDDone,-1,     -1      }, // ExtID
+	{ -1,    -1,     -1,      EDDone,Ndata,  -1      }, // Ws3
+	{ Ws4,   -1,     -1,      -1,    -1,     -1      }, // Ndata
+	{ -1,    -1,     -1,      -1,    NNam,   NNam    }, // Ws4
+	{ WsE,   -1,     -1,      Done,  -1,     -1      }, // NNam
+	{ WsE,   -1,     -1,      Done,  -1,     -1      }, // NNamR
+	{ Ws6,   -1,     -1,      -1,    -1,     -1      }, // PEDec
+	{ -1,    -1,     -1,      -1,    PENam,  PENam   }, // Ws6
+	{ Ws7,   -1,     -1,      -1,    -1,     -1      }, // PENam
+	{ -1,    -1,     PEVal,   -1,    -1,     PEEID   }, // Ws7
+	{ WsE,   -1,     -1,      Done,  -1,     -1      }, // PEVal
+	{ WsE,   -1,     -1,      Done,  -1,     -1      }, // PEValR
+	{ WsE,   -1,     -1,      Done,  -1,     -1      }, // PEEID
+	{ WsE,   -1,     -1,      Done,  -1,     -1      }, // PEEIDR
+	{ -1,    -1,     -1,      Done,  -1,     -1      }  // WsE
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseEntityDecl (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case EValue:
+		if (  !entityExist( name() ) ) {
+		    d->entities.insert( name(), string() );
+		    if ( declHnd ) {
+			if ( !declHnd->internalEntityDecl( name(), string() ) ) {
+			    reportParseError( declHnd->errorString() );
+			    return FALSE;
+			}
+		    }
+		}
+		state = EValueR;
+		break;
+	    case NNam:
+		if (  !entityExist( name() ) ) {
+		    d->externEntities.insert( name(), QXmlSimpleReaderPrivate::ExternEntity( d->publicId, d->systemId, ref() ) );
+		    if ( dtdHnd ) {
+			if ( !dtdHnd->unparsedEntityDecl( name(), d->publicId, d->systemId, ref() ) ) {
+			    reportParseError( declHnd->errorString() );
+			    return FALSE;
+			}
+		    }
+		}
+		state = NNamR;
+		break;
+	    case PEVal:
+		if (  !entityExist( name() ) ) {
+		    d->parameterEntities.insert( name(), string() );
+		    if ( declHnd ) {
+			if ( !declHnd->internalEntityDecl( QString("%")+name(), string() ) ) {
+			    reportParseError( declHnd->errorString() );
+			    return FALSE;
+			}
+		    }
+		}
+		state = PEValR;
+		break;
+	    case PEEID:
+		if (  !entityExist( name() ) ) {
+		    d->externParameterEntities.insert( name(), QXmlSimpleReaderPrivate::ExternParameterEntity( d->publicId, d->systemId ) );
+		    if ( declHnd ) {
+			if ( !declHnd->externalEntityDecl( QString("%")+name(), d->publicId, d->systemId ) ) {
+			    reportParseError( declHnd->errorString() );
+			    return FALSE;
+			}
+		    }
+		}
+		state = PEEIDR;
+		break;
+	    case EDDone:
+		if (  !entityExist( name() ) ) {
+		    d->externEntities.insert( name(), QXmlSimpleReaderPrivate::ExternEntity( d->publicId, d->systemId, QString::null ) );
+		    if ( declHnd ) {
+			if ( !declHnd->externalEntityDecl( name(), d->publicId, d->systemId ) ) {
+			    reportParseError( declHnd->errorString() );
+			    return FALSE;
+			}
+		    }
+		}
+		return TRUE;
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_LETTEREXPECTED );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseEntityDecl, state );
+	    return FALSE;
+	}
+	if        ( is_S(c) ) {
+	    input = InpWs;
+	} else if ( c == '%' ) {
+	    input = InpPer;
+	} else if ( c == '"' || c == '\'' ) {
+	    input = InpQuot;
+	} else if ( c == '>' ) {
+	    input = InpGt;
+	} else if ( c == 'N' ) {
+	    input = InpN;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Ent:
+		d->parseString_s = "NTITY";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws1:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Name:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws2:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case EValue:
+	    case EValueR:
+		if ( !parseEntityValue() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case ExtID:
+		d->parseExternalID_allowPublicID = FALSE;
+		if ( !parseExternalID() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws3:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ndata:
+		d->parseString_s = "NDATA";
+		if ( !parseString() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws4:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case NNam:
+	    case NNamR:
+		d->parseName_useRef = TRUE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case PEDec:
+		next();
+		break;
+	    case Ws6:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case PENam:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws7:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case PEVal:
+	    case PEValR:
+		if ( !parseEntityValue() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case PEEID:
+	    case PEEIDR:
+		d->parseExternalID_allowPublicID = FALSE;
+		if ( !parseExternalID() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case WsE:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityDecl, state );
+		    return FALSE;
+		}
+		break;
+	    case EDDone:
+		next();
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a EntityValue [9]
+*/
+bool QXmlSimpleReader::parseEntityValue()
+{
+    const signed char Init             = 0;
+    const signed char Dq               = 1; // EntityValue is double quoted
+    const signed char DqC              = 2; // signed character
+    const signed char DqPER            = 3; // PERefence
+    const signed char DqRef            = 4; // Reference
+    const signed char Sq               = 5; // EntityValue is double quoted
+    const signed char SqC              = 6; // signed character
+    const signed char SqPER            = 7; // PERefence
+    const signed char SqRef            = 8; // Reference
+    const signed char Done             = 9;
+
+    const signed char InpDq            = 0; // "
+    const signed char InpSq            = 1; // '
+    const signed char InpAmp           = 2; // &
+    const signed char InpPer           = 3; // %
+    const signed char InpUnknown       = 4;
+
+    static const signed char table[9][5] = {
+     /*  InpDq  InpSq  InpAmp  InpPer  InpUnknown */
+	{ Dq,    Sq,    -1,     -1,     -1    }, // Init
+	{ Done,  DqC,   DqRef,  DqPER,  DqC   }, // Dq
+	{ Done,  DqC,   DqRef,  DqPER,  DqC   }, // DqC
+	{ Done,  DqC,   DqRef,  DqPER,  DqC   }, // DqPER
+	{ Done,  DqC,   DqRef,  DqPER,  DqC   }, // DqRef
+	{ SqC,   Done,  SqRef,  SqPER,  SqC   }, // Sq
+	{ SqC,   Done,  SqRef,  SqPER,  SqC   }, // SqC
+	{ SqC,   Done,  SqRef,  SqPER,  SqC   }, // SqPER
+	{ SqC,   Done,  SqRef,  SqPER,  SqC   }  // SqRef
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseEntityValue (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseEntityValue, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_LETTEREXPECTED );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseEntityValue, state );
+	    return FALSE;
+	}
+	if        ( c == '"' ) {
+	    input = InpDq;
+	} else if ( c == '\'' ) {
+	    input = InpSq;
+	} else if ( c == '&' ) {
+	    input = InpAmp;
+	} else if ( c == '%' ) {
+	    input = InpPer;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Dq:
+	    case Sq:
+		stringClear();
+		next();
+		break;
+	    case DqC:
+	    case SqC:
+		stringAddC();
+		next();
+		break;
+	    case DqPER:
+	    case SqPER:
+		d->parsePEReference_context = InEntityValue;
+		if ( !parsePEReference() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityValue, state );
+		    return FALSE;
+		}
+		break;
+	    case DqRef:
+	    case SqRef:
+		d->parseReference_context = InEntityValue;
+		if ( !parseReference() ) {
+		    parseFailed( &QXmlSimpleReader::parseEntityValue, state );
+		    return FALSE;
+		}
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a comment [15].
+
+  Precondition: the beginning '<!' of the comment is already read and the head
+  stands on the first '-' of '<!--'.
+
+  If this funktion was successful, the head-position is on the first
+  character after the comment.
+*/
+bool QXmlSimpleReader::parseComment()
+{
+    const signed char Init             = 0;
+    const signed char Dash1            = 1; // the first dash was read
+    const signed char Dash2            = 2; // the second dash was read
+    const signed char Com              = 3; // read comment
+    const signed char Com2             = 4; // read comment (help state)
+    const signed char ComE             = 5; // finished reading comment
+    const signed char Done             = 6;
+
+    const signed char InpDash          = 0; // -
+    const signed char InpGt            = 1; // >
+    const signed char InpUnknown       = 2;
+
+    static const signed char table[6][3] = {
+     /*  InpDash  InpGt  InpUnknown */
+	{ Dash1,   -1,    -1  }, // Init
+	{ Dash2,   -1,    -1  }, // Dash1
+	{ Com2,    Com,   Com }, // Dash2
+	{ Com2,    Com,   Com }, // Com
+	{ ComE,    Com,   Com }, // Com2
+	{ -1,      Done,  -1  }  // ComE
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseComment (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseComment, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Dash2:
+		stringClear();
+		break;
+	    case Com2:
+		// if next character is not a dash than don't skip it
+		if ( !atEnd() && c != '-' )
+		    stringAddC( '-' );
+		break;
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_ERRORPARSINGCOMMENT );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseComment, state );
+	    return FALSE;
+	}
+	if        ( c == '-' ) {
+	    input = InpDash;
+	} else if ( c == '>' ) {
+	    input = InpGt;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Dash1:
+		next();
+		break;
+	    case Dash2:
+		next();
+		break;
+	    case Com:
+		stringAddC();
+		next();
+		break;
+	    case Com2:
+		next();
+		break;
+	    case ComE:
+		next();
+		break;
+	    case Done:
+		next();
+		break;
+	}
+    }
+}
+
+/*
+    Parse an Attribute [41].
+
+    Precondition: the head stands on the first character of the name
+    of the attribute (i.e. all whitespaces are already parsed).
+
+    The head stand on the next character after the end quotes. The
+    variable name contains the name of the attribute and the variable
+    string contains the value of the attribute.
+*/
+bool QXmlSimpleReader::parseAttribute()
+{
+    const signed char Init             = 0;
+    const signed char PName            = 1; // parse name
+    const signed char Ws               = 2; // eat ws
+    const signed char Eq               = 3; // the '=' was read
+    const signed char Quotes           = 4; // " or ' were read
+
+    const signed char InpNameBe        = 0;
+    const signed char InpEq            = 1; // =
+    const signed char InpDq            = 2; // "
+    const signed char InpSq            = 3; // '
+    const signed char InpUnknown       = 4;
+
+    static const signed char table[4][5] = {
+     /*  InpNameBe  InpEq  InpDq    InpSq    InpUnknown */
+	{ PName,     -1,    -1,      -1,      -1    }, // Init
+	{ -1,        Eq,    -1,      -1,      Ws    }, // PName
+	{ -1,        Eq,    -1,      -1,      -1    }, // Ws
+	{ -1,        -1,    Quotes,  Quotes,  -1    }  // Eq
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseAttribute (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseAttribute, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Quotes:
+		// Done
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseAttribute, state );
+	    return FALSE;
+	}
+	if        ( is_NameBeginning(c) ) {
+	    input = InpNameBe;
+	} else if ( c == '=' ) {
+	    input = InpEq;
+	} else if ( c == '"' ) {
+	    input = InpDq;
+	} else if ( c == '\'' ) {
+	    input = InpSq;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case PName:
+		d->parseName_useRef = FALSE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttribute, state );
+		    return FALSE;
+		}
+		break;
+	    case Ws:
+		if ( !eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttribute, state );
+		    return FALSE;
+		}
+		break;
+	    case Eq:
+		if ( !next_eat_ws() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttribute, state );
+		    return FALSE;
+		}
+		break;
+	    case Quotes:
+		if ( !parseAttValue() ) {
+		    parseFailed( &QXmlSimpleReader::parseAttribute, state );
+		    return FALSE;
+		}
+		break;
+	}
+    }
+}
+
+/*
+  Parse a Name [5] and store the name in name or ref (if useRef is TRUE).
+*/
+bool QXmlSimpleReader::parseName()
+{
+    const signed char Init             = 0;
+    const signed char Name1            = 1; // parse first signed character of the name
+    const signed char Name             = 2; // parse name
+    const signed char Done             = 3;
+
+    const signed char InpNameBe        = 0; // name beginning signed characters
+    const signed char InpNameCh        = 1; // NameChar without InpNameBe
+    const signed char InpUnknown       = 2;
+
+    static const signed char table[3][3] = {
+     /*  InpNameBe  InpNameCh  InpUnknown */
+	{ Name1,     -1,        -1    }, // Init
+	{ Name,      Name,      Done  }, // Name1
+	{ Name,      Name,      Done  }  // Name
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseName (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseName, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_LETTEREXPECTED );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseName, state );
+	    return FALSE;
+	}
+	if        ( is_NameBeginning(c) ) {
+	    input = InpNameBe;
+	} else if ( is_NameChar(c) ) {
+	    input = InpNameCh;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case Name1:
+		if ( d->parseName_useRef ) {
+		    refClear();
+		    refAddC();
+		} else {
+		    nameClear();
+		    nameAddC();
+		}
+		next();
+		break;
+	    case Name:
+		if ( d->parseName_useRef ) {
+		    refAddC();
+		} else {
+		    nameAddC();
+		}
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a Nmtoken [7] and store the name in name.
+*/
+bool QXmlSimpleReader::parseNmtoken()
+{
+    const signed char Init             = 0;
+    const signed char NameF            = 1;
+    const signed char Name             = 2;
+    const signed char Done             = 3;
+
+    const signed char InpNameCh        = 0; // NameChar without InpNameBe
+    const signed char InpUnknown       = 1;
+
+    static const signed char table[3][2] = {
+     /*  InpNameCh  InpUnknown */
+	{ NameF,     -1    }, // Init
+	{ Name,      Done  }, // NameF
+	{ Name,      Done  }  // Name
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseNmtoken (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseNmtoken, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case Done:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_LETTEREXPECTED );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseNmtoken, state );
+	    return FALSE;
+	}
+	if ( is_NameChar(c) ) {
+	    input = InpNameCh;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case NameF:
+		nameClear();
+		nameAddC();
+		next();
+		break;
+	    case Name:
+		nameAddC();
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Parse a Reference [67].
+
+  parseReference_charDataRead is set to TRUE if the reference must not be
+  parsed. The character(s) which the reference mapped to are appended to
+  string. The head stands on the first character after the reference.
+
+  parseReference_charDataRead is set to FALSE if the reference must be parsed.
+  The charachter(s) which the reference mapped to are inserted at the reference
+  position. The head stands on the first character of the replacement).
+*/
+bool QXmlSimpleReader::parseReference()
+{
+    // temporary variables (only used in very local context, so they don't
+    // interfere with incremental parsing)
+    uint tmp;
+    bool ok;
+
+    const signed char Init             =  0;
+    const signed char SRef             =  1; // start of a reference
+    const signed char ChRef            =  2; // parse CharRef
+    const signed char ChDec            =  3; // parse CharRef decimal
+    const signed char ChHexS           =  4; // start CharRef hexadecimal
+    const signed char ChHex            =  5; // parse CharRef hexadecimal
+    const signed char Name             =  6; // parse name
+    const signed char DoneD            =  7; // done CharRef decimal
+    const signed char DoneH            =  8; // done CharRef hexadecimal
+    const signed char DoneN            =  9; // done EntityRef
+
+    const signed char InpAmp           = 0; // &
+    const signed char InpSemi          = 1; // ;
+    const signed char InpHash          = 2; // #
+    const signed char InpX             = 3; // x
+    const signed char InpNum           = 4; // 0-9
+    const signed char InpHex           = 5; // a-f A-F
+    const signed char InpUnknown       = 6;
+
+    static const signed char table[8][7] = {
+     /*  InpAmp  InpSemi  InpHash  InpX     InpNum  InpHex  InpUnknown */
+	{ SRef,   -1,      -1,      -1,      -1,     -1,     -1    }, // Init
+	{ -1,     -1,      ChRef,   Name,    Name,   Name,   Name  }, // SRef
+	{ -1,     -1,      -1,      ChHexS,  ChDec,  -1,     -1    }, // ChRef
+	{ -1,     DoneD,   -1,      -1,      ChDec,  -1,     -1    }, // ChDec
+	{ -1,     -1,      -1,      -1,      ChHex,  ChHex,  -1    }, // ChHexS
+	{ -1,     DoneH,   -1,      -1,      ChHex,  ChHex,  -1    }, // ChHex
+	{ -1,     DoneN,   -1,      -1,      -1,     -1,     -1    }  // Name
+    };
+    signed char state;
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	d->parseReference_charDataRead = FALSE;
+	state = Init;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseReference (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseReference, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	switch ( state ) {
+	    case DoneD:
+		return TRUE;
+	    case DoneH:
+		return TRUE;
+	    case DoneN:
+		return TRUE;
+	    case -1:
+		// Error
+		reportParseError( XMLERR_ERRORPARSINGREFERENCE );
+		return FALSE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseReference, state );
+	    return FALSE;
+	}
+	if        ( c.row() ) {
+	    input = InpUnknown;
+	} else if ( c.cell() == '&' ) {
+	    input = InpAmp;
+	} else if ( c.cell() == ';' ) {
+	    input = InpSemi;
+	} else if ( c.cell() == '#' ) {
+	    input = InpHash;
+	} else if ( c.cell() == 'x' ) {
+	    input = InpX;
+	} else if ( '0' <= c.cell() && c.cell() <= '9' ) {
+	    input = InpNum;
+	} else if ( 'a' <= c.cell() && c.cell() <= 'f' ) {
+	    input = InpHex;
+	} else if ( 'A' <= c.cell() && c.cell() <= 'F' ) {
+	    input = InpHex;
+	} else {
+	    input = InpUnknown;
+	}
+	state = table[state][input];
+
+	switch ( state ) {
+	    case SRef:
+		refClear();
+		next();
+		break;
+	    case ChRef:
+		next();
+		break;
+	    case ChDec:
+		refAddC();
+		next();
+		break;
+	    case ChHexS:
+		next();
+		break;
+	    case ChHex:
+		refAddC();
+		next();
+		break;
+	    case Name:
+		// read the name into the ref
+		d->parseName_useRef = TRUE;
+		if ( !parseName() ) {
+		    parseFailed( &QXmlSimpleReader::parseReference, state );
+		    return FALSE;
+		}
+		break;
+	    case DoneD:
+		tmp = ref().toUInt( &ok, 10 );
+		if ( ok ) {
+		    stringAddC( QChar(tmp) );
+		} else {
+		    reportParseError( XMLERR_ERRORPARSINGREFERENCE );
+		    return FALSE;
+		}
+		d->parseReference_charDataRead = TRUE;
+		next();
+		break;
+	    case DoneH:
+		tmp = ref().toUInt( &ok, 16 );
+		if ( ok ) {
+		    stringAddC( QChar(tmp) );
+		} else {
+		    reportParseError( XMLERR_ERRORPARSINGREFERENCE );
+		    return FALSE;
+		}
+		d->parseReference_charDataRead = TRUE;
+		next();
+		break;
+	    case DoneN:
+		if ( !processReference() )
+		    return FALSE;
+		next();
+		break;
+	}
+    }
+}
+
+/*
+  Helper function for parseReference()
+*/
+bool QXmlSimpleReader::processReference()
+{
+    QString reference = ref();
+    if ( reference == "amp" ) {
+	if ( d->parseReference_context == InEntityValue ) {
+	    // Bypassed
+	    stringAddC( '&' ); stringAddC( 'a' ); stringAddC( 'm' ); stringAddC( 'p' ); stringAddC( ';' );
+	} else {
+	    // Included or Included in literal
+	    stringAddC( '&' );
+	}
+	d->parseReference_charDataRead = TRUE;
+    } else if ( reference == "lt" ) {
+	if ( d->parseReference_context == InEntityValue ) {
+	    // Bypassed
+	    stringAddC( '&' ); stringAddC( 'l' ); stringAddC( 't' ); stringAddC( ';' );
+	} else {
+	    // Included or Included in literal
+	    stringAddC( '<' );
+	}
+	d->parseReference_charDataRead = TRUE;
+    } else if ( reference == "gt" ) {
+	if ( d->parseReference_context == InEntityValue ) {
+	    // Bypassed
+	    stringAddC( '&' ); stringAddC( 'g' ); stringAddC( 't' ); stringAddC( ';' );
+	} else {
+	    // Included or Included in literal
+	    stringAddC( '>' );
+	}
+	d->parseReference_charDataRead = TRUE;
+    } else if ( reference == "apos" ) {
+	if ( d->parseReference_context == InEntityValue ) {
+	    // Bypassed
+	    stringAddC( '&' ); stringAddC( 'a' ); stringAddC( 'p' ); stringAddC( 'o' ); stringAddC( 's' ); stringAddC( ';' );
+	} else {
+	    // Included or Included in literal
+	    stringAddC( '\'' );
+	}
+	d->parseReference_charDataRead = TRUE;
+    } else if ( reference == "quot" ) {
+	if ( d->parseReference_context == InEntityValue ) {
+	    // Bypassed
+	    stringAddC( '&' ); stringAddC( 'q' ); stringAddC( 'u' ); stringAddC( 'o' ); stringAddC( 't' ); stringAddC( ';' );
+	} else {
+	    // Included or Included in literal
+	    stringAddC( '"' );
+	}
+	d->parseReference_charDataRead = TRUE;
+    } else {
+	QMap<QString,QString>::Iterator it;
+	it = d->entities.find( reference );
+	if ( it != d->entities.end() ) {
+	    // "Internal General"
+	    switch ( d->parseReference_context ) {
+		case InContent:
+		    // Included
+		    if ( !insertXmlRef( it.data(), reference, FALSE ) )
+			return FALSE;
+		    d->parseReference_charDataRead = FALSE;
+		    break;
+		case InAttributeValue:
+		    // Included in literal
+		    if ( !insertXmlRef( it.data(), reference, TRUE ) )
+			return FALSE;
+		    d->parseReference_charDataRead = FALSE;
+		    break;
+		case InEntityValue:
+		    {
+			// Bypassed
+			stringAddC( '&' );
+			for ( int i=0; i<(int)reference.length(); i++ ) {
+			    stringAddC( reference[i] );
+			}
+			stringAddC( ';');
+			d->parseReference_charDataRead = TRUE;
+		    }
+		    break;
+		case InDTD:
+		    // Forbidden
+		    d->parseReference_charDataRead = FALSE;
+		    reportParseError( XMLERR_INTERNALGENERALENTITYINDTD );
+		    return FALSE;
+	    }
+	} else {
+	    QMap<QString,QXmlSimpleReaderPrivate::ExternEntity>::Iterator itExtern;
+	    itExtern = d->externEntities.find( reference );
+	    if ( itExtern == d->externEntities.end() ) {
+		// entity not declared
+		// ### check this case for conformance
+		if ( d->parseReference_context == InEntityValue ) {
+		    // Bypassed
+		    stringAddC( '&' );
+		    for ( int i=0; i<(int)reference.length(); i++ ) {
+			stringAddC( reference[i] );
+		    }
+		    stringAddC( ';');
+		    d->parseReference_charDataRead = TRUE;
+		} else {
+		    if ( contentHnd ) {
+			if ( !contentHnd->skippedEntity( reference ) ) {
+			    reportParseError( contentHnd->errorString() );
+			    return FALSE; // error
+			}
+		    }
+		}
+	    } else if ( (*itExtern).notation.isNull() ) {
+		// "External Parsed General"
+		switch ( d->parseReference_context ) {
+		    case InContent:
+			{
+			    // Included if validating
+			    bool skipIt = TRUE;
+			    if ( entityRes ) {
+				QXmlInputSource *ret = 0;
+				if ( !entityRes->resolveEntity( itExtern.data().publicId, itExtern.data().systemId, ret ) ) {
+				    delete ret;
+				    reportParseError( entityRes->errorString() );
+				    return FALSE;
+				}
+				if ( ret ) {
+				    QString xmlRefString = ret->data();
+				    delete ret;
+				    if ( !stripTextDecl( xmlRefString ) ) {
+					reportParseError( XMLERR_ERRORINTEXTDECL );
+					return FALSE;
+				    }
+				    if ( !insertXmlRef( xmlRefString, reference, FALSE ) )
+					return FALSE;
+				    skipIt = FALSE;
+				}
+			    }
+			    if ( skipIt && contentHnd ) {
+				if ( !contentHnd->skippedEntity( reference ) ) {
+				    reportParseError( contentHnd->errorString() );
+				    return FALSE; // error
+				}
+			    }
+			    d->parseReference_charDataRead = FALSE;
+			} break;
+		    case InAttributeValue:
+			// Forbidden
+			d->parseReference_charDataRead = FALSE;
+			reportParseError( XMLERR_EXTERNALGENERALENTITYINAV );
+			return FALSE;
+		    case InEntityValue:
+			{
+			    // Bypassed
+			    stringAddC( '&' );
+			    for ( int i=0; i<(int)reference.length(); i++ ) {
+				stringAddC( reference[i] );
+			    }
+			    stringAddC( ';');
+			    d->parseReference_charDataRead = TRUE;
+			}
+			break;
+		    case InDTD:
+			// Forbidden
+			d->parseReference_charDataRead = FALSE;
+			reportParseError( XMLERR_EXTERNALGENERALENTITYINDTD );
+			return FALSE;
+		}
+	    } else {
+		// "Unparsed"
+		// ### notify for "Occurs as Attribute Value" missing (but this is no refence, anyway)
+		// Forbidden
+		d->parseReference_charDataRead = FALSE;
+		reportParseError( XMLERR_UNPARSEDENTITYREFERENCE );
+		return FALSE; // error
+	    }
+	}
+    }
+    return TRUE; // no error
+}
+
+
+/*
+  Parses over a simple string.
+
+  After the string was successfully parsed, the head is on the first
+  character after the string.
+*/
+bool QXmlSimpleReader::parseString()
+{
+    const signed char InpCharExpected  = 0; // the character that was expected
+    const signed char InpUnknown       = 1;
+
+    signed char state; // state in this function is the position in the string s
+    signed char input;
+
+    if ( d->parseStack==0 || d->parseStack->isEmpty() ) {
+	d->Done = d->parseString_s.length();
+	state = 0;
+    } else {
+	state = d->parseStack->top()->state;
+	d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+	qDebug( "QXmlSimpleReader: parseString (cont) in state %d", state );
+#endif
+	if ( !d->parseStack->isEmpty() ) {
+	    ParseFunction function = d->parseStack->top()->function;
+	    if ( function == &QXmlSimpleReader::eat_ws ) {
+		d->parseStack->remove();
+#if defined(QT_QXML_DEBUG)
+		qDebug( "QXmlSimpleReader: eat_ws (cont)" );
+#endif
+	    }
+	    if ( !(this->*function)() ) {
+		parseFailed( &QXmlSimpleReader::parseString, state );
+		return FALSE;
+	    }
+	}
+    }
+
+    for (;;) {
+	if ( state == d->Done ) {
+	    return TRUE;
+	}
+
+	if ( atEnd() ) {
+	    unexpectedEof( &QXmlSimpleReader::parseString, state );
+	    return FALSE;
+	}
+	if ( c == d->parseString_s[(int)state] ) {
+	    input = InpCharExpected;
+	} else {
+	    input = InpUnknown;
+	}
+	if ( input == InpCharExpected ) {
+	    state++;
+	} else {
+	    // Error
+	    reportParseError( XMLERR_UNEXPECTEDCHARACTER );
+	    return FALSE;
+	}
+
+	next();
+    }
+}
+
+/*
+  This private function inserts and reports an entity substitution. The
+  substituted string is \a data and the name of the entity reference is \a
+  name. If \a inLiteral is TRUE, the entity is IncludedInLiteral (i.e., " and '
+  must be quoted. Otherwise they are not quoted.
+
+  This function returns FALSE on error.
+*/
+bool QXmlSimpleReader::insertXmlRef( const QString &data, const QString &name, bool inLiteral )
+{
+    if ( inLiteral ) {
+	QString tmp = data;
+	d->xmlRef.push( tmp.replace( "\"", "&quot;" ).replace( "'", "&apos;" ) );
+    } else {
+	d->xmlRef.push( data );
+    }
+    d->xmlRefName.push( name );
+    uint n = (uint)QMAX( d->parameterEntities.count(), d->entities.count() );
+    if ( d->xmlRefName.count() > n+1 ) {
+	// recursive entities
+	reportParseError( XMLERR_RECURSIVEENTITIES );
+	return FALSE;
+    }
+    if ( d->reportEntities && lexicalHnd ) {
+	if ( !lexicalHnd->startEntity( name ) ) {
+	    reportParseError( lexicalHnd->errorString() );
+	    return FALSE;
+	}
+    }
+    return TRUE;
+}
+
+/*
+  This private function moves the cursor to the next character.
+*/
+void QXmlSimpleReader::next()
+{
+    int count = (uint)d->xmlRef.count();
+    while ( count != 0 ) {
+	if ( d->xmlRef.top().isEmpty() ) {
+	    d->xmlRef.pop_back();
+	    d->xmlRefName.pop_back();
+	    count--;
+	} else {
+	    c = d->xmlRef.top().constref( 0 );
+	    d->xmlRef.top().remove( (uint)0, 1 );
+	    return;
+	}
+    }
+    // the following could be written nicer, but since it is a time-critical
+    // function, rather optimize for speed
+    if ( c == '\n' ) {
+	c = inputSource->next();
+	lineNr++;
+	columnNr = -1;
+    } else if ( c == '\r' ) {
+	c = inputSource->next();
+	if ( c != '\n' ) {
+	    lineNr++;
+	    columnNr = -1;
+	}
+    } else {
+	c = inputSource->next();
+    }
+    columnNr++;
+}
+
+/*
+  This private function moves the cursor to the next non-whitespace character.
+  This function does not move the cursor if the actual cursor position is a
+  non-whitespace charcter.
+
+  Returns FALSE when you use incremental parsing and this function reaches EOF
+  with reading only whitespace characters. In this case it also poplulates the
+  parseStack with useful information. In all other cases, this function returns
+  TRUE.
+*/
+bool QXmlSimpleReader::eat_ws()
+{
+    while ( !atEnd() ) {
+	if ( !is_S(c) ) {
+	    return TRUE;
+	}
+	next();
+    }
+    if ( d->parseStack != 0 ) {
+	unexpectedEof( &QXmlSimpleReader::eat_ws, 0 );
+	return FALSE;
+    }
+    return TRUE;
+}
+
+bool QXmlSimpleReader::next_eat_ws()
+{
+    next();
+    return eat_ws();
+}
+
+
+/*
+  This private function initializes the reader. \a i is the input source to
+  read the data from.
+*/
+void QXmlSimpleReader::init( const QXmlInputSource *i )
+{
+    lineNr = 0;
+    columnNr = -1;
+    inputSource = (QXmlInputSource *)i;
+    initData();
+
+    d->externParameterEntities.clear();
+    d->parameterEntities.clear();
+    d->externEntities.clear();
+    d->entities.clear();
+
+    d->tags.clear();
+
+    d->doctype = "";
+    d->xmlVersion = "";
+    d->encoding = "";
+    d->standalone = QXmlSimpleReaderPrivate::Unknown;
+    d->error = QString::null;
+}
+
+/*
+  This private function initializes the XML data related variables. Especially,
+  it reads the data from the input source.
+*/
+void QXmlSimpleReader::initData()
+{
+    c = QXmlInputSource::EndOfData;
+    d->xmlRef.clear();
+    next();
+}
+
+/*
+  Returns TRUE if a entity with the name \a e exists,
+  otherwise returns FALSE.
+*/
+bool QXmlSimpleReader::entityExist( const QString& e ) const
+{
+    if (  d->parameterEntities.find(e) == d->parameterEntities.end() &&
+	  d->externParameterEntities.find(e) == d->externParameterEntities.end() &&
+	  d->externEntities.find(e) == d->externEntities.end() &&
+	  d->entities.find(e) == d->entities.end() ) {
+	return FALSE;
+    } else {
+	return TRUE;
+    }
+}
+
+void QXmlSimpleReader::reportParseError( const QString& error )
+{
+    d->error = error;
+    if ( errorHnd ) {
+	if ( d->error.isNull() ) {
+	    errorHnd->fatalError( QXmlParseException( XMLERR_OK, columnNr+1, lineNr+1 ) );
+	} else {
+	    errorHnd->fatalError( QXmlParseException( d->error, columnNr+1, lineNr+1 ) );
+	}
+    }
+}
+
+/*
+  This private function is called when a parsing function encounters an
+  unexpected EOF. It decides what to do (depending on incremental parsing or
+  not). \a where is a pointer to the function where the error occurred and \a
+  state is the parsing state in this function.
+*/
+void QXmlSimpleReader::unexpectedEof( ParseFunction where, int state )
+{
+    if ( d->parseStack == 0 ) {
+	reportParseError( XMLERR_UNEXPECTEDEOF );
+    } else {
+	if ( c == QXmlInputSource::EndOfDocument ) {
+	    reportParseError( XMLERR_UNEXPECTEDEOF );
+	} else {
+	    pushParseState( where, state );
+	}
+    }
+}
+
+/*
+  This private function is called when a parse...() function returned FALSE. It
+  determines if there was an error or if incremental parsing simply went out of
+  data and does the right thing for the case. \a where is a pointer to the
+  function where the error occurred and \a state is the parsing state in this
+  function.
+*/
+void QXmlSimpleReader::parseFailed( ParseFunction where, int state )
+{
+    if ( d->parseStack!=0 && d->error.isNull() ) {
+	pushParseState( where, state );
+    }
+}
+
+/*
+  This private function pushes the function pointer \a function and state \a
+  state to the parse stack. This is used when you are doing an incremental
+  parsing and reach the end of file too early.
+
+  Only call this function when d->parseStack!=0.
+*/
+void QXmlSimpleReader::pushParseState( ParseFunction function, int state )
+{
+    QXmlSimpleReaderPrivate::ParseState *ps = new QXmlSimpleReaderPrivate::ParseState;
+    ps->function = function;
+    ps->state = state;
+    d->parseStack->push( ps );
+}
+
+
+// use buffers instead of QString::operator+= when single characters are read
+QString& QXmlSimpleReader::string()
+{
+    stringValue += QString( stringArray, stringPos );
+    stringPos = 0;
+    return stringValue;
+}
+QString& QXmlSimpleReader::name()
+{
+    nameValue += QString( nameArray, namePos );
+    namePos = 0;
+    return nameValue;
+}
+QString& QXmlSimpleReader::ref()
+{
+    refValue += QString( refArray, refPos );
+    refPos = 0;
+    return refValue;
+}
+
+void QXmlSimpleReader::stringAddC()
+{
+    if ( stringPos >= 256 ) {
+	stringValue += QString( stringArray, stringPos );
+	stringPos = 0;
+    }
+    stringArray[stringPos++] = c;
+}
+void QXmlSimpleReader::nameAddC()
+{
+    if ( namePos >= 256 ) {
+	nameValue += QString( nameArray, namePos );
+	namePos = 0;
+    }
+    nameArray[namePos++] = c;
+}
+void QXmlSimpleReader::refAddC()
+{
+    if ( refPos >= 256 ) {
+	refValue += QString( refArray, refPos );
+	refPos = 0;
+    }
+    refArray[refPos++] = c;
+}
+
+void QXmlSimpleReader::stringAddC(const QChar& ch)
+{
+    if ( stringPos >= 256 ) {
+	stringValue += QString( stringArray, stringPos );
+	stringPos = 0;
+    }
+    stringArray[stringPos++] = ch;
+}
+void QXmlSimpleReader::nameAddC(const QChar& ch)
+{
+    if ( namePos >= 256 ) {
+	nameValue += QString( nameArray, namePos );
+	namePos = 0;
+    }
+    nameArray[namePos++] = ch;
+}
+void QXmlSimpleReader::refAddC(const QChar& ch)
+{
+    if ( refPos >= 256 ) {
+	refValue += QString( refArray, refPos );
+	refPos = 0;
+    }
+    refArray[refPos++] = ch;
+}
+
+} // namespace PsiXml
+
+#endif //QT_NO_XML
Index: psi/tags/0.10-os2-b1/src/tools/qxml/qxml.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/qxml/qxml.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/qxml/qxml.h	(revision 38)
@@ -0,0 +1,557 @@
+/****************************************************************************
+** $Id: qxml.h,v 1.1 2004/02/29 00:05:06 justin Exp $
+**
+** Definition of QXmlSimpleReader and related classes.
+**
+** Created : 000518
+**
+** Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
+**
+** This file is part of the xml module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition licenses may use this
+** file in accordance with the Qt Commercial License Agreement provided
+** with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+**   information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QXML_H
+#define QXML_H
+
+#ifndef QT_H
+#include "qtextstream.h"
+#include "qfile.h"
+#include "qstring.h"
+#include "qstringlist.h"
+#endif // QT_H
+
+#if !defined(QT_MODULE_XML) || defined( QT_LICENSE_PROFESSIONAL ) || defined( QT_INTERNAL_XML )
+#define QM_EXPORT_XML
+#else
+#define QM_EXPORT_XML Q_EXPORT
+#endif
+
+#ifndef QT_NO_XML
+
+namespace PsiXml {
+
+class QXmlNamespaceSupport;
+class QXmlAttributes;
+class QXmlContentHandler;
+class QXmlDefaultHandler;
+class QXmlDTDHandler;
+class QXmlEntityResolver;
+class QXmlErrorHandler;
+class QXmlLexicalHandler;
+class QXmlDeclHandler;
+class QXmlInputSource;
+class QXmlLocator;
+class QXmlNamespaceSupport;
+class QXmlParseException;
+
+class QXmlReader;
+class QXmlSimpleReader;
+
+class QXmlSimpleReaderPrivate;
+class QXmlNamespaceSupportPrivate;
+class QXmlAttributesPrivate;
+class QXmlInputSourcePrivate;
+class QXmlParseExceptionPrivate;
+class QXmlLocatorPrivate;
+class QXmlDefaultHandlerPrivate;
+
+
+//
+// SAX Namespace Support
+//
+
+class QM_EXPORT_XML QXmlNamespaceSupport
+{
+public:
+    QXmlNamespaceSupport();
+    ~QXmlNamespaceSupport();
+
+    void setPrefix( const QString&, const QString& );
+
+    QString prefix( const QString& ) const;
+    QString uri( const QString& ) const;
+    void splitName( const QString&, QString&, QString& ) const;
+    void processName( const QString&, bool, QString&, QString& ) const;
+    QStringList prefixes() const;
+    QStringList prefixes( const QString& ) const;
+
+    void pushContext();
+    void popContext();
+    void reset();
+
+private:
+    QXmlNamespaceSupportPrivate *d;
+};
+
+
+//
+// SAX Attributes
+//
+
+class QM_EXPORT_XML QXmlAttributes
+{
+public:
+    QXmlAttributes() {}
+    virtual ~QXmlAttributes() {}
+
+    int index( const QString& qName ) const;
+    int index( const QString& uri, const QString& localPart ) const;
+    int length() const;
+    int count() const;
+    QString localName( int index ) const;
+    QString qName( int index ) const;
+    QString uri( int index ) const;
+    QString type( int index ) const;
+    QString type( const QString& qName ) const;
+    QString type( const QString& uri, const QString& localName ) const;
+    QString value( int index ) const;
+    QString value( const QString& qName ) const;
+    QString value( const QString& uri, const QString& localName ) const;
+
+    void clear();
+    void append( const QString &qName, const QString &uri, const QString &localPart, const QString &value );
+
+private:
+    QStringList qnameList;
+    QStringList uriList;
+    QStringList localnameList;
+    QStringList valueList;
+
+    QXmlAttributesPrivate *d;
+};
+
+//
+// SAX Input Source
+//
+
+class QM_EXPORT_XML QXmlInputSource
+{
+public:
+    QXmlInputSource();
+    QXmlInputSource( QIODevice *dev );
+    QXmlInputSource( QFile& file ); // obsolete
+    QXmlInputSource( QTextStream& stream ); // obsolete
+    virtual ~QXmlInputSource();
+
+    virtual void setData( const QString& dat );
+    virtual void setData( const QByteArray& dat );
+    virtual void fetchData();
+    virtual QString data();
+    virtual QChar next();
+    virtual void reset();
+
+    static const QChar EndOfData;
+    static const QChar EndOfDocument;
+
+protected:
+    virtual QString fromRawData( const QByteArray &data, bool beginning = FALSE );
+
+private:
+    void init();
+
+    QIODevice *inputDevice;
+    QTextStream *inputStream;
+
+    QString str;
+    const QChar *unicode;
+    int pos;
+    int length;
+    bool nextReturnedEndOfData;
+    QTextDecoder *encMapper;
+
+    QXmlInputSourcePrivate *d;
+};
+
+//
+// SAX Exception Classes
+//
+
+class QM_EXPORT_XML QXmlParseException
+{
+public:
+    QXmlParseException( const QString& name="", int c=-1, int l=-1, const QString& p="", const QString& s="" )
+	: msg( name ), column( c ), line( l ), pub( p ), sys( s )
+    { }
+
+    int columnNumber() const;
+    int lineNumber() const;
+    QString publicId() const;
+    QString systemId() const;
+    QString message() const;
+
+private:
+    QString msg;
+    int column;
+    int line;
+    QString pub;
+    QString sys;
+
+    QXmlParseExceptionPrivate *d;
+};
+
+
+//
+// XML Reader
+//
+
+class QM_EXPORT_XML QXmlReader
+{
+public:
+    virtual bool feature( const QString& name, bool *ok = 0 ) const = 0;
+    virtual void setFeature( const QString& name, bool value ) = 0;
+    virtual bool hasFeature( const QString& name ) const = 0;
+    virtual void* property( const QString& name, bool *ok = 0 ) const = 0;
+    virtual void setProperty( const QString& name, void* value ) = 0;
+    virtual bool hasProperty( const QString& name ) const = 0;
+    virtual void setEntityResolver( QXmlEntityResolver* handler ) = 0;
+    virtual QXmlEntityResolver* entityResolver() const = 0;
+    virtual void setDTDHandler( QXmlDTDHandler* handler ) = 0;
+    virtual QXmlDTDHandler* DTDHandler() const = 0;
+    virtual void setContentHandler( QXmlContentHandler* handler ) = 0;
+    virtual QXmlContentHandler* contentHandler() const = 0;
+    virtual void setErrorHandler( QXmlErrorHandler* handler ) = 0;
+    virtual QXmlErrorHandler* errorHandler() const = 0;
+    virtual void setLexicalHandler( QXmlLexicalHandler* handler ) = 0;
+    virtual QXmlLexicalHandler* lexicalHandler() const = 0;
+    virtual void setDeclHandler( QXmlDeclHandler* handler ) = 0;
+    virtual QXmlDeclHandler* declHandler() const = 0;
+    virtual bool parse( const QXmlInputSource& input ) = 0;
+    virtual bool parse( const QXmlInputSource* input ) = 0;
+};
+
+class QM_EXPORT_XML QXmlSimpleReader : public QXmlReader
+{
+public:
+    QXmlSimpleReader();
+    virtual ~QXmlSimpleReader();
+
+    bool feature( const QString& name, bool *ok = 0 ) const;
+    void setFeature( const QString& name, bool value );
+    bool hasFeature( const QString& name ) const;
+
+    void* property( const QString& name, bool *ok = 0 ) const;
+    void setProperty( const QString& name, void* value );
+    bool hasProperty( const QString& name ) const;
+
+    void setEntityResolver( QXmlEntityResolver* handler );
+    QXmlEntityResolver* entityResolver() const;
+    void setDTDHandler( QXmlDTDHandler* handler );
+    QXmlDTDHandler* DTDHandler() const;
+    void setContentHandler( QXmlContentHandler* handler );
+    QXmlContentHandler* contentHandler() const;
+    void setErrorHandler( QXmlErrorHandler* handler );
+    QXmlErrorHandler* errorHandler() const;
+    void setLexicalHandler( QXmlLexicalHandler* handler );
+    QXmlLexicalHandler* lexicalHandler() const;
+    void setDeclHandler( QXmlDeclHandler* handler );
+    QXmlDeclHandler* declHandler() const;
+
+    bool parse( const QXmlInputSource& input );
+    bool parse( const QXmlInputSource* input );
+    virtual bool parse( const QXmlInputSource* input, bool incremental );
+    virtual bool parseContinue();
+
+private:
+    // variables
+    QXmlContentHandler *contentHnd;
+    QXmlErrorHandler   *errorHnd;
+    QXmlDTDHandler     *dtdHnd;
+    QXmlEntityResolver *entityRes;
+    QXmlLexicalHandler *lexicalHnd;
+    QXmlDeclHandler    *declHnd;
+
+    QXmlInputSource *inputSource;
+
+    QChar c; // the character at reading position
+    int   lineNr; // number of line
+    int   columnNr; // position in line
+
+    int     namePos;
+    QChar   nameArray[256]; // only used for names
+    QString nameValue; // only used for names
+    int     refPos;
+    QChar   refArray[256]; // only used for references
+    QString refValue; // only used for references
+    int     stringPos;
+    QChar   stringArray[256]; // used for any other strings that are parsed
+    QString stringValue; // used for any other strings that are parsed
+
+    QXmlSimpleReaderPrivate* d;
+
+    // inlines
+    bool is_S( const QChar& );
+    bool is_NameBeginning( const QChar& );
+    bool is_NameChar( const QChar& );
+
+    QString& string();
+    void stringClear();
+    void stringAddC();
+    void stringAddC(const QChar&);
+    QString& name();
+    void nameClear();
+    void nameAddC();
+    void nameAddC(const QChar&);
+    QString& ref();
+    void refClear();
+    void refAddC();
+    void refAddC(const QChar&);
+
+    // used by parseReference() and parsePEReference()
+    enum EntityRecognitionContext { InContent, InAttributeValue, InEntityValue, InDTD };
+
+    // private functions
+    bool eat_ws();
+    bool next_eat_ws();
+
+    void next();
+    bool atEnd();
+
+    void init( const QXmlInputSource* i );
+    void initData();
+
+    bool entityExist( const QString& ) const;
+
+    bool parseBeginOrContinue( int state, bool incremental );
+
+    bool parseProlog();
+    bool parseElement();
+    bool processElementEmptyTag();
+    bool processElementETagBegin2();
+    bool processElementAttribute();
+    bool parseMisc();
+    bool parseContent();
+
+    bool parsePI();
+    bool parseDoctype();
+    bool parseComment();
+
+    bool parseName();
+    bool parseNmtoken();
+    bool parseAttribute();
+    bool parseReference();
+    bool processReference();
+
+    bool parseExternalID();
+    bool parsePEReference();
+    bool parseMarkupdecl();
+    bool parseAttlistDecl();
+    bool parseAttType();
+    bool parseAttValue();
+    bool parseElementDecl();
+    bool parseNotationDecl();
+    bool parseChoiceSeq();
+    bool parseEntityDecl();
+    bool parseEntityValue();
+
+    bool parseString();
+
+    bool insertXmlRef( const QString&, const QString&, bool );
+
+    bool reportEndEntities();
+    void reportParseError( const QString& error );
+
+    typedef bool (QXmlSimpleReader::*ParseFunction) ();
+    void unexpectedEof( ParseFunction where, int state );
+    void parseFailed( ParseFunction where, int state );
+    void pushParseState( ParseFunction function, int state );
+
+    friend class QXmlSimpleReaderPrivate;
+    friend class QXmlSimpleReaderLocator;
+};
+
+//
+// SAX Locator
+//
+
+class QM_EXPORT_XML QXmlLocator
+{
+public:
+    QXmlLocator();
+    virtual ~QXmlLocator();
+
+    virtual int columnNumber() = 0;
+    virtual int lineNumber() = 0;
+//    QString getPublicId()
+//    QString getSystemId()
+};
+
+//
+// SAX handler classes
+//
+
+class QM_EXPORT_XML QXmlContentHandler
+{
+public:
+    virtual void setDocumentLocator( QXmlLocator* locator ) = 0;
+    virtual bool startDocument() = 0;
+    virtual bool endDocument() = 0;
+    virtual bool startPrefixMapping( const QString& prefix, const QString& uri ) = 0;
+    virtual bool endPrefixMapping( const QString& prefix ) = 0;
+    virtual bool startElement( const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts ) = 0;
+    virtual bool endElement( const QString& namespaceURI, const QString& localName, const QString& qName ) = 0;
+    virtual bool characters( const QString& ch ) = 0;
+    virtual bool ignorableWhitespace( const QString& ch ) = 0;
+    virtual bool processingInstruction( const QString& target, const QString& data ) = 0;
+    virtual bool skippedEntity( const QString& name ) = 0;
+    virtual QString errorString() = 0;
+};
+
+class QM_EXPORT_XML QXmlErrorHandler
+{
+public:
+    virtual bool warning( const QXmlParseException& exception ) = 0;
+    virtual bool error( const QXmlParseException& exception ) = 0;
+    virtual bool fatalError( const QXmlParseException& exception ) = 0;
+    virtual QString errorString() = 0;
+};
+
+class QM_EXPORT_XML QXmlDTDHandler
+{
+public:
+    virtual bool notationDecl( const QString& name, const QString& publicId, const QString& systemId ) = 0;
+    virtual bool unparsedEntityDecl( const QString& name, const QString& publicId, const QString& systemId, const QString& notationName ) = 0;
+    virtual QString errorString() = 0;
+};
+
+class QM_EXPORT_XML QXmlEntityResolver
+{
+public:
+    virtual bool resolveEntity( const QString& publicId, const QString& systemId, QXmlInputSource*& ret ) = 0;
+    virtual QString errorString() = 0;
+};
+
+class QM_EXPORT_XML QXmlLexicalHandler
+{
+public:
+    virtual bool startDTD( const QString& name, const QString& publicId, const QString& systemId ) = 0;
+    virtual bool endDTD() = 0;
+    virtual bool startEntity( const QString& name ) = 0;
+    virtual bool endEntity( const QString& name ) = 0;
+    virtual bool startCDATA() = 0;
+    virtual bool endCDATA() = 0;
+    virtual bool comment( const QString& ch ) = 0;
+    virtual QString errorString() = 0;
+};
+
+class QM_EXPORT_XML QXmlDeclHandler
+{
+public:
+    virtual bool attributeDecl( const QString& eName, const QString& aName, const QString& type, const QString& valueDefault, const QString& value ) = 0;
+    virtual bool internalEntityDecl( const QString& name, const QString& value ) = 0;
+    virtual bool externalEntityDecl( const QString& name, const QString& publicId, const QString& systemId ) = 0;
+    virtual QString errorString() = 0;
+};
+
+
+class QM_EXPORT_XML QXmlDefaultHandler : public QXmlContentHandler, public QXmlErrorHandler, public QXmlDTDHandler, public QXmlEntityResolver, public QXmlLexicalHandler, public QXmlDeclHandler
+{
+public:
+    QXmlDefaultHandler() { }
+    virtual ~QXmlDefaultHandler() { }
+
+    void setDocumentLocator( QXmlLocator* locator );
+    bool startDocument();
+    bool endDocument();
+    bool startPrefixMapping( const QString& prefix, const QString& uri );
+    bool endPrefixMapping( const QString& prefix );
+    bool startElement( const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts );
+    bool endElement( const QString& namespaceURI, const QString& localName, const QString& qName );
+    bool characters( const QString& ch );
+    bool ignorableWhitespace( const QString& ch );
+    bool processingInstruction( const QString& target, const QString& data );
+    bool skippedEntity( const QString& name );
+
+    bool warning( const QXmlParseException& exception );
+    bool error( const QXmlParseException& exception );
+    bool fatalError( const QXmlParseException& exception );
+
+    bool notationDecl( const QString& name, const QString& publicId, const QString& systemId );
+    bool unparsedEntityDecl( const QString& name, const QString& publicId, const QString& systemId, const QString& notationName );
+
+    bool resolveEntity( const QString& publicId, const QString& systemId, QXmlInputSource*& ret );
+
+    bool startDTD( const QString& name, const QString& publicId, const QString& systemId );
+    bool endDTD();
+    bool startEntity( const QString& name );
+    bool endEntity( const QString& name );
+    bool startCDATA();
+    bool endCDATA();
+    bool comment( const QString& ch );
+
+    bool attributeDecl( const QString& eName, const QString& aName, const QString& type, const QString& valueDefault, const QString& value );
+    bool internalEntityDecl( const QString& name, const QString& value );
+    bool externalEntityDecl( const QString& name, const QString& publicId, const QString& systemId );
+
+    QString errorString();
+
+private:
+    QXmlDefaultHandlerPrivate *d;
+};
+
+
+//
+// inlines
+//
+
+inline bool QXmlSimpleReader::is_S(const QChar& ch)
+{
+    return ch==' ' || ch=='\t' || ch=='\n' || ch=='\r';
+}
+inline bool QXmlSimpleReader::is_NameBeginning( const QChar& ch )
+{
+    return ch=='_' || ch==':' ||
+	ch.isLetter() || // ### Category Lm is not allowed
+	ch.category()==QChar::Number_Letter;
+}
+inline bool QXmlSimpleReader::is_NameChar( const QChar& ch )
+{
+    return ch=='.' || ch=='-' || ch=='_' || ch==':' ||
+    ch.isLetterOrNumber() || // ### Category No is not allowed
+    ch.isMark();
+}
+
+inline bool QXmlSimpleReader::atEnd()
+{ return (c.unicode()|0x0001) == 0xffff; }
+
+inline void QXmlSimpleReader::stringClear()
+{ stringValue = QString::null; stringPos = 0; }
+inline void QXmlSimpleReader::nameClear()
+{ nameValue = QString::null; namePos = 0; }
+inline void QXmlSimpleReader::refClear()
+{ refValue = QString::null; refPos = 0; }
+
+inline int QXmlAttributes::count() const
+{ return length(); }
+
+} // namespace PsiXml
+
+using namespace PsiXml;
+
+#endif //QT_NO_XML
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch.cpp	(revision 38)
@@ -0,0 +1,60 @@
+/*
+ * systemwatch.h - Detect changes in the system state.
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "systemwatch.h"
+#if defined(Q_OS_MAC)
+	#include "systemwatch_mac.h"
+#elif defined(Q_OS_WIN32)
+	#include "systemwatch_win.h"
+#elif defined(Q_WS_PM)
+	#include "systemwatch_pm.h"
+#else
+	#include "systemwatch_unix.h"
+#endif
+
+
+SystemWatch::SystemWatch()
+{
+	SystemWatchImpl* impl;
+#if defined(Q_WS_MAC)
+	impl = MacSystemWatch::instance();
+#elif defined(Q_WS_WIN)
+	impl = WinSystemWatch::instance();
+#elif defined(Q_WS_PM)
+	impl = PMSystemWatch::instance();
+#else
+	impl = UnixSystemWatch::instance();
+#endif
+	connect(impl,SIGNAL(sleep()),this,SIGNAL(sleep()));
+	connect(impl,SIGNAL(idleSleep()),this,SIGNAL(idleSleep()));
+	connect(impl,SIGNAL(wakeup()),this,SIGNAL(wakeup()));
+}
+
+
+SystemWatch* SystemWatch::instance()
+{
+	if (!instance_) 
+		instance_ = new SystemWatch();
+
+	return instance_;
+}
+
+
+SystemWatch* SystemWatch::instance_ = 0;
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch.h	(revision 38)
@@ -0,0 +1,55 @@
+/*
+ * systemwatch.h - Detect changes in the system state.
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef SYSTEMWATCH_H
+#define SYSTEMWATCH_H
+
+#include <qobject.h>
+
+class SystemWatch : public QObject
+{
+	Q_OBJECT
+
+public:
+	static SystemWatch* instance();
+
+signals:
+	virtual void sleep();
+	virtual void idleSleep();
+	virtual void wakeup();
+
+private:
+	SystemWatch();
+
+	static SystemWatch* instance_;
+};
+
+
+class SystemWatchImpl : public QObject
+{
+	Q_OBJECT
+
+signals:
+	virtual void sleep();
+	virtual void idleSleep();
+	virtual void wakeup();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch.pri	(revision 38)
@@ -0,0 +1,21 @@
+systemwatch {
+	HEADERS += $$SYSTEMWATCH_CPP/systemwatch.h
+	SOURCES += $$SYSTEMWATCH_CPP/systemwatch.cpp
+
+	unix:!mac {
+		HEADERS += $$SYSTEMWATCH_CPP/systemwatch_unix.h
+		SOURCES += $$SYSTEMWATCH_CPP/systemwatch_unix.cpp
+	}
+	win32: {
+		HEADERS += $$SYSTEMWATCH_CPP/systemwatch_win.h
+		SOURCES += $$SYSTEMWATCH_CPP/systemwatch_win.cpp
+	}
+	os2: {
+		HEADERS += $$SYSTEMWATCH_CPP/systemwatch_pm.h
+		SOURCES += $$SYSTEMWATCH_CPP/systemwatch_pm.cpp
+	}
+	mac: {
+		HEADERS += $$SYSTEMWATCH_CPP/systemwatch_mac.h
+		SOURCES += $$SYSTEMWATCH_CPP/systemwatch_mac.cpp
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_mac.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_mac.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_mac.cpp	(revision 38)
@@ -0,0 +1,97 @@
+/*
+ * systemwatch_mac.cpp - Detect changes in the system state (Mac OS X).
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <mach/mach_port.h>
+#include <mach/mach_interface.h>
+#include <mach/mach_init.h>
+
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <IOKit/IOMessage.h>
+
+#include "systemwatch_mac.h"
+
+
+// -----------------------------------------------------------------------------
+// Callbacks
+// -----------------------------------------------------------------------------
+
+io_connect_t  root_port;
+
+void sleepCallBack(void *, io_service_t, natural_t messageType, void * messageArgument)
+{
+	//printf("messageType %08lx, arg %08lx\n",
+	//  (long unsigned int)messageType, (long unsigned int)messageArgument);
+	
+	switch (messageType) {
+		case kIOMessageSystemWillSleep:
+			// Sleep
+			MacSystemWatch::instance()->emitSleep();
+			IOAllowPowerChange(root_port, (long)messageArgument);
+			break;
+			
+		case kIOMessageCanSystemSleep:
+			// Idle time sleep
+
+			// TODO: Check if file transfers are running, and don't go to sleep
+			// if there are.
+			//IOCancelPowerChange(root_port, (long)messageArgument);
+
+			MacSystemWatch::instance()->emitIdleSleep();
+			IOAllowPowerChange(root_port, (long)messageArgument);
+			break;
+			
+		case kIOMessageSystemHasPoweredOn:
+			// Wakeup
+			MacSystemWatch::instance()->emitWakeup();
+			break;
+	}
+}
+
+
+// -----------------------------------------------------------------------------
+// MacSystemWatch
+// -----------------------------------------------------------------------------
+
+MacSystemWatch::MacSystemWatch()
+{
+	// Initialize sleep callback
+	IONotificationPortRef  notify;
+	io_object_t			   anIterator;
+	root_port = IORegisterForSystemPower(0, &notify, sleepCallBack, &anIterator);
+	if (root_port == NULL)
+			printf("IORegisterForSystemPower failed\n");
+	else
+		CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notify), kCFRunLoopCommonModes);
+}
+
+
+MacSystemWatch* MacSystemWatch::instance()
+{
+	if (!instance_) 
+		instance_ = new MacSystemWatch();
+
+	return instance_;
+}
+
+MacSystemWatch* MacSystemWatch::instance_ = 0;
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_mac.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_mac.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_mac.h	(revision 38)
@@ -0,0 +1,44 @@
+/*
+ * systemwatch_mac.h - Detect changes in the system state (Mac OS X).
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef SYSTEMWATCH_MAC_H
+#define SYSTEMWATCH_MAC_H
+
+#include "systemwatch.h"
+
+class MacSystemWatch : public SystemWatchImpl
+{
+	Q_OBJECT
+
+public:
+	static MacSystemWatch* instance();
+	
+	// These shouldn't be called from outside, but i can't hide them atm.
+	void emitSleep() { emit sleep(); }
+	void emitIdleSleep() { emit idleSleep(); }
+	void emitWakeup() { emit wakeup(); }
+
+private:
+	MacSystemWatch();
+	
+	static MacSystemWatch* instance_;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_pm.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_pm.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_pm.cpp	(revision 38)
@@ -0,0 +1,40 @@
+/*
+ * systemwatch_pm.h - Detect changes in the system state (OS/2 PM)
+ * Copyright (C) 2006  Dmitry A. Kuminov
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "systemwatch_pm.h"
+
+/// @todo (dmik)
+//  For now, it's a dummy class. Find a way to implement it.
+//  Also, it's not clear for me who deletes the singleton created
+//  by PMSystemWatch::instance(); investigate the code.
+
+PMSystemWatch::PMSystemWatch()
+{
+}
+
+PMSystemWatch* PMSystemWatch::instance()
+{
+	if (!instance_) 
+		instance_ = new PMSystemWatch();
+
+	return instance_;
+}
+
+PMSystemWatch* PMSystemWatch::instance_ = 0;
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_pm.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_pm.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_pm.h	(revision 38)
@@ -0,0 +1,39 @@
+/*
+ * systemwatch_pm.h - Detect changes in the system state (OS/2 PM)
+ * Copyright (C) 2006  Dmitry A. Kuminov
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef SYSTEMWATCH_PM_H
+#define SYSTEMWATCH_PM_H
+
+#include "systemwatch.h"
+
+class PMSystemWatch : public SystemWatchImpl
+{
+	Q_OBJECT
+
+public:
+	static PMSystemWatch *instance();
+	
+private:
+	PMSystemWatch();
+	
+	static PMSystemWatch *instance_;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_unix.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_unix.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_unix.cpp	(revision 38)
@@ -0,0 +1,35 @@
+/*
+ * systemwatch_unix.h - Detect changes in the system state (Unix).
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "systemwatch_unix.h"
+
+UnixSystemWatch::UnixSystemWatch()
+{
+}
+
+UnixSystemWatch* UnixSystemWatch::instance()
+{
+	if (!instance_) 
+		instance_ = new UnixSystemWatch();
+
+	return instance_;
+}
+
+UnixSystemWatch* UnixSystemWatch::instance_ = 0;
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_unix.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_unix.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_unix.h	(revision 38)
@@ -0,0 +1,40 @@
+/*
+ * systemwatch_unix.h - Detect changes in the system state (Unix).
+ * Copyright (C) 2005  Remko Troncon
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef SYSTEMWATCH_UNIX_H
+#define SYSTEMWATCH_UNIX_H
+
+#include "systemwatch.h"
+
+class UnixSystemWatch : public SystemWatchImpl
+{
+	Q_OBJECT
+
+public:
+	static UnixSystemWatch* instance();
+	
+private:
+	UnixSystemWatch();
+	
+	static UnixSystemWatch* instance_;
+};
+
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_win.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_win.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_win.cpp	(revision 38)
@@ -0,0 +1,117 @@
+/*
+ * systemwatch_mac.cpp - Detect changes in the system state (Windows).
+ * Copyright (C) 2005  James Chaldecott
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "systemwatch_win.h"
+
+#include <qwidget.h>
+#include <qt_windows.h>
+
+
+// -----------------------------------------------------------------------------
+// WinSystemWatchPrivate
+// -----------------------------------------------------------------------------
+
+class WinSystemWatch::WinSystemWatchPrivate : public QWidget
+{
+	Q_OBJECT
+
+public:
+	WinSystemWatchPrivate( ) : QWidget( 0 ) { }
+    ~WinSystemWatchPrivate() { }
+
+	bool winEvent( MSG * ); 
+
+signals:
+	void sleep();
+	void wakeup();
+};
+
+
+bool WinSystemWatch::WinSystemWatchPrivate::winEvent( MSG *m ) 
+{
+	if(WM_POWERBROADCAST == m->message) {
+		switch (m->wParam) {
+			case PBT_APMSUSPEND:
+				emit sleep();
+				break;
+
+			case PBT_APMRESUMESUSPEND:
+				emit wakeup();
+				break;
+
+			case PBT_APMRESUMECRITICAL:
+				// The system previously went into SUSPEND state (suddenly)
+				// without sending PBT_APMSUSPEND.  Net connections are
+				// probably invalid.  Not sure what to do about this.
+				// Maybe:
+				emit sleep();
+				emit wakeup();
+				break;
+
+			case PBT_APMQUERYSUSPEND:
+				// TODO: Check if file transfers are running, and don't go
+				// to sleep if there are.  To refuse to suspend, we somehow
+				// need to return BROADCAST_QUERY_DENY from the actual
+				// windows procedure.
+				break;
+
+			case WM_QUERYENDSESSION:
+				// TODO : If we allow the user to cancel suspend if they
+				// are doing a file transfer, we should probably also give
+				// them the chance to cancel a shutdown or log-off
+				break; 
+		} 
+	}
+	return QWidget::winEvent( m );
+}
+
+
+// -----------------------------------------------------------------------------
+// WinSystemWatch
+// -----------------------------------------------------------------------------
+
+WinSystemWatch::WinSystemWatch() 
+{
+	d = new WinSystemWatchPrivate();
+	connect(d,SIGNAL(sleep()),this,SIGNAL(sleep()));
+	connect(d,SIGNAL(wakeup()),this,SIGNAL(wakeup()));
+}
+
+WinSystemWatch::~WinSystemWatch()
+{
+	disconnect(d,SIGNAL(sleep()),this,SIGNAL(sleep()));
+	disconnect(d,SIGNAL(wakeup()),this,SIGNAL(wakeup()));
+	delete d;
+}
+
+WinSystemWatch* WinSystemWatch::instance()
+{
+	if (!instance_) 
+		instance_ = new WinSystemWatch();
+
+	return instance_;
+}
+
+WinSystemWatch* WinSystemWatch::instance_ = 0;
+
+// -----------------------------------------------------------------------------
+
+#include "systemwatch_win.moc"
+
Index: psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_win.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_win.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/systemwatch/systemwatch_win.h	(revision 38)
@@ -0,0 +1,25 @@
+#ifndef SYSTEMWATCH_WIN_H
+#define SYSTEMWATCH_WIN_H
+
+#include "systemwatch.h"
+
+class WinSystemWatch : public SystemWatchImpl
+{
+	Q_OBJECT
+
+public:
+	virtual ~WinSystemWatch();
+	static WinSystemWatch* instance();
+	// Win32 doesn't have an equivalent of idleSleep
+
+private:
+	WinSystemWatch();
+	
+	static WinSystemWatch* instance_;
+public:
+	class WinSystemWatchPrivate;
+private:
+	WinSystemWatchPrivate *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon.cpp	(revision 38)
@@ -0,0 +1,292 @@
+/*
+ * trayicon.cpp - system-independent trayicon class (adapted from Qt example)
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "trayicon.h"
+#include "qpopupmenu.h"
+
+/*!
+  \class TrayIcon qtrayicon.h
+  \brief The TrayIcon class implements an entry in the system tray.
+*/
+
+/*!
+  Creates a TrayIcon object. \a parent and \a name are propagated
+  to the QObject constructor. The icon is initially invisible.
+
+  \sa show
+*/
+TrayIcon::TrayIcon( QObject *parent, const char *name )
+: QObject(parent, name), pop(0), d(0)
+{
+	v_isWMDock = FALSE;
+}
+
+/*!
+  Creates a TrayIcon object displaying \a icon and \a tooltip, and opening
+  \a popup when clicked with the right mousebutton. \a parent and \a name are
+  propagated to the QObject constructor. The icon is initially invisible.
+
+  \sa show
+*/
+TrayIcon::TrayIcon( const QPixmap &icon, const QString &tooltip, QPopupMenu *popup, QObject *parent, const char *name )
+: QObject(parent, name), pop(popup), pm(icon), tip(tooltip), d(0)
+{
+	v_isWMDock = FALSE;
+
+	if ( !pm.width() || !pm.height() )
+		pm = QPixmap( 16, 16 );
+}
+
+/*!
+  Removes the icon from the system tray and frees all allocated resources.
+*/
+TrayIcon::~TrayIcon()
+{
+    sysRemove();
+}
+
+/*!
+  Informs the trayicon that the notification owner has probably been changed;
+  and that it should attempt to register or re-register.
+*/
+void TrayIcon::newTrayOwner()
+{
+    // detach ourself from any existing notification area.
+    hide();
+    // show ourself on the new notification area
+    show();
+}
+
+
+/*!
+  Sets the context menu to \a popup. The context menu will pop up when the
+  user clicks the system tray entry with the right mouse button.
+*/
+void TrayIcon::setPopup( QPopupMenu* popup )
+{
+    pop = popup;
+}
+
+/*!
+  Returns the current popup menu.
+
+  \sa setPopup
+*/
+QPopupMenu* TrayIcon::popup() const
+{
+    return pop;
+}
+
+/*!
+  \property TrayIcon::icon
+  \brief the system tray icon.
+*/
+void TrayIcon::setIcon( const QPixmap &icon )
+{
+    //if(!popup()) {
+    //    tip = "";
+    //}
+
+    pm = icon;
+    sysUpdateIcon();
+}
+
+QPixmap TrayIcon::icon() const
+{
+    return pm;
+}
+
+/*!
+  \property TrayIcon::toolTip
+  \brief the tooltip for the system tray entry
+
+  On some systems, the tooltip's length is limited and will be truncated as necessary.
+*/
+void TrayIcon::setToolTip( const QString &tooltip )
+{
+    tip = tooltip;
+    sysUpdateToolTip();
+}
+
+QString TrayIcon::toolTip() const
+{
+    return tip;
+}
+
+/*!
+  Shows the icon in the system tray.
+
+  \sa hide
+*/
+void TrayIcon::show()
+{
+    sysInstall();
+}
+
+/*!
+  Hides the system tray entry.
+*/
+void TrayIcon::hide()
+{
+    sysRemove();
+}
+
+/*!
+  \reimp
+*/
+bool TrayIcon::event( QEvent *e )
+{
+    switch ( e->type() ) {
+    case QEvent::MouseMove:
+	mouseMoveEvent( (QMouseEvent*)e );
+	break;
+
+    case QEvent::MouseButtonPress:
+	mousePressEvent( (QMouseEvent*)e );
+	break;
+
+    case QEvent::MouseButtonRelease:
+	mouseReleaseEvent( (QMouseEvent*)e );
+	break;
+
+    case QEvent::MouseButtonDblClick:
+	mouseDoubleClickEvent( (QMouseEvent*)e );
+	break;
+    default:
+	return QObject::event( e );
+    }
+
+    return TRUE;
+}
+
+/*!
+  This event handler can be reimplemented in a subclass to receive
+  mouse move events for the system tray entry.
+
+  \sa mousePressEvent(), mouseReleaseEvent(), mouseDoubleClickEvent(),  QMouseEvent
+*/
+void TrayIcon::mouseMoveEvent( QMouseEvent *e )
+{
+    e->ignore();
+}
+
+/*!
+  This event handler can be reimplemented in a subclass to receive
+  mouse press events for the system tray entry.
+
+  \sa mouseReleaseEvent(), mouseDoubleClickEvent(),
+  mouseMoveEvent(), QMouseEvent
+*/
+void TrayIcon::mousePressEvent( QMouseEvent *e )
+{
+#ifndef Q_WS_WIN
+// This is for X11, menus appear on mouse press
+// I'm not sure whether Mac should be here or below.. Somebody check?
+	switch ( e->button() ) {
+		case RightButton:
+			if ( pop ) {
+				pop->popup( e->globalPos() );
+				e->accept();
+			}
+			break;
+		case LeftButton:
+		case MidButton:
+			emit clicked( e->globalPos(), e->button() );
+			break;
+		default:
+			break;
+	}
+#endif
+	e->ignore();
+}
+
+/*!
+  This event handler can be reimplemented in a subclass to receive
+  mouse release events for the system tray entry.
+
+  The default implementations opens the context menu when the entry
+  has been clicked with the right mouse button.
+
+  \sa setPopup(), mousePressEvent(), mouseDoubleClickEvent(),
+  mouseMoveEvent(), QMouseEvent
+*/
+void TrayIcon::mouseReleaseEvent( QMouseEvent *e )
+{
+#ifdef Q_WS_WIN
+// This is for Windows, where menus appear on mouse release
+	switch ( e->button() ) {
+		case RightButton:
+			if ( pop ) {
+				// Necessary to make keyboard focus
+				// and menu closing work on Windows.
+				pop->setActiveWindow();
+				pop->popup( e->globalPos() );
+				pop->setActiveWindow();
+				e->accept();
+			}
+			break;
+		case LeftButton:
+		case MidButton:
+			emit clicked( e->globalPos(), e->button() );
+			break;
+		default:
+			break;
+	}
+#endif
+	e->ignore();
+}
+
+/*!
+  This event handler can be reimplemented in a subclass to receive
+  mouse double click events for the system tray entry.
+
+  Note that the system tray entry gets a mousePressEvent() and a
+  mouseReleaseEvent() before the mouseDoubleClickEvent().
+
+  \sa mousePressEvent(), mouseReleaseEvent(),
+  mouseMoveEvent(), QMouseEvent
+*/
+void TrayIcon::mouseDoubleClickEvent( QMouseEvent *e )
+{
+	if ( e->button() == LeftButton )
+		emit doubleClicked( e->globalPos() );
+	e->accept();
+}
+
+/*!
+  \fn void TrayIcon::clicked( const QPoint &p )
+
+  This signal is emitted when the user clicks the system tray icon
+  with the left mouse button, with \a p being the global mouse position
+  at that moment.
+*/
+
+/*!
+  \fn void TrayIcon::doubleClicked( const QPoint &p )
+
+  This signal is emitted when the user double clicks the system tray
+  icon with the left mouse button, with \a p being the global mouse position
+  at that moment.
+*/
+
+void TrayIcon::gotCloseEvent()
+{
+	closed();
+}
Index: psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon.h	(revision 38)
@@ -0,0 +1,94 @@
+/*
+ * trayicon.h - system-independent trayicon class (adapted from Qt example)
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_TRAYICON_H
+#define CS_TRAYICON_H
+
+#include <qobject.h>
+#include <qimage.h>
+
+class QPopupMenu;
+
+class TrayIcon : public QObject
+{
+	Q_OBJECT
+
+	Q_PROPERTY( QString toolTip READ toolTip WRITE setToolTip )
+	Q_PROPERTY( QPixmap icon READ icon WRITE setIcon )
+
+public:
+	TrayIcon( QObject *parent = 0, const char *name = 0 );
+	TrayIcon( const QPixmap &, const QString &, QPopupMenu *popup = 0, QObject *parent = 0, const char *name = 0 );
+	~TrayIcon();
+
+	// use WindowMaker dock mode.  ignored on non-X11 platforms
+	void setWMDock(bool use) { v_isWMDock = use; }
+	bool isWMDock() { return v_isWMDock; }
+
+	// Set a popup menu to handle RMB
+	void setPopup( QPopupMenu * );
+	QPopupMenu* popup() const;
+
+	QPixmap icon() const;
+	QString toolTip() const;
+
+	void gotCloseEvent();
+
+public slots:
+	void setIcon( const QPixmap &icon );
+	void setToolTip( const QString &tip );
+
+	void show();
+	void hide();
+
+	void newTrayOwner();
+
+signals:
+	void clicked( const QPoint&, int);
+	void doubleClicked( const QPoint& );
+	void closed();
+
+protected:
+	bool event( QEvent * );
+	virtual void mouseMoveEvent( QMouseEvent *e );
+	virtual void mousePressEvent( QMouseEvent *e );
+	virtual void mouseReleaseEvent( QMouseEvent *e );
+	virtual void mouseDoubleClickEvent( QMouseEvent *e );
+
+private:
+	QPopupMenu *pop;
+	QPixmap pm;
+	QString tip;
+	bool v_isWMDock;
+
+	// system-dependant part
+public:
+	class TrayIconPrivate;
+private:
+	TrayIconPrivate *d;
+	void sysInstall();
+	void sysRemove();
+	void sysUpdateIcon();
+	void sysUpdateToolTip();
+
+	friend class TrayIconPrivate;
+};
+
+#endif // CS_TRAYICON_H
Index: psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon.pri	(revision 38)
@@ -0,0 +1,17 @@
+trayicon {
+	HEADERS += $$TRAYICON_CPP/trayicon.h
+	SOURCES += $$TRAYICON_CPP/trayicon.cpp
+
+	unix:!mac {
+		SOURCES += $$TRAYICON_CPP/trayicon_x11.cpp
+	}
+	win32: {
+		SOURCES += $$TRAYICON_CPP/trayicon_win.cpp
+	}
+	os2: {
+		SOURCES += $$TRAYICON_CPP/trayicon_pm.cpp
+	}
+	mac: {
+		SOURCES += $$TRAYICON_CPP/trayicon_mac.cpp
+	}
+}
Index: psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_mac.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_mac.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_mac.cpp	(revision 38)
@@ -0,0 +1,17 @@
+#include "trayicon.h"
+
+void TrayIcon::sysInstall()
+{
+}
+
+void TrayIcon::sysRemove()
+{
+}
+
+void TrayIcon::sysUpdateIcon()
+{
+}
+
+void TrayIcon::sysUpdateToolTip()
+{
+}
Index: psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_pm.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_pm.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_pm.cpp	(revision 38)
@@ -0,0 +1,42 @@
+/*
+ * trayicon_pm.cpp - "trayicon" for OS/2 PM
+ * Copyright (C) 2006  Dmitry A. Kuminov
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "trayicon.h"
+
+/// @todo (dmik)
+//  For now, it's a dummy class. Implement it by either creating a separate
+//  xCenter widget or by using the generalized SysTray API
+//  (http://eros2.by.ru/systray_widget_en.shtml)
+
+void TrayIcon::sysInstall()
+{
+}
+
+void TrayIcon::sysRemove()
+{
+}
+
+void TrayIcon::sysUpdateIcon()
+{
+}
+
+void TrayIcon::sysUpdateToolTip()
+{
+}
Index: psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_win.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_win.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_win.cpp	(revision 38)
@@ -0,0 +1,259 @@
+/*
+ * trayicon_win.cpp - Windows trayicon, adapted from Qt example
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "trayicon.h"
+
+#include <qwidget.h>
+#include <qapplication.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qcursor.h>
+#include <qlibrary.h>
+
+#include <qt_windows.h>
+
+static uint WM_TASKBARCREATED = 0;
+#define WM_NOTIFYICON	(WM_APP+101)
+
+typedef BOOL (WINAPI *PtrShell_NotifyIcon)(DWORD,PNOTIFYICONDATA);
+static PtrShell_NotifyIcon ptrShell_NotifyIcon = 0;
+
+static void resolveLibs()
+{
+	QLibrary lib("shell32");
+	lib.setAutoUnload( FALSE );
+	static bool triedResolve = FALSE;
+	if ( !ptrShell_NotifyIcon && !triedResolve ) {
+		triedResolve = TRUE;
+		ptrShell_NotifyIcon = (PtrShell_NotifyIcon) lib.resolve( "Shell_NotifyIconW" );
+	}
+}
+
+class TrayIcon::TrayIconPrivate : public QWidget
+{
+public:
+    HICON		hIcon;
+    HBITMAP     hMask;
+    TrayIcon	*iconObject;
+
+	TrayIconPrivate( TrayIcon *object )
+        : QWidget( 0 ), hIcon( 0 ), hMask( 0 ), iconObject( object )
+    {
+		if ( !WM_TASKBARCREATED )
+			WM_TASKBARCREATED = RegisterWindowMessage( TEXT("TaskbarCreated") );
+	}
+
+    ~TrayIconPrivate()
+    {
+        if ( hMask ) {
+            DeleteObject( hMask );
+			hMask = 0; // michalj
+		}
+		if ( hIcon ) {
+			DestroyIcon( hIcon );
+			hIcon = 0; // michalj
+		}
+    }
+
+    // the unavoidable A/W versions. Don't forget to keep them in sync!
+    bool trayMessageA( DWORD msg )
+    {
+		NOTIFYICONDATAA tnd;
+		ZeroMemory( &tnd, sizeof(NOTIFYICONDATAA) );
+		tnd.cbSize		= sizeof(NOTIFYICONDATAA);
+ 		tnd.hWnd		= winId();
+		tnd.uID = 1; // michalj
+
+ 		if ( msg != NIM_DELETE ) {
+ 			tnd.uFlags		= NIF_MESSAGE|NIF_ICON|NIF_TIP;
+			tnd.uCallbackMessage= WM_NOTIFYICON;
+ 			tnd.hIcon		= hIcon;
+ 			if ( !iconObject->toolTip().isNull() ) {
+				// Tip is limited to 63 + NULL; lstrcpyn appends a NULL terminator.
+				QString tip = iconObject->toolTip().left( 63 ) + QChar();
+				lstrcpynA(tnd.szTip, (const char*)tip.local8Bit(), QMIN( tip.length()+1, 64 ) );
+ 			}
+ 		}
+
+		return Shell_NotifyIconA(msg, &tnd);
+    }
+
+#ifdef UNICODE
+    bool trayMessageW( DWORD msg )
+    {
+		resolveLibs();
+		if ( ! (ptrShell_NotifyIcon && qWinVersion() & Qt::WV_NT_based) )
+			return trayMessageA( msg );
+
+		NOTIFYICONDATAW tnd;
+		ZeroMemory( &tnd, sizeof(NOTIFYICONDATAW) );
+		tnd.cbSize		= sizeof(NOTIFYICONDATAW);
+		tnd.hWnd		= winId();
+		tnd.uID = 1; // michalj
+
+		if ( msg != NIM_DELETE ) {
+			tnd.uFlags		= NIF_MESSAGE|NIF_ICON|NIF_TIP;
+			tnd.uCallbackMessage= WM_NOTIFYICON;
+			tnd.hIcon		= hIcon;
+			if ( !iconObject->toolTip().isNull() ) {
+				// Tip is limited to 63 + NULL; lstrcpyn appends a NULL terminator.
+				QString tip = iconObject->toolTip().left( 63 ) + QChar();
+				lstrcpynW(tnd.szTip, (TCHAR*)tip.unicode(), QMIN( tip.length()+1, 64 ) );
+				//		lstrcpynW(tnd.szTip, (TCHAR*)qt_winTchar( tip, FALSE ), QMIN( tip.length()+1, 64 ) );
+			}
+		}
+		return ptrShell_NotifyIcon(msg, &tnd);
+    }
+#endif
+
+    bool trayMessage( DWORD msg )
+    {
+		QT_WA(
+			return trayMessageW(msg);
+			,
+			return trayMessageA(msg);
+			)
+    }
+
+    bool iconDrawItem(LPDRAWITEMSTRUCT lpdi)
+    {
+		if (!hIcon)
+			return FALSE;
+
+		DrawIconEx(lpdi->hDC, lpdi->rcItem.left, lpdi->rcItem.top, hIcon, 0, 0, 0, NULL, DI_NORMAL );
+		return TRUE;
+    }
+
+    bool winEvent( MSG *m )
+    {
+		switch(m->message) {
+			case WM_DRAWITEM:
+				return iconDrawItem( (LPDRAWITEMSTRUCT)m->lParam );
+			case WM_NOTIFYICON:
+			{
+				QMouseEvent *e = 0;
+				QPoint gpos = QCursor::pos();
+				switch (m->lParam) {
+					case WM_MOUSEMOVE: e = new QMouseEvent( QEvent::MouseMove, mapFromGlobal( gpos ), gpos, 0, 0 );	break;
+					case WM_LBUTTONDOWN: e = new QMouseEvent( QEvent::MouseButtonPress, mapFromGlobal( gpos ), gpos, LeftButton, LeftButton ); break;
+					case WM_LBUTTONUP: e = new QMouseEvent( QEvent::MouseButtonRelease, mapFromGlobal( gpos ), gpos, LeftButton, LeftButton ); break;
+					case WM_LBUTTONDBLCLK: e = new QMouseEvent( QEvent::MouseButtonDblClick, mapFromGlobal( gpos ), gpos, LeftButton, LeftButton );	break;
+					case WM_RBUTTONDOWN: e = new QMouseEvent( QEvent::MouseButtonPress, mapFromGlobal( gpos ), gpos, RightButton, RightButton ); break;
+					case WM_RBUTTONUP: e = new QMouseEvent( QEvent::MouseButtonRelease, mapFromGlobal( gpos ), gpos, RightButton, RightButton ); break;
+					case WM_RBUTTONDBLCLK: e = new QMouseEvent( QEvent::MouseButtonDblClick, mapFromGlobal( gpos ), gpos, RightButton, RightButton ); break;
+					case WM_MBUTTONDOWN: e = new QMouseEvent( QEvent::MouseButtonPress, mapFromGlobal( gpos ), gpos, MidButton, MidButton ); break;
+					case WM_MBUTTONUP: e = new QMouseEvent( QEvent::MouseButtonRelease, mapFromGlobal( gpos ), gpos, MidButton, MidButton ); break;
+					case WM_MBUTTONDBLCLK: e = new QMouseEvent( QEvent::MouseButtonDblClick, mapFromGlobal( gpos ), gpos, MidButton, MidButton ); break;
+					case WM_CONTEXTMENU: e = new QMouseEvent( QEvent::MouseButtonRelease, mapFromGlobal( gpos ), gpos, RightButton, RightButton ); break;
+				}
+				if ( e ) {
+					bool res = QApplication::sendEvent( iconObject, e );
+					delete e;
+					return res;
+				}
+			}
+			break;
+			default:
+				if ( m->message == WM_TASKBARCREATED ) trayMessage( NIM_ADD );
+		}
+		return QWidget::winEvent( m );
+    }
+};
+
+static HBITMAP createIconMask( const QPixmap &qp )
+{
+    QImage bm = qp.convertToImage();
+    int w = bm.width();
+    int h = bm.height();
+    int bpl = ((w+15)/16)*2;			// bpl, 16 bit alignment
+    uchar *bits = new uchar[bpl*h];
+    bm.invertPixels();
+    for ( int y=0; y<h; y++ )
+		memcpy( bits+y*bpl, bm.scanLine(y), bpl );
+    HBITMAP hbm = CreateBitmap( w, h, 1, 1, bits );
+    delete [] bits;
+    return hbm;
+}
+
+static HICON createIcon( const QPixmap &pm, HBITMAP &hbm )
+{
+    QPixmap maskpm( pm.size(), pm.depth(), QPixmap::NormalOptim );
+    QBitmap mask( pm.size(), FALSE, QPixmap::NormalOptim );
+    if ( pm.mask() ) {
+        maskpm.fill( Qt::black );			// make masked area black
+        bitBlt( &mask, 0, 0, pm.mask() );
+    } else
+        maskpm.fill( Qt::color1 );
+
+    bitBlt( &maskpm, 0, 0, &pm);
+    ICONINFO iconInfo;
+    iconInfo.fIcon    = TRUE;
+    iconInfo.hbmMask  = hbm = createIconMask(mask);
+    iconInfo.hbmColor = maskpm.hbm();
+
+    HICON icon = CreateIconIndirect( &iconInfo );
+    DeleteObject(iconInfo.hbmMask);
+        iconInfo.hbmMask = hbm = 0; // michalj
+    return icon;
+}
+
+void TrayIcon::sysInstall()
+{
+    if ( !d ) {
+		d = new TrayIconPrivate( this );
+		d->hIcon = createIcon( pm, d->hMask );
+
+		d->trayMessage( NIM_ADD );
+	}
+}
+
+void TrayIcon::sysRemove()
+{
+    if ( d ) {
+		d->trayMessage( NIM_DELETE );
+
+		delete d;
+	    d = 0;
+	}
+}
+
+void TrayIcon::sysUpdateIcon()
+{
+    if ( d ) {
+		if ( d->hMask ) {
+		   DeleteObject( d->hMask );
+			d->hMask = 0; // michalj
+		}
+		if ( d->hIcon ) {
+			DestroyIcon( d->hIcon );
+			d->hIcon = 0; // michalj
+		}
+
+		d->hIcon = createIcon( pm, d->hMask );
+		d->trayMessage( NIM_MODIFY );
+	}
+}
+
+void TrayIcon::sysUpdateToolTip()
+{
+    if ( d )
+		d->trayMessage( NIM_MODIFY );
+}
Index: psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_x11.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_x11.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/trayicon/trayicon_x11.cpp	(revision 38)
@@ -0,0 +1,406 @@
+/*
+ * trayicon_x11.cpp - X11 trayicon (for use with KDE and GNOME)
+ * Copyright (C) 2003  Justin Karneges
+ * GNOME2 Notification Area support: Tomasz Sterna
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "trayicon.h"
+
+#include<qapplication.h>
+#include<qimage.h>
+#include<qpixmap.h>
+#include<qtooltip.h>
+#include<qpainter.h>
+
+#include<X11/Xlib.h>
+#include<X11/Xutil.h>
+#include<X11/Xatom.h>
+
+//#if QT_VERSION < 0x030200
+extern Time qt_x_time;
+//#endif
+
+//----------------------------------------------------------------------------
+// common stuff
+//----------------------------------------------------------------------------
+
+// for Gnome2 Notification Area
+static XErrorHandler old_handler = 0;
+static int dock_xerror = 0;
+extern "C" int dock_xerrhandler(Display* dpy, XErrorEvent* err)
+{
+	dock_xerror = err->error_code;
+	return old_handler(dpy, err);
+}
+
+static void trap_errors()
+{
+	dock_xerror = 0;
+	old_handler = XSetErrorHandler(dock_xerrhandler);
+}
+
+static bool untrap_errors()
+{
+	XSetErrorHandler(old_handler);
+	return (dock_xerror == 0);
+}
+
+static bool send_message(
+	Display* dpy,	/* display */
+	Window w,	/* sender (tray icon window) */
+	long message,	/* message opcode */
+	long data1,	/* message data 1 */
+	long data2,	/* message data 2 */
+	long data3	/* message data 3 */
+) {
+	XEvent ev;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.xclient.type = ClientMessage;
+	ev.xclient.window = w;
+	ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
+	ev.xclient.format = 32;
+	ev.xclient.data.l[0] = CurrentTime;
+	ev.xclient.data.l[1] = message;
+	ev.xclient.data.l[2] = data1;
+	ev.xclient.data.l[3] = data2;
+	ev.xclient.data.l[4] = data3;
+
+	trap_errors();
+	XSendEvent(dpy, w, False, NoEventMask, &ev);
+	XSync(dpy, False);
+	return untrap_errors();
+}
+
+#define SYSTEM_TRAY_REQUEST_DOCK    0
+#define SYSTEM_TRAY_BEGIN_MESSAGE   1
+#define SYSTEM_TRAY_CANCEL_MESSAGE  2
+
+//----------------------------------------------------------------------------
+// TrayIcon::TrayIconPrivate
+//----------------------------------------------------------------------------
+
+class TrayIcon::TrayIconPrivate : public QWidget
+{
+public:
+	TrayIconPrivate(TrayIcon *object, int size);
+	~TrayIconPrivate() { }
+
+	virtual void initWM(WId icon);
+
+	virtual void setPixmap(const QPixmap &pm);
+
+	virtual void paintEvent(QPaintEvent *);
+	virtual void enterEvent(QEvent *);
+	virtual void mouseMoveEvent(QMouseEvent *e);
+	virtual void mousePressEvent(QMouseEvent *e);
+	virtual void mouseReleaseEvent(QMouseEvent *e);
+	virtual void mouseDoubleClickEvent(QMouseEvent *e);
+	virtual void closeEvent(QCloseEvent *e);
+
+private:
+	TrayIcon *iconObject;
+	QPixmap pix;
+	int size;
+};
+
+TrayIcon::TrayIconPrivate::TrayIconPrivate(TrayIcon *object, int _size)
+	: QWidget(0, "psidock", WRepaintNoErase)
+{
+	iconObject = object;
+	size = _size;
+
+	setFocusPolicy(NoFocus);
+	setBackgroundMode(X11ParentRelative);
+
+	setMinimumSize(size, size);
+	setMaximumSize(size, size);
+}
+
+// This base stuff is required by both FreeDesktop specification and WindowMaker
+void TrayIcon::TrayIconPrivate::initWM(WId icon)
+{
+	Display *dsp = x11Display();
+	WId leader   = winId();
+
+	// set the class hint
+	XClassHint classhint;
+	classhint.res_name  = (char*)"psidock";
+	classhint.res_class = (char*)"Psi";
+	XSetClassHint(dsp, leader, &classhint);
+
+	// set the Window Manager hints
+	XWMHints *hints;
+	hints = XGetWMHints(dsp, leader);	// init hints
+	hints->flags = WindowGroupHint | IconWindowHint | StateHint;	// set the window group hint
+	hints->window_group = leader;		// set the window hint
+	hints->initial_state = WithdrawnState;	// initial state
+	hints->icon_window = icon;		// in WM, this should be winId() of separate widget
+	hints->icon_x = 0;
+	hints->icon_y = 0;
+	XSetWMHints(dsp, leader, hints);	// set the window hints for WM to use.
+	XFree( hints );
+}
+
+void TrayIcon::TrayIconPrivate::setPixmap(const QPixmap &pm)
+{
+	pix = pm;
+	setIcon(pix);
+	repaint();
+}
+
+void TrayIcon::TrayIconPrivate::paintEvent(QPaintEvent *)
+{
+	QPainter p(this);
+	p.drawPixmap((width() - pix.width())/2, (height() - pix.height())/2, pix);
+}
+
+void TrayIcon::TrayIconPrivate::enterEvent(QEvent *e)
+{
+	// Taken from KSystemTray..
+//#if QT_VERSION < 0x030200
+	//if ( !qApp->focusWidget() ) {
+		XEvent ev;
+		memset(&ev, 0, sizeof(ev));
+		ev.xfocus.display = qt_xdisplay();
+		ev.xfocus.type = FocusIn;
+		ev.xfocus.window = winId();
+		ev.xfocus.mode = NotifyNormal;
+		ev.xfocus.detail = NotifyAncestor;
+		Time time = qt_x_time;
+		qt_x_time = 1;
+		qApp->x11ProcessEvent( &ev );
+		qt_x_time = time;
+	//}
+//#endif
+	QWidget::enterEvent(e);
+}
+
+void TrayIcon::TrayIconPrivate::mouseMoveEvent(QMouseEvent *e)
+{
+	QApplication::sendEvent(iconObject, e);
+}
+
+void TrayIcon::TrayIconPrivate::mousePressEvent(QMouseEvent *e)
+{
+	QApplication::sendEvent(iconObject, e);
+}
+
+void TrayIcon::TrayIconPrivate::mouseReleaseEvent(QMouseEvent *e)
+{
+	QApplication::sendEvent(iconObject, e);
+}
+
+void TrayIcon::TrayIconPrivate::mouseDoubleClickEvent(QMouseEvent *e)
+{
+	QApplication::sendEvent(iconObject, e);
+}
+
+void TrayIcon::TrayIconPrivate::closeEvent(QCloseEvent *e)
+{
+	iconObject->gotCloseEvent();
+	e->accept();
+}
+
+//----------------------------------------------------------------------------
+// TrayIconFreeDesktop
+//----------------------------------------------------------------------------
+
+class TrayIconFreeDesktop : public TrayIcon::TrayIconPrivate
+{
+public:
+	TrayIconFreeDesktop(TrayIcon *object, const QPixmap &pm);
+protected:
+	virtual bool x11Event(XEvent*);
+};
+
+TrayIconFreeDesktop::TrayIconFreeDesktop(TrayIcon *object, const QPixmap &pm)
+	: TrayIconPrivate(object, 22)
+{
+	initWM( winId() );
+
+	// initialize NetWM
+	Display *dsp = x11Display();
+
+	// dock the widget (adapted from SIM-ICQ)
+	Screen *screen = XDefaultScreenOfDisplay(dsp); // get the screen
+	int screen_id = XScreenNumberOfScreen(screen); // and it's number
+
+	// tell X that we want to see ClientMessage and Deleted events, which
+	// are picked up by QApplication::x11EventFilter
+	Window root_window = QApplication::desktop()->winId();
+	XWindowAttributes attr;
+
+	XGetWindowAttributes(dsp, root_window, &attr);
+	XSelectInput(dsp, root_window, attr.your_event_mask | StructureNotifyMask);
+
+	char buf[32];
+	snprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", screen_id);
+	Atom selection_atom = XInternAtom(dsp, buf, false);
+	XGrabServer(dsp);
+	Window manager_window = XGetSelectionOwner(dsp, selection_atom);
+	if ( manager_window != None )
+		XSelectInput(dsp, manager_window, StructureNotifyMask);
+	XUngrabServer(dsp);
+	XFlush(dsp);
+
+	if ( manager_window != None )
+		send_message(dsp, manager_window, SYSTEM_TRAY_REQUEST_DOCK, winId(), 0, 0);
+	else
+	{
+		object->hide();
+		return;
+	}
+
+	// some KDE mumbo-jumbo... why is it there? anybody?
+	Atom kwm_dockwindow_atom = XInternAtom(dsp, "KWM_DOCKWINDOW", false);
+	Atom kde_net_system_tray_window_for_atom = XInternAtom(dsp, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", false);
+
+	long data = 0;
+	XChangeProperty(dsp, winId(), kwm_dockwindow_atom, kwm_dockwindow_atom, 32, PropModeReplace, (uchar*)&data, 1);
+	XChangeProperty(dsp, winId(), kde_net_system_tray_window_for_atom, XA_WINDOW, 32, PropModeReplace, (uchar*)&data, 1);
+
+	setPixmap(pm);
+}
+
+bool TrayIconFreeDesktop::x11Event(XEvent *ev)
+{
+	switch(ev->type)
+	{
+		case ReparentNotify:
+			show();
+
+	}
+	return false;
+}
+
+//----------------------------------------------------------------------------
+// TrayIconWindowMaker
+//----------------------------------------------------------------------------
+
+class TrayIconWharf : public TrayIcon::TrayIconPrivate
+{
+public:
+	TrayIconWharf(TrayIcon *object, const QPixmap &pm)
+		: TrayIconPrivate(object, 44)
+	{
+		// set the class hint
+		XClassHint classhint;
+		classhint.res_name  = (char*)"psidock-wharf";
+		classhint.res_class = (char*)"Psi";
+		XSetClassHint(x11Display(), winId(), &classhint);
+
+		setPixmap(pm);
+	}
+
+	void setPixmap(const QPixmap &_pm)
+	{
+		QPixmap pm;
+		QImage i = _pm.convertToImage();
+		i = i.scale(i.width() * 2, i.height() * 2);
+		pm.convertFromImage(i);
+
+		TrayIconPrivate::setPixmap(pm);
+
+		// thanks to Robert Spier for this:
+		// for some reason the repaint() isn't being honored, or isn't for
+		// the icon.  So force one on the widget behind the icon
+		erase();
+		QPaintEvent pe( rect() );
+		paintEvent(&pe);
+	}
+};
+
+class TrayIconWindowMaker : public TrayIcon::TrayIconPrivate
+{
+public:
+	TrayIconWindowMaker(TrayIcon *object, const QPixmap &pm);
+	~TrayIconWindowMaker();
+
+	void setPixmap(const QPixmap &pm);
+
+private:
+	TrayIconWharf *wharf;
+};
+
+TrayIconWindowMaker::TrayIconWindowMaker(TrayIcon *object, const QPixmap &pm)
+	: TrayIconPrivate(object, 32)
+{
+	wharf = new TrayIconWharf(object, pm);
+
+	initWM( wharf->winId() );
+}
+
+TrayIconWindowMaker::~TrayIconWindowMaker()
+{
+	delete wharf;
+}
+
+void TrayIconWindowMaker::setPixmap(const QPixmap &pm)
+{
+	wharf->setPixmap(pm);
+}
+
+//----------------------------------------------------------------------------
+// TrayIcon
+//----------------------------------------------------------------------------
+
+void TrayIcon::sysInstall()
+{
+	if ( d )
+		return;
+
+	if ( v_isWMDock )
+		d = (TrayIconPrivate *)(new TrayIconWindowMaker(this, pm));
+	else
+		d = (TrayIconPrivate *)(new TrayIconFreeDesktop(this, pm));
+
+	sysUpdateToolTip();
+
+	if ( v_isWMDock )
+		d->show();
+}
+
+void TrayIcon::sysRemove()
+{
+	if ( !d )
+		return;
+
+	delete d;
+	d = 0;
+}
+
+void TrayIcon::sysUpdateIcon()
+{
+	if ( !d )
+		return;
+
+	QPixmap pix = pm;
+	d->setPixmap(pix);
+}
+
+void TrayIcon::sysUpdateToolTip()
+{
+	if ( !d )
+		return;
+
+	if ( tip.isEmpty() )
+		QToolTip::remove(d);
+	else
+		QToolTip::add(d, tip);
+}
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/ChangeLogUnzip
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/ChangeLogUnzip	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/ChangeLogUnzip	(revision 38)
@@ -0,0 +1,38 @@
+Change in 0.15: (19 Mar 98)
+- fix memory leak in minizip.c
+
+Change in 0.14: (10 Mar 98)
+- fix bugs in minizip.c sample for zipping big file
+- fix problem in month in date handling
+- fix bug in unzlocal_GetCurrentFileInfoInternal in unzip.c for 
+    comment handling
+
+Change in 0.13: (6 Mar 98)
+- fix bugs in zip.c
+- add real minizip sample
+
+Change in 0.12: (4 Mar 98)
+- add zip.c and zip.h for creates .zip file
+- fix change_file_date in miniunz.c for Unix (Jean-loup Gailly)
+- fix miniunz.c for file without specific record for directory
+
+Change in 0.11: (3 Mar 98)
+- fix bug in unzGetCurrentFileInfo for get extra field and comment
+- enhance miniunz sample, remove the bad unztst.c sample
+
+Change in 0.10: (2 Mar 98)
+- fix bug in unzReadCurrentFile
+- rename unzip* to unz* function and structure
+- remove Windows-like hungary notation variable name
+- modify some structure in unzip.h
+- add somes comment in source
+- remove unzipGetcCurrentFile function
+- replace ZUNZEXPORT by ZEXPORT
+- add unzGetLocalExtrafield for get the local extrafield info
+- add a new sample, miniunz.c
+
+Change in 0.4: (25 Feb 98)
+- suppress the type unzipFileInZip. 
+  Only on file in the zipfile can be open at the same time
+- fix somes typo in code
+- added tm_unz structure in unzip_file_info (date/time in readable format)
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/Makefile
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/Makefile	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/Makefile	(revision 38)
@@ -0,0 +1,25 @@
+CC=cc
+CFLAGS=-O -I../..
+
+UNZ_OBJS = miniunz.o unzip.o ../../libz.a
+ZIP_OBJS = minizip.o zip.o   ../../libz.a
+
+.c.o:
+	$(CC) -c $(CFLAGS) $*.c
+
+all: miniunz minizip
+
+miniunz:  $(UNZ_OBJS)
+	$(CC) $(CFLAGS) -o $@ $(UNZ_OBJS)
+
+minizip:  $(ZIP_OBJS)
+	$(CC) $(CFLAGS) -o $@ $(ZIP_OBJS)
+
+test:	miniunz minizip
+	./minizip test readme.txt
+	./miniunz -l test.zip
+	mv readme.txt readme.old
+	./miniunz test.zip
+
+clean:
+	/bin/rm -f *.o *~ minizip miniunz
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/miniunz.c
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/miniunz.c	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/miniunz.c	(revision 38)
@@ -0,0 +1,508 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef unix
+# include <unistd.h>
+# include <utime.h>
+#else
+# include <direct.h>
+# include <io.h>
+#endif
+
+#include "unzip.h"
+
+#define CASESENSITIVITY (0)
+#define WRITEBUFFERSIZE (8192)
+
+/*
+  mini unzip, demo of unzip package
+
+  usage :
+  Usage : miniunz [-exvlo] file.zip [file_to_extract]
+
+  list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT
+    if it exists
+*/
+
+
+/* change_file_date : change the date/time of a file
+    filename : the filename of the file where date/time must be modified
+    dosdate : the new date at the MSDos format (4 bytes)
+    tmu_date : the SAME new date at the tm_unz format */
+void change_file_date(filename,dosdate,tmu_date)
+	const char *filename;
+	uLong dosdate;
+	tm_unz tmu_date;
+{
+#ifdef WIN32
+  HANDLE hFile;
+  FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
+
+  hFile = CreateFile(filename,GENERIC_READ | GENERIC_WRITE,
+                      0,NULL,OPEN_EXISTING,0,NULL);
+  GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
+  DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
+  LocalFileTimeToFileTime(&ftLocal,&ftm);
+  SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
+  CloseHandle(hFile);
+#else
+#ifdef unix
+  struct utimbuf ut;
+  struct tm newdate;
+  newdate.tm_sec = tmu_date.tm_sec;
+  newdate.tm_min=tmu_date.tm_min;
+  newdate.tm_hour=tmu_date.tm_hour;
+  newdate.tm_mday=tmu_date.tm_mday;
+  newdate.tm_mon=tmu_date.tm_mon;
+  if (tmu_date.tm_year > 1900)
+      newdate.tm_year=tmu_date.tm_year - 1900;
+  else
+      newdate.tm_year=tmu_date.tm_year ;
+  newdate.tm_isdst=-1;
+
+  ut.actime=ut.modtime=mktime(&newdate);
+  utime(filename,&ut);
+#endif
+#endif
+}
+
+
+/* mymkdir and change_file_date are not 100 % portable
+   As I don't know well Unix, I wait feedback for the unix portion */
+
+int mymkdir(dirname)
+	const char* dirname;
+{
+    int ret=0;
+#ifdef WIN32
+	ret = mkdir(dirname);
+#else
+#ifdef unix
+	ret = mkdir (dirname,0775);
+#endif
+#endif
+	return ret;
+}
+
+int makedir (newdir)
+    char *newdir;
+{
+  char *buffer ;
+  char *p;
+  int  len = strlen(newdir);  
+
+  if (len <= 0) 
+    return 0;
+
+  buffer = (char*)malloc(len+1);
+  strcpy(buffer,newdir);
+  
+  if (buffer[len-1] == '/') {
+    buffer[len-1] = '\0';
+  }
+  if (mymkdir(buffer) == 0)
+    {
+      free(buffer);
+      return 1;
+    }
+
+  p = buffer+1;
+  while (1)
+    {
+      char hold;
+
+      while(*p && *p != '\\' && *p != '/')
+        p++;
+      hold = *p;
+      *p = 0;
+      if ((mymkdir(buffer) == -1) && (errno == ENOENT))
+        {
+          printf("couldn't create directory %s\n",buffer);
+          free(buffer);
+          return 0;
+        }
+      if (hold == 0)
+        break;
+      *p++ = hold;
+    }
+  free(buffer);
+  return 1;
+}
+
+void do_banner()
+{
+	printf("MiniUnz 0.15, demo of zLib + Unz package written by Gilles Vollant\n");
+	printf("more info at http://wwww.winimage/zLibDll/unzip.htm\n\n");
+}
+
+void do_help()
+{	
+	printf("Usage : miniunz [-exvlo] file.zip [file_to_extract]\n\n") ;
+}
+
+
+int do_list(uf)
+	unzFile uf;
+{
+	uLong i;
+	unz_global_info gi;
+	int err;
+
+	err = unzGetGlobalInfo (uf,&gi);
+	if (err!=UNZ_OK)
+		printf("error %d with zipfile in unzGetGlobalInfo \n",err);
+    printf(" Length  Method   Size  Ratio   Date    Time   CRC-32     Name\n");
+    printf(" ------  ------   ----  -----   ----    ----   ------     ----\n");
+	for (i=0;i<gi.number_entry;i++)
+	{
+		char filename_inzip[256];
+		unz_file_info file_info;
+		uLong ratio=0;
+		const char *string_method;
+		err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
+		if (err!=UNZ_OK)
+		{
+			printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
+			break;
+		}
+		if (file_info.uncompressed_size>0)
+			ratio = (file_info.compressed_size*100)/file_info.uncompressed_size;
+
+		if (file_info.compression_method==0)
+			string_method="Stored";
+		else
+		if (file_info.compression_method==Z_DEFLATED)
+		{
+			uInt iLevel=(uInt)((file_info.flag & 0x6)/2);
+			if (iLevel==0)
+			  string_method="Defl:N";
+			else if (iLevel==1)
+			  string_method="Defl:X";
+			else if ((iLevel==2) || (iLevel==3))
+			  string_method="Defl:F"; /* 2:fast , 3 : extra fast*/
+		}
+		else
+			string_method="Unkn. ";
+
+		printf("%7lu  %6s %7lu %3lu%%  %2.2lu-%2.2lu-%2.2lu  %2.2lu:%2.2lu  %8.8lx   %s\n",
+			    file_info.uncompressed_size,string_method,file_info.compressed_size,
+				ratio,
+				(uLong)file_info.tmu_date.tm_mon + 1,
+                (uLong)file_info.tmu_date.tm_mday,
+				(uLong)file_info.tmu_date.tm_year % 100,
+				(uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min,
+				(uLong)file_info.crc,filename_inzip);
+		if ((i+1)<gi.number_entry)
+		{
+			err = unzGoToNextFile(uf);
+			if (err!=UNZ_OK)
+			{
+				printf("error %d with zipfile in unzGoToNextFile\n",err);
+				break;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+int do_extract_currentfile(uf,popt_extract_without_path,popt_overwrite)
+	unzFile uf;
+	const int* popt_extract_without_path;
+    int* popt_overwrite;
+{
+	char filename_inzip[256];
+	char* filename_withoutpath;
+	char* p;
+    int err=UNZ_OK;
+    FILE *fout=NULL;
+    void* buf;
+    uInt size_buf;
+	
+	unz_file_info file_info;
+	uLong ratio=0;
+	err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
+
+	if (err!=UNZ_OK)
+	{
+		printf("error %d with zipfile in unzGetCurrentFileInfo\n",err);
+		return err;
+	}
+
+    size_buf = WRITEBUFFERSIZE;
+    buf = (void*)malloc(size_buf);
+    if (buf==NULL)
+    {
+        printf("Error allocating memory\n");
+        return UNZ_INTERNALERROR;
+    }
+
+	p = filename_withoutpath = filename_inzip;
+	while ((*p) != '\0')
+	{
+		if (((*p)=='/') || ((*p)=='\\'))
+			filename_withoutpath = p+1;
+		p++;
+	}
+
+	if ((*filename_withoutpath)=='\0')
+	{
+		if ((*popt_extract_without_path)==0)
+		{
+			printf("creating directory: %s\n",filename_inzip);
+			mymkdir(filename_inzip);
+		}
+	}
+	else
+	{
+		const char* write_filename;
+		int skip=0;
+
+		if ((*popt_extract_without_path)==0)
+			write_filename = filename_inzip;
+		else
+			write_filename = filename_withoutpath;
+
+		err = unzOpenCurrentFile(uf);
+		if (err!=UNZ_OK)
+		{
+			printf("error %d with zipfile in unzOpenCurrentFile\n",err);
+		}
+
+		if (((*popt_overwrite)==0) && (err==UNZ_OK))
+		{
+			char rep;
+			FILE* ftestexist;
+			ftestexist = fopen(write_filename,"rb");
+			if (ftestexist!=NULL)
+			{
+				fclose(ftestexist);
+				do
+				{
+					char answer[128];
+					printf("The file %s exist. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
+					scanf("%1s",answer);
+					rep = answer[0] ;
+					if ((rep>='a') && (rep<='z'))
+						rep -= 0x20;
+				}
+				while ((rep!='Y') && (rep!='N') && (rep!='A'));
+			}
+
+			if (rep == 'N')
+				skip = 1;
+
+			if (rep == 'A')
+				*popt_overwrite=1;
+		}
+
+		if ((skip==0) && (err==UNZ_OK))
+		{
+			fout=fopen(write_filename,"wb");
+
+            /* some zipfile don't contain directory alone before file */
+            if ((fout==NULL) && ((*popt_extract_without_path)==0) && 
+                                (filename_withoutpath!=(char*)filename_inzip))
+            {
+                char c=*(filename_withoutpath-1);
+                *(filename_withoutpath-1)='\0';
+                makedir(write_filename);
+                *(filename_withoutpath-1)=c;
+                fout=fopen(write_filename,"wb");
+            }
+
+			if (fout==NULL)
+			{
+				printf("error opening %s\n",write_filename);
+			}
+		}
+
+		if (fout!=NULL)
+		{
+			printf(" extracting: %s\n",write_filename);
+
+			do
+			{
+				err = unzReadCurrentFile(uf,buf,size_buf);
+				if (err<0)	
+				{
+					printf("error %d with zipfile in unzReadCurrentFile\n",err);
+					break;
+				}
+				if (err>0)
+					if (fwrite(buf,err,1,fout)!=1)
+					{
+						printf("error in writing extracted file\n");
+                        err=UNZ_ERRNO;
+						break;
+					}
+			}
+			while (err>0);
+			fclose(fout);
+			if (err==0) 
+				change_file_date(write_filename,file_info.dosDate,
+					             file_info.tmu_date);
+		}
+
+        if (err==UNZ_OK)
+        {
+		    err = unzCloseCurrentFile (uf);
+		    if (err!=UNZ_OK)
+		    {
+			    printf("error %d with zipfile in unzCloseCurrentFile\n",err);
+		    }
+        }
+        else
+            unzCloseCurrentFile(uf); /* don't lose the error */       
+	}
+
+    free(buf);    
+    return err;
+}
+
+
+int do_extract(uf,opt_extract_without_path,opt_overwrite)
+	unzFile uf;
+	int opt_extract_without_path;
+    int opt_overwrite;
+{
+	uLong i;
+	unz_global_info gi;
+	int err;
+	FILE* fout=NULL;	
+
+	err = unzGetGlobalInfo (uf,&gi);
+	if (err!=UNZ_OK)
+		printf("error %d with zipfile in unzGetGlobalInfo \n",err);
+
+	for (i=0;i<gi.number_entry;i++)
+	{
+        if (do_extract_currentfile(uf,&opt_extract_without_path,
+                                      &opt_overwrite) != UNZ_OK)
+            break;
+
+		if ((i+1)<gi.number_entry)
+		{
+			err = unzGoToNextFile(uf);
+			if (err!=UNZ_OK)
+			{
+				printf("error %d with zipfile in unzGoToNextFile\n",err);
+				break;
+			}
+		}
+	}
+
+	return 0;
+}
+
+int do_extract_onefile(uf,filename,opt_extract_without_path,opt_overwrite)
+	unzFile uf;
+	const char* filename;
+	int opt_extract_without_path;
+    int opt_overwrite;
+{
+    int err = UNZ_OK;
+    if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
+    {
+        printf("file %s not found in the zipfile\n",filename);
+        return 2;
+    }
+
+    if (do_extract_currentfile(uf,&opt_extract_without_path,
+                                      &opt_overwrite) == UNZ_OK)
+        return 0;
+    else
+        return 1;
+}
+
+
+int main(argc,argv)
+	int argc;
+	char *argv[];
+{
+	const char *zipfilename=NULL;
+    const char *filename_to_extract=NULL;
+	int i;
+	int opt_do_list=0;
+	int opt_do_extract=1;
+	int opt_do_extract_withoutpath=0;
+	int opt_overwrite=0;
+	char filename_try[512];
+	unzFile uf=NULL;
+
+	do_banner();
+	if (argc==1)
+	{
+		do_help();
+		exit(0);
+	}
+	else
+	{
+		for (i=1;i<argc;i++)
+		{
+			if ((*argv[i])=='-')
+			{
+				const char *p=argv[i]+1;
+				
+				while ((*p)!='\0')
+				{			
+					char c=*(p++);;
+					if ((c=='l') || (c=='L'))
+						opt_do_list = 1;
+					if ((c=='v') || (c=='V'))
+						opt_do_list = 1;
+					if ((c=='x') || (c=='X'))
+						opt_do_extract = 1;
+					if ((c=='e') || (c=='E'))
+						opt_do_extract = opt_do_extract_withoutpath = 1;
+					if ((c=='o') || (c=='O'))
+						opt_overwrite=1;
+				}
+			}
+			else
+            {
+				if (zipfilename == NULL)
+					zipfilename = argv[i];
+                else if (filename_to_extract==NULL)
+                        filename_to_extract = argv[i] ;
+            }
+		}
+	}
+
+	if (zipfilename!=NULL)
+	{
+		strcpy(filename_try,zipfilename);
+		uf = unzOpen(zipfilename);
+		if (uf==NULL)
+		{
+			strcat(filename_try,".zip");
+			uf = unzOpen(filename_try);
+		}
+	}
+
+	if (uf==NULL)
+	{
+		printf("Cannot open %s or %s.zip\n",zipfilename,zipfilename);
+		exit (1);
+	}
+    printf("%s opened\n",filename_try);
+
+	if (opt_do_list==1)
+		return do_list(uf);
+	else if (opt_do_extract==1)
+    {
+        if (filename_to_extract == NULL)
+		    return do_extract(uf,opt_do_extract_withoutpath,opt_overwrite);
+        else
+            return do_extract_onefile(uf,filename_to_extract,
+                                      opt_do_extract_withoutpath,opt_overwrite);
+    }
+	unzCloseCurrentFile(uf);
+
+	return 0;  /* to avoid warning */
+}
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/minizip.c
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/minizip.c	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/minizip.c	(revision 38)
@@ -0,0 +1,302 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef unix
+# include <unistd.h>
+# include <utime.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+#else
+# include <direct.h>
+# include <io.h>
+#endif
+
+#include "zip.h"
+
+
+#define WRITEBUFFERSIZE (16384)
+#define MAXFILENAME (256)
+
+#ifdef WIN32
+uLong filetime(f, tmzip, dt)
+    char *f;                /* name of file to get info on */
+    tm_zip *tmzip;             /* return value: access, modific. and creation times */
+    uLong *dt;             /* dostime */
+{
+  int ret = 0;
+  {
+      FILETIME ftLocal;
+      HANDLE hFind;
+      WIN32_FIND_DATA  ff32;
+
+      hFind = FindFirstFile(f,&ff32);
+      if (hFind != INVALID_HANDLE_VALUE)
+      {
+        FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
+        FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
+        FindClose(hFind);
+        ret = 1;
+      }
+  }
+  return ret;
+}
+#else
+#ifdef unix
+uLong filetime(f, tmzip, dt)
+    char *f;                /* name of file to get info on */
+    tm_zip *tmzip;             /* return value: access, modific. and creation times */
+    uLong *dt;             /* dostime */
+{
+  int ret=0;
+  struct stat s;        /* results of stat() */
+  struct tm* filedate;
+  time_t tm_t=0;
+  
+  if (strcmp(f,"-")!=0)
+  {
+    char name[MAXFILENAME];
+    int len = strlen(f);
+    strcpy(name, f);
+    if (name[len - 1] == '/')
+      name[len - 1] = '\0';
+    /* not all systems allow stat'ing a file with / appended */
+    if (stat(name,&s)==0)
+    {
+      tm_t = s.st_mtime;
+      ret = 1;
+    }
+  }
+  filedate = localtime(&tm_t);
+
+  tmzip->tm_sec  = filedate->tm_sec;
+  tmzip->tm_min  = filedate->tm_min;
+  tmzip->tm_hour = filedate->tm_hour;
+  tmzip->tm_mday = filedate->tm_mday;
+  tmzip->tm_mon  = filedate->tm_mon ;
+  tmzip->tm_year = filedate->tm_year;
+
+  return ret;
+}
+#else
+uLong filetime(f, tmzip, dt)
+    char *f;                /* name of file to get info on */
+    tm_zip *tmzip;             /* return value: access, modific. and creation times */
+    uLong *dt;             /* dostime */
+{
+    return 0;
+}
+#endif
+#endif
+
+
+
+
+int check_exist_file(filename)
+    const char* filename;
+{
+	FILE* ftestexist;
+    int ret = 1;
+	ftestexist = fopen(filename,"rb");
+	if (ftestexist==NULL)
+        ret = 0;
+    else
+        fclose(ftestexist);
+    return ret;
+}
+
+void do_banner()
+{
+	printf("MiniZip 0.15, demo of zLib + Zip package written by Gilles Vollant\n");
+	printf("more info at http://wwww.winimage/zLibDll/unzip.htm\n\n");
+}
+
+void do_help()
+{	
+	printf("Usage : minizip [-o] file.zip [files_to_add]\n\n") ;
+}
+
+int main(argc,argv)
+	int argc;
+	char *argv[];
+{
+	int i;
+	int opt_overwrite=0;
+    int opt_compress_level=Z_DEFAULT_COMPRESSION;
+    int zipfilenamearg = 0;
+	char filename_try[MAXFILENAME];
+    int zipok;
+    int err=0;
+    int size_buf=0;
+    void* buf=NULL,
+
+
+	do_banner();
+	if (argc==1)
+	{
+		do_help();
+		exit(0);
+        return 0;
+	}
+	else
+	{
+		for (i=1;i<argc;i++)
+		{
+			if ((*argv[i])=='-')
+			{
+				const char *p=argv[i]+1;
+				
+				while ((*p)!='\0')
+				{			
+					char c=*(p++);;
+					if ((c=='o') || (c=='O'))
+						opt_overwrite = 1;
+                    if ((c>='0') && (c<='9'))
+                        opt_compress_level = c-'0';
+				}
+			}
+			else
+				if (zipfilenamearg == 0)
+                    zipfilenamearg = i ;
+		}
+	}
+
+    size_buf = WRITEBUFFERSIZE;
+    buf = (void*)malloc(size_buf);
+    if (buf==NULL)
+    {
+        printf("Error allocating memory\n");
+        return ZIP_INTERNALERROR;
+    }
+
+	if (zipfilenamearg==0)
+        zipok=0;
+    else
+	{
+        int i,len;
+        int dot_found=0;
+
+        zipok = 1 ;
+		strcpy(filename_try,argv[zipfilenamearg]);
+        len=strlen(filename_try);
+        for (i=0;i<len;i++)
+            if (filename_try[i]=='.')
+                dot_found=1;
+
+        if (dot_found==0)
+            strcat(filename_try,".zip");
+
+        if (opt_overwrite==0)
+            if (check_exist_file(filename_try)!=0)
+			{
+                char rep;
+				do
+				{
+					char answer[128];
+					printf("The file %s exist. Overwrite ? [y]es, [n]o : ",filename_try);
+					scanf("%1s",answer);
+					rep = answer[0] ;
+					if ((rep>='a') && (rep<='z'))
+						rep -= 0x20;
+				}
+				while ((rep!='Y') && (rep!='N'));
+                if (rep=='N')
+                    zipok = 0;
+			}
+    }
+
+    if (zipok==1)
+    {
+        zipFile zf;
+        int errclose;
+        zf = zipOpen(filename_try,0);
+        if (zf == NULL)
+        {
+            printf("error opening %s\n",filename_try);
+            err= ZIP_ERRNO;
+        }
+        else 
+            printf("creating %s\n",filename_try);
+
+        for (i=zipfilenamearg+1;(i<argc) && (err==ZIP_OK);i++)
+        {
+            if (((*(argv[i]))!='-') && ((*(argv[i]))!='/'))
+            {
+                FILE * fin;
+                int size_read;
+                const char* filenameinzip = argv[i];
+                zip_fileinfo zi;
+
+                zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = 
+                zi.tmz_date.tm_mday = zi.tmz_date.tm_min = zi.tmz_date.tm_year = 0;
+                zi.dosDate = 0;
+                zi.internal_fa = 0;
+                zi.external_fa = 0;
+                filetime(filenameinzip,&zi.tmz_date,&zi.dosDate);
+
+
+                err = zipOpenNewFileInZip(zf,filenameinzip,&zi,
+                                 NULL,0,NULL,0,NULL /* comment*/,
+                                 (opt_compress_level != 0) ? Z_DEFLATED : 0,
+                                 opt_compress_level);
+
+                if (err != ZIP_OK)
+                    printf("error in opening %s in zipfile\n",filenameinzip);
+                else
+                {
+                    fin = fopen(filenameinzip,"rb");
+                    if (fin==NULL)
+                    {
+                        err=ZIP_ERRNO;
+                        printf("error in opening %s for reading\n",filenameinzip);
+                    }
+                }
+
+                if (err == ZIP_OK)
+                    do
+                    {
+                        err = ZIP_OK;
+                        size_read = fread(buf,1,size_buf,fin);
+                        if (size_read < size_buf)
+                            if (feof(fin)==0)
+                        {
+                            printf("error in reading %s\n",filenameinzip);
+                            err = ZIP_ERRNO;
+                        }
+
+                        if (size_read>0)
+                        {
+                            err = zipWriteInFileInZip (zf,buf,size_read);
+                            if (err<0)
+                            {
+                                printf("error in writing %s in the zipfile\n",
+                                                 filenameinzip);
+                            }
+                                
+                        }
+                    } while ((err == ZIP_OK) && (size_read>0));
+
+                fclose(fin);
+                if (err<0)
+                    err=ZIP_ERRNO;
+                else
+                {                    
+                    err = zipCloseFileInZip(zf);
+                    if (err!=ZIP_OK)
+                        printf("error in closing %s in the zipfile\n",
+                                    filenameinzip);
+                }
+            }
+        }
+        errclose = zipClose(zf,NULL);
+        if (errclose != ZIP_OK)
+            printf("error in closing %s\n",filename_try);
+   }
+
+    free(buf);
+    exit(0);
+	return 0;  /* to avoid warning */
+}
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/other/zip.c
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/other/zip.c	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/other/zip.c	(revision 38)
@@ -0,0 +1,718 @@
+/* zip.c -- IO on .zip files using zlib 
+   Version 0.15 beta, Mar 19th, 1998,
+
+   Read zip.h for more info
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "zlib.h"
+#include "zip.h"
+
+#ifdef STDC
+#  include <stddef.h>
+#  include <string.h>
+#  include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+    extern int errno;
+#else
+#   include <errno.h>
+#endif
+
+
+#ifndef local
+#  define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+#ifndef VERSIONMADEBY
+# define VERSIONMADEBY   (0x0) /* platform depedent */
+#endif
+
+#ifndef Z_BUFSIZE
+#define Z_BUFSIZE (16384)
+#endif
+
+#ifndef Z_MAXFILENAMEINZIP
+#define Z_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+/*
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+*/
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR    1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END    2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET    0
+#endif
+
+const char zip_copyright[] =
+   " zip 0.15 Copyright 1998 Gilles Vollant ";
+
+
+#define SIZEDATA_INDATABLOCK (4096-(4*4))
+
+#define LOCALHEADERMAGIC    (0x04034b50)
+#define CENTRALHEADERMAGIC  (0x02014b50)
+#define ENDHEADERMAGIC      (0x06054b50)
+
+#define FLAG_LOCALHEADER_OFFSET (0x06)
+#define CRC_LOCALHEADER_OFFSET  (0x0e)
+
+#define SIZECENTRALHEADER (0x2e) /* 46 */
+
+typedef struct linkedlist_datablock_internal_s
+{
+  struct linkedlist_datablock_internal_s* next_datablock;
+  uLong  avail_in_this_block;
+  uLong  filled_in_this_block;
+  uLong  unused; /* for future use and alignement */
+  unsigned char data[SIZEDATA_INDATABLOCK];
+} linkedlist_datablock_internal;
+
+typedef struct linkedlist_data_s
+{
+    linkedlist_datablock_internal* first_block;
+    linkedlist_datablock_internal* last_block;
+} linkedlist_data;
+
+
+typedef struct
+{
+	z_stream stream;            /* zLib stream structure for inflate */
+    int  stream_initialised;    /* 1 is stream is initialised */
+    uInt pos_in_buffered_data;  /* last written byte in buffered_data */
+
+    uLong pos_local_header;     /* offset of the local header of the file 
+                                     currenty writing */
+    char* central_header;       /* central header data for the current file */
+    uLong size_centralheader;   /* size of the central header for cur file */
+    uLong flag;                 /* flag of the file currently writing */
+
+    int  method;                /* compression method of file currenty wr.*/
+    Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/
+    uLong dosDate;
+    uLong crc32;
+} curfile_info;
+
+typedef struct
+{
+    FILE * filezip;
+    linkedlist_data central_dir;/* datablock with central dir in construction*/
+    int  in_opened_file_inzip;  /* 1 if a file in the zip is currently writ.*/
+    curfile_info ci;            /* info on the file curretly writing */
+
+    uLong begin_pos;            /* position of the beginning of the zipfile */
+    uLong number_entry;
+} zip_internal;
+
+local linkedlist_datablock_internal* allocate_new_datablock()
+{
+    linkedlist_datablock_internal* ldi;
+    ldi = (linkedlist_datablock_internal*)
+                 ALLOC(sizeof(linkedlist_datablock_internal));
+    if (ldi!=NULL)
+    {
+        ldi->next_datablock = NULL ;
+        ldi->filled_in_this_block = 0 ;
+        ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ;
+    }
+    return ldi;
+}
+
+local void free_datablock(ldi)
+    linkedlist_datablock_internal* ldi;
+{
+    while (ldi!=NULL)
+    {
+        linkedlist_datablock_internal* ldinext = ldi->next_datablock;
+        TRYFREE(ldi);
+        ldi = ldinext;
+    }
+}
+
+local void init_linkedlist(ll)
+    linkedlist_data* ll;
+{
+    ll->first_block = ll->last_block = NULL;
+}
+
+local void free_linkedlist(ll)
+    linkedlist_data* ll;
+{
+    free_datablock(ll->first_block);
+    ll->first_block = ll->last_block = NULL;
+}
+
+
+local int add_data_in_datablock(ll,buf,len)
+    linkedlist_data* ll;    
+    const void* buf;
+    uLong len;
+{
+    linkedlist_datablock_internal* ldi;
+    const unsigned char* from_copy;
+
+    if (ll==NULL)
+        return ZIP_INTERNALERROR;
+
+    if (ll->last_block == NULL)
+    {
+        ll->first_block = ll->last_block = allocate_new_datablock();
+        if (ll->first_block == NULL)
+            return ZIP_INTERNALERROR;
+    }
+
+    ldi = ll->last_block;
+    from_copy = (unsigned char*)buf;
+
+    while (len>0)
+    {
+        uInt copy_this;
+        uInt i;
+        unsigned char* to_copy;
+
+        if (ldi->avail_in_this_block==0)
+        {
+            ldi->next_datablock = allocate_new_datablock();
+            if (ldi->next_datablock == NULL)
+                return ZIP_INTERNALERROR;
+            ldi = ldi->next_datablock ;
+            ll->last_block = ldi;
+        }
+
+        if (ldi->avail_in_this_block < len)
+            copy_this = (uInt)ldi->avail_in_this_block;
+        else
+            copy_this = (uInt)len;
+
+        to_copy = &(ldi->data[ldi->filled_in_this_block]);
+
+        for (i=0;i<copy_this;i++)
+            *(to_copy+i)=*(from_copy+i);
+
+        ldi->filled_in_this_block += copy_this;
+        ldi->avail_in_this_block -= copy_this;
+        from_copy += copy_this ;
+        len -= copy_this;
+    }
+    return ZIP_OK;
+}
+
+
+local int write_datablock(fout,ll)
+    FILE * fout;
+    linkedlist_data* ll;    
+{
+    linkedlist_datablock_internal* ldi;
+    ldi = ll->first_block;
+    while (ldi!=NULL)
+    {
+        if (ldi->filled_in_this_block > 0)
+            if (fwrite(ldi->data,(uInt)ldi->filled_in_this_block,1,fout)!=1)
+                return ZIP_ERRNO;
+        ldi = ldi->next_datablock;
+    }
+    return ZIP_OK;
+}
+
+/****************************************************************************/
+
+/* ===========================================================================
+   Outputs a long in LSB order to the given file
+   nbByte == 1, 2 or 4 (byte, short or long)
+*/
+
+local int ziplocal_putValue OF((FILE *file, uLong x, int nbByte));
+local int ziplocal_putValue (file, x, nbByte)
+    FILE *file;
+    uLong x;
+    int nbByte;
+{
+    unsigned char buf[4];
+    int n;
+    for (n = 0; n < nbByte; n++) {
+        buf[n] = (unsigned char)(x & 0xff);
+        x >>= 8;
+    }
+    if (fwrite(buf,nbByte,1,file)!=1)
+        return ZIP_ERRNO;
+    else
+        return ZIP_OK;
+}
+
+local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte));
+local void ziplocal_putValue_inmemory (dest, x, nbByte)
+    void* dest;
+    uLong x;
+    int nbByte;
+{
+    unsigned char* buf=(unsigned char*)dest;
+    int n;
+    for (n = 0; n < nbByte; n++) {
+        buf[n] = (unsigned char)(x & 0xff);
+        x >>= 8;
+    }
+}
+/****************************************************************************/
+
+
+local uLong ziplocal_TmzDateToDosDate(ptm,dosDate)
+    tm_zip* ptm;
+    uLong dosDate;
+{
+    uLong year = (uLong)ptm->tm_year;
+    if (year>1980)
+        year-=1980;
+    else if (year>80)
+        year-=80;
+    return
+      (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) |
+        ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour));
+}
+
+
+/****************************************************************************/
+
+extern zipFile ZEXPORT zipOpen (pathname, append)
+    const char *pathname;
+    int append;
+{
+    zip_internal ziinit;
+    zip_internal* zi;
+
+    ziinit.filezip = fopen(pathname,(append == 0) ? "wb" : "ab");
+    if (ziinit.filezip == NULL)
+        return NULL;
+    ziinit.begin_pos = ftell(ziinit.filezip);
+    ziinit.in_opened_file_inzip = 0;
+    ziinit.ci.stream_initialised = 0;
+    ziinit.number_entry = 0;
+    init_linkedlist(&(ziinit.central_dir));
+
+
+    zi = (zip_internal*)ALLOC(sizeof(zip_internal));
+    if (zi==NULL)
+    {
+        fclose(ziinit.filezip);
+        return NULL;
+    }
+
+    *zi = ziinit;
+    return (zipFile)zi;
+}
+
+extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi, 
+                                        extrafield_local, size_extrafield_local,
+                                        extrafield_global, size_extrafield_global,
+                                        comment, method, level)
+    zipFile file;
+    const char* filename;
+    const zip_fileinfo* zipfi;
+    const void* extrafield_local;
+    uInt size_extrafield_local;
+    const void* extrafield_global;
+    uInt size_extrafield_global;
+    const char* comment;
+    int method;
+    int level;
+{
+    zip_internal* zi;
+    uInt size_filename;
+    uInt size_comment;
+    uInt i;
+    int err = ZIP_OK;
+
+    if (file == NULL)
+        return ZIP_PARAMERROR;
+    if ((method!=0) && (method!=Z_DEFLATED))
+        return ZIP_PARAMERROR;
+
+    zi = (zip_internal*)file;
+
+    if (zi->in_opened_file_inzip == 1)
+    {
+        err = zipCloseFileInZip (file);
+        if (err != ZIP_OK)
+            return err;
+    }
+
+
+    if (filename==NULL)
+        filename="-";
+
+    if (comment==NULL)
+        size_comment = 0;
+    else
+        size_comment = strlen(comment);
+
+    size_filename = strlen(filename);
+
+    if (zipfi == NULL)
+        zi->ci.dosDate = 0;
+    else
+    {
+        if (zipfi->dosDate != 0)
+            zi->ci.dosDate = zipfi->dosDate;
+        else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate);
+    }
+
+    zi->ci.flag = 0;
+    if ((level==8) || (level==9))
+      zi->ci.flag |= 2;
+    if ((level==2))
+      zi->ci.flag |= 4;
+    if ((level==1))
+      zi->ci.flag |= 6;
+
+    zi->ci.crc32 = 0;
+    zi->ci.method = method;
+    zi->ci.stream_initialised = 0;
+    zi->ci.pos_in_buffered_data = 0;
+    zi->ci.pos_local_header = ftell(zi->filezip);
+    zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + 
+                                      size_extrafield_global + size_comment;
+    zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader);
+
+    ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4);
+    /* version info */
+    ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2);
+    ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2);
+    ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2);
+    ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2);
+    ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4);
+    ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/
+    ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/
+    ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/
+    ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2);
+    ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2);
+    ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2);
+    ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/
+
+    if (zipfi==NULL)
+        ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); 
+    else
+        ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); 
+
+    if (zipfi==NULL)
+        ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); 
+    else
+        ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4);
+
+    ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header,4);
+
+    for (i=0;i<size_filename;i++)
+        *(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i);
+
+    for (i=0;i<size_extrafield_global;i++)
+        *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) =
+              *(((const char*)extrafield_global)+i);
+
+    for (i=0;i<size_comment;i++)
+        *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+
+              size_extrafield_global+i) = *(filename+i);
+    if (zi->ci.central_header == NULL)
+        return ZIP_INTERNALERROR;
+
+    /* write the local header */
+    err = ziplocal_putValue(zi->filezip,(uLong)LOCALHEADERMAGIC,4);
+
+    if (err==ZIP_OK)
+        err = ziplocal_putValue(zi->filezip,(uLong)20,2);/* version needed to extract */
+    if (err==ZIP_OK)
+        err = ziplocal_putValue(zi->filezip,(uLong)zi->ci.flag,2);
+
+    if (err==ZIP_OK)
+        err = ziplocal_putValue(zi->filezip,(uLong)zi->ci.method,2);
+
+    if (err==ZIP_OK)
+        err = ziplocal_putValue(zi->filezip,(uLong)zi->ci.dosDate,4);
+
+    if (err==ZIP_OK)
+        err = ziplocal_putValue(zi->filezip,(uLong)0,4); /* crc 32, unknown */
+    if (err==ZIP_OK)
+        err = ziplocal_putValue(zi->filezip,(uLong)0,4); /* compressed size, unknown */
+    if (err==ZIP_OK)
+        err = ziplocal_putValue(zi->filezip,(uLong)0,4); /* uncompressed size, unknown */
+
+    if (err==ZIP_OK)
+        err = ziplocal_putValue(zi->filezip,(uLong)size_filename,2);
+
+    if (err==ZIP_OK)
+        err = ziplocal_putValue(zi->filezip,(uLong)size_extrafield_local,2);
+
+    if ((err==ZIP_OK) && (size_filename>0))
+        if (fwrite(filename,(uInt)size_filename,1,zi->filezip)!=1)
+                err = ZIP_ERRNO;
+
+    if ((err==ZIP_OK) && (size_extrafield_local>0))
+        if (fwrite(extrafield_local,(uInt)size_extrafield_local,1,zi->filezip)
+                                                                           !=1)
+                err = ZIP_ERRNO;
+
+    zi->ci.stream.avail_in = (uInt)0;
+    zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+    zi->ci.stream.next_out = zi->ci.buffered_data;
+    zi->ci.stream.total_in = 0;
+    zi->ci.stream.total_out = 0;
+
+    if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED))
+    {
+        zi->ci.stream.zalloc = (alloc_func)0;
+        zi->ci.stream.zfree = (free_func)0;
+        zi->ci.stream.opaque = (voidpf)0;
+
+        err = deflateInit2(&zi->ci.stream, level,
+               Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, 0);
+
+        if (err==Z_OK)
+            zi->ci.stream_initialised = 1;
+    }
+
+
+    if (err==Z_OK)
+        zi->in_opened_file_inzip = 1;
+    return err;
+}
+
+extern int ZEXPORT zipWriteInFileInZip (file, buf, len)
+    zipFile file;
+    const voidp buf;
+    unsigned len;
+{
+    zip_internal* zi;
+    int err=ZIP_OK;
+
+    if (file == NULL)
+        return ZIP_PARAMERROR;
+    zi = (zip_internal*)file;
+
+    if (zi->in_opened_file_inzip == 0)
+        return ZIP_PARAMERROR;
+
+    zi->ci.stream.next_in = buf;
+    zi->ci.stream.avail_in = len;
+    zi->ci.crc32 = crc32(zi->ci.crc32,buf,len);
+
+    while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0))
+    {
+        if (zi->ci.stream.avail_out == 0)
+        {
+            if (fwrite(zi->ci.buffered_data,(uInt)zi->ci.pos_in_buffered_data,1,zi->filezip)
+                                                                           !=1)
+                err = ZIP_ERRNO;
+            zi->ci.pos_in_buffered_data = 0;
+            zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+            zi->ci.stream.next_out = zi->ci.buffered_data;
+        }
+
+        if (zi->ci.method == Z_DEFLATED)
+        {
+            uLong uTotalOutBefore = zi->ci.stream.total_out;
+            err=deflate(&zi->ci.stream,  Z_NO_FLUSH);
+            zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
+
+        }
+        else
+        {
+            uInt copy_this,i;
+            if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
+                copy_this = zi->ci.stream.avail_in;
+            else
+                copy_this = zi->ci.stream.avail_out;
+            for (i=0;i<copy_this;i++)
+                *(((char*)zi->ci.stream.next_out)+i) =
+                    *(((const char*)zi->ci.stream.next_in)+i);
+            {
+                zi->ci.stream.avail_in -= copy_this;
+                zi->ci.stream.avail_out-= copy_this;
+                zi->ci.stream.next_in+= copy_this;
+                zi->ci.stream.next_out+= copy_this;
+                zi->ci.stream.total_in+= copy_this;
+                zi->ci.stream.total_out+= copy_this;
+                zi->ci.pos_in_buffered_data += copy_this;
+            }
+        }
+    }
+
+    return 0;
+}
+
+extern int ZEXPORT zipCloseFileInZip (file)
+    zipFile file;
+{
+    zip_internal* zi;
+    int err=ZIP_OK;
+
+    if (file == NULL)
+        return ZIP_PARAMERROR;
+    zi = (zip_internal*)file;
+
+    if (zi->in_opened_file_inzip == 0)    
+        return ZIP_PARAMERROR;
+    zi->ci.stream.avail_in = 0;
+    
+    if (zi->ci.method == Z_DEFLATED)
+        while (err==ZIP_OK)
+    {
+        uLong uTotalOutBefore;
+        if (zi->ci.stream.avail_out == 0)
+        {
+            if (fwrite(zi->ci.buffered_data,(uInt)zi->ci.pos_in_buffered_data,1,zi->filezip)
+                                                                           !=1)
+                err = ZIP_ERRNO;
+            zi->ci.pos_in_buffered_data = 0;
+            zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+            zi->ci.stream.next_out = zi->ci.buffered_data;
+        }
+        uTotalOutBefore = zi->ci.stream.total_out;
+        err=deflate(&zi->ci.stream,  Z_FINISH);
+        zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
+    }
+
+    if (err==Z_STREAM_END)
+        err=ZIP_OK; /* this is normal */
+
+    if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK))
+        if (fwrite(zi->ci.buffered_data,(uInt)zi->ci.pos_in_buffered_data,1,zi->filezip)
+                                                                       !=1)
+            err = ZIP_ERRNO;
+
+    if ((zi->ci.method == Z_DEFLATED) && (err==ZIP_OK))
+    {
+        err=deflateEnd(&zi->ci.stream);
+        zi->ci.stream_initialised = 0;
+    }
+
+    ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)zi->ci.crc32,4); /*crc*/
+    ziplocal_putValue_inmemory(zi->ci.central_header+20,
+                                (uLong)zi->ci.stream.total_out,4); /*compr size*/
+    ziplocal_putValue_inmemory(zi->ci.central_header+24,
+                                (uLong)zi->ci.stream.total_in,4); /*uncompr size*/
+
+    if (err==ZIP_OK)
+        err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header,
+                                       (uLong)zi->ci.size_centralheader);
+    free(zi->ci.central_header);
+
+    if (err==ZIP_OK)
+    {
+        long cur_pos_inzip = ftell(zi->filezip);
+	    if (fseek(zi->filezip,
+                  zi->ci.pos_local_header + 14,SEEK_SET)!=0)
+		    err = ZIP_ERRNO;
+
+        if (err==ZIP_OK)
+            err = ziplocal_putValue(zi->filezip,(uLong)zi->ci.crc32,4); /* crc 32, unknown */
+
+        if (err==ZIP_OK) /* compressed size, unknown */
+            err = ziplocal_putValue(zi->filezip,(uLong)zi->ci.stream.total_out,4); 
+
+        if (err==ZIP_OK) /* uncompressed size, unknown */
+            err = ziplocal_putValue(zi->filezip,(uLong)zi->ci.stream.total_in,4);
+
+	    if (fseek(zi->filezip,
+                  cur_pos_inzip,SEEK_SET)!=0)
+		    err = ZIP_ERRNO;
+    }
+
+    zi->number_entry ++;
+    zi->in_opened_file_inzip = 0;
+
+    return err;
+}
+
+extern int ZEXPORT zipClose (file, global_comment)
+    zipFile file;
+    const char* global_comment;
+{
+    zip_internal* zi;
+    int err = 0;
+    uLong size_centraldir = 0;
+    uLong centraldir_pos_inzip ;
+    uInt size_global_comment;
+    if (file == NULL)
+        return ZIP_PARAMERROR;
+    zi = (zip_internal*)file;
+
+    if (zi->in_opened_file_inzip == 1)
+    {
+        err = zipCloseFileInZip (file);
+    }
+
+    if (global_comment==NULL)
+        size_global_comment = 0;
+    else
+        size_global_comment = strlen(global_comment);
+
+
+    centraldir_pos_inzip = ftell(zi->filezip);
+    if (err==ZIP_OK)
+    {
+        linkedlist_datablock_internal* ldi = zi->central_dir.first_block ;
+        while (ldi!=NULL)
+        {
+            if ((err==ZIP_OK) && (ldi->filled_in_this_block>0))
+                if (fwrite(ldi->data,(uInt)ldi->filled_in_this_block,
+                                        1,zi->filezip) !=1 )
+                    err = ZIP_ERRNO;
+
+            size_centraldir += ldi->filled_in_this_block;
+            ldi = ldi->next_datablock;
+        }
+    }
+    free_datablock(zi->central_dir.first_block);
+
+    if (err==ZIP_OK) /* Magic End */
+        err = ziplocal_putValue(zi->filezip,(uLong)ENDHEADERMAGIC,4);
+
+    if (err==ZIP_OK) /* number of this disk */
+        err = ziplocal_putValue(zi->filezip,(uLong)0,2);
+
+    if (err==ZIP_OK) /* number of the disk with the start of the central directory */
+        err = ziplocal_putValue(zi->filezip,(uLong)0,2);
+
+    if (err==ZIP_OK) /* total number of entries in the central dir on this disk */
+        err = ziplocal_putValue(zi->filezip,(uLong)zi->number_entry,2);
+
+    if (err==ZIP_OK) /* total number of entries in the central dir */
+        err = ziplocal_putValue(zi->filezip,(uLong)zi->number_entry,2);
+
+    if (err==ZIP_OK) /* size of the central directory */
+        err = ziplocal_putValue(zi->filezip,(uLong)size_centraldir,4);
+
+    if (err==ZIP_OK) /* offset of start of central directory with respect to the 
+	                        starting disk number */
+        err = ziplocal_putValue(zi->filezip,(uLong)centraldir_pos_inzip ,4);
+
+    if (err==ZIP_OK) /* zipfile comment length */
+        err = ziplocal_putValue(zi->filezip,(uLong)size_global_comment,2);
+
+    if ((err==ZIP_OK) && (size_global_comment>0))
+        if (fwrite(global_comment,(uInt)size_global_comment,1,zi->filezip) !=1 )
+                err = ZIP_ERRNO;
+    fclose(zi->filezip);
+    TRYFREE(zi);
+
+    return err;
+}
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/other/zip.def
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/other/zip.def	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/other/zip.def	(revision 38)
@@ -0,0 +1,5 @@
+	zipOpen                       @80
+	zipOpenNewFileInZip           @81
+	zipWriteInFileInZip           @82
+	zipCloseFileInZip             @83
+	zipClose                      @84
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/other/zip.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/other/zip.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/other/zip.h	(revision 38)
@@ -0,0 +1,150 @@
+/* zip.h -- IO for compress .zip files using zlib 
+   Version 0.15 alpha, Mar 19th, 1998,
+
+   Copyright (C) 1998 Gilles Vollant
+
+   This unzip package allow creates .ZIP file, compatible with PKZip 2.04g
+     WinZip, InfoZip tools and compatible.
+   Encryption and multi volume ZipFile (span) are not supported.
+   Old compressions used by old PKZip 1.x are not supported
+
+  For uncompress .zip file, look at unzip.h
+
+   THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE
+   CAN CHANGE IN FUTURE VERSION !!
+   I WAIT FEEDBACK at mail info@winimage.com
+   Visit also http://www.winimage.com/zLibDll/zip.htm for evolution
+
+   Condition of use and distribution are the same than zlib :
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+
+*/
+
+/* for more info about .ZIP format, see 
+      ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip
+   PkWare has also a specification at :
+      ftp://ftp.pkware.com/probdesc.zip
+*/
+
+#ifndef _zip_H
+#define _zip_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#if defined(STRICTZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+    from (void*) without cast */
+typedef struct TagzipFile__ { int unused; } zipFile__; 
+typedef zipFile__ *zipFile;
+#else
+typedef voidp zipFile;
+#endif
+
+#define ZIP_OK                                  (0)
+#define ZIP_ERRNO               (Z_ERRNO)
+#define ZIP_PARAMERROR                  (-102)
+#define ZIP_INTERNALERROR               (-104)
+
+/* tm_zip contain date/time info */
+typedef struct tm_zip_s 
+{
+	uInt tm_sec;            /* seconds after the minute - [0,59] */
+	uInt tm_min;            /* minutes after the hour - [0,59] */
+	uInt tm_hour;           /* hours since midnight - [0,23] */
+	uInt tm_mday;           /* day of the month - [1,31] */
+	uInt tm_mon;            /* months since January - [0,11] */
+	uInt tm_year;           /* years - [1980..2044] */
+} tm_zip;
+
+typedef struct
+{
+	tm_zip      tmz_date;       /* date in understandable format           */
+    uLong       dosDate;       /* if dos_date == 0, tmu_date is used      */
+/*    uLong       flag;        */   /* general purpose bit flag        2 bytes */
+
+    uLong       internal_fa;    /* internal file attributes        2 bytes */
+    uLong       external_fa;    /* external file attributes        4 bytes */
+} zip_fileinfo;
+
+extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append));
+/*
+  Create a zipfile.
+	 pathname contain on Windows NT a filename like "c:\\zlib\\zlib111.zip" or on
+	   an Unix computer "zlib/zlib111.zip".
+	 if the file pathname exist and append=1, the zip will be created at the end
+	   of the file. (useful if the file contain a self extractor code)
+	 If the zipfile cannot be opened, the return value is NULL.
+     Else, the return value is a zipFile Handle, usable with other function
+	   of this zip package.
+
+
+*/
+
+extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file,
+					   const char* filename,
+					   const zip_fileinfo* zipfi,
+					   const void* extrafield_local,
+					   uInt size_extrafield_local,
+					   const void* extrafield_global,
+					   uInt size_extrafield_global,
+					   const char* comment,
+					   int method,
+					   int level));
+/*
+  Open a file in the ZIP for writing.
+  filename : the filename in zip (if NULL, '-' without quote will be used
+  *zipfi contain supplemental information
+  if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
+    contains the extrafield data the the local header
+  if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
+    contains the extrafield data the the local header
+  if comment != NULL, comment contain the comment string
+  method contain the compression method (0 for store, Z_DEFLATED for deflate)
+  level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
+*/
+
+extern int ZEXPORT zipWriteInFileInZip OF((zipFile file,
+					   const voidp buf,
+					   unsigned len));
+/*
+  Write data in the zipfile
+*/
+
+extern int ZEXPORT zipCloseFileInZip OF((zipFile file));
+/*
+  Close the current file in the zipfile
+*/
+
+extern int ZEXPORT zipClose OF((zipFile file,
+				const char* global_comment));
+/*
+  Close the zipfile
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _zip_H */
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/readme.txt
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/readme.txt	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/readme.txt	(revision 38)
@@ -0,0 +1,37 @@
+
+UnZip 0.15 additionnal library
+
+
+  This unzip package allow extract file from .ZIP file, compatible with 
+PKZip 2.04g, WinZip, InfoZip tools and compatible.
+
+  Multi volume ZipFile (span) are not supported, and old compression used by old 
+PKZip 1.x are not supported.
+
+See probdesc.zip from PKWare for specification of .ZIP format.
+
+What is Unzip
+  The Zlib library support the deflate compression and the creation of gzip (.gz) 
+file. Zlib is free and small.
+  The .Zip format, which can contain several compressed files (.gz can containt
+only one file) is a very popular format. This is why I've written a package for reading file compressed in Zipfile.
+
+Using Unzip package
+
+You need source of Zlib (get zlib111.zip and read zlib.h).
+Get unzlb015.zip and read unzip.h (whith documentation of unzip functions)
+
+The Unzip package is only two file : unzip.h and unzip.c. But it use the Zlib 
+  files.
+unztst.c is a simple sample program, which list file in a zipfile and display
+  README.TXT or FILE_ID.DIZ (if these files are found).
+miniunz.c is a mini unzip program.
+
+I'm also currenlyt writing a zipping portion (zip.h, zip.c and test with minizip.c)
+
+Please email me for feedback.
+I hope my source is compatible with Unix system, but I need your help for be sure
+
+Latest revision : Mar 04th, 1998
+
+Check http://www.winimage.com/zLibDll/unzip.html for up to date info.
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/unzip.c
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/unzip.c	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/unzip.c	(revision 38)
@@ -0,0 +1,1299 @@
+/* unzip.c -- IO on .zip files using zlib 
+   Version 0.15 beta, Mar 19th, 1998,
+
+   Read unzip.h for more info
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "zlib.h"
+#include "unzip.h"
+
+#ifdef STDC
+#  include <stddef.h>
+#  include <string.h>
+#  include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+    extern int errno;
+#else
+#   include <errno.h>
+#endif
+
+
+#ifndef local
+#  define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+
+
+#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \
+                      !defined(CASESENSITIVITYDEFAULT_NO)
+#define CASESENSITIVITYDEFAULT_NO
+#endif
+
+
+#ifndef UNZ_BUFSIZE
+#define UNZ_BUFSIZE (16384)
+#endif
+
+#ifndef UNZ_MAXFILENAMEINZIP
+#define UNZ_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR    1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END    2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET    0
+#endif
+
+const char unz_copyright[] =
+   " unzip 0.15 Copyright 1998 Gilles Vollant ";
+
+/* unz_file_info_interntal contain internal info about a file in zipfile*/
+typedef struct unz_file_info_internal_s
+{
+    uLong offset_curfile;/* relative offset of local header 4 bytes */
+} unz_file_info_internal;
+
+
+/* file_in_zip_read_info_s contain internal information about a file in zipfile,
+    when reading and decompress it */
+typedef struct
+{
+	char  *read_buffer;         /* internal buffer for compressed data */
+	z_stream stream;            /* zLib stream structure for inflate */
+
+	uLong pos_in_zipfile;       /* position in byte on the zipfile, for fseek*/
+	uLong stream_initialised;   /* flag set if stream structure is initialised*/
+
+	uLong offset_local_extrafield;/* offset of the local extra field */
+	uInt  size_local_extrafield;/* size of the local extra field */
+	uLong pos_local_extrafield;   /* position in the local extra field in read*/
+
+	uLong crc32;                /* crc32 of all data uncompressed */
+	uLong crc32_wait;           /* crc32 we must obtain after decompress all */
+	uLong rest_read_compressed; /* number of byte to be decompressed */
+	uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/
+	FILE* file;                 /* io structore of the zipfile */
+	uLong compression_method;   /* compression method (0==store) */
+	uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+} file_in_zip_read_info_s;
+
+
+/* unz_s contain internal information about the zipfile
+*/
+typedef struct
+{
+	FILE* file;                 /* io structore of the zipfile */
+	unz_global_info gi;       /* public global information */
+	uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+	uLong num_file;             /* number of the current file in the zipfile*/
+	uLong pos_in_central_dir;   /* pos of the current file in the central dir*/
+	uLong current_file_ok;      /* flag about the usability of the current file*/
+	uLong central_pos;          /* position of the beginning of the central dir*/
+
+	uLong size_central_dir;     /* size of the central directory  */
+	uLong offset_central_dir;   /* offset of start of central directory with
+								   respect to the starting disk number */
+
+	unz_file_info cur_file_info; /* public info about the current file in zip*/
+	unz_file_info_internal cur_file_info_internal; /* private info about it*/
+    file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
+	                                    file if we are decompressing it */
+} unz_s;
+
+
+/* ===========================================================================
+     Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+   for end of file.
+   IN assertion: the stream s has been sucessfully opened for reading.
+*/
+
+
+local int unzlocal_getByte(fin,pi)
+	FILE *fin;
+	int *pi;
+{
+    unsigned char c;
+	int err = fread(&c, 1, 1, fin);
+    if (err==1)
+    {
+        *pi = (int)c;
+        return UNZ_OK;
+    }
+    else
+    {
+        if (ferror(fin)) 
+            return UNZ_ERRNO;
+        else
+            return UNZ_EOF;
+    }
+}
+
+
+/* ===========================================================================
+   Reads a long in LSB order from the given gz_stream. Sets 
+*/
+local int unzlocal_getShort (fin,pX)
+	FILE* fin;
+    uLong *pX;
+{
+    uLong x ;
+    int i;
+    int err;
+
+    err = unzlocal_getByte(fin,&i);
+    x = (uLong)i;
+    
+    if (err==UNZ_OK)
+        err = unzlocal_getByte(fin,&i);
+    x += ((uLong)i)<<8;
+   
+    if (err==UNZ_OK)
+        *pX = x;
+    else
+        *pX = 0;
+    return err;
+}
+
+local int unzlocal_getLong (fin,pX)
+	FILE* fin;
+    uLong *pX;
+{
+    uLong x ;
+    int i;
+    int err;
+
+    err = unzlocal_getByte(fin,&i);
+    x = (uLong)i;
+    
+    if (err==UNZ_OK)
+        err = unzlocal_getByte(fin,&i);
+    x += ((uLong)i)<<8;
+
+    if (err==UNZ_OK)
+        err = unzlocal_getByte(fin,&i);
+    x += ((uLong)i)<<16;
+
+    if (err==UNZ_OK)
+        err = unzlocal_getByte(fin,&i);
+    x += ((uLong)i)<<24;
+   
+    if (err==UNZ_OK)
+        *pX = x;
+    else
+        *pX = 0;
+    return err;
+}
+
+
+/* My own strcmpi / strcasecmp */
+local int strcmpcasenosensitive_internal (fileName1,fileName2)
+	const char* fileName1;
+	const char* fileName2;
+{
+	for (;;)
+	{
+		char c1=*(fileName1++);
+		char c2=*(fileName2++);
+		if ((c1>='a') && (c1<='z'))
+			c1 -= 0x20;
+		if ((c2>='a') && (c2<='z'))
+			c2 -= 0x20;
+		if (c1=='\0')
+			return ((c2=='\0') ? 0 : -1);
+		if (c2=='\0')
+			return 1;
+		if (c1<c2)
+			return -1;
+		if (c1>c2)
+			return 1;
+	}
+}
+
+
+#ifdef  CASESENSITIVITYDEFAULT_NO
+#define CASESENSITIVITYDEFAULTVALUE 2
+#else
+#define CASESENSITIVITYDEFAULTVALUE 1
+#endif
+
+#ifndef STRCMPCASENOSENTIVEFUNCTION
+#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal
+#endif
+
+/* 
+   Compare two filename (fileName1,fileName2).
+   If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+   If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+                                                                or strcasecmp)
+   If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+        (like 1 on Unix, 2 on Windows)
+
+*/
+extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity)
+	const char* fileName1;
+	const char* fileName2;
+	int iCaseSensitivity;
+{
+	if (iCaseSensitivity==0)
+		iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE;
+
+	if (iCaseSensitivity==1)
+		return strcmp(fileName1,fileName2);
+
+	return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2);
+} 
+
+#define BUFREADCOMMENT (0x400)
+
+/*
+  Locate the Central directory of a zipfile (at the end, just before
+    the global comment)
+*/
+local uLong unzlocal_SearchCentralDir(fin)
+	FILE *fin;
+{
+	unsigned char* buf;
+	uLong uSizeFile;
+	uLong uBackRead;
+	uLong uMaxBack=0xffff; /* maximum size of global comment */
+	uLong uPosFound=0;
+	
+	if (fseek(fin,0,SEEK_END) != 0)
+		return 0;
+
+
+	uSizeFile = ftell( fin );
+	
+	if (uMaxBack>uSizeFile)
+		uMaxBack = uSizeFile;
+
+	buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+	if (buf==NULL)
+		return 0;
+
+	uBackRead = 4;
+	while (uBackRead<uMaxBack)
+	{
+		uLong uReadSize,uReadPos ;
+		int i;
+		if (uBackRead+BUFREADCOMMENT>uMaxBack) 
+			uBackRead = uMaxBack;
+		else
+			uBackRead+=BUFREADCOMMENT;
+		uReadPos = uSizeFile-uBackRead ;
+		
+		uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? 
+                     (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
+		if (fseek(fin,uReadPos,SEEK_SET)!=0)
+			break;
+
+		if (fread(buf,(uInt)uReadSize,1,fin)!=1)
+			break;
+
+                for (i=(int)uReadSize-3; (i--)>0;)
+			if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && 
+				((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
+			{
+				uPosFound = uReadPos+i;
+				break;
+			}
+
+		if (uPosFound!=0)
+			break;
+	}
+	TRYFREE(buf);
+	return uPosFound;
+}
+
+/*
+  Open a Zip file. path contain the full pathname (by example,
+     on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer
+	 "zlib/zlib109.zip".
+	 If the zipfile cannot be opened (file don't exist or in not valid), the
+	   return value is NULL.
+     Else, the return value is a unzFile Handle, usable with other function
+	   of this unzip package.
+*/
+extern unzFile ZEXPORT unzOpen (path)
+	const char *path;
+{
+	unz_s us;
+	unz_s *s;
+	uLong central_pos,uL;
+	FILE * fin ;
+
+	uLong number_disk;          /* number of the current dist, used for 
+								   spaning ZIP, unsupported, always 0*/
+	uLong number_disk_with_CD;  /* number the the disk with central dir, used
+								   for spaning ZIP, unsupported, always 0*/
+	uLong number_entry_CD;      /* total number of entries in
+	                               the central dir 
+	                               (same than number_entry on nospan) */
+
+	int err=UNZ_OK;
+
+    if (unz_copyright[0]!=' ')
+        return NULL;
+
+    fin=fopen(path,"rb");
+	if (fin==NULL)
+		return NULL;
+
+	central_pos = unzlocal_SearchCentralDir(fin);
+	if (central_pos==0)
+		err=UNZ_ERRNO;
+
+	if (fseek(fin,central_pos,SEEK_SET)!=0)
+		err=UNZ_ERRNO;
+
+	/* the signature, already checked */
+	if (unzlocal_getLong(fin,&uL)!=UNZ_OK)
+		err=UNZ_ERRNO;
+
+	/* number of this disk */
+	if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK)
+		err=UNZ_ERRNO;
+
+	/* number of the disk with the start of the central directory */
+	if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK)
+		err=UNZ_ERRNO;
+
+	/* total number of entries in the central dir on this disk */
+	if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK)
+		err=UNZ_ERRNO;
+
+	/* total number of entries in the central dir */
+	if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if ((number_entry_CD!=us.gi.number_entry) ||
+		(number_disk_with_CD!=0) ||
+		(number_disk!=0))
+		err=UNZ_BADZIPFILE;
+
+	/* size of the central directory */
+	if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK)
+		err=UNZ_ERRNO;
+
+	/* offset of start of central directory with respect to the 
+	      starting disk number */
+	if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK)
+		err=UNZ_ERRNO;
+
+	/* zipfile comment length */
+	if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if ((central_pos<us.offset_central_dir+us.size_central_dir) && 
+		(err==UNZ_OK))
+		err=UNZ_BADZIPFILE;
+
+	if (err!=UNZ_OK)
+	{
+		fclose(fin);
+		return NULL;
+	}
+
+	us.file=fin;
+	us.byte_before_the_zipfile = central_pos -
+		                    (us.offset_central_dir+us.size_central_dir);
+	us.central_pos = central_pos;
+    us.pfile_in_zip_read = NULL;
+	
+
+	s=(unz_s*)ALLOC(sizeof(unz_s));
+	*s=us;
+	unzGoToFirstFile((unzFile)s);	
+	return (unzFile)s;	
+}
+
+
+/*
+  Close a ZipFile opened with unzipOpen.
+  If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
+    these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+  return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzClose (file)
+	unzFile file;
+{
+	unz_s* s;
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+
+    if (s->pfile_in_zip_read!=NULL)
+        unzCloseCurrentFile(file);
+
+	fclose(s->file);
+	TRYFREE(s);
+	return UNZ_OK;
+}
+
+
+/*
+  Write info about the ZipFile in the *pglobal_info structure.
+  No preparation of the structure is needed
+  return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info)
+	unzFile file;
+	unz_global_info *pglobal_info;
+{
+	unz_s* s;
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+	*pglobal_info=s->gi;
+	return UNZ_OK;
+}
+
+
+/*
+   Translate date/time from Dos format to tm_unz (readable more easilty)
+*/
+local void unzlocal_DosDateToTmuDate (ulDosDate, ptm)
+    uLong ulDosDate;
+    tm_unz* ptm;
+{
+    uLong uDate;
+    uDate = (uLong)(ulDosDate>>16);
+    ptm->tm_mday = (uInt)(uDate&0x1f) ;
+    ptm->tm_mon =  (uInt)((((uDate)&0x1E0)/0x20)-1) ;
+    ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ;
+
+    ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800);
+    ptm->tm_min =  (uInt) ((ulDosDate&0x7E0)/0x20) ;
+    ptm->tm_sec =  (uInt) (2*(ulDosDate&0x1f)) ;
+}
+
+/*
+  Get Info about the current file in the zipfile, with internal only info
+*/
+local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file,
+                                                  unz_file_info *pfile_info,
+                                                  unz_file_info_internal 
+                                                  *pfile_info_internal,
+                                                  char *szFileName,
+												  uLong fileNameBufferSize,
+                                                  void *extraField,
+												  uLong extraFieldBufferSize,
+                                                  char *szComment,
+												  uLong commentBufferSize));
+
+local int unzlocal_GetCurrentFileInfoInternal (file,
+                                              pfile_info,
+                                              pfile_info_internal,
+                                              szFileName, fileNameBufferSize,
+                                              extraField, extraFieldBufferSize,
+                                              szComment,  commentBufferSize)
+	unzFile file;
+	unz_file_info *pfile_info;
+	unz_file_info_internal *pfile_info_internal;
+	char *szFileName;
+	uLong fileNameBufferSize;
+	void *extraField;
+	uLong extraFieldBufferSize;
+	char *szComment;
+	uLong commentBufferSize;
+{
+	unz_s* s;
+	unz_file_info file_info;
+	unz_file_info_internal file_info_internal;
+	int err=UNZ_OK;
+	uLong uMagic;
+	long lSeek=0;
+
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+	if (fseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0)
+		err=UNZ_ERRNO;
+
+
+	/* we check the magic */
+	if (err==UNZ_OK) {
+		if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK)
+			err=UNZ_ERRNO;
+		else if (uMagic!=0x02014b50)
+			err=UNZ_BADZIPFILE;
+	}
+
+	if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+    unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date);
+
+	if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	lSeek+=file_info.size_filename;
+	if ((err==UNZ_OK) && (szFileName!=NULL))
+	{
+		uLong uSizeRead ;
+		if (file_info.size_filename<fileNameBufferSize)
+		{
+			*(szFileName+file_info.size_filename)='\0';
+			uSizeRead = file_info.size_filename;
+		}
+		else
+			uSizeRead = fileNameBufferSize;
+
+		if ((file_info.size_filename>0) && (fileNameBufferSize>0))
+			if (fread(szFileName,(uInt)uSizeRead,1,s->file)!=1)
+				err=UNZ_ERRNO;
+		lSeek -= uSizeRead;
+	}
+
+	
+	if ((err==UNZ_OK) && (extraField!=NULL))
+	{
+		uLong uSizeRead ;
+		if (file_info.size_file_extra<extraFieldBufferSize)
+			uSizeRead = file_info.size_file_extra;
+		else
+			uSizeRead = extraFieldBufferSize;
+
+		if (lSeek!=0) {
+			if (fseek(s->file,lSeek,SEEK_CUR)==0)
+				lSeek=0;
+			else
+				err=UNZ_ERRNO;
+		}
+
+		if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
+			if (fread(extraField,(uInt)uSizeRead,1,s->file)!=1)
+				err=UNZ_ERRNO;
+		lSeek += file_info.size_file_extra - uSizeRead;
+	}
+	else
+		lSeek+=file_info.size_file_extra; 
+
+	
+	if ((err==UNZ_OK) && (szComment!=NULL))
+	{
+		uLong uSizeRead ;
+		if (file_info.size_file_comment<commentBufferSize)
+		{
+			*(szComment+file_info.size_file_comment)='\0';
+			uSizeRead = file_info.size_file_comment;
+		}
+		else
+			uSizeRead = commentBufferSize;
+
+		if (lSeek!=0) {
+			if (fseek(s->file,lSeek,SEEK_CUR)==0)
+				lSeek=0;
+			else
+				err=UNZ_ERRNO;
+		}
+		if ((file_info.size_file_comment>0) && (commentBufferSize>0))
+			if (fread(szComment,(uInt)uSizeRead,1,s->file)!=1)
+				err=UNZ_ERRNO;
+		lSeek+=file_info.size_file_comment - uSizeRead;
+	}
+	else
+		lSeek+=file_info.size_file_comment;
+
+	if ((err==UNZ_OK) && (pfile_info!=NULL))
+		*pfile_info=file_info;
+
+	if ((err==UNZ_OK) && (pfile_info_internal!=NULL))
+		*pfile_info_internal=file_info_internal;
+
+	return err;
+}
+
+
+
+/*
+  Write info about the ZipFile in the *pglobal_info structure.
+  No preparation of the structure is needed
+  return UNZ_OK if there is no problem.
+*/
+extern int ZEXPORT unzGetCurrentFileInfo (file,
+                                                  pfile_info,
+                                                  szFileName, fileNameBufferSize,
+                                                  extraField, extraFieldBufferSize,
+                                                  szComment,  commentBufferSize)
+	unzFile file;
+	unz_file_info *pfile_info;
+	char *szFileName;
+	uLong fileNameBufferSize;
+	void *extraField;
+	uLong extraFieldBufferSize;
+	char *szComment;
+	uLong commentBufferSize;
+{
+	return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,
+												szFileName,fileNameBufferSize,
+												extraField,extraFieldBufferSize,
+												szComment,commentBufferSize);
+}
+
+/*
+  Set the current file of the zipfile to the first file.
+  return UNZ_OK if there is no problem
+*/
+extern int ZEXPORT unzGoToFirstFile (file)
+	unzFile file;
+{
+	int err=UNZ_OK;
+	unz_s* s;
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+	s->pos_in_central_dir=s->offset_central_dir;
+	s->num_file=0;
+	err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+											 &s->cur_file_info_internal,
+											 NULL,0,NULL,0,NULL,0);
+	s->current_file_ok = (err == UNZ_OK);
+	return err;
+}
+
+
+/*
+  Set the current file of the zipfile to the next file.
+  return UNZ_OK if there is no problem
+  return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+extern int ZEXPORT unzGoToNextFile (file)
+	unzFile file;
+{
+	unz_s* s;	
+	int err;
+
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+	if (!s->current_file_ok)
+		return UNZ_END_OF_LIST_OF_FILE;
+	if (s->num_file+1==s->gi.number_entry)
+		return UNZ_END_OF_LIST_OF_FILE;
+
+	s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename +
+			s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ;
+	s->num_file++;
+	err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+											   &s->cur_file_info_internal,
+											   NULL,0,NULL,0,NULL,0);
+	s->current_file_ok = (err == UNZ_OK);
+	return err;
+}
+
+
+/*
+  Try locate the file szFileName in the zipfile.
+  For the iCaseSensitivity signification, see unzipStringFileNameCompare
+
+  return value :
+  UNZ_OK if the file is found. It becomes the current file.
+  UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity)
+	unzFile file;
+	const char *szFileName;
+	int iCaseSensitivity;
+{
+	unz_s* s;	
+	int err;
+
+	
+	uLong num_fileSaved;
+	uLong pos_in_central_dirSaved;
+
+
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+
+    if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP)
+        return UNZ_PARAMERROR;
+
+	s=(unz_s*)file;
+	if (!s->current_file_ok)
+		return UNZ_END_OF_LIST_OF_FILE;
+
+	num_fileSaved = s->num_file;
+	pos_in_central_dirSaved = s->pos_in_central_dir;
+
+	err = unzGoToFirstFile(file);
+
+	while (err == UNZ_OK)
+	{
+		char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1];
+		unzGetCurrentFileInfo(file,NULL,
+								szCurrentFileName,sizeof(szCurrentFileName)-1,
+								NULL,0,NULL,0);
+		if (unzStringFileNameCompare(szCurrentFileName,
+										szFileName,iCaseSensitivity)==0)
+			return UNZ_OK;
+		err = unzGoToNextFile(file);
+	}
+
+	s->num_file = num_fileSaved ;
+	s->pos_in_central_dir = pos_in_central_dirSaved ;
+	return err;
+}
+
+
+/*
+  Read the local header of the current zipfile
+  Check the coherency of the local header and info in the end of central
+        directory about this file
+  store in *piSizeVar the size of extra info in local header
+        (filename and size of extra field data)
+*/
+local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
+													poffset_local_extrafield,
+													psize_local_extrafield)
+	unz_s* s;
+	uInt* piSizeVar;
+	uLong *poffset_local_extrafield;
+	uInt  *psize_local_extrafield;
+{
+	uLong uMagic,uData,uFlags;
+	uLong size_filename;
+	uLong size_extra_field;
+	int err=UNZ_OK;
+
+	*piSizeVar = 0;
+	*poffset_local_extrafield = 0;
+	*psize_local_extrafield = 0;
+
+	if (fseek(s->file,s->cur_file_info_internal.offset_curfile +
+								s->byte_before_the_zipfile,SEEK_SET)!=0)
+		return UNZ_ERRNO;
+
+
+	if (err==UNZ_OK) {
+		if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK)
+			err=UNZ_ERRNO;
+		else if (uMagic!=0x04034b50)
+			err=UNZ_BADZIPFILE;
+	}
+
+	if (unzlocal_getShort(s->file,&uData) != UNZ_OK)
+		err=UNZ_ERRNO;
+/*
+	else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion))
+		err=UNZ_BADZIPFILE;
+*/
+	if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK)
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getShort(s->file,&uData) != UNZ_OK)
+		err=UNZ_ERRNO;
+	else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method))
+		err=UNZ_BADZIPFILE;
+
+    if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) &&
+                         (s->cur_file_info.compression_method!=Z_DEFLATED))
+        err=UNZ_BADZIPFILE;
+
+	if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* date/time */
+		err=UNZ_ERRNO;
+
+	if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* crc */
+		err=UNZ_ERRNO;
+	else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) &&
+		                      ((uFlags & 8)==0))
+		err=UNZ_BADZIPFILE;
+
+	if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size compr */
+		err=UNZ_ERRNO;
+	else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) &&
+							  ((uFlags & 8)==0))
+		err=UNZ_BADZIPFILE;
+
+	if (unzlocal_getLong(s->file,&uData) != UNZ_OK) /* size uncompr */
+		err=UNZ_ERRNO;
+	else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && 
+							  ((uFlags & 8)==0))
+		err=UNZ_BADZIPFILE;
+
+
+	if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK)
+		err=UNZ_ERRNO;
+	else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename))
+		err=UNZ_BADZIPFILE;
+
+	*piSizeVar += (uInt)size_filename;
+
+	if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK)
+		err=UNZ_ERRNO;
+	*poffset_local_extrafield= s->cur_file_info_internal.offset_curfile +
+									SIZEZIPLOCALHEADER + size_filename;
+	*psize_local_extrafield = (uInt)size_extra_field;
+
+	*piSizeVar += (uInt)size_extra_field;
+
+	return err;
+}
+												
+/*
+  Open for reading data the current file in the zipfile.
+  If there is no error and the file is opened, the return value is UNZ_OK.
+*/
+extern int ZEXPORT unzOpenCurrentFile (file)
+	unzFile file;
+{
+	int err=UNZ_OK;
+	int Store;
+	uInt iSizeVar;
+	unz_s* s;
+	file_in_zip_read_info_s* pfile_in_zip_read_info;
+	uLong offset_local_extrafield;  /* offset of the local extra field */
+	uInt  size_local_extrafield;    /* size of the local extra field */
+
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+	if (!s->current_file_ok)
+		return UNZ_PARAMERROR;
+
+    if (s->pfile_in_zip_read != NULL)
+        unzCloseCurrentFile(file);
+
+	if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar,
+				&offset_local_extrafield,&size_local_extrafield)!=UNZ_OK)
+		return UNZ_BADZIPFILE;
+
+	pfile_in_zip_read_info = (file_in_zip_read_info_s*)
+									    ALLOC(sizeof(file_in_zip_read_info_s));
+	if (pfile_in_zip_read_info==NULL)
+		return UNZ_INTERNALERROR;
+
+	pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE);
+	pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield;
+	pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield;
+	pfile_in_zip_read_info->pos_local_extrafield=0;
+
+	if (pfile_in_zip_read_info->read_buffer==NULL)
+	{
+		TRYFREE(pfile_in_zip_read_info);
+		return UNZ_INTERNALERROR;
+	}
+
+	pfile_in_zip_read_info->stream_initialised=0;
+	
+	if ((s->cur_file_info.compression_method!=0) &&
+        (s->cur_file_info.compression_method!=Z_DEFLATED))
+		err=UNZ_BADZIPFILE;
+	Store = s->cur_file_info.compression_method==0;
+
+	pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc;
+	pfile_in_zip_read_info->crc32=0;
+	pfile_in_zip_read_info->compression_method =
+            s->cur_file_info.compression_method;
+	pfile_in_zip_read_info->file=s->file;
+	pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile;
+
+    pfile_in_zip_read_info->stream.total_out = 0;
+
+	if (!Store)
+	{
+	  pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
+	  pfile_in_zip_read_info->stream.zfree = (free_func)0;
+	  pfile_in_zip_read_info->stream.opaque = (voidpf)0; 
+      
+	  err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
+	  if (err == Z_OK)
+	    pfile_in_zip_read_info->stream_initialised=1;
+        /* windowBits is passed < 0 to tell that there is no zlib header.
+         * Note that in this case inflate *requires* an extra "dummy" byte
+         * after the compressed stream in order to complete decompression and
+         * return Z_STREAM_END. 
+         * In unzip, i don't wait absolutely Z_STREAM_END because I known the 
+         * size of both compressed and uncompressed data
+         */
+	}
+	pfile_in_zip_read_info->rest_read_compressed = 
+            s->cur_file_info.compressed_size ;
+	pfile_in_zip_read_info->rest_read_uncompressed = 
+            s->cur_file_info.uncompressed_size ;
+
+	
+	pfile_in_zip_read_info->pos_in_zipfile = 
+            s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + 
+			  iSizeVar;
+	
+	pfile_in_zip_read_info->stream.avail_in = (uInt)0;
+
+
+	s->pfile_in_zip_read = pfile_in_zip_read_info;
+    return UNZ_OK;
+}
+
+
+/*
+  Read bytes from the current file.
+  buf contain buffer where data must be copied
+  len the size of buf.
+
+  return the number of byte copied if somes bytes are copied
+  return 0 if the end of file was reached
+  return <0 with error code if there is an error
+    (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+extern int ZEXPORT unzReadCurrentFile  (file, buf, len)
+	unzFile file;
+	voidp buf;
+	unsigned len;
+{
+	int err=UNZ_OK;
+	uInt iRead = 0;
+	unz_s* s;
+	file_in_zip_read_info_s* pfile_in_zip_read_info;
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+    pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+	if (pfile_in_zip_read_info==NULL)
+		return UNZ_PARAMERROR;
+
+
+	if ((pfile_in_zip_read_info->read_buffer == NULL))
+		return UNZ_END_OF_LIST_OF_FILE;
+	if (len==0)
+		return 0;
+
+	pfile_in_zip_read_info->stream.next_out = (Bytef*)buf;
+
+	pfile_in_zip_read_info->stream.avail_out = (uInt)len;
+	
+	if (len>pfile_in_zip_read_info->rest_read_uncompressed)
+		pfile_in_zip_read_info->stream.avail_out = 
+		  (uInt)pfile_in_zip_read_info->rest_read_uncompressed;
+
+	while (pfile_in_zip_read_info->stream.avail_out>0)
+	{
+		if ((pfile_in_zip_read_info->stream.avail_in==0) &&
+            (pfile_in_zip_read_info->rest_read_compressed>0))
+		{
+			uInt uReadThis = UNZ_BUFSIZE;
+			if (pfile_in_zip_read_info->rest_read_compressed<uReadThis)
+				uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed;
+			if (uReadThis == 0)
+				return UNZ_EOF;
+			if (fseek(pfile_in_zip_read_info->file,
+                      pfile_in_zip_read_info->pos_in_zipfile + 
+                         pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0)
+				return UNZ_ERRNO;
+			if (fread(pfile_in_zip_read_info->read_buffer,uReadThis,1,
+                         pfile_in_zip_read_info->file)!=1)
+				return UNZ_ERRNO;
+			pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
+
+			pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
+			
+			pfile_in_zip_read_info->stream.next_in = 
+                (Bytef*)pfile_in_zip_read_info->read_buffer;
+			pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis;
+		}
+
+		if (pfile_in_zip_read_info->compression_method==0)
+		{
+			uInt uDoCopy,i ;
+			if (pfile_in_zip_read_info->stream.avail_out < 
+                            pfile_in_zip_read_info->stream.avail_in)
+				uDoCopy = pfile_in_zip_read_info->stream.avail_out ;
+			else
+				uDoCopy = pfile_in_zip_read_info->stream.avail_in ;
+				
+			for (i=0;i<uDoCopy;i++)
+				*(pfile_in_zip_read_info->stream.next_out+i) =
+                        *(pfile_in_zip_read_info->stream.next_in+i);
+					
+			pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,
+								pfile_in_zip_read_info->stream.next_out,
+								uDoCopy);
+			pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy;
+			pfile_in_zip_read_info->stream.avail_in -= uDoCopy;
+			pfile_in_zip_read_info->stream.avail_out -= uDoCopy;
+			pfile_in_zip_read_info->stream.next_out += uDoCopy;
+			pfile_in_zip_read_info->stream.next_in += uDoCopy;
+            pfile_in_zip_read_info->stream.total_out += uDoCopy;
+			iRead += uDoCopy;
+		}
+		else
+		{
+			uLong uTotalOutBefore,uTotalOutAfter;
+			const Bytef *bufBefore;
+			uLong uOutThis;
+			int flush=Z_SYNC_FLUSH;
+
+			uTotalOutBefore = pfile_in_zip_read_info->stream.total_out;
+			bufBefore = pfile_in_zip_read_info->stream.next_out;
+
+			/*
+			if ((pfile_in_zip_read_info->rest_read_uncompressed ==
+			         pfile_in_zip_read_info->stream.avail_out) &&
+				(pfile_in_zip_read_info->rest_read_compressed == 0))
+				flush = Z_FINISH;
+			*/
+			err=inflate(&pfile_in_zip_read_info->stream,flush);
+
+			uTotalOutAfter = pfile_in_zip_read_info->stream.total_out;
+			uOutThis = uTotalOutAfter-uTotalOutBefore;
+			
+			pfile_in_zip_read_info->crc32 = 
+                crc32(pfile_in_zip_read_info->crc32,bufBefore,
+                        (uInt)(uOutThis));
+
+			pfile_in_zip_read_info->rest_read_uncompressed -=
+                uOutThis;
+
+			iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
+            
+			if (err==Z_STREAM_END)
+				return (iRead==0) ? UNZ_EOF : iRead;
+			if (err!=Z_OK) 
+				break;
+		}
+	}
+
+	if (err==Z_OK)
+		return iRead;
+	return err;
+}
+
+
+/*
+  Give the current position in uncompressed data
+*/
+extern z_off_t ZEXPORT unztell (file)
+	unzFile file;
+{
+	unz_s* s;
+	file_in_zip_read_info_s* pfile_in_zip_read_info;
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+    pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+	if (pfile_in_zip_read_info==NULL)
+		return UNZ_PARAMERROR;
+
+	return (z_off_t)pfile_in_zip_read_info->stream.total_out;
+}
+
+
+/*
+  return 1 if the end of file was reached, 0 elsewhere 
+*/
+extern int ZEXPORT unzeof (file)
+	unzFile file;
+{
+	unz_s* s;
+	file_in_zip_read_info_s* pfile_in_zip_read_info;
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+    pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+	if (pfile_in_zip_read_info==NULL)
+		return UNZ_PARAMERROR;
+	
+	if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
+		return 1;
+	else
+		return 0;
+}
+
+
+
+/*
+  Read extra field from the current file (opened by unzOpenCurrentFile)
+  This is the local-header version of the extra field (sometimes, there is
+    more info in the local-header version than in the central-header)
+
+  if buf==NULL, it return the size of the local extra field that can be read
+
+  if buf!=NULL, len is the size of the buffer, the extra header is copied in
+	buf.
+  the return value is the number of bytes copied in buf, or (if <0) 
+	the error code
+*/
+extern int ZEXPORT unzGetLocalExtrafield (file,buf,len)
+	unzFile file;
+	voidp buf;
+	unsigned len;
+{
+	unz_s* s;
+	file_in_zip_read_info_s* pfile_in_zip_read_info;
+	uInt read_now;
+	uLong size_to_read;
+
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+    pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+	if (pfile_in_zip_read_info==NULL)
+		return UNZ_PARAMERROR;
+
+	size_to_read = (pfile_in_zip_read_info->size_local_extrafield - 
+				pfile_in_zip_read_info->pos_local_extrafield);
+
+	if (buf==NULL)
+		return (int)size_to_read;
+	
+	if (len>size_to_read)
+		read_now = (uInt)size_to_read;
+	else
+		read_now = (uInt)len ;
+
+	if (read_now==0)
+		return 0;
+	
+	if (fseek(pfile_in_zip_read_info->file,
+              pfile_in_zip_read_info->offset_local_extrafield + 
+			  pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0)
+		return UNZ_ERRNO;
+
+	if (fread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1)
+		return UNZ_ERRNO;
+
+	return (int)read_now;
+}
+
+/*
+  Close the file in zip opened with unzipOpenCurrentFile
+  Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+extern int ZEXPORT unzCloseCurrentFile (file)
+	unzFile file;
+{
+	int err=UNZ_OK;
+
+	unz_s* s;
+	file_in_zip_read_info_s* pfile_in_zip_read_info;
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+    pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+	if (pfile_in_zip_read_info==NULL)
+		return UNZ_PARAMERROR;
+
+
+	if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
+	{
+		if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
+			err=UNZ_CRCERROR;
+	}
+
+
+	TRYFREE(pfile_in_zip_read_info->read_buffer);
+	pfile_in_zip_read_info->read_buffer = NULL;
+	if (pfile_in_zip_read_info->stream_initialised)
+		inflateEnd(&pfile_in_zip_read_info->stream);
+
+	pfile_in_zip_read_info->stream_initialised = 0;
+	TRYFREE(pfile_in_zip_read_info);
+
+    s->pfile_in_zip_read=NULL;
+
+	return err;
+}
+
+
+/*
+  Get the global comment string of the ZipFile, in the szComment buffer.
+  uSizeBuf is the size of the szComment buffer.
+  return the number of byte copied or an error code <0
+*/
+extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf)
+	unzFile file;
+	char *szComment;
+	uLong uSizeBuf;
+{
+	//int err=UNZ_OK;
+	unz_s* s;
+	uLong uReadThis ;
+	if (file==NULL)
+		return UNZ_PARAMERROR;
+	s=(unz_s*)file;
+
+	uReadThis = uSizeBuf;
+	if (uReadThis>s->gi.size_comment)
+		uReadThis = s->gi.size_comment;
+
+	if (fseek(s->file,s->central_pos+22,SEEK_SET)!=0)
+		return UNZ_ERRNO;
+
+	if (uReadThis>0)
+    {
+      *szComment='\0';
+	  if (fread(szComment,(uInt)uReadThis,1,s->file)!=1)
+		return UNZ_ERRNO;
+    }
+
+	if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment))
+		*(szComment+s->gi.size_comment)='\0';
+	return (int)uReadThis;
+}
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/unzip.def
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/unzip.def	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/unzip.def	(revision 38)
@@ -0,0 +1,15 @@
+	unzOpen                       @61
+	unzClose                      @62
+	unzGetGlobalInfo              @63
+	unzGetCurrentFileInfo         @64
+	unzGoToFirstFile              @65
+	unzGoToNextFile               @66
+	unzOpenCurrentFile            @67
+	unzReadCurrentFile            @68
+	unztell                       @70
+	unzeof                        @71
+	unzCloseCurrentFile           @72
+	unzGetGlobalComment           @73
+	unzStringFileNameCompare      @74
+	unzLocateFile                 @75
+	unzGetLocalExtrafield         @76
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/unzip.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/unzip.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/unzip.h	(revision 38)
@@ -0,0 +1,275 @@
+/* unzip.h -- IO for uncompress .zip files using zlib 
+   Version 0.15 beta, Mar 19th, 1998,
+
+   Copyright (C) 1998 Gilles Vollant
+
+   This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g
+     WinZip, InfoZip tools and compatible.
+   Encryption and multi volume ZipFile (span) are not supported.
+   Old compressions used by old PKZip 1.x are not supported
+
+   THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE
+   CAN CHANGE IN FUTURE VERSION !!
+   I WAIT FEEDBACK at mail info@winimage.com
+   Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution
+
+   Condition of use and distribution are the same than zlib :
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+
+*/
+/* for more info about .ZIP format, see 
+      ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip
+   PkWare has also a specification at :
+      ftp://ftp.pkware.com/probdesc.zip */
+
+#ifndef _unz_H
+#define _unz_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+    from (void*) without cast */
+typedef struct TagunzFile__ { int unused; } unzFile__; 
+typedef unzFile__ *unzFile;
+#else
+typedef voidp unzFile;
+#endif
+
+
+#define UNZ_OK                                  (0)
+#define UNZ_END_OF_LIST_OF_FILE (-100)
+#define UNZ_ERRNO               (Z_ERRNO)
+#define UNZ_EOF                 (0)
+#define UNZ_PARAMERROR                  (-102)
+#define UNZ_BADZIPFILE                  (-103)
+#define UNZ_INTERNALERROR               (-104)
+#define UNZ_CRCERROR                    (-105)
+
+/* tm_unz contain date/time info */
+typedef struct tm_unz_s 
+{
+	uInt tm_sec;            /* seconds after the minute - [0,59] */
+	uInt tm_min;            /* minutes after the hour - [0,59] */
+	uInt tm_hour;           /* hours since midnight - [0,23] */
+	uInt tm_mday;           /* day of the month - [1,31] */
+	uInt tm_mon;            /* months since January - [0,11] */
+	uInt tm_year;           /* years - [1980..2044] */
+} tm_unz;
+
+/* unz_global_info structure contain global data about the ZIPfile
+   These data comes from the end of central dir */
+typedef struct unz_global_info_s
+{
+	uLong number_entry;         /* total number of entries in
+				       the central dir on this disk */
+	uLong size_comment;         /* size of the global comment of the zipfile */
+} unz_global_info;
+
+
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_info_s
+{
+    uLong version;              /* version made by                 2 bytes */
+    uLong version_needed;       /* version needed to extract       2 bytes */
+    uLong flag;                 /* general purpose bit flag        2 bytes */
+    uLong compression_method;   /* compression method              2 bytes */
+    uLong dosDate;              /* last mod file date in Dos fmt   4 bytes */
+    uLong crc;                  /* crc-32                          4 bytes */
+    uLong compressed_size;      /* compressed size                 4 bytes */ 
+    uLong uncompressed_size;    /* uncompressed size               4 bytes */ 
+    uLong size_filename;        /* filename length                 2 bytes */
+    uLong size_file_extra;      /* extra field length              2 bytes */
+    uLong size_file_comment;    /* file comment length             2 bytes */
+
+    uLong disk_num_start;       /* disk number start               2 bytes */
+    uLong internal_fa;          /* internal file attributes        2 bytes */
+    uLong external_fa;          /* external file attributes        4 bytes */
+
+    tm_unz tmu_date;
+} unz_file_info;
+
+extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
+												 const char* fileName2,
+												 int iCaseSensitivity));
+/*
+   Compare two filename (fileName1,fileName2).
+   If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+   If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+								or strcasecmp)
+   If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+	(like 1 on Unix, 2 on Windows)
+*/
+
+
+extern unzFile ZEXPORT unzOpen OF((const char *path));
+/*
+  Open a Zip file. path contain the full pathname (by example,
+     on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer
+	 "zlib/zlib111.zip".
+	 If the zipfile cannot be opened (file don't exist or in not valid), the
+	   return value is NULL.
+     Else, the return value is a unzFile Handle, usable with other function
+	   of this unzip package.
+*/
+
+extern int ZEXPORT unzClose OF((unzFile file));
+/*
+  Close a ZipFile opened with unzipOpen.
+  If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
+    these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+  return UNZ_OK if there is no problem. */
+
+extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
+					unz_global_info *pglobal_info));
+/*
+  Write info about the ZipFile in the *pglobal_info structure.
+  No preparation of the structure is needed
+  return UNZ_OK if there is no problem. */
+
+
+extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
+										   char *szComment,
+					   uLong uSizeBuf));
+/*
+  Get the global comment string of the ZipFile, in the szComment buffer.
+  uSizeBuf is the size of the szComment buffer.
+  return the number of byte copied or an error code <0
+*/
+
+
+/***************************************************************************/
+/* Unzip package allow you browse the directory of the zipfile */
+
+extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
+/*
+  Set the current file of the zipfile to the first file.
+  return UNZ_OK if there is no problem
+*/
+
+extern int ZEXPORT unzGoToNextFile OF((unzFile file));
+/*
+  Set the current file of the zipfile to the next file.
+  return UNZ_OK if there is no problem
+  return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+
+extern int ZEXPORT unzLocateFile OF((unzFile file, 
+				     const char *szFileName,
+				     int iCaseSensitivity));
+/*
+  Try locate the file szFileName in the zipfile.
+  For the iCaseSensitivity signification, see unzStringFileNameCompare
+
+  return value :
+  UNZ_OK if the file is found. It becomes the current file.
+  UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+
+
+extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
+					     unz_file_info *pfile_info,
+					     char *szFileName,
+					     uLong fileNameBufferSize,
+					     void *extraField,
+					     uLong extraFieldBufferSize,
+					     char *szComment,
+					     uLong commentBufferSize));
+/*
+  Get Info about the current file
+  if pfile_info!=NULL, the *pfile_info structure will contain somes info about
+	    the current file
+  if szFileName!=NULL, the filemane string will be copied in szFileName
+			(fileNameBufferSize is the size of the buffer)
+  if extraField!=NULL, the extra field information will be copied in extraField
+			(extraFieldBufferSize is the size of the buffer).
+			This is the Central-header version of the extra field
+  if szComment!=NULL, the comment string of the file will be copied in szComment
+			(commentBufferSize is the size of the buffer)
+*/
+
+/***************************************************************************/
+/* for reading the content of the current zipfile, you can open it, read data
+   from it, and close it (you can close it before reading all the file)
+   */
+
+extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
+/*
+  Open for reading data the current file in the zipfile.
+  If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
+/*
+  Close the file in zip opened with unzOpenCurrentFile
+  Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+
+												
+extern int ZEXPORT unzReadCurrentFile OF((unzFile file, 
+					  voidp buf,
+					  unsigned len));
+/*
+  Read bytes from the current file (opened by unzOpenCurrentFile)
+  buf contain buffer where data must be copied
+  len the size of buf.
+
+  return the number of byte copied if somes bytes are copied
+  return 0 if the end of file was reached
+  return <0 with error code if there is an error
+    (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+
+extern z_off_t ZEXPORT unztell OF((unzFile file));
+/*
+  Give the current position in uncompressed data
+*/
+
+extern int ZEXPORT unzeof OF((unzFile file));
+/*
+  return 1 if the end of file was reached, 0 elsewhere 
+*/
+
+extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
+											 voidp buf,
+											 unsigned len));
+/*
+  Read extra field from the current file (opened by unzOpenCurrentFile)
+  This is the local-header version of the extra field (sometimes, there is
+    more info in the local-header version than in the central-header)
+
+  if buf==NULL, it return the size of the local extra field
+
+  if buf!=NULL, len is the size of the buffer, the extra header is copied in
+	buf.
+  the return value is the number of bytes copied in buf, or (if <0) 
+	the error code
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _unz_H */
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/win32/zconf.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/win32/zconf.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/win32/zconf.h	(revision 38)
@@ -0,0 +1,279 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* @(#) $Id: zconf.h,v 1.1 2003/03/15 00:32:36 justin Exp $ */
+
+#ifndef _ZCONF_H
+#define _ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+#  define deflateInit_	z_deflateInit_
+#  define deflate	z_deflate
+#  define deflateEnd	z_deflateEnd
+#  define inflateInit_ 	z_inflateInit_
+#  define inflate	z_inflate
+#  define inflateEnd	z_inflateEnd
+#  define deflateInit2_	z_deflateInit2_
+#  define deflateSetDictionary z_deflateSetDictionary
+#  define deflateCopy	z_deflateCopy
+#  define deflateReset	z_deflateReset
+#  define deflateParams	z_deflateParams
+#  define inflateInit2_	z_inflateInit2_
+#  define inflateSetDictionary z_inflateSetDictionary
+#  define inflateSync	z_inflateSync
+#  define inflateSyncPoint z_inflateSyncPoint
+#  define inflateReset	z_inflateReset
+#  define compress	z_compress
+#  define compress2	z_compress2
+#  define uncompress	z_uncompress
+#  define adler32	z_adler32
+#  define crc32		z_crc32
+#  define get_crc_table z_get_crc_table
+
+#  define Byte		z_Byte
+#  define uInt		z_uInt
+#  define uLong		z_uLong
+#  define Bytef	        z_Bytef
+#  define charf		z_charf
+#  define intf		z_intf
+#  define uIntf		z_uIntf
+#  define uLongf	z_uLongf
+#  define voidpf	z_voidpf
+#  define voidp		z_voidp
+#endif
+
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+#  define WIN32
+#endif
+#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
+#  ifndef __32BIT__
+#    define __32BIT__
+#  endif
+#endif
+#if defined(__MSDOS__) && !defined(MSDOS)
+#  define MSDOS
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#if defined(MSDOS) && !defined(__32BIT__)
+#  define MAXSEG_64K
+#endif
+#ifdef MSDOS
+#  define UNALIGNED_OK
+#endif
+
+#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32))  && !defined(STDC)
+#  define STDC
+#endif
+#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__)
+#  ifndef STDC
+#    define STDC
+#  endif
+#endif
+
+#ifndef STDC
+#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+#    define const
+#  endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
+#  define NO_DUMMY_DECL
+#endif
+
+/* Old Borland C incorrectly complains about missing returns: */
+#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500)
+#  define NEED_DUMMY_RETURN
+#endif
+
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            (1 << (windowBits+2)) +  (1 << (memLevel+9))
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
+   /* MSC small or medium model */
+#  define SMALL_MEDIUM
+#  ifdef _MSC_VER
+#    define FAR _far
+#  else
+#    define FAR far
+#  endif
+#endif
+#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
+#  ifndef __32BIT__
+#    define SMALL_MEDIUM
+#    define FAR _far
+#  endif
+#endif
+
+/* Compile with -DZLIB_DLL for Windows DLL support */
+#if defined(ZLIB_DLL)
+#  if defined(_WINDOWS) || defined(WINDOWS)
+#    ifdef FAR
+#      undef FAR
+#    endif
+#    include <windows.h>
+#    define ZEXPORT  WINAPI
+#    ifdef WIN32
+#      define ZEXPORTVA  WINAPIV
+#    else
+#      define ZEXPORTVA  FAR _cdecl _export
+#    endif
+#  endif
+#  if defined (__BORLANDC__)
+#    if (__BORLANDC__ >= 0x0500) && defined (WIN32)
+#      include <windows.h>
+#      define ZEXPORT __declspec(dllexport) WINAPI
+#      define ZEXPORTRVA __declspec(dllexport) WINAPIV
+#    else
+#      if defined (_Windows) && defined (__DLL__)
+#        define ZEXPORT _export
+#        define ZEXPORTVA _export
+#      endif
+#    endif
+#  endif
+#endif
+
+#if defined (__BEOS__)
+#  if defined (ZLIB_DLL)
+#    define ZEXTERN extern __declspec(dllexport)
+#  else
+#    define ZEXTERN extern __declspec(dllimport)
+#  endif
+#endif
+
+#ifndef ZEXPORT
+#  define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+#  define ZEXPORTVA
+#endif
+#ifndef ZEXTERN
+#  define ZEXTERN extern
+#endif
+
+#ifndef FAR
+#   define FAR
+#endif
+
+#if !defined(MACOS) && !defined(TARGET_OS_MAC)
+typedef unsigned char  Byte;  /* 8 bits */
+#endif
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+#  define Bytef Byte FAR
+#else
+   typedef Byte  FAR Bytef;
+#endif
+typedef char  FAR charf;
+typedef int   FAR intf;
+typedef uInt  FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void FAR *voidpf;
+   typedef void     *voidp;
+#else
+   typedef Byte FAR *voidpf;
+   typedef Byte     *voidp;
+#endif
+
+#ifdef HAVE_UNISTD_H
+#  include <sys/types.h> /* for off_t */
+#  include <unistd.h>    /* for SEEK_* and off_t */
+#  define z_off_t  off_t
+#endif
+#ifndef SEEK_SET
+#  define SEEK_SET        0       /* Seek from beginning of file.  */
+#  define SEEK_CUR        1       /* Seek from current position.  */
+#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+#  define  z_off_t long
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+#   pragma map(deflateInit_,"DEIN")
+#   pragma map(deflateInit2_,"DEIN2")
+#   pragma map(deflateEnd,"DEEND")
+#   pragma map(inflateInit_,"ININ")
+#   pragma map(inflateInit2_,"ININ2")
+#   pragma map(inflateEnd,"INEND")
+#   pragma map(inflateSync,"INSY")
+#   pragma map(inflateSetDictionary,"INSEDI")
+#   pragma map(inflate_blocks,"INBL")
+#   pragma map(inflate_blocks_new,"INBLNE")
+#   pragma map(inflate_blocks_free,"INBLFR")
+#   pragma map(inflate_blocks_reset,"INBLRE")
+#   pragma map(inflate_codes_free,"INCOFR")
+#   pragma map(inflate_codes,"INCO")
+#   pragma map(inflate_fast,"INFA")
+#   pragma map(inflate_flush,"INFLU")
+#   pragma map(inflate_mask,"INMA")
+#   pragma map(inflate_set_dictionary,"INSEDI2")
+#   pragma map(inflate_copyright,"INCOPY")
+#   pragma map(inflate_trees_bits,"INTRBI")
+#   pragma map(inflate_trees_dynamic,"INTRDY")
+#   pragma map(inflate_trees_fixed,"INTRFI")
+#   pragma map(inflate_trees_free,"INTRFR")
+#endif
+
+#endif /* _ZCONF_H */
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/win32/zlib.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/win32/zlib.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/win32/zlib.h	(revision 38)
@@ -0,0 +1,893 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.1.4, March 11th, 2002
+
+  Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+
+  The data format used by the zlib library is described by RFCs (Request for
+  Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+  (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.1.4"
+
+/* 
+     The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed
+  data.  This version of the library supports only one compression method
+  (deflation) but other algorithms will be added later and will have the same
+  stream interface.
+
+     Compression can be done in a single step if the buffers are large
+  enough (for example if an input file is mmap'ed), or can be done by
+  repeated calls of the compression function.  In the latter case, the
+  application must provide more input and/or consume the output
+  (providing more output space) before each call.
+
+     The library also supports reading and writing files in gzip (.gz) format
+  with an interface similar to that of stdio.
+
+     The library does not install any signal handler. The decoder checks
+  the consistency of the compressed data, so the library should never
+  crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void   (*free_func)  OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+    Bytef    *next_in;  /* next input byte */
+    uInt     avail_in;  /* number of bytes available at next_in */
+    uLong    total_in;  /* total nb of input bytes read so far */
+
+    Bytef    *next_out; /* next output byte should be put there */
+    uInt     avail_out; /* remaining free space at next_out */
+    uLong    total_out; /* total nb of bytes output so far */
+
+    char     *msg;      /* last error message, NULL if no error */
+    struct internal_state FAR *state; /* not visible by applications */
+
+    alloc_func zalloc;  /* used to allocate the internal state */
+    free_func  zfree;   /* used to free the internal state */
+    voidpf     opaque;  /* private data object passed to zalloc and zfree */
+
+    int     data_type;  /* best guess about the data type: ascii or binary */
+    uLong   adler;      /* adler32 value of the uncompressed data */
+    uLong   reserved;   /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+   The application must update next_in and avail_in when avail_in has
+   dropped to zero. It must update next_out and avail_out when avail_out
+   has dropped to zero. The application must initialize zalloc, zfree and
+   opaque before calling the init function. All other fields are set by the
+   compression library and must not be updated by the application.
+
+   The opaque value provided by the application will be passed as the first
+   parameter for calls of zalloc and zfree. This can be useful for custom
+   memory management. The compression library attaches no meaning to the
+   opaque value.
+
+   zalloc must return Z_NULL if there is not enough memory for the object.
+   If zlib is used in a multi-threaded application, zalloc and zfree must be
+   thread safe.
+
+   On 16-bit systems, the functions zalloc and zfree must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this
+   if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+   pointers returned by zalloc for objects of exactly 65536 bytes *must*
+   have their offset normalized to zero. The default allocation function
+   provided by this library ensures this (see zutil.c). To reduce memory
+   requirements and avoid any allocation of 64K objects, at the expense of
+   compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+   The fields total_in and total_out can be used for statistics or
+   progress reports. After compression, total_in holds the total size of
+   the uncompressed data and may be saved for use in the decompressor
+   (particularly if the decompressor wants to decompress everything in
+   a single step).
+*/
+
+                        /* constants */
+
+#define Z_NO_FLUSH      0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH    2
+#define Z_FULL_FLUSH    3
+#define Z_FINISH        4
+/* Allowed flush values; see deflate() below for details */
+
+#define Z_OK            0
+#define Z_STREAM_END    1
+#define Z_NEED_DICT     2
+#define Z_ERRNO        (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR   (-3)
+#define Z_MEM_ERROR    (-4)
+#define Z_BUF_ERROR    (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION         0
+#define Z_BEST_SPEED             1
+#define Z_BEST_COMPRESSION       9
+#define Z_DEFAULT_COMPRESSION  (-1)
+/* compression levels */
+
+#define Z_FILTERED            1
+#define Z_HUFFMAN_ONLY        2
+#define Z_DEFAULT_STRATEGY    0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY   0
+#define Z_ASCII    1
+#define Z_UNKNOWN  2
+/* Possible values of the data_type field */
+
+#define Z_DEFLATED   8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+                        /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is
+   not compatible with the zlib.h header file used by the application.
+   This check is automatically made by deflateInit and inflateInit.
+ */
+
+/* 
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+     Initializes the internal stream state for compression. The fields
+   zalloc, zfree and opaque must be initialized before by the caller.
+   If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+   use default allocation functions.
+
+     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+   1 gives best speed, 9 gives best compression, 0 gives no compression at
+   all (the input data is simply copied a block at a time).
+   Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+   compression (currently equivalent to level 6).
+
+     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+   with the version assumed by the caller (ZLIB_VERSION).
+   msg is set to null if there is no error message.  deflateInit does not
+   perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+    deflate compresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may introduce some
+  output latency (reading input without producing any output) except when
+  forced to flush.
+
+    The detailed semantics are as follows. deflate performs one or both of the
+  following actions:
+
+  - Compress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in and avail_in are updated and
+    processing will resume at this point for the next call of deflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly. This action is forced if the parameter flush is non zero.
+    Forcing flush frequently degrades the compression ratio, so this parameter
+    should be set only when necessary (in interactive applications).
+    Some output may be provided even if flush is not set.
+
+  Before the call of deflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating avail_in or avail_out accordingly; avail_out
+  should never be zero before the call. The application can consume the
+  compressed output when it wants, for example when the output buffer is full
+  (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+  and with zero avail_out, it must be called again after making room in the
+  output buffer because there might be more output pending.
+
+    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+  flushed to the output buffer and the output is aligned on a byte boundary, so
+  that the decompressor can get all input data available so far. (In particular
+  avail_in is zero after the call if enough output space has been provided
+  before the call.)  Flushing may degrade compression for some compression
+  algorithms and so it should be used only when necessary.
+
+    If flush is set to Z_FULL_FLUSH, all output is flushed as with
+  Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+  restart from this point if previous compressed data has been damaged or if
+  random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+  the compression.
+
+    If deflate returns with avail_out == 0, this function must be called again
+  with the same value of the flush parameter and more output space (updated
+  avail_out), until the flush is complete (deflate returns with non-zero
+  avail_out).
+
+    If the parameter flush is set to Z_FINISH, pending input is processed,
+  pending output is flushed and deflate returns with Z_STREAM_END if there
+  was enough output space; if deflate returns with Z_OK, this function must be
+  called again with Z_FINISH and more output space (updated avail_out) but no
+  more input data, until it returns with Z_STREAM_END or an error. After
+  deflate has returned Z_STREAM_END, the only possible operations on the
+  stream are deflateReset or deflateEnd.
+  
+    Z_FINISH can be used immediately after deflateInit if all the compression
+  is to be done in a single step. In this case, avail_out must be at least
+  0.1% larger than avail_in plus 12 bytes.  If deflate does not return
+  Z_STREAM_END, then it must be called again as described above.
+
+    deflate() sets strm->adler to the adler32 checksum of all input read
+  so far (that is, total_in bytes).
+
+    deflate() may update data_type if it can make a good guess about
+  the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+  binary. This field is only for information purposes and does not affect
+  the compression algorithm in any manner.
+
+    deflate() returns Z_OK if some progress has been made (more input
+  processed or more output produced), Z_STREAM_END if all input has been
+  consumed and all output has been produced (only when flush is set to
+  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+  if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+  (for example avail_in or avail_out was zero).
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+   stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+   prematurely (some input or output was discarded). In the error case,
+   msg may be set but then points to a static string (which must not be
+   deallocated).
+*/
+
+
+/* 
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+     Initializes the internal stream state for decompression. The fields
+   next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+   the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+   value depends on the compression method), inflateInit determines the
+   compression method from the zlib header and allocates all data structures
+   accordingly; otherwise the allocation will be deferred to the first call of
+   inflate.  If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+   use default allocation functions.
+
+     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller.  msg is set to null if there is no error
+   message. inflateInit does not perform any decompression apart from reading
+   the zlib header if present: this will be done by inflate().  (So next_in and
+   avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+    inflate decompresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may some
+  introduce some output latency (reading input without producing any output)
+  except when forced to flush.
+
+  The detailed semantics are as follows. inflate performs one or both of the
+  following actions:
+
+  - Decompress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in is updated and processing
+    will resume at this point for the next call of inflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly.  inflate() provides as much output as possible, until there
+    is no more input data or no more space in the output buffer (see below
+    about the flush parameter).
+
+  Before the call of inflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating the next_* and avail_* values accordingly.
+  The application can consume the uncompressed output when it wants, for
+  example when the output buffer is full (avail_out == 0), or after each
+  call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+  must be called again after making room in the output buffer because there
+  might be more output pending.
+
+    If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much
+  output as possible to the output buffer. The flushing behavior of inflate is
+  not specified for values of the flush parameter other than Z_SYNC_FLUSH
+  and Z_FINISH, but the current implementation actually flushes as much output
+  as possible anyway.
+
+    inflate() should normally be called until it returns Z_STREAM_END or an
+  error. However if all decompression is to be performed in a single step
+  (a single call of inflate), the parameter flush should be set to
+  Z_FINISH. In this case all pending input is processed and all pending
+  output is flushed; avail_out must be large enough to hold all the
+  uncompressed data. (The size of the uncompressed data may have been saved
+  by the compressor for this purpose.) The next operation on this stream must
+  be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+  is never required, but can be used to inform inflate that a faster routine
+  may be used for the single inflate() call.
+
+     If a preset dictionary is needed at this point (see inflateSetDictionary
+  below), inflate sets strm-adler to the adler32 checksum of the
+  dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise 
+  it sets strm->adler to the adler32 checksum of all output produced
+  so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or
+  an error code as described below. At the end of the stream, inflate()
+  checks that its computed adler32 checksum is equal to that saved by the
+  compressor and returns Z_STREAM_END only if the checksum is correct.
+
+    inflate() returns Z_OK if some progress has been made (more input processed
+  or more output produced), Z_STREAM_END if the end of the compressed data has
+  been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+  corrupted (input stream not conforming to the zlib format or incorrect
+  adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent
+  (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not
+  enough memory, Z_BUF_ERROR if no progress is possible or if there was not
+  enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR
+  case, the application may then call inflateSync to look for a good
+  compression block.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+   was inconsistent. In the error case, msg may be set but then points to a
+   static string (which must not be deallocated).
+*/
+
+                        /* Advanced functions */
+
+/*
+    The following functions are needed only in some special applications.
+*/
+
+/*   
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+                                     int  level,
+                                     int  method,
+                                     int  windowBits,
+                                     int  memLevel,
+                                     int  strategy));
+
+     This is another version of deflateInit with more compression options. The
+   fields next_in, zalloc, zfree and opaque must be initialized before by
+   the caller.
+
+     The method parameter is the compression method. It must be Z_DEFLATED in
+   this version of the library.
+
+     The windowBits parameter is the base two logarithm of the window size
+   (the size of the history buffer).  It should be in the range 8..15 for this
+   version of the library. Larger values of this parameter result in better
+   compression at the expense of memory usage. The default value is 15 if
+   deflateInit is used instead.
+
+     The memLevel parameter specifies how much memory should be allocated
+   for the internal compression state. memLevel=1 uses minimum memory but
+   is slow and reduces compression ratio; memLevel=9 uses maximum memory
+   for optimal speed. The default value is 8. See zconf.h for total memory
+   usage as a function of windowBits and memLevel.
+
+     The strategy parameter is used to tune the compression algorithm. Use the
+   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+   filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
+   string match).  Filtered data consists mostly of small values with a
+   somewhat random distribution. In this case, the compression algorithm is
+   tuned to compress them better. The effect of Z_FILTERED is to force more
+   Huffman coding and less string matching; it is somewhat intermediate
+   between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
+   the compression ratio but not the correctness of the compressed output even
+   if it is not set appropriately.
+
+      deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+   method). msg is set to null if there is no error message.  deflateInit2 does
+   not perform any compression: this will be done by deflate().
+*/
+                            
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the compression dictionary from the given byte sequence
+   without producing any compressed output. This function must be called
+   immediately after deflateInit, deflateInit2 or deflateReset, before any
+   call of deflate. The compressor and decompressor must use exactly the same
+   dictionary (see inflateSetDictionary).
+
+     The dictionary should consist of strings (byte sequences) that are likely
+   to be encountered later in the data to be compressed, with the most commonly
+   used strings preferably put towards the end of the dictionary. Using a
+   dictionary is most useful when the data to be compressed is short and can be
+   predicted with good accuracy; the data can then be compressed better than
+   with the default empty dictionary.
+
+     Depending on the size of the compression data structures selected by
+   deflateInit or deflateInit2, a part of the dictionary may in effect be
+   discarded, for example if the dictionary is larger than the window size in
+   deflate or deflate2. Thus the strings most likely to be useful should be
+   put at the end of the dictionary, not at the front.
+
+     Upon return of this function, strm->adler is set to the Adler32 value
+   of the dictionary; the decompressor may later use this value to determine
+   which dictionary has been used by the compressor. (The Adler32 value
+   applies to the whole dictionary even if only a subset of the dictionary is
+   actually used by the compressor.)
+
+     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+   parameter is invalid (such as NULL dictionary) or the stream state is
+   inconsistent (for example if deflate has already been called for this stream
+   or if the compression method is bsort). deflateSetDictionary does not
+   perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when several compression strategies will be
+   tried, for example when there are several ways of pre-processing the input
+   data with a filter. The streams that will be discarded should then be freed
+   by calling deflateEnd.  Note that deflateCopy duplicates the internal
+   compression state which can be quite large, so this strategy is slow and
+   can consume lots of memory.
+
+     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being NULL). msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to deflateEnd followed by deflateInit,
+   but does not free and reallocate all the internal compression state.
+   The stream will keep the same compression level and any other attributes
+   that may have been set by deflateInit2.
+
+      deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+				      int level,
+				      int strategy));
+/*
+     Dynamically update the compression level and compression strategy.  The
+   interpretation of level and strategy is as in deflateInit2.  This can be
+   used to switch between compression and straight copy of the input data, or
+   to switch to a different kind of input data requiring a different
+   strategy. If the compression level is changed, the input available so far
+   is compressed with the old level (and may be flushed); the new level will
+   take effect only at the next call of deflate().
+
+     Before the call of deflateParams, the stream state must be set as for
+   a call of deflate(), since the currently available input may have to
+   be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+     deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+   stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+   if strm->avail_out was zero.
+*/
+
+/*   
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+                                     int  windowBits));
+
+     This is another version of inflateInit with an extra parameter. The
+   fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+   before by the caller.
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library. The default value is 15 if inflateInit is used
+   instead. If a compressed stream with a larger window size is given as
+   input, inflate() will return with the error code Z_DATA_ERROR instead of
+   trying to allocate a larger window.
+
+      inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
+   memLevel). msg is set to null if there is no error message.  inflateInit2
+   does not perform any decompression apart from reading the zlib header if
+   present: this will be done by inflate(). (So next_in and avail_in may be
+   modified, but next_out and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the decompression dictionary from the given uncompressed byte
+   sequence. This function must be called immediately after a call of inflate
+   if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
+   can be determined from the Adler32 value returned by this call of
+   inflate. The compressor and decompressor must use exactly the same
+   dictionary (see deflateSetDictionary).
+
+     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+   parameter is invalid (such as NULL dictionary) or the stream state is
+   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+   expected one (incorrect Adler32 value). inflateSetDictionary does not
+   perform any decompression: this will be done by subsequent calls of
+   inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/* 
+    Skips invalid compressed data until a full flush point (see above the
+  description of deflate with Z_FULL_FLUSH) can be found, or until all
+  available input is skipped. No output is provided.
+
+    inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+  if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+  or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+  case, the application may save the current current value of total_in which
+  indicates where valid compressed data was found. In the error case, the
+  application may repeatedly call inflateSync, providing more input each time,
+  until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to inflateEnd followed by inflateInit,
+   but does not free and reallocate all the internal decompression state.
+   The stream will keep attributes that may have been set by inflateInit2.
+
+      inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+
+                        /* utility functions */
+
+/*
+     The following utility functions are implemented on top of the
+   basic stream-oriented functions. To simplify the interface, some
+   default options are assumed (compression level and memory usage,
+   standard memory allocation functions). The source code of these
+   utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,
+                                 const Bytef *source, uLong sourceLen));
+/*
+     Compresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be at least 0.1% larger than
+   sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+   compressed buffer.
+     This function can be used to compress a whole file at once if the
+   input file is mmap'ed.
+     compress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,
+                                  const Bytef *source, uLong sourceLen,
+                                  int level));
+/*
+     Compresses the source buffer into the destination buffer. The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer. Upon entry, destLen is the total size of the
+   destination buffer, which must be at least 0.1% larger than sourceLen plus
+   12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
+                                   const Bytef *source, uLong sourceLen));
+/*
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be large enough to hold the
+   entire uncompressed data. (The size of the uncompressed data must have
+   been saved previously by the compressor and transmitted to the decompressor
+   by some mechanism outside the scope of this compression library.)
+   Upon exit, destLen is the actual size of the compressed buffer.
+     This function can be used to decompress a whole file at once if the
+   input file is mmap'ed.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen  OF((const char *path, const char *mode));
+/*
+     Opens a gzip (.gz) file for reading or writing. The mode parameter
+   is as in fopen ("rb" or "wb") but can also include a compression level
+   ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+   Huffman only compression as in "wb1h". (See the description
+   of deflateInit2 for more information about the strategy parameter.)
+
+     gzopen can be used to read a file which is not in gzip format; in this
+   case gzread will directly read from the file without decompression.
+
+     gzopen returns NULL if the file could not be opened or if there was
+   insufficient memory to allocate the (de)compression state; errno
+   can be checked to distinguish the two cases (if errno is zero, the
+   zlib error is Z_MEM_ERROR).  */
+
+ZEXTERN gzFile ZEXPORT gzdopen  OF((int fd, const char *mode));
+/*
+     gzdopen() associates a gzFile with the file descriptor fd.  File
+   descriptors are obtained from calls like open, dup, creat, pipe or
+   fileno (in the file has been previously opened with fopen).
+   The mode parameter is as in gzopen.
+     The next call of gzclose on the returned gzFile will also close the
+   file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+   descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+     gzdopen returns NULL if there was insufficient memory to allocate
+   the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+     Dynamically update the compression level or strategy. See the description
+   of deflateInit2 for the meaning of these parameters.
+     gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+   opened for writing.
+*/
+
+ZEXTERN int ZEXPORT    gzread  OF((gzFile file, voidp buf, unsigned len));
+/*
+     Reads the given number of uncompressed bytes from the compressed file.
+   If the input file was not in gzip format, gzread copies the given number
+   of bytes into the buffer.
+     gzread returns the number of uncompressed bytes actually read (0 for
+   end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT    gzwrite OF((gzFile file, 
+				   const voidp buf, unsigned len));
+/*
+     Writes the given number of uncompressed bytes into the compressed file.
+   gzwrite returns the number of uncompressed bytes actually written
+   (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA   gzprintf OF((gzFile file, const char *format, ...));
+/*
+     Converts, formats, and writes the args to the compressed file under
+   control of the format string, as in fprintf. gzprintf returns the number of
+   uncompressed bytes actually written (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+      Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+      gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+      Reads bytes from the compressed file until len-1 characters are read, or
+   a newline character is read and transferred to buf, or an end-of-file
+   condition is encountered.  The string is then terminated with a null
+   character.
+      gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT    gzputc OF((gzFile file, int c));
+/*
+      Writes c, converted to an unsigned char, into the compressed file.
+   gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT    gzgetc OF((gzFile file));
+/*
+      Reads one byte from the compressed file. gzgetc returns this byte
+   or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT    gzflush OF((gzFile file, int flush));
+/*
+     Flushes all pending output into the compressed file. The parameter
+   flush is as in the deflate() function. The return value is the zlib
+   error number (see function gzerror below). gzflush returns Z_OK if
+   the flush parameter is Z_FINISH and all output could be flushed.
+     gzflush should be called only when strictly necessary because it can
+   degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT    gzseek OF((gzFile file,
+				      z_off_t offset, int whence));
+/* 
+      Sets the starting position for the next gzread or gzwrite on the
+   given compressed file. The offset represents a number of bytes in the
+   uncompressed data stream. The whence parameter is defined as in lseek(2);
+   the value SEEK_END is not supported.
+     If the file is opened for reading, this function is emulated but can be
+   extremely slow. If the file is opened for writing, only forward seeks are
+   supported; gzseek then compresses a sequence of zeroes up to the new
+   starting position.
+
+      gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error, in
+   particular if the file is opened for writing and the new starting position
+   would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT    gzrewind OF((gzFile file));
+/*
+     Rewinds the given file. This function is supported only for reading.
+
+   gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
+/*
+     Returns the starting position for the next gzread or gzwrite on the
+   given compressed file. This position represents a number of bytes in the
+   uncompressed data stream.
+
+   gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+     Returns 1 when EOF has previously been detected reading the given
+   input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT    gzclose OF((gzFile file));
+/*
+     Flushes all pending output if necessary, closes the compressed file
+   and deallocates all the (de)compression state. The return value is the zlib
+   error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+     Returns the error message for the last error which occurred on the
+   given compressed file. errnum is set to zlib error number. If an
+   error occurred in the file system and not in the compression library,
+   errnum is set to Z_ERRNO and the application may consult errno
+   to get the exact error code.
+*/
+
+                        /* checksum functions */
+
+/*
+     These functions are not related to compression but are exported
+   anyway because they might be useful in applications using the
+   compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum. If buf is NULL, this function returns
+   the required initial value for the checksum.
+   An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+   much faster. Usage example:
+
+     uLong adler = adler32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       adler = adler32(adler, buffer, length);
+     }
+     if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));
+/*
+     Update a running crc with the bytes buf[0..len-1] and return the updated
+   crc. If buf is NULL, this function returns the required initial value
+   for the crc. Pre- and post-conditioning (one's complement) is performed
+   within this function so it shouldn't be done by the application.
+   Usage example:
+
+     uLong crc = crc32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       crc = crc32(crc, buffer, length);
+     }
+     if (crc != original_crc) error();
+*/
+
+
+                        /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,
+                                      int windowBits, int memLevel,
+                                      int strategy, const char *version,
+                                      int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
+                                      const char *version, int stream_size));
+#define deflateInit(strm, level) \
+        deflateInit_((strm), (level),       ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+        inflateInit_((strm),                ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+        deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+                      (strategy),           ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+        inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL)
+    struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char   * ZEXPORT zError           OF((int err));
+ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table    OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZLIB_H */
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/zlibvc.def
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/zlibvc.def	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/zlibvc.def	(revision 38)
@@ -0,0 +1,74 @@
+LIBRARY		"zlib"
+
+DESCRIPTION	'"""zlib data compression library"""'
+
+
+VERSION		1.11
+
+
+HEAPSIZE	1048576,8192
+
+EXPORTS
+    adler32                        @1
+    compress                       @2
+    crc32                          @3
+    deflate                        @4
+    deflateCopy                    @5
+    deflateEnd                     @6
+    deflateInit2_                  @7
+    deflateInit_                   @8
+    deflateParams                  @9
+    deflateReset                   @10
+    deflateSetDictionary           @11
+    gzclose                        @12
+    gzdopen                        @13
+    gzerror                        @14
+    gzflush                        @15
+    gzopen                         @16
+    gzread                         @17
+    gzwrite                        @18
+    inflate                        @19
+    inflateEnd                     @20
+    inflateInit2_                  @21
+    inflateInit_                   @22
+    inflateReset                   @23
+    inflateSetDictionary           @24
+    inflateSync                    @25
+    uncompress                     @26
+    zlibVersion                    @27
+    gzprintf                       @28
+    gzputc                         @29
+    gzgetc                         @30
+    gzseek                         @31
+    gzrewind                       @32
+    gztell                         @33
+    gzeof                          @34
+    gzsetparams                    @35
+    zError                         @36
+    inflateSyncPoint               @37
+    get_crc_table                  @38
+    compress2                      @39
+    gzputs                         @40
+    gzgets                         @41
+
+	unzOpen                       @61
+	unzClose                      @62
+	unzGetGlobalInfo              @63
+	unzGetCurrentFileInfo         @64
+	unzGoToFirstFile              @65
+	unzGoToNextFile               @66
+	unzOpenCurrentFile            @67
+	unzReadCurrentFile            @68
+	unztell                       @70
+	unzeof                        @71
+	unzCloseCurrentFile           @72
+	unzGetGlobalComment           @73
+	unzStringFileNameCompare      @74
+	unzLocateFile                 @75
+	unzGetLocalExtrafield         @76
+
+	zipOpen                       @80
+	zipOpenNewFileInZip           @81
+	zipWriteInFileInZip           @82
+	zipCloseFileInZip             @83
+	zipClose                      @84
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/zlibvc.dsp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/zlibvc.dsp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/zlibvc.dsp	(revision 38)
@@ -0,0 +1,651 @@
+# Microsoft Developer Studio Project File - Name="zlibvc" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+# TARGTYPE "Win32 (ALPHA) Dynamic-Link Library" 0x0602
+
+CFG=zlibvc - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "zlibvc.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "zlibvc.mak" CFG="zlibvc - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "zlibvc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "zlibvc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "zlibvc - Win32 ReleaseAxp" (based on\
+ "Win32 (ALPHA) Dynamic-Link Library")
+!MESSAGE "zlibvc - Win32 ReleaseWithoutAsm" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "zlibvc - Win32 ReleaseWithoutCrtdll" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\Release"
+# PROP Intermediate_Dir ".\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+CPP=cl.exe
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_WINDLL" /D "_WIN32" /D "BUILD_ZLIBDLL" /D "ZLIB_DLL" /D "DYNAMIC_CRC_TABLE" /D "ASMV" /FAcs /FR /FD /c
+# SUBTRACT CPP /YX
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x40c /d "NDEBUG"
+# ADD RSC /l 0x40c /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 gvmat32.obj kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib crtdll.lib /nologo /subsystem:windows /dll /map /machine:I386 /nodefaultlib /out:".\Release\zlib.dll"
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\Debug"
+# PROP Intermediate_Dir ".\Debug"
+# PROP Target_Dir ""
+CPP=cl.exe
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_WINDLL" /D "_WIN32" /D "BUILD_ZLIBDLL" /D "ZLIB_DLL" /FD /c
+# SUBTRACT CPP /YX
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x40c /d "_DEBUG"
+# ADD RSC /l 0x40c /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:".\Debug\zlib.dll"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "zlibvc__"
+# PROP BASE Intermediate_Dir "zlibvc__"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "zlibvc__"
+# PROP Intermediate_Dir "zlibvc__"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+CPP=cl.exe
+# ADD BASE CPP /nologo /MT /Gt0 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WIN32" /D "BUILD_ZLIBDLL" /D "ZLIB_DLL" /D "DYNAMIC_CRC_TABLE" /FAcs /FR /YX /FD /c
+# ADD CPP /nologo /MT /Gt0 /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WIN32" /D "BUILD_ZLIBDLL" /D "ZLIB_DLL" /D "DYNAMIC_CRC_TABLE" /FAcs /FR /FD /c
+# SUBTRACT CPP /YX
+RSC=rc.exe
+# ADD BASE RSC /l 0x40c /d "NDEBUG"
+# ADD RSC /l 0x40c /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 crtdll.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /map /machine:ALPHA /nodefaultlib /out:".\Release\zlib.dll"
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 crtdll.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /map /machine:ALPHA /nodefaultlib /out:"zlibvc__\zlib.dll"
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "zlibvc_0"
+# PROP BASE Intermediate_Dir "zlibvc_0"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "zlibvc_0"
+# PROP Intermediate_Dir "zlibvc_0"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+CPP=cl.exe
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_WINDLL" /D "_WIN32" /D "BUILD_ZLIBDLL" /D "ZLIB_DLL" /D "DYNAMIC_CRC_TABLE" /FAcs /FR /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_WINDLL" /D "_WIN32" /D "BUILD_ZLIBDLL" /D "ZLIB_DLL" /D "DYNAMIC_CRC_TABLE" /FAcs /FR /FD /c
+# SUBTRACT CPP /YX
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x40c /d "NDEBUG"
+# ADD RSC /l 0x40c /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib crtdll.lib /nologo /subsystem:windows /dll /map /machine:I386 /nodefaultlib /out:".\Release\zlib.dll"
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib crtdll.lib /nologo /subsystem:windows /dll /map /machine:I386 /nodefaultlib /out:".\zlibvc_0\zlib.dll"
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "zlibvc_1"
+# PROP BASE Intermediate_Dir "zlibvc_1"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "zlibvc_1"
+# PROP Intermediate_Dir "zlibvc_1"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+CPP=cl.exe
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_WINDLL" /D "_WIN32" /D "BUILD_ZLIBDLL" /D "ZLIB_DLL" /D "DYNAMIC_CRC_TABLE" /D "ASMV" /FAcs /FR /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_WINDLL" /D "_WIN32" /D "BUILD_ZLIBDLL" /D "ZLIB_DLL" /D "DYNAMIC_CRC_TABLE" /D "ASMV" /FAcs /FR /FD /c
+# SUBTRACT CPP /YX
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+# ADD BASE RSC /l 0x40c /d "NDEBUG"
+# ADD RSC /l 0x40c /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 gvmat32.obj kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib crtdll.lib /nologo /subsystem:windows /dll /map /machine:I386 /nodefaultlib /out:".\Release\zlib.dll"
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 gvmat32.obj kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib crtdll.lib /nologo /subsystem:windows /dll /map /machine:I386 /nodefaultlib /out:".\zlibvc_1\zlib.dll"
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF 
+
+# Begin Target
+
+# Name "zlibvc - Win32 Release"
+# Name "zlibvc - Win32 Debug"
+# Name "zlibvc - Win32 ReleaseAxp"
+# Name "zlibvc - Win32 ReleaseWithoutAsm"
+# Name "zlibvc - Win32 ReleaseWithoutCrtdll"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\adler32.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_ADLER=\
+	".\zconf.h"\
+	".\zlib.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\compress.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_COMPR=\
+	".\zconf.h"\
+	".\zlib.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\crc32.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_CRC32=\
+	".\zconf.h"\
+	".\zlib.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\deflate.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_DEFLA=\
+	".\deflate.h"\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\gvmat32c.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\gzio.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_GZIO_=\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\infblock.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_INFBL=\
+	".\infblock.h"\
+	".\infcodes.h"\
+	".\inftrees.h"\
+	".\infutil.h"\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\infcodes.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_INFCO=\
+	".\infblock.h"\
+	".\infcodes.h"\
+	".\inffast.h"\
+	".\inftrees.h"\
+	".\infutil.h"\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffast.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_INFFA=\
+	".\infblock.h"\
+	".\infcodes.h"\
+	".\inffast.h"\
+	".\inftrees.h"\
+	".\infutil.h"\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\inflate.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_INFLA=\
+	".\infblock.h"\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\inftrees.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_INFTR=\
+	".\inftrees.h"\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\infutil.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_INFUT=\
+	".\infblock.h"\
+	".\infcodes.h"\
+	".\inftrees.h"\
+	".\infutil.h"\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\trees.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_TREES=\
+	".\deflate.h"\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\uncompr.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_UNCOM=\
+	".\zconf.h"\
+	".\zlib.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\unzip.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\zip.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\zlib.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\zlibvc.def
+# End Source File
+# Begin Source File
+
+SOURCE=.\zutil.c
+
+!IF  "$(CFG)" == "zlibvc - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 Debug"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseAxp"
+
+DEP_CPP_ZUTIL=\
+	".\zconf.h"\
+	".\zlib.h"\
+	".\zutil.h"\
+	
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutAsm"
+
+!ELSEIF  "$(CFG)" == "zlibvc - Win32 ReleaseWithoutCrtdll"
+
+!ENDIF 
+
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\deflate.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\infblock.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\infcodes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inffast.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inftrees.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\infutil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zconf.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zutil.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: psi/tags/0.10-os2-b1/src/tools/zip/minizip/zlibvc.dsw
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/minizip/zlibvc.dsw	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/minizip/zlibvc.dsw	(revision 38)
@@ -0,0 +1,41 @@
+Microsoft Developer Studio Workspace File, Format Version 5.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "zlibstat"=.\zlibstat.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "zlibvc"=.\zlibvc.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
Index: psi/tags/0.10-os2-b1/src/tools/zip/zip.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/zip.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/zip.cpp	(revision 38)
@@ -0,0 +1,160 @@
+/*
+ * zip.cpp - zip/unzip files
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"zip.h"
+
+#include<qcstring.h>
+#include<qstringlist.h>
+#include<qfile.h>
+
+#include"minizip/unzip.h"
+
+class UnZipPrivate
+{
+public:
+	UnZipPrivate() {}
+
+	QString name;
+	unzFile uf;
+	QStringList listing;
+};
+
+UnZip::UnZip(const QString &name)
+{
+	d = new UnZipPrivate;
+	d->uf = 0;
+	d->name = name;
+}
+
+UnZip::~UnZip()
+{
+	close();
+	delete d;
+}
+
+void UnZip::setName(const QString &name)
+{
+	d->name = name;
+}
+
+const QString & UnZip::name() const
+{
+	return d->name;
+}
+
+bool UnZip::open()
+{
+	close();
+
+	d->uf = unzOpen(QFile::encodeName(d->name).data());
+	if(!d->uf)
+		return false;
+
+	return getList();
+}
+
+void UnZip::close()
+{
+	if(d->uf) {
+		unzClose(d->uf);
+		d->uf = 0;
+	}
+
+	d->listing.clear();
+}
+
+const QStringList & UnZip::list() const
+{
+	return d->listing;
+}
+
+bool UnZip::getList()
+{
+	unz_global_info gi;
+	int err = unzGetGlobalInfo(d->uf, &gi);
+	if(err != UNZ_OK)
+		return false;
+
+	QStringList l;
+	for(int n = 0; n < (int)gi.number_entry; ++n) {
+		char filename_inzip[256];
+		unz_file_info file_info;
+		int err = unzGetCurrentFileInfo(d->uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
+		if(err != UNZ_OK)
+			return false;
+
+		l += filename_inzip;
+
+		if((n+1) < (int)gi.number_entry) {
+			err = unzGoToNextFile(d->uf);
+			if(err != UNZ_OK)
+				return false;
+		}
+	}
+
+	d->listing = l;
+
+	return true;
+}
+
+bool UnZip::readFile(const QString &fname, QByteArray *buf, int max)
+{
+	int err = unzLocateFile(d->uf, QFile::encodeName(fname).data(), 0);
+	if(err != UNZ_OK)
+		return false;
+
+	char filename_inzip[256];
+	unz_file_info file_info;
+	err = unzGetCurrentFileInfo(d->uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
+	if(err != UNZ_OK)
+		return false;
+
+	err = unzOpenCurrentFile(d->uf);
+	if(err != UNZ_OK)
+		return false;
+
+	QByteArray a(0);
+	QByteArray chunk(16384);
+	while(1) {
+		err = unzReadCurrentFile(d->uf, chunk.data(), chunk.size());
+		if(err < 0) {
+			unzCloseCurrentFile(d->uf);
+			return false;
+		}
+		if(err == 0)
+			break;
+
+		int oldsize = a.size();
+		if(max > 0 && oldsize + err > max)
+			err = max - oldsize;
+		a.resize(oldsize + err);
+		memcpy(a.data() + oldsize, chunk.data(), err);
+
+		if(max > 0 && (int)a.size() >= max)
+			break;
+	}
+
+	err = unzCloseCurrentFile(d->uf);
+	if(err != UNZ_OK)
+		return false;
+
+	*buf = a;
+	return true;
+}
Index: psi/tags/0.10-os2-b1/src/tools/zip/zip.h
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/zip.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/zip.h	(revision 38)
@@ -0,0 +1,50 @@
+/*
+ * zip.h - zip/unzip files
+ * Copyright (C) 2003  Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef CS_ZIP_H
+#define CS_ZIP_H
+
+#include<qstring.h>
+#include<qcstring.h>
+
+class QStringList;
+
+class UnZip
+{
+public:
+	UnZip(const QString &fname="");
+	~UnZip();
+
+	void setName(const QString &);
+	const QString & name() const;
+
+	bool open();
+	void close();
+
+	const QStringList & list() const;
+	bool readFile(const QString &, QByteArray *, int max=0);
+
+private:
+	class UnZipPrivate *d;
+
+	bool getList();
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/tools/zip/zip.pri
===================================================================
--- psi/tags/0.10-os2-b1/src/tools/zip/zip.pri	(revision 38)
+++ psi/tags/0.10-os2-b1/src/tools/zip/zip.pri	(revision 38)
@@ -0,0 +1,20 @@
+zip {
+	HEADERS += \
+		$$ZIP_CPP/zip.h
+
+	SOURCES += \
+		$$ZIP_CPP/zip.cpp \
+		$$ZIP_CPP/minizip/unzip.c
+
+	win32: {
+		INCLUDEPATH += $$ZIP_CPP/minizip/win32
+		LIBS += $$ZIP_CPP/minizip/win32/libz.a
+	}
+	os2: {
+		INCLUDEPATH += $$ZIP_CPP/minizip/win32
+	}
+	mac: {
+		QMAKE_LFLAGS += -lz
+	}
+}
+
Index: psi/tags/0.10-os2-b1/src/ui_about.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_about.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_about.ui	(revision 38)
@@ -0,0 +1,308 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AboutDlg</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>AboutDlg</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>504</width>
+            <height>320</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>About Psi</string>
+    </property>
+    <grid>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="IconLabel" row="0" column="0">
+            <property name="name">
+                <cstring>lb_icon</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>NoFrame</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Plain</enum>
+            </property>
+            <property name="iconName" stdset="0">
+                <string>psi/psi48</string>
+            </property>
+        </widget>
+        <widget class="QLabel" row="0" column="1">
+            <property name="name">
+                <cstring>lb_name</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>7</hsizetype>
+                    <vsizetype>5</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="text">
+                <string>&lt;h3&gt;&lt;b&gt;%1 v%2&lt;/b&gt;&lt;/h3&gt;</string>
+            </property>
+            <property name="textFormat">
+                <enum>RichText</enum>
+            </property>
+        </widget>
+        <widget class="QTabWidget" row="1" column="0" rowspan="1" colspan="2">
+            <property name="name">
+                <cstring>tw_tabs</cstring>
+            </property>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>About</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <spacer>
+                        <property name="name">
+                            <cstring>spacer70</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>20</width>
+                                <height>40</height>
+                            </size>
+                        </property>
+                    </spacer>
+                    <widget class="QLabel">
+                        <property name="name">
+                            <cstring>lb_about</cstring>
+                        </property>
+                        <property name="sizePolicy">
+                            <sizepolicy>
+                                <hsizetype>7</hsizetype>
+                                <vsizetype>7</vsizetype>
+                                <horstretch>0</horstretch>
+                                <verstretch>0</verstretch>
+                            </sizepolicy>
+                        </property>
+                        <property name="text">
+                            <string>A cross-platform Jabber client designed for the Jabber power user.&lt;br&gt;
+&lt;br&gt;
+Copyright (C) 2001 - 2005 by Justin Karneges&lt;br&gt;
+</string>
+                        </property>
+                        <property name="textFormat">
+                            <enum>RichText</enum>
+                        </property>
+                    </widget>
+                    <widget class="URLLabel">
+                        <property name="name">
+                            <cstring>uRLLabel1</cstring>
+                        </property>
+                        <property name="url">
+                            <string>http://psi-im.org</string>
+                        </property>
+                        <property name="title">
+                            <string>&lt;div align="center"&gt;http://psi-im.org&lt;/div&gt;</string>
+                        </property>
+                    </widget>
+                    <spacer>
+                        <property name="name">
+                            <cstring>spacer71</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>20</width>
+                                <height>40</height>
+                            </size>
+                        </property>
+                    </spacer>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Authors</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="PsiTextView">
+                        <property name="name">
+                            <cstring>te_authors</cstring>
+                        </property>
+                    </widget>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Thanks To</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="PsiTextView">
+                        <property name="name">
+                            <cstring>te_thanks</cstring>
+                        </property>
+                    </widget>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Translation</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="PsiTextView">
+                        <property name="name">
+                            <cstring>te_translation</cstring>
+                        </property>
+                        <property name="sizePolicy">
+                            <sizepolicy>
+                                <hsizetype>5</hsizetype>
+                                <vsizetype>7</vsizetype>
+                                <horstretch>0</horstretch>
+                                <verstretch>0</verstretch>
+                            </sizepolicy>
+                        </property>
+                        <property name="text">
+                            <string>I. M. Anonymous &lt;note text="replace with your real name"&gt;&lt;br&gt;
+&amp;nbsp;&amp;nbsp;&lt;a href="mailto:noone@nowhere.com"&gt;nowhere@nowhere.com&lt;/a&gt; &lt;note text="replace with your email"&gt;&lt;br&gt;
+&amp;nbsp;&amp;nbsp;&lt;a href="http://blah.com"&gt;http://blah.com&lt;/a&gt; &lt;note text="write your site URL here"&gt;&lt;br&gt;
+&amp;nbsp;&amp;nbsp;Jabber: &lt;a href="jabber:blah@jabber.org"&gt;blah@jabber.org &lt;note text="write your JID here"&gt;&lt;/a&gt;&lt;br&gt;
+&amp;nbsp;&amp;nbsp;Translator &lt;note text="write description for the work, you've done"&gt;&lt;br&gt;
+&lt;br&gt;
+&lt;note text="you can add as many people here as you want"&gt;
+
+&lt;note text="and translate following text"&gt;
+Join the translation team today! Go to &lt;a href="http://psi.affinix.com/forums/index.php?act=SF&amp;f=3"&gt;http://psi.affinix.com/forums/index.php?act=SF&amp;f=3&lt;/a&gt; for further details!
+</string>
+                            <comment>This page is a special place for translators, to write some info about them. Write information about your translation team, some hints are in &lt;note&gt; tags to help you. Keep up the good work :-).</comment>
+                        </property>
+                    </widget>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>License</string>
+                </attribute>
+                <hbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QTextEdit">
+                        <property name="name">
+                            <cstring>te_license</cstring>
+                        </property>
+                        <property name="readOnly">
+                            <bool>true</bool>
+                        </property>
+                    </widget>
+                </hbox>
+            </widget>
+        </widget>
+        <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="2">
+            <property name="name">
+                <cstring>layout26</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer2</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>418</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </grid>
+</widget>
+<connections>
+    <connection>
+        <sender>pb_close</sender>
+        <signal>clicked()</signal>
+        <receiver>AboutDlg</receiver>
+        <slot>accept()</slot>
+    </connection>
+</connections>
+<includes>
+    <include location="local" impldecl="in implementation">common.h</include>
+    <include location="global" impldecl="in implementation">qfile.h</include>
+    <include location="global" impldecl="in implementation">qtextstream.h</include>
+    <include location="global" impldecl="in implementation">qtranslator.h</include>
+    <include location="global" impldecl="in implementation">qapplication.h</include>
+    <include location="local" impldecl="in implementation">ui_about.ui.h</include>
+</includes>
+<slots>
+    <slot access="protected">init()</slot>
+</slots>
+<functions>
+    <function returnType="QString">loadText( const QString &amp; fileName )</function>
+    <function access="private" returnType="QString">details( QString name, QString email, QString jabber, QString www, QString desc )</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>fancylabel.h</includehint>
+    <includehint>psitextview.h</includehint>
+    <includehint>psitextview.h</includehint>
+    <includehint>psitextview.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_about.ui.h
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_about.ui.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_about.ui.h	(revision 38)
@@ -0,0 +1,111 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+void AboutDlg::init()
+{
+	lb_name->setText ( lb_name->text().arg(PROG_NAME).arg(PROG_VERSION) );
+
+	te_license->setText ( loadText(g.pathBase + "/COPYING") );
+
+	QString lang_name = qApp->translate( "@default", "language_name" );
+	if ( lang_name == "language_name" ) // remove the translation tab, if no translation is used
+		tw_tabs->removePage ( tw_tabs->page(3) );
+
+	// fill in Authors tab...
+	QString authors;
+	authors += details(QString::fromUtf8("Justin Karneges"),
+			   "justin@affinix.com", "", "",
+			   tr("Founder and Original Author"));
+	authors += details(QString::fromUtf8("Kevin Smith"),
+			   "aboutpsi@kismith.co.uk", "", "",
+			   tr("Project Lead/Maintainer"));
+	authors += details(QString::fromUtf8("Michail Pishchagin"),
+			   "mblsha@users.sourceforge.net", "", "",
+			   tr("Lead Developer"));
+	authors += details(QString::fromUtf8("Remko TronÃ§on"),
+			   "remko@psi-im.org", "", "",
+			   tr("Developer"));
+	authors += details(QString::fromUtf8("Akito Nozaki"),
+			   "anpluto@usa.net", "", "",
+			   tr("Miscellaneous Developer"));
+	te_authors->setText( authors );
+
+	// fill in Thanks To tab...
+	QString thanks;
+	thanks += details(QString::fromUtf8("Jan Niehusmann"),
+			  "jan@gondor.com", "", "",
+			  tr("Build setup, miscellaneous assistance"));
+	thanks += details(QString::fromUtf8("Everaldo Coelho"),
+			  "", "", "http://www.everaldo.com",
+			  tr("Many icons are from his Crystal icon theme"));
+	thanks += details(QString::fromUtf8("Jason Kim"),
+			  "", "", "",
+			  tr("Graphics"));
+	thanks += details(QString::fromUtf8("Hideaki Omuro"),
+			  "", "", "",
+			  tr("Graphics"));
+	thanks += details(QString::fromUtf8("Bill Myers"),
+			  "", "", "",
+			  tr("Original Mac Port"));
+	thanks += details(QString::fromUtf8("Eric Smith (Tarkvara Design, Inc.)"),
+			 "eric@tarkvara.org", "", "",
+			 tr("Mac OS X Port"));
+	thanks += details(QString::fromUtf8("Tony Collins"),
+	 		 "", "", "",
+	 		 tr("Original End User Documentation"));
+	thanks += details(QString::fromUtf8("Hal Rottenberg"),
+			  "", "", "",
+			 tr("Webmaster, Marketing"));
+	thanks += details(QString::fromUtf8("Mircea Bardac"),
+			 "", "", "",
+			 tr("Bug Tracker Management"));
+	thanks += details(QString::fromUtf8("Jacek Tomasiak"),
+			 "", "", "",
+			 tr("Patches"));
+					  
+	//thanks += tr("Thanks to many others.\n"
+	//	     "The above list only reflects the contributors I managed to keep track of.\n"
+	//	     "If you're not included but you think that you must be in the list, contact the developers.");
+	te_thanks->setText( thanks );
+}
+
+QString AboutDlg::loadText( const QString & fileName )
+{
+	QString text;
+
+	QFile f(fileName);
+	if(f.open(IO_ReadOnly)) {
+		QTextStream t(&f);
+		while(!t.eof())
+			text += t.readLine() + '\n';
+		f.close();
+	}
+
+	return text;
+}
+
+
+QString AboutDlg::details( QString name, QString email, QString jabber, QString www, QString desc )
+{
+	QString ret;
+	const QString nbsp = "&nbsp;&nbsp;";
+	ret += name + "<br>\n";
+	if ( !email.isEmpty() )
+		ret += nbsp + "E-mail: " + "<a href=\"mailto:" + email + "\">" + email + "</a><br>\n";
+	if ( !jabber.isEmpty() )
+		ret += nbsp + "Jabber: " + "<a href=\"jabber:" + jabber + "\">" + jabber + "</a><br>\n";
+	if ( !www.isEmpty() )
+		ret += nbsp + "WWW: " + "<a href=\"" + www + "\">" + www + "</a><br>\n";
+	if ( !desc.isEmpty() )
+		ret += nbsp + desc + "<br>\n";
+	ret += "<br>\n";
+
+	return ret;
+}
Index: psi/tags/0.10-os2-b1/src/ui_accountadd.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_accountadd.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_accountadd.ui	(revision 38)
@@ -0,0 +1,200 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AccountAddUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>AccountAddUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>465</width>
+            <height>252</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Add Account</string>
+    </property>
+    <hbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QTextEdit">
+            <property name="name">
+                <cstring>TextEdit1</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>7</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="minimumSize">
+                <size>
+                    <width>200</width>
+                    <height>0</height>
+                </size>
+            </property>
+            <property name="maximumSize">
+                <size>
+                    <width>200</width>
+                    <height>32767</height>
+                </size>
+            </property>
+            <property name="text">
+                <string>&lt;qt&gt;Please choose a friendly &lt;b&gt;Name&lt;/b&gt; that Psi can use to refer to this account.&lt;br&gt;
+&lt;br&gt;
+Click the &lt;b&gt;Register New Account&lt;/b&gt; checkbox if you want Psi to try and create an account for you on a remote server.  If you are adding an existing Jabber account then leave this box unchecked.</string>
+            </property>
+            <property name="readOnly">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout7</cstring>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>Layout21</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>0</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>TextLabel1</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Name:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit">
+                            <property name="name">
+                                <cstring>le_name</cstring>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_reg</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Register new account</string>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer20</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Vertical</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>20</width>
+                            <height>130</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="Line">
+                    <property name="name">
+                        <cstring>Line3</cstring>
+                    </property>
+                    <property name="frameShape">
+                        <enum>HLine</enum>
+                    </property>
+                    <property name="frameShadow">
+                        <enum>Sunken</enum>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout6</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer19</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Horizontal</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>118</width>
+                                    <height>20</height>
+                                </size>
+                            </property>
+                        </spacer>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_close</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Close</string>
+                            </property>
+                        </widget>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_add</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Add</string>
+                            </property>
+                            <property name="accel">
+                                <string>Alt+A</string>
+                            </property>
+                            <property name="default">
+                                <bool>true</bool>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/addContact</string>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+            </vbox>
+        </widget>
+    </hbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_accountmanage.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_accountmanage.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_accountmanage.ui	(revision 38)
@@ -0,0 +1,199 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AccountManageUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>AccountManageUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>481</width>
+            <height>282</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Jabber Accounts</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout14</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QListView">
+                    <column>
+                        <property name="text">
+                            <string>Name</string>
+                        </property>
+                        <property name="clickable">
+                            <bool>true</bool>
+                        </property>
+                        <property name="resizable">
+                            <bool>true</bool>
+                        </property>
+                    </column>
+                    <column>
+                        <property name="text">
+                            <string>Server</string>
+                        </property>
+                        <property name="clickable">
+                            <bool>true</bool>
+                        </property>
+                        <property name="resizable">
+                            <bool>true</bool>
+                        </property>
+                    </column>
+                    <column>
+                        <property name="text">
+                            <string>Status</string>
+                        </property>
+                        <property name="clickable">
+                            <bool>true</bool>
+                        </property>
+                        <property name="resizable">
+                            <bool>true</bool>
+                        </property>
+                    </column>
+                    <property name="name">
+                        <cstring>lv_accs</cstring>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout13</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_add</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Add</string>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/addContact</string>
+                            </property>
+                        </widget>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_modify</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Modify</string>
+                            </property>
+                        </widget>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_remove</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Rem&amp;ove</string>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/remove</string>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer12</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Vertical</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>20</width>
+                                    <height>127</height>
+                                </size>
+                            </property>
+                        </spacer>
+                    </vbox>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>Line1</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout12</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer11</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>395</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+C</string>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_accountmodify.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_accountmodify.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_accountmodify.ui	(revision 38)
@@ -0,0 +1,795 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AccountModifyUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>AccountModifyUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>371</width>
+            <height>420</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Account Properties</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout10</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>TextLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Name:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit">
+                    <property name="name">
+                        <cstring>le_name</cstring>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QTabWidget">
+            <property name="name">
+                <cstring>tab_main</cstring>
+            </property>
+            <property name="tabShape">
+                <enum>Rounded</enum>
+            </property>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Account</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QGroupBox">
+                        <property name="name">
+                            <cstring>gb_account</cstring>
+                        </property>
+                        <property name="title">
+                            <string>Account</string>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <property name="margin">
+                                <number>11</number>
+                            </property>
+                            <property name="spacing">
+                                <number>6</number>
+                            </property>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout121</cstring>
+                                </property>
+                                <grid>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QLabel" row="1" column="1">
+                                        <property name="name">
+                                            <cstring>textLabel3_2</cstring>
+                                        </property>
+                                        <property name="enabled">
+                                            <bool>false</bool>
+                                        </property>
+                                        <property name="font">
+                                            <font>
+                                                <italic>1</italic>
+                                            </font>
+                                        </property>
+                                        <property name="text">
+                                            <string>Example: juliet@capulet.com</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLabel" row="0" column="0">
+                                        <property name="name">
+                                            <cstring>TextLabel2_4</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Jabber ID:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit" row="0" column="1">
+                                        <property name="name">
+                                            <cstring>le_jid</cstring>
+                                        </property>
+                                    </widget>
+                                </grid>
+                            </widget>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout24</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QLabel">
+                                        <property name="name">
+                                            <cstring>textLabel1</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Password:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit">
+                                        <property name="name">
+                                            <cstring>le_pass</cstring>
+                                        </property>
+                                        <property name="echoMode">
+                                            <enum>Password</enum>
+                                        </property>
+                                    </widget>
+                                    <widget class="QPushButton">
+                                        <property name="name">
+                                            <cstring>pb_changepw</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Change...</string>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                        </vbox>
+                    </widget>
+                    <widget class="QGroupBox">
+                        <property name="name">
+                            <cstring>groupBox46</cstring>
+                        </property>
+                        <property name="title">
+                            <string>Settings</string>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QCheckBox">
+                                <property name="name">
+                                    <cstring>ck_auto</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Automatically connect on startup</string>
+                                </property>
+                            </widget>
+                            <widget class="QCheckBox">
+                                <property name="name">
+                                    <cstring>ck_reconn</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Automatically reconnect if disconnected</string>
+                                </property>
+                            </widget>
+                            <widget class="QCheckBox">
+                                <property name="name">
+                                    <cstring>ck_log</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Log message history</string>
+                                </property>
+                            </widget>
+                        </vbox>
+                    </widget>
+                    <spacer>
+                        <property name="name">
+                            <cstring>spacer69</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>20</width>
+                                <height>20</height>
+                            </size>
+                        </property>
+                    </spacer>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Details</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QGroupBox">
+                        <property name="name">
+                            <cstring>gb_personal</cstring>
+                        </property>
+                        <property name="title">
+                            <string>Personal Information</string>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <property name="margin">
+                                <number>11</number>
+                            </property>
+                            <property name="spacing">
+                                <number>6</number>
+                            </property>
+                            <widget class="QLabel">
+                                <property name="name">
+                                    <cstring>TextLabel3</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>&lt;qt&gt;Information about you is stored as a VCard on the server, which other people can retrieve at any time.&lt;/qt&gt;</string>
+                                </property>
+                            </widget>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>Layout12</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <property name="margin">
+                                        <number>0</number>
+                                    </property>
+                                    <property name="spacing">
+                                        <number>6</number>
+                                    </property>
+                                    <spacer>
+                                        <property name="name">
+                                            <cstring>Spacer10</cstring>
+                                        </property>
+                                        <property name="orientation">
+                                            <enum>Horizontal</enum>
+                                        </property>
+                                        <property name="sizeType">
+                                            <enum>Expanding</enum>
+                                        </property>
+                                        <property name="sizeHint">
+                                            <size>
+                                                <width>0</width>
+                                                <height>0</height>
+                                            </size>
+                                        </property>
+                                    </spacer>
+                                    <widget class="QPushButton">
+                                        <property name="name">
+                                            <cstring>pb_vcard</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Edit Personal &amp;Details...</string>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                        </vbox>
+                    </widget>
+                    <widget class="QGroupBox">
+                        <property name="name">
+                            <cstring>gb_pgp</cstring>
+                        </property>
+                        <property name="title">
+                            <string>OpenPGP</string>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout29</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="IconLabel">
+                                        <property name="name">
+                                            <cstring>lb_keyicon</cstring>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLabel">
+                                        <property name="name">
+                                            <cstring>lb_keyname</cstring>
+                                        </property>
+                                        <property name="sizePolicy">
+                                            <sizepolicy>
+                                                <hsizetype>7</hsizetype>
+                                                <vsizetype>5</vsizetype>
+                                                <horstretch>0</horstretch>
+                                                <verstretch>0</verstretch>
+                                            </sizepolicy>
+                                        </property>
+                                        <property name="text">
+                                            <string>placeholder2</string>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>layout12</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <spacer>
+                                        <property name="name">
+                                            <cstring>spacer9</cstring>
+                                        </property>
+                                        <property name="orientation">
+                                            <enum>Horizontal</enum>
+                                        </property>
+                                        <property name="sizeType">
+                                            <enum>Expanding</enum>
+                                        </property>
+                                        <property name="sizeHint">
+                                            <size>
+                                                <width>40</width>
+                                                <height>20</height>
+                                            </size>
+                                        </property>
+                                    </spacer>
+                                    <widget class="QPushButton">
+                                        <property name="name">
+                                            <cstring>pb_key</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Select &amp;Key...</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QPushButton">
+                                        <property name="name">
+                                            <cstring>pb_keyclear</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Use None</string>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                        </vbox>
+                    </widget>
+                    <spacer>
+                        <property name="name">
+                            <cstring>Spacer11</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>20</width>
+                                <height>20</height>
+                            </size>
+                        </property>
+                    </spacer>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Connection</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QGroupBox">
+                        <property name="name">
+                            <cstring>gb_proxy</cstring>
+                        </property>
+                        <property name="title">
+                            <string>Proxy</string>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QLabel">
+                                <property name="name">
+                                    <cstring>lb_proxychooser</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>proxychooser</string>
+                                </property>
+                            </widget>
+                        </vbox>
+                    </widget>
+                    <widget class="QGroupBox">
+                        <property name="name">
+                            <cstring>groupBox7</cstring>
+                        </property>
+                        <property name="title">
+                            <string>Advanced</string>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QCheckBox">
+                                <property name="name">
+                                    <cstring>ck_ssl</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Use SSL encryption (to server)</string>
+                                </property>
+                            </widget>
+                            <widget class="QCheckBox">
+                                <property name="name">
+                                    <cstring>ck_ignoreSSLWarnings</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Ignore SSL warnings</string>
+                                </property>
+                            </widget>
+                            <widget class="QCheckBox">
+                                <property name="name">
+                                    <cstring>ck_plain</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Allow Plaintext Login</string>
+                                </property>
+                            </widget>
+                            <widget class="QCheckBox">
+                                <property name="name">
+                                    <cstring>ck_keepAlive</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Send "Keep-alive" packets (for NAT timeouts)</string>
+                                </property>
+                            </widget>
+                            <widget class="QCheckBox">
+                                <property name="name">
+                                    <cstring>ck_host</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Manually Specify Server Host/Port:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>Layout4</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <property name="margin">
+                                        <number>0</number>
+                                    </property>
+                                    <property name="spacing">
+                                        <number>6</number>
+                                    </property>
+                                    <widget class="QLabel">
+                                        <property name="name">
+                                            <cstring>TextLabel1_8</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Host:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit">
+                                        <property name="name">
+                                            <cstring>le_host</cstring>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLabel">
+                                        <property name="name">
+                                            <cstring>TextLabel4_2</cstring>
+                                        </property>
+                                        <property name="text">
+                                            <string>Port:</string>
+                                        </property>
+                                    </widget>
+                                    <widget class="QLineEdit">
+                                        <property name="name">
+                                            <cstring>le_port</cstring>
+                                        </property>
+                                        <property name="minimumSize">
+                                            <size>
+                                                <width>56</width>
+                                                <height>0</height>
+                                            </size>
+                                        </property>
+                                        <property name="maximumSize">
+                                            <size>
+                                                <width>56</width>
+                                                <height>32767</height>
+                                            </size>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                        </vbox>
+                    </widget>
+                    <spacer>
+                        <property name="name">
+                            <cstring>spacer8</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>20</width>
+                                <height>40</height>
+                            </size>
+                        </property>
+                    </spacer>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Misc.</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QLayoutWidget">
+                        <property name="name">
+                            <cstring>layout11</cstring>
+                        </property>
+                        <hbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QLabel">
+                                <property name="name">
+                                    <cstring>TextLabel1_2</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Resource:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit">
+                                <property name="name">
+                                    <cstring>le_resource</cstring>
+                                </property>
+                                <property name="minimumSize">
+                                    <size>
+                                        <width>100</width>
+                                        <height>0</height>
+                                    </size>
+                                </property>
+                            </widget>
+                            <widget class="QLabel">
+                                <property name="name">
+                                    <cstring>TextLabel2</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Priority:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit">
+                                <property name="name">
+                                    <cstring>le_priority</cstring>
+                                </property>
+                                <property name="sizePolicy">
+                                    <sizepolicy>
+                                        <hsizetype>0</hsizetype>
+                                        <vsizetype>0</vsizetype>
+                                        <horstretch>0</horstretch>
+                                        <verstretch>0</verstretch>
+                                    </sizepolicy>
+                                </property>
+                                <property name="minimumSize">
+                                    <size>
+                                        <width>56</width>
+                                        <height>0</height>
+                                    </size>
+                                </property>
+                                <property name="maximumSize">
+                                    <size>
+                                        <width>56</width>
+                                        <height>32767</height>
+                                    </size>
+                                </property>
+                            </widget>
+                        </hbox>
+                    </widget>
+                    <widget class="QLayoutWidget">
+                        <property name="name">
+                            <cstring>layout16</cstring>
+                        </property>
+                        <hbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QLabel">
+                                <property name="name">
+                                    <cstring>textLabel3</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Data Transfer Proxy:</string>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit">
+                                <property name="name">
+                                    <cstring>le_dtProxy</cstring>
+                                </property>
+                            </widget>
+                        </hbox>
+                    </widget>
+                    <spacer>
+                        <property name="name">
+                            <cstring>Spacer5</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>20</width>
+                                <height>70</height>
+                            </size>
+                        </property>
+                    </spacer>
+                </vbox>
+            </widget>
+        </widget>
+        <spacer>
+            <property name="name">
+                <cstring>Spacer8</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Vertical</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>20</width>
+                    <height>16</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="Line">
+            <property name="name">
+                <cstring>Line2</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout26</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer1</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>251</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_save</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Save</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+S</string>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+    <tabstop>le_name</tabstop>
+    <tabstop>tab_main</tabstop>
+    <tabstop>le_jid</tabstop>
+    <tabstop>le_pass</tabstop>
+    <tabstop>pb_changepw</tabstop>
+    <tabstop>ck_auto</tabstop>
+    <tabstop>ck_reconn</tabstop>
+    <tabstop>ck_log</tabstop>
+    <tabstop>pb_close</tabstop>
+    <tabstop>pb_save</tabstop>
+    <tabstop>pb_vcard</tabstop>
+    <tabstop>pb_key</tabstop>
+    <tabstop>pb_keyclear</tabstop>
+    <tabstop>ck_ssl</tabstop>
+    <tabstop>ck_ignoreSSLWarnings</tabstop>
+    <tabstop>ck_plain</tabstop>
+    <tabstop>ck_keepAlive</tabstop>
+    <tabstop>ck_host</tabstop>
+    <tabstop>le_host</tabstop>
+    <tabstop>le_port</tabstop>
+    <tabstop>le_resource</tabstop>
+    <tabstop>le_priority</tabstop>
+    <tabstop>le_dtProxy</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>fancylabel.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_accountreg.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_accountreg.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_accountreg.ui	(revision 38)
@@ -0,0 +1,317 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AccountRegUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>AccountRegUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>378</width>
+            <height>401</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Register Account</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>gb_account</cstring>
+            </property>
+            <property name="title">
+                <string>Account</string>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLineEdit" row="0" column="1">
+                    <property name="name">
+                        <cstring>le_jid</cstring>
+                    </property>
+                    <property name="minimumSize">
+                        <size>
+                            <width>200</width>
+                            <height>0</height>
+                        </size>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>TextLabel2_4</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Jabber ID:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="3" column="0">
+                    <property name="name">
+                        <cstring>TextLabel2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Confirm Password:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="3" column="1">
+                    <property name="name">
+                        <cstring>le_confirm</cstring>
+                    </property>
+                    <property name="echoMode">
+                        <enum>Password</enum>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="2" column="1">
+                    <property name="name">
+                        <cstring>le_pass</cstring>
+                    </property>
+                    <property name="echoMode">
+                        <enum>Password</enum>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Password:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="1">
+                    <property name="name">
+                        <cstring>textLabel3_2</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="font">
+                        <font>
+                            <italic>1</italic>
+                        </font>
+                    </property>
+                    <property name="text">
+                        <string>Example: juliet@capulet.com</string>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>gb_proxy</cstring>
+            </property>
+            <property name="title">
+                <string>Proxy</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_proxychooser</cstring>
+                    </property>
+                    <property name="text">
+                        <string>proxychooser</string>
+                    </property>
+                </widget>
+            </vbox>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>gb_advanced</cstring>
+            </property>
+            <property name="title">
+                <string>Advanced</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_ssl</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Use SSL encryption (to server)</string>
+                    </property>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_host</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Manually Specify Server Host/Port:</string>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>Layout4_2</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>0</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>TextLabel1_8_2</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Host:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit">
+                            <property name="name">
+                                <cstring>le_host</cstring>
+                            </property>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>TextLabel4_2_2</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Port:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit">
+                            <property name="name">
+                                <cstring>le_port</cstring>
+                            </property>
+                            <property name="minimumSize">
+                                <size>
+                                    <width>56</width>
+                                    <height>0</height>
+                                </size>
+                            </property>
+                            <property name="maximumSize">
+                                <size>
+                                    <width>56</width>
+                                    <height>32767</height>
+                                </size>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+            </vbox>
+        </widget>
+        <spacer>
+            <property name="name">
+                <cstring>Spacer17</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Vertical</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>20</width>
+                    <height>20</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line2</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout23</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="BusyWidget">
+                    <property name="name">
+                        <cstring>busy</cstring>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer16</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>80</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_reg</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Register</string>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                    <property name="iconName" stdset="0">
+                        <string>psi/register</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>pb_close</sender>
+        <signal>clicked()</signal>
+        <receiver>AccountRegUI</receiver>
+        <slot>close()</slot>
+    </connection>
+</connections>
+<tabstops>
+    <tabstop>le_jid</tabstop>
+    <tabstop>le_pass</tabstop>
+    <tabstop>le_confirm</tabstop>
+    <tabstop>ck_ssl</tabstop>
+    <tabstop>ck_host</tabstop>
+    <tabstop>le_host</tabstop>
+    <tabstop>le_port</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_accountremove.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_accountremove.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_accountremove.ui	(revision 38)
@@ -0,0 +1,204 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AccountRemoveUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>AccountRemoveUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>429</width>
+            <height>173</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Remove Account</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>gb_account</cstring>
+            </property>
+            <property name="title">
+                <string>Remove Account</string>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>11</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_remove</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Remove account from Psi only.</string>
+                    </property>
+                </widget>
+                <widget class="QRadioButton">
+                    <property name="name">
+                        <cstring>rb_removeAndUnreg</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Remove account and try to unregister it from the server.</string>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>Layout3</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>0</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>lb_pass</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Password:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit">
+                            <property name="name">
+                                <cstring>le_pass</cstring>
+                            </property>
+                            <property name="maximumSize">
+                                <size>
+                                    <width>100</width>
+                                    <height>32767</height>
+                                </size>
+                            </property>
+                            <property name="echoMode">
+                                <enum>Password</enum>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer2</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Horizontal</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>30</width>
+                                    <height>0</height>
+                                </size>
+                            </property>
+                        </spacer>
+                    </hbox>
+                </widget>
+            </vbox>
+        </widget>
+        <spacer>
+            <property name="name">
+                <cstring>Spacer3</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Vertical</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>16</width>
+                    <height>16</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout35</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="BusyWidget">
+                    <property name="name">
+                        <cstring>busy</cstring>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer16</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>179</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout34</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_close</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Close</string>
+                            </property>
+                        </widget>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_remove</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Remove</string>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/remove</string>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>busywidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_addurl.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_addurl.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_addurl.ui	(revision 38)
@@ -0,0 +1,149 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AddUrlUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>AddUrlUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>337</width>
+            <height>123</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Add URL</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout2</cstring>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLineEdit" row="1" column="1">
+                    <property name="name">
+                        <cstring>le_desc</cstring>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>TextLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>URL:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>TextLabel2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Description:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="0" column="1">
+                    <property name="name">
+                        <cstring>le_url</cstring>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <spacer>
+            <property name="name">
+                <cstring>Spacer2</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Vertical</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>16</width>
+                    <height>16</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout38</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer1</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>203</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_ok</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;OK</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+O</string>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+    <tabstop>le_url</tabstop>
+    <tabstop>le_desc</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_adduser.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_adduser.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_adduser.ui	(revision 38)
@@ -0,0 +1,617 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AddUserUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>AddUserUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>601</width>
+            <height>367</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Add User</string>
+    </property>
+    <hbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QTextEdit">
+            <property name="name">
+                <cstring>TextEdit1</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>7</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="minimumSize">
+                <size>
+                    <width>200</width>
+                    <height>0</height>
+                </size>
+            </property>
+            <property name="maximumSize">
+                <size>
+                    <width>200</width>
+                    <height>32767</height>
+                </size>
+            </property>
+            <property name="text">
+                <string>&lt;qt&gt;To add a &lt;b&gt;Jabber&lt;/b&gt; user, simply fill out the Jabber ID (and optional nickname and group) at the bottom and press &lt;i&gt;Add&lt;/i&gt;.&lt;br&gt;
+&lt;br&gt;
+To add a contact from a &lt;b&gt;non-Jabber&lt;/b&gt; service, make sure you are registered with the service first (see Service Discovery from the main menu), and then select the service from the box at the top.  Follow the instructions in the &lt;i&gt;Service ID Translation&lt;/i&gt; box and press the &lt;i&gt;Get Jabber ID&lt;/i&gt; button to generate a Jabber ID for the contact.&lt;br&gt;
+&lt;/qt&gt;</string>
+            </property>
+            <property name="readOnly">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout10</cstring>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>Layout4</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>0</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>TextLabel5</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Service:</string>
+                            </property>
+                        </widget>
+                        <widget class="QComboBox">
+                            <item>
+                                <property name="text">
+                                    <string>Jabber</string>
+                                </property>
+                            </item>
+                            <property name="name">
+                                <cstring>cb_service</cstring>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer1</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Horizontal</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>20</width>
+                                    <height>0</height>
+                                </size>
+                            </property>
+                        </spacer>
+                    </hbox>
+                </widget>
+                <widget class="QGroupBox">
+                    <property name="name">
+                        <cstring>gb_trans</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="title">
+                        <string>Service ID Translation</string>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>11</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>lb_transDesc</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>5</hsizetype>
+                                    <vsizetype>7</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                            <property name="text">
+                                <string>No description</string>
+                            </property>
+                        </widget>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>Layout5</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <property name="margin">
+                                    <number>0</number>
+                                </property>
+                                <property name="spacing">
+                                    <number>6</number>
+                                </property>
+                                <widget class="QLineEdit">
+                                    <property name="name">
+                                        <cstring>le_transPrompt</cstring>
+                                    </property>
+                                </widget>
+                                <widget class="QPushButton">
+                                    <property name="name">
+                                        <cstring>pb_transGet</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Get Jabber ID</string>
+                                    </property>
+                                </widget>
+                            </hbox>
+                        </widget>
+                    </vbox>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout8</cstring>
+                    </property>
+                    <grid>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QLayoutWidget" row="0" column="1">
+                            <property name="name">
+                                <cstring>layout6</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <widget class="QLineEdit">
+                                    <property name="name">
+                                        <cstring>le_jid</cstring>
+                                    </property>
+                                </widget>
+                                <widget class="IconToolButton">
+                                    <property name="name">
+                                        <cstring>tb_vCard</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string></string>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/vCard</string>
+                                    </property>
+                                </widget>
+                            </hbox>
+                        </widget>
+                        <widget class="QLabel" row="2" column="0">
+                            <property name="name">
+                                <cstring>TextLabel8</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Group</string>
+                            </property>
+                        </widget>
+                        <widget class="QLayoutWidget" row="1" column="1">
+                            <property name="name">
+                                <cstring>layout7</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <widget class="QLineEdit">
+                                    <property name="name">
+                                        <cstring>le_nick</cstring>
+                                    </property>
+                                </widget>
+                                <widget class="QToolButton">
+                                    <property name="name">
+                                        <cstring>tb_resolveNick</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Resolve</string>
+                                    </property>
+                                </widget>
+                            </hbox>
+                        </widget>
+                        <widget class="QLabel" row="1" column="0">
+                            <property name="name">
+                                <cstring>TextLabel7</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Nickname (optional)</string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel" row="0" column="0">
+                            <property name="name">
+                                <cstring>TextLabel6</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Jabber ID</string>
+                            </property>
+                        </widget>
+                        <widget class="QComboBox" row="2" column="1">
+                            <property name="name">
+                                <cstring>cb_group</cstring>
+                            </property>
+                            <property name="editable">
+                                <bool>true</bool>
+                            </property>
+                        </widget>
+                    </grid>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_authreq</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Request authorization when adding</string>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer3</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Vertical</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>16</width>
+                            <height>33</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Close window after adding</string>
+                    </property>
+                </widget>
+                <widget class="Line">
+                    <property name="name">
+                        <cstring>Line2</cstring>
+                    </property>
+                    <property name="frameShape">
+                        <enum>HLine</enum>
+                    </property>
+                    <property name="frameShadow">
+                        <enum>Sunken</enum>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout47</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="BusyWidget">
+                            <property name="name">
+                                <cstring>busy</cstring>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer2</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Horizontal</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>140</width>
+                                    <height>20</height>
+                                </size>
+                            </property>
+                        </spacer>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_close</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Close</string>
+                            </property>
+                        </widget>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_add</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Add</string>
+                            </property>
+                            <property name="accel">
+                                <string>Alt+A</string>
+                            </property>
+                            <property name="default">
+                                <bool>true</bool>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/addContact</string>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+            </vbox>
+        </widget>
+    </hbox>
+</widget>
+<customwidgets>
+    <customwidget>
+        <class>BusyWidget</class>
+        <header location="local">busywidget.h</header>
+        <sizehint>
+            <width>-1</width>
+            <height>-1</height>
+        </sizehint>
+        <container>0</container>
+        <sizepolicy>
+            <hordata>5</hordata>
+            <verdata>5</verdata>
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+        </sizepolicy>
+        <pixmap>image0</pixmap>
+        <signal>destroyed()</signal>
+        <signal>destroyed(QObject*)</signal>
+        <slot access="public" specifier="">deleteLater()</slot>
+        <slot access="private" specifier="">cleanupEventFilter(QObject*)</slot>
+        <slot access="public" specifier="">setEnabled(bool)</slot>
+        <slot access="public" specifier="">setDisabled(bool)</slot>
+        <slot access="public" specifier="">setCaption(const QString&amp;)</slot>
+        <slot access="public" specifier="">setIcon(const QPixmap&amp;)</slot>
+        <slot access="public" specifier="">setIconText(const QString&amp;)</slot>
+        <slot access="public" specifier="">setMouseTracking(bool)</slot>
+        <slot access="public" specifier="">setFocus()</slot>
+        <slot access="public" specifier="">clearFocus()</slot>
+        <slot access="public" specifier="">setUpdatesEnabled(bool)</slot>
+        <slot access="public" specifier="">update()</slot>
+        <slot access="public" specifier="">update(int,int,int,int)</slot>
+        <slot access="public" specifier="">update(const QRect&amp;)</slot>
+        <slot access="public" specifier="">repaint()</slot>
+        <slot access="public" specifier="">repaint(bool)</slot>
+        <slot access="public" specifier="">repaint(int,int,int,int)</slot>
+        <slot access="public" specifier="">repaint(int,int,int,int,bool)</slot>
+        <slot access="public" specifier="">repaint(const QRect&amp;)</slot>
+        <slot access="public" specifier="">repaint(const QRect&amp;,bool)</slot>
+        <slot access="public" specifier="">repaint(const QRegion&amp;)</slot>
+        <slot access="public" specifier="">repaint(const QRegion&amp;,bool)</slot>
+        <slot access="public" specifier="">show()</slot>
+        <slot access="public" specifier="">hide()</slot>
+        <slot access="public" specifier="">setShown(bool)</slot>
+        <slot access="public" specifier="">setHidden(bool)</slot>
+        <slot access="public" specifier="">iconify()</slot>
+        <slot access="public" specifier="">showMinimized()</slot>
+        <slot access="public" specifier="">showMaximized()</slot>
+        <slot access="public" specifier="">showFullScreen()</slot>
+        <slot access="public" specifier="">showNormal()</slot>
+        <slot access="public" specifier="">polish()</slot>
+        <slot access="public" specifier="">constPolish()</slot>
+        <slot access="public" specifier="">close()</slot>
+        <slot access="public" specifier="">raise()</slot>
+        <slot access="public" specifier="">lower()</slot>
+        <slot access="public" specifier="">stackUnder(QWidget*)</slot>
+        <slot access="public" specifier="">move(int,int)</slot>
+        <slot access="public" specifier="">move(const QPoint&amp;)</slot>
+        <slot access="public" specifier="">resize(int,int)</slot>
+        <slot access="public" specifier="">resize(const QSize&amp;)</slot>
+        <slot access="public" specifier="">setGeometry(int,int,int,int)</slot>
+        <slot access="public" specifier="">setGeometry(const QRect&amp;)</slot>
+        <slot access="public" specifier="">adjustSize()</slot>
+        <slot access="private" specifier="">focusProxyDestroyed()</slot>
+        <slot access="public" specifier="">start()</slot>
+        <slot access="public" specifier="">stop()</slot>
+        <slot access="public" specifier="">setActive(bool)</slot>
+        <slot access="private" specifier="">animate()</slot>
+        <property type="CString">name</property>
+        <property type="Bool">enabled</property>
+        <property type="Rect">geometry</property>
+        <property type="SizePolicy">sizePolicy</property>
+        <property type="Size">minimumSize</property>
+        <property type="Size">maximumSize</property>
+        <property type="Size">sizeIncrement</property>
+        <property type="Size">baseSize</property>
+        <property type="Color">paletteForegroundColor</property>
+        <property type="Color">paletteBackgroundColor</property>
+        <property type="Pixmap">paletteBackgroundPixmap</property>
+        <property type="Palette">palette</property>
+        <property type="BackgroundOrigin">backgroundOrigin</property>
+        <property type="Font">font</property>
+        <property type="Cursor">cursor</property>
+        <property type="String">caption</property>
+        <property type="Pixmap">icon</property>
+        <property type="String">iconText</property>
+        <property type="Bool">mouseTracking</property>
+        <property type="FocusPolicy">focusPolicy</property>
+        <property type="Bool">acceptDrops</property>
+        <property type="Bool">active</property>
+    </customwidget>
+    <customwidget>
+        <class>IconButton</class>
+        <header location="local">iconwidget.h</header>
+        <sizehint>
+            <width>-1</width>
+            <height>-1</height>
+        </sizehint>
+        <container>0</container>
+        <sizepolicy>
+            <hordata>5</hordata>
+            <verdata>5</verdata>
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+        </sizepolicy>
+        <pixmap>image0</pixmap>
+        <signal>destroyed()</signal>
+        <signal>destroyed(QObject*)</signal>
+        <signal>pressed()</signal>
+        <signal>released()</signal>
+        <signal>clicked()</signal>
+        <signal>toggled(bool)</signal>
+        <signal>stateChanged(int)</signal>
+        <slot access="public" specifier="">deleteLater()</slot>
+        <slot access="private" specifier="">cleanupEventFilter(QObject*)</slot>
+        <slot access="public" specifier="">setEnabled(bool)</slot>
+        <slot access="public" specifier="">setDisabled(bool)</slot>
+        <slot access="public" specifier="">setCaption(const QString&amp;)</slot>
+        <slot access="public" specifier="">setIcon(const QPixmap&amp;)</slot>
+        <slot access="public" specifier="">setIconText(const QString&amp;)</slot>
+        <slot access="public" specifier="">setMouseTracking(bool)</slot>
+        <slot access="public" specifier="">setFocus()</slot>
+        <slot access="public" specifier="">clearFocus()</slot>
+        <slot access="public" specifier="">setUpdatesEnabled(bool)</slot>
+        <slot access="public" specifier="">update()</slot>
+        <slot access="public" specifier="">update(int,int,int,int)</slot>
+        <slot access="public" specifier="">update(const QRect&amp;)</slot>
+        <slot access="public" specifier="">repaint()</slot>
+        <slot access="public" specifier="">repaint(bool)</slot>
+        <slot access="public" specifier="">repaint(int,int,int,int)</slot>
+        <slot access="public" specifier="">repaint(int,int,int,int,bool)</slot>
+        <slot access="public" specifier="">repaint(const QRect&amp;)</slot>
+        <slot access="public" specifier="">repaint(const QRect&amp;,bool)</slot>
+        <slot access="public" specifier="">repaint(const QRegion&amp;)</slot>
+        <slot access="public" specifier="">repaint(const QRegion&amp;,bool)</slot>
+        <slot access="public" specifier="">show()</slot>
+        <slot access="public" specifier="">hide()</slot>
+        <slot access="public" specifier="">setShown(bool)</slot>
+        <slot access="public" specifier="">setHidden(bool)</slot>
+        <slot access="public" specifier="">iconify()</slot>
+        <slot access="public" specifier="">showMinimized()</slot>
+        <slot access="public" specifier="">showMaximized()</slot>
+        <slot access="public" specifier="">showFullScreen()</slot>
+        <slot access="public" specifier="">showNormal()</slot>
+        <slot access="public" specifier="">polish()</slot>
+        <slot access="public" specifier="">constPolish()</slot>
+        <slot access="public" specifier="">close()</slot>
+        <slot access="public" specifier="">raise()</slot>
+        <slot access="public" specifier="">lower()</slot>
+        <slot access="public" specifier="">stackUnder(QWidget*)</slot>
+        <slot access="public" specifier="">move(int,int)</slot>
+        <slot access="public" specifier="">move(const QPoint&amp;)</slot>
+        <slot access="public" specifier="">resize(int,int)</slot>
+        <slot access="public" specifier="">resize(const QSize&amp;)</slot>
+        <slot access="public" specifier="">setGeometry(int,int,int,int)</slot>
+        <slot access="public" specifier="">setGeometry(const QRect&amp;)</slot>
+        <slot access="public" specifier="">adjustSize()</slot>
+        <slot access="private" specifier="">focusProxyDestroyed()</slot>
+        <slot access="public" specifier="">animateClick()</slot>
+        <slot access="public" specifier="">toggle()</slot>
+        <slot access="private" specifier="">animateTimeout()</slot>
+        <slot access="private" specifier="">autoRepeatTimeout()</slot>
+        <slot access="private" specifier="">emulateClick()</slot>
+        <slot access="public" specifier="">setOn(bool)</slot>
+        <slot access="private" specifier="">popupPressed()</slot>
+        <slot access="public" specifier="">setIcon(const Icon*)</slot>
+        <slot access="public" specifier="">setIcon(const Icon*,bool)</slot>
+        <slot access="public" specifier="">forceSetIcon(const Icon*)</slot>
+        <slot access="public" specifier="">forceSetIcon(const Icon*,bool)</slot>
+        <slot access="public" specifier="">setIcon(const QString&amp;)</slot>
+        <slot access="public" specifier="">iconName()</slot>
+        <slot access="public" specifier="">setText(const QString&amp;)</slot>
+        <slot access="public" specifier="">textVisible()</slot>
+        <slot access="public" specifier="">setTextVisible(bool)</slot>
+        <property type="CString">name</property>
+        <property type="Bool">enabled</property>
+        <property type="Rect">geometry</property>
+        <property type="SizePolicy">sizePolicy</property>
+        <property type="Size">minimumSize</property>
+        <property type="Size">maximumSize</property>
+        <property type="Size">sizeIncrement</property>
+        <property type="Size">baseSize</property>
+        <property type="Color">paletteForegroundColor</property>
+        <property type="Color">paletteBackgroundColor</property>
+        <property type="Pixmap">paletteBackgroundPixmap</property>
+        <property type="Palette">palette</property>
+        <property type="BackgroundOrigin">backgroundOrigin</property>
+        <property type="Font">font</property>
+        <property type="Cursor">cursor</property>
+        <property type="String">caption</property>
+        <property type="Pixmap">icon</property>
+        <property type="String">iconText</property>
+        <property type="Bool">mouseTracking</property>
+        <property type="FocusPolicy">focusPolicy</property>
+        <property type="Bool">acceptDrops</property>
+        <property type="String">text</property>
+        <property type="Pixmap">pixmap</property>
+        <property type="KeySequence">accel</property>
+        <property type="Bool">autoRepeat</property>
+        <property type="Bool">autoDefault</property>
+        <property type="Bool">default</property>
+        <property type="IconSet">iconSet</property>
+        <property type="Bool">toggleButton</property>
+        <property type="Bool">on</property>
+        <property type="Bool">flat</property>
+        <property type="Bool">autoMask</property>
+        <property type="String">iconName</property>
+        <property type="Bool">textVisible</property>
+    </customwidget>
+</customwidgets>
+<images>
+    <image name="image0">
+        <data format="PNG" length="1125">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042c49444154789cb5954f6c14551cc73fefcd7476b65bdaae4bb78bb5502a14d404e4801c88182d1c4c2c693da847400f9c24c68b878684238660e2b1e01f12c19493012ef2478c814412d354a46017a8a564bb6da5bbedccee767776e63d0ffb073751d483bfe49799974c3eeffb7ebf37df9fd05a530b2184040cc0042420aaf9a4d0d554800f045a6b256ae0e1e1e1d6bebebe838ee31c48a7d39b5cd7fd075e251cc7617272f2ded8d8d819cff33e0316819259537aead4a9839d5dd6d1784f91f55b0a94830242088404d304292bef68a89f520802a598fecddaa04f1a876f5c250c7c0a64cdeac686e33807e23d45e6b297c8b877f1831542614550b6599835c83c2a81b6786a75134faf2f1169f12997350881d9021d0903e06de0745d3160a6d3e94dbd5b0a64dcbb94b5831d0e3375ab892b1772dcf9790528543f8dd0d367b36768153b5e31503a0f1aecb004580b44ffac58baae8b1714f0833c7638cc8dab303a320f4822ab4c7a37c69196203de3319d5ce1c4d13c733331dedc67a129a154fd128401ab0616d55a130ac3d42d93d1913940d13fd0c9ee0183685c60da01c5421bd72f7a8c8efccef9afd374267ad93d642365be0636a0d28ec7600941d9e6f23917f0e97f23ce5bef35d19ec863da0ed9059b2be70bec196c66dfa10ec0e49b338f7017258651bf95021035c595429bb0903248fe52a2b5b595dd7b4d945cc2340cdca536be389ee3f67886c5798f773fe8e0dac508c989659277a2180da4ca4ff07821058b8b251445d63d6b13ed1098a6417e39cac85197dbe31962ab9bd9f1f22a226d45366f6d0620fdb08c900d281af6110284b20085b414861d905d88f2e52739ee8cbb8022143259d3dd84691730aa2d52da441a8de0c6958068870022a41e9629ad3473fd3b8fdbe319dadb9b4924da994d2d716c7896fbe35152f78b48245d6b2da4507faf582be8eaf159b721cc837b05ae7debb1f79d08cb8b515edad942a22bc4b1c33eb3d34b1c797f06af90a72d16e2f96d9a74aa11dca8586b222d01af0fb60070f6c402d72f15d97f28c6f6d7027a5f5ce6c3233dc4e2ede496b278be4fff608cee8d3e1add806aeca51094cbb06397c1ecc328e746537c7e3ccdb5cb1136bf60635882d4d41c6ec6836ab37efa214f72208ed9f4d7cdd38ee310280542e38b1c43fb6de26b3672e1ec3cc99bcb246f66a938a3241ab3e91f7c861fbf77710b1e5e49915bae974203ba0e9e9c9cbc373d6d6d305a040a89c2a77f50b27d5782bbbf7acccf28349235dd16cf6dd374f7295e1de8a45c02d37499182b01cc0201a085d61a2144d8b2ac8fb6ed340e77240c4261890e04c250185262546d534a032154b59e0ad394e41c98182bf268ce6721ed9f064e0253356f6da2e24c1f030f783c15fe6da680af8021602bd051532ca9b8521488559f61aa86c29343578fbf0264a94c906c7d3409214c20043457a116ff6de6795578012889ff6b98fe016ea0ce1c203e47720000000049454e44ae426082</data>
+    </image>
+</images>
+<tabstops>
+    <tabstop>cb_service</tabstop>
+    <tabstop>le_transPrompt</tabstop>
+    <tabstop>pb_transGet</tabstop>
+    <tabstop>le_jid</tabstop>
+    <tabstop>le_nick</tabstop>
+    <tabstop>cb_group</tabstop>
+    <tabstop>ck_authreq</tabstop>
+    <tabstop>ck_close</tabstop>
+    <tabstop>TextEdit1</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>busywidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_browser.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_browser.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_browser.ui	(revision 38)
@@ -0,0 +1,195 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>BrowserUI</class>
+<widget class="QMainWindow">
+    <property name="name">
+        <cstring>BrowserUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>505</width>
+            <height>369</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Browse Services</string>
+    </property>
+    <property name="dockWindowsMovable">
+        <bool>false</bool>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout20</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>textLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Address:</string>
+                    </property>
+                    <property name="buddy" stdset="0">
+                        <cstring>cb_browse</cstring>
+                    </property>
+                </widget>
+                <widget class="QComboBox">
+                    <property name="name">
+                        <cstring>cb_browse</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>7</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="editable">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>pb_browse</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Browse</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QListView">
+            <column>
+                <property name="text">
+                    <string>Name</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <column>
+                <property name="text">
+                    <string>JID</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <property name="name">
+                <cstring>lv_browse</cstring>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>cb_preBrowse</cstring>
+            </property>
+            <property name="text">
+                <string>Auto-browse into objects</string>
+            </property>
+            <property name="checked">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line3</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout51</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="BusyWidget">
+                    <property name="name">
+                        <cstring>busy</cstring>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer23</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>347</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<toolbars>
+</toolbars>
+<connections>
+    <connection>
+        <sender>lv_browse</sender>
+        <signal>selectionChanged(QListViewItem*)</signal>
+        <receiver>BrowserUI</receiver>
+        <slot>itemSelected(QListViewItem*)</slot>
+    </connection>
+    <connection>
+        <sender>lv_browse</sender>
+        <signal>doubleClicked(QListViewItem*)</signal>
+        <receiver>BrowserUI</receiver>
+        <slot>itemDoubleclicked(QListViewItem*)</slot>
+    </connection>
+    <connection>
+        <sender>pb_close</sender>
+        <signal>clicked()</signal>
+        <receiver>BrowserUI</receiver>
+        <slot>close()</slot>
+    </connection>
+</connections>
+<slots>
+    <slot>itemSelected(QListViewItem*)</slot>
+    <slot>itemDoubleclicked(QListViewItem*)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>busywidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_changepw.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_changepw.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_changepw.ui	(revision 38)
@@ -0,0 +1,187 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>ChangePasswordUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>ChangePasswordUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>331</width>
+            <height>149</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Change Password</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout1</cstring>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLineEdit" row="2" column="1">
+                    <property name="name">
+                        <cstring>le_pwver</cstring>
+                    </property>
+                    <property name="echoMode">
+                        <enum>Password</enum>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="0" column="1">
+                    <property name="name">
+                        <cstring>le_pwcur</cstring>
+                    </property>
+                    <property name="echoMode">
+                        <enum>Password</enum>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>lb_pwver</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Confirm new password:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>lb_pwcur</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Current password:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="1" column="1">
+                    <property name="name">
+                        <cstring>le_pwnew</cstring>
+                    </property>
+                    <property name="echoMode">
+                        <enum>Password</enum>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>lb_pwnew</cstring>
+                    </property>
+                    <property name="text">
+                        <string>New password:</string>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <spacer>
+            <property name="name">
+                <cstring>Spacer2</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Vertical</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>20</width>
+                    <height>16</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="Line">
+            <property name="name">
+                <cstring>Line1</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout54</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="BusyWidget">
+                    <property name="name">
+                        <cstring>busy</cstring>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer1</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>110</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_apply</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Apply</string>
+                    </property>
+                    <property name="iconName" stdset="0">
+                        <string>psi/register</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<tabstops>
+    <tabstop>le_pwcur</tabstop>
+    <tabstop>le_pwnew</tabstop>
+    <tabstop>le_pwver</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>busywidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_disco.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_disco.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_disco.ui	(revision 38)
@@ -0,0 +1,225 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>DiscoUI</class>
+<widget class="QMainWindow">
+    <property name="name">
+        <cstring>DiscoUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>507</width>
+            <height>374</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Service Discovery</string>
+    </property>
+    <property name="dockWindowsMovable">
+        <bool>false</bool>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout3</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>textLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Address:</string>
+                    </property>
+                    <property name="buddy" stdset="0">
+                        <cstring>cb_address</cstring>
+                    </property>
+                </widget>
+                <widget class="QComboBox">
+                    <property name="name">
+                        <cstring>cb_address</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>7</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="editable">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_node</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Node:</string>
+                    </property>
+                    <property name="buddy" stdset="0">
+                        <cstring>cb_node</cstring>
+                    </property>
+                </widget>
+                <widget class="QComboBox">
+                    <property name="name">
+                        <cstring>cb_node</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>5</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="editable">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>pb_browse</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Browse</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QListView">
+            <column>
+                <property name="text">
+                    <string>Name</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <column>
+                <property name="text">
+                    <string>JID</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <column>
+                <property name="text">
+                    <string>Node</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <property name="name">
+                <cstring>lv_disco</cstring>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autoItems</cstring>
+            </property>
+            <property name="text">
+                <string>Auto-browse into objects</string>
+            </property>
+            <property name="checked">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QCheckBox">
+            <property name="name">
+                <cstring>ck_autoInfo</cstring>
+            </property>
+            <property name="text">
+                <string>Automatically get item information</string>
+            </property>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line3</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout57</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="BusyWidget">
+                    <property name="name">
+                        <cstring>busy</cstring>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer23</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>349</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<toolbars>
+</toolbars>
+<connections>
+    <connection>
+        <sender>pb_close</sender>
+        <signal>clicked()</signal>
+        <receiver>DiscoUI</receiver>
+        <slot>close()</slot>
+    </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>busywidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_filetrans.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_filetrans.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_filetrans.ui	(revision 38)
@@ -0,0 +1,204 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>FileTransUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>FileTransUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>334</width>
+            <height>268</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Form1</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>lb_accountlabel</cstring>
+            </property>
+            <property name="text">
+                <string>accountlabel</string>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout4</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_to</cstring>
+                    </property>
+                    <property name="text">
+                        <string>To:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit">
+                    <property name="name">
+                        <cstring>le_to</cstring>
+                    </property>
+                    <property name="minimumSize">
+                        <size>
+                            <width>100</width>
+                            <height>0</height>
+                        </size>
+                    </property>
+                    <property name="readOnly">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout7</cstring>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>textLabel2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>File:</string>
+                    </property>
+                </widget>
+                <widget class="IconToolButton" row="0" column="2">
+                    <property name="name">
+                        <cstring>tb_browse</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                    <property name="iconName" stdset="0">
+                        <string>psi/browse</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="1" rowspan="1" colspan="2">
+                    <property name="name">
+                        <cstring>lb_size</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>textLabel3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Size:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="0" column="1">
+                    <property name="name">
+                        <cstring>le_fname</cstring>
+                    </property>
+                    <property name="readOnly">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>textLabel1</cstring>
+            </property>
+            <property name="text">
+                <string>Description:</string>
+            </property>
+        </widget>
+        <widget class="QTextEdit">
+            <property name="name">
+                <cstring>te_desc</cstring>
+            </property>
+        </widget>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>lb_status</cstring>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line1</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout2</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="BusyWidget">
+                    <property name="name">
+                        <cstring>busy</cstring>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer1</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>40</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>pb_stop</cstring>
+                    </property>
+                    <property name="text">
+                        <string>stop</string>
+                    </property>
+                </widget>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>pb_start</cstring>
+                    </property>
+                    <property name="text">
+                        <string>start</string>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_gcjoin.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_gcjoin.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_gcjoin.ui	(revision 38)
@@ -0,0 +1,250 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>GCJoinUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>GCJoinUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>280</width>
+            <height>235</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Join Groupchat</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout3</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>TextLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Identity:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_ident</cstring>
+                    </property>
+                    <property name="text">
+                        <string>ident</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout3</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>TextLabel1_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Recent:</string>
+                    </property>
+                </widget>
+                <widget class="QComboBox">
+                    <property name="name">
+                        <cstring>cb_recent</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>7</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QGroupBox">
+            <property name="name">
+                <cstring>gb_info</cstring>
+            </property>
+            <property name="title">
+                <string>Room information:</string>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>11</number>
+                </property>
+                <property name="spacing">
+                    <number>6</number>
+                </property>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>TextLabel2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Host:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="0" column="1">
+                    <property name="name">
+                        <cstring>le_host</cstring>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>TextLabel3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Room:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="1" column="1">
+                    <property name="name">
+                        <cstring>le_room</cstring>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>TextLabel4</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Nickname:</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="2" column="1">
+                    <property name="name">
+                        <cstring>le_nick</cstring>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <spacer>
+            <property name="name">
+                <cstring>Spacer2</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Vertical</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>16</width>
+                    <height>16</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="Line">
+            <property name="name">
+                <cstring>Line1</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout61</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="BusyWidget">
+                    <property name="name">
+                        <cstring>busy</cstring>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer1</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>31</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_join</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Join</string>
+                    </property>
+                    <property name="iconName" stdset="0">
+                        <string>psi/groupChat</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<tabstops>
+    <tabstop>le_host</tabstop>
+    <tabstop>le_room</tabstop>
+    <tabstop>le_nick</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>busywidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_info.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_info.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_info.ui	(revision 38)
@@ -0,0 +1,620 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>InfoUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>InfoUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>455</width>
+            <height>337</height>
+        </rect>
+    </property>
+    <property name="minimumSize">
+        <size>
+            <width>129</width>
+            <height>129</height>
+        </size>
+    </property>
+    <property name="caption">
+        <string>User Info</string>
+    </property>
+    <property name="sizeGripEnabled">
+        <bool>true</bool>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QTabWidget">
+            <property name="name">
+                <cstring>tabwidget</cstring>
+            </property>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>Widget8</cstring>
+                </property>
+                <attribute name="title">
+                    <string>General</string>
+                </attribute>
+                <grid>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <widget class="QLabel" row="0" column="0">
+                        <property name="name">
+                            <cstring>TextLabel1_2_2</cstring>
+                        </property>
+                        <property name="text">
+                            <string>Full Name</string>
+                        </property>
+                    </widget>
+                    <widget class="QLabel" row="3" column="0">
+                        <property name="name">
+                            <cstring>TextLabel1_3_2_2_2_2</cstring>
+                        </property>
+                        <property name="text">
+                            <string>Phone #</string>
+                        </property>
+                    </widget>
+                    <widget class="QLabel" row="4" column="0">
+                        <property name="name">
+                            <cstring>TextLabel1_2_3_2</cstring>
+                        </property>
+                        <property name="text">
+                            <string>Homepage</string>
+                        </property>
+                    </widget>
+                    <widget class="QLineEdit" row="4" column="1">
+                        <property name="name">
+                            <cstring>le_homepage</cstring>
+                        </property>
+                    </widget>
+                    <widget class="QLabel" row="5" column="0">
+                        <property name="name">
+                            <cstring>TextLabel1_2_2_2_2</cstring>
+                        </property>
+                        <property name="text">
+                            <string>E-Mail</string>
+                        </property>
+                    </widget>
+                    <widget class="QLabel" row="1" column="0">
+                        <property name="name">
+                            <cstring>TextLabel1_2</cstring>
+                        </property>
+                        <property name="text">
+                            <string>Nickname</string>
+                        </property>
+                    </widget>
+                    <widget class="QLineEdit" row="3" column="1">
+                        <property name="name">
+                            <cstring>le_phone</cstring>
+                        </property>
+                    </widget>
+                    <widget class="QLabel" row="2" column="0">
+                        <property name="name">
+                            <cstring>TextLabel1_3_2_3_2</cstring>
+                        </property>
+                        <property name="text">
+                            <string>Birthday</string>
+                        </property>
+                    </widget>
+                    <widget class="QLineEdit" row="2" column="1">
+                        <property name="name">
+                            <cstring>le_bday</cstring>
+                        </property>
+                    </widget>
+                    <widget class="QLineEdit" row="5" column="1">
+                        <property name="name">
+                            <cstring>le_email</cstring>
+                        </property>
+                    </widget>
+                    <widget class="QLineEdit" row="1" column="1">
+                        <property name="name">
+                            <cstring>le_nickname</cstring>
+                        </property>
+                    </widget>
+                    <widget class="QLineEdit" row="0" column="1" rowspan="1" colspan="2">
+                        <property name="name">
+                            <cstring>le_fullname</cstring>
+                        </property>
+                    </widget>
+                    <widget class="QLayoutWidget" row="1" column="2" rowspan="5" colspan="1">
+                        <property name="name">
+                            <cstring>ly_photo</cstring>
+                        </property>
+                        <vbox>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <widget class="QLabel">
+                                <property name="name">
+                                    <cstring>label_photo</cstring>
+                                </property>
+                                <property name="sizePolicy">
+                                    <sizepolicy>
+                                        <hsizetype>1</hsizetype>
+                                        <vsizetype>4</vsizetype>
+                                        <horstretch>0</horstretch>
+                                        <verstretch>0</verstretch>
+                                    </sizepolicy>
+                                </property>
+                                <property name="minimumSize">
+                                    <size>
+                                        <width>129</width>
+                                        <height>129</height>
+                                    </size>
+                                </property>
+                                <property name="maximumSize">
+                                    <size>
+                                        <width>32767</width>
+                                        <height>129</height>
+                                    </size>
+                                </property>
+                                <property name="frameShape">
+                                    <enum>PopupPanel</enum>
+                                </property>
+                                <property name="frameShadow">
+                                    <enum>Raised</enum>
+                                </property>
+                                <property name="lineWidth">
+                                    <number>1</number>
+                                </property>
+                                <property name="margin">
+                                    <number>2</number>
+                                </property>
+                                <property name="scaledContents">
+                                    <bool>false</bool>
+                                </property>
+                                <property name="alignment">
+                                    <set>AlignCenter</set>
+                                </property>
+                            </widget>
+                            <widget class="QLayoutWidget">
+                                <property name="name">
+                                    <cstring>ly_photo_buttons</cstring>
+                                </property>
+                                <hbox>
+                                    <property name="name">
+                                        <cstring>unnamed</cstring>
+                                    </property>
+                                    <widget class="QPushButton">
+                                        <property name="name">
+                                            <cstring>pb_open</cstring>
+                                        </property>
+                                        <property name="sizePolicy">
+                                            <sizepolicy>
+                                                <hsizetype>1</hsizetype>
+                                                <vsizetype>0</vsizetype>
+                                                <horstretch>0</horstretch>
+                                                <verstretch>0</verstretch>
+                                            </sizepolicy>
+                                        </property>
+                                        <property name="text">
+                                            <string>&amp;Open...</string>
+                                        </property>
+                                        <property name="accel">
+                                            <string>Alt+O</string>
+                                        </property>
+                                        <property name="autoDefault">
+                                            <bool>false</bool>
+                                        </property>
+                                        <property name="flat">
+                                            <bool>false</bool>
+                                        </property>
+                                    </widget>
+                                    <widget class="QPushButton">
+                                        <property name="name">
+                                            <cstring>pb_clear</cstring>
+                                        </property>
+                                        <property name="sizePolicy">
+                                            <sizepolicy>
+                                                <hsizetype>1</hsizetype>
+                                                <vsizetype>0</vsizetype>
+                                                <horstretch>0</horstretch>
+                                                <verstretch>0</verstretch>
+                                            </sizepolicy>
+                                        </property>
+                                        <property name="text">
+                                            <string>&amp;Clear</string>
+                                        </property>
+                                        <property name="accel">
+                                            <string>Alt+C</string>
+                                        </property>
+                                        <property name="autoDefault">
+                                            <bool>false</bool>
+                                        </property>
+                                        <property name="flat">
+                                            <bool>false</bool>
+                                        </property>
+                                    </widget>
+                                </hbox>
+                            </widget>
+                        </vbox>
+                    </widget>
+                    <spacer row="6" column="1">
+                        <property name="name">
+                            <cstring>spacer34</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>20</width>
+                                <height>60</height>
+                            </size>
+                        </property>
+                    </spacer>
+                </grid>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Work</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <property name="margin">
+                        <number>11</number>
+                    </property>
+                    <property name="spacing">
+                        <number>6</number>
+                    </property>
+                    <widget class="QLayoutWidget">
+                        <property name="name">
+                            <cstring>Layout8</cstring>
+                        </property>
+                        <grid>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <property name="margin">
+                                <number>0</number>
+                            </property>
+                            <property name="spacing">
+                                <number>6</number>
+                            </property>
+                            <widget class="QLineEdit" row="2" column="1">
+                                <property name="name">
+                                    <cstring>le_title</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="0" column="0">
+                                <property name="name">
+                                    <cstring>TextLabel1_2_2_2_3</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Company</string>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="2" column="0">
+                                <property name="name">
+                                    <cstring>TextLabel1_3_2_2_2_3_2</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Position</string>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit" row="0" column="1">
+                                <property name="name">
+                                    <cstring>le_orgName</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="1" column="0">
+                                <property name="name">
+                                    <cstring>TextLabel1_2_3_3</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Department</string>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit" row="1" column="1">
+                                <property name="name">
+                                    <cstring>le_orgUnit</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit" row="3" column="1">
+                                <property name="name">
+                                    <cstring>le_role</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="3" column="0">
+                                <property name="name">
+                                    <cstring>TextLabel1_3_2_2_2_4</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Role</string>
+                                </property>
+                            </widget>
+                        </grid>
+                    </widget>
+                    <spacer>
+                        <property name="name">
+                            <cstring>Spacer7</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>0</width>
+                                <height>20</height>
+                            </size>
+                        </property>
+                    </spacer>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>Widget9</cstring>
+                </property>
+                <attribute name="title">
+                    <string>Location</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <property name="margin">
+                        <number>11</number>
+                    </property>
+                    <property name="spacing">
+                        <number>6</number>
+                    </property>
+                    <widget class="QLayoutWidget">
+                        <property name="name">
+                            <cstring>Layout5</cstring>
+                        </property>
+                        <property name="minimumSize">
+                            <size>
+                                <width>0</width>
+                                <height>0</height>
+                            </size>
+                        </property>
+                        <property name="maximumSize">
+                            <size>
+                                <width>32767</width>
+                                <height>32767</height>
+                            </size>
+                        </property>
+                        <grid>
+                            <property name="name">
+                                <cstring>unnamed</cstring>
+                            </property>
+                            <property name="margin">
+                                <number>0</number>
+                            </property>
+                            <property name="spacing">
+                                <number>6</number>
+                            </property>
+                            <widget class="QLabel" row="5" column="0">
+                                <property name="name">
+                                    <cstring>TextLabel1_3_2_2_2</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Country</string>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="4" column="0">
+                                <property name="name">
+                                    <cstring>TextLabel1_3_2_2_2_3</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Postal Code</string>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit" row="1" column="1">
+                                <property name="name">
+                                    <cstring>le_ext</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit" row="5" column="1">
+                                <property name="name">
+                                    <cstring>le_country</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit" row="4" column="1">
+                                <property name="name">
+                                    <cstring>le_pcode</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="3" column="0">
+                                <property name="name">
+                                    <cstring>TextLabel1_2_3</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>State</string>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="2" column="0">
+                                <property name="name">
+                                    <cstring>TextLabel1_2_2_2</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>City</string>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit" row="0" column="1">
+                                <property name="name">
+                                    <cstring>le_street</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLabel" row="0" column="0">
+                                <property name="name">
+                                    <cstring>TextLabel1_3_2_3</cstring>
+                                </property>
+                                <property name="text">
+                                    <string>Street</string>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit" row="3" column="1">
+                                <property name="name">
+                                    <cstring>le_state</cstring>
+                                </property>
+                            </widget>
+                            <widget class="QLineEdit" row="2" column="1">
+                                <property name="name">
+                                    <cstring>le_city</cstring>
+                                </property>
+                            </widget>
+                        </grid>
+                    </widget>
+                    <spacer>
+                        <property name="name">
+                            <cstring>Spacer3</cstring>
+                        </property>
+                        <property name="orientation">
+                            <enum>Vertical</enum>
+                        </property>
+                        <property name="sizeType">
+                            <enum>Expanding</enum>
+                        </property>
+                        <property name="sizeHint">
+                            <size>
+                                <width>0</width>
+                                <height>20</height>
+                            </size>
+                        </property>
+                    </spacer>
+                </vbox>
+            </widget>
+            <widget class="QWidget">
+                <property name="name">
+                    <cstring>tab</cstring>
+                </property>
+                <attribute name="title">
+                    <string>About</string>
+                </attribute>
+                <vbox>
+                    <property name="name">
+                        <cstring>unnamed</cstring>
+                    </property>
+                    <property name="margin">
+                        <number>11</number>
+                    </property>
+                    <property name="spacing">
+                        <number>6</number>
+                    </property>
+                    <widget class="QTextEdit">
+                        <property name="name">
+                            <cstring>te_desc</cstring>
+                        </property>
+                    </widget>
+                </vbox>
+            </widget>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout68</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="BusyWidget">
+                    <property name="name">
+                        <cstring>busy</cstring>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Horizontal Spacing2</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>190</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_submit</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Publish</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_refresh</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Retrieve</string>
+                    </property>
+                    <property name="iconName" stdset="0">
+                        <string>psi/reload</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>pb_close</sender>
+        <signal>clicked()</signal>
+        <receiver>InfoUI</receiver>
+        <slot>accept()</slot>
+    </connection>
+</connections>
+<tabstops>
+    <tabstop>tabwidget</tabstop>
+    <tabstop>le_fullname</tabstop>
+    <tabstop>le_nickname</tabstop>
+    <tabstop>le_bday</tabstop>
+    <tabstop>le_phone</tabstop>
+    <tabstop>le_homepage</tabstop>
+    <tabstop>le_email</tabstop>
+    <tabstop>pb_open</tabstop>
+    <tabstop>pb_clear</tabstop>
+    <tabstop>pb_submit</tabstop>
+    <tabstop>pb_refresh</tabstop>
+    <tabstop>pb_close</tabstop>
+    <tabstop>le_orgName</tabstop>
+    <tabstop>le_orgUnit</tabstop>
+    <tabstop>le_title</tabstop>
+    <tabstop>le_role</tabstop>
+    <tabstop>le_street</tabstop>
+    <tabstop>le_ext</tabstop>
+    <tabstop>le_city</tabstop>
+    <tabstop>le_state</tabstop>
+    <tabstop>le_pcode</tabstop>
+    <tabstop>le_country</tabstop>
+    <tabstop>te_desc</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_passphrase.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_passphrase.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_passphrase.ui	(revision 38)
@@ -0,0 +1,158 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PassphraseUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>PassphraseUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>385</width>
+            <height>118</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>OpenPGP Passphrase</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout8</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout5</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>pixmapLabel1</cstring>
+                            </property>
+                            <property name="pixmap">
+                                <pixmap>image0</pixmap>
+                            </property>
+                            <property name="scaledContents">
+                                <bool>true</bool>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>spacer2</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Vertical</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>20</width>
+                                    <height>30</height>
+                                </size>
+                            </property>
+                        </spacer>
+                    </vbox>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout6</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>textLabel1</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Your passphrase is needed to use OpenPGP security.
+Please enter your passphrase below:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit">
+                            <property name="name">
+                                <cstring>le_pass</cstring>
+                            </property>
+                            <property name="echoMode">
+                                <enum>Password</enum>
+                            </property>
+                        </widget>
+                    </vbox>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout91</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer1</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>243</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_cancel</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Cancel</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_ok</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;OK</string>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<images>
+    <image name="image0">
+        <data format="XPM.GZ" length="16190">789ccd9ad7ae2d377286eff51407c3bb81c1e91c60f862e79cf3367cc1cc5e3be730f0bb9babfeea96ceb1c2e848328c2d01fad424ab58ac2a16c9f58fbf7f39dfdff9f2f77ffcf0f4ac9e07f3c544f5f8e5eff6e5e6e6e33fffeb3ffef9c3dfaaee4bfaa7cdfa2fc5dffeed87bf1d3c7f315f76ef6edd1cdc620291d9a22a2d714eec8aa6cae6ec57c155dfb6c41b73ce8b425515f135b306db9eb84c5cd37892b82a6cd5507b4bdce65d49eddd327157665547bc4fec8bac24fdbc1eb922e5fd07735e1a6a7f3be7222900fded297389ef619db82c6d9dd3f78eb82a0dd895c47562ccf793b829da0aedef98fbaa9cb311c46dd1433f7b3232e6e796c055dd507fff4edc151dda3b8cd727fb28fa5e80abbe817dd7997583ef865815a3be90afaaac267ddc1eb19eec7fcb6c61af0079ba2a6afa1e6e987de369fc8ad8a4efb45e6a93d8561df47707c4aef090af7b7055d6d07781d89779d5d377ea9f56b3c57743f62cf3f45d537bc75c62fe7a035c972dd9df9d839bbaa3f67a9b382d20c64fae4d9cec43f6368a387d66ffba02577d8dfeb7ccaa267f5039b8ce1bf297c0df937ccc8ffca3aceaba857da17fddb41db5d73bc44dd95498df02738bf998b391b13e6a6cdfa3bdc2fc9b647ff2773f809baec37a603e6da91a5a3f57802b53d3f80af6e84a85f1dceec4d4dfc07e7d9247fa9a7770e56bc413c57ba94a057fd7f7cc1afdddc4f8ae5e990de23360bd52ef86be0b016efa8eec2ba09f2e3de25f93bfa5e8722de96f1bf0d8dee1bb4df149f31505b3873c733a31ec5382eb82e3ab9e98ec2d2e99ab86d6df1e13bbd2c17fd50bb8ca2bc4e313b16f5c4ffdfdd69cab6cb2d726b8ae118f8af4abf2343ee96bc95e5531c58f67ae607fb9c7dc623efa81b84cf143fa8837e606f16128bfa5de0d8f5732b7682ff1bdae7ac4838813537f45f19c46eb6afa2e47ee2bac0ff97fd246231f28b25fd5a57821960db385bd078a87aacf7d46eb6d2438b527ffd3b0974af145f69107cc0efd35e59f4a2779d4deb6e0c6b4f45d9e82dbb6277dedeac4349ec07c4dc5f95bee82eb0cfe693370ea8ef9413f9b98ece1b28911ffb097ab5c4df942417f57fb06fe78096e5c87f586bce47d1c7f015ce7f00fb1c35cf0fcdf992bb627e58fa46dc1f9e089d923ff485adf341ae753b1054e1b10b59787c4c9bbf05d3d4f4cfdbd18b9237f11bbcc0daf3fe99746e7fe229b98ec23c9ff92b63dda0fb43e75128ff5b60be0d6f558cf75709729ec8f2bccb9423e9811a7ec5f239f6c32f71dd95b16cc1edf05d9b34ede037bca0566079ed1fe5fa7fd0dfac82b706d317f39631edb53feac4d99a1bd8be0da75147f92ea95da3663fef2ccae237b6bac87ed6acccfaf11bbb6ebb0de940fd3eaa916fb2df4f7ad817ff919b3eb693d0ce6ebbb4ed1fcedcbc4b41e03f97f931596edbd076e0af89fa47cdae44d83fd425d10175daf48beb86556184f5f4f8c78a2fd256d579c6fcd2338c517e913ae9915e4e92b70ea4ff22cd9afa9d2fe84f85a068fdf07daef9aba2ca1ffb00faeb28cf49135b851f077f50e6e5bd8db62bcba2b907f07b257d3544546fa0d94af9aae32397d9f09669fa37ec4fcbaa41fe2730ddc79cc7f46f55393f6bf1cf5c201b8d1d8cfe53e73f200fa3e31f66383f548d6c37802ebd5f739cf9ff255a3ca8ae74ffed3e8ca413f8bf174637bf81be6afdb0cf5b4cc990def8714ff8d49fa213f65e0240ff918f6b295c9a0df13b8f1185f5d82fb42d17803f96be3ca1afacd2433c7d3b002ae7486fc087d5d97439f01ed7d55613d8719b82e72b287417f9f0a5a8c47ebd366659761bf1dc0693c9c2f281fb579d5a2ffb006aeeb9ce4c92370e738def87b5f633e91ea8fb618fd2d96e03a877f0c57cc0dc65377e0be5134de40f9a12dcb9ef57b03b7b643fd01f965df6ad41314af69f356c87f6a1d9cb617fa1e293edbba7219f47d04f71af688743e689baa86fde231b84e53a4f6d0afe9c6fd4983d3f8d06f6362f2a781f27fdb969c2fc20c9ce217f9ea9299f399da63f6989fdd1db9877f523dd876bdd194af22ad77db970dec138f983533f4edeb2ea7f9c605709317a8271a703be6fb169cd61ff15332b7585f49f1d9aaa41ff62ff88beab20efba7602e711e0857cc5d4ff3d154dfb4bab5a81f34e6a7933ce4af07e6122cf6987bf4572db346be0e74be694d5dc29f24d6c37415e2c19e31f3f9442a66d3637da91e6cd3f929237dc20538f937ce4b6fcca644beaec1299fa2bef2e0713d24d6c7257de18f57ccaa47fdb809ee3be4078bf5732ad3a89f213fed37c867e29399e7eb9726a6f5d24fcc1ef59320ff4dab617a9c7f1e991dd7676760956bea1fc9de5d5ee519d9377e329719e2f383b9e7fd7c00f719fbff3d388d47f223ed6f5daaaf793cc39c4a74d2ff029ce21df9f4159cf483ff58701a1ff531f42b927d906f3f98730dffe4f15501fb29c55c699aafa3fdb24b07b802fa4a70b217eaa33df0284fde4e8cfdf799b9c57e22e9fc93bcab427e34b0775dbb02eb83f1ebae817f899ed9f6143f01f64a1b20f657a3c1e90088f30dd58f5d3bb28ccc5cdf58d8ab55b5463d49f9ac4ba707ae47d6c09de2fa7105dce7f007b9c45cf17897e0341eea8575e616f653588f543d18acff05b3827c47f1dce9b62ab0bf5e3377b88f8ae89fe295ebb763f068ef007d4dd5c23f2cfcd5a6f840bc637dad5206f721f03f97e295ec3d40df745ae5fb29f84bf276d45b91f6afceb71efa885570b22fd6f792b957188ff68f3eabb99e369b60a535f94ba4fcd6e795827f8b7b702ac7b19e1d5819d8cb537eed8b3ec7f92562bcb22e512fb96df07c47a7fe3d581706f99ff2799f0e9488c748f5565fd72a43be5c022b0ffdf4d1c806f9e7029cca05d427e4dfa99c2ac0e289b9040f94effab6d5889741339b02fa8ddf1deedb14e69bfc11f94cd27ed3cffd09f3a778e955e3b01f8973701a1ef9ae64ae9967cce9c049e3a1bd4eed918f305fd394a87f22ec954e175c1f1d805b8bfd2d90bff5ae2932dc1f5d82d3ffc1fa2e825587fac0421faf7b83fa86eabb947dc6fc43e70795f71efee528be54d11505ee1b1fc08af71ffd0a4ee3e1beb36256187fa07a51954d9d217f92bfa8aa36988f7e03a7feb08f633616f3a5f852e900c8f505adbf6a6ac7f1710eee9a82ec6f307ea38d41fdb3c16c2df9db40fea052c1c3f6ec99d9befa10ac9dc57d23ed6f6a9e7f501f0ee0b6407ed70aac2de6ef28be55afbdc1fd50049b0c2cc8df52b46718dfc33e26d90bfa435f33b6b7989fd59c1f22dabbd11fe222b86f0bdcf7413f670af48f585fdf78f88731e0341ef6f3c0ec0cee330f983d58bf834d81f5107662d2c72c33a70330b5a7fc93bca9c0fe253b66037fd727605362fe5a305760f13c31b50f347f9d372ec7faa17fdef2fab905b0e9a0cf40fea78bb6cdb15fdc8093ff223ff1f74ea3de8da7e05ec1bfe323381578c8df9b605d348837cca74ceb8ffb1f0736b9c1febac1dc5adcdf858971fe5e65eec092f6075d9906fa8b4366ee1f28de74adeb16fbc535b3c6f9c3415eaab6787f5f07a7f208f2a89e4cd1a5c03e673616f505f457a93dea874f6605fb4b8a179d96cf62bf9c315b8b7a04f3d136b7a8bfc91fb419e36980fd4daf0bd44b9067359f6f05d6c31acdfaae333be8a78f993de43bf8871de519f2efe4ad25ea454bfaa6e8b1b09f09609b617e91e66fd2790efb792479a6681be82b16c1e3f832632e2ce299fc3115cb36277d22f3fcc188e445706a8ff5391ad9219f90fd4cca6f5c9fd4e0b1fd40fbb9a9bb1ef94dec83c7ef86fcc3b4c6237e23e51fd325f9388fdf81fb16fa098c9fbc0df610d713e3bde8636487fb7db44fd542017b2c81d38683f69f13e3bc9f33f7989ffa9818fb19d59f46f5bc7f445abf940c1cd7dbc760db42be5860e6fea69e18f5f323b302478cef3a8bfdc52f83ade6f69713e3be80e2c57895f3faef820ddfe78a03b0ed91efdc19b371f04fd23779478e7a5e1d31a71d83dabf331b87f3a667f6902f299ee7dee470ffb73931f20dd9d326ffe2fd2e803bdefffc2bb86f4be87b03b68ec77f66f60ef76d176097e37ba0f549c7418ff36464ee2bf88b7b038ffde50a38f5c77b28e5cbb9b7e07bc0f7b6cfe17fe18cb9e0f1af4746fc8796b9cc512f40df368d8ffbb0b58921ff9eb974885fda3fd3e15ac13f3dc6eb8dc77db65060577bb24fa078b1aaef73ec8737cc1af2875bb0ca72e4cf65b0ab1dada7c6fc749f4a18eaffceece1cf0ef2b4e6f75787f5d1ae71a8c728bf59a3f8bd42ae82c7ef7a7f62eaaf0e2746bdf0c0dc79f4a7fadddab1bdc9985bb0f7ccdcde633dd2e93347be407f97e21bf505e6e35c87fe361f19fd03e557ebbb71bf853f25efc57ac89eb987beea86597bd46734becb8cc17b68a0f9bb545fb23fec8035df9f9925b0332c9fe2d1a5f32ff6efc1802dbf1745aacf5ca93b8e8f1d66ae2fe50938e9037fa0fcecaa341ef69716ec3ad42bf68dd9a0bda9982dd893fd5cdd3bd67f17ac159f4f0bb0731ef741cfcc1ef688e41f2e9d4ff01eeea05f5a4d7c0f18bf1de763676037ee8f547fa5d5293cdeef0e986b8f78a4fad7f55ac15efa149c3c16f3277f71ca299ca73cd533a9f8ab512f0db04faa47e1cfc321d894f8bd41a07cec9ce1fd52435fe77b8ff715acafb735e2514bf0f87d20fd7ca6c7783a9b18f1e9990dde2b03e5779fea3bc4ab237ff365b210f6d317b0ef3cf64ff24f9faa47f60fcaffbe36fc1e680fc08eef33dd3138e947e347cab7be3105de27c3297389f1063acffa56fb12fbff3ed818bcf78457b0cd4abca7af80e70535d903e377bde5f3c33278f26ffe6e6abcdf0709b615ea5185f977deb23dc9ff7c3a30727c513ded55928fdf6bec33e7fcfb9075b0cb1bfc1e463397b8aff1940fbd3625d6c71e82bdc6f94909660ff991e23569637bcc17fa78d540bec37a7bcdbfbff177e0940e10df4fcc2dde93b467eef11ea70b66050e8fcc9e7f9f82f5f1b6c2ef25c21bb3c27bfa703432ee27864f668bf7eca026a6f889336657e3bee78199df07c3c9c82c0ff6f5aec0fbb73f602ef1fe6fc5c4b81fbc67aec0c3d6c4b8af657bb81aefd1c3257383f1ccd5c4b06f3b728bdf4368e616bf3730dbcc1df41ddcc498cf3bf3f87b9fc3915bf8d3e7c4a89fd8fe4ef3ef51d6980dde1f633b31d9d7aa8931de33b3853d878591a19fb99e18f5503932eeefe3c1c488bf51be457e8b35b3c378766362d4cf3b1363bfe4f5752964898f46e6f709b6a7cff19ea1cf26c679f49899dfc7073f31e26d7364bc27e8f389713f6626463ef89818fef83631f6ebb17d89f6766b62dc9fdc4e8cfd6d6964dcd79a1be61afdfde3c4d83f2c730b763f327ebff43931ecb93b31ec37daa3c3fb9fb513637d2e26c6fe594c8cf976ccfc3e3c8cf6e3f7a46026c6fcc6f114bfbfe889514fde4d0c7b6d4f8cf92e328ffba39b18bf67ea26467c3f4d4cf315d3777e0f1bc71bfd756564fefd879b18fbc1c2c4d8ef9626467cb03f2703217eef47867e616162e4fb9389e12fa33c8ff71cb73e31fcf97262f8fbcec4341f6d26463db13831fc359b18f7b57e62dc271d4f8cf7fcd9c4f0273b31cecb3ff6473d3ccd07f7af6ab4a7c7fdab9ae6cfef93a71363bf9af4c5fda75a9a18e7b7ab8971de6e27c6fd713331e6f7637bdc872c4f8c7a61d21fe7413dd99fefafe3c8b83f8fc3c4a4af799d18f5d6343ecefbf67a62f8d7eac4588f7c62dc071e4d8cdf83f413e3be66d207e75d33f917f6fbb03d31ee375e26463d723e31c63b1c19f701fe7962dc87ed4d0cff9aec8bf3b55e9b18f69aec837a5a9d4d8cdfa3fcc8d4fee0f9affdfbff2c430aa9a4feeb644823ad74d2cb20a31ce44c5ea5bfeb3f4f86bc91b7f24edecb07f9289fe4b37c91aff22dfdbdcb0ff92917feb80cb92897e47292b02257e59a5c971b72536ec96db92377e59edc9707f2f08fc89047c942c72ce1449eca33792e2fe4a5cc644edf0b59ca4ad6b291c577cb686547121e642fa41042092d8cb0c2093fb611414431a47f67df27435c896bac83b811b7e24edc8b07f1289ec4b37811af53ab997813efe2e33b657c8a0592b02896c4b25811abf24cac8975b12136c5d64fdacdc4b6d811bbdf25634fec93840371288ec4b13849324ec599381717e2f2ab9699c845f15d324a512509b568442b3ad12ba9c4fcf9787e85aedcd76d955741c5df2f430d6251cdd495ba124dfab755d7ea46ddaa3b75af1ed4a37afaaaedb37a51afbf4f867a53efea437daa05b5a896929c65b59266d3a855b5a6d6d586da545bdff4d8563b6af75f95a1f6d4be3a9067ea501da96375926670aaced4b93a4e525a75a12e55a672f54d4ca872fe33de7f45866ad4fc0783bd965a68a5b536da6a970ead41473de8d9dc66fa4a5feb1b7dfb754f7da7eff5c36fcbd08ffa493feb17fdaadff4bbfed09f7a412fa65197f4b25ed1ab7a2dcd6b2e655d6fe8cd6ffa6ee96dbdf3eb32f4aeded3fbfa401fea237dac4ff4a93ed3e7fa425fea4ce7bad0e5fc27cbbad1b3f98f8be63f0ffababf914618f5cb32a431da18638d33de0413cd6066e6ca5c9b1b736beee6f632f7e6c13c9a27f36c5ed4b179356fe6fd1b191fe6d32cfcbc0c7993c65f344b66d9ac9855b366d6cd4692b069b6ccb6d9d1e766776e2f75a2aecc9ed93707e6d01ca905736c4ebe91716aceccf9cfc93017e6d264263785294d656ad398364998bf74492bacb2f327586b9df56a452dda60a31deccc5ed96b7bf3b50c7b6befecfdb732ec837db44ff6d9bed857fb66dfed87fdb40b76d12ed965bb6257ed9a5db71b76d36ed96dbb937c6a412dd95dbb67f7ed8168eda13dfa8984637b624f471a65d8337b6e2feca5cd6c6e0bb9624b5bd93a8dddd8d6ce7f4023ddfc664e9b1d679c75e93f9c4f71b2e2828b6e7033ddb82bc7fbadbb71b7eecedd7f2bc33db847f7e49edd8b7b756feedd7db84fb7e016dd925b1edbba15b7ead6602fb76e3d4959701b6e33fd6db96db7e376d3df9edb77076eefc7594d320edd913b7627eed49db97377e12e5de6d2fee60a57b284cad5ae71ad55fadc6eb98eedb538ff6198975ea4bff913f3fc913c89ffe9da8c32e4adcd534917ec9b8f7ef0337fe5af9dfc694b7fe36ffd9d68fcbd7fb0c63ffaa7e4c5b334932bffec5ffcab7ff3effec37ffa05bff8f5fa8f32fc925ff62b7e551ffb35bfee377c8a5bff933ce7b7fd8edff57b7edf1ff8437f64b7fcb13fd1993ad1b93ff567fedc5ffc52be98645cfacce7eec917bef4552a9eeaafe6d078fa695b904104157430c15a139cbe544bc1871062187e49c28f32c22c5c85eb70136ec3ddb76dc27d78088fe1293c8797f01adec27bf8089f61212c86a5b01c56c26a58fb65095fc760584ffe29c3c6ff92b1a95dd80adb6127ec86bdb01f0ec261380ac7e1c4bc86d37016cec32fdae95b193fff172e4316f250882694a10a7568421bba907260945144157534bf3ec26fc98836bae86308cb31c621cee255bc8e3791778b7817efe3c3af4bf817643cc6a7f89cacbe1d5fe26b7c8beff1237ec69fad6bbf4f465c8c4b7139ae041f57e35a5c8f1b71336ec5eddf23e1d765c49db81bf7e2be3a8e07f1509ec594f5e2713cf93de3ffa68cd37816cfd569bc8897318b792c62f97bc7ff7519b18a756cec556c63a76dec07398841fd9932063d98c10e6ef04318a25e1c8621d5e2c3d59f2763b81e6e86dbe16eb81f1e86c7e169781e5e86d7df3ffaafca781bde870fb5307c0e0bc3e2b0342c0f2bc3eaf0ab19e377cb581f3686cd616bd81e7686dd616fd8ffde39fcb28c24e560381c8e86e3e164384d7f677f858c24e57cb8182e876cc887b5a118becb6b7f4b4692520df5d00ced1f99c36fc94852baa19fc999f82b651c3ccfd44ccf7e2377ff51197fc6dfff898cfffef71ffe07cc56e742</data>
+    </image>
+</images>
+<tabstops>
+    <tabstop>le_pass</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_pgpkey.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_pgpkey.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_pgpkey.ui	(revision 38)
@@ -0,0 +1,116 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PGPKeyUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>PGPKeyUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>454</width>
+            <height>251</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Form1</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>textLabel1</cstring>
+            </property>
+            <property name="text">
+                <string>Please select the key to use for security:</string>
+            </property>
+        </widget>
+        <widget class="QListView">
+            <column>
+                <property name="text">
+                    <string>Key ID</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <column>
+                <property name="text">
+                    <string>User ID</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <property name="name">
+                <cstring>lv_keys</cstring>
+            </property>
+            <property name="allColumnsShowFocus">
+                <bool>true</bool>
+            </property>
+            <property name="resizeMode">
+                <enum>LastColumn</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout93</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer1</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>312</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_cancel</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Cancel</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_ok</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;OK</string>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_profilemanage.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_profilemanage.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_profilemanage.ui	(revision 38)
@@ -0,0 +1,175 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>ProfileManageUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>ProfileManageUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>330</width>
+            <height>254</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Manage Profiles</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout99</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QListBox">
+                    <property name="name">
+                        <cstring>lbx_profiles</cstring>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout98</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_new</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;New</string>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/addContact</string>
+                            </property>
+                        </widget>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_rename</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Rename</string>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer1</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Vertical</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>20</width>
+                                    <height>48</height>
+                                </size>
+                            </property>
+                        </spacer>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_delete</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Delete</string>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/remove</string>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer3</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Vertical</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>20</width>
+                                    <height>47</height>
+                                </size>
+                            </property>
+                        </spacer>
+                    </vbox>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>Line1</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout97</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer2</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>244</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_profilenew.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_profilenew.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_profilenew.ui	(revision 38)
@@ -0,0 +1,247 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>ProfileNewUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>ProfileNewUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>459</width>
+            <height>311</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>New Profile</string>
+    </property>
+    <hbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="PsiTextView">
+            <property name="name">
+                <cstring>te_desc</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>0</hsizetype>
+                    <vsizetype>7</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="minimumSize">
+                <size>
+                    <width>200</width>
+                    <height>0</height>
+                </size>
+            </property>
+            <property name="maximumSize">
+                <size>
+                    <width>200</width>
+                    <height>32767</height>
+                </size>
+            </property>
+            <property name="text">
+                <string>Keep your&lt;i&gt; Profile Name&lt;/i&gt; simple. It should be a single word comprised of only letters or numbers.&lt;br&gt;
+&lt;br&gt;
+The&lt;i&gt; Default Action&lt;/i&gt; is what happens when you double click a contact in your list. The choices are&lt;b&gt; Message&lt;/b&gt; (ICQ style) and&lt;b&gt; Chat&lt;/b&gt; (AIM style). You can change this later from the Options menu.&lt;br&gt;
+&lt;br&gt;
+Check the &lt;i&gt;Enable Emoticons&lt;/i&gt; checkbox if you'd like text such as &lt;b&gt;:-)&lt;/b&gt; to be turned into graphics like &lt;icon name="psi/smile"&gt;.</string>
+            </property>
+            <property name="readOnly">
+                <bool>true</bool>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout104</cstring>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>Layout1</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>0</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>TextLabel1</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Profile Name:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit">
+                            <property name="name">
+                                <cstring>le_name</cstring>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+                <widget class="QButtonGroup">
+                    <property name="name">
+                        <cstring>bg_defAct</cstring>
+                    </property>
+                    <property name="title">
+                        <string>Default Action</string>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>11</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QRadioButton">
+                            <property name="name">
+                                <cstring>rb_message</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Message</string>
+                            </property>
+                        </widget>
+                        <widget class="QRadioButton">
+                            <property name="name">
+                                <cstring>rb_chat</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Chat</string>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_useEmoticons</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Enable Emoticons</string>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer1</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Vertical</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>20</width>
+                            <height>131</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="Line">
+                    <property name="name">
+                        <cstring>Line1</cstring>
+                    </property>
+                    <property name="frameShape">
+                        <enum>HLine</enum>
+                    </property>
+                    <property name="frameShadow">
+                        <enum>Sunken</enum>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout103</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer2</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Horizontal</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>95</width>
+                                    <height>20</height>
+                                </size>
+                            </property>
+                        </spacer>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_close</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Close</string>
+                            </property>
+                        </widget>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_create</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Create</string>
+                            </property>
+                            <property name="accel">
+                                <string>Alt+C</string>
+                            </property>
+                            <property name="autoDefault">
+                                <bool>false</bool>
+                            </property>
+                            <property name="default">
+                                <bool>true</bool>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/addContact</string>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+            </vbox>
+        </widget>
+    </hbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+    <tabstop>le_name</tabstop>
+    <tabstop>rb_message</tabstop>
+    <tabstop>rb_chat</tabstop>
+    <tabstop>ck_useEmoticons</tabstop>
+    <tabstop>te_desc</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_profileopen.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_profileopen.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_profileopen.ui	(revision 38)
@@ -0,0 +1,264 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>ProfileOpenUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>ProfileOpenUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>305</width>
+            <height>197</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Open Profile</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>0</number>
+        </property>
+        <property name="spacing">
+            <number>0</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout13</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>0</number>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_left</cstring>
+                    </property>
+                    <property name="text">
+                        <string>left</string>
+                    </property>
+                </widget>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_logo</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Image goes here</string>
+                    </property>
+                </widget>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_right</cstring>
+                    </property>
+                    <property name="text">
+                        <string>right</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout111</cstring>
+            </property>
+            <vbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>11</number>
+                </property>
+                <widget class="QGroupBox">
+                    <property name="name">
+                        <cstring>gb_open</cstring>
+                    </property>
+                    <property name="title">
+                        <string>Open Profile</string>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>11</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>Layout4</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <property name="margin">
+                                    <number>0</number>
+                                </property>
+                                <property name="spacing">
+                                    <number>6</number>
+                                </property>
+                                <widget class="QLabel">
+                                    <property name="name">
+                                        <cstring>TextLabel4</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Profile:</string>
+                                    </property>
+                                </widget>
+                                <widget class="QComboBox">
+                                    <property name="name">
+                                        <cstring>cb_profile</cstring>
+                                    </property>
+                                    <property name="sizePolicy">
+                                        <sizepolicy>
+                                            <hsizetype>7</hsizetype>
+                                            <vsizetype>0</vsizetype>
+                                            <horstretch>0</horstretch>
+                                            <verstretch>0</verstretch>
+                                        </sizepolicy>
+                                    </property>
+                                </widget>
+                            </hbox>
+                        </widget>
+                        <widget class="QCheckBox">
+                            <property name="name">
+                                <cstring>ck_auto</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Automatically open on startup</string>
+                            </property>
+                        </widget>
+                    </vbox>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>Layout5</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>0</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>lb_lang</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Language:</string>
+                            </property>
+                        </widget>
+                        <widget class="QComboBox">
+                            <property name="name">
+                                <cstring>cb_lang</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>7</hsizetype>
+                                    <vsizetype>0</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+                <widget class="Line">
+                    <property name="name">
+                        <cstring>Line1</cstring>
+                    </property>
+                    <property name="frameShape">
+                        <enum>HLine</enum>
+                    </property>
+                    <property name="frameShadow">
+                        <enum>Sunken</enum>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout110</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_close</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Quit</string>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/quit</string>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer1</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Horizontal</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>59</width>
+                                    <height>20</height>
+                                </size>
+                            </property>
+                        </spacer>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_profiles</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Profiles...</string>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/profile</string>
+                            </property>
+                        </widget>
+                        <widget class="IconButton">
+                            <property name="name">
+                                <cstring>pb_open</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Open</string>
+                            </property>
+                            <property name="iconName" stdset="0">
+                                <string>psi/psi16</string>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+            </vbox>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_proxy.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_proxy.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_proxy.ui	(revision 38)
@@ -0,0 +1,233 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>ProxyUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>ProxyUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>307</width>
+            <height>174</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Proxy Profiles</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout121</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout120</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QListBox">
+                            <property name="name">
+                                <cstring>lbx_proxy</cstring>
+                            </property>
+                            <property name="sizePolicy">
+                                <sizepolicy>
+                                    <hsizetype>1</hsizetype>
+                                    <vsizetype>7</vsizetype>
+                                    <horstretch>0</horstretch>
+                                    <verstretch>0</verstretch>
+                                </sizepolicy>
+                            </property>
+                        </widget>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout119</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <widget class="IconButton">
+                                    <property name="name">
+                                        <cstring>pb_new</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>&amp;New</string>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/addContact</string>
+                                    </property>
+                                </widget>
+                                <widget class="IconButton">
+                                    <property name="name">
+                                        <cstring>pb_remove</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Rem&amp;ove</string>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/remove</string>
+                                    </property>
+                                </widget>
+                            </hbox>
+                        </widget>
+                    </vbox>
+                </widget>
+                <widget class="QGroupBox">
+                    <property name="name">
+                        <cstring>gb_prop</cstring>
+                    </property>
+                    <property name="title">
+                        <string>Properties</string>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout30</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <widget class="QLabel">
+                                    <property name="name">
+                                        <cstring>textLabel1</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Name:</string>
+                                    </property>
+                                </widget>
+                                <widget class="QLineEdit">
+                                    <property name="name">
+                                        <cstring>le_name</cstring>
+                                    </property>
+                                </widget>
+                            </hbox>
+                        </widget>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout21</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <widget class="QLabel">
+                                    <property name="name">
+                                        <cstring>textLabel2</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>Type:</string>
+                                    </property>
+                                </widget>
+                                <widget class="QComboBox">
+                                    <property name="name">
+                                        <cstring>cb_type</cstring>
+                                    </property>
+                                    <property name="sizePolicy">
+                                        <sizepolicy>
+                                            <hsizetype>7</hsizetype>
+                                            <vsizetype>0</vsizetype>
+                                            <horstretch>0</horstretch>
+                                            <verstretch>0</verstretch>
+                                        </sizepolicy>
+                                    </property>
+                                </widget>
+                            </hbox>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>lb_proxyedit</cstring>
+                            </property>
+                            <property name="text">
+                                <string>proxyedit</string>
+                            </property>
+                        </widget>
+                    </vbox>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line2</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout118</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer11</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>125</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_save</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Save</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<tabstops>
+    <tabstop>lbx_proxy</tabstop>
+    <tabstop>le_name</tabstop>
+    <tabstop>cb_type</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_search.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_search.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_search.ui	(revision 38)
@@ -0,0 +1,334 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>SearchUI</class>
+<widget class="QWidget">
+    <property name="name">
+        <cstring>SearchUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>568</width>
+            <height>422</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Search: %1</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>6</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout140</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QGroupBox">
+                    <property name="name">
+                        <cstring>gb_search</cstring>
+                    </property>
+                    <property name="maximumSize">
+                        <size>
+                            <width>256</width>
+                            <height>32767</height>
+                        </size>
+                    </property>
+                    <property name="title">
+                        <string>Search Fields</string>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>11</number>
+                        </property>
+                        <property name="spacing">
+                            <number>6</number>
+                        </property>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout139</cstring>
+                            </property>
+                            <vbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <widget class="QLabel">
+                                    <property name="name">
+                                        <cstring>lb_instructions</cstring>
+                                    </property>
+                                    <property name="frameShape">
+                                        <enum>Panel</enum>
+                                    </property>
+                                    <property name="frameShadow">
+                                        <enum>Sunken</enum>
+                                    </property>
+                                    <property name="text">
+                                        <string>Instructions go here.</string>
+                                    </property>
+                                </widget>
+                                <widget class="QLabel">
+                                    <property name="name">
+                                        <cstring>lb_form</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>form</string>
+                                    </property>
+                                </widget>
+                                <widget class="QLayoutWidget">
+                                    <property name="name">
+                                        <cstring>layout138</cstring>
+                                    </property>
+                                    <hbox>
+                                        <property name="name">
+                                            <cstring>unnamed</cstring>
+                                        </property>
+                                        <widget class="IconButton">
+                                            <property name="name">
+                                                <cstring>pb_search</cstring>
+                                            </property>
+                                            <property name="text">
+                                                <string>&amp;Search</string>
+                                            </property>
+                                            <property name="iconName" stdset="0">
+                                                <string>psi/search</string>
+                                            </property>
+                                        </widget>
+                                        <spacer>
+                                            <property name="name">
+                                                <cstring>Spacer1</cstring>
+                                            </property>
+                                            <property name="orientation">
+                                                <enum>Horizontal</enum>
+                                            </property>
+                                            <property name="sizeType">
+                                                <enum>Expanding</enum>
+                                            </property>
+                                            <property name="sizeHint">
+                                                <size>
+                                                    <width>30</width>
+                                                    <height>16</height>
+                                                </size>
+                                            </property>
+                                        </spacer>
+                                        <widget class="IconButton">
+                                            <property name="name">
+                                                <cstring>pb_stop</cstring>
+                                            </property>
+                                            <property name="text">
+                                                <string>&amp;Stop</string>
+                                            </property>
+                                            <property name="iconName" stdset="0">
+                                                <string>psi/stop</string>
+                                            </property>
+                                        </widget>
+                                    </hbox>
+                                </widget>
+                            </vbox>
+                        </widget>
+                    </vbox>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout137</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QListView">
+                            <column>
+                                <property name="text">
+                                    <string>Nickname</string>
+                                </property>
+                                <property name="clickable">
+                                    <bool>true</bool>
+                                </property>
+                                <property name="resizable">
+                                    <bool>true</bool>
+                                </property>
+                            </column>
+                            <column>
+                                <property name="text">
+                                    <string>First Name</string>
+                                </property>
+                                <property name="clickable">
+                                    <bool>true</bool>
+                                </property>
+                                <property name="resizable">
+                                    <bool>true</bool>
+                                </property>
+                            </column>
+                            <column>
+                                <property name="text">
+                                    <string>Last Name</string>
+                                </property>
+                                <property name="clickable">
+                                    <bool>true</bool>
+                                </property>
+                                <property name="resizable">
+                                    <bool>true</bool>
+                                </property>
+                            </column>
+                            <column>
+                                <property name="text">
+                                    <string>E-Mail Address</string>
+                                </property>
+                                <property name="clickable">
+                                    <bool>true</bool>
+                                </property>
+                                <property name="resizable">
+                                    <bool>true</bool>
+                                </property>
+                            </column>
+                            <column>
+                                <property name="text">
+                                    <string>Jabber ID</string>
+                                </property>
+                                <property name="clickable">
+                                    <bool>true</bool>
+                                </property>
+                                <property name="resizable">
+                                    <bool>true</bool>
+                                </property>
+                            </column>
+                            <property name="name">
+                                <cstring>lv_results</cstring>
+                            </property>
+                            <property name="allColumnsShowFocus">
+                                <bool>true</bool>
+                            </property>
+                            <property name="resizeMode">
+                                <enum>AllColumns</enum>
+                            </property>
+                        </widget>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout136</cstring>
+                            </property>
+                            <hbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <spacer>
+                                    <property name="name">
+                                        <cstring>Spacer3</cstring>
+                                    </property>
+                                    <property name="orientation">
+                                        <enum>Horizontal</enum>
+                                    </property>
+                                    <property name="sizeType">
+                                        <enum>Expanding</enum>
+                                    </property>
+                                    <property name="sizeHint">
+                                        <size>
+                                            <width>222</width>
+                                            <height>16</height>
+                                        </size>
+                                    </property>
+                                </spacer>
+                                <widget class="IconButton">
+                                    <property name="name">
+                                        <cstring>pb_add</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>&amp;Add Contact</string>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/addContact</string>
+                                    </property>
+                                </widget>
+                                <widget class="IconButton">
+                                    <property name="name">
+                                        <cstring>pb_info</cstring>
+                                    </property>
+                                    <property name="text">
+                                        <string>User &amp;Info</string>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/vCard</string>
+                                    </property>
+                                </widget>
+                            </hbox>
+                        </widget>
+                    </vbox>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout135</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="BusyWidget">
+                    <property name="name">
+                        <cstring>busy</cstring>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer2</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>485</width>
+                            <height>16</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>busywidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_services.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_services.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_services.ui	(revision 38)
@@ -0,0 +1,228 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>ServicesUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>ServicesUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>482</width>
+            <height>302</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Form1</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <property name="margin">
+            <number>11</number>
+        </property>
+        <property name="spacing">
+            <number>4</number>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout4</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>4</number>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>TextLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Server to browse:</string>
+                    </property>
+                </widget>
+                <widget class="QComboBox">
+                    <property name="name">
+                        <cstring>cb_browse</cstring>
+                    </property>
+                    <property name="sizePolicy">
+                        <sizepolicy>
+                            <hsizetype>7</hsizetype>
+                            <vsizetype>0</vsizetype>
+                            <horstretch>0</horstretch>
+                            <verstretch>0</verstretch>
+                        </sizepolicy>
+                    </property>
+                    <property name="editable">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>pb_browse</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Browse</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout6</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>4</number>
+                </property>
+                <widget class="QListBox">
+                    <property name="name">
+                        <cstring>li_services</cstring>
+                    </property>
+                </widget>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>Layout5</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="margin">
+                            <number>0</number>
+                        </property>
+                        <property name="spacing">
+                            <number>4</number>
+                        </property>
+                        <widget class="QPushButton">
+                            <property name="name">
+                                <cstring>pb_register</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Register</string>
+                            </property>
+                        </widget>
+                        <widget class="QPushButton">
+                            <property name="name">
+                                <cstring>pb_search</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Search</string>
+                            </property>
+                        </widget>
+                        <widget class="QPushButton">
+                            <property name="name">
+                                <cstring>pb_join</cstring>
+                            </property>
+                            <property name="text">
+                                <string>&amp;Join</string>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>Spacer2</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Vertical</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>0</width>
+                                    <height>80</height>
+                                </size>
+                            </property>
+                        </spacer>
+                    </vbox>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>Line1</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>Layout5</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>4</number>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>lb_busywidget</cstring>
+                    </property>
+                    <property name="text">
+                        <string>busywidget</string>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>Spacer3</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>250</width>
+                            <height>0</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<tabstops>
+    <tabstop>cb_browse</tabstop>
+    <tabstop>li_services</tabstop>
+    <tabstop>pb_browse</tabstop>
+    <tabstop>pb_register</tabstop>
+    <tabstop>pb_search</tabstop>
+    <tabstop>pb_close</tabstop>
+</tabstops>
+<layoutdefaults spacing="4" margin="11"/>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_sslcert.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_sslcert.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_sslcert.ui	(revision 38)
@@ -0,0 +1,201 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>SSLCertUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>SSLCertUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>507</width>
+            <height>369</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>SSL Cert</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout8</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLayoutWidget">
+                    <property name="name">
+                        <cstring>layout7</cstring>
+                    </property>
+                    <vbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>textLabel4</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Certificate Validation:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>lb_valid</cstring>
+                            </property>
+                            <property name="text">
+                                <string></string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>textLabel2</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Valid From:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>lb_notBefore</cstring>
+                            </property>
+                            <property name="text">
+                                <string></string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>textLabel3</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Valid Until:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>lb_notAfter</cstring>
+                            </property>
+                            <property name="text">
+                                <string></string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>textLabel1</cstring>
+                            </property>
+                            <property name="text">
+                                <string>Serial Number:</string>
+                            </property>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>lb_sn</cstring>
+                            </property>
+                            <property name="text">
+                                <string></string>
+                            </property>
+                        </widget>
+                        <spacer>
+                            <property name="name">
+                                <cstring>spacer4</cstring>
+                            </property>
+                            <property name="orientation">
+                                <enum>Vertical</enum>
+                            </property>
+                            <property name="sizeType">
+                                <enum>Expanding</enum>
+                            </property>
+                            <property name="sizeHint">
+                                <size>
+                                    <width>20</width>
+                                    <height>106</height>
+                                </size>
+                            </property>
+                        </spacer>
+                    </vbox>
+                </widget>
+                <widget class="QTextBrowser">
+                    <property name="name">
+                        <cstring>tb_cert</cstring>
+                    </property>
+                    <property name="minimumSize">
+                        <size>
+                            <width>350</width>
+                            <height>300</height>
+                        </size>
+                    </property>
+                    <property name="hScrollBarMode">
+                        <enum>AlwaysOff</enum>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                    <property name="wrapPolicy">
+                        <enum>AtWordOrDocumentBoundary</enum>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line1</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout134</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer5</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>421</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>pb_close</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<tabstops>
+    <tabstop>tb_cert</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_tip.ui
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_tip.ui	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_tip.ui	(revision 38)
@@ -0,0 +1,374 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>TipUI</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>TipUI</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>428</width>
+            <height>351</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Useful Tips</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QFrame">
+            <property name="name">
+                <cstring>frame11</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>StyledPanel</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <property name="margin">
+                    <number>0</number>
+                </property>
+                <property name="spacing">
+                    <number>0</number>
+                </property>
+                <widget class="QLayoutWidget" row="0" column="0">
+                    <property name="name">
+                        <cstring>layout48</cstring>
+                    </property>
+                    <hbox>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <property name="spacing">
+                            <number>0</number>
+                        </property>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout47</cstring>
+                            </property>
+                            <vbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <property name="spacing">
+                                    <number>0</number>
+                                </property>
+                                <widget class="IconLabel">
+                                    <property name="name">
+                                        <cstring>lb_icon</cstring>
+                                    </property>
+                                    <property name="paletteBackgroundColor">
+                                        <color>
+                                            <red>47</red>
+                                            <green>74</green>
+                                            <blue>114</blue>
+                                        </color>
+                                    </property>
+                                    <property name="iconName" stdset="0">
+                                        <string>psi/psiMac</string>
+                                    </property>
+                                </widget>
+                                <widget class="QLabel">
+                                    <property name="name">
+                                        <cstring>textLabel4</cstring>
+                                    </property>
+                                    <property name="sizePolicy">
+                                        <sizepolicy>
+                                            <hsizetype>5</hsizetype>
+                                            <vsizetype>7</vsizetype>
+                                            <horstretch>0</horstretch>
+                                            <verstretch>0</verstretch>
+                                        </sizepolicy>
+                                    </property>
+                                    <property name="paletteBackgroundColor">
+                                        <color>
+                                            <red>47</red>
+                                            <green>74</green>
+                                            <blue>114</blue>
+                                        </color>
+                                    </property>
+                                    <property name="text">
+                                        <string></string>
+                                    </property>
+                                </widget>
+                            </vbox>
+                        </widget>
+                        <widget class="QLayoutWidget">
+                            <property name="name">
+                                <cstring>layout43</cstring>
+                            </property>
+                            <vbox>
+                                <property name="name">
+                                    <cstring>unnamed</cstring>
+                                </property>
+                                <property name="spacing">
+                                    <number>0</number>
+                                </property>
+                                <widget class="QLabel">
+                                    <property name="name">
+                                        <cstring>textLabel1</cstring>
+                                    </property>
+                                    <property name="minimumSize">
+                                        <size>
+                                            <width>0</width>
+                                            <height>8</height>
+                                        </size>
+                                    </property>
+                                    <property name="maximumSize">
+                                        <size>
+                                            <width>32767</width>
+                                            <height>8</height>
+                                        </size>
+                                    </property>
+                                    <property name="paletteBackgroundColor">
+                                        <color>
+                                            <red>47</red>
+                                            <green>74</green>
+                                            <blue>114</blue>
+                                        </color>
+                                    </property>
+                                    <property name="text">
+                                        <string></string>
+                                    </property>
+                                </widget>
+                                <widget class="PsiTextView">
+                                    <property name="name">
+                                        <cstring>tv_psi</cstring>
+                                    </property>
+                                    <property name="paletteBackgroundColor">
+                                        <color>
+                                            <red>214</red>
+                                            <green>226</green>
+                                            <blue>240</blue>
+                                        </color>
+                                    </property>
+                                    <property name="frameShape">
+                                        <enum>NoFrame</enum>
+                                    </property>
+                                    <property name="frameShadow">
+                                        <enum>Sunken</enum>
+                                    </property>
+                                    <property name="lineWidth">
+                                        <number>0</number>
+                                    </property>
+                                    <property name="margin">
+                                        <number>10</number>
+                                    </property>
+                                    <property name="midLineWidth">
+                                        <number>0</number>
+                                    </property>
+                                    <property name="text">
+                                        <string></string>
+                                    </property>
+                                </widget>
+                                <widget class="QLabel">
+                                    <property name="name">
+                                        <cstring>textLabel3</cstring>
+                                    </property>
+                                    <property name="minimumSize">
+                                        <size>
+                                            <width>0</width>
+                                            <height>8</height>
+                                        </size>
+                                    </property>
+                                    <property name="maximumSize">
+                                        <size>
+                                            <width>32767</width>
+                                            <height>8</height>
+                                        </size>
+                                    </property>
+                                    <property name="paletteBackgroundColor">
+                                        <color>
+                                            <red>47</red>
+                                            <green>74</green>
+                                            <blue>114</blue>
+                                        </color>
+                                    </property>
+                                    <property name="text">
+                                        <string></string>
+                                    </property>
+                                </widget>
+                            </vbox>
+                        </widget>
+                        <widget class="QLabel">
+                            <property name="name">
+                                <cstring>textLabel2</cstring>
+                            </property>
+                            <property name="minimumSize">
+                                <size>
+                                    <width>8</width>
+                                    <height>0</height>
+                                </size>
+                            </property>
+                            <property name="maximumSize">
+                                <size>
+                                    <width>8</width>
+                                    <height>32767</height>
+                                </size>
+                            </property>
+                            <property name="paletteBackgroundColor">
+                                <color>
+                                    <red>47</red>
+                                    <green>74</green>
+                                    <blue>114</blue>
+                                </color>
+                            </property>
+                            <property name="text">
+                                <string></string>
+                            </property>
+                        </widget>
+                    </hbox>
+                </widget>
+            </grid>
+        </widget>
+        <widget class="Line">
+            <property name="name">
+                <cstring>line8</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>HLine</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout41</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QCheckBox">
+                    <property name="name">
+                        <cstring>ck_showTips</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Show tips at startup</string>
+                    </property>
+                </widget>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer32</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>40</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>iconButton19</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Previous</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+P</string>
+                    </property>
+                    <property name="iconName" stdset="0">
+                        <string>psi/arrowLeft</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>iconButton20</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Next</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+N</string>
+                    </property>
+                    <property name="iconName" stdset="0">
+                        <string>psi/arrowRight</string>
+                    </property>
+                </widget>
+                <widget class="IconButton">
+                    <property name="name">
+                        <cstring>iconButton21</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Close</string>
+                    </property>
+                    <property name="accel">
+                        <string>Alt+C</string>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+    <connection>
+        <sender>iconButton21</sender>
+        <signal>clicked()</signal>
+        <receiver>TipUI</receiver>
+        <slot>accept()</slot>
+    </connection>
+    <connection>
+        <sender>ck_showTips</sender>
+        <signal>toggled(bool)</signal>
+        <receiver>TipUI</receiver>
+        <slot>showTipsChanged(bool)</slot>
+    </connection>
+    <connection>
+        <sender>iconButton19</sender>
+        <signal>clicked()</signal>
+        <receiver>TipUI</receiver>
+        <slot>previous()</slot>
+    </connection>
+    <connection>
+        <sender>iconButton20</sender>
+        <signal>clicked()</signal>
+        <receiver>TipUI</receiver>
+        <slot>next()</slot>
+    </connection>
+</connections>
+<includes>
+    <include location="local" impldecl="in implementation">ui_tip.ui.h</include>
+</includes>
+<variables>
+    <variable>QStringList tips;</variable>
+</variables>
+<slots>
+    <slot access="protected">init()</slot>
+    <slot access="protected">updateTip()</slot>
+    <slot access="protected">next()</slot>
+    <slot access="protected">previous()</slot>
+    <slot access="protected">showTipsChanged( bool e )</slot>
+    <slot>addTip( QString tip, QString author )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+    <includehint>fancylabel.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+    <includehint>iconwidget.h</includehint>
+</includehints>
+</UI>
Index: psi/tags/0.10-os2-b1/src/ui_tip.ui.h
===================================================================
--- psi/tags/0.10-os2-b1/src/ui_tip.ui.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/ui_tip.ui.h	(revision 38)
@@ -0,0 +1,112 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+#include "common.h"
+
+void TipUI::init()
+{
+#ifdef Q_WS_MAC
+	lb_icon->setIcon(0);
+#endif
+
+	setCaption( CAP(caption()) );
+	//ck_showTips->hide();
+
+	// add useful tips here
+	addTip( tr("Hello! Thank you for downloading Psi!\nWe hope that you will enjoy using it as we have enjoyed making it!\n"
+		   "<br><br>If you want to download another language translation, iconset or a new version of Psi, then you need to visit the <a href=\"http://psi.affinix.com\">Psi HomePage</a>.\n"
+		   "<br><br>If you think, that you have found a bug or you just want to chat with other Psi users, then visit the <a href=\"http://psi.affinix.com/forums/\">Psi Forums</a>.\n"
+		   "<br><br><div align=\"right\"><i>the Psi Team</i></div>"), "" );
+	addTip( tr("You can select multiple emoticon iconsets, and assign them priorities using the options dialog."), "" );
+	addTip( tr("You can use multiple useful shortcuts while typing chat messages:<br>\n"
+		   "<ul>\n"
+		   "<li>Ctrl+Enter to send message</li>\n"
+		   "<li>Ctrl+M to add newline character</li>\n"
+		   "<li>Ctrl+H to display message history dialog</li>\n"
+		   "<li>Alt+S to send message</li>\n"
+		   "<li>Ctrl+U to clear edit buffer</li>\n"
+		   "<li>Ctrl+PgUp/PgDn to scroll chat view</li>\n"
+		   "</ul>"), "" );
+	addTip( tr("You can type these special commands in chat and groupchat dialogs:\n"
+		   "<ul>\n"
+		   "<li>\"/clear\" to clear chat view</li>\n"
+		   "<li>\"/me &lt;message&gt;\" '/me' is replaced by your nick</li>\n"
+		   "</ul>\n"
+		   "And these work only in groupchat dialog:\n"
+		   "<ul>\n"
+		   "<li>\"/nick &lt;new_nickname&gt;\" to change your nickname</li>\n"
+		   "</ul>"), "" );
+	addTip( tr("Did you know that you can register multiple Jabber accounts with Psi? If you like to separate your work from your personal account, you can.  If you are a power user who wants to test the latest Jabber features on an unstable server, you can do that -- without running a second client to connect to your stable server.  Just click Add in the Account Setup screen."),
+		"Hal Rottenberg" );
+	addTip( tr("Do you chat on third-party IM networks such as AIM and ICQ?  Try enabling the \"transport-specific icons\" option.  This will allow you to quickly see at a glance which network your buddy is using.  Then you can convince him to switch to Jabber. <icon name=\"psi/smile\">"),
+		"Hal Rottenberg" );
+	addTip( tr("Did you know that you can use checkboxes in Account Setup dialog to enable/disable accounts? This may be useful in the case of an account that you use rarely, so it will not clutter your roster."),
+		"Iain MacDonnell" );
+	addTip( tr("Don't like the buttons where they are?  Want a shortcut button to change your status to Away?  Check out the Configure Toolbars window, it's available through toolbars' context menu.  You can even make a toolbar that floats!"),
+		"Hal Rottenberg" );
+	addTip( tr("Did you know that Psi is one of the only Jabber clients that allows you to connect to multiple servers at the same time?  You can be known as \"mrcool@jabber.org\" to your friends, and \"John.J.Smith_the_fourth@mycompany.com\" to business associates."),
+		"Hal Rottenberg" );
+	addTip( tr("Have you converted over from Gadu-Gadu or Trillian and you miss the cool emoticons?  Fear not, we have you covered!  Check out <a href=\"http://jisp.netflint.net\">http://jisp.netflint.net</a> for tons of \"Iconsets\" that can be added to Psi to make it look the way you like!"),
+		"Hal Rottenberg" );
+	addTip( tr("Did you know that a middle-click (the middle mouse button on a 3-button mouse) will \"perform the default action\" on many objects within Psi?  Try middle-clicking on a contact or a popup."),
+		"Hal Rottenberg" );
+	addTip( tr("In order to add contacts from different IM networks, you need to add a corresponding agent from your Jabber server. Take a look at Psi Menu -> Service Discovery."),
+		"Philipp Droessler" );
+	addTip( tr("You can right-click on the server name in your roster to perform several different actions.  You can change status, modify account settings, perform administrative options (if you have permission), and more."),
+		"Hal Rottenberg" );
+	addTip( tr("Looking for a transport or chatroom, but your server provides nothing appropriate? Use Psi Menu -> Service Discovery to look on <i>any</i> Jabber Server for nice services by typing its domain in the address field.\n<br><br>\nNote: Some server may disable transport registration to users from different servers, but that's not common yet."),
+		"Patrick Hanft" );
+
+	// this MUST be the last tip
+	addTip( tr("This is the last tip.\n<br><br>If you want to contribute your own \"tip of the day\", please publish it on the <a href=\"http://psi.affinix.com/forums\">Psi Forums</a> (or mail it to the one of the developers), and we'll be happy to integrate it for the next release."), "" );
+
+	updateTip();
+	ck_showTips->setChecked( option.showTips );
+}
+
+
+void TipUI::updateTip()
+{
+	if ( option.tipNum < 0 )
+		option.tipNum = tips.count() - 1;
+	if ( option.tipNum >= (int)tips.count() )
+		option.tipNum = 0;
+
+	tv_psi->setText( tips[option.tipNum] );
+
+	option.tipNum++;
+}
+
+
+void TipUI::next()
+{
+	updateTip();
+}
+
+
+void TipUI::previous()
+{
+	option.tipNum -= 2;
+	updateTip();
+}
+
+
+void TipUI::showTipsChanged( bool e )
+{
+	option.showTips = e;
+}
+
+
+void TipUI::addTip( QString tip, QString author )
+{
+	QString t = tip;
+	if ( !author.isEmpty() )
+		t += "<br><br><i>" + tr("Contributed by") + " " + author + "</i>";
+
+	tips += t;
+}
Index: psi/tags/0.10-os2-b1/src/userlist.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/userlist.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/userlist.cpp	(revision 38)
@@ -0,0 +1,587 @@
+/*
+ * userlist.cpp - high-level roster
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"userlist.h"
+
+#include"im.h"
+#include"openpgp.h"
+#include"common.h"
+#include"contactview.h"
+#include"avatars.h"
+
+using namespace XMPP;
+
+static QString dot_truncate(const QString &in, int clip)
+{
+	if((int)in.length() <= clip)
+		return in;
+	QString s = in;
+	s.truncate(clip);
+	s += "...";
+	return s;
+}
+
+//----------------------------------------------------------------------------
+// UserResource
+//----------------------------------------------------------------------------
+UserResource::UserResource()
+:Resource()
+{
+}
+
+UserResource::UserResource(const Resource &r)
+{
+	setResource(r);
+}
+
+UserResource::~UserResource()
+{
+}
+
+void UserResource::setResource(const Resource &r)
+{
+	setName(r.name());
+	setStatus(r.status());
+}
+
+const QString & UserResource::versionString() const
+{
+	return v_ver;
+}
+
+const QString & UserResource::clientName() const
+{
+	return v_clientName;
+}
+
+const QString & UserResource::clientVersion() const
+{
+	return v_clientVersion;
+}
+
+const QString & UserResource::clientOS() const
+{
+	return v_clientOS;
+}
+
+void UserResource::setClient(const QString &name, const QString& version, const QString& os)
+{
+	v_clientName = name;
+	v_clientVersion = version;
+	v_clientOS = os;
+	v_ver = v_clientName + " " + v_clientVersion;
+	if ( !v_clientOS.isEmpty() )
+		v_ver += " / " + v_clientOS;
+}
+
+const QString & UserResource::publicKeyID() const
+{
+	return v_keyID;
+}
+
+int UserResource::pgpVerifyStatus() const
+{
+	return v_pgpVerifyStatus;
+}
+
+QDateTime UserResource::sigTimestamp() const
+{
+	return sigts;
+}
+
+void UserResource::setPublicKeyID(const QString &s)
+{
+	v_keyID = s;
+}
+
+void UserResource::setPGPVerifyStatus(int x)
+{
+	v_pgpVerifyStatus = x;
+}
+
+void UserResource::setSigTimestamp(const QDateTime &ts)
+{
+	sigts = ts;
+}
+
+bool operator<(const UserResource &r1, const UserResource &r2)
+{
+	return r1.priority() > r2.priority();
+}
+
+bool operator<=(const UserResource &r1, const UserResource &r2)
+{
+	return r1.priority() >= r2.priority();
+}
+
+bool operator==(const UserResource &r1, const UserResource &r2)
+{
+	return r1.priority() == r2.priority();
+}
+
+bool operator>(const UserResource &r1, const UserResource &r2)
+{
+	return r1.priority() < r2.priority();
+}
+
+bool operator>=(const UserResource &r1, const UserResource &r2)
+{
+	return r1.priority() <= r2.priority();
+}
+
+
+//----------------------------------------------------------------------------
+// UserResourceList
+//----------------------------------------------------------------------------
+UserResourceList::UserResourceList()
+:QValueList<UserResource>()
+{
+}
+
+UserResourceList::~UserResourceList()
+{
+}
+
+UserResourceList::Iterator UserResourceList::find(const QString & _find)
+{
+	for(UserResourceList::Iterator it = begin(); it != end(); ++it) {
+		if((*it).name() == _find)
+			return it;
+	}
+
+	return end();
+}
+
+UserResourceList::Iterator UserResourceList::priority()
+{
+	UserResourceList::Iterator highest = end();
+
+	for(UserResourceList::Iterator it = begin(); it != end(); ++it) {
+		if(highest == end() || (*it).priority() > (*highest).priority())
+			highest = it;
+	}
+
+	return highest;
+}
+
+UserResourceList::ConstIterator UserResourceList::find(const QString & _find) const
+{
+	for(UserResourceList::ConstIterator it = begin(); it != end(); ++it) {
+		if((*it).name() == _find)
+			return it;
+	}
+
+	return end();
+}
+
+UserResourceList::ConstIterator UserResourceList::priority() const
+{
+	UserResourceList::ConstIterator highest = end();
+
+	for(UserResourceList::ConstIterator it = begin(); it != end(); ++it) {
+		if(highest == end() || (*it).priority() > (*highest).priority())
+			highest = it;
+	}
+
+	return highest;
+}
+
+void UserResourceList::sort()
+{
+	qHeapSort(*this);
+}
+
+
+//----------------------------------------------------------------------------
+// UserListItem
+//----------------------------------------------------------------------------
+UserListItem::UserListItem(bool self)
+{
+	v_inList = false;
+	v_self = self;
+	v_private = false;
+	lastmsgtype = -1;
+	v_avatarFactory = 0;
+}
+
+UserListItem::~UserListItem()
+{
+}
+
+bool UserListItem::inList() const
+{
+	return v_inList;
+}
+
+void UserListItem::setJid(const Jid &j)
+{
+	LiveRosterItem::setJid(j);
+
+	int n = jid().full().find('@');
+	if(n == -1)
+		v_isTransport = true;
+	else
+		v_isTransport = false;
+}
+
+bool UserListItem::isTransport() const
+{
+	return v_isTransport;
+}
+
+bool UserListItem::isAvailable() const
+{
+	return !v_url.isEmpty();
+}
+
+bool UserListItem::isHidden() const
+{
+	return groups().contains(ContactView::tr("Hidden"));
+}
+
+bool UserListItem::isAway() const
+{
+	int status;
+	if(!isAvailable())
+		status = STATUS_OFFLINE;
+	else
+		status = makeSTATUS((*userResourceList().priority()).status());
+
+	if(status == STATUS_AWAY || status == STATUS_XA || status == STATUS_DND)
+		return true;
+	else
+		return false;
+}
+
+QDateTime UserListItem::lastAvailable() const
+{
+	return v_t;
+}
+
+int UserListItem::lastMessageType() const
+{
+	return lastmsgtype;
+}
+
+void UserListItem::setLastMessageType(const int mtype)
+{
+//	printf("setting message type to %i\n", mtype);
+	lastmsgtype = mtype;
+}
+
+const QString & UserListItem::presenceError() const
+{
+	return v_perr;
+}
+
+bool UserListItem::isSelf() const
+{
+	return v_self;
+}
+
+void UserListItem::setInList(bool b)
+{
+	v_inList = b;
+}
+
+void UserListItem::setLastAvailable(const QDateTime &t)
+{
+	v_t = t;
+}
+
+void UserListItem::setPresenceError(const QString &e)
+{
+	v_perr = e;
+}
+
+UserResourceList & UserListItem::userResourceList()
+{
+	return v_url;
+}
+
+UserResourceList::Iterator UserListItem::priority()
+{
+	return v_url.priority();
+}
+
+const UserResourceList & UserListItem::userResourceList() const
+{
+	return v_url;
+}
+
+UserResourceList::ConstIterator UserListItem::priority() const
+{
+	return v_url.priority();
+}
+
+QString UserListItem::makeTip(bool trim, bool doLinkify) const
+{
+	return "<qt>" + makeBareTip(trim,doLinkify) + "</qt>";
+}
+
+QString UserListItem::makeBareTip(bool trim, bool doLinkify) const
+{
+	QString str;
+
+#ifdef AVATARS
+	str += "<table cellspacing=\"0\"><tr>";
+	if (option.avatarsEnabled) {
+		QString client;
+		QString res;
+		if (!userResourceList().isEmpty()) {
+			client = (*userResourceList().priority()).clientName();
+			res = (*userResourceList().priority()).name();
+		}
+		if (avatarFactory()) {
+			QPixmap p = avatarFactory()->getAvatar(jid().withResource(res),client);
+			if (!p.isNull()) {
+				str += "<td align=\"center\">";
+				QMimeSourceFactory::defaultFactory()->setPixmap("avatar.png",p);
+				str += "<img src=\"avatar.png\">";
+				str += "</td>";
+			}
+		}
+	}
+
+	str += "<td>";
+#endif
+
+	QString nick = jidnick(jid().full(), name());
+	str += QString("<nobr>%1</nobr>").arg(expandEntities(nick));
+	if(jid().full() != nick)
+		str += QString("<br>[%1]").arg(expandEntities(jid().full()));
+
+	// subscription
+	if(!v_self)
+		str += QString("<br><nobr>") + QObject::tr("Subscription") + ": " + subscription().toString() + "</nobr>";
+
+	if(!v_keyID.isEmpty())
+		str += QString("<br><nobr>") + QObject::tr("OpenPGP") + ": " + v_keyID.right(8) + "</nobr>";
+
+	// resources
+	if(!userResourceList().isEmpty()) {
+		UserResourceList srl = userResourceList();
+		srl.sort();
+
+		for(UserResourceList::ConstIterator rit = srl.begin(); rit != srl.end(); ++rit) {
+			const UserResource &r = *rit;
+			QString name;
+			if(!r.name().isEmpty())
+				name = r.name();
+			else
+				name = QObject::tr("[blank]");
+
+			int status = makeSTATUS(r.status());
+			QString istr = "status/offline";
+			if(status == STATUS_ONLINE)
+				istr = "status/online";
+			else if(status == STATUS_AWAY)
+				istr = "status/away";
+			else if(status == STATUS_XA)
+				istr = "status/xa";
+			else if(status == STATUS_DND)
+				istr = "status/dnd";
+			else if(status == STATUS_CHAT)
+				istr = "status/chat";
+			else if(status == STATUS_INVISIBLE)
+				istr = "status/invisible"; //this shouldn't happen
+
+			QString imgTag = "icon name"; // or 'img src' if appropriate QMimeSourceFactory is installed. but mblsha noticed that QMimeSourceFactory unloads sometimes
+			QString secstr;
+			if(isSecure(r.name()))
+				secstr += QString(" <%1=\"psi/cryptoYes\">").arg(imgTag);
+			str += QString("<br><nobr>") + QString("<%1=\"%1\"> ").arg(imgTag).arg(istr) + QString("<b>%1</b> ").arg(expandEntities(name)) + QString("(%1)").arg(r.priority()) + secstr + "</nobr>";
+
+			if(!r.publicKeyID().isEmpty()) {
+				int v = r.pgpVerifyStatus();
+				if(v == OpenPGP::VerifyGood || v == OpenPGP::VerifyNoKey || v == OpenPGP::VerifyBad) {
+					if(v == OpenPGP::VerifyGood) {
+						QString d = r.sigTimestamp().toString(Qt::TextDate);
+						str += QString("<br><nobr>") + QObject::tr("Signed") + " @ " + "<font color=\"#2A993B\">" + d + "</font>";
+					}
+					else if(v == OpenPGP::VerifyNoKey) {
+						QString d = r.sigTimestamp().toString(Qt::TextDate);
+						str += QString("<br><nobr>") + QObject::tr("Signed") + " @ " + d;
+					}
+					else if(v == OpenPGP::VerifyBad) {
+						str += QString("<br><nobr>") + "<font color=\"#810000\">" + QObject::tr("Bad signature") + "</font>";
+					}
+
+					if(v_keyID != r.publicKeyID())
+						str += QString(" [%1]").arg(r.publicKeyID().right(8));
+					str += "</nobr>";
+				}
+			}
+
+			// last status
+			if(r.status().timeStamp().isValid()) {
+				QString d = r.status().timeStamp().toString(Qt::TextDate);
+				str += QString("<br><nobr>") + QObject::tr("Last Status") + " @ " + d + "</nobr>";
+			}
+
+			// gabber music
+			if(!r.status().songTitle().isEmpty()) {
+				QString s = r.status().songTitle();
+				if(trim)
+					s = dot_truncate(s, 80);
+				s = expandEntities(s);
+				str += QString("<br><nobr>") + QObject::tr("Listening to") + QString(": %1").arg(s) + "</nobr>";
+			}
+
+			// client
+			if(!r.versionString().isEmpty()) {
+				QString ver = r.versionString();
+				if(trim)
+					ver = dot_truncate(ver, 80);
+				ver = expandEntities(ver);
+				str += QString("<br><nobr>") + QObject::tr("Using") + QString(": %1").arg(ver) + "</nobr>";
+			}
+
+			// status message
+			QString s = r.status().status();
+			if(!s.isEmpty()) {
+				QString head = QObject::tr("Status Message");
+				if(trim)
+					s = plain2rich(clipStatus(s, 200, 12));
+				else
+					s = plain2rich(s);
+				if ( doLinkify )
+					s = linkify(s);
+				if( option.useEmoticons && !doLinkify )
+					s = emoticonify(s);
+				str += QString("<br><nobr><u>%1</u></nobr><br>%2").arg(head).arg(s);
+			}
+		}
+	}
+	else {
+		// last available
+		if(!lastAvailable().isNull()) {
+			QString d = lastAvailable().toString(Qt::TextDate);
+			str += QString("<br><nobr>") + QObject::tr("Last Available") + " @ " + d + "</nobr>";
+		}
+
+		// presence error
+		if(!v_perr.isEmpty()) {
+			str += QString("<br><nobr>") + QObject::tr("Presence Error") + QString(": %1").arg(expandEntities(v_perr)) + "</nobr>";
+		}
+
+		// status message
+		QString s = lastUnavailableStatus().status();
+		if(!s.isEmpty()) {
+			QString head = QObject::tr("Last Status Message");
+			if(trim)
+				s = plain2rich(clipStatus(s, 200, 12));
+			else {
+				s = plain2rich(clipStatus(s, 200, 12));
+				if ( doLinkify )
+					s = linkify(s);
+			}
+			str += QString("<br><nobr><u>%1</u></nobr><br>%2").arg(head).arg(s);
+		}
+	}
+
+#ifdef AVATARS
+	str += "</td>";
+	str += "</tr></table>";
+#endif
+
+	return str;
+}
+
+QString UserListItem::makeDesc() const
+{
+	return makeTip(false);
+}
+
+bool UserListItem::isPrivate() const
+{
+	return v_private;
+}
+
+void UserListItem::setPrivate(bool b)
+{
+	v_private = b;
+}
+
+bool UserListItem::isSecure(const QString &rname) const
+{
+	for(QStringList::ConstIterator it = secList.begin(); it != secList.end(); ++it) {
+		if(*it == rname)
+			return true;
+	}
+	return false;
+}
+
+void UserListItem::setSecure(const QString &rname, bool b)
+{
+	for(QStringList::Iterator it = secList.begin(); it != secList.end(); ++it) {
+		if(*it == rname) {
+			if(!b)
+				secList.remove(it);
+			return;
+		}
+	}
+	if(b)
+		secList.append(rname);
+}
+
+const QString & UserListItem::publicKeyID() const
+{
+	return v_keyID;
+}
+
+void UserListItem::setPublicKeyID(const QString &k)
+{
+	v_keyID = k;
+}
+
+AvatarFactory* UserListItem::avatarFactory() const
+{
+	return v_avatarFactory;
+}
+
+void UserListItem::setAvatarFactory(AvatarFactory* av)
+{
+	v_avatarFactory = av;
+}
+
+
+
+//----------------------------------------------------------------------------
+// UserList
+//----------------------------------------------------------------------------
+UserList::UserList()
+{
+}
+
+UserList::~UserList()
+{
+}
+
+UserListItem *UserList::find(const XMPP::Jid &j)
+{
+	UserListIt it(*this);
+	for(UserListItem *i; (i = it.current()); ++it) {
+		if(i->jid().compare(j))
+			return i;
+	}
+	return 0;
+}
+
Index: psi/tags/0.10-os2-b1/src/userlist.h
===================================================================
--- psi/tags/0.10-os2-b1/src/userlist.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/userlist.h	(revision 38)
@@ -0,0 +1,147 @@
+/*
+ * userlist.h - high-level roster
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef USERLIST_H
+#define USERLIST_H
+
+#include<qstring.h>
+#include<qdatetime.h>
+#include<qvaluelist.h>
+#include"im.h"
+
+class AvatarFactory;
+
+class UserResource : public XMPP::Resource
+{
+public:
+	UserResource();
+	UserResource(const XMPP::Resource &);
+	~UserResource();
+
+	void setResource(const XMPP::Resource &);
+
+	const QString& versionString() const;
+	const QString& clientVersion() const;
+	const QString& clientName() const;
+	const QString& clientOS() const;
+	void setClient(const QString& name, const QString& version, const QString& os);
+
+	const QString & publicKeyID() const;
+	int pgpVerifyStatus() const;
+	QDateTime sigTimestamp() const;
+	void setPublicKeyID(const QString &);
+	void setPGPVerifyStatus(int);
+	void setSigTimestamp(const QDateTime &);
+
+private:
+	QString v_ver, v_clientName, v_clientVersion, v_clientOS, v_keyID;
+	int v_pgpVerifyStatus;
+	QDateTime sigts;
+};
+
+bool operator<(const UserResource &r1, const UserResource &r2);
+bool operator<=(const UserResource &r1, const UserResource &r2);
+bool operator==(const UserResource &r1, const UserResource &r2);
+bool operator>(const UserResource &r1, const UserResource &r2);
+bool operator>=(const UserResource &r1, const UserResource &r2);
+
+class UserResourceList : public QValueList<UserResource>
+{
+public:
+	UserResourceList();
+	~UserResourceList();
+
+	void sort();
+
+	UserResourceList::Iterator find(const QString &);
+	UserResourceList::Iterator priority();
+
+	UserResourceList::ConstIterator find(const QString &) const;
+	UserResourceList::ConstIterator priority() const;
+};
+
+class UserListItem : public XMPP::LiveRosterItem
+{
+public:
+	UserListItem(bool self=false);
+	~UserListItem();
+
+	bool inList() const;
+	bool isTransport() const;
+	bool isAvailable() const;
+	bool isHidden() const;
+	bool isAway() const;
+	QDateTime lastAvailable() const;
+	int lastMessageType() const;
+	void setLastMessageType(const int mtype);
+	const QString & presenceError() const;
+	bool isSelf() const;
+	QString makeTip(bool trim = true, bool doLinkify = true) const;
+	QString makeBareTip(bool trim, bool doLinkify) const;
+	QString makeDesc() const;
+	bool isPrivate() const;
+
+	void setJid(const XMPP::Jid &);
+	void setInList(bool);
+	void setLastAvailable(const QDateTime &);
+	void setPresenceError(const QString &);
+	void setPrivate(bool);
+
+	UserResourceList & userResourceList();
+	UserResourceList::Iterator priority();
+	const UserResourceList & userResourceList() const;
+	UserResourceList::ConstIterator priority() const;
+
+	bool isSecure(const QString &rname) const;
+	void setSecure(const QString &rname, bool);
+
+	const QString & publicKeyID() const;
+	void setPublicKeyID(const QString &);
+	
+	AvatarFactory* avatarFactory() const;
+	void setAvatarFactory(AvatarFactory* av);
+
+private:
+	int lastmsgtype;
+	bool v_inList;
+	QDateTime v_t;
+	UserResourceList v_url;
+	QString v_perr;
+	bool v_self, v_isTransport;
+	bool v_private;
+	QStringList secList;
+	QString v_keyID;
+	AvatarFactory* v_avatarFactory;
+
+};
+
+typedef QPtrListIterator<UserListItem> UserListIt;
+
+class UserList : public QPtrList<UserListItem>
+{
+public:
+	UserList();
+	~UserList();
+
+	UserListItem *find(const XMPP::Jid &);
+};
+
+#endif
+
Index: psi/tags/0.10-os2-b1/src/varlist.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/varlist.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/varlist.cpp	(revision 38)
@@ -0,0 +1,119 @@
+/****************************************************************************
+** varlist.cpp - class for handling a list of string vars
+** Copyright (C) 2001, 2002  Justin Karneges
+**
+** 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.
+**
+****************************************************************************/
+
+#include"varlist.h"
+
+
+VarList::VarList()
+:QValueList<VarListItem>()
+{
+}
+
+QStringList VarList::varsToStringList()
+{
+	QStringList list;
+	for(VarList::Iterator it = begin(); it != end(); ++it)
+		list.append((*it).key());
+	return list;
+}
+
+QDomElement VarList::toXml(QDomDocument &doc, const QString &tagName)
+{
+	QDomElement base = doc.createElement(tagName);
+	for(VarList::Iterator it = begin(); it != end(); ++it) {
+		QDomElement tag = doc.createElement("item");
+		tag.setAttribute("name", (*it).key());
+		QDomText text = doc.createTextNode((*it).data());
+		tag.appendChild(text);
+		base.appendChild(tag);
+	}
+	return base;
+}
+
+void VarList::fromXml(const QDomElement &e)
+{
+	clear();
+
+	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+		QDomElement i = n.toElement();
+		if(i.isNull())
+			continue;
+		if(i.tagName() == "item") {
+			QString var, val;
+			var = i.attribute("name");
+			QDomText t = i.firstChild().toText();
+			if(!t.isNull())
+				val = t.data();
+			set(var, val);
+		}
+	}
+}
+
+void VarList::set(const QString &key, const QString &data)
+{
+	VarList::Iterator it = findByKey(key);
+	if(it == end()) {
+		VarListItem v;
+		v.setKey(key);
+		v.setData(data);
+		append(v);
+	}
+	else {
+		(*it).setData(data);
+	}
+}
+
+void VarList::unset(const QString &key)
+{
+	VarList::Iterator it = findByKey(key);
+	if(it == end())
+		return;
+	remove(it);
+}
+
+const QString & VarList::get(const QString &key)
+{
+	VarList::Iterator it = findByKey(key);
+	return (*it).data();
+}
+
+VarList::Iterator VarList::findByKey(const QString &key)
+{
+	VarList::Iterator it;
+	for(it = begin(); it != end(); ++it) {
+		if((*it).key() == key)
+			break;
+	}
+
+	return it;
+}
+
+VarList::Iterator VarList::findByNum(int x)
+{
+	int n = 0;
+	VarList::Iterator it;
+	for(it = begin(); it != end(); ++it) {
+		if(n >= x)
+			break;
+		++n;
+	}
+
+	return it;
+}
Index: psi/tags/0.10-os2-b1/src/varlist.h
===================================================================
--- psi/tags/0.10-os2-b1/src/varlist.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/varlist.h	(revision 38)
@@ -0,0 +1,63 @@
+/****************************************************************************
+** varlist.h - class for handling a list of string vars
+** Copyright (C) 2001, 2002  Justin Karneges
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef VARLIST_H
+#define VARLIST_H
+
+#include<qstring.h>
+#include<qstringlist.h>
+#include<qvaluelist.h>
+#include<qdom.h>
+
+
+class VarListItem
+{
+public:
+	VarListItem() { }
+
+	const QString & key() const { return v_key; }
+	const QString & data() const { return v_data; }
+
+	void setKey(const QString &s) { v_key = s; }
+	void setData(const QString &s) { v_data = s; }
+
+private:
+	QString v_key;
+	QString v_data;
+};
+
+class VarList : public QValueList<VarListItem>
+{
+public:
+	VarList();
+
+	void set(const QString &, const QString &);
+	void unset(const QString &);
+	const QString & get(const QString &);
+
+	VarList::Iterator findByKey(const QString &);
+	VarList::Iterator findByNum(int);
+
+	QStringList varsToStringList();
+	QDomElement toXml(QDomDocument &doc, const QString &tagName);
+	void fromXml(const QDomElement &);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/vcardfactory.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/vcardfactory.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/vcardfactory.cpp	(revision 38)
@@ -0,0 +1,189 @@
+/*
+ * vcardfactory.cpp - class for caching vCards
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "vcardfactory.h"
+
+#include <qobject.h>
+#include <qapplication.h>
+
+#include <qdict.h>
+#include <qmap.h>
+
+#include <qdom.h>
+#include <qfile.h>
+#include <qtextstream.h>
+
+#include "common.h" // jidEncode
+#include "profiles.h"
+
+//----------------------------------------------------------------------------
+// VCardFactory
+//----------------------------------------------------------------------------
+
+class VCardFactoryPrivate : public QObject
+{
+	Q_OBJECT
+
+private:
+	static const uint dictSize;
+	static QStringList vcardList;
+	static QDict<VCard> *vcardDict;
+
+public slots:
+	void taskFinished();
+
+signals:
+	void vcardChanged(const Jid&);
+
+public:
+	VCardFactoryPrivate();
+	~VCardFactoryPrivate();
+
+	static QDict<VCard> *vcards();
+	static void checkLimit(QString jid, VCard *vcard);
+};
+
+const uint VCardFactoryPrivate::dictSize = 5; // don't cache too many vCards at a time
+QDict<VCard> *VCardFactoryPrivate::vcardDict = 0;
+QStringList VCardFactoryPrivate::vcardList;
+
+VCardFactoryPrivate::VCardFactoryPrivate()
+: QObject(qApp, "VCardFactoryPrivate")
+{
+	vcardDict = new QDict<VCard>;
+	vcardDict->setAutoDelete(true);
+}
+
+VCardFactoryPrivate::~VCardFactoryPrivate()
+{
+	delete vcardDict;
+}
+
+QDict<VCard> *VCardFactoryPrivate::vcards()
+{
+	return vcardDict;
+}
+
+void VCardFactoryPrivate::checkLimit(QString jid, VCard *vcard)
+{
+	if ( vcardList.find(jid) != vcardList.end() ) {
+		vcardDict->remove(jid);
+		vcardList.remove(jid);
+	}
+	else if ( vcardList.size() > dictSize ) {
+		vcardDict->remove( vcardList.back() );
+		vcardList.pop_back();
+	}
+
+	vcardDict->insert(jid, vcard);
+	vcardList.push_front(jid);
+}
+
+void VCardFactoryPrivate::taskFinished()
+{
+	JT_VCard *task = (JT_VCard *)sender();
+	if ( task->success() ) {
+		Jid j = task->jid();
+
+		VCard *vcard = new VCard;
+		*vcard = task->vcard();
+		checkLimit(j.userHost(), vcard);
+
+		// save vCard to disk
+
+		// ensure that there's a vcard directory to save into
+		QDir p(pathToProfile(activeProfile));
+		QDir v(pathToProfile(activeProfile) + "/vcard");
+		if(!v.exists())
+			p.mkdir("vcard");
+
+		QFile file ( getVCardDir() + "/" + jidEncode(j.userHost()).lower() + ".xml" );
+		file.open ( IO_WriteOnly );
+		QTextStream out ( &file );
+		out.setEncoding ( QTextStream::UnicodeUTF8 );
+		QDomDocument doc;
+		doc.appendChild( vcard->toXml ( &doc ) );
+		out << doc.toString(4);
+
+		emit vcardChanged(j);
+	}
+}
+
+static VCardFactoryPrivate *vcardFactoryPrivate = 0;
+
+const VCard *VCardFactory::vcard(const Jid &j)
+{
+	if ( !vcardFactoryPrivate )
+		vcardFactoryPrivate = new VCardFactoryPrivate;
+
+	// first, try to get vCard from runtime cache
+	QDict<VCard> *vcards = VCardFactoryPrivate::vcards();
+	if ( vcards->find( j.userHost() ) )
+		return vcards->find( j.userHost() );
+
+	// then try to load from cache on disk
+	QFile file ( getVCardDir() + "/" + jidEncode(j.userHost()).lower() + ".xml" );
+	file.open (IO_ReadOnly);
+	QDomDocument doc;
+	VCard *vcard = new VCard;
+	if ( doc.setContent(&file, false) ) {
+		vcard->fromXml( doc.documentElement() );
+		VCardFactoryPrivate::checkLimit(j.userHost(), vcard);
+
+		return vcard;
+	}
+
+	delete vcard;
+	return 0;
+}
+
+void VCardFactory::setVCard(const Jid &j, const VCard &v)
+{
+	if ( !vcardFactoryPrivate )
+		vcardFactoryPrivate = new VCardFactoryPrivate;
+
+	VCard *vcard = new VCard;
+	*vcard = v;
+	VCardFactoryPrivate::checkLimit(j.userHost(), vcard);
+}
+
+JT_VCard *VCardFactory::getVCard(const Jid &jid, Task *rootTask, const QObject *obj, const char *slot, bool cacheVCard)
+{
+	if ( !vcardFactoryPrivate )
+		vcardFactoryPrivate = new VCardFactoryPrivate;
+
+	JT_VCard *task = new JT_VCard( rootTask );
+	if ( cacheVCard )
+		task->connect(task, SIGNAL(finished()), vcardFactoryPrivate, SLOT(taskFinished()));
+	task->connect(task, SIGNAL(finished()), obj, slot);
+	task->get(jid);
+	task->go(true);
+	return task;
+}
+	
+void VCardFactory::registerVCardChanged(const QObject* obj, const char* slot)
+{
+	if ( !vcardFactoryPrivate )
+		vcardFactoryPrivate = new VCardFactoryPrivate;
+	QObject::connect(vcardFactoryPrivate,SIGNAL(vcardChanged(const Jid&)),obj,slot);
+}
+
+#include"vcardfactory.moc"
+
Index: psi/tags/0.10-os2-b1/src/vcardfactory.h
===================================================================
--- psi/tags/0.10-os2-b1/src/vcardfactory.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/vcardfactory.h	(revision 38)
@@ -0,0 +1,48 @@
+/*
+ * vcardfactory.h - class for caching vCards
+ * Copyright (C) 2003  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef VCARDFACTORY_H
+#define VCARDFACTORY_H
+
+#include <qobject.h>
+
+#include "im.h"
+#include "xmpp_vcard.h"
+#include "xmpp_tasks.h"
+
+using namespace XMPP;
+
+class VCardFactory
+{
+public:
+	// call this, when you need a cached vCard
+	static const VCard *vcard(const Jid &);
+
+	// call this when you need to update vCard in cache
+	static void setVCard(const Jid &, const VCard &);
+
+	// call this when you need to retrieve fresh vCard from server (and store it in cache afterwards)
+	static JT_VCard *getVCard(const Jid &, Task *rootTask, const QObject *, const char *slot, bool cacheVCard = true);
+
+	// Register for updates to vcard changes
+	static void registerVCardChanged(const QObject*, const char* slot);
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/xdata_widget.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/xdata_widget.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/xdata_widget.cpp	(revision 38)
@@ -0,0 +1,515 @@
+/*
+ * xdata_widget.cpp - a class for displaying jabber:x:data forms
+ * Copyright (C) 2003-2004  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "xdata_widget.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qlistbox.h>
+#include <qlineedit.h>
+#include <qptrlist.h>
+#include <qobjectlist.h>
+#include <qtooltip.h>
+#include <qtextedit.h>
+
+//----------------------------------------------------------------------------
+// XDataField
+//----------------------------------------------------------------------------
+class XDataField
+{
+public:
+	XDataField(XData::Field f)
+	{
+		_field = f;
+	}
+	virtual ~XDataField()
+	{
+	}
+
+	virtual XData::Field field() const
+	{
+		return _field;
+	}
+
+	QString labelText() const
+	{
+		QString text = _field.label();
+		if ( text.isEmpty() )
+			text = _field.var();
+		return text + ": ";
+	}
+
+	QString reqText() const
+	{
+		QString req;
+		if ( _field.required() )
+			req = "*";
+		if ( !_field.desc().isEmpty() ) {
+			if ( !req.isEmpty() )
+				req += " ";
+			req += "(?)";
+		}
+		return req;
+	}
+
+	virtual bool isValid() const
+	{
+		return field().isValid();
+	}
+
+private:
+	XData::Field _field;
+};
+
+////////////////////////////////////////
+
+class XDataField_Hidden : public XDataField
+{
+public:
+	XDataField_Hidden(XData::Field f)
+	: XDataField(f)
+	{
+	}
+};
+
+////////////////////////////////////////
+
+class XDataField_Boolean : public XDataField
+{
+public:
+	XDataField_Boolean(XData::Field f, QGridLayout *grid, int row, QWidget *parent)
+	: XDataField(f)
+	{
+		bool checked = false;
+		if ( f.value().count() ) {
+			QString s = f.value().first();
+			if ( s == "1" || s == "true" || s == "yes" )
+				checked = true;
+		}
+
+		QLabel *label = new QLabel(labelText(), parent);
+		grid->addWidget(label, row, 0);
+
+		check = new QCheckBox(parent);
+		check->setChecked(checked);
+		grid->addWidget(check, row, 1);
+
+		QLabel *req = new QLabel(reqText(), parent);
+		grid->addWidget(req, row, 2);
+
+		if ( !f.desc().isEmpty() ) {
+			QToolTip::add(label, f.desc());
+			QToolTip::add(check, f.desc());
+			QToolTip::add(req, f.desc());
+		}
+	}
+
+	XData::Field field() const
+	{
+		XData::Field f = XDataField::field();
+		QStringList val;
+		val << QString( check->isChecked() ? "1" : "0" );
+		f.setValue(val);
+		return f;
+	}
+
+private:
+	QCheckBox *check;
+};
+
+////////////////////////////////////////
+
+class XDataField_Fixed : public XDataField
+{
+public:
+	XDataField_Fixed(XData::Field f, QGridLayout *grid, int row, QWidget *parent)
+	: XDataField(f)
+	{
+		QString text;
+		QStringList val = f.value();
+		QStringList::Iterator it = val.begin();
+		for ( ; it != val.end(); ++it) {
+			if ( !text.isEmpty() )
+				text += "<br>";
+			text += *it;
+		}
+
+		QLabel *fixed = new QLabel("<qt>" + text + "</qt>", parent);
+		grid->addMultiCellWidget(fixed, row, row, 0, 2);
+
+		if ( !f.desc().isEmpty() ) {
+			QToolTip::add(fixed, f.desc());
+		}
+	}
+};
+
+////////////////////////////////////////
+
+class XDataField_TextSingle : public XDataField
+{
+public:
+	XDataField_TextSingle(XData::Field f, QGridLayout *grid, int row, QWidget *parent)
+	: XDataField(f)
+	{
+		QString text;
+		if ( f.value().count() )
+			text = f.value().first();
+
+		QLabel *label = new QLabel(labelText(), parent);
+		grid->addWidget(label, row, 0);
+
+		edit = new QLineEdit(parent);
+		edit->setText(text);
+		grid->addWidget(edit, row, 1);
+
+		QLabel *req = new QLabel(reqText(), parent);
+		grid->addWidget(req, row, 2);
+
+		if ( !f.desc().isEmpty() ) {
+			QToolTip::add(label, f.desc());
+			QToolTip::add(edit, f.desc());
+			QToolTip::add(req, f.desc());
+		}
+	}
+
+	XData::Field field() const
+	{
+		XData::Field f = XDataField::field();
+		QStringList val;
+		val << edit->text();
+		f.setValue(val);
+		return f;
+	}
+
+protected:
+	QLineEdit *edit;
+};
+
+////////////////////////////////////////
+
+class XDataField_TextPrivate : public XDataField_TextSingle
+{
+public:
+	XDataField_TextPrivate(XData::Field f, QGridLayout *grid, int row, QWidget *parent)
+	: XDataField_TextSingle(f, grid, row, parent)
+	{
+		edit->setEchoMode(QLineEdit::Password);
+	}
+};
+
+////////////////////////////////////////
+
+class XDataField_JidSingle : public XDataField_TextSingle
+{
+public:
+	XDataField_JidSingle(XData::Field f, QGridLayout *grid, int row, QWidget *parent)
+	: XDataField_TextSingle(f, grid, row, parent)
+	{
+		// TODO: add proper validation
+	}
+};
+
+////////////////////////////////////////
+
+class XDataField_ListSingle : public XDataField
+{
+public:
+	XDataField_ListSingle(XData::Field f, QGridLayout *grid, int row, QWidget *parent)
+	: XDataField(f)
+	{
+		QLabel *label = new QLabel(labelText(), parent);
+		grid->addWidget(label, row, 0);
+
+		combo = new QComboBox(parent);
+		grid->addWidget(combo, row, 1);
+		combo->setInsertionPolicy(QComboBox::NoInsertion);
+
+		QString sel;
+		if ( !f.value().isEmpty() )
+			sel = f.value().first();
+
+		XData::Field::OptionList opts = f.options();
+		XData::Field::OptionList::Iterator it = opts.begin();
+		for ( ; it != opts.end(); ++it) {
+			QString lbl = (*it).label;
+			if ( lbl.isEmpty() )
+				lbl = (*it).value;
+
+			combo->insertItem(lbl);
+			if ( (*it).value == sel )
+				combo->setCurrentText( lbl );
+		}
+
+		QLabel *req = new QLabel(reqText(), parent);
+		grid->addWidget(req, row, 2);
+
+		if ( !f.desc().isEmpty() ) {
+			QToolTip::add(label, f.desc());
+			QToolTip::add(combo, f.desc());
+			QToolTip::add(req, f.desc());
+		}
+	}
+
+	XData::Field field() const
+	{
+		QString lbl = combo->currentText();
+
+		XData::Field f = XDataField::field();
+		QStringList val;
+
+		XData::Field::OptionList opts = f.options();
+		XData::Field::OptionList::Iterator it = opts.begin();
+		for ( ; it != opts.end(); ++it) {
+			if ( (*it).label == lbl || (*it).value == lbl ) {
+				val << (*it).value;
+				break;
+			}
+		}
+
+		f.setValue(val);
+		return f;
+	}
+
+private:
+	QComboBox *combo;
+};
+
+////////////////////////////////////////
+
+class XDataField_ListMulti : public XDataField
+{
+public:
+	XDataField_ListMulti(XData::Field f, QGridLayout *grid, int row, QWidget *parent)
+	: XDataField(f)
+	{
+		QLabel *label = new QLabel(labelText(), parent);
+		grid->addWidget(label, row, 0);
+
+		list = new QListBox(parent);
+		grid->addWidget(list, row, 1);
+		list->setSelectionMode(QListBox::Multi);
+
+		QListBoxText *last = 0;
+		XData::Field::OptionList opts = f.options();
+		XData::Field::OptionList::Iterator it = opts.begin();
+		for ( ; it != opts.end(); ++it) {
+			QString lbl = (*it).label;
+			if ( lbl.isEmpty() )
+				lbl = (*it).value;
+
+			QListBoxText *item = new QListBoxText(list, lbl, last);
+
+			QStringList val = f.value();
+			QStringList::Iterator sit = val.begin();
+			for ( ; sit != val.end(); ++sit)
+				if ( (*it).label == *sit || (*it).value == *sit )
+					list->setSelected(item, true);
+
+			last = item;
+		}
+
+		QLabel *req = new QLabel(reqText(), parent);
+		grid->addWidget(req, row, 2);
+
+		if ( !f.desc().isEmpty() ) {
+			QToolTip::add(label, f.desc());
+			QToolTip::add(list, f.desc());
+			QToolTip::add(req, f.desc());
+		}
+	}
+
+	XData::Field field() const
+	{
+		XData::Field f = XDataField::field();
+		QStringList val;
+
+		QListBoxItem *item = list->firstItem();
+		while ( item ) {
+			if ( item->isSelected() ) {
+				QString lbl = item->text();
+				XData::Field::OptionList opts = f.options();
+				XData::Field::OptionList::Iterator it = opts.begin();
+				for ( ; it != opts.end(); ++it) {
+					if ( (*it).label == lbl || (*it).value == lbl ) {
+						val << (*it).value;
+						break;
+					}
+				}
+			}
+
+			item = item->next();
+		}
+
+		f.setValue(val);
+		return f;
+	}
+
+private:
+	QListBox *list;
+};
+
+////////////////////////////////////////
+
+class XDataField_TextMulti : public XDataField
+{
+public:
+	XDataField_TextMulti(XData::Field f, QGridLayout *grid, int row, QWidget *parent)
+	: XDataField(f)
+	{
+		QLabel *label = new QLabel(labelText(), parent);
+		grid->addWidget(label, row, 0);
+
+		edit = new QTextEdit(parent);
+		grid->addWidget(edit, row, 1);
+
+		QString text;
+		QStringList val = f.value();
+		QStringList::Iterator it = val.begin();
+		for ( ; it != val.end(); ++it) {
+			if ( !text.isEmpty() )
+				text += "\n";
+			text += *it;
+		}
+		edit->setText(text);
+
+		QLabel *req = new QLabel(reqText(), parent);
+		grid->addWidget(req, row, 2);
+
+		if ( !f.desc().isEmpty() ) {
+			QToolTip::add(label, f.desc());
+			QToolTip::add(edit, f.desc());
+			QToolTip::add(req, f.desc());
+		}
+	}
+
+	XData::Field field() const
+	{
+		XData::Field f = XDataField::field();
+		f.setValue( QStringList::split("\n", edit->text(), true) );
+		return f;
+	}
+
+private:
+	QTextEdit *edit;
+};
+
+////////////////////////////////////////
+
+class XDataField_JidMulti : public XDataField_TextMulti
+{
+public:
+	XDataField_JidMulti(XData::Field f, QGridLayout *grid, int row, QWidget *parent)
+	: XDataField_TextMulti(f, grid, row, parent)
+	{
+		// TODO: improve validation
+	}
+};
+
+//----------------------------------------------------------------------------
+// XDataWidget
+//----------------------------------------------------------------------------
+class XDataWidget::Private
+{
+public:
+	Private() {
+		fields.setAutoDelete(true);
+	}
+
+	typedef QPtrList<XDataField> XDataFieldList;
+	XDataFieldList fields;
+};
+
+XDataWidget::XDataWidget(QWidget *parent, const char *name)
+: QWidget(parent, name)
+{
+	d = new Private;
+}
+
+XDataWidget::~XDataWidget()
+{
+	delete d;
+}
+
+XData::FieldList XDataWidget::fields() const
+{
+	XData::FieldList f;
+
+	QPtrListIterator<XDataField> it (d->fields);
+	XDataField *field;
+	for ( ; (field = it.current()); ++it)
+		f.append( field->field() );
+
+	return f;
+}
+
+void XDataWidget::setFields(const XData::FieldList &f)
+{
+	d->fields.clear();
+
+	// delete all child widgets
+	QObjectList *objlist = queryList();
+	objlist->setAutoDelete(true);
+	delete objlist;
+
+	if ( f.count() ) {
+		QGridLayout *grid = new QGridLayout(this, 3, f.count(), 0, 3);
+
+		int row = 0;
+		XData::FieldList::ConstIterator it = f.begin();
+		for ( ; it != f.end(); ++it, ++row) {
+			XDataField *f;
+			switch ( (*it).type() ) {
+				case XData::Field::Field_Boolean:
+					f = new XDataField_Boolean(*it, grid, row, this);
+					break;
+				case XData::Field::Field_Fixed:
+					f = new XDataField_Fixed(*it, grid, row, this);
+					break;
+				case XData::Field::Field_Hidden:
+					f = new XDataField_Hidden(*it);
+					break;
+				case XData::Field::Field_JidSingle:
+					f = new XDataField_JidSingle(*it, grid, row, this);
+					break;
+				case XData::Field::Field_ListMulti:
+					f = new XDataField_ListMulti(*it, grid, row, this);
+					break;
+				case XData::Field::Field_ListSingle:
+					f = new XDataField_ListSingle(*it, grid, row, this);
+					break;
+				case XData::Field::Field_TextMulti:
+					f = new XDataField_TextMulti(*it, grid, row, this);
+					break;
+				case XData::Field::Field_JidMulti:
+					f = new XDataField_JidMulti(*it, grid, row, this);
+					break;
+				case XData::Field::Field_TextPrivate:
+					f = new XDataField_TextPrivate(*it, grid, row, this);
+					break;
+
+				default:
+					f = new XDataField_TextSingle(*it, grid, row, this);
+			}
+			d->fields.append(f);
+		}
+	}
+}
Index: psi/tags/0.10-os2-b1/src/xdata_widget.h
===================================================================
--- psi/tags/0.10-os2-b1/src/xdata_widget.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/xdata_widget.h	(revision 38)
@@ -0,0 +1,44 @@
+/*
+ * xdata_widget.h - a class for displaying jabber:x:data forms
+ * Copyright (C) 2003-2004  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef XDATAWIDGET_H
+#define XDATAWIDGET_H
+
+#include <qwidget.h>
+
+#include "xmpp_xdata.h"
+
+using namespace XMPP;
+
+class XDataWidget : public QWidget
+{
+public:
+	XDataWidget(QWidget *parent = 0, const char *name = 0);
+	~XDataWidget();
+
+	XData::FieldList fields() const;
+	void setFields(const XData::FieldList &);
+
+private:
+	class Private;
+	Private *d;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/xmlconsole.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/xmlconsole.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/xmlconsole.cpp	(revision 38)
@@ -0,0 +1,168 @@
+/*
+ * xmlconsole.cpp - dialog for interacting manually with Jabber
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include"xmlconsole.h"
+
+#include<qlayout.h>
+#include<qpushbutton.h>
+#include<qcheckbox.h>
+#include<qtextedit.h>
+#include"common.h"
+#include"psiaccount.h"
+#include"im.h"
+
+
+//----------------------------------------------------------------------------
+// XmlConsole
+//----------------------------------------------------------------------------
+XmlConsole::XmlConsole(PsiAccount *_pa, QWidget *parent, const char *name)
+:QWidget(parent, name)
+{
+	pa = _pa;
+	pa->dialogRegister(this);
+	connect(pa, SIGNAL(updatedAccount()), SLOT(updateCaption()));
+	connect(pa->client(), SIGNAL(xmlIncoming(const QString &)), SLOT(client_xmlIncoming(const QString &)));
+	connect(pa->client(), SIGNAL(xmlOutgoing(const QString &)), SLOT(client_xmlOutgoing(const QString &)));
+	updateCaption();
+
+	QVBoxLayout *vb1 = new QVBoxLayout(this, 8);
+
+	prompt = 0;
+
+	te = new QTextEdit(this);
+	te->setUndoRedoEnabled(false);
+	te->setReadOnly(true);
+	te->setTextFormat(PlainText);
+
+	te->setPaper(QBrush(Qt::black));
+	vb1->addWidget(te);
+
+	QHBoxLayout *hb1 = new QHBoxLayout(vb1);
+	ck_enable = new QCheckBox(tr("Enable"), this);
+	hb1->addWidget(ck_enable);
+	hb1->addStretch(1);
+
+	QPushButton *pb;
+	pb = new QPushButton(tr("Clear"), this);
+	pb->setMinimumWidth(80);
+	connect(pb, SIGNAL(clicked()), te, SLOT(clear()));
+	hb1->addWidget(pb);
+
+	pb = new QPushButton(tr("&XML Input..."), this);
+	pb->setMinimumWidth(80);
+	connect(pb, SIGNAL(clicked()), SLOT(insertXml()));
+	hb1->addWidget(pb);
+
+	pb = new QPushButton(tr("&Close"), this);
+	pb->setMinimumWidth(80);
+	connect(pb, SIGNAL(clicked()), SLOT(close()));
+	hb1->addWidget(pb);
+
+	resize(560,400);
+}
+
+XmlConsole::~XmlConsole()
+{
+	pa->dialogUnregister(this);
+}
+
+void XmlConsole::updateCaption()
+{
+	setCaption(pa->name() + ": " + tr("XML Console"));
+}
+
+void XmlConsole::enable()
+{
+	ck_enable->setChecked(true);
+}
+
+void XmlConsole::client_xmlIncoming(const QString &str)
+{
+	if(ck_enable->isChecked()) {
+		te->setColor(Qt::yellow);
+		te->append(str + '\n');
+	}
+}
+
+void XmlConsole::client_xmlOutgoing(const QString &str)
+{
+	if(ck_enable->isChecked()) {
+		te->setColor(Qt::red);
+		te->append(str + '\n');
+	}
+}
+
+void XmlConsole::insertXml()
+{
+	if(prompt)
+		bringToFront(prompt);
+	else {
+		prompt = new XmlPrompt(this);
+		connect(prompt, SIGNAL(textReady(const QString &)), SLOT(xml_textReady(const QString &)));
+		prompt->show();
+	}
+}
+
+void XmlConsole::xml_textReady(const QString &str)
+{
+	pa->client()->send(str);
+}
+
+
+//----------------------------------------------------------------------------
+// XmlPrompt
+//----------------------------------------------------------------------------
+XmlPrompt::XmlPrompt(QWidget *parent, const char *name)
+:QDialog(parent, name, false, WDestructiveClose)
+{
+	setCaption(tr("XML Input"));
+
+	QVBoxLayout *vb1 = new QVBoxLayout(this, 8);
+
+	te = new QTextEdit(this);
+	vb1->addWidget(te);
+
+	QHBoxLayout *hb1 = new QHBoxLayout(vb1);
+	QPushButton *pb;
+
+	pb = new QPushButton(tr("&Transmit"), this);
+	pb->setDefault(TRUE);
+	connect(pb, SIGNAL(clicked()), SLOT(doTransmit()));
+	hb1->addWidget(pb);
+	hb1->addStretch(1);
+
+	pb = new QPushButton(tr("&Close"), this);
+	connect(pb, SIGNAL(clicked()), SLOT(close()));
+	hb1->addWidget(pb);
+
+	resize(320,240);
+}
+
+XmlPrompt::~XmlPrompt()
+{
+}
+
+void XmlPrompt::doTransmit()
+{
+	QString str = te->text();
+	textReady(str);
+	close();
+}
+
Index: psi/tags/0.10-os2-b1/src/xmlconsole.h
===================================================================
--- psi/tags/0.10-os2-b1/src/xmlconsole.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/xmlconsole.h	(revision 38)
@@ -0,0 +1,72 @@
+/*
+ * xmlconsole.h - dialog for interacting manually with Jabber
+ * Copyright (C) 2001, 2002  Justin Karneges
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef XMLCONSOLE_H
+#define XMLCONSOLE_H
+
+#include<qwidget.h>
+#include<qdialog.h>
+#include<qguardedptr.h>
+
+class QTextEdit;
+class QCheckBox;
+class PsiAccount;
+class XmlPrompt;
+
+class XmlConsole : public QWidget
+{
+	Q_OBJECT
+public:
+	XmlConsole(PsiAccount *, QWidget *parent=0, const char *name=0);
+	~XmlConsole();
+	void enable();
+	
+private slots:
+	void updateCaption();
+	void insertXml();
+	void client_xmlIncoming(const QString &);
+	void client_xmlOutgoing(const QString &);
+	void xml_textReady(const QString &);
+
+private:
+	QTextEdit *te;
+	QCheckBox *ck_enable;
+	PsiAccount *pa;
+	QGuardedPtr<XmlPrompt> prompt;
+};
+
+class XmlPrompt : public QDialog
+{
+	Q_OBJECT
+public:
+	XmlPrompt(QWidget *parent=0, const char *name=0);
+	~XmlPrompt();
+
+signals:
+	void textReady(const QString &);
+
+private slots:
+	void doTransmit();
+
+private:
+	QTextEdit *te;
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/src/xmpp_xdata.cpp
===================================================================
--- psi/tags/0.10-os2-b1/src/xmpp_xdata.cpp	(revision 38)
+++ psi/tags/0.10-os2-b1/src/xmpp_xdata.cpp	(revision 38)
@@ -0,0 +1,489 @@
+/*
+ * xmpp_xdata.cpp - a class for jabber:x:data forms
+ * Copyright (C) 2003-2004  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "xmpp_xdata.h"
+
+#include <qshared.h>
+
+#include "im.h" // for JID validation
+#include "xmpp_xmlcommon.h"
+
+using namespace XMPP;
+using namespace XMLHelper;
+
+// TODO: report, item
+
+//----------------------------------------------------------------------------
+// XData::Field
+//----------------------------------------------------------------------------
+XData::Field::Field()
+{
+}
+
+XData::Field::~Field()
+{
+}
+
+QString XData::Field::desc() const
+{
+	return _desc;
+}
+
+void XData::Field::setDesc(const QString &d)
+{
+	_desc = d;
+}
+
+XData::Field::OptionList XData::Field::options() const
+{
+	return _options;
+}
+
+void XData::Field::setOptions(XData::Field::OptionList o)
+{
+	_options = o;
+}
+
+bool XData::Field::required() const
+{
+	return _required;
+}
+
+void XData::Field::setRequired(bool r)
+{
+	_required = r;
+}
+
+QString XData::Field::label() const
+{
+	return _label;
+}
+
+void XData::Field::setLabel(const QString &l)
+{
+	_label = l;
+}
+
+QString XData::Field::var() const
+{
+	return _var;
+}
+
+void XData::Field::setVar(const QString &v)
+{
+	_var = v;
+}
+
+QStringList XData::Field::value() const
+{
+	return _value;
+}
+
+void XData::Field::setValue(const QStringList &v)
+{
+	_value = v;
+}
+
+XData::Field::Type XData::Field::type() const
+{
+	return _type;
+}
+
+void XData::Field::setType(XData::Field::Type t)
+{
+	_type = t;
+}
+
+bool XData::Field::isValid() const
+{
+	if ( _required && _value.isEmpty() )
+		return false;
+
+	if ( _type == Field_Boolean ) {
+		if ( _value.count() != 1 )
+			return false;
+
+		QString str = _value.first();
+		if ( str == "0" || str == "1" || str == "true" || str == "false" || str == "yes" || str == "no" )
+			return true;
+	}
+	if ( _type == Field_TextSingle || _type == Field_TextPrivate ) {
+		if ( _value.count() == 1 )
+			return true;
+	}
+	if ( _type == Field_JidSingle ) {
+		if ( _value.count() != 1 )
+			return false;
+
+		Jid j( _value.first() );
+		return j.isValid();
+	}
+	if ( _type == Field_JidMulti ) {
+		QStringList::ConstIterator it = _value.begin();
+		bool allValid = true;
+		for ( ; it != _value.end(); ++it) {
+			Jid j(*it);
+			if ( !j.isValid() ) {
+				allValid = false;
+				break;
+			}
+		}
+		return allValid;
+	}
+
+	return false;
+}
+
+void XData::Field::fromXml(const QDomElement &e)
+{
+	if ( e.tagName() != "field" )
+		return;
+
+	_var = e.attribute("var");
+	_label = e.attribute("label");
+
+	QString type = e.attribute("type");
+	if ( type == "boolean" )
+		_type = Field_Boolean;
+	else if ( type == "fixed" )
+		_type = Field_Fixed;
+	else if ( type == "hidden" )
+		_type = Field_Hidden;
+	else if ( type == "jid-multi" )
+		_type = Field_JidMulti;
+	else if ( type == "jid-single" )
+		_type = Field_JidSingle;
+	else if ( type == "list-multi" )
+		_type = Field_ListMulti;
+	else if ( type == "list-single" )
+		_type = Field_ListSingle;
+	else if ( type == "text-multi" )
+		_type = Field_TextMulti;
+	else if ( type == "text-private" )
+		_type = Field_TextPrivate;
+	else
+		_type = Field_TextSingle;
+
+	_required = false;
+	_desc     = QString::null;
+	_options.clear();
+	_value.clear();
+
+	QDomNode n = e.firstChild();
+	for ( ; !n.isNull(); n = n.nextSibling() ) {
+		QDomElement i = n.toElement();
+		if ( i.isNull() )
+			continue;
+
+		QString tag = i.tagName();
+		if ( tag == "required" )
+			_required = true;
+		else if ( tag == "desc" )
+			_desc = i.text().simplifyWhiteSpace();
+		else if ( tag == "option" ) {
+			Option o;
+			bool found;
+			o.label = i.attribute("label");
+
+			QDomElement e = findSubTag( i, "value", &found );
+			o.value = ( found ? e.text() : QString("") );
+			_options.append(o);
+		}
+		else if ( tag == "value" ) {
+			_value.append(i.text());
+		}
+	}
+}
+
+QDomElement XData::Field::toXml(QDomDocument *doc, bool submitForm) const
+{
+	QDomElement f = doc->createElement("field");
+
+	// setting attributes...
+	if ( !_var.isEmpty() )
+		f.setAttribute("var", _var);
+	if ( !submitForm && !_label.isEmpty() )
+		f.setAttribute("label", _label);
+
+	// now we're gonna get the 'type'
+	QString type = "text-single";
+	if ( _type == Field_Boolean )
+		type = "boolean";
+	else if ( _type == Field_Fixed )
+		type = "fixed";
+	else if ( _type == Field_Hidden )
+		type = "hidden";
+	else if ( _type == Field_JidMulti )
+		type = "jid-multi";
+	else if ( _type == Field_JidSingle )
+		type = "jid-single";
+	else if ( _type == Field_ListMulti )
+		type = "list-multi";
+	else if ( _type == Field_ListSingle )
+		type = "list-single";
+	else if ( _type == Field_TextMulti )
+		type = "text-multi";
+	else if ( _type == Field_TextPrivate )
+		type = "text-private";
+
+	f.setAttribute("type", type);
+
+	// now, setting nested tags...
+	if ( !submitForm && _required )
+		f.appendChild( emptyTag(doc, "required") );
+
+	if ( !submitForm && !_desc.isEmpty() )
+		f.appendChild( textTag(doc, "desc", _desc) );
+
+	if ( !submitForm && !_options.isEmpty() ) {
+		OptionList::ConstIterator it = _options.begin();
+		for ( ; it != _options.end(); ++it) {
+			QDomElement o = doc->createElement("option");
+			o.appendChild(textTag(doc, "value", (*it).value));
+			if ( !(*it).label.isEmpty() )
+				o.setAttribute("label", (*it).label);
+			f.appendChild(o);
+		}
+	}
+
+	if ( !_value.isEmpty() ) {
+		QStringList::ConstIterator it = _value.begin();
+		for ( ; it != _value.end(); ++it) 
+			f.appendChild( textTag(doc, "value", *it) );
+	}
+
+	return f;
+}
+
+//----------------------------------------------------------------------------
+// XData
+//----------------------------------------------------------------------------
+class XData::Private : public QShared {
+public:
+	QString title, instructions;
+	XData::Type type;
+	FieldList fields;
+	QValueList<ReportField> report;
+	QValueList<ReportItem>  reportItems;
+};
+
+XData::XData()
+{
+	d = new Private;
+}
+
+XData::~XData()
+{
+	if ( d->deref() )
+		delete d;
+}
+
+XData::XData(const XData &x)
+{
+	d = x.d;
+	d->ref();
+}
+
+QString XData::title() const
+{
+	return d->title;
+}
+
+void XData::setTitle(const QString &t)
+{
+	detach();
+	d->title = t;
+}
+
+QString XData::instructions() const
+{
+	return d->instructions;
+}
+
+void XData::setInstructions(const QString &i)
+{
+	detach();
+	d->instructions = i;
+}
+
+XData::Type XData::type() const
+{
+	return d->type;
+}
+
+void XData::setType(Type t)
+{
+	detach();
+	d->type = t;
+}
+
+XData::FieldList XData::fields() const
+{
+	return d->fields;
+}
+
+void XData::setFields(const FieldList &f)
+{
+	detach();
+	d->fields = f;
+}
+
+void XData::fromXml(const QDomElement &e)
+{
+	if ( e.attribute("xmlns") != "jabber:x:data" )
+		return;
+
+	detach();
+
+	QString type = e.attribute("type");
+	if ( type == "result" )
+		d->type = Data_Result;
+	else if ( type == "submit" )
+		d->type = Data_Submit;
+	else if ( type == "cancel" )
+		d->type = Data_Cancel;
+	else
+		d->type = Data_Form;
+
+	d->title        = subTagText(e, "title");
+	d->instructions = subTagText(e, "instructions");
+
+	d->fields.clear();
+
+	QDomNode n = e.firstChild();
+	for ( ; !n.isNull(); n = n.nextSibling() ) {
+		QDomElement i = n.toElement();
+		if ( i.isNull() )
+			continue;
+
+		if ( i.tagName() == "field" ) {
+			Field f;
+			f.fromXml(i);
+			d->fields.append(f);
+		}
+		else if ( i.tagName() == "reported" ) {
+			d->report.clear();
+			d->reportItems.clear();
+
+			QDomNode nn = i.firstChild();
+			for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+				QDomElement ii = nn.toElement();
+				if ( ii.isNull() )
+					continue;
+
+				if ( ii.tagName() == "field" ) {
+					d->report.append( ReportField( ii.attribute("label"), ii.attribute("var") ) );
+				}
+			}
+		}
+		else if ( i.tagName() == "item" ) {
+			ReportItem item;
+
+			QDomNode nn = i.firstChild();
+			for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+				QDomElement ii = nn.toElement();
+				if ( ii.isNull() )
+					continue;
+
+				if ( ii.tagName() == "field" ) {
+					QString name = ii.attribute("var");
+					QString value;
+
+					bool found;
+					QDomElement e = findSubTag( ii, "value", &found );
+					if ( found )
+						value = e.text();
+
+					item[name] = value;
+				}
+			}
+
+			d->reportItems.append( item );
+		}
+	}
+}
+
+QDomElement XData::toXml(QDomDocument *doc, bool submitForm) const
+{
+	QDomElement x = doc->createElement("x");
+	x.setAttribute("xmlns", "jabber:x:data");
+
+	QString type = "form";
+	if ( d->type == Data_Result )
+		type = "result";
+	else if ( d->type == Data_Submit )
+		type = "submit";
+	else if ( d->type == Data_Cancel )
+		type = "cancel";
+
+	x.setAttribute("type", type);
+
+	if ( !submitForm && !d->title.isEmpty() )
+		x.appendChild( textTag(doc, "title", d->title) );
+	if ( !submitForm && !d->instructions.isEmpty() )
+		x.appendChild( textTag(doc, "instructions", d->instructions) );
+
+	if ( !d->fields.isEmpty() ) {
+		FieldList::ConstIterator it = d->fields.begin();
+		for ( ; it != d->fields.end(); ++it) {
+			Field f = *it;
+			if ( !(submitForm && f.var().isEmpty()) )
+				x.appendChild( f.toXml(doc, submitForm) );
+		}
+	}
+
+	return x;
+}
+
+XData &XData::operator= (const XData &from)
+{
+	if ( d->deref() )
+		delete d;
+
+	d = from.d;
+	d->ref();
+
+	return *this;
+}
+
+XData XData::copy() const
+{
+	XData foo;
+	delete foo.d;
+	foo.d = new Private( *this->d );
+
+	return foo;
+}
+
+void XData::detach()
+{
+	if ( d->count != 1 ) // only if >1 reference
+		*this = copy();
+}
+
+const QValueList<XData::ReportField> &XData::report() const
+{
+	return d->report;
+}
+
+const QValueList<XData::ReportItem> &XData::reportItems() const
+{
+	return d->reportItems;
+}
Index: psi/tags/0.10-os2-b1/src/xmpp_xdata.h
===================================================================
--- psi/tags/0.10-os2-b1/src/xmpp_xdata.h	(revision 38)
+++ psi/tags/0.10-os2-b1/src/xmpp_xdata.h	(revision 38)
@@ -0,0 +1,146 @@
+/*
+ * xmpp_xdata.h - a class for jabber:x:data forms
+ * Copyright (C) 2003-2004  Michail Pishchagin
+ *
+ * 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef XMPPXDATA_H
+#define XMPPXDATA_H
+
+#include <qvaluelist.h>
+
+#include "xmpp_tasks.h"
+
+namespace XMPP {
+
+	class XData
+	{
+	public:
+		XData();
+		XData(const XData &);
+		~XData();
+
+		QString title() const;
+		void setTitle(const QString &);
+
+		QString instructions() const;
+		void setInstructions(const QString &);
+
+		enum Type {
+			Data_Form,
+			Data_Result,
+			Data_Submit,
+			Data_Cancel
+		};
+
+		Type type() const;
+		void setType(Type);
+
+		struct ReportField {
+			ReportField() { }
+			ReportField( QString _label, QString _name ) { label = _label; name = _name; }
+			QString label;
+			QString name;
+		};
+		const QValueList<ReportField> &report() const;
+
+		typedef QMap<QString, QString> ReportItem;
+		const QValueList<ReportItem> &reportItems() const;
+
+		void fromXml(const QDomElement &);
+		QDomElement toXml(QDomDocument *, bool submitForm = true) const;
+
+	public:
+		class Field {
+		public:
+			Field();
+			~Field();
+
+			QString desc() const;
+			void setDesc(const QString &);
+
+			struct Option {
+				QString label;
+				QString value;
+			};
+
+			typedef QValueList<Option> OptionList;
+			OptionList options() const;
+			void setOptions(OptionList);
+
+			bool required() const;
+			void setRequired(bool);
+
+			QString label() const;
+			void setLabel(const QString &);
+
+			QString var() const;
+			void setVar(const QString &);
+
+			// generic value variable, because every possible Type
+			// can be converted to QStringList. Field_Single will
+			// use just one string in QStringList. Field_Boolean will
+			// use just one string, and that string will equal 0 or 1.
+			// and so on...
+			QStringList value() const;
+			void setValue(const QStringList &);
+
+			enum Type {
+				Field_Boolean,
+				Field_Fixed,
+				Field_Hidden,
+				Field_JidMulti,
+				Field_JidSingle,
+				Field_ListMulti,
+				Field_ListSingle,
+				Field_TextMulti,
+				Field_TextPrivate,
+				Field_TextSingle
+			};
+
+			Type type() const;
+			void setType(Type);
+
+			bool isValid() const;
+
+			void fromXml(const QDomElement &);
+			QDomElement toXml(QDomDocument *, bool submitForm = true) const;
+
+		private:
+			QString _desc, _label, _var;
+			QValueList<Option> _options;
+			bool _required;
+			Type _type;
+			QStringList _value;
+		};
+
+		typedef QValueList<Field> FieldList;
+
+		FieldList fields() const;
+		void setFields(const FieldList &);
+		XData &operator= (const XData &);
+		XData copy() const;
+		void detach();
+
+	private:
+		class Private;
+		Private *d;
+	};
+
+};
+
+#endif
Index: psi/tags/0.10-os2-b1/win32/psi_win32.rc
===================================================================
--- psi/tags/0.10-os2-b1/win32/psi_win32.rc	(revision 38)
+++ psi/tags/0.10-os2-b1/win32/psi_win32.rc	(revision 38)
@@ -0,0 +1,3 @@
+IDI_ICON1 ICON DISCARDABLE "app32.ico"
+IDI_ICON2 ICON DISCARDABLE "app16.ico"
+IDI_ICON3 ICON DISCARDABLE "app48.ico"
