Index: 3thparty/jmessenger/contrib/cp_expresso.sh
===================================================================
--- 3thparty/jmessenger/contrib/cp_expresso.sh	(revision 3952)
+++ 3thparty/jmessenger/contrib/cp_expresso.sh	(revision 3952)
@@ -0,0 +1,37 @@
+#!/bin/bash
+### Expresso Livre - CELEPAR ###
+# @Author : Alexandre Correia (alexandrecorreia@celepar.pr.gov.br)
+# @Date   : 17/06/2009
+# @Description : cp os applets para o workspace expresso.
+#########################################################################
+#
+# IMPORTANTE : CONFIGURE ESTE SCRIPT, CONFORME SUA NECESSIDADE
+#
+#########################################################################
+
+#########################################################################
+# Configure sua workspace #
+#########################################################################
+
+EXPRESSO_APPLET="<informe_aqui_sua_workspace>/jabberit_messenger"
+EXPRESSO_PLUGIN="<informe_aqui_sua_workspace>/expresso/jabberit_messenger/plugins"
+APPLET="<informe_aqui_sua_workspace>/java_source/dist/applet/applet.jar"
+PLUGINS="<informe_aqui_sua_workspace>/java_source/dist/applet/plugins"
+
+if [ -e $EXPRESSO_APPLET ];
+then
+	rm -rf $EXPRESSO_APPLET"/"applet.jar
+	rm -rf $EXPRESSO_PLUGIN"/"*.jar
+fi
+
+if [ -e $APPLET ];
+then
+	echo ""
+	echo "Copiando applet.jar ...."
+	cp $APPLET $EXPRESSO_APPLET
+	chmod 777 $EXPRESSO_APPLET"/"applet.jar
+
+	echo "Copiando plugins ...."
+	cp $PLUGINS"/"* $EXPRESSO_PLUGIN
+	chmod -R 777 $EXPRESSO_PLUGIN"/"*.jar
+fi
Index: 3thparty/jmessenger/contrib/remove.sh
===================================================================
--- 3thparty/jmessenger/contrib/remove.sh	(revision 3952)
+++ 3thparty/jmessenger/contrib/remove.sh	(revision 3952)
@@ -0,0 +1,35 @@
+#!/bin/bash
+### Expresso Livre - CELEPAR ###
+# @Author : Alexandre Correia (alexandrecorreia@celepar.pr.gov.br)
+# @Date   : 17/06/2009
+# @Description : remove o diretorio dist e build dentro de seu workspace java
+#########################################################################
+#
+# IMPORTANTE : CONFIGURE ESTE SCRIPT, CONFORME SUA NECESSIDADE
+#
+#########################################################################
+
+#########################################################################
+# Configure sua workspace #
+#########################################################################
+JAVA_DIST="<informe_aqui_sua_workspace>/java_source/dist"
+JAVA_BUILD="<informe_aqui_sua_workspace>/java_source/build"
+
+## Remove o diretorio dist
+if [ -e "$JAVA_DIST" ];
+then
+	rm -rf $JAVA_DIST
+else
+	echo "O diretÃ³rio $JAVA_DIST nÃ£o existe !"
+
+fi
+
+## Remove o diretorio build
+if [ -e "$JAVA_BUILD" ];
+then
+	rm -rf $JAVA_BUILD
+else
+	echo "O diretÃ³rio $JAVA_BUILD nÃ£o existe !"
+fi
+
+clear
Index: 3thparty/jmessenger/contrib/assinando_applet.sh
===================================================================
--- 3thparty/jmessenger/contrib/assinando_applet.sh	(revision 3952)
+++ 3thparty/jmessenger/contrib/assinando_applet.sh	(revision 3952)
@@ -0,0 +1,98 @@
+#!/bin/bash
+### Expresso Livre - CELEPAR ###
+# @Author : Alexandre Correia (alexandrecorreia@celepar.pr.gov.br)
+# @Date   : 17/06/2009
+# @Description : Script para assinar applets(Java) e seus plugins.
+#########################################################################
+#
+# IMPORTANTE : CONFIGURE ESTE SCRIPT, CONFORME SUA NECESSIDADE
+#
+#########################################################################
+
+#########################################################################
+# Configure sua workspace #
+#########################################################################
+WORKSPACE_DIST="<informe_aqui_sua_workspace>/java_source/dist"
+WORKSPACE_BUILD="<informe_aqui_sua_workspace>/java_source/build"
+
+#########################################################################
+# Assinando Applet - Dados da Assinatura #
+#########################################################################
+CN="Expresso Livre - Celepar"
+OU="Diser"
+_O="CELEPAR"
+_C="BR"
+DAYS="1460"
+PASSWORD_STORE="celepar"
+ALIAS_NAME="celepar"
+KEY_STORE="jarsigned/celeparIM"
+
+#########################################################################
+# Verifica a escolha do usuario #
+#########################################################################
+choice_user()
+{
+	case $1 in
+		s|S) 
+			jarsing_java_applets	
+		;;
+
+		n|N)
+			echo "Nao sera assinado os .jar"
+		;;
+
+		*)
+			echo "Opcao invalida !"
+		;;
+	esac
+}
+
+#########################################################################
+# Assina os .jar gerados #
+#########################################################################
+jarsing_java_applets()
+{
+	APPLET=$WORKSPACE_DIST"/applet/applet.jar"
+	PLUGINS=$WORKSPACE_DIST"/applet/plugins"
+
+	if [ -e $APPLET ]
+	then
+		if [ -e $KEY_STORE ]
+		then
+			echo " Assinando applet...."
+			jarsigner -keystore $KEY_STORE -storepass $PASSWORD_STORE $APPLET $ALIAS_NAME
+		
+			for i in $(ls $PLUGINS)
+			do
+				PLUGIN_FILE=$PLUGINS"/"$i
+				echo "Assinando Plugin : " $i
+				jarsigner -keystore $KEY_STORE -storepass $PASSWORD_STORE $PLUGIN_FILE $ALIAS_NAME
+			done
+		else
+			echo "ERRO no KEY_STORE"
+		fi
+	fi
+}
+
+#########################################################################
+# Verifica a existencia de um certificado
+#########################################################################
+if [ -a $KEY_STORE ]
+then
+	echo "JÃ¡ existe uma chave criada."
+	echo "Deseja assinar agora seus .jar? (S/N)"
+	read YES_NO
+	choice_user $YES_NO
+else
+	mkdir jarsigned
+	keytool -genkey -dname "cn=$CN,ou=$OU,o=$_O,c=$_C" -alias $ALIAS_NAME -keystore $KEY_STORE -storepass $PASSWORD_STORE -validity $DAYS
+	if [ -a $KEY_STORE ] 
+	then
+		echo "Chave Criada com sucesso!"
+		echo "Deseja assinar agora seus .jar? (S/N)"
+		read YES_NO
+		choice_user $YES_NO
+	else
+		echo "Problemas na criacao da chave!"
+	fi
+fi
Index: 3thparty/jmessenger/src/gpl.txt
===================================================================
--- 3thparty/jmessenger/src/gpl.txt	(revision 3952)
+++ 3thparty/jmessenger/src/gpl.txt	(revision 3952)
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: 3thparty/jmessenger/src/buildfiles/links.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/links.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/links.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/links.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/links/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/lookandfeel.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/lookandfeel.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/lookandfeel.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/lookandfeel.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/lookandfeel/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/systemtray.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/systemtray.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/systemtray.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/systemtray.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/systemtray/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/metaltheme.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/metaltheme.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/metaltheme.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/metaltheme.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/metaltheme/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/Applet.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/Applet.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/Applet.xml	(revision 3952)
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist/applet"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+	
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}"/>
+
+		<!-- Put everything in jeti.jar file -->
+		<jar jarfile="${dist}/applet.jar">
+			<fileset dir="${build}" 
+				includes="nu/fw/jeti/**" 
+				excludes="nu/fw/jeti/plugins/** nu/fw/jeti/ui/login/**" />
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/**">
+				<depth max="4" />
+			</fileset>
+			<fileset dir="${src}" 
+				includes="META-INF/services/* nu/fw/jeti/**/*.png nu/fw/jeti/**/*.cfg nu/fw/jeti/**/*.gif" 
+				excludes="nu/fw/jeti/plugins/**" />
+			<manifest>
+					<attribute name="Main-Class" value="nu.fw.jeti.backend.Start"/>
+			 </manifest>
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/autoloadgroupchat.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/autoloadgroupchat.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/autoloadgroupchat.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/autoloadgroupchat.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/autoloadgroupchat/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/titlescroller.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/titlescroller.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/titlescroller.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/titlescroller.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/titlescroller/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/Compile.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/Compile.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/Compile.xml	(revision 3952)
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="compile" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="init">
+		<!-- Create the time stamp -->
+		<tstamp/>
+		<!-- Create the build directory structure used by compile -->
+		<mkdir dir="${build}"/>
+	</target>
+	
+	 <target name="versionInfo">
+	        <propertyfile
+	            file="${build}/version.properties" comment="Build version info">
+	            <entry key="buildDate"  value="${DSTAMP}"/>
+	            <entry key="buildNum" default="0" type="int" operation="+" value="1"/>
+	        </propertyfile>
+	    	 <copy file="${build}/version.properties" tofile="${build}/nu/fw/jeti/backend/version.properties"/>
+	  </target>
+
+	<target name="compile" depends="init,versionInfo"
+        description="compile the source " >
+		<!-- Compile the java code from ${src} into ${build} -->
+		<javac srcdir="${src}" destdir="${build}"
+		debug="on"
+		debuglevel="lines,source"
+		source="1.4"
+    	includes="nu/fw/jeti/** "
+    	excludes="	nu/fw/jeti/plugins/spell/** 
+					nu/fw/jeti/plugins/searchlogs/**
+					nu/fw/jeti/plugins/ants/**
+					nu/fw/jeti/plugins/translate/**
+					nu/fw/jeti/plugins/drawing/**
+			nu/fw/jeti/plugins/jingle/**
+			nu/fw/jeti/plugins/xmpp/**
+			nu/fw/jeti/plugins/compression/**
+					nu/fw/jeti/util/JIDPrep.java
+					"/>
+	</target>
+	
+	<target name="clean" description="clean up">
+		<!-- Delete the ${build} and ${dist} directory trees -->
+		<delete dir="${build}"/>
+	</target>
+</project>
Index: 3thparty/jmessenger/src/buildfiles/groupchat.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/groupchat.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/groupchat.xml	(revision 3952)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/groupchat.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/groupchat/**" />
+			<fileset dir="${src}" includes="nu/fw/jeti/plugins/groupchat**/*.gif" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/alertwindow.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/alertwindow.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/alertwindow.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/alertwindow.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/alertwindow/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/build.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/build.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/build.xml	(revision 3952)
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src" />
+	<property name="build" location="build" />
+	<property name="dist" location="dist" />
+
+	<target name="dist" description="generate the distribution">
+		<!-- Create Jeti -->
+		<ant antfile="src/buildfiles/Jeti.xml" target="dist" />
+		<!-- Create Plugins -->
+		<ant antfile="src/buildfiles/alertwindow.xml" target="dist" />
+		<ant antfile="src/buildfiles/groupchatalerter.xml" target="dist" />
+		<ant antfile="src/buildfiles/cam.xml" target="dist" />
+		<ant antfile="src/buildfiles/emoticons.xml" target="dist" />
+		<ant antfile="src/buildfiles/fileTransfer.xml" target="dist" />
+		<ant antfile="src/buildfiles/fontsize.xml" target="dist" />
+		<ant antfile="src/buildfiles/groupchat.xml" target="dist" />
+		<ant antfile="src/buildfiles/idle.xml" target="dist" />
+		<ant antfile="src/buildfiles/keyboardflash.xml" target="dist" />
+		<ant antfile="src/buildfiles/links.xml" target="dist" />
+		<ant antfile="src/buildfiles/lookandfeel.xml" target="dist" />
+		<ant antfile="src/buildfiles/messagelog.xml" target="dist" />
+		<ant antfile="src/buildfiles/sound.xml" target="dist" />
+		<ant antfile="src/buildfiles/titleflash.xml" target="dist" />
+		<ant antfile="src/buildfiles/titlescroller.xml" target="dist" />
+		<ant antfile="src/buildfiles/windowsutils.xml" target="dist" />
+		<ant antfile="src/buildfiles/xhtml.xml" target="dist" />
+		<ant antfile="src/buildfiles/search.xml" target="dist" />
+		<ant antfile="src/buildfiles/vcard.xml" target="dist" />
+		<ant antfile="src/buildfiles/ichatavatars.xml" target="dist" />
+		<ant antfile="src/buildfiles/spell.xml" target="dist" />
+		<ant antfile="src/buildfiles/xmpp.xml" target="dist" />
+		<ant antfile="src/buildfiles/systemtray.xml" target="dist" />
+		<ant antfile="src/buildfiles/xmppuri.xml" target="dist" />
+	</target>
+
+</project>
Index: 3thparty/jmessenger/src/buildfiles/titleflash.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/titleflash.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/titleflash.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/titleflash.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/titleflash/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/vcard.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/vcard.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/vcard.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/vcard.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/vcard/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/buddyspacemaps.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/buddyspacemaps.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/buddyspacemaps.xml	(revision 3952)
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/buddyspacemaps.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/buddyspacemaps/**" />
+			<fileset dir="${src}" includes="nu/fw/jeti/plugins/buddyspacemaps/gui/*.gif" />
+			<fileset dir="${src}" includes="nu/fw/jeti/plugins/buddyspacemaps/editor/*.gif" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/xmppuri.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/xmppuri.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/xmppuri.xml	(revision 3952)
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/xmppuri.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/xmppuri/**" />
+			<manifest>
+				<attribute name="Main-Class" value="nu.fw.jeti.plugins.xmppuri.Client"/>
+			 </manifest>
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/colormessages.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/colormessages.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/colormessages.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/colormessages.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/colormessages/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/jingle.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/jingle.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/jingle.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/jingle.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/jingle/** de/javawi/jstun/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/callVoip.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/callVoip.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/callVoip.xml	(revision 3952)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/callVoip.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/callVoip/**" />
+			<fileset dir="${src}" includes="nu/fw/jeti/plugins/callVoip**/*.gif" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/idle.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/idle.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/idle.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/idle.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/idle/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/keyboardflash.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/keyboardflash.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/keyboardflash.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/keyboardflash.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/keyboardflash/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/xmpp.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/xmpp.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/xmpp.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/xmpp.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/xmpp/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/fileTransfer.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/fileTransfer.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/fileTransfer.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/filetransfer.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/filetransfer/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/messagelog.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/messagelog.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/messagelog.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/messagelog.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/messagelog/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/sound.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/sound.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/sound.xml	(revision 3952)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/sound.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/sound/**" />
+			<fileset dir="${src}" includes="nu/fw/jeti/plugins/sound**/*.WAV" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/drawing.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/drawing.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/drawing.xml	(revision 3952)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+	
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/drawing.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/drawing/**" />
+			<fileset dir="${src}" includes="nu/fw/jeti/plugins/drawing/ui/images/*.gif nu/fw/jeti/plugins/drawing/ui/images/*.png" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/Jeti.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/Jeti.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/Jeti.xml	(revision 3952)
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}"/>
+
+		<!-- Put everything in jeti.jar file -->
+		<jar jarfile="${dist}/Jeti.jar">
+			<fileset dir="${build}" 
+				includes="nu/fw/jeti/**" 
+				excludes="nu/fw/jeti/plugins/** nu/fw/jeti/applet/**" />
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/**">
+				<depth max="4" />
+			</fileset>
+			<fileset dir="${src}"
+				includes="nu/fw/jeti/**/*.png nu/fw/jeti/**/*.cfg nu/fw/jeti/**/*.gif servers.xml" 
+				excludes="nu/fw/jeti/plugins/**" />
+			<manifest>
+				<attribute name="Class-Path" value="retroweaver-rt-2.0.jar"/>
+				<attribute name="Main-Class" value="nu.fw.jeti.backend.Start"/>
+			</manifest>
+		</jar>
+	</target>
+	
+	<target name="clean">
+		<delete>
+			<fileset dir="${build}" 
+					includes="nu/fw/jeti/**" 
+					excludes="nu/fw/jeti/plugins/** nu/fw/jeti/applet/**" />
+		</delete>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/xhtml.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/xhtml.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/xhtml.xml	(revision 3952)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/xhtml.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/xhtml/**" />
+			<fileset dir="${src}" includes="nu/fw/jeti/plugins/xhtml**/*.gif" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/IBB.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/IBB.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/IBB.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/ibb.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/ibb/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/translate.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/translate.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/translate.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/translate.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/translate/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/buildAppletExpresso.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/buildAppletExpresso.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/buildAppletExpresso.xml	(revision 3952)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src" />
+	<property name="build" location="build" />
+	<property name="dist" location="dist/applet" />
+
+	<target name="dist" description="generate the distribution">
+		<!-- Create Jeti Applet-->
+		<ant antfile="src/buildfiles/Applet.xml" target="dist" />
+		<!-- Create Plugins -->
+		<ant antfile="src/buildfiles/callVoip.xml" target="dist" />
+		<ant antfile="src/buildfiles/fileTransfer.xml" target="dist" />
+		<ant antfile="src/buildfiles/xhtml.xml" target="dist" />
+	</target>
+
+</project>
Index: 3thparty/jmessenger/src/buildfiles/emoticons.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/emoticons.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/emoticons.xml	(revision 3952)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/emoticons.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/emoticons/**" />
+			<fileset dir="${src}" includes="nu/fw/jeti/plugins/emoticons/*.gif" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/cam.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/cam.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/cam.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/cam.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/cam/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/fontsize.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/fontsize.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/fontsize.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/fontsize.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/fontsize/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/search.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/search.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/search.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/search.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/search/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/buildApplet.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/buildApplet.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/buildApplet.xml	(revision 3952)
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src" />
+	<property name="build" location="build" />
+	<property name="dist" location="dist/applet" />
+
+	<target name="dist" description="generate the distribution">
+		<!-- Create Jeti Applet-->
+		<ant antfile="src/buildfiles/Applet.xml" target="dist" />
+		<!-- Create Plugins -->
+		<ant antfile="src/buildfiles/alertwindow.xml" target="dist" />
+		<ant antfile="src/buildfiles/emoticons.xml" target="dist" />
+		<ant antfile="src/buildfiles/fileTransfer.xml" target="dist" />
+		<ant antfile="src/buildfiles/groupchat.xml" target="dist" />
+		<ant antfile="src/buildfiles/groupchatalerter.xml" target="dist" />
+		<ant antfile="src/buildfiles/keyboardflash.xml" target="dist" />
+		<ant antfile="src/buildfiles/links.xml" target="dist" />
+		<ant antfile="src/buildfiles/lookandfeel.xml" target="dist" />
+		<ant antfile="src/buildfiles/search.xml" target="dist" />
+		<ant antfile="src/buildfiles/sound.xml" target="dist" />
+		<ant antfile="src/buildfiles/titlescroller.xml" target="dist" />
+		<ant antfile="src/buildfiles/xhtml.xml" target="dist" />
+		<ant antfile="src/buildfiles/vcard.xml" target="dist" />
+		<ant antfile="src/buildfiles/metaltheme.xml" target="dist" />
+		<ant antfile="src/buildfiles/xmpp.xml" target="dist" />
+		<ant antfile="src/buildfiles/xmppuri.xml" target="dist" />
+	</target>
+
+</project>
Index: 3thparty/jmessenger/src/buildfiles/openpgp.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/openpgp.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/openpgp.xml	(revision 3952)
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/openpgp.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/openpgp/**" />
+			<manifest>
+				<attribute name="Main-Class" value="nu.fw.jeti.plugins.openpgp.GenerateAndWriteKey"/>
+				<attribute name="Class-Path" value="../lib/cryptix-pki-api.jar ../lib/cryptix-jce-api.jar ../lib/cryptix-openpgp-provider.jar ../lib/cryptix-message-api.jar ../lib/cryptix-jce-provider.jar"/>
+				<!--<attribute name="Class-Path" value="lib/cryptix-pki-api.jar lib/cryptix-jce-api.jar lib/cryptix-openpgp-provider.jar lib/cryptix-message-api.jar lib/cryptix-jce-provider.jar"/>-->
+			</manifest>
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/ichatavatars.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/ichatavatars.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/ichatavatars.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/ichatavatars.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/ichatavatars/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/tts.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/tts.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/tts.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/tts.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/tts/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/groupchatalerter.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/groupchatalerter.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/groupchatalerter.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/groupchatalerter.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/groupchatalerter/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/Compile1.4.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/Compile1.4.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/Compile1.4.xml	(revision 3952)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="weave" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+		
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+	
+	 <taskdef name="retroweaver" classname="net.sourceforge.retroweaver.ant.RetroWeaverTask"/>
+ 			
+	<target name="weave" depends="compile" description="generate the distribution" >
+			  	   <retroweaver srcdir="${build}"/>
+	</target>	  
+	
+	<target name="clean" description="clean up">
+		<!-- Delete the ${build} and ${dist} directory trees -->
+		<delete dir="${build}"/>
+	</target>
+</project>
Index: 3thparty/jmessenger/src/buildfiles/windowsutils.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/windowsutils.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/windowsutils.xml	(revision 3952)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/windowsutils.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/windowsutils/**" />
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/buildfiles/spell.xml
===================================================================
--- 3thparty/jmessenger/src/buildfiles/spell.xml	(revision 3952)
+++ 3thparty/jmessenger/src/buildfiles/spell.xml	(revision 3952)
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<project name="Jeti" default="dist" basedir="../..">
+	<description>
+        simple example build file
+    </description>
+	<!-- set global properties for this build -->
+	<property name="src" location="src"/>
+	<property name="build" location="build"/>
+	<property name="dist"  location="dist"/>
+
+	<target name="compile" description="compile the source " >
+		<ant antfile="src/buildfiles/Compile.xml" target="compile"/>
+		<javac srcdir="${src}" destdir="${build}"
+		debug="on"
+		debuglevel="lines,source"
+		source="1.4"
+    	includes="nu/fw/jeti/plugins/spell/** com/swabunga/**"/>
+	</target>
+
+	<target name="dist" depends="compile"
+        description="generate the distribution" >
+		<!-- Create the distribution directory -->
+		<mkdir dir="${dist}/plugins"/>
+		<jar jarfile="${dist}/plugins/spell.jar">
+			<fileset dir="${build}" includes="nu/fw/jeti/plugins/spell/** com/swabunga/**" />
+			<fileset dir="${src}" includes="com/swabunga/**/*.properties" />
+			<manifest>
+				<attribute name="Main-Class" value="nu.fw.jeti.plugins.spell.BuildDictionaryFile"/>
+			 </manifest>
+		</jar>
+	</target>
+	
+</project>
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/Emoticons.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/Emoticons.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/Emoticons.java	(revision 3952)
@@ -0,0 +1,23 @@
+package nu.fw.jeti.plugins;
+import java.util.List;
+
+import javax.swing.text.*;
+import javax.swing.*;
+
+/**
+  * <p>Copyright: Copyright (c) 2001</p>
+  * @author E.S. de Boer
+  */
+
+public interface Emoticons
+{
+	public void insertEmoticons(List wordList);
+		
+	public void init(JTextPane txtOutput,JPanel controls,JTextPane txtInput,JPopupMenu popup,String type,JMenu menu) throws IllegalStateException;
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/Notifiers.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/Notifiers.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/Notifiers.java	(revision 3952)
@@ -0,0 +1,50 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ *
+ *	Created on 11-jan-2004
+ */
+ 
+package nu.fw.jeti.plugins;
+
+import javax.swing.JFrame;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public interface Notifiers
+{
+
+	public void init(JFrame frame, String title);
+	
+	public void setTitle(String title);
+		
+	public void start(String title);
+	
+	public void stop();
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/buttonFileTransfer.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/buttonFileTransfer.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/buttonFileTransfer.java	(revision 3952)
@@ -0,0 +1,12 @@
+package nu.fw.jeti.plugins.filetransfer;
+
+import nu.fw.jeti.jabber.JID;
+
+public class buttonFileTransfer extends Plugin
+{
+	public static void sendFile(Object Jid)
+	{
+		new SendFileWindow(getBackend(), (JID)Jid).setVisible(true);
+	}
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/SendFileWindow.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/SendFileWindow.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/SendFileWindow.java	(revision 3952)
@@ -0,0 +1,185 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+ 
+package nu.fw.jeti.plugins.filetransfer;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.text.MessageFormat;
+
+import javax.swing.*;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+import nu.fw.jeti.util.Utils;
+
+//24-0kt-2004
+public class SendFileWindow extends JFrame
+{
+	private Backend backend;
+	private JID  jid;
+    private JLabel jLabel1 = new JLabel();
+    private JTextArea txtDescription = new JTextArea(4, 30);
+    private JButton btnSelect = new JButton();
+    private JTextField txtFilename = new JTextField();
+    private JLabel jLabel2 = new JLabel();
+    private JPanel btnPanel = new JPanel();
+    private JButton btnSend = new JButton();
+    private JButton btnCancel = new JButton();
+	private File file;
+
+	public SendFileWindow(Backend backend,JID to) {
+        this(backend, to, new File(""));
+    }
+
+	public SendFileWindow(Backend backend, JID to, File file) {
+		this.backend = backend;
+        this.file = file;
+		jid = to;
+		try {
+			jbInit();
+        } catch(Exception e) {
+            e.printStackTrace();
+        }
+		pack();
+		setLocationRelativeTo(null);
+        toFront();
+		//TODO check if si supported by using disco
+	}
+
+	private void jbInit() throws Exception
+    {
+		getRootPane().setDefaultButton(btnSend);
+        setTitle(I18N.gettext("filetransfer.File_Transfer"));
+
+		txtFilename.setText(file.toString());
+
+        I18N.setTextAndMnemonic("filetransfer.Description", jLabel1);
+        jLabel1.setLabelFor(txtDescription);
+		jLabel1.setHorizontalAlignment(SwingConstants.LEFT);
+        btnSelect.setText("...");
+        btnSelect.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                btnSelect_actionPerformed(e);
+            }
+        });
+        I18N.setTextAndMnemonic("filetransfer.File_Name",jLabel2);
+        jLabel2.setLabelFor(txtFilename);
+		I18N.setTextAndMnemonic("filetransfer.Send",btnSend);
+        btnSend.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                btnSend_actionPerformed(e);
+
+            }
+        });
+        Action cancelAction = new AbstractAction(I18N.gettext("Cancel"))
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				dispose();
+			}
+		};
+		Utils.addCancelButton(this, btnCancel, cancelAction);
+		
+        getContentPane().setLayout(new GridBagLayout());
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.anchor = GridBagConstraints.LINE_START;
+        c.fill = GridBagConstraints.BOTH;
+        c.insets = new Insets(3, 5, 0, 3);
+        c.weightx = 1.0;
+
+        getContentPane().add(jLabel2, c);
+
+        c.gridwidth = 1;
+        getContentPane().add(txtFilename, c);
+
+        c.weightx = 0.0;
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        getContentPane().add(btnSelect, c);
+
+        getContentPane().add(jLabel1, c);
+
+        c.weightx = 1.0;
+        c.weighty = 1.0;
+        getContentPane().add(new JScrollPane(txtDescription), c);
+
+        c.weighty = 0.0;
+
+        btnPanel.add(btnSend, null);
+        btnPanel.add(btnCancel, null);
+        getContentPane().add(btnPanel, c);
+    }
+
+    void btnSelect_actionPerformed(ActionEvent e)
+    {
+    	
+    	JFileChooser fileChooser = Plugin.getFileChooser();
+    	String dir = Preferences.getString("filetransfer", "uploadDir",null);
+    	if(dir!=null)fileChooser.setCurrentDirectory(new File(dir));
+		int s = fileChooser.showOpenDialog(this);
+		if(s != JFileChooser.APPROVE_OPTION) {
+            return; //cancel
+        }
+		File file = fileChooser.getSelectedFile();
+		Preferences.putString("filetransfer", "uploadDir",file.getParent());
+		txtFilename.setText(file.getAbsolutePath());
+    }
+
+	void btnSend_actionPerformed(ActionEvent e)
+    {
+		file = new File(txtFilename.getText());
+        if (!file.isAbsolute()) {
+            file = new File(Plugin.getFileChooser().getCurrentDirectory(),
+                            txtFilename.getText());
+        }
+		if(!file.exists())
+		{
+			Popups.errorPopup(MessageFormat.format(I18N.gettext("filetransfer.{0}_does_not_exist"), new Object[] { txtFilename.getText() }),I18N.gettext("filetransfer.File_Error"));
+			return;
+		}
+		if(!file.canRead())
+		{
+			Popups.errorPopup(MessageFormat.format(I18N.gettext("filetransfer.{0}_is_not_readable"), new Object[] { txtFilename.getText() }),"File Error");
+			return;
+		}
+
+        new SendFileProgress(backend,jid,file,txtDescription.getText()).setVisible(true);
+        this.dispose();
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/IQSiHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/IQSiHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/IQSiHandler.java	(revision 3952)
@@ -0,0 +1,60 @@
+// Created on 20-okt-2004
+package nu.fw.jeti.plugins.filetransfer;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XData;
+import nu.fw.jeti.jabber.elements.XMPPErrorTag;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.util.Log;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class IQSiHandler extends ExtensionHandler
+{
+	private String mimeType;
+	private String id;
+	private String profile;
+	private XData form;
+	private XSiFileTransfer siprofile;
+	
+	public void startHandling(Attributes attr)
+	{
+		mimeType=attr.getValue("mime-type");
+		id=attr.getValue("id");
+		profile=attr.getValue("profile");
+		form=null;
+		siprofile=null;
+	}
+	
+	public void addExtension(Extension extension)
+	{
+		if(extension instanceof XData)
+		{
+			form = (XData)extension;
+		}
+		else if(extension instanceof XSiFileTransfer)siprofile = (XSiFileTransfer)extension;
+		else Log.notParsedXML("Si Extension not known" + extension);
+	}
+//	public void endElement(String name)
+//	{
+//		System.out.println(name);
+//		data = getText();
+//		clearCurrentChars();
+//	}
+
+	public Extension build()
+	{
+		if(!getName().equals("si"))return new XMPPErrorTag(getName(),"http://jabber.org/protocol/si");
+		return new IQSi(id,profile,mimeType,form,siprofile);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/IQSi.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/IQSi.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/IQSi.java	(revision 3952)
@@ -0,0 +1,118 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+// Created on 20-okt-2004
+package nu.fw.jeti.plugins.filetransfer;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQExtension;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.XData;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class IQSi extends Extension implements IQExtension
+{
+	private String mimeType;
+	private String id;
+	private String profile;
+	private XData form;
+	private XSiFileTransfer siprofile;
+
+	public IQSi(XData form, XSiFileTransfer siprofile)
+	{
+		this.form = form;
+		this.siprofile = siprofile;
+	}
+	
+	public IQSi(String id, String profile,String mimeType, XData form, XSiFileTransfer siprofile)
+	{
+		this.mimeType = mimeType;
+		this.id = id;
+		this.profile = profile;
+		this.form = form;
+		this.siprofile = siprofile;
+	}
+		
+	public XData getXDataForm()
+	{
+		return form;
+	}
+	
+	public String getId()
+	{
+		return id;
+	}
+	
+	public String getMimeType()
+	{
+		return mimeType;
+	}
+	
+	public String getProfile()
+	{
+		return profile;
+	}
+	
+	public XSiFileTransfer getSiprofile()
+	{
+		return siprofile;
+	}
+	
+	
+	public void execute(InfoQuery iq, Backend backend)
+	{
+		if(iq.getType().equals("set"))
+		{
+			new GetFileWindow(backend,iq);
+		}
+	}
+	
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<si xmlns='http://jabber.org/protocol/si'");
+		appendAttribute(xml,"id",id);
+		appendAttribute(xml,"mime-type",mimeType);
+		appendAttribute(xml,"profile",profile);
+		xml.append(">");
+		if(siprofile!=null)
+		{
+			siprofile.appendToXML(xml);
+		}
+		if(form!=null)
+		{
+			xml.append("<feature xmlns='http://jabber.org/protocol/feature-neg'>");
+			form.appendToXML(xml);
+			xml.append("</feature>");
+		}
+		xml.append("</si>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/StreamSend.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/StreamSend.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/StreamSend.java	(revision 3952)
@@ -0,0 +1,43 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 24-okt-2004
+ */
+ 
+package nu.fw.jeti.plugins.filetransfer;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public interface StreamSend
+{
+	long getBytes();
+	
+	void cancel();
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/XSiFileTransferHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/XSiFileTransferHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/XSiFileTransferHandler.java	(revision 3952)
@@ -0,0 +1,78 @@
+// Created on 20-okt-2004
+package nu.fw.jeti.plugins.filetransfer;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XData;
+import nu.fw.jeti.jabber.elements.XMPPError;
+import nu.fw.jeti.jabber.elements.XMPPErrorTag;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.util.Log;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class XSiFileTransferHandler extends ExtensionHandler
+{//TODO range
+	private String name;
+	private String hash;
+	private String date;
+	private String description;
+	private long size;
+	private int length;
+	private long offset;
+		
+	public void startHandling(Attributes attr)
+	{
+		name=attr.getValue("name");
+		hash=attr.getValue("hash");
+		date=attr.getValue("date");
+		try
+		{
+			size=Long.parseLong(attr.getValue("size"));
+		}
+		catch(NumberFormatException e)
+		{
+			size=0;
+			Log.xmlReceivedError("Si profile, size is not a number");
+		}
+		length=0;
+		offset=0;
+	}
+	
+	public void startElement(String name,Attributes attr)
+	{
+		if(name.equals("range"))
+		{
+			try
+			{
+				length=Integer.parseInt(attr.getValue("length"));
+				offset=Long.parseLong(attr.getValue("offset"));
+			}
+			catch(NumberFormatException e)
+			{
+				size=0;
+				Log.xmlReceivedError("Si profile, range is not a number");
+			}
+		}
+	}
+		
+	public void endElement(String name)
+	{
+		if(name.equals("desc"))description = getText();
+		clearCurrentChars();
+	}
+
+	public Extension build()
+	{
+		return new XSiFileTransfer(name,hash,date,size,description,length,offset);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/GetFileWindow.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/GetFileWindow.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/GetFileWindow.java	(revision 3952)
@@ -0,0 +1,333 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.plugins.filetransfer;
+
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.swing.*;
+
+import nu.fw.jeti.images.StatusIcons;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.*;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+import nu.fw.jeti.util.Utils;
+
+//29-okt-2004
+
+public class GetFileWindow extends JFrame
+{
+	private JProgressBar progressBar = new JProgressBar();
+    private JButton btnDownload = new JButton();
+    private JButton btnCancel = new JButton();
+	private Backend backend;
+	private String streamMethod;	
+    private InfoQuery iq;
+    private OutputStream out;
+    private Timer timer;
+    private StreamReceive streamReceive;
+    private String fromName;
+    private String fileName;
+    private long fileSize;
+    private String description;
+
+    private int ibbMaxSize;
+    
+    public GetFileWindow(Backend backend,InfoQuery iq)
+    {
+    	ibbMaxSize = Preferences.getInteger("filetransfer", "ibbMaxSize",
+                4096);
+    	
+    	this.iq=iq;
+    	this.backend = backend;
+        if (init()) {
+        	jbInit();
+    		pack();
+    		setLocationRelativeTo(null);
+    		setVisible(true);
+      }
+	}
+    
+    private boolean init()
+    {
+    	IQSi iqSi =(IQSi) iq.getIQExtension();
+    	
+	    ArrayList streamOptions = new ArrayList();
+		XData data= iqSi.getXDataForm();
+		if(data.hasFields())
+		{
+			for(Iterator i = data.getFields();i.hasNext();)
+			{
+				XDataField datafield= (XDataField)i.next();
+				if("stream-method".equals(datafield.getVar()))
+				{
+					if(datafield.hasOptions())
+					{
+						for(Iterator j = datafield.getOptionsIterator();j.hasNext();)
+						{
+							streamOptions.add(((Object[])j.next())[1]);
+						}
+					}
+				}
+			}
+		}
+		String value=null;
+		
+		XSiFileTransfer sifi = iqSi.getSiprofile();
+		if(sifi!=null)
+		{//TODO range		
+			if(streamOptions.contains("http://jabber.org/protocol/bytestreams")
+               && Preferences.getBoolean("filetransfer", "bytestreams.enable",
+                                         true)) {
+                value="http://jabber.org/protocol/bytestreams";
+            }
+	        // select inband for files < ibbMaxSize or if bytestreams not possible
+			if((value==null||(sifi.getSize()<ibbMaxSize))
+               && Preferences.getBoolean("filetransfer", "ibb.enable", true)
+               && streamOptions.contains("http://jabber.org/protocol/ibb")) {
+				value="http://jabber.org/protocol/ibb";
+			}
+			if(value==null) {
+				XMPPError error = new XMPPError("cancel",400);
+				error.addError(new XMPPErrorTag("bad-request"));
+				error.addError(new XMPPErrorTag("no-valid-streams","http://jabber.org/protocol/si"));
+				backend.send(new InfoQuery(iq.getFrom(),iq.getID(),error));
+				return false;
+			}
+			streamMethod = value;
+						
+			JIDStatus j = backend.getJIDStatus(iq.getFrom());
+			if(j==null) {
+                fromName = iq.getFrom().toString();
+			} else {
+                fromName = j.getNick();
+            }
+            fileName = sifi.getName();
+            fileSize = sifi.getSize();
+        	progressBar.setMaximum((int)(fileSize/1024));
+            description = sifi.getDescription();
+		}
+        return true;
+	}
+
+
+	private void jbInit()
+    {
+		//setIconImage(StatusIcons.getImageIcon("jeti").getImage());
+		setTitle(I18N.gettext("filetransfer.File_Transfer"));
+
+        getContentPane().setLayout(new GridBagLayout());
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridwidth = 1;
+        c.anchor = GridBagConstraints.LINE_START;
+        c.fill = GridBagConstraints.BOTH;
+        c.insets = new Insets(3, 5, 0, 3);
+        GridBagConstraints c2 = (GridBagConstraints)c.clone();
+        c2.gridwidth = GridBagConstraints.REMAINDER;
+        c2.weightx = 1.0;
+
+        JLabel lbl = new JLabel(I18N.gettext("filetransfer.From"));
+        getContentPane().add(lbl, c);
+        lbl = new JLabel(fromName);
+        getContentPane().add(lbl, c2);
+
+        lbl = new JLabel(I18N.gettext("filetransfer.File_Name"));
+        getContentPane().add(lbl, c);
+        lbl = new JLabel(fileName);
+        getContentPane().add(lbl, c2);
+        
+        lbl = new JLabel(I18N.gettext("filetransfer.File_Size"));
+        getContentPane().add(lbl, c);
+        lbl = new JLabel(Plugin.getSizeText(fileSize));
+        getContentPane().add(lbl, c2);
+
+        if (description != null && description.length() > 0) {
+            lbl = new JLabel(I18N.gettext("filetransfer.Description"));
+            getContentPane().add(lbl, c);
+            JTextArea d = new JTextArea(description);
+            d.setEditable(false);
+            getContentPane().add(new JScrollPane(d), c2);
+        }
+
+        c2.anchor = GridBagConstraints.CENTER;
+        progressBar.setPreferredSize(new Dimension(300, 17));
+        getContentPane().add(progressBar, c2);
+
+        c2.fill = GridBagConstraints.NONE;
+        JPanel panel = new JPanel();
+        btnDownload.setText(I18N.gettext("filetransfer.Download"));
+        getRootPane().setDefaultButton(btnDownload);
+        btnDownload.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                btnDownload_actionPerformed(e);
+            }
+        });
+        Action cancelAction =  new AbstractAction(I18N.gettext("Cancel")) {
+			public void actionPerformed(ActionEvent e) {
+				btnCancel_actionPerformed(e);
+			}
+		};
+		Utils.addCancelButton(this, btnCancel, cancelAction);
+        panel.add(btnDownload);
+        panel.add(btnCancel);
+        getContentPane().add(panel, c2);
+    }
+
+    void btnDownload_actionPerformed(ActionEvent e)
+    {
+    	JFileChooser fileChooser = Plugin.getFileChooser();
+    	fileChooser.setDialogTitle("Save " + fileName);
+    	String dir = Preferences.getString("filetransfer", "downloadDir",null);
+    	if(dir!=null)fileChooser.setCurrentDirectory(new File(dir));
+    	fileChooser.setSelectedFile(new File(fileName));
+    	int a = fileChooser.showSaveDialog(backend.getMainWindow());
+		if(a != JFileChooser.APPROVE_OPTION) return;
+		File file = fileChooser.getSelectedFile();
+		Preferences.putString("filetransfer", "downloadDir",file.getParent());
+		
+		if(file.exists())
+		{
+			int opt =JOptionPane.showConfirmDialog(this,MessageFormat.format(
+					I18N.gettext("filetransfer.{0}_already_exist,_overwrite?")
+					, new String[]{file.toString()})
+					,I18N.gettext("filetransfer.File_Transfer")
+					,JOptionPane.YES_NO_OPTION,JOptionPane.WARNING_MESSAGE);
+			if(opt == JOptionPane.NO_OPTION) return;
+		}
+		try{
+			out = new FileOutputStream(file);
+		}catch(FileNotFoundException e2)
+		{
+	//		timer.stop();
+			Popups.errorPopup(MessageFormat.format(I18N.gettext("filetransfer.{0}_could_not_be_openend_in_write_mode"),
+					new Object[]{file.getAbsolutePath()}),
+					I18N.gettext("filetransfer.File_Transfer"));
+			return;
+		}
+					
+    	XDataBuilder xdb = new XDataBuilder();
+		xdb.type ="submit";
+		xdb.addField(new XDataField("stream-method",streamMethod));
+		XData xdata=null;
+		try
+		{
+			xdata = xdb.build();
+		} catch (InstantiationException e1)
+		{
+			e1.printStackTrace();
+		}
+		backend.send(new InfoQuery(iq.getFrom(),"result",iq.getID(),new IQSi(xdata,null)));
+		IQSi iqSi =(IQSi) iq.getIQExtension();
+		Plugin.addGetFile(this,iq.getFrom(),iqSi.getId());
+		fileName = file.getName();
+		btnDownload.setEnabled(false);
+	}
+
+	void btnCancel_actionPerformed(ActionEvent e)
+    {
+		if(btnCancel.getText().equals(I18N.gettext("filetransfer.Close")))
+		{
+			dispose();
+		}
+		else if(streamReceive!=null)
+		{
+			timer.stop();
+		    streamReceive.cancel();
+		}
+		else
+		{
+			XMPPError error = new XMPPError("cancel",403);
+			error.addError(new XMPPErrorTag("forbidden"));
+			//TODO<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Offer Declined</text>
+			backend.send(new InfoQuery(iq.getFrom(),iq.getID(),error));
+		}
+		dispose();
+    }
+	
+	public OutputStream getOutputStream()
+	{
+		return out;
+	}
+	
+	public StreamReceive getStreamReceive()
+	{
+		return streamReceive;
+	}
+	
+	public void startDownloading(StreamReceive receive)
+	{
+		this.streamReceive = receive;
+		initTimer();
+	}
+	
+   private void initTimer()
+    {
+    	timer = new javax.swing.Timer(1000,new ActionListener()
+		    {
+				public void actionPerformed(ActionEvent evt)
+				{
+					long bytes = streamReceive.getBytes();
+					progressBar.setValue((int)(bytes/1024));
+					int percent = (int) (((double)bytes/fileSize)*100);
+					setTitle(percent + "% " +  fileName);
+				}
+		    }
+		);
+		timer.start();
+    }
+	
+	public void stopDownloading()
+	{
+		if(Preferences.getBoolean("filetransfer", "closeOnComplete", false))
+		{
+			dispose();
+		}
+		else
+		{
+			btnCancel.setText(I18N.gettext("filetransfer.Close"));
+			progressBar.setValue(progressBar.getMaximum());
+			setTitle("100% " +  fileName);
+		}
+		timer.stop();
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/Socks5Send.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/Socks5Send.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/Socks5Send.java	(revision 3952)
@@ -0,0 +1,344 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at jeti@jabber.org
+ */
+package nu.fw.jeti.plugins.filetransfer.socks5;
+
+import java.io.*;
+import java.net.*;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import nu.fw.jeti.events.IQResultListener;
+import nu.fw.jeti.events.StatusChangeListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.IQExtension;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.plugins.filetransfer.Plugin;
+import nu.fw.jeti.plugins.filetransfer.SendFileProgress;
+import nu.fw.jeti.plugins.filetransfer.StreamSend;
+import nu.fw.jeti.plugins.filetransfer.socks5.StreamHost;
+import nu.fw.jeti.plugins.filetransfer.socks5.jsocks.ProxyServer;
+import nu.fw.jeti.plugins.filetransfer.socks5.jsocks.ServerAuthenticatorNone;
+import nu.fw.jeti.plugins.filetransfer.socks5.jsocks.Socks5Proxy;
+import nu.fw.jeti.plugins.filetransfer.socks5.jsocks.SocksSocket;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+
+
+
+//31-okt-2004
+public class Socks5Send implements StreamSend
+{
+	private Backend backend;
+	private String sid;
+	private static List proxies = new LinkedList();
+	private List streamHosts;
+	private File file;
+	private long bytes;
+	private SendFileProgress window;
+	private ProxyServer proxyServer;
+	private Thread proxyServerThread;
+	private Thread sendThread;
+	private int port = 7777;
+		
+	public Socks5Send(File file,final Backend backend,SendFileProgress f,JID jid,String id)
+	{
+		window = f;
+		sid = id;
+    	streamHosts = new LinkedList();
+    	streamHosts.addAll(proxies);
+    	this.backend = backend;
+    	this.file = file;
+    	 
+    	/*
+    	 * Esta alteração visa recupera a inteface de rede local, usada para transferir arquivo.
+    	 * Previne erro no caso do cliente ter múltiplas interface de rede.
+    	 * 
+    	 * emmanuel.ferro@serpro.gov.br em 08/10/2010
+    	 * 
+    	 */    	
+
+    	String ip = backend.getLocalIP().getHostAddress();
+		System.out.println("getLocalIP = " + ip);
+		streamHosts.add( new StreamHost( backend.getMyJID(), ip, 7777, null));
+		startSocksServer(file, backend, jid);
+	
+    	if( Preferences.getBoolean( "filetransfer", "useLocalIP", true ) )
+    	{
+    		ip = Plugin.getIP();
+
+    		if ( ip != null )
+    		{
+    		   	streamHosts.add(new StreamHost(backend.getMyJID(),ip, Preferences.getInteger("filetransfer","port",7777),null));
+				startSocksServer(file, backend, jid);
+    		}
+    	}
+
+    	backend.send(new InfoQuery(jid,"set",backend.getIdentifier(),new Socks5Extension(sid,streamHosts)),
+		new IQResultListener()
+		{
+    		public void iqResult(InfoQuery iq)
+			{
+    			if(iq.getType().equals("result"))
+				{
+					IQExtension e = iq.getIQExtension();
+					if(e instanceof Socks5Extension)
+					{
+						JID streamHostJID = ((Socks5Extension)e).getStreamHostUsed();
+						if(streamHostJID!=null)
+						{
+							if(!streamHostJID.equals(backend.getMyJID()))
+							{
+								if(proxyServerThread!=null)proxyServerThread.interrupt();
+								doProxy(streamHostJID,iq.getFrom());
+							}
+						}
+					}
+				}
+				else
+				{  //TODO add refuse error
+					Popups.messagePopup(I18N.gettext("filetransfer.Problem_during_file_transfer,_transfer_aborted"),I18N.gettext("filetransfer.File_Transfer"));
+				}
+			}
+		},0);
+
+	}
+	
+	public static void initProxies(final Backend backend)
+	{
+		backend.addListener(StatusChangeListener.class,new StatusChangeListener()
+		{
+			public void connectionChanged(boolean online)
+			{
+				String proxy = Preferences.getString("filetransfer", "proxy", null);
+				
+				if( proxy != null )
+					getProxyInfo(backend,new JID(proxy));
+
+				//getProxyInfo(backend,new JID("proxy.jabber.org"));
+				//getProxyInfo(backend,new JID("proxy65.jabber.ccc.de"));
+				//getProxyInfo(backend,new JID("proxy65.jabber.autocom.pl"));
+		    	//getProxyInfo(backend,new JID("proxy.netlab.cz"));
+				//getProxyInfo(backend,new JID("proxy.jabber.cd.chalmers.se"));
+				//getProxyInfo(backend,new JID("proxy65.jaim.at"));
+			}
+
+			public void ownPresenceChanged(int show, String status)	{}
+
+			public void exit(){}
+		});
+	}
+	
+	public static boolean isProxyAvailable()
+	{
+		System.out.println(proxies);
+		System.out.println(!proxies.isEmpty()); 
+		return !proxies.isEmpty();
+	}
+	
+	private static void getProxyInfo(final Backend backend,JID jid)
+	{
+		backend.send(new InfoQuery(jid,"get",new Socks5Extension((JID)null,null)), new IQResultListener()
+		{
+			public void iqResult(InfoQuery iq)
+			{
+				Socks5Extension s = (Socks5Extension)iq.getIQExtension();
+				if(s!=null && s.hasStreamHosts())
+				{
+					for(Iterator i= s.getStreamHosts();i.hasNext();)
+					{
+						StreamHost h = (StreamHost)i.next();
+						proxies.add(h);
+					}
+				}
+			}
+		}, 100);
+	}
+	
+	private void startSocksServer(final File file,final Backend backend, final JID jid)
+	{
+		proxyServerThread = new Thread(new Runnable()
+		  {
+		  	public void run()
+		  	{
+//		  	set socks5 server
+		  		String digest =Plugin.sha(sid,backend.getMyJID(),jid);
+		  		ServerSocket ss=null;			
+				try{
+					 ss = new ServerSocket(port);
+			        // while(true){
+			          Socket s = ss.accept();
+			          System.out.println("Accepted from:"+s.getInetAddress().getHostName()+":"
+			                              +s.getPort());
+			          proxyServer = new ProxyServer(new ServerAuthenticatorNone(),s,file,digest,Socks5Send.this);
+			        //}
+			      }catch(IOException ioe){
+			        ioe.printStackTrace();
+			      }
+			      finally
+				  {
+			      	try {
+						if(ss!=null)ss.close();
+					} catch (IOException e) {
+						// TODO Auto-generated catch block
+						e.printStackTrace();
+					}
+				  }
+		  	}
+		  });
+		proxyServerThread.setPriority(Thread.NORM_PRIORITY);
+		proxyServerThread.start();
+	}
+
+	private void doProxy(final JID streamHostID,final JID targetJID)
+	{
+		new Thread(new Runnable()
+		{
+		  	public void run()
+		  	{
+		  		StreamHost streamHost=null;
+		  		for(Iterator i= streamHosts.iterator();i.hasNext();)
+		  		{
+		  			StreamHost s = (StreamHost)i.next();
+		  			if(streamHostID.equals(s.getJID()))
+		  			{
+		  				streamHost =s;
+		  				break;
+		  			}
+		  		}
+		  		//if(streamHost==null) sendError();
+		  		
+				 
+				try{
+					//TODO stringprep
+					
+					String digest =Plugin.sha(sid,backend.getMyJID(),targetJID);
+									
+					
+					final SocksSocket s = new SocksSocket(new Socks5Proxy(streamHost.getHost(),streamHost.getPort()),digest,0);
+		//			TODO get sid?
+					final StreamHost chosenStreamHost = streamHost;
+					backend.send(new InfoQuery(streamHostID,"set",backend.getIdentifier(),
+					new Socks5Extension(sid,targetJID)),new IQResultListener()
+					{
+						public void iqResult(InfoQuery iq)
+						{
+							if(iq.getType().equals("result"))sendFile(s);
+							else if(iq.getType().equals("error"))
+							{//try different streamhost and remove faulty one
+								proxies.remove(chosenStreamHost);
+								streamHosts.remove(chosenStreamHost);
+								Popups.errorPopup("file could not be send, try again","File transfer");
+								cancel();
+								return;
+							}
+						}
+					},0);
+				}
+				catch (IOException e2)
+				{
+					e2.printStackTrace();
+					proxies.remove(streamHost);
+					streamHosts.remove(streamHost);
+					//sendError();
+					Popups.errorPopup("file could not be downloaded","File transfer");
+					cancel();
+					return;
+				}
+		  	}
+		}).start();
+	}
+	
+	private void sendFile(final SocksSocket s)
+	{
+		sendThread = new Thread(new Runnable()
+		{
+		  	public void run()
+		  	{
+		  		try
+				{
+					InputStream is = new FileInputStream(file.getAbsolutePath());
+					OutputStream out = s.getOutputStream();
+					//OutputStream out = (OutputStream) ps;
+					byte[] buf = new byte[4096];
+					try
+					{
+						int n;
+						while ((n = is.read(buf)) > 0)
+						{
+							out.write(buf, 0, n);
+							out.flush();
+							bytes+=n;
+							if (Thread.interrupted())
+							{
+								is.close();
+								s.close();
+								return;
+							}
+							//if(ps.checkError()) throw new IOException("I/O Error");
+						}
+					} finally
+					{
+						is.close();
+						s.close();
+					}
+					window.done();
+				} catch (IOException e)
+				{
+					e.printStackTrace();
+				}
+		  	}
+		});
+		sendThread.setPriority(Thread.NORM_PRIORITY);
+		sendThread.start();
+	}
+		
+	public void finished()
+	{
+		window.stop();
+	}
+
+	public void addBytes(int bytes)
+	{
+		this.bytes+=bytes;
+	}
+	
+	public long getBytes()
+	{
+		return bytes;
+	}
+	
+	public void cancel()
+	{
+		if(proxyServer!=null)proxyServer.stop();
+		if(sendThread!=null)sendThread.interrupt();
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/Socks5Extension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/Socks5Extension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/Socks5Extension.java	(revision 3952)
@@ -0,0 +1,136 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+// Created on 28-okt-2004
+package nu.fw.jeti.plugins.filetransfer.socks5;
+
+import java.util.Iterator;
+import java.util.List;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQExtension;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.plugins.filetransfer.GetFileWindow;
+import nu.fw.jeti.plugins.filetransfer.Plugin;
+import nu.fw.jeti.util.Log;
+import nu.fw.jeti.util.Popups;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class Socks5Extension extends Extension implements IQExtension
+{
+	private String sid;
+	private JID streamHost;
+	private List streamHosts;
+	private JID activate;
+
+	public Socks5Extension(String sid, JID streamHost, List streamHosts, JID activate)
+	{
+		this.sid = sid;
+		this.streamHost = streamHost;
+		this.streamHosts = streamHosts;
+		this.activate = activate;
+	}
+	public Socks5Extension(String sid,List streamhosts)
+	{
+		this.sid=sid;
+		this.streamHosts = streamhosts;
+	}
+	
+	public Socks5Extension(String sid,JID activation)
+	{
+		this.sid=sid;
+		this.activate = activation;
+	}
+	
+	public Socks5Extension(JID streamHost,String sid)
+	{
+		this.sid=sid;
+		this.streamHost = streamHost;
+	}
+	
+	public String getSid()
+	{
+		return sid;
+	}
+	
+	public JID getStreamHostUsed()
+	{
+		return streamHost;
+	}
+	
+	public boolean hasStreamHosts()
+	{
+		return streamHosts!=null;
+	}
+	
+	public Iterator getStreamHosts()
+	{
+		return streamHosts.iterator();
+	}
+
+	public void execute(InfoQuery iq, Backend backend)
+	{
+		if(iq.getType().equals("set"))
+		{
+			GetFileWindow w = Plugin.getGetFile(iq.getFrom(),sid);
+			if(w==null) Log.xmlReceivedError("Socks stream from unknown");
+			else 
+			{
+				w.startDownloading(new ReceiveSocks5(backend,iq,w));
+			}
+		}
+	}
+	
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<query xmlns= 'http://jabber.org/protocol/bytestreams'");
+		appendAttribute(xml,"sid",sid);
+		xml.append(">");
+		appendElement(xml,"activate",activate);
+		if(streamHost!=null)
+		{
+			xml.append("<streamhost-used");
+			appendAttribute(xml,"jid",streamHost);
+			xml.append("/>");
+		}
+		if(streamHosts!=null)
+		{
+			for(Iterator i=streamHosts.iterator();i.hasNext();)
+			{
+				((StreamHost)i.next()).appendToXML(xml);
+			}
+		}
+		
+		xml.append("</query>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/Socks5Handler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/Socks5Handler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/Socks5Handler.java	(revision 3952)
@@ -0,0 +1,133 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+// Created on 18-sept-2004
+package nu.fw.jeti.plugins.filetransfer.socks5;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.util.Log;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class Socks5Handler extends ExtensionHandler
+{
+	private String sid;
+	private JID streamHost;
+	private List streamHosts;
+	private JID activate;
+	private JID jid;
+	private String host;
+	private int port;
+	private String zeroConf;
+	
+	public void startHandling(Attributes attr)
+	{
+		sid=null;
+		streamHost=null;
+		streamHosts=null;
+		activate=null;
+		host=zeroConf=null;
+		port=0;
+		jid=null;
+		sid= attr.getValue("sid");
+	}
+	
+	public void startElement(String name,Attributes attr)
+	{
+		if(name.equals("streamhost-used"))
+		{
+			try
+			{
+				streamHost=JID.checkedJIDFromString(attr.getValue("jid"));
+			} catch (InstantiationException e)
+			{
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		else if(name.equals("streamhost"))
+		{
+			try
+			{
+				jid=JID.checkedJIDFromString(attr.getValue("jid"));
+			} catch (InstantiationException e)
+			{
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+			host = attr.getValue("host");
+			try{
+				port = Integer.parseInt(attr.getValue("port"));
+			}catch (NumberFormatException e)
+			{
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+			zeroConf = attr.getValue("zeroconf");
+		}
+	}
+	
+	public void endElement(String name)
+	{
+		if(name.equals("activate"))
+		{
+			try
+			{
+				activate =JID.checkedJIDFromString(getText());
+			} catch (InstantiationException e)
+			{
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
+		else if(name.equals("streamhost"))
+		{
+			StreamHost h = new StreamHost(jid,host,port,zeroConf);
+			if(streamHosts==null)streamHosts= new LinkedList();
+			streamHosts.add(h);
+			host=zeroConf=null;
+			port=0;
+			jid=null;
+		}
+		clearCurrentChars();
+	}
+
+	public Extension build()
+	{
+		return new Socks5Extension(sid,streamHost,streamHosts,activate);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/StreamHost.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/StreamHost.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/StreamHost.java	(revision 3952)
@@ -0,0 +1,75 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 28-okt-2004
+ */
+ 
+package nu.fw.jeti.plugins.filetransfer.socks5;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.Extension;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class StreamHost extends Extension
+{
+	private JID jid;
+	private String host;
+	private int port;
+	private String zeroConf;
+	
+	public StreamHost(JID jid, String host, int port, String zeroConf)
+	{
+		this.jid = jid;
+		this.host = host;
+		this.port = port;
+		this.zeroConf = zeroConf;
+	}
+	
+	public String getHost()
+	{
+		return host;
+	}
+	public JID getJID()
+	{
+		return jid;
+	}
+	public int getPort()
+	{
+		return port;
+	}
+	public String getZeroConf()
+	{
+		return zeroConf;
+	}
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<streamhost");
+		appendAttribute(xml,"jid",jid);
+		appendAttribute(xml,"host",host);
+		appendAttribute(xml,"port",String.valueOf(port));
+		appendAttribute(xml,"zeroconf",zeroConf);
+		xml.append("/>");
+	}
+	
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/ReceiveSocks5.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/ReceiveSocks5.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/ReceiveSocks5.java	(revision 3952)
@@ -0,0 +1,188 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.plugins.filetransfer.socks5;
+
+import java.io.*;
+import java.util.Iterator;
+
+import javax.swing.ProgressMonitorInputStream;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.XMPPError;
+import nu.fw.jeti.jabber.elements.XMPPErrorTag;
+import nu.fw.jeti.plugins.filetransfer.GetFileWindow;
+import nu.fw.jeti.plugins.filetransfer.Plugin;
+import nu.fw.jeti.plugins.filetransfer.StreamReceive;
+import nu.fw.jeti.plugins.filetransfer.socks5.jsocks.Socks5Proxy;
+import nu.fw.jeti.plugins.filetransfer.socks5.jsocks.SocksSocket;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+
+//31-okt-2004
+
+public class ReceiveSocks5 extends Thread implements StreamReceive
+{
+	static final int BUF_SIZE = 8192;
+ 	private Backend backend;
+    private InfoQuery iq;
+    private GetFileWindow getFileWindow;
+    private long bytes;
+
+  
+    public ReceiveSocks5(Backend backend,InfoQuery iq,GetFileWindow window)
+    {
+		this.iq =iq;
+		this.getFileWindow = window;
+		this.backend = backend;
+		setPriority(NORM_PRIORITY);
+		start();
+    }
+    
+	public void run()
+	{
+		Socks5Extension s5 =(Socks5Extension)iq.getIQExtension();
+		if(!s5.hasStreamHosts())sendError();
+		//s5.getSid();
+				
+
+	    //check if enough space on hd
+
+		
+		
+//			TODO stringprep
+		String digest =Plugin.sha(s5.getSid(),iq.getFrom(),backend.getMyJID());
+		
+	
+		SocksSocket s=null;	
+		if(s5.hasStreamHosts())
+		{
+			for(Iterator i=s5.getStreamHosts();i.hasNext();)
+			{
+			
+				StreamHost streamHost = (StreamHost)i.next();
+								
+				
+				try{
+					//Proxy p = new Socks5Proxy("192.168.10.2",5080);
+					//TODO add timeout	
+					s = new SocksSocket(new Socks5Proxy(streamHost.getHost(),streamHost.getPort()),digest,0);
+	//				check if connection is open
+					backend.send(new InfoQuery(iq.getFrom(),"result",iq.getID(),
+								new Socks5Extension(streamHost.getJID(),null)));
+					break;
+				}
+				catch (IOException e2)
+				{
+					//e2.printStackTrace();
+					System.out.println(e2.getMessage());
+					continue;
+				}
+			}
+		}
+		
+		if(s==null)
+		{
+			sendError();
+			Popups.messagePopup(I18N.gettext("filetransfer.Problem_during_file_transfer,_transfer_aborted"),I18N.gettext("filetransfer.File_Transfer"));
+			getFileWindow.stopDownloading();
+			return;
+		}
+		
+		InputStream in=null;
+		OutputStream out=getFileWindow.getOutputStream();
+		in = s.getInputStream();	
+	
+			if(in!=null)
+			{	
+				if(out!=null)
+				{	
+					try{
+						byte[] buf = new byte[BUF_SIZE];
+						int n;
+						while ((n = in.read(buf)) > 0)
+						{
+							out.write(buf, 0, n);
+							out.flush();
+							bytes+=n;
+							yield();
+							if(Thread.interrupted())
+							{
+								out.close();
+								in.close();
+								return;
+							}
+							
+					    }
+						//download ok
+//						backend.send(new InfoQuery(jid,"result",id,null)); 
+					}catch (IOException e2)  //probaly out of disk space
+					{
+						Popups.errorPopup(e2.getMessage() + " while downloading " ,"File transfer");
+						getFileWindow.stopDownloading();
+						//download not ok
+	//					backend.send(new InfoQuery(jid,"error",id,null,"Not Acceptable",406)); 
+					}//interuppeted io exception for cancel
+					finally
+					{
+						try
+						{
+							out.close();
+							in.close();
+						} catch (IOException e)
+						{
+							e.printStackTrace();
+						}
+						getFileWindow.stopDownloading();
+					}
+				}
+				
+			}
+			
+	}
+	
+	public long getBytes()
+	{
+		return bytes;
+	}
+	
+	public void cancel()
+	{
+		interrupt();
+	}
+	
+	private void sendError()
+	{
+		XMPPError error = new XMPPError("cancel",404);
+		error.addError(new XMPPErrorTag("item-not-found"));
+		backend.send(new InfoQuery(iq.getFrom(),iq.getID(),error));
+	}
+		
+	
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/AuthenticationNone.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/AuthenticationNone.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/AuthenticationNone.java	(revision 3952)
@@ -0,0 +1,17 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+/**
+  SOCKS5 none authentication. Dummy class does almost nothing.
+*/
+public class AuthenticationNone implements Authentication{
+
+    public Object[] doSocksAuthentication(int methodId,
+                                          java.net.Socket proxySocket)
+           throws java.io.IOException{
+
+       if(methodId!=0) return null;
+
+       return new Object[] { proxySocket.getInputStream(),
+                             proxySocket.getOutputStream()};
+   }
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/UserPasswordAuthentication.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/UserPasswordAuthentication.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/UserPasswordAuthentication.java	(revision 3952)
@@ -0,0 +1,75 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+/**
+  SOCKS5 User Password authentication scheme.
+*/
+public class UserPasswordAuthentication implements Authentication{
+
+   /**SOCKS ID for User/Password authentication method*/
+   public final static int METHOD_ID = 2;
+
+   String userName, password;
+   byte[] request;
+
+   /**
+     Create an instance of UserPasswordAuthentication.
+     @param userName User Name to send to SOCKS server.
+     @param password Password to send to SOCKS server.
+   */
+   public UserPasswordAuthentication(String userName,String password){
+     this.userName = userName;
+     this.password = password;
+     formRequest();
+   }
+   /** Get the user name.
+   @return User name.
+   */
+   public String getUser(){
+     return userName;
+   }
+   /** Get password
+   @return Password
+   */
+   public String getPassword(){
+     return password;
+   }
+   /**
+    Does User/Password authentication as defined in rfc1929.
+    @return An array containnig in, out streams, or null if authentication
+    fails.
+   */
+   public Object[] doSocksAuthentication(int methodId,
+                                         java.net.Socket proxySocket)
+                   throws java.io.IOException{
+
+      if(methodId != METHOD_ID) return null;
+
+      java.io.InputStream in = proxySocket.getInputStream();
+      java.io.OutputStream out = proxySocket.getOutputStream();
+
+      out.write(request);
+      int version = in.read();
+      if(version < 0) return null; //Server closed connection
+      int status = in.read();
+      if(status != 0) return null; //Server closed connection, or auth failed.
+
+      return new Object[] {in,out};
+   }
+
+//Private methods
+//////////////////
+
+/** Convert UserName password in to binary form, ready to be send to server*/
+   private void formRequest(){
+      byte[] user_bytes = userName.getBytes();
+      byte[] password_bytes = password.getBytes();
+
+      request = new byte[3+user_bytes.length+password_bytes.length];
+      request[0] = (byte) 1; 
+      request[1] = (byte) user_bytes.length;
+      System.arraycopy(user_bytes,0,request,2,user_bytes.length);
+      request[2+user_bytes.length] = (byte) password_bytes.length;
+      System.arraycopy(password_bytes,0,
+                       request,3+user_bytes.length,password_bytes.length);
+   }
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ServerAuthenticator.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ServerAuthenticator.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ServerAuthenticator.java	(revision 3952)
@@ -0,0 +1,118 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.DatagramPacket;
+
+
+/**
+ Classes implementing this interface should provide socks server with
+ authentication and authorization of users.
+**/
+public interface ServerAuthenticator{
+
+   /**
+     This method is called when a new connection accepted by the server.
+     <p>
+     At this point no data have been extracted from the connection. It is
+     responsibility of this method to ensure that the next byte in the
+     stream after this method have been called is the first byte of the
+     socks request message. For SOCKSv4 there is no authentication data and
+     the first byte in the stream is part of the request. With SOCKSv5 however
+     there is an authentication data first. It is expected that implementaions
+     will process this authentication data. 
+     <p>
+     If authentication was successful an instance of ServerAuthentication
+     should be returned, it later will be used by the server to perform
+     authorization and some other things. If authentication fails null should
+     be returned, or an exception may be thrown.
+
+     @param s Accepted Socket.
+     @return An instance of ServerAuthenticator to be used for this connection
+     or null
+   */
+   ServerAuthenticator startSession(Socket s) throws IOException;
+
+   /**
+    This method should return input stream which should be used on the
+    accepted socket.
+    <p>
+    SOCKSv5 allows to have multiple authentication methods, and these methods
+    might require some kind of transformations being made on the data.
+    <p>
+    This method is called on the object returned from the startSession
+    function.
+   */
+   InputStream getInputStream();
+   /**
+    This method should return output stream to use to write to the accepted
+    socket.
+    <p>
+    SOCKSv5 allows to have multiple authentication methods, and these methods
+    might require some kind of transformations being made on the data.
+    <p>
+    This method is called on the object returned from the startSession
+    function.
+   */
+   OutputStream getOutputStream();
+
+   /**
+    This method should return UDPEncapsulation, which should be used
+    on the datagrams being send in/out.
+    <p>
+    If no transformation should be done on the datagrams, this method
+    should return null.
+    <p>
+    This method is called on the object returned from the startSession
+    function.
+   */
+
+   UDPEncapsulation getUdpEncapsulation();
+
+   /**
+    This method is called when a request have been read.
+    <p>
+    Implementation should decide wether to grant request or not. Returning
+    true implies granting the request, false means request should be rejected.
+    <p>
+    This method is called on the object returned from the startSession
+    function.
+    @param msg Request message.
+    @return true to grant request, false to reject it.
+   */
+   boolean checkRequest(ProxyMessage msg);
+
+   /**
+    This method is called when datagram is received by the server.
+    <p>
+    Implementaions should decide wether it should be forwarded or dropped.
+    It is expecteed that implementation will use datagram address and port
+    information to make a decision, as well as anything else. Address and
+    port of the datagram are always correspond to remote machine. It is
+    either destination or source address. If out is true address is destination
+    address, else it is a source address, address of the machine from which
+    datagram have been received for the client.
+    <p>
+    Implementaions should return true if the datagram is to be forwarded, and
+    false if the datagram should be dropped.
+    <p>
+    This method is called on the object returned from the startSession
+    function.
+
+    @param out If true the datagram is being send out(from the client),
+               otherwise it is an incoming datagram.
+    @return True to forward datagram false drop it silently.
+   */
+   boolean checkRequest(DatagramPacket dp, boolean out);
+
+   /**
+    This method is called when session is completed. Either due to normal
+    termination or due to any error condition.
+    <p>
+    This method is called on the object returned from the startSession
+    function.
+   */
+   void endSession();
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/SocksException.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/SocksException.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/SocksException.java	(revision 3952)
@@ -0,0 +1,78 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+/**
+ Exception thrown by various socks classes to indicate errors
+ with protocol or unsuccessfull server responses.
+*/
+public class SocksException extends java.io.IOException{
+   /**
+    Construct a SocksException with given errorcode.
+    <p>
+    Tries to look up message which corresponds to this error code.
+    @param errCode Error code for this exception.
+   */
+   public SocksException(int errCode){
+       this.errCode = errCode;
+       if((errCode >> 16) == 0){
+          //Server reply error message
+          errString = errCode <= serverReplyMessage.length ?
+                      serverReplyMessage[errCode] :
+                      UNASSIGNED_ERROR_MESSAGE;
+       }else{
+          //Local error
+          errCode = (errCode >> 16) -1;
+          errString = errCode <= localErrorMessage.length ?
+                      localErrorMessage[errCode] :
+                      UNASSIGNED_ERROR_MESSAGE;
+       }
+   }
+   /**
+    Constructs a SocksException with given error code and message.
+    @param errCode  Error code.
+    @param errString Error Message.
+   */
+   public SocksException(int errCode,String errString){
+       this.errCode = errCode;
+       this.errString = errString;
+   }
+   /**
+    Get the error code associated with this exception.
+    @return Error code associated with this exception.
+   */
+   public int getErrorCode(){
+      return errCode;
+   }
+   /**
+    Get human readable representation of this exception.
+    @return String represntation of this exception.
+   */
+   public String toString(){
+      return errString;
+   }
+
+   static final String UNASSIGNED_ERROR_MESSAGE =
+                  "Unknown error message";
+   static final String serverReplyMessage[] = { 
+                  "Succeeded", 
+                  "General SOCKS server failure",
+                  "Connection not allowed by ruleset",
+                  "Network unreachable",
+                  "Host unreachable",
+                  "Connection refused",
+                  "TTL expired",
+                  "Command not supported",
+                  "Address type not supported" };
+
+   static final String localErrorMessage[] ={
+                  "SOCKS server not specified",
+                  "Unable to contact SOCKS server",
+                  "IO error",
+                  "None of Authentication methods are supported",
+                  "Authentication failed",
+                  "General SOCKS fault" };
+
+   String errString;
+   int errCode;
+
+}//End of SocksException class
+
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ProxyServer.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ProxyServer.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ProxyServer.java	(revision 3952)
@@ -0,0 +1,646 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+import java.io.*;
+import java.net.*;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import nu.fw.jeti.plugins.filetransfer.socks5.Socks5Send;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+
+
+/**
+    SOCKS4 and SOCKS5 proxy, handles both protocols simultaniously.
+    Implements all SOCKS commands, including UDP relaying.
+    <p>
+    In order to use it you will need to implement ServerAuthenticator
+    interface. There is an implementation of this interface which does
+    no authentication ServerAuthenticatorNone, but it is very dangerous
+    to use, as it will give access to your local network to anybody
+    in the world. One should never use this authentication scheme unless
+    one have pretty good reason to do so.
+    There is a couple of other authentication schemes in socks.server package.
+    @see socks.server.ServerAuthenticator
+*/
+public class ProxyServer implements Runnable{
+
+   ServerAuthenticator auth;
+   ProxyMessage msg = null;
+
+   Socket sock=null,remote_sock=null;
+   ServerSocket ss=null;
+   UDPRelayServer relayServer = null;
+   InputStream in; //,remote_in;
+   OutputStream out;//,remote_out;
+   File file;
+   String sha1Digest;
+   Thread pst;
+   Socks5Send send;
+   
+   int mode;
+   static final int START_MODE	= 0;
+   static final int ACCEPT_MODE	= 1;
+   static final int PIPE_MODE	= 2;
+   static final int ABORT_MODE	= 3;
+
+   static final int BUF_SIZE	= 8192;
+
+  // Thread pipe_thread1,pipe_thread2;
+   long lastReadTime;
+
+   static int iddleTimeout	= 180000; //3 minutes
+   static int acceptTimeout	= 180000; //3 minutes
+
+   static PrintStream log = null;
+   static Proxy proxy;
+
+
+//Public Constructors
+/////////////////////
+
+
+   /**
+    Creates a proxy server with given Authentication scheme.
+    @param auth Authentication scheme to be used.
+    */
+   public ProxyServer(ServerAuthenticator auth){
+     this.auth = auth;
+   }
+
+//Other constructors
+////////////////////
+
+  public  ProxyServer(ServerAuthenticator auth,Socket s,File file,String digest,Socks5Send send){
+      this.auth  = auth;
+      this.sock  = s;
+      this.file = file;
+      this.send = send;
+      sha1Digest = digest;
+      mode = START_MODE;
+      setLog(System.out);
+      pst = new Thread(this);
+	  pst.start();
+   }
+
+//Public methods
+/////////////////
+
+   /**
+     Set the logging stream. Specifying null disables logging.
+   */
+   public static void setLog(OutputStream out){
+      if(out == null){
+        log = null;
+      }else{
+        log = new PrintStream(out,true);
+      }
+
+      UDPRelayServer.log = log;
+   }
+   
+   /**
+    Set proxy.
+    <p>
+    Allows Proxy chaining so that one Proxy server is connected to another
+    and so on. If proxy supports SOCKSv4, then only some SOCKSv5 requests
+    can be handled, UDP would not work, however CONNECT and BIND will be
+    translated.
+
+    @param p Proxy which should be used to handle user requests.
+   */
+   public static void setProxy(Proxy p){
+      proxy =p;
+      UDPRelayServer.proxy = proxy;
+   }
+
+   /**
+    Get proxy.
+    @return Proxy wich is used to handle user requests.
+   */
+   public static Proxy getProxy(){
+      return proxy;
+   }
+
+   /**
+    Sets the timeout for connections, how long shoud server wait
+    for data to arrive before dropping the connection.<br>
+    Zero timeout implies infinity.<br>
+    Default timeout is 3 minutes.
+   */
+   public static void setIddleTimeout(int timeout){
+      iddleTimeout = timeout;
+   }
+   /**
+    Sets the timeout for BIND command, how long the server should
+    wait for the incoming connection.<br>
+    Zero timeout implies infinity.<br>
+    Default timeout is 3 minutes.
+   */
+   public static void setAcceptTimeout(int timeout){
+      acceptTimeout = timeout;
+   }
+
+   /**
+    Sets the timeout for UDPRelay server.<br>
+    Zero timeout implies infinity.<br>
+    Default timeout is 3 minutes.
+   */
+   public static void setUDPTimeout(int timeout){
+      UDPRelayServer.setTimeout(timeout);
+   }
+
+   /**
+     Sets the size of the datagrams used in the UDPRelayServer.<br>
+     Default size is 64K, a bit more than maximum possible size of the
+     datagram.
+    */
+   public static void setDatagramSize(int size){
+      UDPRelayServer.setDatagramSize(size);
+   }
+
+
+   /**
+     Start the Proxy server at given port.<br>
+     This methods blocks.
+    */
+   public void start(int port){
+      start(port,5,null);
+   }
+
+   /**
+     Create a server with the specified port, listen backlog, and local 
+     IP address to bind to. The localIP argument can be used on a multi-homed
+     host for a ServerSocket that will only accept connect requests to one of 
+     its addresses. If localIP is null, it will default accepting connections 
+     on any/all local addresses. The port must be between 0 and 65535, 
+     inclusive. <br>
+     This methods blocks. 
+    */
+   public void start(int port,int backlog,InetAddress localIP){
+      try{
+        ss = new ServerSocket(port,backlog,localIP);
+        log("Starting SOCKS Proxy on:"+ss.getInetAddress().getHostAddress()+":"
+                                      +ss.getLocalPort());
+        while(true){
+          Socket s = ss.accept();
+          log("Accepted from:"+s.getInetAddress().getHostName()+":"
+                              +s.getPort());
+          ProxyServer ps = new ProxyServer(auth,s,null,null,null);
+          (new Thread(ps)).start();
+        }
+      }catch(IOException ioe){
+        ioe.printStackTrace();
+      }finally{
+      }
+   }
+
+   /**
+     Stop server operation.It would be wise to interrupt thread running the
+     server afterwards.
+    */
+   public void stop(){
+     try{
+       if(ss != null) ss.close();
+     }catch(IOException ioe){
+     }
+     pst.interrupt();
+   }
+
+//Runnable interface
+////////////////////
+   public void run(){
+      switch(mode){
+        case START_MODE:
+         try{
+           startSession();
+         }catch(IOException ioe){
+           handleException(ioe);
+           //ioe.printStackTrace();
+         }finally{
+           abort();
+           if(auth!=null) auth.endSession();
+           log("Main thread(client->remote)stopped.");
+         }
+        break;
+        case ACCEPT_MODE:
+          try{
+            doAccept();
+            mode = PIPE_MODE;
+            //pipe_thread1.interrupt(); //Tell other thread that connection have
+                                      //been accepted.
+           // System.out.println("accept pipe");
+           // pipe(remote_in,out);
+          }catch(IOException ioe){
+            //log("Accept exception:"+ioe);
+            handleException(ioe);
+          }finally{
+            abort();
+            log("Accept thread(remote->client) stopped");
+          }
+        break;
+        case PIPE_MODE:
+         try{
+         	//System.out.println("pipe mode");
+            //pipe(remote_in,out);
+    
+        // }catch(IOException ioe){
+         }finally{
+            abort();
+            log("Support thread(remote->client) stopped");
+         }
+        break;
+        case ABORT_MODE:
+        break;
+        default:
+         log("Unexpected MODE "+mode);
+      }
+   }
+
+//Private methods
+/////////////////
+   private void startSession() throws IOException{
+     sock.setSoTimeout(iddleTimeout);
+
+     try{
+        auth = auth.startSession(sock);
+     }catch(IOException ioe){
+       log("Auth throwed exception:"+ioe);
+       auth = null;
+       return;
+     }
+
+     if(auth == null){ //Authentication failed
+        log("Authentication failed");
+        return;
+     }
+
+     in = auth.getInputStream();
+     out = auth.getOutputStream();
+
+     msg = readMsg(in);
+     handleRequest(msg);
+   }
+   
+   private void handleRequest(ProxyMessage msg)
+                throws IOException{
+      if(!auth.checkRequest(msg)) throw new 
+                                  SocksException(Proxy.SOCKS_FAILURE);
+
+      if(msg.ip == null){
+        if(msg instanceof Socks5Message){
+         // msg.ip = InetAddress.getByName(msg.host);
+        }else
+          throw new SocksException(Proxy.SOCKS_FAILURE);
+      }
+      log(msg);
+
+      switch(msg.command){
+        case Proxy.SOCKS_CMD_CONNECT:
+          onConnect(msg);
+        break;
+        case Proxy.SOCKS_CMD_BIND:
+          //onBind(msg);
+        break;
+        case Proxy.SOCKS_CMD_UDP_ASSOCIATE:
+  //        onUDP(msg);
+        break;
+        default:
+          throw new SocksException(Proxy.SOCKS_CMD_NOT_SUPPORTED);
+      }
+   }
+
+   private void handleException(IOException ioe){
+   	
+   	Popups.messagePopup(I18N.gettext("filetransfer.Problem_during_file_transfer,_transfer_aborted"),I18N.gettext("filetransfer.File_Transfer"));
+   	send.finished();
+   	
+   	ioe.printStackTrace();
+   	
+      //If we couldn't read the request, return;
+      if(msg == null) return;
+      //If have been aborted by other thread
+      if(mode == ABORT_MODE) return;
+      //If the request was successfully completed, but exception happened later
+      if(mode == PIPE_MODE) return;
+
+      int error_code = Proxy.SOCKS_FAILURE;
+
+      if(ioe instanceof SocksException)
+          error_code = ((SocksException)ioe).errCode;
+      else if(ioe instanceof NoRouteToHostException)
+          error_code = Proxy.SOCKS_HOST_UNREACHABLE;
+      else if(ioe instanceof ConnectException)
+          error_code = Proxy.SOCKS_CONNECTION_REFUSED;
+      else if(ioe instanceof InterruptedIOException)
+          error_code = Proxy.SOCKS_TTL_EXPIRE;
+
+      if(error_code > Proxy.SOCKS_ADDR_NOT_SUPPORTED || error_code < 0){
+          error_code = Proxy.SOCKS_FAILURE; 
+      }
+
+      sendErrorMessage(error_code);
+   }
+
+   private void onConnect(ProxyMessage msg) throws IOException{
+      Socket s;
+      ProxyMessage response = null;
+
+//      if(proxy == null)
+//         s = new Socket(msg.ip,msg.port);
+//      else
+//         s = new SocksSocket(proxy,msg.ip,msg.port);
+
+    //  System.out.println(sha1Digest);
+    //  System.out.println(msg.host);
+      
+     if(msg.host.equals(sha1Digest) && msg.port==0)
+     {
+		 log("Connected to "+msg.host+":"+msg.port);
+	
+	      if(msg instanceof Socks5Message){
+	        response = new Socks5Message(Proxy.SOCKS_SUCCESS,
+	        								msg.host,
+	                                        msg.port);
+	      }
+	       
+	      //response = new Socks4Message(Socks4Message.REPLY_OK, s.getLocalAddress(),s.getLocalPort());
+		      
+	      response.write(out);
+	      startPipe(null);
+   	  }
+     else
+     {
+     	System.out.println("invalid entry");
+     	throw new ConnectException();
+     }
+     	
+   }
+
+   private void onBind(ProxyMessage msg) throws IOException{
+      ProxyMessage response = null;
+
+      if(proxy == null)
+        ss = new ServerSocket(0);
+      else
+        ss = new SocksServerSocket(proxy, msg.ip, msg.port);
+
+      ss.setSoTimeout(acceptTimeout);
+
+      log("Trying accept on "+ss.getInetAddress()+":"+ss.getLocalPort());
+
+      if(msg.version == 5)
+         response = new Socks5Message(Proxy.SOCKS_SUCCESS,ss.getInetAddress(),
+                                                          ss.getLocalPort());
+      else
+         response = new Socks4Message(Socks4Message.REPLY_OK,
+                                      ss.getInetAddress(),
+                                      ss.getLocalPort());
+      response.write(out);
+
+      mode = ACCEPT_MODE;
+
+      //pipe_thread1 = Thread.currentThread();
+      //pipe_thread2 = new Thread(this);
+      //pipe_thread2.start();
+
+      //Make timeout infinit.
+      sock.setSoTimeout(0);
+      int eof=0;
+
+      try{
+        while((eof=in.read())>=0){
+          if(mode != ACCEPT_MODE){
+            if(mode != PIPE_MODE) return;//Accept failed
+
+            //remote_out.write(eof);
+            break;
+          }
+        }
+      }catch(EOFException eofe){
+        //System.out.println("EOF exception");
+        return;//Connection closed while we were trying to accept.
+      }catch(InterruptedIOException iioe){ 
+        //Accept thread interrupted us.
+        //System.out.println("Interrupted");
+        if(mode != PIPE_MODE)
+          return;//If accept thread was not successfull return.
+      }finally{
+        //System.out.println("Finnaly!");
+      }
+
+      if(eof < 0)//Connection closed while we were trying to accept;
+        return;
+      
+      //Do not restore timeout, instead timeout is set on the
+      //remote socket. It does not make any difference.
+
+     // pipe(in,remote_out);
+   }
+
+   private void onUDP(ProxyMessage msg) throws IOException{
+      if(msg.ip.getHostAddress().equals("0.0.0.0"))
+         msg.ip = sock.getInetAddress();
+      log("Creating UDP relay server for "+msg.ip+":"+msg.port);
+      relayServer = new UDPRelayServer(msg.ip,msg.port,
+                        Thread.currentThread(),sock,auth);
+
+      ProxyMessage response;
+
+      response = new Socks5Message(Proxy.SOCKS_SUCCESS,
+                                   relayServer.relayIP,relayServer.relayPort);
+
+      response.write(out);
+
+      relayServer.start();
+
+      //Make timeout infinit.
+      sock.setSoTimeout(0);
+      try{
+        while(in.read()>=0) /*do nothing*/;
+      }catch(EOFException eofe){
+      }
+   }
+
+//Private methods
+//////////////////
+
+   private void doAccept() throws IOException{
+   //	System.out.println("doaccept");
+      Socket s;
+      long startTime = System.currentTimeMillis();
+
+      while(true){
+         s = ss.accept();
+         if(s.getInetAddress().equals(msg.ip)){
+            //got the connection from the right host
+            //Close listenning socket.
+            ss.close();
+            break;
+         }else if(ss instanceof SocksServerSocket){
+            //We can't accept more then one connection
+            s.close();
+            ss.close();
+            throw new SocksException(Proxy.SOCKS_FAILURE);
+         }else{
+            if(acceptTimeout!=0){ //If timeout is not infinit
+               int newTimeout = acceptTimeout-(int)(System.currentTimeMillis()-
+                                                           startTime);
+               if(newTimeout <= 0) throw new InterruptedIOException(
+                                "In doAccept()");
+               ss.setSoTimeout(newTimeout);
+            }
+            s.close(); //Drop all connections from other hosts
+         }
+      }
+
+      //Accepted connection
+      remote_sock = s;
+  //    remote_in = s.getInputStream();
+   //   remote_out = s.getOutputStream();
+
+      //Set timeout
+      remote_sock.setSoTimeout(iddleTimeout);
+
+      log("Accepted from "+s.getInetAddress()+":"+s.getPort());
+
+      ProxyMessage response;
+
+      if(msg.version == 5)
+         response = new Socks5Message(Proxy.SOCKS_SUCCESS, s.getInetAddress(),
+                                                           s.getPort());
+      else
+         response = new Socks4Message(Socks4Message.REPLY_OK,
+                                      s.getInetAddress(), s.getPort());
+      response.write(out);
+   }
+
+   private ProxyMessage readMsg(InputStream in) throws IOException{
+      PushbackInputStream push_in;  
+      if(in instanceof PushbackInputStream)
+         push_in = (PushbackInputStream) in;
+      else
+         push_in = new PushbackInputStream(in);
+
+      int version = push_in.read();
+      push_in.unread(version);
+
+
+      ProxyMessage msg;
+
+      if(version == 5){
+        msg = new Socks5Message(push_in,false);
+      }else if(version == 4){
+        msg = new Socks4Message(push_in,false);
+      }else{
+        throw new SocksException(Proxy.SOCKS_FAILURE);
+      }
+      return msg;
+   }
+
+   private void startPipe(Socket s){
+      mode = PIPE_MODE;
+      remote_sock = s;
+      try{
+  ///       remote_in = s.getInputStream();
+  //       remote_out = s.getOutputStream();
+        // pipe_thread1 = Thread.currentThread();
+        // pipe_thread2 = new Thread(this);
+        // pipe_thread2.start();
+        // pipe(in,remote_out);
+      	pipe(out);
+      }catch(IOException ioe){
+      }
+   }
+
+   private void sendErrorMessage(int error_code){
+      ProxyMessage err_msg;
+      if(msg instanceof Socks4Message)
+         err_msg = new Socks4Message(Socks4Message.REPLY_REJECTED);
+      else
+         err_msg = new Socks5Message(error_code);
+      try{
+         err_msg.write(out);
+      }catch(IOException ioe){}
+   }
+
+   private synchronized void abort(){
+      if(mode == ABORT_MODE) return;
+      mode = ABORT_MODE;
+      try{
+         log("Aborting operation");
+         if(remote_sock != null) remote_sock.close();
+         if(sock != null) sock.close();
+         if(relayServer!=null) relayServer.stop();
+         if(ss!=null) ss.close();
+  //       if(pipe_thread1 != null) pipe_thread1.interrupt();
+        // if(pipe_thread2 != null) pipe_thread2.interrupt();
+      }catch(IOException ioe){}
+   }
+
+   static final void log(String s){
+     if(log != null){
+       log.println(s);
+       log.flush();
+     }
+   }
+
+   static final void log(ProxyMessage msg){
+      log("Request version:"+msg.version+
+          "\tCommand: "+command2String(msg.command));
+      log("IP:"+msg.ip +"\tPort:"+msg.port+
+         (msg.version==4?"\tUser:"+msg.user:""));
+   }
+   
+   private void pipe(OutputStream out) throws IOException
+	{
+		InputStream is = new FileInputStream(file.getAbsolutePath());
+		byte[] buf = new byte[BUF_SIZE];
+		//System.out.println("begin sending");
+		try
+		{
+			int n;
+			while ((n = is.read(buf)) > 0)
+			{
+				out.write(buf, 0, n);
+				out.flush();
+				send.addBytes(n);
+				if (Thread.interrupted()) { return; }
+				Thread.yield();
+			}
+			send.finished();
+		} finally
+		{
+			is.close();
+		}
+	}
+   
+
+//   private void pipe(InputStream in,OutputStream out) throws IOException{
+//      lastReadTime = System.currentTimeMillis();
+//      byte[] buf = new byte[BUF_SIZE];
+//      int len = 0;
+//      while(len >= 0){
+//         try{
+//           if(len!=0){
+//             out.write(buf,0,len);
+//             out.flush();
+//           }
+//           len= in.read(buf);
+//           lastReadTime = System.currentTimeMillis();
+//         }catch(InterruptedIOException iioe){
+//           if(iddleTimeout == 0) return;//Other thread interrupted us.
+//           long timeSinceRead = System.currentTimeMillis() - lastReadTime;
+//           if(timeSinceRead >= iddleTimeout - 1000) //-1s for adjustment.
+//              return;
+//           len = 0;
+//
+//         }
+//      }
+//   }
+   static final String command_names[] = {"CONNECT","BIND","UDP_ASSOCIATE"};
+
+   static final String command2String(int cmd){
+      if(cmd > 0 && cmd < 4) return command_names[cmd-1];
+      else return "Unknown Command "+cmd;
+   }
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5Proxy.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5Proxy.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5Proxy.java	(revision 3952)
@@ -0,0 +1,248 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+import java.net.*;
+import java.io.*;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/**
+ SOCKS5 Proxy.
+*/
+
+public class Socks5Proxy extends Proxy implements Cloneable{
+
+//Data members
+   private Hashtable authMethods = new Hashtable();
+   private int selectedMethod;
+
+   boolean resolveAddrLocally = true;
+   UDPEncapsulation udp_encapsulation=null;
+
+
+//Public Constructors
+//====================
+
+   /**
+     Creates SOCKS5 proxy.
+     @param p Proxy to use to connect to this proxy, allows proxy chaining.
+     @param proxyHost Host on which a Proxy server runs.
+     @param proxyPort Port on which a Proxy server listens for connections.
+     @throws UnknownHostException If proxyHost can't be resolved.
+   */
+   public Socks5Proxy(Proxy p,String proxyHost,int proxyPort)
+          throws UnknownHostException{ 
+      super(p,proxyHost,proxyPort);
+      version = 5;
+      setAuthenticationMethod(0,new AuthenticationNone());
+   }
+
+   /**
+     Creates SOCKS5 proxy.
+     @param proxyHost Host on which a Proxy server runs.
+     @param proxyPort Port on which a Proxy server listens for connections.
+     @throws UnknownHostException If proxyHost can't be resolved.
+   */
+   public Socks5Proxy(String proxyHost,int proxyPort)
+          throws UnknownHostException{ 
+      this(null,proxyHost,proxyPort);
+   }
+
+
+   /**
+     Creates SOCKS5 proxy.
+     @param p Proxy to use to connect to this proxy, allows proxy chaining.
+     @param proxyIP Host on which a Proxy server runs.
+     @param proxyPort Port on which a Proxy server listens for connections.
+   */
+   public Socks5Proxy(Proxy p,InetAddress proxyIP,int proxyPort){
+      super(p,proxyIP,proxyPort);
+      version = 5;
+      setAuthenticationMethod(0,new AuthenticationNone());
+   }
+
+   /**
+     Creates SOCKS5 proxy.
+     @param proxyIP Host on which a Proxy server runs.
+     @param proxyPort Port on which a Proxy server listens for connections.
+   */
+   public Socks5Proxy(InetAddress proxyIP,int proxyPort){
+      this(null,proxyIP,proxyPort);
+   }
+
+//Public instance methods
+//========================
+
+
+   /**
+    * Wether to resolve address locally or to let proxy do so.
+      <p>
+      SOCKS5 protocol allows to send host names rather then IPs in the
+      requests, this option controls wether the hostnames should be send
+      to the proxy server as names, or should they be resolved locally.
+      @param doResolve Wether to perform resolution locally.
+      @return Previous settings.
+    */
+   public boolean resolveAddrLocally(boolean doResolve){
+      boolean old = resolveAddrLocally;
+      resolveAddrLocally = doResolve;
+      return old;
+   }
+   /**
+    Get current setting on how the addresses should be handled.
+    @return Current setting for address resolution.
+    @see Socks5Proxy#resolveAddrLocally(boolean doResolve)
+   */
+   public boolean resolveAddrLocally(){
+      return resolveAddrLocally;
+   }
+
+   /**
+     Adds another authentication method.
+     @param methodId Authentication method id, see rfc1928
+     @param method Implementation of Authentication
+     @see Authentication
+   */
+   public boolean setAuthenticationMethod(int methodId,
+                                          Authentication method){
+      if(methodId<0 || methodId > 255)
+          return false;
+      if(method == null){
+        //Want to remove a particular method
+	return (authMethods.remove(new Integer(methodId)) != null);
+      }else{//Add the method, or rewrite old one
+	authMethods.put(new Integer(methodId),method);
+      }
+      return true;
+   }
+   
+   /**
+    Get authentication method, which corresponds to given method id
+    @param methodId Authentication method id.
+    @return Implementation for given method or null, if one was not set.
+   */
+   public Authentication getAuthenticationMethod(int methodId){
+      Object method = authMethods.get(new Integer(methodId));
+      if(method == null) return null;
+      return (Authentication)method;
+   }
+
+   /**
+    Creates a clone of this Proxy.
+   */
+   public Object clone(){
+      Socks5Proxy newProxy = new Socks5Proxy(proxyIP,proxyPort);
+      newProxy.authMethods = (Hashtable) this.authMethods.clone();
+      newProxy.directHosts = (InetRange)directHosts.clone();
+      newProxy.resolveAddrLocally = resolveAddrLocally;
+      newProxy.chainProxy = chainProxy;
+      return newProxy;
+   }
+
+//Public Static(Class) Methods
+//==============================
+
+
+//Protected Methods
+//=================
+
+   protected Proxy copy(){
+       Socks5Proxy copy = new Socks5Proxy(proxyIP,proxyPort);
+       copy.authMethods = this.authMethods; //same Hash, no copy
+       copy.directHosts = this.directHosts;
+       copy.chainProxy = this.chainProxy;
+       copy.resolveAddrLocally = this.resolveAddrLocally;
+       return copy;
+    }
+   /**
+    *
+    *
+    */
+   protected void startSession()throws SocksException{
+      super.startSession();
+      Authentication auth;
+      Socket ps = proxySocket; //The name is too long
+
+      try{
+
+	 byte nMethods = (byte) authMethods.size();  //Number of methods
+
+         byte[] buf = new byte[2+nMethods]; //2 is for VER,NMETHODS
+         buf[0] = (byte) version;
+         buf[1] = nMethods;                 //Number of methods 
+         int i=2;
+
+         Enumeration ids = authMethods.keys();
+         while(ids.hasMoreElements())
+            buf[i++] = (byte)((Integer)ids.nextElement()).intValue();
+
+         out.write(buf);
+         out.flush();
+
+         int versionNumber  = in.read();
+         selectedMethod = in.read();
+
+         if(versionNumber < 0 || selectedMethod < 0){
+           //EOF condition was reached
+           endSession();
+           throw(new SocksException(SOCKS_PROXY_IO_ERROR,
+                 "Connection to proxy lost."));
+         }
+         if(versionNumber < version){
+           //What should we do??
+         }
+         if(selectedMethod == 0xFF){ //No method selected
+            ps.close();
+            throw ( new SocksException(SOCKS_AUTH_NOT_SUPPORTED));
+         }
+
+         auth = getAuthenticationMethod(selectedMethod);
+         if(auth == null){
+            //This shouldn't happen, unless method was removed by other
+            //thread, or the server stuffed up
+            throw(new SocksException(SOCKS_JUST_ERROR,
+                      "Speciefied Authentication not found!"));
+         }
+         Object[] in_out = auth.doSocksAuthentication(selectedMethod,ps);
+         if(in_out == null){
+            //Authentication failed by some reason
+            throw(new SocksException(SOCKS_AUTH_FAILURE));
+         }
+         //Most authentication methods are expected to return
+         //simply the input/output streams associated with
+         //the socket. However if the auth. method requires
+         //some kind of encryption/decryption being done on the
+         //connection it should provide classes to handle I/O.
+
+         in = (InputStream) in_out[0];
+         out = (OutputStream) in_out[1];
+         if(in_out.length > 2) 
+            udp_encapsulation = (UDPEncapsulation) in_out[2];
+
+      }catch(SocksException s_ex){
+         throw s_ex;
+      }catch(UnknownHostException uh_ex){
+         throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
+      }catch(SocketException so_ex){
+         throw(new SocksException(SOCKS_PROXY_NO_CONNECT));
+      }catch(IOException io_ex){
+         //System.err.println(io_ex);
+         throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex));
+      }
+   }
+
+   protected ProxyMessage formMessage(int cmd,InetAddress ip,int port){
+       return new Socks5Message(cmd,ip,port);
+   }
+   protected ProxyMessage formMessage(int cmd,String host,int port)
+             throws UnknownHostException{
+       //if(resolveAddrLocally)
+         // return formMessage(cmd,InetAddress.getByName(host),port);
+       //else
+          return new Socks5Message(cmd,host,port);
+   }
+   protected ProxyMessage formMessage(InputStream in)
+             throws SocksException,
+                    IOException{
+       return new Socks5Message(in);
+   }
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks4Message.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks4Message.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks4Message.java	(revision 3952)
@@ -0,0 +1,157 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+import java.io.*;
+import java.net.*;
+
+/** 
+  SOCKS4 Reply/Request message.
+*/
+
+class Socks4Message extends ProxyMessage{
+
+   private byte[] msgBytes;
+   private int msgLength;
+
+   /**
+    * Server failed reply, cmd command for failed request
+    */
+   public Socks4Message(int cmd){
+      super(cmd,null,0);
+      this.user    = null;
+
+      msgLength = 2;
+      msgBytes = new byte[2];
+
+      msgBytes[0] = (byte) 0;
+      msgBytes[1] = (byte) command;
+   }
+
+   /** 
+    *  Server successfull reply
+    */
+   public Socks4Message(int cmd,InetAddress ip,int port){
+      this(0,cmd,ip,port,null);
+   }
+
+   /** 
+    *  Client request
+    */
+   public Socks4Message(int cmd,InetAddress ip,int port,String user){
+      this(SOCKS_VERSION,cmd,ip,port,user);
+   }
+
+   /**
+    * Most general constructor
+    */
+   public Socks4Message(int version, int cmd,
+                        InetAddress ip,int port,String user){
+      super(cmd,ip,port);
+      this.user    = user;
+      this.version = version;
+
+      msgLength = user == null?8:9+user.length();
+      msgBytes = new byte[msgLength];
+
+      msgBytes[0] = (byte) version;
+      msgBytes[1] = (byte) command;
+      msgBytes[2] = (byte) (port >> 8);
+      msgBytes[3] = (byte) port;
+
+      byte[] addr;
+
+      if(ip != null)
+        addr = ip.getAddress();
+      else{
+        addr = new byte[4];
+        addr[0]=addr[1]=addr[2]=addr[3]=0;
+      }
+      System.arraycopy(addr,0,msgBytes,4,4);
+
+      if(user != null){
+         byte[] buf = user.getBytes();
+         System.arraycopy(buf,0,msgBytes,8,buf.length);
+         msgBytes[msgBytes.length -1 ] = 0;
+      }
+   }
+
+   /**
+    *Initialise from the stream
+    *If clientMode is true attempts to read a server response
+    *otherwise reads a client request
+    *see read for more detail
+    */
+   public Socks4Message(InputStream in, boolean clientMode) throws IOException{
+      msgBytes = null;
+      read(in,clientMode);
+   }
+
+   public void read(InputStream in) throws IOException{
+        read(in,true);
+   }
+
+   public void read(InputStream in, boolean clientMode) throws IOException{
+       DataInputStream d_in = new DataInputStream(in);
+       version= d_in.readUnsignedByte();
+       command = d_in.readUnsignedByte();
+       if(clientMode && command != REPLY_OK){
+          String errMsg;
+          if(command >REPLY_OK && command < REPLY_BAD_IDENTD)
+             errMsg = replyMessage[command-REPLY_OK];
+          else
+             errMsg = "Unknown Reply Code";
+          throw new SocksException(command,errMsg);
+       }
+       port = d_in.readUnsignedShort();
+       byte[] addr = new byte[4];
+       d_in.readFully(addr);
+       ip=bytes2IP(addr);
+       host = ip.getHostName();
+       if(!clientMode){
+          int b = in.read();
+          //Hope there are no idiots with user name bigger than this
+          byte[] userBytes = new byte[256];
+          int i = 0;
+          for(i =0;i<userBytes.length && b>0;++i){
+             userBytes[i] = (byte) b;
+             b = in.read();
+          }
+          user = new String(userBytes,0,i);
+       }
+   }
+   public void write(OutputStream out) throws IOException{
+      if(msgBytes == null){
+         Socks4Message msg = new Socks4Message(version,command,ip,port,user);
+         msgBytes = msg.msgBytes;
+         msgLength = msg.msgLength;
+      }
+      out.write(msgBytes);
+   }
+
+   //Class methods
+   static InetAddress bytes2IP(byte[] addr){
+      String s = bytes2IPV4(addr,0);
+      try{
+         return InetAddress.getByName(s);
+      }catch(UnknownHostException uh_ex){
+        return null;
+      }
+   }
+
+   //Constants
+
+   static final String[] replyMessage ={
+          "Request Granted",
+          "Request Rejected or Failed",
+          "Failed request, can't connect to Identd",
+          "Failed request, bad user name"};
+
+   static final int SOCKS_VERSION = 4;
+
+   public final static int REQUEST_CONNECT		= 1;
+   public final static int REQUEST_BIND   		= 2;
+
+   public final static int REPLY_OK 			= 90;
+   public final static int REPLY_REJECTED		= 91;
+   public final static int REPLY_NO_CONNECT		= 92;
+   public final static int REPLY_BAD_IDENTD		= 93;
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/UDPRelayServer.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/UDPRelayServer.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/UDPRelayServer.java	(revision 3952)
@@ -0,0 +1,214 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.PrintStream;
+import java.net.*;
+
+
+/**
+ UDP Relay server, used by ProxyServer to perform udp forwarding.
+*/
+class UDPRelayServer implements Runnable{
+
+
+    DatagramSocket client_sock; 
+    DatagramSocket remote_sock;
+
+    Socket controlConnection;
+
+    int relayPort;
+    InetAddress relayIP;
+
+    Thread pipe_thread1,pipe_thread2;
+    Thread master_thread;
+
+    ServerAuthenticator auth;
+
+    long lastReadTime;
+
+    static PrintStream log = null;
+    static Proxy proxy = null;
+    static int datagramSize = 0xFFFF;//64K, a bit more than max udp size
+    static int iddleTimeout = 180000;//3 minutes
+
+
+    /**
+      Constructs UDP relay server to communicate with client
+      on given ip and port.
+      @param clientIP Address of the client from whom datagrams
+      will be recieved and to whom they will be forwarded.
+      @param clientPort Clients port.
+      @param master_thread Thread which will be interrupted, when
+      UDP relay server stoppes for some reason.
+      @param controlConnection Socket which will be closed, before
+      interrupting the master thread, it is introduced due to a bug
+      in windows JVM which does not throw InterruptedIOException in
+      threads which block in I/O operation.
+    */
+    public UDPRelayServer(InetAddress clientIP,int clientPort,
+                          Thread master_thread,
+                          Socket controlConnection,
+                          ServerAuthenticator auth)
+                          throws IOException{
+       this.master_thread = master_thread;
+       this.controlConnection = controlConnection;
+       this.auth = auth;
+
+       client_sock = new Socks5DatagramSocket(true,auth.getUdpEncapsulation(),
+                                              clientIP,clientPort);
+       relayPort = client_sock.getLocalPort();
+       relayIP   = client_sock.getLocalAddress();
+
+       if(relayIP.getHostAddress().equals("0.0.0.0"))
+         relayIP   = InetAddress.getLocalHost();
+
+       if(proxy == null)
+          remote_sock = new DatagramSocket();
+       else
+          remote_sock = new Socks5DatagramSocket(proxy,0,null);
+    }
+
+
+//Public methods
+/////////////////
+
+
+   /**
+    Sets the timeout for UDPRelay server.<br>
+    Zero timeout implies infinity.<br>
+    Default timeout is 3 minutes.
+    */
+
+    static public void setTimeout(int timeout){
+      iddleTimeout = timeout;
+    }
+
+
+   /**
+     Sets the size of the datagrams used in the UDPRelayServer.<br>
+     Default size is 64K, a bit more than maximum possible size of the
+     datagram.
+    */
+    static public void setDatagramSize(int size){
+      datagramSize = size;
+    }
+
+    /**
+      Port to which client should send datagram for association.
+    */
+    public int getRelayPort(){
+       return relayPort;
+    }
+    /**
+     IP address to which client should send datagrams for association.
+    */
+    public InetAddress getRelayIP(){
+       return relayIP;
+    }
+
+    /**
+      Starts udp relay server.
+      Spawns two threads of execution and returns.
+    */
+    public void start() throws IOException{
+       remote_sock.setSoTimeout(iddleTimeout);
+       client_sock.setSoTimeout(iddleTimeout);
+
+       log("Starting UDP relay server on "+relayIP+":"+relayPort);
+       log("Remote socket "+remote_sock.getLocalAddress()+":"+
+                            remote_sock.getLocalPort());
+
+       pipe_thread1 = new Thread(this,"pipe1");
+       pipe_thread2 = new Thread(this,"pipe2");
+
+       lastReadTime = System.currentTimeMillis();
+
+       pipe_thread1.start();
+       pipe_thread2.start();
+    }
+
+    /**
+     Stops Relay server.
+     <p>
+     Does not close control connection, does not interrupt master_thread.
+    */
+    public synchronized void stop(){
+       master_thread = null;
+       controlConnection = null;
+       abort();
+    }
+
+//Runnable interface
+////////////////////
+    public void run(){
+       try{
+          if(Thread.currentThread().getName().equals("pipe1"))
+             pipe(remote_sock,client_sock,false);
+          else
+             pipe(client_sock,remote_sock,true);
+       }catch(IOException ioe){
+       }finally{
+          abort();
+          log("UDP Pipe thread "+Thread.currentThread().getName()+" stopped.");
+       }
+
+    }
+
+//Private methods
+/////////////////
+    private synchronized void abort(){
+       if(pipe_thread1 == null) return;
+
+       log("Aborting UDP Relay Server");
+
+       remote_sock.close();
+       client_sock.close();
+
+       if(controlConnection != null) 
+          try{ controlConnection.close();} catch(IOException ioe){}
+
+       if(master_thread!=null) master_thread.interrupt();
+
+       pipe_thread1.interrupt();
+       pipe_thread2.interrupt();
+
+       pipe_thread1 = null;
+    }
+
+
+    static private void log(String s){
+      if(log != null){
+        log.println(s);
+        log.flush();
+      }
+    }
+
+    private void pipe(DatagramSocket from,DatagramSocket to,boolean out)
+                             throws IOException{
+       byte[] data = new byte[datagramSize];
+       DatagramPacket dp = new DatagramPacket(data,data.length);
+
+       while(true){
+          try{
+            from.receive(dp);
+            lastReadTime = System.currentTimeMillis();
+
+            if(auth.checkRequest(dp,out))
+               to.send(dp);
+
+          }catch(UnknownHostException uhe){
+            log("Dropping datagram for unknown host");
+          }catch(InterruptedIOException iioe){
+            //log("Interrupted: "+iioe);
+            //If we were interrupted by other thread.
+            if(iddleTimeout == 0) return;
+
+            //If last datagram was received, long time ago, return.
+            long timeSinceRead = System.currentTimeMillis() - lastReadTime;
+            if(timeSinceRead >= iddleTimeout -100) //-100 for adjustment
+               return;
+          }
+          dp.setLength(data.length);
+       }
+    }
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/InetRange.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/InetRange.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/InetRange.java	(revision 3952)
@@ -0,0 +1,440 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.*;
+
+/**
+ * Class InetRange provides the means of defining the range of inetaddresses.
+ * It's used by Proxy class to store and look up addresses of machines, that
+ * should be contacted directly rather then through the proxy.
+ * <P>
+ * InetRange provides several methods to add either standalone addresses, or
+ * ranges (e.g. 100.200.300.0:100.200.300.255, which covers all addresses
+ * on on someones local network). It also provides methods for checking wether
+ * given address is in this range. Any number of ranges and standalone 
+ * addresses can be added to the range.
+ */
+public class InetRange implements Cloneable{
+
+    Hashtable host_names;
+    Vector all;
+    Vector end_names;
+
+    boolean useSeparateThread = true;
+
+    /**
+     * Creates the empty range.
+     */
+    public InetRange(){
+      all = new Vector();
+      host_names = new Hashtable();
+      end_names = new Vector();
+    }
+
+    /**
+     *  Adds another host or range to this range.
+        The String can be one of those:
+        <UL>
+        <li> Host name. eg.(Athena.myhost.com or 45.54.56.65)
+        
+        <li> Range in the form .myhost.net.au <BR>
+             In which case anything that ends with .myhost.net.au will
+             be considered in the range.
+
+        <li> Range in the form ddd.ddd.ddd. <BR>
+             This will be treated as range ddd.ddd.ddd.0 to ddd.ddd.ddd.255.
+             It is not necessary to specify 3 first bytes you can use just 
+             one or two. For example 130. will cover address between 130.0.0.0
+             and 13.255.255.255.
+
+        <li> Range in the form host_from[: \t\n\r\f]host_to. <br>
+             That is two hostnames or ips separated by either whitespace 
+             or colon.
+        </UL>
+     */
+    public synchronized boolean add(String s){
+      if(s == null) return false;
+
+      s = s.trim();
+      if(s.length() == 0) return false;
+
+      Object[] entry;
+
+      if(s.charAt(s.length()-1) == '.'){
+         //thing like: 111.222.33. 
+         //it is being treated as range 111.222.33.000 - 111.222.33.255
+
+         int[] addr = ip2intarray(s);
+         long from,to;
+         from = to = 0;
+
+         if(addr == null) return false;
+         for(int i = 0; i< 4;++i){
+            if(addr[i]>=0)
+              from += (((long)addr[i]) << 8*(3-i));
+            else{
+              to = from;
+              while(i<4)
+                to += 255l << 8*(3-i++);
+              break;
+            }
+         }
+         entry = new Object[] {s,null,new Long(from),new Long(to)};
+         all.addElement(entry);
+
+      }else if(s.charAt(0) == '.'){
+         //Thing like: .myhost.com
+
+         end_names.addElement(s);
+         all.addElement(new Object[]{s,null,null,null});
+      }else{
+         StringTokenizer tokens = new StringTokenizer(s," \t\r\n\f:");
+         if(tokens.countTokens() > 1){
+           entry = new Object[] {s,null,null,null};
+           resolve(entry,tokens.nextToken(),tokens.nextToken());
+           all.addElement(entry);
+         }else{
+           entry = new Object[] {s,null,null,null};
+           all.addElement(entry);
+           host_names.put(s,entry);
+           resolve(entry);
+         }
+
+      }
+
+      return true;
+    }
+
+    /**
+     *  Adds another ip for this range.
+        @param ip IP os the host which should be added to this range.
+     */
+    public synchronized void add(InetAddress ip){
+       long from, to;
+       from = to = ip2long(ip);
+       all.addElement(new Object[]{ip.getHostName(),ip,new Long(from),
+                                                       new Long(to)});
+    }
+
+    /**
+     *  Adds another range of ips for this range.Any host with ip address
+        greater than or equal to the address of from and smaller than or equal
+        to the address of to will be included in the range.
+        @param from IP from where range starts(including).
+        @param to   IP where range ends(including).
+     */
+    public synchronized void add(InetAddress from,InetAddress to){
+       all.addElement(new Object[]{from.getHostAddress()+":"+to.getHostAddress()
+                            ,null,new Long(ip2long(from)),
+                                  new Long(ip2long(to))});
+    }
+
+    /**
+     * Checks wether the givan host is in the range. Attempts to resolve
+       host name if required.
+       @param host Host name to check.
+       @return true If host is in the range, false otherwise.
+     * @see InetRange#contains(String,boolean)
+     */
+    public synchronized boolean contains(String host){
+       return contains(host,true);
+    }
+
+    /**
+     *  Checks wether the given host is in the range.
+     *  <P>
+     *  Algorithm: <BR>
+     *  <ol>
+     *  <li>Look up if the hostname is in the range (in the Hashtable).
+     *  <li>Check if it ends with one of the speciefied endings.
+     *  <li>Check if it is ip(eg.130.220.35.98). If it is check if it is
+     *      in the range.
+     *  <li>If attemptResolve is true, host is name, rather than ip, and
+     *      all previous attempts failed, try to resolve the hostname, and
+     *      check wether the ip associated with the host is in the range.It
+     *      also repeats all previos steps with the hostname obtained from
+     *      InetAddress, but the name is not allways the full name,it is 
+     *      quite likely to be the same. Well it was on my machine.
+     *  </ol>
+       @param host Host name to check.
+       @param attemptResolve Wether to lookup ip address which corresponds
+       to the host,if required.
+       @return true If host is in the range, false otherwise.
+     */
+    public synchronized boolean contains(String host,boolean attemptResolve){
+       if(all.size() ==0) return false; //Empty range
+
+       host = host.trim();
+       if(host.length() == 0) return false;
+
+       if(checkHost(host)) return true;
+       if(checkHostEnding(host)) return true;
+
+       long l = host2long(host);
+       if(l >=0) return contains(l);
+
+       if(!attemptResolve) return false;
+
+       try{
+          InetAddress ip = InetAddress.getByName(host);
+          return contains(ip);
+       }catch(UnknownHostException uhe){
+
+       }
+
+       return false;
+    }
+
+    /**
+     * Checks wether the given ip is in the range.
+       @param ip Address of the host to check.
+       @return true If host is in the range, false otherwise.
+     */
+    public synchronized boolean contains(InetAddress ip){
+       if(checkHostEnding(ip.getHostName())) return true;
+       if(checkHost(ip.getHostName())) return true;
+       return contains(ip2long(ip));
+    }
+    /**
+       Get all entries in the range as strings. <BR>
+       These strings can be used to delete entries from the range
+       with remove function.
+       @return Array of entries as strings.
+       @see InetRange#remove(String)
+     */
+    public synchronized String[] getAll(){
+       int size = all.size();
+       Object entry[];
+       String all_names[] = new String[size];
+
+       for(int i=0;i<size;++i){
+          entry = (Object[]) all.elementAt(i);
+          all_names[i] = (String) entry[0];
+       }
+       return all_names;
+    }
+    /**
+      Removes an entry from this range.<BR>
+      @param s Entry to remove.
+      @return true if successfull.
+     */
+    public synchronized boolean remove(String s){
+      Enumeration e = all.elements();
+      while(e.hasMoreElements()){
+        Object[] entry = (Object[]) e.nextElement();
+        if(s.equals(entry[0])){
+          all.removeElement(entry);
+          end_names.removeElement(s);
+          host_names.remove(s);
+          return true;
+        }
+      }
+      return false;
+    }
+
+    /** Get string representaion of this Range.*/
+    public String toString(){
+       String all[] = getAll();
+       if(all.length == 0) return "";
+
+       String s = all[0];
+       for(int i=1;i<all.length;++i)
+          s += "; "+all[i];
+       return s;
+    }
+
+    /** Creates a clone of this Object*/
+    public Object clone(){
+      InetRange new_range = new InetRange();
+      new_range.all = (Vector)all.clone();
+      new_range.end_names = (Vector) end_names.clone();
+      new_range.host_names = (Hashtable)host_names.clone();
+      return new_range;
+    }
+
+
+//Private methods
+/////////////////
+    /**
+     * Same as previous but used internally, to avoid
+     * unnecessary convertion of IPs, when checking subranges
+     */
+    private synchronized boolean contains(long ip){
+       Enumeration e = all.elements();
+       while(e.hasMoreElements()){
+         Object[] obj = (Object[]) e.nextElement();
+         Long from = obj[2]==null?null:(Long)obj[2];
+         Long to   = obj[3]==null?null:(Long)obj[3];
+         if(from != null && from.longValue()<= ip 
+                         && to.longValue() >= ip) return true;
+
+       }
+       return false;
+    }
+
+    private boolean checkHost(String host){
+       return host_names.containsKey(host);
+    }
+    private boolean checkHostEnding(String host){
+       Enumeration e = end_names.elements();
+       while(e.hasMoreElements()){
+          if(host.endsWith((String) e.nextElement())) return true;
+       }
+       return false;
+    }
+    private void resolve(Object[] entry){
+       //First check if it's in the form ddd.ddd.ddd.ddd.
+       long ip = host2long((String) entry[0]);
+       if(ip >= 0){
+         entry[2] = entry[3] = new Long(ip);
+       }else{
+         InetRangeResolver res = new InetRangeResolver(entry);
+         res.resolve(useSeparateThread);
+       }
+    }
+    private void resolve(Object[] entry,String from,String to){
+       long f,t;
+       if((f=host2long(from))>= 0 && (t=host2long(to)) >= 0){
+         entry[2] = new Long(f);
+         entry[3] = new Long(t);
+       }else{
+         InetRangeResolver res = new InetRangeResolver(entry,from,to);
+         res.resolve(useSeparateThread);
+       }
+    }
+
+
+
+//Class methods
+///////////////
+
+    //Converts ipv4 to long value(unsigned int)
+    ///////////////////////////////////////////
+    static long ip2long(InetAddress ip){
+        long l=0;
+        byte[] addr = ip.getAddress();
+
+        if(addr.length ==4){ //IPV4
+          for(int i=0;i<4;++i)
+             l += (((long)addr[i] &0xFF) << 8*(3-i));
+        }else{ //IPV6
+          return 0;  //Have no idea how to deal with those
+        }
+        return l;
+    }
+
+
+    long host2long(String host){
+      long ip=0;
+
+      //check if it's ddd.ddd.ddd.ddd
+      if(!Character.isDigit(host.charAt(0))) return -1;
+
+      int[] addr = ip2intarray(host); 
+      if(addr == null) return -1;
+
+      for(int i=0;i<addr.length;++i)
+          ip += ((long)(addr[i]>=0 ? addr[i] : 0)) << 8*(3-i);
+
+      return ip;
+    }
+
+    static int[] ip2intarray(String host){
+       int[] address = {-1,-1,-1,-1};
+       int i=0;
+       StringTokenizer tokens = new StringTokenizer(host,".");
+       if(tokens.countTokens() > 4) return null;
+       while(tokens.hasMoreTokens()){
+         try{
+           address[i++] = Integer.parseInt(tokens.nextToken()) & 0xFF;
+         }catch(NumberFormatException nfe){
+            return null;
+         }
+
+       }
+       return address;
+    }
+
+
+/*
+//* This was the test main function
+//**********************************
+ 
+    public static void main(String args[])throws UnknownHostException{
+       int i;
+
+       InetRange ir = new InetRange();
+
+
+       for(i=0;i<args.length;++i){
+         System.out.println("Adding:" + args[i]);
+         ir.add(args[i]);
+       }
+
+       String host;
+       java.io.DataInputStream din = new java.io.DataInputStream(System.in);
+       try{
+          host = din.readLine();
+          while(host!=null){
+            if(ir.contains(host)){
+              System.out.println("Range contains ip:"+host);
+            }else{
+              System.out.println(host+" is not in the range");
+            }
+            host = din.readLine();
+          }
+       }catch(java.io.IOException io_ex){
+          io_ex.printStackTrace();
+       }
+    }
+********************/
+
+    class InetRangeResolver implements Runnable{
+
+        Object[] entry;
+
+        String from, to;
+
+        InetRangeResolver(Object[] entry){
+            this.entry = entry;
+            from = to = null;
+        }
+        InetRangeResolver(Object[] entry,String from,String to){
+            this.entry = entry;
+            this.from  = from;
+            this.to    = to;
+        }
+        public final void resolve(){
+            resolve(true);
+        }
+        public final void resolve(boolean inSeparateThread){
+            if(inSeparateThread){
+                Thread t = new Thread(this);
+                t.start();
+            }else
+                run();
+
+        }
+        public void run(){
+            try{
+                if(from == null){
+                    InetAddress ip = InetAddress.getByName((String) entry[0]);
+                    entry[1] = ip;
+                    Long l = new Long(InetRange.ip2long(ip));
+                    entry[2] = entry[3] = l;
+                }else{
+                    InetAddress f = InetAddress.getByName(from);
+                    InetAddress t = InetAddress.getByName(to);
+                    entry[2] = new Long(InetRange.ip2long(f));
+                    entry[3] = new Long(InetRange.ip2long(t));
+
+                }
+            }catch(UnknownHostException uhe){
+                //System.err.println("Resolve failed for "+from+','+to+','+entry[0]);
+            }
+        }
+
+    }
+}
+
+
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5Message.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5Message.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5Message.java	(revision 3952)
@@ -0,0 +1,291 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.DataInputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ SOCKS5 request/response message.
+*/
+
+class Socks5Message extends ProxyMessage{
+   /** Address type of given message*/
+   public int addrType;
+
+   byte[] data;
+
+   /**
+    Server error response.
+    @param cmd Error code.
+   */
+   public Socks5Message(int cmd){
+      super(cmd,null,0);
+      data = new byte[3];
+      data[0] = SOCKS_VERSION; //Version.
+      data[1] = (byte)cmd;     //Reply code for some kind of failure.
+      data[2] = 0;             //Reserved byte.
+   }
+
+   /**
+    Construct client request or server response.
+    @param cmd - Request/Response code.
+    @param ip  - IP field.
+    @paarm port - port field.
+   */
+   public Socks5Message(int cmd,InetAddress ip,int port){
+      super(cmd,ip,port);
+      this.host = ip==null?"0.0.0.0":ip.getHostName();
+      this.version = SOCKS_VERSION;
+
+      byte[] addr;
+
+      if(ip == null){
+         addr = new byte[4];
+         addr[0]=addr[1]=addr[2]=addr[3]=0;
+      }else
+         addr = ip.getAddress();
+
+      addrType = addr.length == 4 ? SOCKS_ATYP_IPV4
+                                  : SOCKS_ATYP_IPV6;
+ 
+      data = new byte[6+addr.length];
+      data[0] = (byte) SOCKS_VERSION;		//Version
+      data[1] = (byte) command;			//Command
+      data[2] = (byte) 0;			//Reserved byte
+      data[3] = (byte) addrType;		//Address type
+ 
+      //Put Address
+      System.arraycopy(addr,0,data,4,addr.length);
+      //Put port
+      data[data.length-2] = (byte)(port>>8);
+      data[data.length-1] = (byte)(port);
+   }
+
+
+   /**
+    Construct client request or server response.
+    @param cmd - Request/Response code.
+    @param hostName  - IP field as hostName, uses ADDR_TYPE of HOSTNAME.
+    @paarm port - port field.
+   */
+   public Socks5Message(int cmd,String hostName,int port){
+      super(cmd,null,port);
+      this.host = hostName;
+      this.version = SOCKS_VERSION;
+
+      //System.out.println("Doing ATYP_DOMAINNAME");
+
+      addrType = SOCKS_ATYP_DOMAINNAME;
+      byte addr[] = hostName.getBytes();
+ 
+      data =new byte[7+addr.length];
+      data[0] = (byte) SOCKS_VERSION;		//Version
+      data[1] = (byte) command;			//Command
+      data[2] = (byte) 0;			//Reserved byte
+      data[3] = (byte) SOCKS_ATYP_DOMAINNAME;	//Address type
+      data[4] = (byte) addr.length;		//Length of the address
+
+      //Put Address
+      System.arraycopy(addr,0,data,5,addr.length);
+      //Put port
+      data[data.length-2] = (byte)(port >>8);
+      data[data.length-1] = (byte)(port);
+   }
+
+   /**
+     Initialises Message from the stream. Reads server response from
+     given stream.
+     @param in Input stream to read response from.
+     @throws SocksException If server response code is not SOCKS_SUCCESS(0), or
+     if any error with protocol occurs.
+     @throws IOException If any error happens with I/O.
+   */
+   public Socks5Message(InputStream in) throws SocksException,
+                                              IOException{
+      this(in,true);
+   }
+
+   /**
+     Initialises Message from the stream. Reads server response or client 
+     request from given stream.
+     
+     @param in Input stream to read response from.
+     @param clinetMode If true read server response, else read client request.
+     @throws SocksException If server response code is not SOCKS_SUCCESS(0) and
+     reading in client mode, or if any error with protocol occurs.
+     @throws IOException If any error happens with I/O.
+   */
+   public Socks5Message(InputStream in,boolean clientMode)throws SocksException,
+                                              IOException{
+      read(in,clientMode);
+   }
+
+
+   /**
+     Initialises Message from the stream. Reads server response from
+     given stream.
+     @param in Input stream to read response from.
+     @throws SocksException If server response code is not SOCKS_SUCCESS(0), or
+     if any error with protocol occurs.
+     @throws IOException If any error happens with I/O.
+   */
+   public void read(InputStream in) throws SocksException,
+                                           IOException{
+       read(in,true);
+   }
+
+
+   /**
+     Initialises Message from the stream. Reads server response or client 
+     request from given stream.
+     
+     @param in Input stream to read response from.
+     @param clinetMode If true read server response, else read client request.
+     @throws SocksException If server response code is not SOCKS_SUCCESS(0) and
+     reading in client mode, or if any error with protocol occurs.
+     @throws IOException If any error happens with I/O.
+   */
+   public void read(InputStream in,boolean clientMode) throws SocksException,
+                                           IOException{
+      data = null;
+      ip = null;
+
+      DataInputStream di = new DataInputStream(in);
+
+      version = di.readUnsignedByte();
+      command = di.readUnsignedByte();
+      if(clientMode && command != 0)
+        throw new SocksException(command);
+
+      int reserved = di.readUnsignedByte();
+      addrType = di.readUnsignedByte();
+
+      byte addr[];
+
+      switch(addrType){
+         case SOCKS_ATYP_IPV4:
+            addr = new byte[4];
+            di.readFully(addr);
+            host = bytes2IPV4(addr,0);
+         break;
+         case SOCKS_ATYP_IPV6:
+           addr = new byte[SOCKS_IPV6_LENGTH];//I believe it is 16 bytes,huge!
+           di.readFully(addr);
+           host = bytes2IPV6(addr,0);
+         break;
+         case SOCKS_ATYP_DOMAINNAME:
+           //System.out.println("Reading ATYP_DOMAINNAME");
+           addr = new byte[di.readUnsignedByte()];//Next byte shows the length
+           di.readFully(addr);
+           host = new String(addr);
+         break;
+         default:
+            throw(new SocksException(Proxy.SOCKS_JUST_ERROR));
+      }
+
+      port = di.readUnsignedShort();
+
+      if(addrType != SOCKS_ATYP_DOMAINNAME && doResolveIP){
+         try{
+            ip = InetAddress.getByName(host);
+         }catch(UnknownHostException uh_ex){
+         }
+      }
+   }
+
+   /**
+    Writes the message to the stream.
+    @param out Output stream to which message should be written.
+   */
+   public void write(OutputStream out)throws SocksException,
+                                             IOException{
+     if(data == null){
+       Socks5Message msg;
+
+       if(addrType == SOCKS_ATYP_DOMAINNAME)
+          msg = new Socks5Message(command,host,port);
+       else{
+          if(ip == null){
+             try{
+               ip = InetAddress.getByName(host);
+             }catch(UnknownHostException uh_ex){
+               throw new SocksException(Proxy.SOCKS_JUST_ERROR);
+             }
+          }
+          msg = new Socks5Message(command,ip,port);
+       }
+       data = msg.data;
+     }
+     out.write(data);
+   }
+
+   /**
+    Returns IP field of the message as IP, if the message was created
+    with ATYP of HOSTNAME, it will attempt to resolve the hostname,
+    which might fail.
+    @throws UnknownHostException if host can't be resolved.
+   */
+   public InetAddress getInetAddress() throws UnknownHostException{
+     if(ip!=null) return ip;
+
+     return (ip=InetAddress.getByName(host));
+   }
+
+   /**
+     Returns string representation of the message.
+   */
+   public String toString(){
+      String s=
+        "Socks5Message:"+"\n"+
+        "VN   "+version+"\n"+
+        "CMD  "+command+"\n"+
+        "ATYP "+addrType+"\n"+
+        "ADDR "+host+"\n"+
+        "PORT "+port+"\n";
+      return s;
+   }
+            
+
+   /**
+    *Wether to resolve hostIP returned from SOCKS server
+    *that is wether to create InetAddress object from the
+    *hostName string
+    */
+   static public boolean resolveIP(){ return doResolveIP;}
+
+   /**
+    *Wether to resolve hostIP returned from SOCKS server
+    *that is wether to create InetAddress object from the
+    *hostName string
+    *@param doResolve Wether to resolve hostIP from SOCKS server.
+    *@return Previous value.
+    */
+   static public boolean resolveIP(boolean doResolve){
+      boolean old = doResolveIP;
+      doResolveIP = doResolve;
+      return old;
+   }
+
+   /*
+   private static final void debug(String s){
+      if(DEBUG)
+         System.out.print(s);
+   }
+   private static final boolean DEBUG = false;
+   */
+
+   //SOCKS5 constants
+   public static final int SOCKS_VERSION		=5;
+
+   public static final int SOCKS_ATYP_IPV4		=0x1; //Where is 2??
+   public static final int SOCKS_ATYP_DOMAINNAME	=0x3; //!!!!rfc1928
+   public static final int SOCKS_ATYP_IPV6		=0x4;
+
+   public static final int SOCKS_IPV6_LENGTH		=16;
+
+   static boolean doResolveIP = true;
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/UDPEncapsulation.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/UDPEncapsulation.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/UDPEncapsulation.java	(revision 3952)
@@ -0,0 +1,29 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+/**
+ This interface provides for datagram encapsulation for SOCKSv5 protocol.
+ <p>
+ SOCKSv5 allows for datagrams to be encapsulated for purposes of integrity
+ and/or authenticity. How it should be done is aggreed during the 
+ authentication stage, and is authentication dependent. This interface is
+ provided to allow this encapsulation.
+ @see Authentication
+*/
+public interface UDPEncapsulation{
+
+    /**
+    This method should provide any authentication depended transformation
+    on datagrams being send from/to the client.
+
+    @param data Datagram data (including any SOCKS related bytes), to be
+                encapsulated/decapsulated.
+    @param out  Wether the data is being send out. If true method should 
+                encapsulate/encrypt data, otherwise it should decapsulate/
+                decrypt data.
+    @throw IOException if for some reason data can be transformed correctly.
+    @return Should return byte array containing data after transformation.
+            It is possible to return same array as input, if transformation
+            only involves bit mangling, and no additional data is being
+            added or removed.
+    */
+    byte[] udpEncapsulate(byte[] data, boolean out) throws java.io.IOException;
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Authentication.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Authentication.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Authentication.java	(revision 3952)
@@ -0,0 +1,34 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+/**
+ The Authentication interface provides for performing method specific
+ authentication for SOCKS5 connections.
+*/
+public interface Authentication{
+   /**
+     This method is called when SOCKS5 server have selected a particular
+     authentication method, for whch an implementaion have been registered.
+
+     <p>
+     This method should return an array {inputstream,outputstream 
+     [,UDPEncapsulation]}. The reason for that is that SOCKS5 protocol
+     allows to have method specific encapsulation of data on the socket for 
+     purposes of integrity or security. And this encapsulation should be 
+     performed by those streams returned from the method. It is also possible
+     to encapsulate datagrams. If authentication method supports such 
+     encapsulation an instance of the UDPEncapsulation interface should be
+     returned as third element of the array, otherwise either null should be
+     returned as third element, or array should contain only 2 elements.
+
+     @param methodId Authentication method selected by the server.
+     @param proxySocket Socket used to conect to the proxy.
+     @return Two or three element array containing 
+             Input/Output streams which should be used on this connection.
+             Third argument is optional and should contain an instance
+             of UDPEncapsulation. It should be provided if the authentication
+             method used requires any encapsulation to be done on the
+             datagrams.
+   */
+   Object[] doSocksAuthentication(int methodId,java.net.Socket proxySocket)
+           throws java.io.IOException;
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Proxy.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Proxy.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Proxy.java	(revision 3952)
@@ -0,0 +1,419 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+/**
+  Abstract class Proxy, base for classes Socks4Proxy and Socks5Proxy.
+  Defines methods for specifying default proxy, to be
+  used by all classes of this package.
+*/
+
+public abstract class Proxy{
+
+//Data members
+   protected InetRange directHosts = new InetRange();
+
+   protected InetAddress proxyIP = null;
+   protected String proxyHost = null;
+   protected int proxyPort;
+   protected Socket proxySocket = null;
+
+   protected InputStream in;
+   protected OutputStream out;
+
+   protected int version;
+
+   protected Proxy chainProxy = null;
+
+
+//Protected static/class variables
+   protected static Proxy defaultProxy = null;
+
+//Constructors
+//====================
+   Proxy(Proxy chainProxy,
+         String proxyHost,int proxyPort)throws UnknownHostException{ 
+      this.chainProxy = chainProxy;
+      this.proxyHost = proxyHost;
+
+      if(chainProxy == null)
+         this.proxyIP   = InetAddress.getByName(proxyHost);
+      
+      this.proxyPort = proxyPort;
+   }
+
+  
+   Proxy(String proxyHost,int proxyPort)throws UnknownHostException{ 
+      this(null,proxyHost,proxyPort);
+   }
+
+   Proxy(Proxy chainProxy,InetAddress proxyIP,int proxyPort){
+      this.chainProxy = chainProxy;
+      this.proxyIP = proxyIP;
+      this.proxyPort = proxyPort;
+   }
+
+   Proxy(InetAddress proxyIP,int proxyPort){
+      this(null,proxyIP,proxyPort);
+   }
+
+   Proxy(Proxy p){
+      this.proxyIP = p.proxyIP;
+      this.proxyPort = p.proxyPort;
+      this.version = p.version;
+      this.directHosts = p.directHosts;
+   }
+
+//Public instance methods
+//========================
+
+   /**
+      Get the port on which proxy server is running.
+    * @return Proxy port.
+    */
+   public int getPort(){
+     return proxyPort;
+   }
+   /**
+      Get the ip address of the proxy server host.
+    * @return Proxy InetAddress.
+    */
+   public InetAddress getInetAddress(){
+     return proxyIP;
+   }
+   /**
+    * Adds given ip to the list of direct addresses.
+    * This machine will be accessed without using proxy.
+    */
+   public void addDirect(InetAddress ip){
+      directHosts.add(ip);
+   }
+   /**
+    * Adds host to the list of direct addresses.
+    * This machine will be accessed without using proxy.
+    */
+   public boolean addDirect(String host){
+      return directHosts.add(host);
+   }
+   /**
+    * Adds given range of addresses to the lsit of direct addresses,
+    * machines within this range will be accessed without using proxy.
+    */
+   public void addDirect(InetAddress from,InetAddress to){
+      directHosts.add(from,to);
+   }
+   /**
+    * Sets given InetRange as the list of direct address, previous
+    * list will be discarded, any changes done previously with
+    * addDirect(Inetaddress) will be lost.
+    * The machines in this range will be accessed without using proxy.
+    * @param ir InetRange which should be used to look up direct addresses.
+    * @see InetRange
+    */
+   public void setDirect(InetRange ir){
+     directHosts = ir;
+   }
+
+   /**
+      Get the list of direct hosts.
+    * @return Current range of direct address as InetRange object.
+    * @see InetRange
+    */
+   public InetRange getDirect(){
+     return directHosts;
+   }
+   /**
+      Check wether the given host is on the list of direct address.
+      @param host Host name to check.
+    * @return true if the given host is specified as the direct addresses.
+    */
+    public boolean isDirect(String host){
+        return directHosts.contains(host);
+    }
+   /**
+      Check wether the given host is on the list of direct addresses.
+      @param host Host address to check.
+    * @return true if the given host is specified as the direct address.
+    */
+    public boolean isDirect(InetAddress host){
+        return directHosts.contains(host);
+    }
+    /**
+      Set the proxy which should be used to connect to given proxy.
+      @param chainProxy Proxy to use to connect to this proxy.
+    */
+    public void setChainProxy(Proxy chainProxy){
+        this.chainProxy = chainProxy;
+    }
+    
+    /**
+      Get proxy which is used to connect to this proxy.
+      @return Proxy which is used to connect to this proxy, or null
+              if proxy is to be contacted directly.
+    */
+    public Proxy getChainProxy(){
+       return chainProxy;
+    }
+
+    /**
+       Get string representation of this proxy.
+     * @returns string in the form:proxyHost:proxyPort \t Version versionNumber
+     */
+    public String toString(){
+       return (""+proxyIP.getHostName()+":"+proxyPort+"\tVersion "+version);
+    }
+
+
+//Public Static(Class) Methods
+//==============================
+
+   
+
+   
+   /**
+    * Sets SOCKS5 proxy as default.
+    * Default proxy only supports no-authentication.
+      @param hostName Host name on which SOCKS5 server is running.
+      @param port Port on which SOCKS5 server is running.
+    */
+   public static void setDefaultProxy(String hostName,int port)
+                             throws UnknownHostException{
+      defaultProxy = new Socks5Proxy(hostName,port);
+   }
+   /**
+    * Sets SOCKS5 proxy as default.
+    * Default proxy only supports no-authentication.
+      @param ipAddress Host address on which SOCKS5 server is running.
+      @param port Port on which SOCKS5 server is running.
+    */
+   public static void setDefaultProxy(InetAddress ipAddress,int port){
+      defaultProxy = new Socks5Proxy(ipAddress,port);
+   }
+   /**
+    * Sets default proxy.
+      @param p Proxy to use as default proxy.
+    */
+   public static void setDefaultProxy(Proxy p){
+     defaultProxy = p;
+   }
+
+   /**
+      Get current default proxy.
+    * @return Current default proxy, or null if none is set.
+    */
+   public static Proxy getDefaultProxy(){
+     return defaultProxy;
+   }
+
+  
+
+//Protected Methods
+//=================
+
+   protected void startSession()throws SocksException{
+       try{
+         if(chainProxy == null)
+            proxySocket = new Socket(proxyIP,proxyPort);
+         else if(proxyIP != null)
+            proxySocket = new SocksSocket(chainProxy,proxyIP,proxyPort);
+         else
+            proxySocket = new SocksSocket(chainProxy,proxyHost,proxyPort);
+
+         in = proxySocket.getInputStream();
+         out = proxySocket.getOutputStream();
+       }catch(SocksException se){
+         throw se;
+       }catch(IOException io_ex){
+         throw new SocksException(SOCKS_PROXY_IO_ERROR,""+io_ex);
+       }
+   }
+
+   protected abstract Proxy copy();
+   protected abstract ProxyMessage formMessage(int cmd,InetAddress ip,int port);
+   protected abstract ProxyMessage formMessage(int cmd,String host,int port)
+             throws UnknownHostException;
+   protected abstract ProxyMessage formMessage(InputStream in)
+             throws SocksException,
+                    IOException;
+   
+
+   protected ProxyMessage connect(InetAddress ip,int port)
+             throws SocksException{
+      try{
+         startSession();
+         ProxyMessage request  = formMessage(SOCKS_CMD_CONNECT,
+			                     ip,port);
+         return exchange(request);
+      }catch(SocksException se){
+         endSession();
+         throw se;
+      }
+   }
+   protected ProxyMessage connect(String host,int port)
+             throws UnknownHostException,SocksException{
+      try{
+         startSession();
+         ProxyMessage request  = formMessage(SOCKS_CMD_CONNECT,
+			                     host,port);
+         return exchange(request);
+      }catch(SocksException se){
+         endSession();
+         throw se;
+      }
+   }
+
+   protected ProxyMessage bind(InetAddress ip,int port)
+             throws SocksException{
+      try{
+         startSession();
+         ProxyMessage request  = formMessage(SOCKS_CMD_BIND,
+				             ip,port);
+         return exchange(request);
+      }catch(SocksException se){
+         endSession();
+         throw se;
+      }
+   }
+   protected ProxyMessage bind(String host,int port)
+             throws UnknownHostException,SocksException{
+      try{
+         startSession();
+         ProxyMessage request  = formMessage(SOCKS_CMD_BIND,
+				             host,port);
+         return exchange(request);
+      }catch(SocksException se){
+         endSession();
+         throw se;
+      }
+   }
+
+   protected ProxyMessage accept()
+             throws IOException,SocksException{
+      ProxyMessage msg;
+      try{
+         msg = formMessage(in);
+      }catch(InterruptedIOException iioe){
+         throw iioe;
+      }catch(IOException io_ex){
+         endSession();
+         throw new SocksException(SOCKS_PROXY_IO_ERROR,"While Trying accept:"
+         +io_ex);
+      }
+      return msg;
+   }
+
+   protected ProxyMessage udpAssociate(InetAddress ip,int port)
+             throws SocksException{
+      try{
+         startSession();
+         ProxyMessage request  = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
+				             ip,port);
+         if(request != null)
+           return exchange(request);
+      }catch(SocksException se){
+         endSession();
+         throw se;
+      }
+      //Only get here if request was null
+      endSession();
+      throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
+      "This version of proxy does not support UDP associate, use version 5");
+   }
+   protected ProxyMessage udpAssociate(String host,int port)
+             throws UnknownHostException,SocksException{
+      try{
+         startSession();
+         ProxyMessage request  = formMessage(SOCKS_CMD_UDP_ASSOCIATE,
+				             host,port);
+         if(request != null) return exchange(request);
+      }catch(SocksException se){
+         endSession();
+         throw se;
+      }
+      //Only get here if request was null
+      endSession();
+      throw new SocksException(SOCKS_METHOD_NOTSUPPORTED,
+      "This version of proxy does not support UDP associate, use version 5");
+   }
+
+
+   protected void endSession(){
+      try{
+         if(proxySocket!=null) proxySocket.close();
+         proxySocket = null;
+      }catch(IOException io_ex){
+      }
+   }
+
+   /**
+    *Sends the request to SOCKS server
+    */
+   protected void sendMsg(ProxyMessage msg)throws SocksException,
+                                                  IOException{
+      msg.write(out);
+   }
+
+   /** 
+    * Reads the reply from the SOCKS server
+    */
+   protected ProxyMessage readMsg()throws SocksException,
+                                          IOException{
+      return formMessage(in);
+   }
+   /**
+    *Sends the request reads reply and returns it
+    *throws exception if something wrong with IO
+    *or the reply code is not zero
+    */
+   protected ProxyMessage exchange(ProxyMessage request)
+                           throws SocksException{
+      ProxyMessage reply;
+      try{
+         request.write(out);
+         reply = formMessage(in);
+      }catch(SocksException s_ex){
+         throw s_ex;
+      }catch(IOException ioe){
+         throw(new SocksException(SOCKS_PROXY_IO_ERROR,""+ioe));
+      }
+      return reply;
+   }
+
+
+//Private methods
+//===============
+
+
+//Constants
+
+   public static final int SOCKS_SUCCESS		=0;
+   public static final int SOCKS_FAILURE		=1;
+   public static final int SOCKS_BADCONNECT		=2;
+   public static final int SOCKS_BADNETWORK		=3;
+   public static final int SOCKS_HOST_UNREACHABLE	=4;
+   public static final int SOCKS_CONNECTION_REFUSED	=5;
+   public static final int SOCKS_TTL_EXPIRE		=6;
+   public static final int SOCKS_CMD_NOT_SUPPORTED	=7;
+   public static final int SOCKS_ADDR_NOT_SUPPORTED	=8;
+
+   public static final int SOCKS_NO_PROXY		=1<<16;
+   public static final int SOCKS_PROXY_NO_CONNECT	=2<<16;
+   public static final int SOCKS_PROXY_IO_ERROR		=3<<16;
+   public static final int SOCKS_AUTH_NOT_SUPPORTED	=4<<16;
+   public static final int SOCKS_AUTH_FAILURE		=5<<16;
+   public static final int SOCKS_JUST_ERROR		=6<<16;
+
+   public static final int SOCKS_DIRECT_FAILED		=7<<16;
+   public static final int SOCKS_METHOD_NOTSUPPORTED	=8<<16;
+
+
+   static final int SOCKS_CMD_CONNECT 		=0x1;
+   static final int SOCKS_CMD_BIND		=0x2;
+   static final int SOCKS_CMD_UDP_ASSOCIATE	=0x3;
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ServerAuthenticatorNone.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ServerAuthenticatorNone.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ServerAuthenticatorNone.java	(revision 3952)
@@ -0,0 +1,169 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.net.Socket;
+
+/**
+ An implementation of ServerAuthenticator, which does <b>not</b> do
+ any authentication.
+<P>
+<FONT size="+3" color ="FF0000"> Warning!!</font><br> Should not be
+used on machines which are not behind the firewall.
+<p>
+It is only provided to make implementing other authentication schemes
+easier.<br>
+For Example: <tt><pre>
+   class MyAuth extends socks.server.ServerAuthenticator{
+    ...
+    public ServerAuthenticator startSession(java.net.Socket s){
+      if(!checkHost(s.getInetAddress()) return null;
+      return super.startSession(s);
+    }
+
+    boolean checkHost(java.net.Inetaddress addr){
+      boolean allow;
+      //Do it somehow
+      return allow;
+    }
+   }
+</pre></tt>
+*/
+public class ServerAuthenticatorNone implements ServerAuthenticator{
+
+   static final byte[] socks5response = {5,0};
+
+   InputStream in;
+   OutputStream out;
+
+   /**
+    Creates new instance of the ServerAuthenticatorNone.
+   */
+   public ServerAuthenticatorNone(){
+     this.in = null;
+     this.out = null;
+   }
+   /**
+    Constructs new ServerAuthenticatorNone object suitable for returning
+    from the startSession function.
+    @param in Input stream to return from getInputStream method.
+    @param out Output stream to return from getOutputStream method.
+   */
+   public ServerAuthenticatorNone(InputStream in, OutputStream out){
+      this.in = in;
+      this.out = out;
+   }
+   /**
+    Grants access to everyone.Removes authentication related bytes from
+    the stream, when a SOCKS5 connection is being made, selects an
+    authentication NONE.
+    */
+   public ServerAuthenticator startSession(Socket s)
+                                  throws IOException{
+
+     PushbackInputStream in =  new PushbackInputStream(s.getInputStream());
+     OutputStream out = s.getOutputStream();
+
+     int version = in.read();
+     if(version == 5){
+       if(!selectSocks5Authentication(in,out,0))
+          return null;
+     }else if(version == 4){
+       //Else it is the request message allready, version 4
+       in.unread(version);
+     }else
+       return null;
+     
+
+     return new ServerAuthenticatorNone(in,out);
+   }
+
+   /**
+     Get input stream.
+     @return Input stream speciefied in the constructor.
+   */
+   public InputStream getInputStream(){
+      return in;
+   }
+   /**
+     Get output stream.
+     @return Output stream speciefied in the constructor.
+   */
+   public OutputStream getOutputStream(){
+      return out;
+   }
+   /**
+     Allways returns null.
+     @return null
+   */
+   public UDPEncapsulation getUdpEncapsulation(){
+      return null;
+   }
+
+   /**
+    Allways returns true.
+   */
+   public boolean checkRequest(ProxyMessage msg){
+     return true;
+   }
+
+   /**
+    Allways returns true.
+   */
+   public boolean checkRequest(java.net.DatagramPacket dp, boolean out){
+     return true;
+   }
+
+   /**
+    Does nothing.
+    */
+   public void endSession(){
+   }
+
+   /**
+     Convinience routine for selecting SOCKSv5 authentication.
+     <p>
+     This method reads in authentication methods that client supports,
+     checks wether it supports given method. If it does, the notification
+     method is written back to client, that this method have been chosen
+     for authentication. If given method was not found, authentication
+     failure message is send to client ([5,FF]).
+     @param in Input stream, version byte should be removed from the stream
+               before calling this method.
+     @param out Output stream.
+     @param methodId Method which should be selected.
+     @return true if methodId was found, false otherwise.
+   */
+   static public boolean selectSocks5Authentication(InputStream in, 
+                                                    OutputStream out,
+                                                    int methodId)
+                                                    throws IOException{
+                                                    
+      int num_methods = in.read();
+      if (num_methods <= 0) return false;
+      byte method_ids[] = new byte[num_methods];
+      byte response[] = new byte[2];
+      boolean found = false;
+
+      response[0] = (byte) 5;    //SOCKS version
+      response[1] = (byte) 0xFF; //Not found, we are pessimistic
+
+      int bread = 0; //bytes read so far
+      while(bread < num_methods)
+         bread += in.read(method_ids,bread,num_methods-bread);
+
+      for(int i=0;i<num_methods;++i)
+         if(method_ids[i] == methodId){
+           found = true;
+           response[1] = (byte) methodId;
+           break;
+         }
+
+      out.write(response);
+      return found;
+   }
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5DatagramSocket.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5DatagramSocket.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/Socks5DatagramSocket.java	(revision 3952)
@@ -0,0 +1,487 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+import java.net.*;
+import java.io.*;
+
+/**
+  Datagram socket to interract through the firewall.<BR>
+  Can be used same way as the normal DatagramSocket. One should
+  be carefull though with the datagram sizes used, as additional data
+  is present in both incomming and outgoing datagrams.
+   <p>
+   SOCKS5 protocol allows to send host address as either:
+   <ul>
+    <li> IPV4, normal 4 byte address. (10 bytes header size)
+    <li> IPV6, version 6 ip address (not supported by Java as for now).
+         22 bytes header size.
+    <li> Host name,(7+length of the host name bytes header size).
+   </ul>
+  As with other Socks equivalents, direct addresses are handled
+  transparently, that is data will be send directly when required 
+  by the proxy settings.
+  <p>
+  <b>NOTE:</b><br>
+  Unlike other SOCKS Sockets, it <b>does not</b> support proxy chaining,
+  and will throw an exception if proxy has a chain proxy attached. The
+  reason for that is not my laziness, but rather the restrictions of
+  the SOCKSv5 protocol. Basicaly SOCKSv5 proxy server, needs to know from
+  which host:port datagrams will be send for association, and returns address
+  to which datagrams should be send by the client, but it does not
+  inform client from which host:port it is going to send datagrams, in fact
+  there is even no guarantee they will be send at all and from the same address
+  each time.
+ 
+ */
+public class Socks5DatagramSocket extends DatagramSocket{
+
+   InetAddress relayIP;
+   int relayPort;
+   Socks5Proxy proxy;
+   private boolean server_mode = false;
+   UDPEncapsulation encapsulation;
+
+
+   /**
+      Construct Datagram socket for communication over SOCKS5 proxy
+      server. This constructor uses default proxy, the one set with
+      Proxy.setDefaultProxy() method. If default proxy is not set or 
+      it is set to version4 proxy, which does not support datagram
+      forwarding, throws SocksException.
+
+    */
+   public Socks5DatagramSocket() throws SocksException,
+                                        IOException{
+      this(Proxy.defaultProxy,0,null);
+   }
+   /**
+      Construct Datagram socket for communication over SOCKS5 proxy
+      server. And binds it to the specified local port.
+      This constructor uses default proxy, the one set with
+      Proxy.setDefaultProxy() method. If default proxy is not set or 
+      it is set to version4 proxy, which does not support datagram
+      forwarding, throws SocksException.
+    */
+   public Socks5DatagramSocket(int port) throws SocksException,
+                                                IOException{
+      this(Proxy.defaultProxy,port,null);
+   }
+   /**
+      Construct Datagram socket for communication over SOCKS5 proxy
+      server. And binds it to the specified local port and address.
+      This constructor uses default proxy, the one set with
+      Proxy.setDefaultProxy() method. If default proxy is not set or 
+      it is set to version4 proxy, which does not support datagram
+      forwarding, throws SocksException.
+    */
+   public Socks5DatagramSocket(int port,InetAddress ip) throws SocksException,
+                                        IOException{
+      this(Proxy.defaultProxy,port,ip);
+   }
+
+   /**
+     Constructs datagram socket for communication over specified proxy.
+     And binds it to the given local address and port. Address of null 
+     and port of 0, signify any availabale port/address.
+     Might throw SocksException, if:
+     <ol>
+      <li> Given version of proxy does not support UDP_ASSOCIATE.
+      <li> Proxy can't be reached.
+      <li> Authorization fails.
+      <li> Proxy does not want to perform udp forwarding, for any reason.
+     </ol>
+     Might throw IOException if binding dtagram socket to given address/port
+     fails.
+     See java.net.DatagramSocket for more details.
+    */
+   public Socks5DatagramSocket(Proxy p,int port,InetAddress ip)
+                                        throws SocksException,
+                                               IOException{
+      super(port,ip);
+      if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY);
+      if(!(p instanceof Socks5Proxy)) 
+         throw new SocksException(-1,"Datagram Socket needs Proxy version 5");
+
+      if(p.chainProxy != null)
+           throw new SocksException(Proxy.SOCKS_JUST_ERROR,
+               "Datagram Sockets do not support proxy chaining.");
+
+      proxy =(Socks5Proxy) p.copy();
+
+      ProxyMessage msg = proxy.udpAssociate(super.getLocalAddress(),
+                                            super.getLocalPort());
+      relayIP = msg.ip;
+      if(relayIP.getHostAddress().equals("0.0.0.0")) relayIP = proxy.proxyIP;
+      relayPort = msg.port;
+
+      encapsulation = proxy.udp_encapsulation;
+
+      //debug("Datagram Socket:"+getLocalAddress()+":"+getLocalPort()+"\n");
+      //debug("Socks5Datagram: "+relayIP+":"+relayPort+"\n");
+   }
+
+   /**
+     Used by UDPRelayServer.
+    */
+   Socks5DatagramSocket(boolean server_mode,UDPEncapsulation encapsulation,
+                        InetAddress relayIP,int relayPort)
+                        throws IOException{
+      super();
+      this.server_mode = server_mode;
+      this.relayIP = relayIP;
+      this.relayPort = relayPort;
+      this.encapsulation = encapsulation;
+      this.proxy = null;
+   }
+
+   /**
+     Sends the Datagram either through the proxy or directly depending
+     on current proxy settings and destination address. <BR>
+
+     <B> NOTE: </B> DatagramPacket size should be at least 10 bytes less
+                    than the systems limit.
+
+     <P>
+     See documentation on java.net.DatagramSocket
+     for full details on how to use this method. 
+     @param dp Datagram to send.
+     @throws IOException If error happens with I/O.
+    */
+   public void send(DatagramPacket dp) throws IOException{
+     //If the host should be accessed directly, send it as is.
+     if(!server_mode && proxy.isDirect(dp.getAddress())){
+        super.send(dp);
+        //debug("Sending directly:");
+        return;
+     }
+
+     byte[] head = formHeader(dp.getAddress(),dp.getPort());
+     byte[] buf = new byte[head.length + dp.getLength()];
+     byte[] data = dp.getData();
+     //Merge head and data
+     System.arraycopy(head,0,buf,0,head.length);
+     //System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength());
+     System.arraycopy(data,0,buf,head.length,dp.getLength());
+
+     if(encapsulation != null)
+        buf = encapsulation.udpEncapsulate(buf,true);
+
+     super.send(new DatagramPacket(buf,buf.length,relayIP,relayPort));
+   }
+   /**
+     This method allows to send datagram packets with address type DOMAINNAME.
+     SOCKS5 allows to specify host as names rather than ip addresses.Using
+     this method one can send udp datagrams through the proxy, without having
+     to know the ip address of the destination host.
+     <p> 
+     If proxy specified for that socket has an option resolveAddrLocally set
+     to true host will be resolved, and the datagram will be send with address
+     type IPV4, if resolve fails, UnknownHostException is thrown.
+     @param dp Datagram to send, it should contain valid port and data
+     @param host Host name to which datagram should be send.
+     @throws IOException If error happens with I/O, or the host can't be 
+     resolved when proxy settings say that hosts should be resolved locally.
+     @see Socks5Proxy#resolveAddrLocally(boolean)
+    */
+   public void send(DatagramPacket dp, String host) throws IOException{
+     if(proxy.isDirect(host)){
+        dp.setAddress(InetAddress.getByName(host));
+        super.send(dp);
+        return;
+     }
+
+     if(((Socks5Proxy)proxy).resolveAddrLocally){
+        dp.setAddress(InetAddress.getByName(host));
+     }
+
+     byte[] head = formHeader(host,dp.getPort());
+     byte[] buf = new byte[head.length + dp.getLength()];
+     byte[] data = dp.getData();
+     //Merge head and data
+     System.arraycopy(head,0,buf,0,head.length);
+     //System.arraycopy(data,dp.getOffset(),buf,head.length,dp.getLength());
+     System.arraycopy(data,0,buf,head.length,dp.getLength());
+
+     if(encapsulation != null)
+        buf = encapsulation.udpEncapsulate(buf,true);
+
+     super.send(new DatagramPacket(buf,buf.length,relayIP,relayPort));
+   }
+
+   /**
+    * Receives udp packet. If packet have arrived from the proxy relay server,
+    * it is processed and address and port of the packet are set to the
+    * address and port of sending host.<BR>
+    * If the packet arrived from anywhere else it is not changed.<br>
+    * <B> NOTE: </B> DatagramPacket size should be at least 10 bytes bigger
+    * than the largest packet you expect (this is for IPV4 addresses). 
+    * For hostnames and IPV6 it is even more.
+      @param dp Datagram in which all relevent information will be copied.
+    */
+   public void receive(DatagramPacket dp) throws IOException{
+      super.receive(dp);
+
+      if(server_mode){
+        //Drop all datagrams not from relayIP/relayPort
+         int init_length = dp.getLength();
+         int initTimeout = getSoTimeout();
+         long startTime = System.currentTimeMillis();
+
+         while(!relayIP.equals(dp.getAddress()) ||
+                relayPort != dp.getPort()){
+
+           //Restore datagram size
+           dp.setLength(init_length);
+
+           //If there is a non-infinit timeout on this socket
+           //Make sure that it happens no matter how often unexpected
+           //packets arrive.
+           if(initTimeout != 0){
+             int newTimeout = initTimeout - (int)(System.currentTimeMillis() - 
+                                                        startTime);
+             if(newTimeout <= 0) throw new InterruptedIOException(
+                                 "In Socks5DatagramSocket->receive()");
+             setSoTimeout(newTimeout);
+           }
+
+           super.receive(dp);
+         }
+
+         //Restore timeout settings
+         if(initTimeout != 0) setSoTimeout(initTimeout);
+
+      }else if(!relayIP.equals(dp.getAddress()) ||
+                relayPort != dp.getPort()) 
+          return; // Recieved direct packet
+      //If the datagram is not from the relay server, return it it as is.
+
+      byte[] data;
+      data = dp.getData();
+
+      if(encapsulation != null)
+         data = encapsulation.udpEncapsulate(data,false);
+
+      int offset = 0; //Java 1.1
+      //int offset = dp.getOffset(); //Java 1.2
+
+      ByteArrayInputStream bIn = new ByteArrayInputStream(data,offset,
+                                                              dp.getLength());
+
+
+      ProxyMessage msg = new Socks5Message(bIn);
+      dp.setPort(msg.port);
+      dp.setAddress(msg.getInetAddress());
+
+      //what wasn't read by the Message is the data
+      int data_length = bIn.available();
+      //Shift data to the left
+      System.arraycopy(data,offset+dp.getLength()-data_length,
+                       data,offset,data_length);
+
+
+      dp.setLength(data_length);
+   }
+
+   /**
+    * Returns port assigned by the proxy, to which datagrams are relayed.
+    * It is not the same port to which other party should send datagrams.
+      @return Port assigned by socks server to which datagrams are send
+      for association.
+    */
+   public int getLocalPort(){
+     if(server_mode) return super.getLocalPort();
+     return relayPort;
+   }
+   /**
+    * Address assigned by the proxy, to which datagrams are send for relay.
+    * It is not necesseraly the same address, to which other party should send
+    * datagrams.
+      @return Address to which datagrams are send for association.
+    */
+   public InetAddress getLocalAddress(){
+     if(server_mode) return super.getLocalAddress();
+     return relayIP;
+   }
+
+   /**
+    * Closes datagram socket, and proxy connection.
+    */
+   public void close(){
+      if(!server_mode) proxy.endSession();
+      super.close();
+   }
+
+   /**
+     This method checks wether proxy still runs udp forwarding service
+     for this socket.
+     <p>
+     This methods checks wether the primary connection to proxy server
+     is active. If it is, chances are that proxy continues to forward
+     datagrams being send from this socket. If it was closed, most likely
+     datagrams are no longer being forwarded by the server.
+     <p>
+     Proxy might decide to stop forwarding datagrams, in which case it
+     should close primary connection. This method allows to check, wether
+     this have been done.
+     <p>
+     You can specify timeout for which we should be checking EOF condition
+     on the primary connection. Timeout is in milliseconds. Specifying 0 as
+     timeout implies infinity, in which case method will block, until 
+     connection to proxy is closed or an error happens, and then return false.
+     <p>
+     One possible scenario is to call isProxyactive(0) in separate thread,
+     and once it returned notify other threads about this event.
+
+     @param timeout For how long this method should block, before returning.
+     @return true if connection to proxy is active, false if eof or error
+             condition have been encountered on the connection.
+   */
+   public boolean isProxyAlive(int timeout){
+     if(server_mode) return false;
+     if(proxy != null){
+         try{
+           proxy.proxySocket.setSoTimeout(timeout);
+
+           int eof = proxy.in.read();
+           if(eof < 0) return false; // EOF encountered.
+           else return true;         // This really should not happen
+
+         }catch(InterruptedIOException iioe){
+            return true;          // read timed out.
+         }catch(IOException ioe){
+            return false;
+         }
+     }
+     return false;
+   }
+
+//PRIVATE METHODS
+//////////////////
+
+
+   private byte[] formHeader(InetAddress ip, int port){
+      Socks5Message request = new Socks5Message(0,ip,port);
+      request.data[0] = 0;
+      return request.data;
+   }
+
+
+   private byte[] formHeader(String host,int port){
+      Socks5Message request = new Socks5Message(0,host,port);
+      request.data[0] = 0;
+      return request.data;
+   }
+
+
+/*======================================================================
+
+//Mainly Test functions
+//////////////////////
+
+   private String bytes2String(byte[] b){
+      String s="";
+      char[] hex_digit = { '0','1','2','3','4','5','6','7','8','9',
+                           'A','B','C','D','E','F'}; 
+      for(int i=0;i<b.length;++i){
+          int i1 = (b[i] & 0xF0) >> 4;
+          int i2 = b[i] & 0xF;
+          s+=hex_digit[i1];
+          s+=hex_digit[i2];
+          s+=" ";
+      }
+      return s;
+   }
+   private static final void debug(String s){
+      if(DEBUG)
+         System.out.print(s);
+   }
+
+   private static final boolean DEBUG = true;
+
+
+   public static void usage(){
+      System.err.print(
+    "Usage: java Socks.SocksDatagramSocket host port [socksHost socksPort]\n");
+   }
+
+   static final int defaultProxyPort = 1080;           //Default Port
+   static final String defaultProxyHost = "www-proxy"; //Default proxy
+
+   public static void main(String args[]){
+      int port;
+      String host;
+      int proxyPort;
+      String proxyHost;
+      InetAddress ip;
+
+      if(args.length > 1 && args.length < 5){
+	 try{
+
+	     host = args[0];
+	     port = Integer.parseInt(args[1]);
+
+	     proxyPort =(args.length > 3)? Integer.parseInt(args[3])	     
+	                                 : defaultProxyPort;
+
+	     host = args[0];
+	     ip = InetAddress.getByName(host);
+
+	     proxyHost =(args.length > 2)? args[2]
+	                                 : defaultProxyHost;
+
+	     Proxy.setDefaultProxy(proxyHost,proxyPort);
+	     Proxy p = Proxy.getDefaultProxy();
+	     p.addDirect("lux");
+
+
+	     DatagramSocket ds = new Socks5DatagramSocket();
+	                             
+
+	     BufferedReader in = new BufferedReader(
+				 new InputStreamReader(System.in));
+             String s;
+
+             System.out.print("Enter line:");
+             s = in.readLine();
+
+	     while(s != null){
+                byte[] data = (s+"\r\n").getBytes();
+                DatagramPacket dp = new DatagramPacket(data,0,data.length,
+                                        ip,port);
+                System.out.println("Sending to: "+ip+":"+port);
+                ds.send(dp);
+	        dp = new DatagramPacket(new byte[1024],1024);
+
+	        System.out.println("Trying to recieve on port:"+
+	                            ds.getLocalPort());
+	        ds.receive(dp);
+	        System.out.print("Recieved:\n"+
+	                         "From:"+dp.getAddress()+":"+dp.getPort()+
+	                         "\n\n"+
+                new String(dp.getData(),dp.getOffset(),dp.getLength())+"\n"
+                );
+                System.out.print("Enter line:");
+                s = in.readLine();
+
+	     }
+	     ds.close();
+	     System.exit(1);
+
+	 }catch(SocksException s_ex){
+	   System.err.println("SocksException:"+s_ex);
+	   s_ex.printStackTrace();
+	   System.exit(1); 
+	 }catch(IOException io_ex){
+	   io_ex.printStackTrace();
+	   System.exit(1);
+	 }catch(NumberFormatException num_ex){
+	   usage();
+	   num_ex.printStackTrace();
+	   System.exit(1);
+	 }
+
+      }else{
+	usage();
+      }
+   }
+*/
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/SocksServerSocket.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/SocksServerSocket.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/SocksServerSocket.java	(revision 3952)
@@ -0,0 +1,207 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+import java.net.*;
+import java.io.*;
+//TODO remove if bind not necessary
+/**
+   SocksServerSocket allows to accept connections from one particular
+   host through the SOCKS4 or SOCKS5 proxy.
+*/
+public class SocksServerSocket extends ServerSocket{
+   //Data members
+   protected Proxy proxy;
+   protected String localHost;
+   protected InetAddress localIP;
+   protected int localPort;
+
+   boolean doing_direct = false;
+   InetAddress  remoteAddr;
+
+   /**
+    * Creates ServerSocket capable of accepting one connection
+    * through the firewall, uses default Proxy.
+    *@param host Host from which the connection should be recieved.
+    *@param port Port number of the primary connection.
+    */
+   public SocksServerSocket(String host,int port)
+	  throws SocksException,UnknownHostException,IOException{
+      this(Proxy.defaultProxy,host,port);
+   }
+   /**
+    *Creates ServerSocket capable of accepting one connection
+    *through the firewall, uses given proxy.
+    *@param p    Proxy object to use.
+    *@param host Host from which the connection should be recieved.
+    *@param port Port number of the primary connection.
+    */
+   public SocksServerSocket(Proxy p,String host,int port)
+	  throws SocksException,UnknownHostException,IOException{
+
+
+      super(0);
+      if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY);
+      //proxy=p;
+      proxy = p.copy();
+      if(proxy.isDirect(host)){
+         remoteAddr = InetAddress.getByName(host);
+         proxy = null;
+         doDirect();
+      }else{
+         processReply(proxy.bind(host,port));
+      }
+   }
+
+   /**
+    * Creates ServerSocket capable of accepting one connection
+    * through the firewall, uses default Proxy.
+    *@param ip Host from which the connection should be recieved.
+    *@param port Port number of the primary connection.
+    */
+   public SocksServerSocket(InetAddress ip, int port) throws SocksException,
+                                                             IOException{
+      this(Proxy.defaultProxy,ip,port);
+   }
+
+   /**
+    *Creates ServerSocket capable of accepting one connection
+    *through the firewall, uses given proxy.
+    *@param p    Proxy object to use.
+    *@param ip   Host from which the connection should be recieved.
+    *@param port Port number of the primary connection.
+    */
+   public SocksServerSocket(Proxy p,InetAddress ip, int port)
+          throws SocksException,IOException{
+      super(0);
+
+      if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY);
+      this.proxy = p.copy();
+
+      if(proxy.isDirect(ip)){
+         remoteAddr = ip;
+         doDirect();
+      }else{
+         processReply(proxy.bind(ip,port));
+      }
+   }
+
+
+   /**
+    * Accepts the incoming connection.
+    */
+   public Socket accept() throws IOException{
+      Socket s;
+
+      if(!doing_direct){
+         if(proxy == null) return null;
+
+         ProxyMessage msg = proxy.accept();
+         s = msg.ip == null? new SocksSocket(msg.host,msg.port,proxy)
+                                  : new SocksSocket(msg.ip,msg.port,proxy);
+         //Set timeout back to 0
+         proxy.proxySocket.setSoTimeout(0);
+      }else{ //Direct Connection
+
+          //Mimic the proxy behaviour,
+          //only accept connections from the speciefed host.
+          while(true){
+            s = super.accept();
+            if(s.getInetAddress().equals(remoteAddr)){
+               //got the connection from the right host
+               //Close listenning socket.
+               break;
+            }else
+               s.close(); //Drop all connections from other hosts
+          }
+
+      }
+      proxy = null;
+      //Return accepted socket
+      return s;
+   }
+
+   /**
+    * Closes the connection to proxy if socket have not been accepted, if
+    * the direct connection is used, closes direct ServerSocket. If the 
+    * client socket have been allready accepted, does nothing.
+    */
+   public void close() throws IOException{
+      super.close();
+      if(proxy != null) proxy.endSession();
+      proxy = null;
+   }
+
+   /**
+     Get the name of the host proxy is using to listen for incoming
+     connection.
+     <P>
+     Usefull when address is returned by proxy as the hostname.
+     @return the hostname of the address proxy is using to listen
+     for incoming connection.
+    */
+   public String getHost(){
+      return localHost;
+   }
+
+   /**
+    * Get address assigned by proxy to listen for incomming
+    * connections, or the local machine address if doing direct
+    * connection.
+    */
+   public InetAddress getInetAddress(){
+      if(localIP == null){
+	 try{
+	   localIP = InetAddress.getByName(localHost);
+	 }catch(UnknownHostException e){
+	   return null;
+	 }
+      }
+      return localIP;
+   }
+
+   /**
+    *  Get port assigned by proxy to listen for incoming connections, or
+       the port chosen by local system, if accepting directly.
+    */
+   public int getLocalPort(){
+      return localPort;
+   }
+
+   /**
+    Set Timeout.
+
+    @param timeout Amount of time in milliseconds, accept should wait for
+                   incoming connection before failing with exception.
+                   Zero timeout implies infinity.
+   */
+   public void setSoTimeout(int timeout) throws SocketException{
+      super.setSoTimeout(timeout);
+      if(!doing_direct) proxy.proxySocket.setSoTimeout(timeout);
+   }
+
+
+//Private Methods
+//////////////////
+
+   private void processReply(ProxyMessage reply)throws SocksException{
+      localPort = reply.port;
+      /*
+       * If the server have assigned same host as it was contacted on
+       * it might return an address of all zeros
+       */
+      if(reply.host.equals("0.0.0.0")){
+         localIP = proxy.proxyIP;
+         localHost = localIP.getHostName();
+      }else{
+         localHost = reply.host;
+         localIP = reply.ip;
+      }
+   }
+
+   private void doDirect(){
+      doing_direct = true;
+      localPort = super.getLocalPort();
+      localIP = super.getInetAddress();
+      localHost = localIP.getHostName();
+   }
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ProxyMessage.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ProxyMessage.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/ProxyMessage.java	(revision 3952)
@@ -0,0 +1,110 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.DataInputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ Abstract class which describes SOCKS4/5 response/request.
+*/
+public abstract class ProxyMessage{
+   /** Host as an IP address */
+   public InetAddress ip=null;
+   /** SOCKS version, or version of the response for SOCKS4*/
+   public int version;
+   /** Port field of the request/response*/
+   public int port;
+   /** Request/response code as an int*/
+   public int command;
+   /** Host as string.*/
+   public String host=null;
+   /** User field for SOCKS4 request messages*/
+   public String user=null;
+
+   ProxyMessage(int command,InetAddress ip,int port){
+      this.command = command;
+      this.ip      = ip;
+      this.port    = port;
+   }
+
+   ProxyMessage(){
+   }
+
+
+   /**
+     Initialises Message from the stream. Reads server response from
+     given stream.
+     @param in Input stream to read response from.
+     @throws SocksException If server response code is not SOCKS_SUCCESS(0), or
+     if any error with protocol occurs.
+     @throws IOException If any error happens with I/O.
+   */
+   public abstract void read(InputStream in)
+                                    throws SocksException,
+                                           IOException;
+
+
+   /**
+     Initialises Message from the stream. Reads server response or client 
+     request from given stream.
+     
+     @param in Input stream to read response from.
+     @param clinetMode If true read server response, else read client request.
+     @throws SocksException If server response code is not SOCKS_SUCCESS(0) and
+     reading in client mode, or if any error with protocol occurs.
+     @throws IOException If any error happens with I/O.
+   */
+   public abstract void read(InputStream in,boolean client_mode)
+                                    throws SocksException,
+                                           IOException;
+
+
+   /**
+    Writes the message to the stream.
+    @param out Output stream to which message should be written.
+   */
+   public abstract void write(OutputStream out)throws SocksException,
+                                             IOException;
+
+   /**
+    Get the Address field of this message as InetAddress object.
+    @return Host address or null, if one can't be determined.
+   */
+   public InetAddress getInetAddress() throws UnknownHostException{
+     return ip;
+   }
+
+
+   /**
+    Get string representaion of this message.
+    @return string representation of this message.
+   */
+   public String toString(){
+      return 
+      "Proxy Message:\n"+
+      "Version:"+ version+"\n"+
+      "Command:"+ command+"\n"+
+      "IP:     "+ ip+"\n"+
+      "Port:   "+ port+"\n"+
+      "User:   "+ user+"\n" ;
+   }
+
+//Package methods
+//////////////////
+
+   static final String bytes2IPV4(byte[] addr,int offset){
+      String hostName = ""+(addr[offset] & 0xFF);
+      for(int i = offset+1;i<offset+4;++i)
+        hostName+="."+(addr[i] & 0xFF);
+      return hostName;
+   }
+
+   static final String bytes2IPV6(byte[] addr,int offset){
+     //Have no idea how they look like!
+     return null;
+   }
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/SocksSocket.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/SocksSocket.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/socks5/jsocks/SocksSocket.java	(revision 3952)
@@ -0,0 +1,332 @@
+package nu.fw.jeti.plugins.filetransfer.socks5.jsocks;
+
+import java.net.*;
+import java.io.*;
+
+/**
+ * SocksSocket tryies to look very similar to normal Socket,
+ * while allowing connections through the SOCKS4 or 5 proxy.
+ * To use this class you will have to identify proxy you need
+ * to use, Proxy class allows you to set default proxy, which
+ * will be used by all Socks aware sockets. You can also create
+ * either Socks4Proxy or Socks5Proxy, and use them by passing to the 
+ * appropriate constructors.
+ * <P>
+ * Using Socks package can be as easy as that:
+ *
+ * <pre><tt>
+ *
+ *     import Socks.*;
+ *     ....
+ *
+ *     try{
+ *        //Specify SOCKS5 proxy
+ *        Proxy.setDefaultProxy("socks-proxy",1080);
+ *
+ *        //OR you still use SOCKS4
+ *        //Code below uses SOCKS4 proxy
+ *        //Proxy.setDefaultProxy("socks-proxy",1080,userName);
+ *
+ *        Socket s = SocksSocket("some.host.of.mine",13);
+ *        readTimeFromSock(s);
+ *     }catch(SocksException sock_ex){
+ *        //Usually it will turn in more or less meaningfull message
+ *        System.err.println("SocksException:"+sock_ex);
+ *     }
+ *
+ * </tt></pre>
+ *<P>
+ * However if the need exist for more control, like resolving addresses
+ * remotely, or using some non-trivial authentication schemes, it can be done.
+ */
+
+public class SocksSocket extends Socket{
+   //Data members
+   protected Proxy proxy;
+   protected String localHost, remoteHost;
+   protected InetAddress localIP, remoteIP;
+   protected int localPort,remotePort;
+
+   private Socket directSock = null;
+
+   /**
+    * Tryies to connect to given host and port
+    * using default proxy. If no default proxy speciefied
+    * it throws SocksException with error code SOCKS_NO_PROXY.
+      @param host Machine to connect to.
+      @param port Port to which to connect.
+    * @see SocksSocket#SocksSocket(Proxy,String,int)
+    * @see Socks5Proxy#resolveAddrLocally
+    */
+   public SocksSocket(String host,int port)
+	  throws SocksException,UnknownHostException{
+      this(Proxy.defaultProxy,host,port);
+   }
+   /**
+    * Connects to host port using given proxy server.
+      @param p Proxy to use.
+      @param host Machine to connect to.
+      @param port Port to which to connect.
+      @throws UnknownHostException 
+      If one of the following happens:
+      <ol>
+
+      <li> Proxy settings say that address should be resolved locally, but
+           this fails.
+      <li> Proxy settings say that the host should be contacted directly but
+           host name can't be resolved. 
+      </ol>
+      @throws SocksException
+      If one of the following happens:
+      <ul>
+       <li> Proxy is is null.
+       <li> Proxy settings say that the host should be contacted directly but
+            this fails.
+       <li> Socks Server can't be contacted.
+       <li> Authentication fails.
+       <li> Connection is not allowed by the SOCKS proxy.
+       <li> SOCKS proxy can't establish the connection.
+       <li> Any IO error occured.
+       <li> Any protocol error occured.
+      </ul>
+      @throws IOexception if anything is wrong with I/O.
+      @see Socks5Proxy#resolveAddrLocally
+    */
+   public SocksSocket(Proxy p,String host,int port)
+	  throws SocksException,UnknownHostException{
+
+
+      if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY);
+      //proxy=p;
+      proxy = p.copy();
+      remoteHost = host;
+      remotePort = port;
+      if(proxy.isDirect(host)){
+         remoteIP = InetAddress.getByName(host);
+         doDirect();
+      }
+      else
+         processReply(proxy.connect(host,port));
+   }
+
+
+   /**
+    * Tryies to connect to given ip and port
+    * using default proxy. If no default proxy speciefied
+    * it throws SocksException with error code SOCKS_NO_PROXY.
+      @param ip Machine to connect to.
+      @param port Port to which to connect.
+    * @see SocksSocket#SocksSocket(Proxy,String,int)
+    */
+   public SocksSocket(InetAddress ip, int port) throws SocksException{
+      this(Proxy.defaultProxy,ip,port);
+   }
+
+   /**
+      Connects to given ip and port using given Proxy server.
+      @param p Proxy to use.
+      @param ip Machine to connect to.
+      @param port Port to which to connect.
+
+    */
+   public SocksSocket(Proxy p,InetAddress ip, int port) throws SocksException{
+      if(p == null) throw new SocksException(Proxy.SOCKS_NO_PROXY);
+      this.proxy = p.copy();
+      this.remoteIP = ip;
+      this.remotePort = port;
+      this.remoteHost = ip.getHostName();
+      if(proxy.isDirect(remoteIP))
+        doDirect();
+      else
+        processReply(proxy.connect(ip,port));
+   }
+
+
+   /**
+    * These 2 constructors are used by the SocksServerSocket.
+    * This socket simply overrides remoteHost, remotePort
+    */
+   protected SocksSocket(String  host,int port,Proxy proxy){
+      this.remotePort = port;
+      this.proxy = proxy;
+      this.localIP = proxy.proxySocket.getLocalAddress();
+      this.localPort = proxy.proxySocket.getLocalPort();
+      this.remoteHost = host;
+   }
+   protected SocksSocket(InetAddress ip,int port,Proxy proxy){
+      remoteIP = ip;
+      remotePort = port;
+      this.proxy = proxy;
+      this.localIP = proxy.proxySocket.getLocalAddress();
+      this.localPort = proxy.proxySocket.getLocalPort();
+      remoteHost = remoteIP.getHostName();
+   }
+
+   /**
+    * Same as Socket
+    */
+   public void close() throws IOException{
+      if(proxy!= null)proxy.endSession();
+      proxy = null;
+   }
+   /**
+    * Same as Socket
+    */
+   public InputStream getInputStream(){
+      return proxy.in;
+   }
+   /**
+    * Same as Socket
+    */
+   public OutputStream getOutputStream(){
+      return proxy.out;
+   }
+   /**
+    * Same as Socket
+    */
+   public int getPort(){
+      return remotePort;
+   }
+   /**
+    * Returns remote host name, it is usefull in cases when addresses
+    * are resolved by proxy, and we can't create InetAddress object.
+      @return The name of the host this socket is connected to.
+    */
+   public String getHost(){
+      return remoteHost;
+   }
+   /**
+    * Get remote host as InetAddress object, might return null if 
+    * addresses are resolved by proxy, and it is not possible to resolve
+    * it locally
+      @return Ip address of the host this socket is connected to, or null
+      if address was returned by the proxy as DOMAINNAME and can't be
+      resolved locally.
+    */
+   public InetAddress getInetAddress(){
+      if(remoteIP == null){
+	 try{
+	   remoteIP = InetAddress.getByName(remoteHost);
+	 }catch(UnknownHostException e){
+	   return null;
+	 }
+      }
+      return remoteIP;
+   }
+
+   /**
+    * Get the port assigned by the proxy for the socket, not
+    * the port on locall machine as in Socket. 
+      @return Port of the socket used on the proxy server.
+    */
+   public int getLocalPort(){
+      return localPort;
+   }
+
+   /**
+    * Get address assigned by proxy to make a remote connection,
+    * it might be different from the host specified for the proxy.
+    * Can return null if socks server returned this address as hostname
+    * and it can't be resolved locally, use getLocalHost() then.
+      @return Address proxy is using to make a connection.
+    */
+   public InetAddress getLocalAddress(){
+      if(localIP == null){
+	 try{
+	    localIP = InetAddress.getByName(localHost);
+	 }catch(UnknownHostException e){
+	   return null;
+	 }
+      }
+      return localIP;
+   }
+   /**
+      Get name of the host, proxy has assigned to make a remote connection
+      for this socket. This method is usefull when proxy have returned
+      address as hostname, and we can't resolve it on this machine.
+      @return The name of the host proxy is using to make a connection.
+   */
+   public String getLocalHost(){
+      return localHost;
+   }
+
+   /**
+     Same as socket.
+   */
+   public void setSoLinger(boolean on,int val) throws SocketException{
+      proxy.proxySocket.setSoLinger(on,val);
+   }
+   /**
+     Same as socket.
+   */
+   public int getSoLinger(int timeout) throws SocketException{
+      return proxy.proxySocket.getSoLinger();
+   }
+   /**
+     Same as socket.
+   */
+   public void setSoTimeout(int timeout) throws SocketException{
+      proxy.proxySocket.setSoTimeout(timeout);
+   }
+   /**
+     Same as socket.
+   */
+   public int getSoTimeout(int timeout) throws SocketException{
+      return proxy.proxySocket.getSoTimeout();
+   }
+   /**
+     Same as socket.
+   */
+   public void setTcpNoDelay(boolean on) throws SocketException{
+     proxy.proxySocket.setTcpNoDelay(on);
+   }
+   /**
+     Same as socket.
+   */
+   public boolean getTcpNoDelay() throws SocketException{
+     return proxy.proxySocket.getTcpNoDelay();
+   }
+
+   /**
+     Get string representation of the socket.
+   */
+   public String toString(){
+      if(directSock!=null) return "Direct connection:"+directSock;
+      return ("Proxy:"+proxy+";"+"addr:"+remoteHost+",port:"+remotePort
+                                +",localport:"+localPort);
+
+   }
+
+//Private Methods
+//////////////////
+
+   private void processReply(ProxyMessage reply)throws SocksException{
+      localPort = reply.port;
+      /*
+       * If the server have assigned same host as it was contacted on
+       * it might return an address of all zeros
+       */
+      if(reply.host.equals("0.0.0.0")){
+         localIP = proxy.proxyIP;
+         localHost = localIP.getHostName();
+      }else{
+         localHost = reply.host;
+         localIP = reply.ip;
+      }
+   }
+   private void doDirect()throws SocksException{
+      try{
+         //System.out.println("IP:"+remoteIP+":"+remotePort);
+         directSock = new Socket(remoteIP,remotePort);
+         proxy.out = directSock.getOutputStream();
+         proxy.in  = directSock.getInputStream();
+         proxy.proxySocket = directSock;
+         localIP = directSock.getLocalAddress();
+         localPort = directSock.getLocalPort();
+      }catch(IOException io_ex){
+         throw new SocksException(Proxy.SOCKS_DIRECT_FAILED,
+                                  "Direct connect failed:"+io_ex);
+      }
+   }
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/XSiFileTransfer.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/XSiFileTransfer.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/XSiFileTransfer.java	(revision 3952)
@@ -0,0 +1,106 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+// Created on 20-okt-2004
+package nu.fw.jeti.plugins.filetransfer;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XExtension;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class XSiFileTransfer extends Extension implements XExtension
+{
+	private String name;
+	private String hash;
+	private String date;
+	private String description;
+	private long size;
+	private int length;
+	private long offset;
+
+	public XSiFileTransfer(String name, String hash, String date, long size,String description,int length,long offset)
+	{
+		this.name = name;
+		this.hash = hash;
+		this.date = date;
+		this.size = size;
+		this.description=description;
+	}
+	
+	public String getHash()
+	{
+		return hash;
+	}
+	
+	public int getLength()
+	{
+		return length;
+	}
+	
+	public String getName()
+	{
+		return name;
+	}
+	
+	public String getDescription()
+	{
+		return description;
+	}
+	
+	public long getOffset()
+	{
+		return offset;
+	}
+	
+	public long getSize()
+	{
+		return size;
+	}
+		
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'");
+		appendAttribute(xml,"name",name);
+		appendAttribute(xml,"hash",hash);
+		appendAttribute(xml,"date",date);
+		appendAttribute(xml,"size",String.valueOf(size));
+		xml.append(">");
+		appendElement(xml,"desc",description);
+		if(length>0)
+		{
+			xml.append("<range");
+			appendAttribute(xml,"lengt",String.valueOf(length));
+			appendAttribute(xml,"offset",String.valueOf(offset));
+			xml.append("/>");
+		}
+		xml.append("</file>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/PrefPanel.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/PrefPanel.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/PrefPanel.java	(revision 3952)
@@ -0,0 +1,137 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.plugins.filetransfer;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import javax.swing.*;
+import javax.swing.Box;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.plugins.PreferencesPanel;
+import nu.fw.jeti.plugins.filetransfer.socks5.StreamHost;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+
+//7-2-2004
+/**
+ * @author E.S. de Boer
+ */
+
+public class PrefPanel extends PreferencesPanel
+{
+	private JComboBox cmbIP;
+	private JTextField txtPort = new JTextField();
+	private JCheckBox chkUseLocal;
+	private JCheckBox chkCloseOnComplete;
+
+	public PrefPanel(Backend backend)
+	{//TODO add network config wizard with auto network detection (connect to php)
+		setLayout(new GridBagLayout());
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.anchor = GridBagConstraints.LINE_START;
+        c.fill = GridBagConstraints.HORIZONTAL;
+        c.insets = new Insets(3, 5, 0, 3);
+
+        String choosen = Preferences.getString("filetransfer","ip",null);
+        String ip = Plugin.getIP();
+        String[] ips;
+        if(choosen!=null && !choosen.equals("automatic"))
+        {
+        		if(ip!=null) ips = new String[]{choosen,ip
+        				,I18N.gettext("filetransfer.automatic")+ " (" + ip + ")"};
+        		else ips = new String[]{choosen};
+        }
+        else if (ip!=null) ips = new String[]{I18N.gettext("filetransfer.automatic")+ " (" + ip + ")",ip};
+        else ips = new String[0];
+        cmbIP = new JComboBox(ips);
+        cmbIP.setEditable(true);
+        
+        //cmbIP.setText();
+		
+        
+        
+		txtPort.setText(Preferences.getString("filetransfer","port", "7777"));
+		chkUseLocal = new JCheckBox(I18N.gettext("filetransfer.behind_firewall_or_NAT"));
+		chkUseLocal.setSelected(!Preferences.getBoolean("filetransfer", "useLocalIP", false));
+		add(chkUseLocal,c);
+				
+		c.gridwidth = 1;
+		c.anchor=GridBagConstraints.LINE_START;
+		add(new JLabel(I18N.gettext("filetransfer.ip")),c);
+		c.gridwidth = GridBagConstraints.REMAINDER;
+		add(cmbIP,c);
+		c.gridwidth = 1;
+        add(new JLabel(I18N.gettext("filetransfer.port")),c);
+        c.gridwidth = GridBagConstraints.REMAINDER;
+		add(txtPort,c);
+		c.gridwidth = GridBagConstraints.REMAINDER;
+        chkCloseOnComplete = new JCheckBox(I18N.gettext("filetransfer.close_download_windows_when_download_ready"));
+        chkCloseOnComplete.setSelected(Preferences.getBoolean("filetransfer", "closeOnComplete", false));
+		add(chkCloseOnComplete,c);
+		c.gridwidth = 2;
+        c.weighty = 1.0;
+        c.weightx = 1.0;
+        add(Box.createVerticalGlue(), c);
+	}
+
+	public void savePreferences()
+	{
+		Preferences.putBoolean("filetransfer", "useLocalIP", !chkUseLocal.isSelected());
+		Preferences.putBoolean("filetransfer", "closeOnComplete", chkCloseOnComplete.isSelected());
+		if(cmbIP.getSelectedIndex()!=-1 || (cmbIP.getSelectedItem()!=null && !cmbIP.getSelectedItem().equals("")))
+		{	
+			String ip =cmbIP.getSelectedItem().toString();
+			if(ip!=null)
+			{
+				if(ip.startsWith(I18N.gettext("filetransfer.automatic")))ip = "automatic";
+				Preferences.putString("filetransfer","ip",ip);
+			}
+		}
+		if(!txtPort.getText().equals(""))
+		{	
+			try 
+			{
+				int scrollTime = Integer.parseInt(txtPort.getText());
+				Preferences.putInteger("titlescroller","scrolltime", scrollTime);
+			}
+			catch(NumberFormatException e) {}
+		}
+		
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBReceive.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBReceive.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBReceive.java	(revision 3952)
@@ -0,0 +1,164 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.plugins.filetransfer.ibb;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.util.LinkedList;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.plugins.filetransfer.GetFileWindow;
+import nu.fw.jeti.plugins.filetransfer.StreamReceive;
+import nu.fw.jeti.util.Base64;
+import nu.fw.jeti.util.Popups;
+
+//24-okt-2004
+public class IBBReceive extends Thread implements StreamReceive
+{
+	private String sid;
+	private JID jid;
+	private Backend backend;
+	private long bytes;
+	private LinkedList queue = new LinkedList();
+	private volatile boolean isDownloading=true;
+	private GetFileWindow getFileWindow;
+
+
+    public IBBReceive(JID jid,String sid,Backend backend,GetFileWindow window)
+    {
+		this.jid = jid;
+		this.sid=sid;
+		this.backend = backend;
+		getFileWindow =window;
+		setPriority(NORM_PRIORITY);
+		start();
+	}
+		
+    public void addData(String data)
+    {
+    	synchronized(queue)
+		{
+			queue.addLast(data);
+			queue.notifyAll();
+		}
+    }
+    
+    public void stopDownloading()
+    {
+    	isDownloading = false;
+		synchronized(queue){queue.notifyAll();}
+    }
+    
+    public long getBytes()
+    {
+    	return bytes;
+    }
+    
+    public void cancel()
+    {
+    	backend.send(new InfoQuery(jid,"set",new IBBExtension(sid)));
+    	stopDownloading();
+    }
+      
+	public void run()
+	{
+									
+		BufferedOutputStream out= new BufferedOutputStream(getFileWindow.getOutputStream());
+//		try{
+//			out = new BufferedOutputStream (new FileOutputStream(file));
+//		}catch(FileNotFoundException e2)
+//		{
+//			Popups.errorPopup(file.getAbsolutePath() + " could not be openend in write mode","File transfer");
+//			getFileWindow.stopDownloading();
+//		}
+		if(out!=null)
+		{	
+			try{
+				while(!queue.isEmpty() || isDownloading)
+				{
+					String base64Data;
+					synchronized(queue)
+					{
+						if (queue.isEmpty())
+						{
+							try
+							{
+								//System.out.println("waiting");
+								queue.wait();
+							}
+							catch(InterruptedException e)
+							{//bug when thrown? called when interrupted
+								e.printStackTrace();
+								return;
+							}
+							continue;
+						}
+						base64Data = (String)queue.removeFirst();
+						//System.out.println("data read");
+					}	
+					//System.out.println(base64Data);
+					//System.out.println(Base64.decode2(base64Data));
+					
+					byte[] data = Base64.decode(base64Data);
+												
+					
+					//System.out.println("data converted");
+					out.write(data, 0, data.length);
+					//System.out.println("data written");
+					bytes+=data.length;
+					//progressMonitor.setProgress(bytes);
+					//bytes++;
+					//if (Thread.interrupted()) throw new InterruptedException();
+					//yield();
+				}
+			}catch (IOException e2)  //probably out of hd
+			{
+				Popups.errorPopup(e2.getMessage() + " while downloading " ,"File transfer");
+				//download not ok
+				backend.send(new InfoQuery(jid,"set",new IBBExtension(sid)));
+				getFileWindow.stopDownloading();
+			}
+			finally
+			{
+				try
+				{
+					out.close();
+				} catch (IOException e)
+				{
+					e.printStackTrace();
+				}
+			}
+		}
+		getFileWindow.stopDownloading();
+		//System.out.println("downloaded");
+	}
+	
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBSend.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBSend.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBSend.java	(revision 3952)
@@ -0,0 +1,140 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+package nu.fw.jeti.plugins.filetransfer.ibb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import nu.fw.jeti.events.IQResultListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.Message;
+import nu.fw.jeti.plugins.filetransfer.StreamSend;
+import nu.fw.jeti.plugins.filetransfer.SendFileProgress;
+import nu.fw.jeti.util.Base64;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+
+public class IBBSend extends Thread implements StreamSend
+{
+	final static int BUF_SIZE = 4096;
+    private File file;
+	
+    private SendFileProgress window;
+	private long bytes;
+	private int sequence;
+	private Backend backend;
+	private JID to;
+	private String sid;
+    /* buffer to use for requests */
+    byte[] buf;
+ 
+    public IBBSend(File file,Backend backend,SendFileProgress ws,JID to,String id) {
+        buf = new byte[BUF_SIZE];
+		this.file = file;
+		this.backend = backend;
+		window =ws;
+		this.to = to;
+		sid = id;
+		setPriority(NORM_PRIORITY);
+		backend.send(new InfoQuery(to,"set","ibb" + backend.getIdentifier(), new IBBExtension(sid,BUF_SIZE)),new IQResultListener()
+		{
+			public void iqResult(InfoQuery iq)
+			{
+				if(iq.getType().equals("result"))
+				{
+					start();
+				}
+				else
+				{//TODO add refuse error
+					Popups.messagePopup(I18N.gettext("filetransfer.Problem_during_file_transfer,_transfer_aborted"),I18N.gettext("filetransfer.File_Transfer"));
+					window.stop();
+				}
+			}
+		},0);
+	}
+
+    public void run() 
+    {
+       	InputStream is = null;
+    	try {
+    		is = new FileInputStream(file.getAbsolutePath());
+            for(int readed = is.read(buf);readed > 0;readed =is.read(buf))
+            {
+            	String encoded = Base64.encodeBytes(buf,0,readed);
+            	backend.send(new Message(null,to,new IBBExtension(sid,sequence,encoded)));
+                //TODO add message resultlistener instead of iqresultlistener
+				sequence++;
+				bytes+=readed;
+				if (Thread.interrupted())
+				{
+					sendStreamClose();
+					is.close();
+					window.stop();
+					return;
+				}
+				Thread.yield();
+            }
+            backend.send(new InfoQuery(to,"set",new IBBExtension(sid)));
+    	}catch(IOException e)
+		{
+    		sendStreamClose();
+    		Popups.messagePopup(I18N.gettext("filetransfer.Problem_during_file_transfer,_transfer_aborted"),I18N.gettext("filetransfer.File_Transfer"));
+			window.stop();
+		}
+        finally {
+        	try{
+            is.close();
+        	}catch(IOException e){e.printStackTrace();}
+		}
+        window.done();
+    }
+
+    private void sendStreamClose()
+    {
+    	backend.send(new InfoQuery(to,"set","ibb" + backend.getIdentifier(), new IBBExtension(sid)));
+    }
+    			
+    public long getBytes()
+	{
+		return bytes;
+	}
+	
+	public void cancel()
+	{
+		interrupt();
+	}
+}
+
+
+
+
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBExtension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBExtension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBExtension.java	(revision 3952)
@@ -0,0 +1,194 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+// Created on 8-sept-2004
+package nu.fw.jeti.plugins.filetransfer.ibb;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import nu.fw.jeti.backend.XExecutableExtension;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQExtension;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.Packet;
+import nu.fw.jeti.jabber.elements.XMPPError;
+import nu.fw.jeti.jabber.elements.XMPPErrorTag;
+import nu.fw.jeti.plugins.filetransfer.GetFileWindow;
+import nu.fw.jeti.plugins.filetransfer.Plugin;
+import nu.fw.jeti.util.Log;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class IBBExtension extends Extension implements IQExtension, XExecutableExtension
+{
+	private boolean close;
+	private boolean open;
+	private String sid;
+	private int sequence;
+	private int blockSize=4096;
+	//private byte[] data;
+	private String base64data;
+	private static Map fileWindows = new HashMap(10);
+
+	public IBBExtension(String sid,int blockSize)
+	{
+		open=true;
+		this.blockSize = blockSize;
+		this.sid=sid;
+	}
+	
+	public IBBExtension(String sid)
+	{
+		close=true;
+		this.sid=sid;
+	}
+	
+	
+	public IBBExtension(String sid,int sequence, String data)
+	{
+		this.sid = sid;
+		this.sequence = sequence;
+		base64data = data;
+	}
+	
+	public IBBExtension(boolean open,boolean close, String sid, int blockSize,int sequence, String data)
+	{
+		this.open=open;
+		this.close = close;
+		this.sid = sid;
+		this.blockSize=blockSize;
+		this.sequence = sequence;
+		base64data = data;
+	}
+	
+	public String getData()
+	{
+		return base64data;
+	}
+	
+	public String getSid()
+	{
+		return sid;
+	}
+	
+	public boolean isOpen()
+	{
+		return open;
+	}
+	
+	public boolean isClose()
+	{
+		return close;
+	}
+	
+	
+	public void execute(Packet iq, Backend backend)
+	{
+		//System.out.println(getSid());
+		GetFileWindow w= (GetFileWindow)fileWindows.get(sid);
+		if(w==null)
+		{
+			Log.xmlReceivedError("ibb stream from unknown");
+		}
+		else
+		{
+			IBBReceive r= (IBBReceive)w.getStreamReceive();
+			r.addData(getData());
+		}
+	}
+
+	public void execute(InfoQuery iq, Backend backend)
+	{
+		if(iq.getType().equals("set"))
+		{
+			//Plugin.ibb(iq.getFrom(),this);
+						
+			if(isOpen())
+			{
+				GetFileWindow w= Plugin.getGetFile(iq.getFrom(),getSid());
+				if(w!=null)
+				{//file accepted by filetransfer protocol;
+					//System.out.println(getSid());
+					w.startDownloading(new IBBReceive(iq.getFrom(),getSid(),backend,w));
+					fileWindows.put(getSid(),w);
+					backend.send(new InfoQuery(iq.getFrom(),"result",iq.getID(),null));
+					Log.xmlReceivedError("ibb stream init from unknown");
+				}
+				else
+				{
+					XMPPError e = new XMPPError("cancel",501);
+					e.addError(new XMPPErrorTag("feature-not-implemented"));
+					backend.send(new InfoQuery(iq.getFrom(),iq.getID(),e));
+				}
+			}
+			if(isClose())
+			{
+				GetFileWindow w=  (GetFileWindow) fileWindows.remove(sid);
+				if(w!=null)((IBBReceive)w.getStreamReceive()).stopDownloading();
+				//System.out.println(" close  " + sid);
+				backend.send(new InfoQuery(iq.getFrom(),"result",iq.getID(),null));
+			}
+		}
+	}
+	
+	public void appendToXML(StringBuffer xml)
+	{
+		if(open)
+		{
+			xml.append("<open xmlns= 'http://jabber.org/protocol/ibb'");
+			appendAttribute(xml,"sid",sid);
+			appendAttribute(xml,"block-size",String.valueOf(blockSize));
+			xml.append("/>");
+		}
+		else if (close)
+		{
+			xml.append("<close xmlns= 'http://jabber.org/protocol/ibb'");
+			appendAttribute(xml,"sid",sid);
+			xml.append("/>");
+		}
+		else
+		{
+			xml.append("<data xmlns= 'http://jabber.org/protocol/ibb'");
+			appendAttribute(xml,"sid",sid);
+			appendAttribute(xml,"seq",String.valueOf(sequence));
+			xml.append(">");
+			//addData(data,xml);
+			xml.append(base64data);
+			xml.append("</data>");
+			//TODO change to AMP protocol instead of hardcoding
+			xml.append("<amp xmlns='http://jabber.org/protocol/amp'>");
+			xml.append("<rule condition='deliver-at' value='stored' action='error'/>");
+			xml.append("<rule condition='match-resource' value='exact' action='error'/>");
+			xml.append("</amp>");
+		}
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/ibb/IBBHandler.java	(revision 3952)
@@ -0,0 +1,91 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+// Created on 18-sept-2004
+package nu.fw.jeti.plugins.filetransfer.ibb;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.util.Log;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class IBBHandler extends ExtensionHandler
+{
+	private boolean close;
+	private boolean open;
+	private String sid;
+	private int sequence;
+	private int blockSize;
+	private String data;
+	
+	public void startHandling(Attributes attr)
+	{
+		close=open=false;
+		open=true;
+		sid=null;
+		data=null;
+		blockSize=4096;
+		sid= attr.getValue("sid");
+		try{
+			blockSize = Integer.parseInt(attr.getValue("block-size"));
+			open=true;
+		}
+		catch(NumberFormatException e)
+		{
+			open=false;
+		}
+		try{
+			sequence =  Integer.parseInt(attr.getValue("seq"));
+			close = false;
+		}
+		catch(NumberFormatException e)
+		{
+			if(!open)close=true;
+		}
+	}
+	
+//	public void endElement(String name)
+//	{
+//		System.out.println(name);
+//		data = getText();
+//		clearCurrentChars();
+//	}
+
+	public Extension build()
+	{
+		data = getText();
+		clearCurrentChars();
+		return new IBBExtension(open,close,sid,blockSize,sequence,data);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/Plugin.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/Plugin.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/Plugin.java	(revision 3952)
@@ -0,0 +1,327 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.plugins.filetransfer;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.File;
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.NumberFormat;
+import java.util.*;
+
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JTree;
+import javax.swing.TransferHandler;
+
+import nu.fw.jeti.backend.roster.PrimaryJIDStatus;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XData;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.plugins.Plugins;
+import nu.fw.jeti.plugins.RosterMenuListener;
+import nu.fw.jeti.plugins.filetransfer.ibb.IBBHandler;
+import nu.fw.jeti.plugins.filetransfer.socks5.Socks5Handler;
+import nu.fw.jeti.plugins.filetransfer.socks5.Socks5Send;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+import nu.fw.jeti.util.Utils;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ 
+ */
+//Created on 20-okt-2004
+public class Plugin implements Plugins
+{
+	private static Backend backend;
+	private static Plugin plugin;
+    private static JFileChooser fileChooser = new JFileChooser();
+	private Map fileWindows = new HashMap(10);
+	public final static String VERSION = "0.1";
+	public final static String DESCRIPTION = "filetransfer.File_Transfer";
+	public final static String MIN_JETI_VERSION = "0.6.1";
+	public final static String NAME = "filetransfer";
+	public final static String ABOUT = "by E.S. de Boer";
+	
+	public static void init(final Backend backend)
+    {
+		backend.getMain().addToOnlineRosterMenu(I18N.gettext("filetransfer.Transfer_File")+ "...",new RosterMenuListener()
+		{
+			public void actionPerformed(JIDStatus jidStatus,nu.fw.jeti.backend.roster.JIDStatusGroup group)
+			{
+				if(Socks5Send.isProxyAvailable() ||
+						Preferences.getBoolean("filetransfer", "useLocalIP", true))
+				{
+					new SendFileWindow(backend,jidStatus.getCompleteJID()).setVisible(true);
+				}
+				else
+				{
+					Popups.errorPopup(I18N.gettext("filetransfer.File_Transfer_not_available,_because_no_filetransfer_proxy_is_available"),I18N.gettext("filetransfer.File_Transfer"));
+				}
+			}
+		});
+
+		plugin = new Plugin();
+		Plugin.backend = backend;
+		backend.addExtensionHandler("http://jabber.org/protocol/si",new IQSiHandler());
+		backend.addExtensionHandler("http://jabber.org/protocol/si/profile/file-transfer",new XSiFileTransferHandler());
+		backend.addCapability("si", "http://jabber.org/protocol/bytestreams");
+		backend.addCapability("si", "http://jabber.org/protocol/ibb");
+		backend.addCapability("si", "http://jabber.org/protocol/si");
+		backend.addCapability("si", "http://jabber.org/protocol/si/profile/file-transfer");
+		backend.addExtensionHandler("http://jabber.org/protocol/feature-neg",new ExtensionHandler()
+		{
+			private XData data;
+			
+			public void startHandling(Attributes attr)
+			{
+				data=null;
+			}
+			
+			public void addExtension(Extension extension)
+			{
+				if(extension instanceof XData) data = (XData)extension;
+			}
+
+			public Extension build() throws InstantiationException
+			{
+				return data;
+			}
+		});
+		backend.addExtensionHandler("http://jabber.org/protocol/ibb",new IBBHandler());
+		backend.addExtensionHandler("http://jabber.org/protocol/bytestreams",new Socks5Handler());
+		Socks5Send.initProxies(backend);
+        backend.getMain().getOnlinePanel().setTransferHandler(new FileTransferhandler());
+    }
+
+	protected static Backend getBackend()
+	{
+		return backend;
+	}
+	
+	public static void addGetFile(GetFileWindow w, JID jid,String id)
+	{
+		//FIXME add sid to getfile to know the precise file
+		plugin.fileWindows.put(jid,w);
+		//plugin.fileWindows.put(new JIDSID(jid,id),w);
+	}
+	
+	public static GetFileWindow getGetFile(JID jid,String sid)
+	{
+		//FIXME add sid to getfile to know the precise file
+		return (GetFileWindow)plugin.fileWindows.remove(jid);
+		//return (GetFileWindow)plugin.fileWindows.remove(new JIDSID(jid,sid));
+	}
+	
+	public static void unload(Backend backend)
+	{
+		backend.getMain().removeFromRosterMenu(I18N.gettext("filetransfer.Transfer_File")+ "...");
+		//backend.getMain().removeFromMenu(plugin.menuItem);
+		backend.removeExtensionHandler("http://jabber.org/protocol/ibb");
+		backend.removeExtensionHandler("http://jabber.org/protocol/si");
+		backend.removeExtensionHandler("http://jabber.org/protocol/si/profile/file-transfer");
+		backend.removeExtensionHandler("http://jabber.org/protocol/feature-neg");
+		backend.removeCapability("si", "http://jabber.org/protocol/bytestreams");
+		backend.removeCapability("si", "http://jabber.org/protocol/ibb");
+		backend.removeCapability("si", "http://jabber.org/protocol/si");
+		backend.removeCapability("si", "http://jabber.org/protocol/si/profile/file-transfer");
+		
+		plugin = null;
+	}
+	
+	public void unload() {}
+	
+	public static String sha(String sid,JID initiator,JID target)
+	{
+		//System.out.println(sid.toString());
+		//System.out.println(initiator.toString());
+		//System.out.println(target.toString());
+		
+		MessageDigest sha=null;
+		try
+		{
+			sha = MessageDigest.getInstance("SHA");
+		} catch (NoSuchAlgorithmException e)
+		{
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		sha.update(sid.getBytes());
+		sha.update(stringPrep(initiator).getBytes());
+		return Utils.toString(sha.digest(stringPrep(target).getBytes()));
+	}
+	
+	private static String stringPrep(JID jid)
+	{//TODO make real stringprep
+		StringBuffer j = new StringBuffer();
+		if (jid.getUser() != null){
+			j.append(jid.getUser().toLowerCase());
+			j.append("@");
+		}
+		j.append(jid.getDomain().toLowerCase());
+		if (jid.getResource() != null){
+			j.append("/");
+			j.append(jid.getResource());
+		}
+		return j.toString();
+	}
+		
+	public static String getIP()
+	{
+		Enumeration interfaceEnum;
+		try
+		{
+			interfaceEnum = NetworkInterface.getNetworkInterfaces();
+			while (interfaceEnum.hasMoreElements())
+			{
+				NetworkInterface n = (NetworkInterface)
+									interfaceEnum.nextElement();
+				//System.out.println(n.getDisplayName() + ":");
+				Enumeration ipEnum = n.getInetAddresses();
+				while (ipEnum.hasMoreElements())
+				{
+					InetAddress ip = (InetAddress) ipEnum.nextElement();
+					if(ip instanceof Inet4Address)
+					{
+						if (!ip.isLoopbackAddress())
+						{
+							return ip.getHostAddress();
+						}
+					}
+				}
+			}
+		} catch (SocketException e)
+		{
+			e.printStackTrace();
+		}
+
+		return null;
+	}
+
+    protected static JFileChooser getFileChooser() {
+        return fileChooser;
+    }
+
+	public static String getSizeText(long size)
+	{
+		if(size<1024) return size + " bytes";
+		double s;
+		NumberFormat n = NumberFormat.getNumberInstance();
+		n.setMaximumFractionDigits(2);
+		s = size/1024d;
+		if(s<1024) return n.format(s) + " kB";
+		s = s/1024d;
+		if(s<1024) return n.format(s) + " MB";
+		s = s/1024d;
+		return n.format(s) + " GB";
+	}
+
+	static private class FileTransferhandler extends TransferHandler {
+
+        public boolean canImport(JComponent comp, DataFlavor[] flavors) {
+            for (int i = 0; i < flavors.length; i++) {
+                if (flavors[i].equals(DataFlavor.javaFileListFlavor)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public boolean importData(JComponent comp, Transferable t) {
+            Object o = ((JTree)comp).getLastSelectedPathComponent();
+            JID to;
+
+            if(!Socks5Send.isProxyAvailable() &&
+					!Preferences.getBoolean("filetransfer", "useLocalIP", true))
+			{
+            	Popups.errorPopup(I18N.gettext("filetransfer.File_Transfer_not_available,_because_no_filetransfer_proxy_is_available"),I18N.gettext("filetransfer.File_Transfer"));
+            	return false;
+			}
+            
+			if (o instanceof JIDStatus) {
+                to = ((JIDStatus)o).getCompleteJID();
+            } else if (o instanceof PrimaryJIDStatus) {
+                PrimaryJIDStatus p = (PrimaryJIDStatus)o;
+                to = p.getJIDPrimaryStatus().getCompleteJID();
+            } else {
+                return false;
+            }
+
+            if (!t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
+                return false;
+            }
+            try {
+                Collection files = (Collection) t.getTransferData(
+                    DataFlavor.javaFileListFlavor);
+
+                if (files == null || files.size() == 0) {
+                    return false;
+                }
+
+                // Check that everything is regular files
+                for (Iterator i = files.iterator(); i.hasNext(); ) {
+                    File file = (File) i.next();
+                    if (!file.isFile()) {
+                        Popups.errorPopup(
+                            I18N.gettext("filetransfer.Can_not_transfer_directories"),
+                            I18N.gettext("main.error.Error"));
+                        return false;
+                    }
+                }
+
+                for (Iterator i = files.iterator(); i.hasNext(); ) {
+                    File file = (File) i.next();
+                    new SendFileWindow(backend, to, file).setVisible(true);
+                }
+
+                return true;
+            } catch (UnsupportedFlavorException e) {
+                System.out.println("unsupported data");
+            } catch (IOException e) {
+                System.out.println("IOException");
+            }
+            return false;
+        }
+    }
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/SendFileProgress.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/SendFileProgress.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/SendFileProgress.java	(revision 3952)
@@ -0,0 +1,336 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+ 
+package nu.fw.jeti.plugins.filetransfer;
+
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.text.MessageFormat;
+import java.util.Iterator;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JLayeredPane;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.KeyStroke;
+import javax.swing.SwingConstants;
+
+import nu.fw.jeti.backend.roster.Roster;
+import nu.fw.jeti.events.ErrorListener;
+import nu.fw.jeti.events.IQResultListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.XData;
+import nu.fw.jeti.jabber.elements.XDataBuilder;
+import nu.fw.jeti.jabber.elements.XDataField;
+import nu.fw.jeti.jabber.elements.XDataFieldBuilder;
+import nu.fw.jeti.plugins.filetransfer.ibb.IBBSend;
+import nu.fw.jeti.plugins.filetransfer.socks5.Socks5Send;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+
+//24-0kt-2004
+public class SendFileProgress extends JFrame implements IQResultListener
+{
+    private Backend backend;
+    private JID to;
+    private StreamSend streamSend;
+    private javax.swing.Timer timer;
+    private String id;
+    private File file;
+    private boolean begun = false;
+
+    private JButton btnAbort = new JButton();
+    private JLabel toTxt = new JLabel();
+    private JLabel fileTxt = new JLabel();
+    private JLabel sizeTxt = new JLabel();
+    private JLabel statusTxt = new JLabel();
+    private JProgressBar progressBar = new JProgressBar();
+
+    public SendFileProgress(Backend backend, JID to, File file,
+                            String description) {
+        this.backend = backend;
+        this.to = to;
+        this.file = file;
+        try {
+            jbInit();
+        } catch(Exception e) {
+            e.printStackTrace();
+        }
+        JIDStatus js = Roster.getJIDStatus(to);
+        if(js!=null)toTxt.setText(js.getNick());
+        else toTxt.setText(to.toString()); 
+        	
+        fileTxt.setText(file.toString());
+        sizeTxt.setText(Plugin.getSizeText(file.length()));
+        statusTxt.setText(I18N.gettext("filetransfer.Opening_connection") + "...");
+
+        pack();
+        setLocationRelativeTo(null);
+
+        progressBar.setMaximum((int)(file.length()/1024));
+
+        id = "file"+backend.getIdentifier();
+        XDataBuilder xdb = new XDataBuilder();
+        xdb.type ="form";
+        XDataFieldBuilder xdfb = new XDataFieldBuilder();
+        xdfb.var="stream-method";
+        xdfb.type="list-single";
+
+        if (Preferences.getBoolean("filetransfer","bytestreams.enable",true))
+        {
+        	xdfb.addOption("http://jabber.org/protocol/bytestreams");
+        }
+        if (Preferences.getBoolean("filetransfer", "ibb.enable", true))
+        {
+        	xdfb.addOption("http://jabber.org/protocol/ibb");
+        }
+        xdb.addField(xdfb.build());
+        XData xdata=null;
+        try {
+            xdata = xdb.build();
+        } catch (InstantiationException e1) {
+            e1.printStackTrace();
+        }
+        IQSi si = new IQSi(
+            id, "http://jabber.org/protocol/si/profile/file-transfer",null, 
+            xdata,
+            new XSiFileTransfer(file.getName(), null, null, file.length(),
+                                description, 0, 0));
+        backend.send(
+            new InfoQuery(to,"set","filetransfer"+backend.getIdentifier(),si),
+            this, 0);
+		
+        backend.addListener(ErrorListener.class,this);
+    }
+
+    private void jbInit() throws Exception
+    {
+        setIconImage(
+            nu.fw.jeti.images.StatusIcons.getImageIcon("jeti").getImage());
+        setTitle(I18N.gettext("filetransfer.File_Transfer"));
+
+        getContentPane().setLayout(new GridBagLayout());
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridwidth = 1;
+        c.anchor = GridBagConstraints.LINE_START;
+        c.fill = GridBagConstraints.BOTH;
+        c.insets = new Insets(3, 5, 0, 3);
+        GridBagConstraints c2 = (GridBagConstraints)c.clone();
+        c2.gridwidth = GridBagConstraints.REMAINDER;
+        c2.weightx = 1.0;
+
+        JLabel lbl = new JLabel(I18N.gettext("filetransfer.To"));
+        getContentPane().add(lbl, c);
+        getContentPane().add(toTxt, c2);
+        
+        lbl = new JLabel(I18N.gettext("filetransfer.File_Name"));
+        getContentPane().add(lbl, c);
+        getContentPane().add(fileTxt, c2);
+
+        lbl = new JLabel(I18N.gettext("filetransfer.File_Size"));
+        getContentPane().add(lbl, c);
+        getContentPane().add(sizeTxt, c2);
+
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.insets = new Insets(5, 5, 0, 5);
+        c.anchor = GridBagConstraints.CENTER;
+        c.weightx = 1.0;
+        c.fill = GridBagConstraints.NONE;
+        getContentPane().add(statusTxt, c);
+
+        progressBar.setPreferredSize(new Dimension(300, 17));
+        c.fill = GridBagConstraints.BOTH;
+        c.insets = new Insets(0, 5, 0, 5);
+        getContentPane().add(progressBar, c);
+
+        getRootPane().setDefaultButton(btnAbort);
+        c.fill = GridBagConstraints.NONE;
+        c.insets = new Insets(5, 5, 5, 5);
+        getContentPane().add(btnAbort, c);
+
+        Action abortAction =
+            new AbstractAction(I18N.gettext("filetransfer.Abort")) {
+            public void actionPerformed(ActionEvent e) {
+                btnAbort_actionPerformed(e);
+            }
+        };
+        btnAbort.setAction(abortAction);
+
+        KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
+        JLayeredPane layeredPane = getLayeredPane();
+        layeredPane.getActionMap().put("cancel", abortAction);
+        layeredPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(stroke, "abort");
+    }
+
+    void btnAbort_actionPerformed(ActionEvent e)
+    {
+        if(streamSend!=null)
+        {
+            timer.stop();
+            streamSend.cancel();
+        }
+        this.dispose();
+    }
+    
+    public void iqResult(InfoQuery query)
+    {
+
+    	if(query.getType().equals("result"))
+    	{
+            IQSi iqSi =(IQSi) query.getIQExtension();
+            XSiFileTransfer sifi = iqSi.getSiprofile();
+            if( sifi != null )
+            {//TODO range
+    			
+            }
+            String streamMethod=null;
+            XData data= iqSi.getXDataForm();
+            if(data.hasFields())
+            {
+                for(Iterator i = data.getFields();i.hasNext();)
+                {
+                    XDataField datafield= (XDataField)i.next();
+                    if("stream-method".equals(datafield.getVar()))
+                    {
+                        streamMethod = datafield.getValue();
+                    }
+                }
+            }
+            
+            /**
+             * @Author(s): Alexandre Correia
+             * @Date : 10/03/2009
+             * @Brief : Modificado para o funcionamento dentro do applet 
+             */
+
+            if("http://jabber.org/protocol/ibb".equals(streamMethod) || "http://jabber.org/protocol/bytestreams".equals(streamMethod))
+            {            	
+            	streamSend = new Socks5Send(file,backend,this,to,id);
+            }
+            else
+            {
+            	Popups.messagePopup(I18N.gettext("filetransfer.Problem_during_file_transfer,_transfer_aborted"),I18N.gettext("filetransfer.File_Transfer"));
+                this.dispose();
+                return;
+            }
+            initTimer();
+        }
+    	else if(query.getType().equals("error"))
+    	{
+    		//FIXME jidstatus can be null (groupchat) 
+            String msg;
+            if( query.getErrorCode()== 403 )
+            {
+                msg = MessageFormat.format(
+                    I18N.gettext("filetransfer.{0}_did_not_accept_the_file"),
+                    new Object[] { Roster.getJIDStatus(to).getNick() });
+            }
+            else if ( query.getErrorCode() == 404 )
+            {
+                msg = MessageFormat.format(
+                    I18N.gettext("filetransfer.{0}_is_not_online"),
+                    new Object[] { Roster.getJIDStatus(to).getNick() });
+            }
+            else if ( query.getErrorCode()== 400 )
+            {
+                msg = I18N.gettext("filetransfer.No_compatible_transfer_protocols_found");
+            }
+            else if (query.getErrorCode()== 501)
+            {
+                msg = MessageFormat.format(
+                    I18N.gettext("filetransfer.{0}_does_not_support_file_transfer_or_is_offline"),
+                    new Object[] { Roster.getJIDStatus(to).getNick() });
+            }
+            else
+            {
+            	msg = I18N.gettext("filetransfer.Problem_during_file_transfer,_transfer_aborted");
+            }
+            Popups.messagePopup(msg, file.getName());
+            this.dispose();
+    	}
+    }
+
+    private void updateProgress() {
+        int bytes=0;
+        bytes = (int)streamSend.getBytes()/1024;
+        if (bytes >0 && !begun) {
+            statusTxt.setText(I18N.gettext("filetransfer.Transferring") + "...");
+            begun = true;
+        }
+        progressBar.setValue(bytes);
+    }
+
+    private void initTimer()
+    {
+        timer = new javax.swing.Timer(1000,new ActionListener() {
+            public void actionPerformed(ActionEvent evt) {
+                updateProgress();
+            }
+        });
+        timer.start();
+    }
+
+    public void stop()
+    {
+        timer.stop();
+        dispose();
+    }
+
+    public void done()
+    {
+        timer.stop();
+        updateProgress();
+        streamSend = null;
+        if(Preferences.getBoolean("filetransfer", "closeOnComplete", false))
+        {
+        	dispose();
+        }
+        else
+        {
+	        statusTxt.setText(I18N.gettext("filetransfer.Transfer_complete"));
+	        btnAbort.setText(I18N.gettext("filetransfer.Close"));
+	        progressBar.setValue(progressBar.getMaximum());
+        }
+    }
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/StreamReceive.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/StreamReceive.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/filetransfer/StreamReceive.java	(revision 3952)
@@ -0,0 +1,43 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 24-okt-2004
+ */
+ 
+package nu.fw.jeti.plugins.filetransfer;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public interface StreamReceive
+{
+	long getBytes();
+	
+	void cancel();
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/NativeUtils.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/NativeUtils.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/NativeUtils.java	(revision 3952)
@@ -0,0 +1,29 @@
+/*
+ * Created on Feb 12, 2004
+ *
+ * To change the template for this generated file go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+package nu.fw.jeti.plugins;
+
+import java.awt.Component;
+
+import javax.swing.JMenu;
+
+import nu.fw.jeti.ui.ChatSplitPane;
+
+public interface NativeUtils
+{
+	public void addChatMenus(JMenu menu ,ChatSplitPane chatPane);
+	public boolean supportsAlpha();
+	public void flash(Component c, boolean flash);
+	public void windowAlwaysOnTop(Component c, boolean flag);
+	public void setWindowAlpha(Component c, int alpha);
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/OpenPGP.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/OpenPGP.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/OpenPGP.java	(revision 3952)
@@ -0,0 +1,51 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 10-okt-2004
+ */
+ 
+package nu.fw.jeti.plugins;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XExtension;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public interface OpenPGP
+{
+	public boolean canEncrypt(JID jid);
+		
+	public XExtension encrypt(String text,JID jid);
+	
+	public String decrypt(Extension extension);
+	
+	public Extension signPresence(String status);
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/RosterMenuListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/RosterMenuListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/RosterMenuListener.java	(revision 3952)
@@ -0,0 +1,18 @@
+package nu.fw.jeti.plugins;
+
+import nu.fw.jeti.backend.roster.JIDStatusGroup;
+
+/**
+
+ */
+
+public interface RosterMenuListener
+{
+	public void actionPerformed(nu.fw.jeti.jabber.JIDStatus jidStatus,JIDStatusGroup group);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/Word.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/Word.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/Word.java	(revision 3952)
@@ -0,0 +1,99 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ *
+ *	Created on 13-feb-2004
+ */
+ 
+package nu.fw.jeti.plugins;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class Word implements Cloneable
+{//TODO check attributes return not null ipv fontsize plugin
+	public String word;
+	private MutableAttributeSet attributes;
+	private MutableAttributeSet mas;
+
+	public Word(StringBuffer word)
+	{
+		this .word = word.toString();
+	}
+	
+	public Word(StringBuffer word,MutableAttributeSet al)
+	{
+		this .word = word.toString();
+		this.attributes = al;
+	}
+	
+	public Word(String word)
+	{
+		this .word = word;
+	}
+	
+	public Word(String word,MutableAttributeSet al)
+	{
+		this .word = word;
+		this.attributes = al;
+	}
+	
+	public void addAttributes(AttributeSet attr)
+	{
+		if(mas==null)
+		{	
+			mas = new SimpleAttributeSet();
+			if(attributes!=null)mas.addAttributes(attributes);
+		}
+		mas.addAttributes(attr);
+	}
+	
+	public MutableAttributeSet getAttributes()
+	{
+		if(mas!=null) return mas;
+		if(attributes== null) attributes = new SimpleAttributeSet();
+		return attributes;
+	}
+	
+	public String toString()
+	{
+		return word;
+	}
+	
+	public Object clone() throws CloneNotSupportedException
+	{
+		Word w = (Word) super.clone();
+		if(attributes!=null)w.attributes = new SimpleAttributeSet(attributes);
+		if(mas!=null)w.mas = new SimpleAttributeSet(mas);
+		return w;
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/XHTMLIMHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/XHTMLIMHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/XHTMLIMHandler.java	(revision 3952)
@@ -0,0 +1,48 @@
+package nu.fw.jeti.plugins.xhtml;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.plugins.Word;
+import nu.fw.jeti.util.Log;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public class XHTMLIMHandler extends ExtensionHandler
+{
+	private Extension xhtml;
+	
+	public void startHandling(Attributes attr)
+	{
+		xhtml = null;
+	}
+	
+	public void addExtension(Extension extension)
+	{
+		xhtml = extension;
+	}
+	
+	public Extension build()
+	{
+		Extension e = xhtml;
+		xhtml=null;
+		return e;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/AttributeComponent.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/AttributeComponent.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/AttributeComponent.java	(revision 3952)
@@ -0,0 +1,67 @@
+/*
+ * SimplyHTML, a word processor based on Java, HTML and CSS
+ * Copyright (C) 2002 Ulrich Hilger
+ *
+ * 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.
+ */
+
+package nu.fw.jeti.plugins.xhtml.fontchooser;
+
+import javax.swing.text.AttributeSet;
+
+/**
+ * Defines a set of methods common to components bound to AttributeSets.
+ *
+ * @author Ulrich Hilger
+ * @author Light Development
+ * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
+ * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
+ * @author published under the terms and conditions of the
+ *      GNU General Public License,
+ *      for details see file gpl.txt in the distribution
+ *      package of this software
+ *
+ * @version stage 9, release 4, January 12, 2003
+ */
+
+public interface AttributeComponent {
+
+  /**
+   * set the value of this <code>AttributeComponent</code>
+   *
+   * @param a  the set of attributes possibly having an
+   *          attribute this component can display
+   *
+   * @return true, if the set of attributes had a matching attribute,
+   *            false if not
+   */
+  public boolean setValue(AttributeSet a);
+
+  /**
+   * get the value of this <code>AttributeComponent</code>
+   *
+   * @return the value selected from this component
+   */
+  public AttributeSet getValue();
+
+  public AttributeSet getValue(boolean includeUnchanged);
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/FontDialog.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/FontDialog.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/FontDialog.java	(revision 3952)
@@ -0,0 +1,83 @@
+/*
+ * SimplyHTML, a word processor based on Java, HTML and CSS
+ * Copyright (C) 2002 Ulrich Hilger
+ *
+ * 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.
+ */
+
+package nu.fw.jeti.plugins.xhtml.fontchooser;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Frame;
+
+import javax.swing.text.AttributeSet;
+
+/**
+ * Dialog to show and manipulate font attributes.
+ *
+ * @author Ulrich Hilger
+ * @author Light Development
+ * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
+ * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
+ * @author published under the terms and conditions of the
+ *      GNU General Public License,
+ *      for details see file gpl.txt in the distribution
+ *      package of this software
+ *
+ * @version stage 9, release 4, January 12, 2003
+ */
+
+public class FontDialog extends DialogShell {
+
+  /** the font panel to use in this dialog */
+  private FontPanel fontPanel;
+
+  /**
+   * constructor
+   *
+   * @param parent  the main frame having the ResourceBundle
+   * @param title  the title for this dialog
+   * @param a  the set of attributes to show and manipulate
+   */
+  public FontDialog(Frame parent, String title, AttributeSet a) {
+    super(parent, title);
+
+    // construct font panel
+    fontPanel = new FontPanel(a);
+
+    // add font panel to content pane of DialogShell
+    Container contentPane = super.getContentPane();
+    contentPane.add(fontPanel, BorderLayout.CENTER);
+
+    // cause optimal placement of all elements
+    pack();
+  }
+
+  /**
+   * get the set of attributes set in this dialog
+   *
+   * @return the attributes set in this dialog
+   */
+  public AttributeSet getAttributes() {
+    return fontPanel.getAttributes();
+  }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/FontPanel.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/FontPanel.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/FontPanel.java	(revision 3952)
@@ -0,0 +1,587 @@
+/*
+ * SimplyHTML, a word processor based on Java, HTML and CSS
+ * Copyright (C) 2002 Ulrich Hilger
+ *
+ * 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.
+ */
+
+package nu.fw.jeti.plugins.xhtml.fontchooser;
+
+import java.awt.*;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+import javax.swing.border.EtchedBorder;
+import javax.swing.border.TitledBorder;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.html.CSS;
+import javax.swing.text.html.StyleSheet;
+
+import nu.fw.jeti.util.I18N;
+
+/**
+ * A panel for showing and manipulating font information.
+ * <p>
+ * <code>FontPanel</code> shows and manipulates CSS attributes.
+ *  To set it to HTML attributes, methods setAttributes and getAttributes have to be overridden.
+ * </p>
+ * 
+ * @author Ulrich Hilger
+ * @author Light Development
+ * @author <a href="http://www.lightdev.com">http://www.lightdev.com </a>
+ * @author <a href="mailto:info@lightdev.com">info@lightdev.com </a>
+ * @author published under the terms and conditions of the GNU General Public License,
+ *  for details see file gpl.txt in the distribution package of this software
+ * @version stage 9, release 4, January 12, 2003
+ */
+
+public class FontPanel extends JPanel implements TitledPickList.TitledPickListListener, ColorPanel.ColorPanelListener
+{
+
+	/** a text field to show a sample of the selected font attributes */
+	JTextField sample = new JTextField();
+
+	/** table for automatic font component value read/write */
+	private Vector fontComponents = new Vector(0);
+
+	public FontPanel()
+	{
+		setLayout(new BorderLayout(5, 5));
+
+		/** create a label for previewing font selections */
+		sample.setText("");
+		sample.setEditable(false);
+		sample.setPreferredSize(new Dimension(200, 50));
+		sample.setHorizontalAlignment(SwingConstants.CENTER);
+		sample.setText(I18N.gettext("xhtml.The_quick_brown_fox_jumped_over_the_lazy_dogs"));
+		JPanel previewPanel = new JPanel(new BorderLayout());
+		previewPanel.add(sample, BorderLayout.CENTER);
+		previewPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), I18N.gettext("xhtml.Preview")));
+
+		/**
+		 * create a pick list for family filled with available font family names
+		 */
+		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+		FamilyPickList family = new FamilyPickList(ge.getAvailableFontFamilyNames(), I18N.gettext("xhtml.Font_Family"));
+		family.addTitledPickListListener(this);
+		fontComponents.add(family);
+
+		/** create a pick list for font size */
+		String[] fontSizes = new String[] { "8", "10", "12", "14", "18", "24"};
+		SizePickList size = new SizePickList(fontSizes, (I18N.gettext("xhtml.Size")));
+		size.addTitledPickListListener(this);
+		fontComponents.add(size);
+
+		/** wrap together family and size */
+		JPanel familySizePanel = new JPanel(new BorderLayout(5, 5));
+		familySizePanel.add(family, BorderLayout.CENTER);
+		familySizePanel.add(size, BorderLayout.EAST);
+
+		/** create a panel to put font parts family, size and stlye in */
+		JPanel fontPartsPanel = new JPanel(new BorderLayout(5, 5));
+		fontPartsPanel.add(familySizePanel, BorderLayout.CENTER);
+		String[] fontStyles = new String[] { I18N.gettext("xhtml.Plain"), I18N.gettext("xhtml.Bold"), I18N.gettext("xhtml.Italic"),
+				I18N.gettext("xhtml.BoldItalic")};
+		StylePickList style = new StylePickList(fontStyles, I18N.gettext("xhtml.Style"));
+		style.addTitledPickListListener(this);
+		fontPartsPanel.add(style, BorderLayout.EAST);
+		fontComponents.add(style);
+
+		/** create a panel for underline / line through */
+		EffectPanel linePanel = new EffectPanel();
+		fontComponents.add(linePanel);
+
+		/** create a panel for color choices */
+		JPanel colorPanel = new JPanel(new GridLayout(2, 1, 3, 3));
+		colorPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), I18N.gettext("xhtml.Color")));
+		ColorPanel fCol = new ColorPanel(I18N.gettext("xhtml.foreground"), Color.black);
+		fCol.addColorPanelListener(this);
+		fontComponents.add(fCol);
+		ColorPanel bCol = new ColorPanel(I18N.gettext("xhtml.background"), Color.white);
+		bCol.addColorPanelListener(this);
+		fontComponents.add(bCol);
+		colorPanel.add(fCol);
+		colorPanel.add(bCol);
+
+		sample.setForeground(Color.black);
+		sample.setBackground(Color.white);
+
+		/** create a panel to combine line and color choices */
+		JPanel eastPanel = new JPanel(new BorderLayout());
+		eastPanel.add(linePanel, BorderLayout.NORTH);
+		eastPanel.add(colorPanel, BorderLayout.SOUTH);
+
+		/** add all font controls to our font panel */
+		add(fontPartsPanel, BorderLayout.CENTER);
+		add(eastPanel, BorderLayout.EAST);
+		add(previewPanel, BorderLayout.SOUTH);
+		add(new JPanel(), BorderLayout.NORTH);
+		add(new JPanel(), BorderLayout.WEST);
+	}
+
+	/**
+	 * construct a FontPanel and display a set of attributes
+	 * 
+	 * @param frame the main frame having the ResourceBundle
+	 * @param a the set of attributes to display
+	 */
+	public FontPanel(AttributeSet a)
+	{
+		this();
+
+		/** set the new FontPanel to display our set of attributes */
+		setAttributes(a);
+	}
+
+	/**
+	 * handle ColorChangeEvents from one of our color panels
+	 * 
+	 * @param e the ColorPanelEvent to handle
+	 */
+	public void colorChanged(ColorPanel.ColorPanelEvent e)
+	{
+		ColorPanel source = (ColorPanel) e.getSource();
+		if (source.getAttributeKey() == CSS.Attribute.COLOR)
+		{
+			sample.setForeground(source.getColor());
+		} else if (source.getAttributeKey() == CSS.Attribute.BACKGROUND_COLOR)
+		{
+			sample.setBackground(source.getColor());
+		}
+	}
+
+	/**
+	 * set all components of this FontPanel to reflect a set of attributes.
+	 * 
+	 * @param a the set of attributes to show
+	 */
+	public void setAttributes(AttributeSet a)
+	{
+		Enumeration components = fontComponents.elements();
+		while (components.hasMoreElements())
+		{
+			((AttributeComponent) components.nextElement()).setValue(a);
+		}
+	}
+
+	/**
+	 * get the set of attributes resulting from the settings on this FontPanel.
+	 * 
+	 * @return the set of attributes set in this FontPanel
+	 */
+	public AttributeSet getAttributes()
+	{
+		SimpleAttributeSet attributes = new SimpleAttributeSet();
+		Enumeration components = fontComponents.elements();
+		while (components.hasMoreElements())
+		{
+			attributes.addAttributes(((AttributeComponent) components.nextElement()).getValue());
+		}
+		return attributes;
+	}
+
+	public AttributeSet getAttributes(boolean includeUnchanged)
+	{
+		if (includeUnchanged)
+		{
+			SimpleAttributeSet attributes = new SimpleAttributeSet();
+			Enumeration components = fontComponents.elements();
+			while (components.hasMoreElements())
+			{
+				attributes.addAttributes(((AttributeComponent) components.nextElement()).getValue(includeUnchanged));
+			}
+			return attributes;
+		} else
+		{
+			return getAttributes();
+		}
+	}
+
+	public void reset()
+	{
+		Object c;
+		for (int i = 0; i < fontComponents.size(); i++)
+		{
+			c = fontComponents.get(i);
+			if (c instanceof FamilyPickList)
+			{
+				((FamilyPickList) c).reset();
+			} else if (c instanceof SizePickList)
+			{
+				((SizePickList) c).reset();
+			} else if (c instanceof StylePickList)
+			{
+				((StylePickList) c).reset();
+			}
+		}
+	}
+
+	/**
+	 * if another value was picked from a list, update the sample
+	 */
+	public void valueChanged(TitledPickList.TitledPickListEvent e)
+	{
+		Object source = e.getSource();
+		Font saveFont = sample.getFont();
+		if (source instanceof FamilyPickList)
+		{
+			sample.setFont(new Font(((FamilyPickList) source).getFamily(), saveFont.getStyle(), saveFont.getSize()));
+		} else if (source instanceof SizePickList)
+		{
+			sample.setFont(new Font(saveFont.getFamily(), saveFont.getStyle(), Integer.parseInt((String) ((SizePickList) source).getSelection())));
+			/* adjustFontSize(Integer.parseInt((String) ((SizePickList) source).getSelection())))); */
+		} else if (source instanceof StylePickList)
+		{
+			sample.setFont(new Font(saveFont.getFamily(), ((StylePickList) source).getFontStyle(), saveFont.getSize()));
+		}
+	}
+
+	/**
+	 * extend <code>TitledPickList</code> with a way to set values special to font family values
+	 */
+	static class FamilyPickList extends TitledPickList implements AttributeComponent
+	{
+
+		private int setValCount = 0;
+		private Object originalValue;
+
+		/**
+		 * constructor
+		 * 
+		 * @param options the options to be selectable in this list
+		 * @param titleText the title for the pick list
+		 */
+		FamilyPickList(String[] options, String titleText)
+		{
+			super(options, titleText);
+		}
+
+		/**
+		 * set the value of this <code>TitledPickList</code>
+		 * 
+		 * @param a the set of attributes possibly having a font family attribute this pick list could display
+		 * @return true, if the set of attributes had a font family attribute, false if not
+		 */
+		public boolean setValue(AttributeSet a)
+		{
+			boolean success = false;
+			ignoreTextChanges = true;
+			Object newSelection;
+			//      if(a.isDefined(CSS.Attribute.FONT_FAMILY)) {
+			//        newSelection = a.getAttribute(CSS.Attribute.FONT_FAMILY);
+			// 	setSelection(a.getAttribute(CSS.Attribute.FONT_FAMILY));
+			//	success = true;
+			//      }
+			//a.isDefined(StyleConstants.FontFamily)
+			String family = StyleConstants.getFontFamily(a);
+			if (family != null)
+			{
+
+				newSelection = family;
+				setSelection(family);
+				success = true;
+			} else
+			{
+				newSelection = "SansSerif";
+				setSelection(newSelection);
+			}
+			ignoreTextChanges = false;
+			if (++setValCount < 2)
+			{
+				originalValue = newSelection;
+			}
+			return success;
+		}
+
+		public AttributeSet getValue()
+		{
+			SimpleAttributeSet set = new SimpleAttributeSet();
+			Object value = getSelection();
+			//System.out.println("FamilyPicker getValue originalValue=" + originalValue);
+			//System.out.println("FamilyPicker getValue value=" + value);
+			if (((originalValue == null) && (value != null))
+					|| ((originalValue != null) && (value != null) && (!originalValue.toString().equalsIgnoreCase(value.toString()))))
+			{
+				//Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_FAMILY, value.toString());
+				StyleConstants.setFontFamily(set, value.toString());
+			}
+			return set;
+		}
+
+		public AttributeSet getValue(boolean includeUnchanged)
+		{
+			if (includeUnchanged)
+			{
+				SimpleAttributeSet set = new SimpleAttributeSet();
+				Object value = getSelection();
+				//System.out.println("FamilyPicker getValue originalValue=" + originalValue);
+				//System.out.println("FamilyPicker getValue value=" + value);
+				//Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_FAMILY, value.toString());
+				StyleConstants.setFontFamily(set, value.toString());
+				return set;
+			} else
+			{
+				return getValue();
+			}
+		}
+
+		public String getFamily()
+		{
+			return (String) getSelection();
+		}
+
+		public void reset()
+		{
+			setValCount = 0;
+			originalValue = null;
+		}
+	}
+
+	/**
+	 * extend <code>TitledPickList</code> with a way to set values special to font size values
+	 */
+	static class SizePickList extends TitledPickList implements AttributeComponent
+	{
+
+		// private Object key;
+		private int setValCount = 0;
+		private String originalValue;
+
+		/**
+		 * constructor
+		 * 
+		 * @param options the options to be selectable in this list
+		 * @param titleText the title for the pick list
+		 */
+		SizePickList(String[] options, String titleText)
+		{
+			super(options, titleText);
+			//this.key = key;
+		}
+
+		/**
+		 * set the value of this <code>TitledPickList</code>
+		 * 
+		 * @param a the set of attributes possibly having a font size attribute this pick list could display
+		 * @return true, if the set of attributes had a font size attribute, false if not
+		 */
+		public boolean setValue(AttributeSet a)
+		{
+			ignoreTextChanges = true;
+			boolean success = false;
+			//Object attr = a.getAttribute(key);
+			String newSelection;
+			if (a != null)
+			{
+				//LengthValue lv = new LengthValue(a.getAttribute(key));
+				//int val = new Float(lv.getAttrValue(attr.toString(), LengthValue.pt)).intValue();
+				int val = StyleConstants.getFontSize(a); //(int) Util.getAttrValue(a.getAttribute(key));
+				if (val > 0)
+				{
+					success = true;
+					newSelection = new Integer(val).toString();
+					setSelection(newSelection);
+				} else
+				{
+					newSelection = "12";
+					setSelection(newSelection);
+				}
+			} else
+			{
+				newSelection = "12";
+				setSelection(newSelection);
+			}
+			ignoreTextChanges = false;
+			if (++setValCount < 2)
+			{
+				originalValue = newSelection;
+			}
+			return success;
+		}
+
+		public AttributeSet getValue()
+		{
+			SimpleAttributeSet set = new SimpleAttributeSet();
+			String value = (String) getSelection();
+			if (((originalValue == null) && (value != null)) || ((originalValue != null) && (!originalValue.equalsIgnoreCase(value))))
+			{
+				//       Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_SIZE,
+				//  set.addAttribute("size", (String) getSelection() /*+ "pt"*/);
+				StyleConstants.setFontSize(set, Integer.parseInt((String) getSelection()));
+			}
+			return set;
+		}
+
+		public AttributeSet getValue(boolean includeUnchanged)
+		{
+			if (includeUnchanged)
+			{
+				SimpleAttributeSet set = new SimpleAttributeSet();
+
+				//Util.styleSheet().addCSSAttribute(set, CSS.Attribute.FONT_SIZE,
+				//(String) getSelection() /*+ "pt"*/);
+				StyleConstants.setFontSize(set, Integer.parseInt((String) getSelection()));
+				return set;
+			} else
+			{
+				return getValue();
+			}
+		}
+
+		public void reset()
+		{
+			setValCount = 0;
+			originalValue = null;
+		}
+	}
+
+	private static StyleSheet styleSheet = new StyleSheet();
+	/**
+	 * extend <code>TitledPickList</code> with a way to set values special to font style values
+	 */
+	static class StylePickList extends TitledPickList implements AttributeComponent
+	{
+
+		private int setValCount = 0;
+
+		/**
+		 * constructor
+		 * 
+		 * @param options the options to be selectable in this list
+		 * @param titleText the title for the pick list
+		 */
+		StylePickList(String[] options, String titleText)
+		{
+			super(options, titleText);
+		}
+
+		/**
+		 * set the value of this <code>TitledPickList</code>
+		 * 
+		 * @param a the set of attributes possibly having a font style attribute this pick list could display
+		 * @return true, if the set of attributes had a font style attribute, false if not
+		 */
+		public boolean setValue(AttributeSet a)
+		{
+			ignoreTextChanges = true;
+			boolean success = false;
+			int styleNo = 0;
+			String value;
+			if (StyleConstants.isBold(a))
+			{
+				styleNo++;
+			}
+			if (StyleConstants.isItalic(a))
+			{
+				styleNo += 2;
+			}
+			setSelection(styleNo);
+			if (++setValCount < 2)
+			{
+
+			}
+			ignoreTextChanges = false;
+			return success;
+		}
+
+		public AttributeSet getValue()
+		{
+			SimpleAttributeSet set = new SimpleAttributeSet();
+			int styleNo = getIndex();
+			switch (styleNo)
+			{
+				case 1:
+					StyleConstants.setBold(set,true);
+					break;
+				case 2:
+					StyleConstants.setItalic(set,true);
+					break;
+				case 3:
+					StyleConstants.setBold(set,true);
+					StyleConstants.setItalic(set,true);
+					break;
+			}
+			return set;
+		}
+
+		public AttributeSet getValue(boolean includeUnchanged)
+		{
+//			if (includeUnchanged)
+//			{
+//				String value = "CSS_ATTRIBUTE_NORMAL";
+//				SimpleAttributeSet set = new SimpleAttributeSet();
+//				int styleNo = getIndex();
+//				switch (styleNo)
+//				{
+//					case 0:
+//						styleSheet.addCSSAttribute(set, CSS.Attribute.FONT_STYLE, value);
+//						break;
+//					case 1:
+//						styleSheet.addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, StyleConstants.Bold.toString());
+//						break;
+//					case 2:
+//						styleSheet.addCSSAttribute(set, CSS.Attribute.FONT_STYLE, StyleConstants.Italic.toString());
+//						break;
+//					case 3:
+//						styleSheet.addCSSAttribute(set, CSS.Attribute.FONT_WEIGHT, StyleConstants.Bold.toString());
+//						styleSheet.addCSSAttribute(set, CSS.Attribute.FONT_STYLE, StyleConstants.Italic.toString());
+//						break;
+//				}
+//				return set;
+//			} else
+			{
+				return getValue();
+			}
+		}
+
+		public int getFontStyle()
+		{
+			int fontStyle = 0;
+			switch (getIndex())
+			{
+				case 0:
+					fontStyle = Font.PLAIN;
+					break;
+				case 1:
+					fontStyle = Font.BOLD;
+					break;
+				case 2:
+					fontStyle = Font.ITALIC;
+					break;
+				case 3:
+					fontStyle = Font.BOLD | Font.ITALIC;
+					break;
+			}
+			return fontStyle;
+		}
+
+		public void reset()
+		{
+			setValCount = 0;
+
+		}
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/DialogShell.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/DialogShell.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/DialogShell.java	(revision 3952)
@@ -0,0 +1,257 @@
+/*
+ * SimplyHTML, a word processor based on Java, HTML and CSS
+ * Copyright (C) 2002 Ulrich Hilger
+ *
+ * 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.
+ */
+
+package nu.fw.jeti.plugins.xhtml.fontchooser;
+
+import java.awt.event.ActionListener;
+import java.awt.AWTEvent;
+import java.awt.event.WindowEvent;
+import java.awt.event.ActionEvent;
+import javax.swing.JDialog;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+
+import nu.fw.jeti.util.I18N;
+
+import java.awt.Frame;
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Container;
+import java.awt.Dialog;
+
+
+/**
+ * Base class for other dialogs of application SimplyHTML.
+ *
+ * @author Ulrich Hilger
+ * @author Light Development
+ * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
+ * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
+ * @author published under the terms and conditions of the
+ *      GNU General Public License,
+ *      for details see file gpl.txt in the distribution
+ *      package of this software
+ *
+ * @version stage 9, release 4, January 12, 2003
+ */
+
+public class DialogShell extends JDialog implements ActionListener {
+
+  /** panel containing dialog buttons */
+  protected JPanel buttonPanel;
+
+  /** button to confirm the operation */
+  protected JButton okButton;
+
+  /** button to cancel the operation */
+  protected JButton cancelButton;
+
+  /** button to display context sensitive help */
+  protected JButton helpButton;
+
+  /**
+   * the result of the operation, one of RESULT_CANCEL and RESULT_OK
+   */
+  private int result;
+
+  /** result value for a cancelled operation */
+  public static final int RESULT_CANCEL = 1;
+
+  /** result value for a confirmed operation */
+  public static final int RESULT_OK = 0;
+
+  /** id of associated help topic (if any) */
+  protected String helpTopicId = null;
+
+  //private HelpBroker myHelpBroker;
+
+  /**
+   * constructor
+   *
+   * @param parent  the parent dialog
+   * @param title  the title for this dialog
+   */
+  public DialogShell(Dialog parent, String title) {
+    super(parent, title);
+    buildDialog();
+  }
+
+  /**
+   * constructor
+   *
+   * @param parent  the parent frame
+   * @param title  the title for this dialog
+   */
+  public DialogShell(Frame parent, String title) {
+    super(parent, title);
+    buildDialog();
+  }
+
+  /**
+   * constructor
+   *
+   * @param parent  the parent frame
+   * @param title  the title for this dialog
+   * @param helpTopicId  the id of the help topic to display for this dialog
+   */
+  public DialogShell(Frame parent, String title, String helpTopicId) {
+    super(parent, title);
+    this.helpTopicId = helpTopicId;
+    buildDialog();
+  }
+
+  /**
+   * constructor
+   *
+   * @param parent  the parent dialog
+   * @param title  the title for this dialog
+   * @param helpTopicId  the id of the help topic to display for this dialog
+   */
+  public DialogShell(Dialog parent, String title, String helpTopicId) {
+    super(parent, title);
+    this.helpTopicId = helpTopicId;
+    buildDialog();
+  }
+
+  /**
+   * constructor
+   *
+   * @param parent  the parent dialog
+   * @param title  the title for this dialog
+   * @param helpTopicId  the id of the help topic to display for this dialog
+   * @param helpBroker  the helpBroker to use for context sensitive help
+   */
+//  public DialogShell(Dialog parent, String title, String helpTopicId, HelpBroker broker) {
+//    super(parent, title);
+//    this.helpTopicId = helpTopicId;
+//    this.myHelpBroker = broker;
+//    buildDialog();
+//  }
+
+  /**
+   * constructor
+   *
+   * @param parent  the parent dialog
+   * @param title  the title for this dialog
+   * @param helpTopicId  the id of the help topic to display for this dialog
+   * @param helpBroker  the helpBroker to use for context sensitive help
+   */
+//  public DialogShell(Frame parent, String title, String helpTopicId, HelpBroker broker) {
+//    super(parent, title);
+//    this.helpTopicId = helpTopicId;
+//    this.myHelpBroker = broker;
+//    buildDialog();
+//  }
+
+  /**
+   * create dialog components
+   */
+  private void buildDialog() {
+
+    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
+
+    // construct dialog buttons
+   // okButton = new JButton(FrmMain.dynRes.getResourceString(FrmMain.resources, "okBtnName"));
+   okButton = new JButton(I18N.gettext("OK"));
+    //cancelButton = new JButton(FrmMain.dynRes.getResourceString(FrmMain.resources, "cancelBtnName"));
+	cancelButton = new JButton(I18N.gettext("Cancel"));
+    cancelButton.addActionListener(this);
+    okButton.addActionListener(this);
+
+    // construct button panel
+    buttonPanel = new JPanel(new FlowLayout());
+    buttonPanel.add(okButton);
+    buttonPanel.add(cancelButton);
+
+    // construct help button
+ //   if(helpTopicId != null) {
+   //   helpButton = new JButton(FrmMain.dynRes.getResourceString(FrmMain.resources, "helpLabel"));
+     // CSH.setHelpIDString(helpButton, helpTopicId);
+//      if(myHelpBroker == null) {
+//        helpButton.addActionListener(
+//            new CSH.DisplayHelpFromSource(FrmMain.getHelpBroker()));
+//      }
+//      else {
+//        helpButton.addActionListener(
+//            new CSH.DisplayHelpFromSource(myHelpBroker));
+//      }
+  //    buttonPanel.add(helpButton);
+    //}
+
+    // add all to content pane of dialog
+    Container contentPane = getContentPane();
+    contentPane.setLayout(new BorderLayout(5,5));
+    contentPane.add(buttonPanel, BorderLayout.SOUTH);
+  }
+
+  /**
+   * dispose the dialog properly in case of window close events
+   */
+  protected void processWindowEvent(WindowEvent e) {
+    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
+      cancel();
+    }
+    super.processWindowEvent(e);
+  }
+
+  /**
+   * cancel the operation
+   */
+  protected void cancel() {
+    result = RESULT_CANCEL;
+    dispose();
+  }
+
+  /**
+   * confirm the operation
+   */
+  protected void confirm() {
+    result = RESULT_OK;
+    dispose();
+  }
+
+  /**
+   * get the result of the operation performed in this dialog
+   *
+   * @return the result, one of RESULT_OK and RESULT_CANCEL
+   */
+  public int getResult() {
+    return result;
+  }
+
+  /**
+   * implements the ActionListener interface to be notified of
+   * clicks onto the ok and cancel button.
+   */
+  public void actionPerformed(ActionEvent e) {
+    Object src = e.getSource();
+    if(src == cancelButton) {
+      cancel();
+    }
+    else if(src == okButton) {
+      confirm();
+    }
+  }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/TitledPickList.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/TitledPickList.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/TitledPickList.java	(revision 3952)
@@ -0,0 +1,270 @@
+/*
+ * SimplyHTML, a word processor based on Java, HTML and CSS
+ * Copyright (C) 2002 Ulrich Hilger
+ *
+ * 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.
+ */
+
+package nu.fw.jeti.plugins.xhtml.fontchooser;
+
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.CaretEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.KeyEvent;
+import javax.swing.*;
+import java.awt.BorderLayout;
+import java.util.Vector;
+import java.util.EventObject;
+import java.util.EventListener;
+import java.util.Enumeration;
+
+/**
+ * A pick list typically being used in font dialogs, consisting
+ * of a list title, a text field for the currently selected
+ * value and the actual pick list containing all possible values.
+ *
+ * As three different lists are needed in our font panel for
+ * family, style and size, its quite handy to have the code
+ * making up such a component in a separate class only once.
+ *
+ * As well in a separate class it is easier to implement the
+ * special 'behaviour', i.e. when list is clicked, the value
+ * of the text field is set accordingly and when a value is
+ * typed in the text field, the value in the list changes
+ * accordingly.
+ *
+ * @author Ulrich Hilger
+ * @author Light Development
+ * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
+ * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
+ * @author published under the terms and conditions of the
+ *      GNU General Public License,
+ *      for details see file gpl.txt in the distribution
+ *      package of this software
+ *
+ * @version stage 9, release 4, January 12, 2003
+ */
+class TitledPickList extends JPanel
+  implements ListSelectionListener, CaretListener, FocusListener, KeyListener
+{
+  /** the chosen list entry */
+  private JTextField choice;
+
+  boolean ignoreTextChanges = false;
+
+  /** the list having all possible entries */
+  private JList optionsList;
+
+  /**
+   * constructor
+   *
+   * @param options  the options to be selectable in this list
+   * @param titleText  the title for the pick list
+   */
+  public TitledPickList(String[] options, String titleText) {
+    super(new BorderLayout());
+
+    choice = new JTextField();
+    choice.addCaretListener(this);
+    choice.addFocusListener(this);
+    choice.addKeyListener(this);
+
+    optionsList = new JList(options);
+    optionsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+    optionsList.addListSelectionListener(this);
+
+    JScrollPane scrollableList = new JScrollPane(optionsList);
+
+    JPanel pickListPanel = new JPanel(new BorderLayout());
+    pickListPanel.add(choice, BorderLayout.NORTH);
+    pickListPanel.add(scrollableList, BorderLayout.CENTER);
+
+    JLabel title = new JLabel();
+    title.setText(titleText);
+
+    add(title, BorderLayout.NORTH);
+    add(pickListPanel, BorderLayout.CENTER);
+  }
+
+  /**
+   * if the caret was updated, i.e. the user typed into
+   * the text field, try to find a font family name starting
+   * with what was typed so far, set the list to that family
+   */
+  public void caretUpdate(CaretEvent ce) {
+    if(!ignoreTextChanges) {
+      if(choice.hasFocus()) {
+	ListModel model = optionsList.getModel();
+	String key = choice.getText().toLowerCase();
+	if(key != null) {
+	  int i = 0;
+	  int modelSize = model.getSize();
+	  String listEntry = (String) model.getElementAt(i);
+	  while(++i < modelSize && !listEntry.toLowerCase().startsWith(key)) {
+	    listEntry = (String) model.getElementAt(i);
+	  }
+	  if(i < modelSize) {
+	    optionsList.setSelectedValue(listEntry, true);
+	  }
+	}
+      }
+    }
+  }
+
+  /** for FocusListener implementation, but unused here */
+  public void focusGained(FocusEvent e) {
+  }
+
+  /**
+   * put the currently selected value in the list
+   * into the text field when user leaves field
+   */
+  public void focusLost(FocusEvent e) {
+    updateTextFromList();
+  }
+
+  /**
+   * put the currently selected value in the list
+   * into the text field when user pressed enter in field
+   */
+  public void keyPressed(KeyEvent e) {
+    if(e.getKeyCode() == KeyEvent.VK_ENTER) {
+      updateTextFromList();
+    }
+  }
+
+  /** for KeyListener implementation, but unused here */
+  public void keyReleased(KeyEvent e) {
+  }
+
+  /** for KeyListener implementation, but unused here */
+  public void keyTyped(KeyEvent e) {
+  }
+
+  /** put selected value into text field */
+  private void updateTextFromList() {
+    Object value = optionsList.getSelectedValue();
+    if(value != null) {
+      choice.setText(value.toString());
+    }
+  }
+
+  /**
+   * get the value selected from the pick list
+   *
+   * @return the selected value or null if nothing is selected
+   */
+  public Object getSelection() {
+    return optionsList.getSelectedValue();
+  }
+
+  /**
+   * set the value selected in the pick list
+   *
+   * @param value  the value to be selected in the list
+   */
+  public void setSelection(Object value) {
+    optionsList.setSelectedValue(value.toString(), true);
+    updateTextFromList();
+  }
+
+  /**
+   * set the selected index in the pick list
+   *
+   * @param index  the index of the value to be selected in the list
+   */
+  public void setSelection(int index) {
+    optionsList.setSelectedIndex(index);
+    updateTextFromList();
+  }
+
+  /**
+   * get the index of the value selected in the pick list
+   *
+   * @return the index of the selected value or -1 if none is selected
+   */
+  public int getIndex() {
+    return optionsList.getSelectedIndex();
+  }
+
+  /**
+   * if another value was picked from the list, set the
+   * textfield according to the choice and update the
+   * sample
+   */
+  public void valueChanged(ListSelectionEvent e) {
+    if(optionsList.hasFocus()) {
+      updateTextFromList();
+    }
+    fireValueChanged();
+  }
+
+  /* ------------- event handling start ------------ */
+
+  /** the listeners for TitledPickkListEvents */
+  private Vector listeners = new Vector(0);
+
+  /**
+   * add an event listener.
+   *
+   * @param  listener  the event listener to add
+   */
+  public void addTitledPickListListener(TitledPickListListener listener) {
+    listeners.addElement(listener);
+  }
+
+  /**
+   * remove an event listener.
+   *
+   * @param  listener  the event listener to remove
+   */
+  public void removeTitledPickListListener(TitledPickListListener listener) {
+    listeners.removeElement(listener);
+  }
+
+  /** fire a value changed event to all registered listeners */
+  void fireValueChanged() {
+    Enumeration listenerList = listeners.elements();
+    while(listenerList.hasMoreElements()) {
+      ((TitledPickListListener) listenerList.nextElement()).valueChanged(
+                                          new TitledPickListEvent(this));
+    }
+  }
+
+  /** the event object definition for ColorPanels */
+  static class TitledPickListEvent extends EventObject {
+    public TitledPickListEvent(Object source) {
+      super(source);
+    }
+  }
+
+  /** the event listener definition for ColorPanels */
+  interface TitledPickListListener extends EventListener {
+    public void valueChanged(TitledPickListEvent e);
+  }
+
+  /* ------------- event handling end ------------ */
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/EffectPanel.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/EffectPanel.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/EffectPanel.java	(revision 3952)
@@ -0,0 +1,158 @@
+/*
+ * SimplyHTML, a word processor based on Java, HTML and CSS
+ * Copyright (C) 2002 Ulrich Hilger
+ *
+ * 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.
+ */
+
+package nu.fw.jeti.plugins.xhtml.fontchooser;
+
+import java.awt.Font;
+import java.awt.GridLayout;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.UIManager;
+import javax.swing.border.EtchedBorder;
+import javax.swing.border.TitledBorder;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+
+/**
+ * a panel to display and change line attributes
+ *
+ * @author Ulrich Hilger
+ * @author Light Development
+ * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
+ * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
+ * @author published under the terms and conditions of the
+ *      GNU General Public License,
+ *      for details see file gpl.txt in the distribution
+ *      package of this software
+ *
+ * @version stage 9, release 4, January 12, 2003
+ */
+public class EffectPanel extends JPanel implements AttributeComponent {
+
+  /** a radio button for the underline attribute */
+  JRadioButton uLine;
+
+  /** a radio button for the strike through attribute */
+  JRadioButton strike;
+
+  /** a radio button if no line effect is set */
+  JRadioButton noLine;
+
+  private Object originalValue;
+
+  
+  String selection = "Util.CSS_ATTRIBUTE_NONE";
+
+  public EffectPanel() {
+    super(new GridLayout(3,1,3,3));
+
+    /** initialize the line effects button group */
+    noLine = new JRadioButton("noLine");
+    uLine = new JRadioButton("underLine");
+    strike = new JRadioButton("striketrough");
+    ButtonGroup effectGroup = new ButtonGroup();
+    effectGroup.add(noLine);
+    effectGroup.add(uLine);
+    effectGroup.add(strike);
+
+    //JPanel linePanel = new JPanel(new GridLayout(3,1,3,3));
+    setBorder(new TitledBorder(new EtchedBorder(
+            EtchedBorder.LOWERED),"effect"));
+    Font font = UIManager.getFont("TextField.font");
+    uLine.setFont(font);
+    strike.setFont(font);
+    noLine.setFont(font);
+    add(noLine);
+    add(uLine);
+    add(strike);
+  }
+
+  public AttributeSet getValue(boolean includeUnchanged) {
+    if(includeUnchanged) {
+      return getAttributes();
+    }
+    else {
+      return getValue();
+    }
+  }
+
+  private AttributeSet getAttributes() {
+    SimpleAttributeSet set = new SimpleAttributeSet();
+    if(uLine.isSelected()) {
+    	StyleConstants.setUnderline(set, true);
+    	selection="sdf";
+    }
+    else if(strike.isSelected()) {
+      StyleConstants.setStrikeThrough(set, true);
+      selection="sdf";
+    }
+    return set;
+  }
+
+  public AttributeSet getValue() {
+    if(((originalValue == null)) ||
+        ((originalValue != null) && (!originalValue.toString().equalsIgnoreCase(selection))))
+    {
+      return getAttributes();
+    }
+    else {
+      return getAttributes();
+    	//return new SimpleAttributeSet();
+    }
+  }
+
+  public boolean setValue(AttributeSet a) {
+//    boolean success = false;
+//    if(a.isDefined(CSS.Attribute.TEXT_DECORATION)) {
+//      String value = a.getAttribute(CSS.Attribute.TEXT_DECORATION).toString();
+//      if(value.equalsIgnoreCase(Util.CSS_ATTRIBUTE_UNDERLINE)) {
+//        uLine.setSelected(true);
+//        if(++setValCount < 2) {
+//          originalValue = Util.CSS_ATTRIBUTE_UNDERLINE;
+//        }
+//	success = true;
+//      }
+//      else if(value.equalsIgnoreCase(Util.CSS_ATTRIBUTE_LINE_THROUGH)) {
+//        strike.setSelected(true);
+//        if(++setValCount < 2) {
+//          originalValue = Util.CSS_ATTRIBUTE_LINE_THROUGH;
+//        }
+//	success = true;
+//      }
+//      else {
+//        noLine.setSelected(true);
+//      }
+//    }
+//    else {
+//      noLine.setSelected(true);
+//    }
+//    return success;
+//  }
+  	return true;
+  }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/ColorPanel.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/ColorPanel.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/fontchooser/ColorPanel.java	(revision 3952)
@@ -0,0 +1,360 @@
+/*
+ * SimplyHTML, a word processor based on Java, HTML and CSS
+ * Copyright (C) 2002 Ulrich Hilger
+ *
+ * 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.
+ */
+
+package nu.fw.jeti.plugins.xhtml.fontchooser;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.Vector;
+
+import javax.swing.*;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+
+import nu.fw.jeti.util.I18N;
+
+/**
+ * a panel to display and change a color setting
+ *
+ * @author Ulrich Hilger
+ * @author Light Development
+ * @author <a href="http://www.lightdev.com">http://www.lightdev.com</a>
+ * @author <a href="mailto:info@lightdev.com">info@lightdev.com</a>
+ * @author published under the terms and conditions of the
+ *      GNU General Public License,
+ *      for details see file gpl.txt in the distribution
+ *      package of this software
+ *
+ * @version stage 9, release 4, January 12, 2003
+ */
+public class ColorPanel extends JPanel implements ActionListener, AttributeComponent
+{
+
+	/** the component showing the chosen color */
+	JTextField colorDisplay = new JTextField();
+
+	/** default color */
+	private Color defaultColor;
+
+	/** the attribute key, this component returns values for */
+	private Object attributeKey;
+
+	/** value to compare for determining changes */
+	private Color originalColor;
+
+	/** indicates if setValue is called initially */
+	private int setValCount = 0;
+
+	/**
+	 * construct a color panel
+	 *
+	 * @param title  the title of the color panel
+	 * @param col  the color to be displayed first
+	 * @param titleFont  font for title
+	 * @param key  the attribute key this component shall return color values for
+	 */
+	public ColorPanel(String title, Color col)
+	{
+		super(new BorderLayout(5, 5));
+
+		this.defaultColor = col;
+		this.attributeKey = title;
+
+		/** adjust the color display */
+		colorDisplay.setBackground(col);
+		Dimension dim = new Dimension(20, 15);
+		colorDisplay.setMinimumSize(dim);
+		colorDisplay.setMaximumSize(dim);
+		colorDisplay.setPreferredSize(dim);
+		colorDisplay.setEditable(false);
+
+		/** a button to open a color chooser window */
+		JButton browseButton = new JButton();
+		browseButton.setText("...");
+		dim = new Dimension(20, 15);
+		browseButton.setMinimumSize(dim);
+		browseButton.setMaximumSize(dim);
+		browseButton.setPreferredSize(dim);
+		browseButton.addActionListener(this);
+
+		/** a helper panel for proper component placement */
+		JPanel eastPanel = new JPanel(new FlowLayout());
+		eastPanel.add(colorDisplay);
+		eastPanel.add(browseButton);
+
+		/** set the title */
+		if ((title != null) && (title.length() > 0))
+		{
+			JLabel titleLabel = new JLabel(title);
+			titleLabel.setFont(UIManager.getFont("TextField.font"));
+			add(titleLabel, BorderLayout.WEST);
+			add(eastPanel, BorderLayout.EAST);
+		}
+		else
+		{
+			add(eastPanel, BorderLayout.WEST);
+		}
+
+	}
+
+	/**
+	 * get the selected color
+	 *
+	 * @return the selected color
+	 */
+	public Color getColor()
+	{
+		return colorDisplay.getBackground();
+	}
+
+	/**
+	 * set the selected color
+	 *
+	 * @param color the selected color
+	 */
+	private void setColor(Color color)
+	{
+		//System.out.println("ColorPanel setColor attributeKey=" + attributeKey + ", color=" + color);
+		colorDisplay.setBackground(color);
+		if (++setValCount < 2)
+		{
+			originalColor = color;
+		}
+		fireColorChanged();
+	}
+
+	/**
+	 * open a color chooser when a 'Browse' button
+	 * is clicked and change the associated color
+	 * display accordingly, when another color
+	 * is selected from the color chooser
+	 */
+	public void actionPerformed(ActionEvent e)
+	{
+		Color color = JColorChooser.showDialog(this, I18N.gettext("xhtml.Select_Color"), colorDisplay.getBackground());
+		if (color != null)
+		{
+			setColor(color);
+		}
+	}
+
+	/**
+	 * set the value of this <code>AttributeComponent</code>
+	 *
+	 * @param a  the set of attributes possibly having an
+	 *          attribute this component can display
+	 *
+	 * @return true, if the set of attributes had a matching attribute,
+	 *            false if not
+	 */
+	public boolean setValue(AttributeSet a)
+	{
+		//System.out.println(a);
+		boolean success = false;
+		if (defaultColor == Color.black)
+		{
+			//if( a.isDefined(StyleConstants.Foreground)) {
+			Color c = StyleConstants.getForeground(a);
+			if (c != null)
+			{
+				//System.out.println("ColorPanel setValue attributeKey=" + attributeKey + ", a=" + a.getAttribute(attributeKey));
+				//String value = a.getAttribute(getAttributeKey()).toString();
+				//setValue(value);
+				setColor(c);
+				success = true;
+			}
+			else
+				setColor(defaultColor);
+		}
+		else
+		{
+			Color c = StyleConstants.getBackground(a);
+			if (c != null)
+			{
+				//if( a.isDefined(StyleConstants.Background)) {
+				//System.out.println("ColorPanel setValue attributeKey=" + attributeKey + ", a=" + a.getAttribute(attributeKey));
+				//String value = a.getAttribute(getAttributeKey()).toString();
+				//setValue(value);
+				setColor(c);
+				success = true;
+			}
+			else
+				setColor(defaultColor);
+		}
+
+		return success;
+	}
+
+	//  public void setValue(String value) {
+	//    //System.out.println("ColorPanel setValue value=" + value);
+	//    try {
+	//      setColor(new Color(Integer.parseInt(
+	//          value.toString().substring(1).toUpperCase(), 16)));
+	//    }
+	//    catch(Exception e) {
+	//      try {
+	//        //setColor(Util.styleSheet().getForeground(a));
+	//        //System.out.println("ColorPanel setValue value=" + value + "=" + Color.getColor(value));
+	//        setColor(Color.getColor(value));
+	//      }
+	//      catch(Exception e2) {
+	//        Util.errMsg(null, null, e2);
+	//      }
+	//    }
+	//  }
+
+	public String getAttr()
+	{
+		String color = "#" + Integer.toHexString(getColor().getRGB()).substring(2);
+		return color;
+	}
+
+	/**
+	 * get the value of this <code>AttributeComponent</code>
+	 *
+	 * @return the value selected from this component
+	 */
+	public AttributeSet getValue()
+	{
+		SimpleAttributeSet set = new SimpleAttributeSet();
+		Color value = getColor();
+		if (value != originalColor)
+		{
+			//      String color = "#" + Integer.toHexString(
+			//          value.getRGB()).substring(2);
+			//      try {
+			//        Util.styleSheet().addCSSAttribute(set,
+			//            (CSS.Attribute) getAttributeKey(), color);
+			//      }
+			//      catch(Exception e) {
+			//        set.addAttribute(getAttributeKey(), color);
+			//      }
+			//if (attributeKey.equals("foreground"))	StyleConstants.setForeground(set, value);
+			if(defaultColor==Color.BLACK) StyleConstants.setForeground(set, value);
+			else StyleConstants.setBackground(set, value);
+			//System.out.println("ColorPanel getValue color=" + color);
+		}
+		return set;
+	}
+
+	public AttributeSet getValue(boolean includeUnchanged)
+	{
+		if (includeUnchanged)
+		{
+			SimpleAttributeSet set = new SimpleAttributeSet();
+			Color value = getColor();
+			//      String color = "#" + Integer.toHexString(
+			//          value.getRGB()).substring(2);
+			//      try {
+			//        Util.styleSheet().addCSSAttribute(set,
+			//            (CSS.Attribute) getAttributeKey(), color);
+			//      }
+			//      catch(Exception e) {
+			//        set.addAttribute(getAttributeKey(), color);
+			//      }
+			//if (attributeKey.equals("foreground"))
+			if(defaultColor==Color.BLACK) StyleConstants.setForeground(set, value);
+			else StyleConstants.setBackground(set, value);
+
+			//System.out.println("ColorPanel getValue color=" + color);
+			return set;
+		}
+		else
+		{
+			return getValue();
+		}
+	}
+
+	/**
+	 * get the attribute key this object was created for
+	 *
+	 * @return the attribute key this ColorPanel returns values for
+	 */
+	public Object getAttributeKey()
+	{
+		return attributeKey;
+	}
+
+	/* -------------- event listener implementation start ----------- */
+
+	/** the listeners for ColorPanelEvents */
+	private Vector listeners = new Vector(0);
+
+	/**
+	 * add an event listener.
+	 *
+	 * @param  listener  the event listener to add
+	 */
+	public void addColorPanelListener(ColorPanelListener listener)
+	{
+		listeners.addElement(listener);
+	}
+
+	/**
+	 * remove an event listener.
+	 *
+	 * @param  listener  the event listener to remove
+	 */
+	public void removeColorPanelListener(ColorPanelListener listener)
+	{
+		listeners.removeElement(listener);
+	}
+
+	/** fire a color changed event to all registered listeners */
+	void fireColorChanged()
+	{
+		Enumeration listenerList = listeners.elements();
+		while (listenerList.hasMoreElements())
+		{
+			((ColorPanelListener) listenerList.nextElement()).colorChanged(new ColorPanelEvent(this));
+		}
+	}
+
+	/** the event object definition for ColorPanels */
+	static class ColorPanelEvent extends EventObject
+	{
+		public ColorPanelEvent(Object source)
+		{
+			super(source);
+		}
+	}
+
+	/** the event listener definition for ColorPanels */
+	interface ColorPanelListener extends EventListener
+	{
+		public void colorChanged(ColorPanelEvent e);
+	}
+
+	/* -------------- event listener implementation end ----------- */
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/XHTMLHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/XHTMLHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/XHTMLHandler.java	(revision 3952)
@@ -0,0 +1,295 @@
+package nu.fw.jeti.plugins.xhtml;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.plugins.Word;
+import nu.fw.jeti.util.Log;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public class XHTMLHandler extends ExtensionHandler
+{
+	private List wordList = new ArrayList();
+	private SimpleAttributeSet currentAttributes;
+	
+	private StringBuffer body;
+
+	public void startHandling(Attributes attr)
+	{
+		reset();
+		body = new StringBuffer(); //attr.getValue("from");
+	}
+
+	private void reset()
+	{
+		body = null;
+		wordList.clear();
+		currentAttributes = new SimpleAttributeSet();
+	}
+
+	public void startElement(String name, Attributes attr)
+	{
+		addToWordList(getUntrimmedText());
+		
+		//create xhtml string to print in log window
+		body.append(getText() + "<" + name + " ");
+		for (int i = 0; i < attr.getLength(); i++)
+		{
+			body.append(attr.getQName(i) + "=\"" + attr.getValue(i) + "\"");
+		}
+		body.append(">");
+		
+		SimpleAttributeSet sas = new SimpleAttributeSet();
+		sas.setResolveParent(currentAttributes);
+		if(name.equals("body"))
+		{
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+		}
+		else if(name.equals("blockquote"))
+		{//TODO indent does not work
+			StyleConstants.setLeftIndent(sas, 10f);
+			StyleConstants.setRightIndent(sas, 10f);
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+			wordList.add(new Word("\n",sas));
+		}
+		else if(name.equals("q"))
+		{//quote cite=url attribute not implemented???
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+			wordList.add(new Word("'",sas));
+		}
+		else if(name.equals("pre"))
+		{
+			StyleConstants.setFontFamily(sas,"courier");
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+		}
+		else if(name.equals("li") || name.equals("ol") || name.equals("ul"))
+		{//TODO implement lists
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+		}
+		else if(name.equals("h1"))
+		{//todo h1 =?
+			StyleConstants.setFontSize(sas,32);
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+		}
+		else if(name.equals("h2"))
+		{
+			StyleConstants.setFontSize(sas,24);
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+		}
+		else if(name.equals("h3"))
+		{
+			StyleConstants.setFontSize(sas,18);
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+		}
+		
+		
+		else if(name.equals("a"))
+		{//TODO link
+			StyleConstants.setFontFamily(sas,"courier");
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+		}
+		
+		
+		else if(name.equals("img"))
+		{//TODO img
+			StyleConstants.setFontFamily(sas,"courier");
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+		}
+	
+		else if(name.equals("span") || name.equals("div") || name.equals("p")  )
+		{
+			parseStyle(attr.getValue("style"),sas);
+			currentAttributes = sas;
+		}
+		else if(name.equals("em")|| name.equals("cite") )
+		{
+			StyleConstants.setItalic(sas,true);
+			currentAttributes = sas;
+		}
+		else if(name.equals("strong"))
+		{
+			StyleConstants.setBold(sas,true);
+			currentAttributes = sas;
+		}
+		else if(name.equals("code"))
+		{//render in monospace font (courier)
+			StyleConstants.setFontFamily(sas,"courier");
+			currentAttributes = sas;
+		}
+		else
+		{
+			StringBuffer temp = new StringBuffer();
+			temp.append("<" + name + " ");
+			for (int i = 0; i < attr.getLength(); i++)
+			{
+				temp.append(attr.getQName(i) + "=\"" + attr.getValue(i) + "\"");
+			}
+			temp.append(">");
+			Log.notParsedXML(temp.toString());
+		}
+		
+		clearCurrentChars();
+	}
+	
+	private void parseStyle(String styleAttributes,SimpleAttributeSet sas)
+	{
+		if(styleAttributes==null)return;
+		String[] styles = styleAttributes.split(";");
+		for(int i=0;i<styles.length;i++)
+		{
+			String[] temp = styles[i].split(":");
+			if(temp.length!=2)continue;
+			String style = temp[0].trim();
+			String value = temp[1].trim();
+			if(style.equals("color"))
+			{
+				if(value.length()!=7)
+				{
+					value+="000000";
+					value =value.substring(0, 7);
+				}
+				Color color;
+				try {
+					color = Color.decode(value);
+				}catch (NumberFormatException e)
+				{
+					color = Color.BLACK;
+				}
+				StyleConstants.setForeground(sas,color);
+			}
+			else if(style.equals("background-color"))
+			{//not oficialy supported
+				Color color;
+				try {
+					color = Color.decode(value);
+				}catch (NumberFormatException e)
+				{
+					color = Color.WHITE;
+				}
+				StyleConstants.setBackground(sas,color);
+			}
+			else if(style.equals("font-family"))
+			{
+				StyleConstants.setFontFamily(sas,value);
+			}
+			else if(style.equals("font-size"))
+			{
+				int size;
+				try 
+				{
+					size = 	Integer.parseInt(value.substring(0,value.length()-2));
+				}
+				catch (NumberFormatException e)
+				{
+					size = 12;
+				}
+				StyleConstants.setFontSize(sas,size);
+			}
+			else if(style.equals("text-decoration"))
+			{//TODO overline || blink 
+				if(value.equals("underline"))StyleConstants.setUnderline(sas, true);
+				else if(value.equals("line-through"))StyleConstants.setStrikeThrough(sas, true);
+				
+			}
+			else if(style.equals("text-align"))
+			{
+				if(value.equals("left"))StyleConstants.setAlignment(sas,StyleConstants.ALIGN_LEFT);
+				else if(value.equals("rigth"))StyleConstants.setAlignment(sas,StyleConstants.ALIGN_RIGHT);
+				else if(value.equals("center"))StyleConstants.setAlignment(sas,StyleConstants.ALIGN_CENTER);
+				else if(value.equals("justify"))StyleConstants.setAlignment(sas,StyleConstants.ALIGN_JUSTIFIED);
+			}
+		}
+	}
+	
+
+	public void endElement(String name)
+	{
+		addToWordList(getUntrimmedText());
+		body.append(getText() + "</" + name + ">");
+		
+		if(name.equals("a") || name.equals("body") || name.equals("cite") 
+				|| name.equals("code") || name.equals("div") || name.equals("em") || name.equals("h1") 
+				|| name.equals("h2")|| name.equals("h3") || name.equals("img") || name.equals("li") 
+				|| name.equals("ol")|| name.equals("p")|| name.equals("pre")|| name.equals("ul")
+				||  name.equals("strong") || name.equals("span"))
+		{
+			currentAttributes =(SimpleAttributeSet)currentAttributes.getResolveParent();
+		}
+		else if(name.equals("br"))
+		{
+			wordList.add(new Word("\n"));
+		}
+		else if(name.equals("q"))
+		{//quote cite=url attribute not implemented???
+			wordList.add(new Word("'",currentAttributes));
+			currentAttributes =(SimpleAttributeSet)currentAttributes.getResolveParent();
+		}
+		else if(name.equals("blockquote"))
+		{
+			wordList.add(new Word("\n",null));
+			currentAttributes =(SimpleAttributeSet)currentAttributes.getResolveParent();
+		}
+		clearCurrentChars();
+	}
+	
+	private void addToWordList(StringBuffer text)
+	{
+		if(text.length()<1)return;
+		StringBuffer temp = new StringBuffer();
+		for(int i = 0;i<text.length();i++)
+		{//split text up in words
+			char c = text.charAt(i);
+			switch (c)
+			{
+				case ' ':	addWordFromTemp(temp); wordList.add(new Word(" "));  temp = new StringBuffer(); break;
+				case '\n':	addWordFromTemp(temp); wordList.add(new Word("\n")); temp = new StringBuffer(); break;
+				case '\t':	addWordFromTemp(temp); wordList.add(new Word("\t")); temp = new StringBuffer();break;
+				default: temp.append(c);
+			}
+		}
+		addWordFromTemp(temp);
+	}
+	
+	private void addWordFromTemp(StringBuffer temp)
+	{
+		if(temp.length()>0)wordList.add(new Word(temp,currentAttributes));
+	}
+
+	public Extension build()
+	{
+		addToWordList(getUntrimmedText());
+		body.append(getText());
+		clearCurrentChars();
+		Extension e = new XHTML(body.toString(),new ArrayList(wordList));
+		reset();
+		return e;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/PrefPanel.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/PrefPanel.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/PrefPanel.java	(revision 3952)
@@ -0,0 +1,107 @@
+package nu.fw.jeti.plugins.xhtml;
+
+import java.awt.Color;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.Box;
+import javax.swing.JLabel;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.html.StyleSheet;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.plugins.PreferencesPanel;
+import nu.fw.jeti.plugins.xhtml.fontchooser.FontPanel;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+
+/**
+ * <p>Title: J2M</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2002</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class PrefPanel extends PreferencesPanel
+{
+	private FontPanel fontPanel = new FontPanel();
+	private StyleSheet styleSheet = new StyleSheet(); 
+	
+	public PrefPanel(Backend backend)
+	{
+        setLayout(new GridBagLayout());
+        GridBagConstraints c = new GridBagConstraints();
+        c.anchor = GridBagConstraints.LINE_START;
+        c.insets = new Insets(3, 5, 0, 3);
+        c.gridwidth = GridBagConstraints.REMAINDER;
+
+		//this.backend = backend;
+		MutableAttributeSet set = new SimpleAttributeSet();
+		int i = Preferences.getInteger("xhtml", "foreground", 0);
+		if (i != 0)
+		{
+			StyleConstants.setForeground(set, new Color(i));
+		}
+		i = Preferences.getInteger("xhtml", "background", 0);
+		if (i != 0)
+		{		
+				StyleConstants.setBackground(set, new Color(i));
+		}
+		i = Preferences.getInteger("xhtml", "font-size", 0);
+		if (i != 0)
+		{
+			StyleConstants.setFontSize(set, i);
+		}
+		StyleConstants.setBold(set,Preferences.getBoolean("xhtml","bold",false));
+	    StyleConstants.setItalic(set,Preferences.getBoolean("xhtml","italic",false));
+		StyleConstants.setFontFamily(set, Preferences.getString("xhtml", "font-family", "Arial"));
+		fontPanel.setAttributes(set);
+
+ 		add(new JLabel(I18N.gettext("xhtml.The_font_used_when_sending_messages")), c);
+		add(fontPanel, c);
+
+        c.weighty = 1.0;
+        c.weightx = 1.0;
+        add(Box.createVerticalGlue(), c);
+	}
+
+	public void savePreferences()
+	{
+		AttributeSet set = fontPanel.getAttributes();
+		Color c;
+		if (set.isDefined(StyleConstants.Foreground))
+		{
+			c = (Color) set.getAttribute(StyleConstants.Foreground);
+			Preferences.putInteger("xhtml", "foreground", c.getRGB());
+		}
+		if (set.isDefined(StyleConstants.Background))
+		{
+			c = (Color) set.getAttribute(StyleConstants.Background);
+			Preferences.putInteger("xhtml", "background", c.getRGB());
+		}
+		if (set.isDefined(StyleConstants.FontFamily))
+		{
+			Preferences.putString("xhtml", "font-family", StyleConstants.getFontFamily(set));
+		}
+		if (set.isDefined(StyleConstants.FontSize))
+		{
+			Preferences.putInteger("xhtml", "font-size", StyleConstants.getFontSize(set));
+		}
+		Preferences.putBoolean("xhtml","bold",StyleConstants.isBold(set));
+		Preferences.putBoolean("xhtml","italic",StyleConstants.isItalic(set));
+	}
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/XHTML.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/XHTML.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/XHTML.java	(revision 3952)
@@ -0,0 +1,301 @@
+package nu.fw.jeti.plugins.xhtml;
+
+import java.awt.Color;
+import java.util.List;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.StyleConstants;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XExtension;
+import nu.fw.jeti.plugins.Word;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public class XHTML extends Extension implements XExtension
+{
+	private String body;
+	private List wordList;
+	
+	public XHTML(){}
+
+	public XHTML(List wordList)
+	{
+		this.wordList = wordList;
+		//this.body = "<body>" + body + "</body>";
+	}
+	
+	public XHTML(String body,List text)
+	{
+		this.body = body;
+		this.wordList = text;
+	}
+	
+	public List getWordList()
+	{
+		return wordList;
+	}
+		
+	public String getBody(){return body;}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		if(body==null) generateBody();
+		xml.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
+		xml.append(body);
+		xml.append("</html>");
+	}
+	
+	private void generateBody()
+	{
+		StringBuffer temp = new StringBuffer();
+		temp.append("<body>");
+		parseElement(temp,0,wordList.size(),false);
+		temp.append("</body>");
+		body=temp.toString();
+	}
+	
+	
+	
+	
+	/************************Methods to convert wordlist to xml*************************/
+	
+	private void parseElement(StringBuffer xhtml,int start, int end,boolean openStyle)
+	{//msn transport needs lettertype + color in 1 style to work
+		if(start>=end)return;
+		AttributeSet set = ((Word)wordList.get(start)).getAttributes();
+		int newEnd =0;
+		if(set==null);//remove empty set
+		else if (set.isDefined(StyleConstants.FontFamily))
+		{	
+			String insert ="font-family: " + StyleConstants.getFontFamily(set);
+			newEnd = addStyle(xhtml, start, end, openStyle, set,insert,StyleConstants.FontFamily);
+//			xhtml.append("<span style=\"");
+//		xhtml.append();
+//		Object value = set.getAttribute(StyleConstants.FontFamily);
+//		newEnd = getStyleEnd(StyleConstants.FontFamily,value,start,end);
+//		parseElement(xhtml, start, newEnd,true);
+//		xhtml.append("</span>");
+			
+			
+			openStyle=false;
+		}
+		else if (set.isDefined(StyleConstants.FontSize))
+		{	
+			String insert =" font-size: " + StyleConstants.getFontSize(set) + "pt";
+			newEnd = addStyle(xhtml, start, end, openStyle, set,insert,StyleConstants.FontSize);
+			openStyle=false;
+		}
+		else if (set.isDefined(StyleConstants.Foreground))
+		{
+			String hex = colorToHexString((Color)set.getAttribute(StyleConstants.Foreground));
+			String insert =" color: #" + hex;
+			newEnd = addStyle(xhtml, start, end, openStyle, set,insert,StyleConstants.Foreground);
+			openStyle=false;
+			
+//			Object value = set.getAttribute(StyleConstants.Foreground);
+//			if(!set.isDefined(StyleConstants.Foreground+value.toString()))
+//			{	
+//				newEnd = getStyleEnd(StyleConstants.Foreground,value,start,end);
+//				if(openStyle)
+//				{
+// 					if(newEnd<end)xhtml.append("\"><span style=\"");
+//					else xhtml.append(';');
+//				}
+//				else if(!openStyle) xhtml.append("<span style=\"");
+//				String hex = colorToHexString((Color)value);
+//				xhtml.append(" color: #" + hex);
+//				if(newEnd != 0)
+//				{
+//					parseElement(xhtml, start, newEnd,true);
+//				}
+//				if(!openStyle)xhtml.append("</span>");
+//				openStyle=false;
+//			}
+		}
+		else if (set.isDefined(StyleConstants.Background))
+		{
+			String hex = colorToHexString((Color)set.getAttribute(StyleConstants.Background));
+			String insert =" background-color: #" + hex;
+			newEnd = addStyle(xhtml, start, end, openStyle, set,insert,StyleConstants.Background);
+			openStyle=false;
+						
+//			Object value = set.getAttribute(StyleConstants.Background);
+//			if(!set.isDefined(StyleConstants.Background+value.toString()))
+//			{	
+//				xhtml.append("<span style=\"");
+//				String hex = colorToHexString((Color)value);
+//				xhtml.append("background: #" + hex);
+//				newEnd = getStyleEnd(StyleConstants.Background,value,start,end);
+//				if(newEnd != 0)
+//				{
+//					parseElement(xhtml, start, newEnd,true);
+//				}
+//				xhtml.append("</span>");
+//			}
+		}
+		else if (set.isDefined(StyleConstants.StrikeThrough))
+		{	
+			String insert =" text-decoration: line-through";
+			newEnd = addStyle(xhtml, start, end, openStyle, set,insert,StyleConstants.StrikeThrough);
+			openStyle=false;
+			
+//			Object value = set.getAttribute(StyleConstants.StrikeThrough);
+//			if(!set.isDefined(StyleConstants.StrikeThrough+value.toString()))
+//			{	
+//				xhtml.append("<span style=\"");
+//				xhtml.append("text-decoration: line-through");
+//				newEnd = getStyleEnd(StyleConstants.StrikeThrough,value,start,end);
+//				if(newEnd != 0)
+//				{
+//					parseElement(xhtml, start, newEnd,true);
+//				}
+//				xhtml.append("</span>");
+//			}
+		}
+		else if (set.containsAttribute(StyleConstants.Underline,Boolean.TRUE))
+		{
+			String insert =" text-decoration: underline";
+			newEnd = addStyle(xhtml, start, end, openStyle, set,insert,StyleConstants.Underline);
+			openStyle=false;
+			
+//			Object value = set.getAttribute(StyleConstants.Underline);
+//			xhtml.append("<span style=\"");
+//			xhtml.append("text-decoration: underline");
+//			newEnd = getStyleEnd(StyleConstants.Underline,value,start,end);
+//			parseElement(xhtml, start, newEnd,true);
+//			xhtml.append("</span>");
+			
+		}
+		else if (StyleConstants.isBold(set))
+		{
+			if(openStyle)xhtml.append("; margin:0 \">");
+			openStyle=false;
+			xhtml.append("<strong>");
+			Object value = set.getAttribute(StyleConstants.Bold);
+			newEnd = getStyleEnd(StyleConstants.Bold,value,start,end);
+			parseElement(xhtml, start, newEnd,false);
+			xhtml.append("</strong>");
+		}
+		else if (StyleConstants.isItalic(set))
+		{
+			if(openStyle)xhtml.append("; margin:0 \">");
+			openStyle=false;
+			xhtml.append("<em>");
+			Object value = set.getAttribute(StyleConstants.Italic);
+			newEnd = getStyleEnd(StyleConstants.Italic,value,start,end);
+			parseElement(xhtml, start, newEnd,false);
+			xhtml.append("</em>");
+
+		}
+		if(openStyle)xhtml.append("; margin:0 \">");//margin because msn transport needs ; after all styles
+		//no more styles so print this one
+		if(newEnd==0)
+		{
+			StringBuffer b = new StringBuffer();
+			escapeString(b, wordList.get(start).toString());
+			xhtml.append(b);
+			if(start+1<end)parseElement(xhtml, start+1, end,false);
+		}//parse words after these words
+		else if(newEnd<end)parseElement(xhtml, newEnd, end,false);
+			
+	}
+
+	private int addStyle(StringBuffer xhtml, int start, int end, boolean openStyle, AttributeSet set,String insert,Object styleConstant)
+	{
+		int newEnd;
+		Object value = set.getAttribute(styleConstant);
+		newEnd = getStyleEnd(styleConstant,value,start,end);
+		if(openStyle)
+		{
+			if(newEnd<end)xhtml.append("\"><span style=\"");
+			else xhtml.append(';');
+		}
+		else xhtml.append("<span style=\"");
+		xhtml.append(insert);
+		parseElement( xhtml, start, newEnd,true);
+		if(!openStyle)xhtml.append("</span>");
+		return newEnd;
+	}
+
+	private int getStyleEnd(Object styleConstant,Object value, int start, int end)
+	{
+		for(int i=start;i<end;i++)
+		{
+			MutableAttributeSet set =((Word)wordList.get(i)).getAttributes();
+			if(set==null)return i;
+			if(set.isDefined(styleConstant))
+			{	
+				if(value.equals(set.getAttribute(styleConstant)))
+				{
+					set.removeAttribute(styleConstant);
+					//set.addAttribute(styleConstant+value.toString(), value);
+				}
+			}
+			else return i;
+		}
+		return end;
+	}
+	
+	private static final char[] HEX_DIG = {
+						'0', '1', '2', '3', '4', '5', '6', '7',
+						'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+						};
+	
+	public String colorToHexString(Color c) 
+	{
+		int rgb = c.getRGB();
+		char[] cdata = new char[6];
+		for (int i = 0; i < 6; i++) {
+			cdata[5-i] = HEX_DIG[rgb & 0xF];
+			rgb = rgb >> 4;
+		}
+		return new String(cdata);
+	}
+	
+//	  /**
+//   * resolve sets of attributes that are recursively stored in each other
+//   *
+//   * @param style  the set of attributes containing other sets of attributes
+//   */
+//  public static AttributeSet resolveAttributes(AttributeSet style) {
+//    SimpleAttributeSet set = new SimpleAttributeSet();
+//    if(style != null) {
+//      Enumeration names = style.getAttributeNames();
+//      Object value;
+//      Object key;
+//      while(names.hasMoreElements()) {
+//        key = names.nextElement();
+//        //System.out.println("Util resolveAttributes key=" + key);
+//        value = style.getAttribute(key);
+//        //System.out.println("Util resolveAttributes value=" + value);
+//        if( (!key.equals(StyleConstants.NameAttribute)) &&
+//            (!key.equals(StyleConstants.ResolveAttribute)) &&
+//            (!key.equals(AttributeSet.ResolveAttribute)) &&
+//            (!key.equals(AttributeSet.NameAttribute)))
+//        {
+//          set.addAttribute(key, value);
+//        }
+//        else {
+//          if(key.equals(StyleConstants.ResolveAttribute) ||
+//             key.equals(AttributeSet.ResolveAttribute)) {
+//            //System.out.println("Util resolveAttributes resolving key=" + key);
+//            set.addAttributes(resolveAttributes((AttributeSet) value));
+//          }
+//        }
+//      }
+//    }
+//    return set;
+//  }
+	
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/.cvsignore
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/.cvsignore	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/.cvsignore	(revision 3952)
@@ -0,0 +1,1 @@
+*.lst
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/Plugin.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/Plugin.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/xhtml/Plugin.java	(revision 3952)
@@ -0,0 +1,404 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2002 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at jeti@jabber.org
+ */
+
+
+package nu.fw.jeti.plugins.xhtml;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.Action;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JColorChooser;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextPane;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Element;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
+import javax.swing.text.StyledEditorKit;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XExtension;
+import nu.fw.jeti.plugins.FormattedMessage;
+import nu.fw.jeti.plugins.Plugins;
+import nu.fw.jeti.plugins.Word;
+import nu.fw.jeti.plugins.xhtml.fontchooser.DialogShell;
+import nu.fw.jeti.plugins.xhtml.fontchooser.FontDialog;
+import nu.fw.jeti.ui.ChatSplitPane;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+import nu.fw.jeti.util.ServerExpresso;
+import nu.fw.jeti.plugins.filetransfer.buttonFileTransfer;
+import nu.fw.jeti.plugins.callVoip.buttonCallVoip;;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public class Plugin implements Plugins, FormattedMessage
+{
+	private JButton btnFont;
+	private JButton btnColor;
+	private JButton btnBold;
+	private JButton btnItalic;
+	private JButton btnUnderline;
+	private JButton btnFileTransfer;
+	private JButton btnCallVoip;
+	private JTextPane txtInvoer;
+	private ChatSplitPane chatPane;
+	private Object jidUser;
+	private Object jidName;
+	private boolean showXHTML = true;
+	public final static String VERSION = "0.3";
+	public final static String DESCRIPTION = "xhtml.Formats_messages";
+	public final static String MIN_JETI_VERSION = "0.5";
+	public final static String NAME = "xhtml";
+	public final static String ABOUT = "by E.S. de boer, uses Fontchooser from SimplyHTML by Ulrich Hilger";
+	
+	public static void init(Backend backend)
+	{
+		backend.addExtensionHandler("http://jabber.org/protocol/xhtml-im",new XHTMLIMHandler());
+		backend.addExtensionHandler("http://www.w3.org/1999/xhtml",new XHTMLHandler());
+		backend.addCapability("xhtml","http://jabber.org/protocol/xhtml-im");
+	}
+
+	public void initXHTML(ChatSplitPane chatPane, final JTextPane txtInvoer, JPanel pnlControl, Object JIDUser, Object JIDName)
+	{
+		this.txtInvoer	= txtInvoer;
+		this.chatPane	= chatPane;
+		this.jidUser	= JIDUser;
+		this.jidName	= JIDName;
+		
+		MutableAttributeSet set = new SimpleAttributeSet();
+		int i = Preferences.getInteger("xhtml", "foreground", 0);
+		if (i != 0)
+		{
+			StyleConstants.setForeground(set, new Color(i));
+		}
+		i = Preferences.getInteger("xhtml", "background", 0);
+		if (i != 0)
+		{
+			StyleConstants.setBackground(set, new Color(i));
+		}
+		i = Preferences.getInteger("xhtml", "font-size", 0);
+		if (i != 0)
+		{
+			StyleConstants.setFontSize(set, i);
+		}
+		StyleConstants.setFontFamily(set, Preferences.getString("xhtml", "font-family", "Arial"));
+		StyleConstants.setBold(set,Preferences.getBoolean("xhtml","bold",false));
+	    StyleConstants.setItalic(set,Preferences.getBoolean("xhtml","italic",false));
+	    
+		txtInvoer.setParagraphAttributes(set, false);
+
+		// BUTTON-font
+		btnFont = new JButton(new ImageIcon(getClass().getResource("font.gif")));
+		btnFont.setToolTipText(I18N.gettext("xhtml.Font"));
+		btnFont.setMargin(new Insets(0, 0, 0, 0));
+		btnFont.setPreferredSize(new Dimension(23, 23));
+		btnFont.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+
+				AttributeSet attr = txtInvoer.getCharacterAttributes(); //new SimpleAttributeSet();
+				Frame frame = JOptionPane.getFrameForComponent(txtInvoer.getTopLevelAncestor());
+				FontDialog d = new FontDialog(frame , I18N.gettext("xhtml.Choose_Font"), attr);
+				d.setModal(true);
+				d.setVisible(true);
+
+				if (d.getResult() == DialogShell.RESULT_OK)
+				{
+					attr = d.getAttributes();
+					int start = txtInvoer.getSelectionStart();
+					int end = txtInvoer.getSelectionEnd();
+					if (end != start)
+					{
+						StyledDocument doc = (StyledDocument) txtInvoer.getDocument();
+						doc.setCharacterAttributes(start, end - start, attr, false);
+					}
+					else txtInvoer.setParagraphAttributes(attr, false);
+					txtInvoer.requestFocus();
+				}
+			}
+		});
+		pnlControl.add(btnFont);
+
+		// BUTTON-color
+		btnColor = new JButton(new ImageIcon(getClass().getResource("color.gif")));
+		btnColor.setToolTipText(I18N.gettext("xhtml.Color"));
+		btnColor.setMargin(new Insets(0, 0, 0, 0));
+		btnColor.setPreferredSize(new Dimension(23, 23));
+		btnColor.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				Color color = JColorChooser.showDialog(txtInvoer.getTopLevelAncestor(), I18N.gettext("xhtml.Color"), null);
+				if (color != null)
+				{
+					int start = txtInvoer.getSelectionStart();
+					int end = txtInvoer.getSelectionEnd();
+					MutableAttributeSet set = new SimpleAttributeSet();
+					set.addAttribute(StyleConstants.Foreground, color);
+					if (end != start)
+					{
+						StyledDocument doc = (StyledDocument) txtInvoer.getDocument();
+						doc.setCharacterAttributes(start, end - start, set, false);
+					}
+					else txtInvoer.setParagraphAttributes(set, false);
+					txtInvoer.requestFocus();
+				}
+			}
+		});
+		pnlControl.add(btnColor);
+
+		ActionListener returnFocus = new ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+                txtInvoer.requestFocus();
+            }
+        };
+
+        // Click button filetransfer;	
+        ActionListener Ftransfer = new ActionListener()
+        {
+        	
+        	public void actionPerformed(ActionEvent e)
+        	{
+        		buttonFileTransfer sFile = new buttonFileTransfer();
+        		sFile.sendFile(jidUser);
+        	}
+        };
+        
+        // Click button CallVoip;
+        ActionListener callVoip = new ActionListener()
+        {
+        	public void actionPerformed(ActionEvent e)
+        	{
+        		String _return = buttonCallVoip.connExpresso(jidUser.toString(), nu.fw.jeti.applet.Jeti.USERID);        		
+        		javax.swing.JOptionPane.showMessageDialog(null, I18N.gettext("callVoip.Calling") + " : " + jidName + "\n\n" +
+        				I18N.gettext("callVoip.Status") + " : " + _return );
+        	}
+        };
+        
+		// BUTTON-bold
+		Action boldAction = new StyledEditorKit.BoldAction();
+		boldAction.putValue(Action.NAME, null);
+		boldAction.putValue(Action.SHORT_DESCRIPTION, I18N.gettext("xhtml.Bold"));
+		boldAction.putValue(Action.SMALL_ICON, new ImageIcon(getClass().getResource("Bold16.gif")));
+		btnBold = new JButton(boldAction);
+		btnBold.setMargin(new Insets(0, 0, 0, 0));
+		btnBold.setPreferredSize(new Dimension(23, 23));
+        btnBold.addActionListener(returnFocus);
+		pnlControl.add(btnBold);
+
+		// BUTTON-italic
+		Action italicAction = new StyledEditorKit.ItalicAction();
+		italicAction.putValue(Action.NAME, null);
+		italicAction.putValue(Action.SHORT_DESCRIPTION, I18N.gettext("xhtml.Italic"));
+		italicAction.putValue(Action.SMALL_ICON,new ImageIcon(getClass().getResource("Italic16.gif")));
+		btnItalic = new JButton(italicAction);
+		btnItalic.setMargin(new Insets(0, 0, 0, 0));
+		btnItalic.setPreferredSize(new Dimension(23, 23));
+        btnItalic.addActionListener(returnFocus);
+		pnlControl.add(btnItalic);
+
+		// BUTTON-underline
+		Action underlineAction = new StyledEditorKit.UnderlineAction();
+		underlineAction.putValue(Action.NAME, null);
+		underlineAction.putValue(Action.SHORT_DESCRIPTION, I18N.gettext("xhtml.Underline"));
+		underlineAction.putValue(Action.SMALL_ICON,new ImageIcon(getClass().getResource("Underline16.gif")));
+		btnUnderline = new JButton(underlineAction);
+		btnUnderline.setMargin(new Insets(0, 0, 0, 0));
+		btnUnderline.setPreferredSize(new Dimension(23, 23));
+        btnUnderline.addActionListener(returnFocus);
+		pnlControl.add(btnUnderline);
+
+		// BUTTON-filetransfer
+		if( nu.fw.jeti.applet.Jeti.JAVAPLUGINS.indexOf("filetransfer") != -1 )
+		{
+			btnFileTransfer = new JButton(new ImageIcon(getClass().getResource("ftransfer16.gif")));
+			btnFileTransfer.setMargin(new Insets(0,0,0,0));
+			btnFileTransfer.setPreferredSize(new Dimension(23, 23));
+			btnFileTransfer.setToolTipText(I18N.gettext("xhtml.Transfer_File"));
+			btnFileTransfer.addActionListener(Ftransfer);
+			pnlControl.add(btnFileTransfer);
+		}
+		
+		// BUTTON-callVoip
+		if( nu.fw.jeti.applet.Jeti.JAVAPLUGINS.indexOf("callVoip") != -1 )
+		{
+			btnCallVoip = new JButton();
+			btnCallVoip = buttonCallVoip.button(btnCallVoip);
+			btnCallVoip.addActionListener(callVoip);
+			pnlControl.add(btnCallVoip);
+		}
+	}
+
+	public List getWordList(Extension extension)
+	{
+		if(extension instanceof XHTML)
+		{	
+			return ((XHTML) extension).getWordList();
+		}
+		return null;
+	}
+		
+	public void useXHTML(boolean useXHTMl,String name)
+	{
+		//look if other one sends xhtml
+		//javax.swing.JOptionPane.showMessageDialog(null, "Directory " + dir + " could not be created, Jeti will close");
+		if (useXHTMl != showXHTML && !useXHTMl)
+		{
+			//chatPane.appendSystemMessage(MessageFormat.format(
+			//		I18N.gettext("xhtml.{0}_does_not_support_formatted_messages")
+			//		,new Object[]{name}));
+		}
+		showXHTML=useXHTMl;
+	}
+	
+	public XExtension getXHTMLExtension(List wordList)
+	{
+		return new XHTML(wordList);
+	}
+	
+	public boolean sendXML()
+	{
+		return showXHTML;
+	}
+	
+	public List makeWordListFromDocument()
+	{
+		Element[] element = txtInvoer.getDocument().getRootElements();
+		List wordList = new ArrayList();
+		parseElement(element[0], wordList, new SimpleAttributeSet());
+		return wordList;
+	}
+	
+	private void addToWordList(String text,SimpleAttributeSet currentAttributes, List wordList)
+	{
+		if(text.equals(""))return;
+		StringBuffer temp = new StringBuffer();
+		for(int i = 0;i<text.length();i++)
+		{
+			//split text up in words
+			char c = text.charAt(i);
+			switch (c)
+			{
+				case ' ':	addWordFromTemp(temp, wordList, currentAttributes); wordList.add(new Word(" ", (SimpleAttributeSet)currentAttributes.clone())); temp = new StringBuffer(); break;
+				case '\n':	addWordFromTemp(temp, wordList, currentAttributes); temp = new StringBuffer(); break;
+				case '\t':	addWordFromTemp(temp, wordList, currentAttributes); wordList.add(new Word("\t", (SimpleAttributeSet)currentAttributes.clone()));temp = new StringBuffer();break;
+				default:	temp.append(c);
+			}
+		}
+		addWordFromTemp(temp, wordList, currentAttributes);
+	}
+	
+	private void addWordFromTemp(StringBuffer temp, List wordList, SimpleAttributeSet currentAttributes)
+	{
+		if(temp.length()>0)wordList.add(new Word(temp,(SimpleAttributeSet)currentAttributes.clone()));
+	}
+	
+	
+	/**
+	 * Method parseElement.
+	 * parses a styled document to a wordList 
+	 * @param elem element to parse
+	 * 
+	 */
+	private void parseElement(Element elem, List wordList,SimpleAttributeSet attr)
+	{
+		AttributeSet set = elem.getAttributes();
+		attr.addAttributes(set);
+		if (elem.getName().equals("paragraph"))
+		{
+			if (elem.getStartOffset() != 0)
+			{//only <br/>?
+				wordList.add(new Word("\n",(SimpleAttributeSet)attr.clone()));
+			}
+		}
+		if (elem.getName().equals("content"))
+		{
+			if (elem.getElementCount() > 0)
+			{
+				for (int i = 0; i < elem.getElementCount(); i++)
+				{
+					parseElement(elem.getElement(i),wordList, new SimpleAttributeSet(attr));
+				}
+			}
+			else
+			{
+				try
+				{
+					int offset = elem.getEndOffset() - elem.getStartOffset();
+					String text = elem.getDocument().getText(elem.getStartOffset(), offset);
+					//remove linebreaks
+					addToWordList(text, attr, wordList);
+				}
+				catch (BadLocationException ble)
+				{
+					ble.printStackTrace();
+				}
+			}
+		}
+		else if (elem.getElementCount() > 0)
+		{
+			for (int i = 0; i < elem.getElementCount(); i++)
+			{
+				parseElement(elem.getElement(i), wordList,new SimpleAttributeSet(attr));
+			}
+		}
+	}
+
+	public void unload(){}
+	
+	public static void unload(Backend backend)
+	{
+		backend.removeExtensionHandler("http://jabber.org/protocol/xhtml-im");
+		backend.removeExtensionHandler("http://www.w3.org/1999/xhtml");
+		backend.removeCapability("xhtml","http://jabber.org/protocol/xhtml-im");
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/PluginsHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/PluginsHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/PluginsHandler.java	(revision 3952)
@@ -0,0 +1,116 @@
+
+package nu.fw.jeti.plugins;
+
+import org.xml.sax.helpers.*;
+import java.util.*;
+
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *
+ *	Created in 2002
+ */
+
+public class PluginsHandler extends DefaultHandler
+{
+	private List plugins;
+	private StringBuffer text= new StringBuffer();
+	private Object[] current;
+	private boolean found = false;
+
+    public PluginsHandler(List plugins)
+    {
+		this.plugins = plugins;
+    }
+
+	//public void startDocument()
+	
+
+	//public void endDocument()
+	
+//	public void startElement(String namespaceURI,
+//							String sName, // simple name
+//							String qName, // qualified name
+//							Attributes attrs)
+//							throws SAXException
+//	{
+//	  // if(qName.equals("icon")) texts = new LinkedList();
+//		/*
+//		if(qName.equals("plugin"))
+//		{
+//			current = new Object[6];
+//			current[1] = new Boolean(false);
+//		}
+//		*/
+//	}
+
+
+	public void endElement(String namespaceURI,String sName,String qName)
+	{
+
+		if(qName.equals("name"))
+		{//search name
+			String name = text.toString().trim();
+			for(Iterator i = plugins.iterator();i.hasNext();)
+			{
+				Object[] temp = (Object[])i.next();
+				if(name.equals(temp[0]))
+				{
+					current = temp;
+					found = true;
+					break;
+				}
+			}
+			if(!found)
+			{
+				current = new Object[6];
+				current[0] = name;
+				current[1] = Boolean.FALSE;
+				current[3] = "";
+			}
+		}
+		else if(qName.equals("description")) current[2] = text.toString().trim();
+		else if(qName.equals("version"))
+		{
+			String t = text.toString().trim();
+			if(t.equals("")) current[3] = text;
+//			else if (current[3]==null) current[3]
+		}
+		else if(qName.equals("min_jeti_version")) current[4] = text.toString().trim();
+		else if(qName.equals("parent")) current[5] = text.toString().trim();
+		else if(qName.equals("plugin"))
+		{
+			if(!found)	plugins.add(current);
+			else found = false;
+		}
+		text = new StringBuffer();
+	}
+
+	public void characters(char buf[], int offset, int Len)
+	{
+		   text.append(buf, offset, Len);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/Spell.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/Spell.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/Spell.java	(revision 3952)
@@ -0,0 +1,29 @@
+// Created on 16-okt-2003
+package nu.fw.jeti.plugins;
+
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+import javax.swing.JMenu;
+import javax.swing.text.JTextComponent;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public interface Spell
+{
+	public boolean rightClick(JTextComponent text,MouseEvent e);
+	
+	public void keyReleased(KeyEvent e,JTextComponent text);
+	
+	public void addChangeDictoryMenuEntry(JMenu menu);
+
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/Plugins.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/Plugins.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/Plugins.java	(revision 3952)
@@ -0,0 +1,37 @@
+package nu.fw.jeti.plugins;
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ * An Jeti plugin should be called Plugin and defined in a subpackage of nu.fw.jeti.plugins 
+ * and have a constructor Plugin(nu.fw.jeti.backend Backend backend) that is called to initialize the plugin
+ * 
+ * 
+ * 
+ */
+public interface Plugins
+{
+
+	//back to interface??
+	//String getName(){return NAME;}
+	//abstract String getDescription();
+	//abstract String getVersion();
+	// public void unload(Backend backend);
+	//remove??
+	//public String[] getInfo(){return null;}
+	//TODO change to unload
+	
+	/**
+	 * Perform unload cleaning of loaded plugins.
+	 * Please make sure that your plugin instances 
+	 * are not referenced anymore after this method 
+ 	 */
+	 public void unload();
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/PreferencesPanel.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/PreferencesPanel.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/PreferencesPanel.java	(revision 3952)
@@ -0,0 +1,43 @@
+package nu.fw.jeti.plugins;
+
+import javax.swing.JPanel;
+
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *
+ *	Created in 2002
+ */
+
+public abstract class PreferencesPanel extends JPanel
+{
+
+	public abstract void savePreferences();
+
+    public boolean inhibited() {
+        return false;
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/XMPP.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/XMPP.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/XMPP.java	(revision 3952)
@@ -0,0 +1,41 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 16-okt-2005
+ */
+
+package nu.fw.jeti.plugins;
+
+import java.util.Map;
+
+import nu.fw.jeti.backend.Connect;
+import nu.fw.jeti.backend.ConnectionPacketReceiver;
+import nu.fw.jeti.backend.LoginInfo;
+
+public interface XMPP
+{
+	public ConnectionPacketReceiver getConnectionPacketReceiver(LoginInfo loginInfo,Connect connect);
+	
+	public String resolveXMPPDomain(String server);
+	
+	public Map getXMPPHandlers();
+	
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/PluginData.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/PluginData.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/PluginData.java	(revision 3952)
@@ -0,0 +1,292 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2002 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.plugins;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.xml.parsers.SAXParser;
+
+import nu.fw.jeti.backend.Start;
+import nu.fw.jeti.backend.XMLDataFile;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class PluginData extends XMLDataFile
+{
+	private List plugins;
+	//private List enabledPlugins;
+
+	public PluginData(){plugins = Preferences.getPlugins();}
+
+    public PluginData(SAXParser parser)
+    {
+		plugins = Preferences.getPlugins();
+		InputStream  data = null;
+		data = getClass().getResourceAsStream("/plugins.xml");
+		if(data==null)
+		{
+			try
+	        {
+				//if(Start.programURL != null)
+				{
+					//loadremote plugins
+					data = (new URL(Start.programURL + "plugins.xml")).openStream();
+				}
+	            //else data = new FileInputStream(Start.path +"plugins.xml");
+				//data = new URL(local  + "plugins.xml").openStream();
+			}
+	        catch (IOException ex)
+	        {//plugins xml not found so make one
+				scanPlugins();
+			}
+		}
+		if(data != null)
+		{
+			try
+			{
+				parser.parse(new InputSource(new InputStreamReader(data)),new PluginsHandler(plugins));
+			}
+			catch (SAXException ex)
+			{
+				ex.printStackTrace();
+			}
+			catch (IOException ex)
+			{
+				ex.printStackTrace();
+			}
+			for(Iterator i = plugins.iterator();i.hasNext();)
+			{//remove plugins whitout description /deleted plugins still in preferences
+				Object[] object = (Object[])i.next();
+				if(object[2] == null) i.remove();//was 3
+			}
+		}
+    }
+
+	public void scanPlugins()
+	{
+		List oldPlugins = new ArrayList(plugins);
+		plugins.clear();
+        searchPlugins(Start.pluginPath + "plugins");
+		try
+		{
+			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
+					new FileOutputStream(Start.pluginPath +"plugins.xml"), "UTF8"));
+			writer.write(this.toString());
+			writer.close();
+		}
+		catch (Exception ex2)
+		{
+                    System.err.println("Failed to save 'plugins.xml': " + ex2.getMessage());
+		}
+		for(Iterator i = oldPlugins.iterator();i.hasNext();)
+		{
+			Object[] oldPlugin =(Object[])i.next();
+			for(Iterator j = plugins.iterator();j.hasNext();)
+			{	
+				Object[] newPlugin =(Object[])j.next();
+				if(oldPlugin[0].equals(newPlugin[0]))
+				{
+					newPlugin[1] = oldPlugin[1];
+					break;
+				}
+			}
+		}
+	}
+
+	private void searchPlugins(String dir)
+	{
+            try {
+        File path = new File(dir);
+		File file[] = path.listFiles();
+		if(file == null)
+		{
+			System.err.println(MessageFormat.format(I18N.gettext("main.error.{0}_contains_no_plugins"),new Object[]{dir}));
+			return;
+		}
+		for (int tel=0;tel<file.length;tel++)
+		{
+			File currentFile = file[tel];
+			if(currentFile.toString().endsWith(".jar"))
+			{
+				getPluginInfo(currentFile);
+			}
+		}
+            } catch (Exception e) {
+                // Ignore
+            	e.printStackTrace();
+            }
+	}
+
+	private void getPluginInfo(File file)
+	{
+		URL url[] = new URL[1];
+		try{
+			url[0] = file.toURL();
+		}catch(MalformedURLException e){}
+		//System.out.println(url[0]);
+		String name =file.getName();
+		name = name.substring(0,name.length()-4);
+		//System.out.println(name);
+		Class pluginClass=null;
+		try
+		{
+			URLClassLoader loader = new URLClassLoader(url,this.getClass().getClassLoader());
+			pluginClass = loader.loadClass("nu.fw.jeti.plugins." + name + ".Plugin");
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+			return;
+		}
+		try
+		{
+			String name2 = pluginClass.getField("NAME").get(null).toString();
+			boolean found = false;
+			for(Iterator i = plugins.iterator();i.hasNext();)
+			{
+				Object[] temp = (Object[])i.next();
+				if(name2.equals(temp[0]))
+				{
+					fillPluginsDef(temp,pluginClass ,name2);
+					found = true;
+					break;
+				}
+			}
+			if(!found)
+			{//new plugin
+				Object[] temp = new Object[6];
+				fillPluginsDef(temp,pluginClass ,name2);
+				temp[1] = Boolean.FALSE;
+				plugins.add(temp);
+			}
+			//pluginClass.getMethod("getInfo",new Class[]{}).invoke();
+		}
+		catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+
+	private void fillPluginsDef(Object[] temp,Class pluginClass,String name) throws Exception
+	{
+		temp[0]=name;
+		temp[2]=pluginClass.getField("DESCRIPTION").get(null).toString();
+		temp[3]=pluginClass.getField("VERSION").get(null).toString();
+		temp[4]=pluginClass.getField("MIN_JETI_VERSION").get(null).toString();
+		try 
+		{
+			temp[5]=pluginClass.getField("PARENT").get(null).toString();
+		}
+		catch (NoSuchFieldException e) {}
+		
+	}
+
+	public List getPlugins()
+	{
+		return plugins;
+	}
+
+//	private static void readJAR(URL url)
+//	{
+//		try
+//		{
+//			URL urlJar = new URL("jar:"+url+"!/");
+//			JarFile jarFile = new JarFile(  ""  );
+//			//BufferedReader data =new BufferedReader(new InputStreamReader(jarFile.getInputStream(jarFile.getEntry("info.cfg"))));
+//
+//			BufferedReader data = null;
+//			try
+//			{
+//				data =new BufferedReader(new InputStreamReader(new URL(urlJar + "info.cfg").openStream()));
+//			}
+//			catch (IOException ex)
+//			{
+//				data =new BufferedReader(new InputStreamReader(new URL(urlJar + "icondef.xml").openStream()));
+//			}
+//		}
+//		catch(IOException e)
+//		{
+//			//System.out.println(currentFile.getName() + " is not a valid jar file");
+//			System.out.println(e.toString());
+//		}
+//	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		appendHeader(xml);
+		appendOpenTag(xml,"<plugins>");
+		for(Iterator i = plugins.iterator();i.hasNext();)
+		{
+			appendOpenTag(xml,"<plugin>");
+			Object[] temp = (Object[]) i.next();
+			appendElement(xml,"name",(String)temp[0]);
+			appendElement(xml,"description",(String)temp[2]);
+			appendElement(xml,"version",(String)temp[3]);
+			appendElement(xml,"min_jeti_version",(String)temp[4]);
+			appendElement(xml,"parent",((String)temp[5]));
+			appendCloseTag(xml,"</plugin>");
+		}
+		appendCloseTag(xml,"</plugins>");
+	}
+
+    public static void main(String[] args) {
+        if (args.length != 2) {
+            System.err.println("Arguments: dir_to_scan outfile");
+            System.exit(1);
+        }
+        new Preferences();
+        PluginData data = new PluginData();
+        data.plugins.clear();
+        data.searchPlugins(args[0]);
+        try {
+        	BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
+					new FileOutputStream(args[1]), "UTF8"));
+            writer.write(data.toString());
+            writer.close();
+        } catch (IOException e) {
+            System.err.println("Failed to write file: " + e);
+            System.exit(1);
+        }
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/callVoip/buttonCallVoip.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/callVoip/buttonCallVoip.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/callVoip/buttonCallVoip.java	(revision 3952)
@@ -0,0 +1,42 @@
+package nu.fw.jeti.plugins.callVoip;
+
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.ImageIcon;
+
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.ServerExpresso;
+
+public class buttonCallVoip extends Plugin
+{
+    
+	public static JButton button( JButton voip )
+	{
+		buttonCallVoip classButton = new buttonCallVoip();
+		voip = new JButton(classButton.getIcon("phone16.gif"));
+		voip.setMargin(new Insets(0,0,0,0));
+		voip.setPreferredSize(new Dimension(23,23));
+		voip.setToolTipText(I18N.gettext("callVoip.Dialing_for_business_phone"));
+
+		return voip;
+	}
+	
+	public static String connExpresso(String JidUser, String UserId)
+	{
+		ServerExpresso conn = new ServerExpresso();
+		String _jidUser = JidUser.toString().substring(0, JidUser.toString().indexOf("@"));
+		String _return = conn.connectCallVoip( UserId, _jidUser );
+		
+		return _return;
+	}
+	
+	private ImageIcon getIcon(String nameFile)
+	{
+		return new ImageIcon(getClass().getResource(nameFile));
+	}
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/callVoip/Plugin.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/callVoip/Plugin.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/callVoip/Plugin.java	(revision 3952)
@@ -0,0 +1,35 @@
+/**
+ * @author Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+ * @
+ */
+
+package nu.fw.jeti.plugins.callVoip;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.plugins.Plugins;
+import nu.fw.jeti.util.ServerExpresso;
+import nu.fw.jeti.util.I18N;
+
+public class Plugin implements Plugins 
+{
+	private static Backend backend;
+	private static Plugin plugin;
+	public final static String VERSION = "0.1";
+	public final static String DESCRIPTION = "callVoip.Call_Voip";
+	public final static String NAME = "callVoip";
+	public final static String ABOUT = "by Alexandre Correia";
+	
+	public static void init(final Backend backend)
+	{
+		plugin = new Plugin();
+		Plugin.backend = backend;
+	}
+	
+	public static void unload(Backend backend)
+	{
+		plugin = null;
+	}
+	
+	public void unload() {}
+	
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/Translator.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/Translator.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/Translator.java	(revision 3952)
@@ -0,0 +1,47 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ *
+ *	Created on 14-nov-2003
+ */
+ 
+package nu.fw.jeti.plugins;
+
+import java.util.List;
+
+import javax.swing.text.JTextComponent;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public interface Translator
+{
+	public void init(JTextComponent text);
+	
+	public void translate(List wordList);	
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/FormattedMessage.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/FormattedMessage.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/FormattedMessage.java	(revision 3952)
@@ -0,0 +1,38 @@
+package nu.fw.jeti.plugins;
+
+import java.util.List;
+
+import javax.swing.JPanel;
+import javax.swing.JTextPane;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XExtension;
+import nu.fw.jeti.ui.ChatSplitPane;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public interface FormattedMessage
+{
+	public void initXHTML(ChatSplitPane chatPane, JTextPane txtInvoer, JPanel pnlControl, Object Jid, Object JidName);
+
+	List getWordList(Extension extension);
+	
+	void useXHTML(boolean useXHTMl,String name);
+	
+	List makeWordListFromDocument();
+	
+	XExtension getXHTMLExtension(List wordList);
+	
+	boolean sendXML();
+
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/Translate.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/Translate.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/Translate.java	(revision 3952)
@@ -0,0 +1,34 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 28-mei-2006
+ */
+
+package nu.fw.jeti.plugins;
+
+import javax.swing.JTextPane;
+
+import nu.fw.jeti.ui.ChatSplitPane;
+
+public interface Translate
+{
+	public void init(ChatSplitPane chatPane,JTextPane txtInput);
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/plugins/PluginsInfo.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/plugins/PluginsInfo.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/plugins/PluginsInfo.java	(revision 3952)
@@ -0,0 +1,244 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *
+ *	Created on 26-dec-2003
+ */
+ 
+package nu.fw.jeti.plugins;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.text.MessageFormat;
+import java.util.*;
+import javax.xml.parsers.SAXParser;
+import nu.fw.jeti.backend.Start;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class PluginsInfo
+{
+	private static Backend backend;
+	private static Map loadedPlugins = new HashMap();
+	public static Map loadedPreferencePanels = new HashMap();
+	private static Map pluginInstances = new HashMap();
+	private static String pluginsJava;
+			
+	public PluginsInfo( Backend backend, Start start )
+	{
+		PluginsInfo.backend		= backend;
+		PluginsInfo.pluginsJava = nu.fw.jeti.applet.Jeti.JAVAPLUGINS;
+		
+		/**
+		 * Carregando plugins:
+		 * 
+		 * nu.fw.jeti.plugins.<NOME_do_Plugin>.Plugin.init( backend );
+		 * loadedPlugins.put("<NOME_do_Plugin>", nu.fw.jeti.plugins.<NOME_do_Plugin>.Plugin.class);
+		 * 
+		 * Exemplo : Plugin xhtml
+		 * nu.fw.jeti.plugins.xhtml.Plugin.init( backend );
+		 * loadedPlugins.put("xhtml", nu.fw.jeti.plugins.xhtml.Plugin.class);
+		 */
+		
+		String[] plugins = pluginsJava.split(",");	
+		
+		for(int i = 0 ; i < plugins.length; i++)
+			PluginsInfo.loadPlugin(plugins[i].toString());
+	}
+
+	public static boolean isPluginLoaded(String name)
+	{
+		return loadedPlugins.containsKey(name);
+	}
+
+	private static void loadPlugin(String name)
+	{
+		Class loadedClass = nu.fw.jeti.applet.Jeti.getPlugin(name);
+		
+		try
+		{
+			//Init plugin
+			Method m = loadedClass.getMethod("init",new Class[]{Backend.class});
+			m.invoke(null,new Object[]{backend});
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+			return;
+		}
+		
+		loadedPlugins.put(name, loadedClass);
+	}
+	
+	public static void unloadPlugin(String name)
+	{
+		Class loadedClass = (Class)loadedPlugins.remove(name);
+        
+		if (loadedClass == null)
+			return;
+
+        System.out.println(I18N.gettext("main.pluginsInfo.Plugin_removing") + " : " + name);
+		
+        loadedPreferencePanels.remove(name);
+        
+		List list = (List) pluginInstances.remove(name);
+		
+		try
+		{
+			//unload cleaning
+			Method m = loadedClass.getMethod("unload",new Class[]{Backend.class});
+			m.invoke(null,new Object[]{backend});
+		}
+		catch(NoSuchMethodException e)
+		{
+			System.out.println("ERRO : " + name + " " + I18N.gettext("main.pluginsInfo.has_no_remove"));
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+				
+		if ( list == null)
+			return;
+		
+		for (Iterator i = list.iterator(); i.hasNext();)
+		{	
+			//unload loaded plugins
+			Plugins plugin = (Plugins)((WeakReference)i.next()).get();
+			
+			if ( plugin != null )
+				plugin.unload();
+		}
+		
+		System.out.println( "Removed plugin : " + name );
+	}
+	
+	/**
+	 * Gets a single instance of a plugin, the plugin has to have the static getInstance() method
+	 * @param name name of the plugin
+	 * @return instance of the plugin
+	 */
+	public static Object getPluginInstance(String name)
+	{
+		Object o = null;
+		try
+		{
+			Class loadedClass = (Class) loadedPlugins.get(name);
+			try
+			{
+				Method m = loadedClass.getMethod("getInstance",null);
+				o = m.invoke(null,null);
+			}
+			catch(NoSuchMethodException e)
+			{
+				System.out.println(name + " has no instance method");
+			}
+			catch(Exception e)
+			{
+				e.printStackTrace();
+			}
+		}
+		catch (Exception ie)
+		{
+			ie.printStackTrace();
+			return null;
+		}
+
+		return o;
+	}
+	
+	public static Plugins newPluginInstance(String name)
+	{
+		Object o = null;
+		
+		try
+		{
+			o = ((Class) loadedPlugins.get(name)).newInstance();
+			addInstance(name,o);
+		}
+		catch (Exception ie)
+		{
+			ie.printStackTrace();
+			return null;
+		}
+		
+		return (Plugins) o;
+	}
+
+	private static void addInstance(String name, Object object)
+	{
+		List list = (List) pluginInstances.get(name);
+		
+		if (list == null)
+		{
+			list = new LinkedList();
+			pluginInstances.put(name, list);
+		}
+		
+		list.add(new WeakReference(object));
+	}
+	
+	public static String getAbout()
+	{
+		StringBuffer buffer = new StringBuffer();
+		
+		for(Iterator i = loadedPlugins.entrySet().iterator();i.hasNext();)
+		{
+			Map.Entry entry = (Map.Entry)i.next();
+			String text = null;
+			try
+			{
+				 text = (String) ((Class)entry.getValue()).getField("ABOUT").get(null);
+			}
+			catch (Exception e1){}
+			
+			if( text != null)
+				buffer.append("\n - " + (String)entry.getKey() + '\n' + text);
+		}
+		 
+		return buffer.toString(); 
+	} 
+	
+	// Remove plugins
+	public void exit()
+	{
+		String[] temp = new String[loadedPlugins.size()];
+		loadedPlugins.keySet().toArray(temp);
+		
+		for(int i=0;i<temp.length;i++)
+		{
+			unloadPlugin(temp[i]);
+		}
+		
+		System.out.println(I18N.gettext("main.pluginsInfo.plugins_unloaded"));
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/StatusChangeListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/StatusChangeListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/StatusChangeListener.java	(revision 3952)
@@ -0,0 +1,35 @@
+package nu.fw.jeti.events;
+
+/**
+ * The listener interface for receiving status events.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface StatusChangeListener extends JETIListener
+{
+
+	/**
+	 * Called when the connection status has changed.
+	 * @param online true when the connection is online
+	 */
+	void connectionChanged(boolean online);
+
+	/**
+	 * Called when you have changed your own presence status
+	 * @param show The new show status, one of Presence FREE_FOR_CHAT, AVAILABLE, AWAY, DND, XA or UNAVAILABLE.
+	 * @param status The new status message (can be null)
+	 */
+	void ownPresenceChanged(int show, String status);
+
+	/**
+	 * Called when Jeti will be closed
+	 */
+	void exit();
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/ErrorListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/ErrorListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/ErrorListener.java	(revision 3952)
@@ -0,0 +1,26 @@
+package nu.fw.jeti.events;
+
+/**
+ * Created on 1-mrt-2003
+ * @author E.S. de Boer
+ * Signals protocol errors 
+ *
+ */
+public interface ErrorListener extends JETIListener
+{
+	
+	/**
+	 * The errorcode and the errorMessage from a error infoquery 
+	 * @param errorCode
+	 * @param error
+	 */
+	void error(int errorCode,String error);
+
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/JETIListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/JETIListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/JETIListener.java	(revision 3952)
@@ -0,0 +1,19 @@
+package nu.fw.jeti.events;
+
+/**
+ * This interface is usesed to mark a listener interface as a JetiListener. The class implementing
+ * the interface can then register it self with {@link nu.fw.jeti.jabber.Backend#addListener(Class,JetiListener)}
+ * to recieve the requested information.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface JETIListener
+{
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/JavaErrorListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/JavaErrorListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/JavaErrorListener.java	(revision 3952)
@@ -0,0 +1,24 @@
+package nu.fw.jeti.events;
+
+/**
+ * @author E.S. de Boer
+ *
+ *	Signals that a error has happend
+ */
+public interface JavaErrorListener extends JETIListener
+{
+
+	
+	/**
+	 * signals occurence of an error so it can be shown in the ui
+	 */
+	void error();
+
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/PresenceListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/PresenceListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/PresenceListener.java	(revision 3952)
@@ -0,0 +1,23 @@
+package nu.fw.jeti.events;
+
+/**
+ * The listener interface for listening to presence packets.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface PresenceListener extends JETIListener
+{
+	/**
+	 * Called when someones presence has been changed.
+	 * The subscription part of presence is handeld by Roster.
+	 * @param presence The changed presence.
+	 */
+	void presenceChanged(nu.fw.jeti.jabber.elements.Presence presence);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/ServerListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/ServerListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/ServerListener.java	(revision 3952)
@@ -0,0 +1,20 @@
+package nu.fw.jeti.events;
+
+/**
+ * The listener interface for receiving server roster events.
+ * This listener is used to inform the frontside server roster of changes in the backside server roster
+ * see {@link RosterTreeModel} for a TreeModel implementation.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface ServerListener extends RosterListener
+{
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/CompleteRosterListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/CompleteRosterListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/CompleteRosterListener.java	(revision 3952)
@@ -0,0 +1,22 @@
+package nu.fw.jeti.events;
+
+/**
+ * receive complete roster (to wrtite your own roster instead of suplied one)
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface CompleteRosterListener extends JETIListener
+{
+	void rosterReceived(nu.fw.jeti.jabber.elements.InfoQuery infoQuery, nu.fw.jeti.jabber.elements.IQXRoster roster);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/DiscoveryListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/DiscoveryListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/DiscoveryListener.java	(revision 3952)
@@ -0,0 +1,50 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 7-aug-2004
+ */
+ 
+package nu.fw.jeti.events;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.DiscoveryInfo;
+import nu.fw.jeti.jabber.elements.DiscoveryItem;
+
+/**
+ * @author E.S. de Boer
+ * The listener interface for receiving disco or browse results.
+ * The results are cached for later use.
+ */
+public interface DiscoveryListener extends JETIListener
+{
+
+	void discoveryItemResult(JID jid,DiscoveryItem item);
+	
+	void discoveryInfoResult(JID jid,DiscoveryInfo info);
+		
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/IQResultListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/IQResultListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/IQResultListener.java	(revision 3952)
@@ -0,0 +1,27 @@
+package nu.fw.jeti.events;
+
+import nu.fw.jeti.jabber.elements.InfoQuery;
+
+/**
+ * Created on 18-okt-2004
+ * @author E.S. de Boer
+ * returns IQ results 
+ *
+ */
+public interface IQResultListener extends JETIListener
+{
+	
+	/**
+	 * The errorcode and the errorMessage from a error infoquery 
+	 * @param iq the infoquery result
+	 */
+	void iqResult(InfoQuery iq);
+
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/LoginListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/LoginListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/LoginListener.java	(revision 3952)
@@ -0,0 +1,35 @@
+// Created on 7-jul-2003
+package nu.fw.jeti.events;
+
+/**
+ * Listener for information about the login
+ * @author E.S. de Boer
+ *
+ */
+public interface LoginListener extends JETIListener
+{
+
+	/**
+	 *	Messages send when logging in
+	 *  @param message
+	 */
+	void loginMessage(String message);
+		
+	/**
+	 * Error messages when logging in 
+	 * @param errorMessage
+	 */
+	void loginError(String errorMessage);
+	
+	/**
+	 * Unauthorized to this server
+	 */
+	void unauthorized();
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/OwnMessageListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/OwnMessageListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/OwnMessageListener.java	(revision 3952)
@@ -0,0 +1,22 @@
+package nu.fw.jeti.events;
+
+/**
+ * The listener interface for listening to your own send messages.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface OwnMessageListener extends JETIListener
+{
+	/**
+	 * Called when you have sent a message
+	 * @param message The sent message.
+	 */
+	void sendMessage(nu.fw.jeti.jabber.elements.Message  message);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/PreferenceListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/PreferenceListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/PreferenceListener.java	(revision 3952)
@@ -0,0 +1,34 @@
+package nu.fw.jeti.events;
+
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2002 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+public interface PreferenceListener extends JETIListener
+{
+	void preferencesChanged();
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/RosterListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/RosterListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/RosterListener.java	(revision 3952)
@@ -0,0 +1,104 @@
+package nu.fw.jeti.events;
+
+import nu.fw.jeti.backend.roster.*;
+import nu.fw.jeti.jabber.*;
+import nu.fw.jeti.ui.models.RosterTreeModel;
+
+/**
+ * The listener interface for receiving roster events.
+ * This listener is used to inform the frontside roster of changes in the backside
+ * see {@link RosterTreeModel} for a TreeModel implementation.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface RosterListener extends JETIListener
+{
+	/**
+	 * Called on a complete roster refresh.
+	 * @param tree Tree containing the new roster.
+	 */
+	public void rosterReplaced(JIDStatusTree tree);
+
+	/**
+	 * Called when a group has been added to the roster.
+	 * @param group The new group.
+	 * @param index the index of the new group in the tree.
+	 */
+	void groupAdded(JIDStatusGroup group,int index);
+
+	/**
+	 * Called when a group has been deleted.
+	 * @param group The deleted group.
+	 * @param index The index of the group in the tree before deletion.
+	 */
+	void groupDeleted(JIDStatusGroup group,int index);
+	
+	
+	/**
+	 * Called when a group has changed textual info.
+	 * @param group The deleted group.
+	 * @param index The index of the group in the tree.
+	 */
+	public void groupUpdated(final JIDStatusGroup group,int index);
+	
+
+	/**
+	 * Called when a primaryJidStatus has been added
+	 * @param jidGroup The group where the PrimaryJIDStatus is in.
+	 * @param primary The added primaryJIDStatus
+	 * @param index The index of the primaryJIDStatus in the group.
+	 */
+	void primaryAdded(JIDStatusGroup jidGroup,PrimaryJIDStatus primary,int index);
+
+	/**
+	 * Called when a primaryJidStatus has been deleted.
+	 * @param jidGroup The group the primaryJIDStatus was in.
+	 * @param primary The deleted primaryJIDStatus.
+	 * @param index The index of the primaryJIDStaus in the group before deletion.
+	 */
+	void primaryDeleted(JIDStatusGroup jidGroup,PrimaryJIDStatus primary,int index);
+
+	/**
+	 * Called when the primaryJidstatus has been changed
+	 * @param jidGroup The group the primaryJIDStatus is in.
+	 * @param primary The changed primaryJIDStatus
+	 * @param index The index of the primaryJIDStatus in the group
+	 */
+	//weg? only server uses this
+	void primaryUpdated(JIDStatusGroup jidGroup,PrimaryJIDStatus primary,int index);
+
+	/**
+	 * Called when a JIDStatus has been added.
+	 * @param jidGroup The group where the primaryJIDStatus, where this jidstatus has been added to, is in.
+	 * @param primary The primaryJIDStatus where this JIDStatus has been added to.
+	 * @param jidStatus The new JIDStatus
+	 * @param index The index of the new JIDStatus in the PrimaryJIDStatus.
+	 */
+	void jidStatusAdded(JIDStatusGroup jidGroup,PrimaryJIDStatus primary,JIDStatus jidStatus,int index);
+
+	/**
+	 * Called when a JIDStatus has been deleted.
+	 * @param jidGroup The group where the primaryJIDStatus, where this jidstatus has been deleted from, is in.
+	 * @param primary The primaryJIDStatus where this JIDStatus has been deleted from.
+	 * @param jidStatus The deleted JIDStatus
+	 * @param index The index of jidStatus in the PrimaryJIDStatus before deletion.
+	 */
+	void jidStatusDeleted(JIDStatusGroup jidGroup,PrimaryJIDStatus primary,JIDStatus jidStatus,int index);
+
+	//void jidStatusUpdated(JIDGroup2 jidGroup,JIDPrimaryStatus primary,JIDStatus2 jidStatus,int index);
+
+	/**
+	 * Called when the JIDStatussen of a primaryJIDStatus are updated.
+	 * @param jidGroup The group where primary is in.
+	 * @param primary The primaryJIDStatus that has been updated.
+	 */
+	void jidStatussenUpdated(JIDStatusGroup jidGroup,PrimaryJIDStatus primary);
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/ChatEndedListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/ChatEndedListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/ChatEndedListener.java	(revision 3952)
@@ -0,0 +1,34 @@
+package nu.fw.jeti.events;
+
+import java.util.Date;
+
+import javax.swing.text.Document;
+
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * Listener interface signalling end of a chat session, used to save chat
+ * 27-4-2004
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+
+public interface ChatEndedListener extends JETIListener
+{
+	/**
+	 * Called when a chat session has ended
+	 * @param jid The JID of the chat session (room jid or person)
+	 */
+	void chatEnded(JID jid);
+}
+
+
+
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/OOBListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/OOBListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/OOBListener.java	(revision 3952)
@@ -0,0 +1,31 @@
+package nu.fw.jeti.events;
+
+import nu.fw.jeti.jabber.elements.IQXOOB;
+
+/**
+ * doesn't work yet
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface OOBListener extends JETIListener
+{
+	/**
+	 * 
+	 * 
+	 * @param jid
+	 * @param id
+	 * @param oob
+	 */
+	void oob(nu.fw.jeti.jabber.JID jid,String id,IQXOOB oob);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/RegisterListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/RegisterListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/RegisterListener.java	(revision 3952)
@@ -0,0 +1,22 @@
+package nu.fw.jeti.events;
+
+/**
+ * The listener interface for listening to register queries.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface RegisterListener extends JETIListener
+{
+	/**
+	 * Called when a register iq has been received
+	 * @param register The register element
+	 */
+	void register(nu.fw.jeti.jabber.elements.IQRegister register,String id);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/MessageListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/MessageListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/MessageListener.java	(revision 3952)
@@ -0,0 +1,22 @@
+package nu.fw.jeti.events;
+
+/**
+ * The listener interface for receiving messages.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface MessageListener extends JETIListener
+{
+	/**
+	 * Called when a new message has arrived.
+	 * @param message The new message.
+	 */
+	void message(nu.fw.jeti.jabber.elements.Message message);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/events/MessageEventListener.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/events/MessageEventListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/events/MessageEventListener.java	(revision 3952)
@@ -0,0 +1,22 @@
+package nu.fw.jeti.events;
+
+import nu.fw.jeti.jabber.elements.XMessageEvent;
+
+/**
+ * The listener interface for receiving message events.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+//verbeter met ids etc
+public interface MessageEventListener extends JETIListener
+{
+	void onComposing(nu.fw.jeti.jabber.JID jid,String thread,XMessageEvent messageEvent);
+
+	void requestComposing(nu.fw.jeti.jabber.JID jid, String id,String thread);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/applet/PreferencesHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/applet/PreferencesHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/applet/PreferencesHandler.java	(revision 3952)
@@ -0,0 +1,101 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ *
+ *	Created on 14-aug-2003
+ */
+
+package nu.fw.jeti.applet;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.util.Preferences;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class PreferencesHandler extends ExtensionHandler
+{
+	
+	private Map preferences;
+	//private List plugins;
+	//private String xmlVersion;
+
+	public void startHandling(Attributes attributes)
+	{
+		preferences = new HashMap();
+		//xmlVersion = attributes.getValue("xmlVersion");
+	}
+
+	public void startElement(String qName,Attributes attrs)
+	{
+		if(qName.equals("plugin"))
+		{
+			String type = attrs.getValue("type");
+			String name = attrs.getValue("name");
+			Boolean enabled = Boolean.valueOf(attrs.getValue("enabled"));
+			List plugables=Preferences.getPlugable(type);
+			boolean found = false;
+			for(Iterator i=plugables.iterator();i.hasNext();)
+			{
+				Object[] o =(Object[])i.next();
+				if(o[0].equals(name))
+				{
+					o[1] =enabled;
+					found = true;
+				}
+			}
+			if(!found)
+			{
+				Object[] temp = new Object[6];
+				temp[0] = name;
+				temp[1] = enabled;
+				plugables.add(temp);
+			}
+		}
+		else if(qName.equals("preference"))
+		{
+			String key = attrs.getValue("key");
+			String value = attrs.getValue("value");
+			preferences.put(key,value);
+		}
+	}
+
+	public Extension build()
+	{
+		return new JetiPrivatePreferencesExtension(new Preferences(preferences));
+	}
+	
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/applet/Crypto.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/applet/Crypto.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/applet/Crypto.java	(revision 3952)
@@ -0,0 +1,71 @@
+package nu.fw.jeti.applet;
+
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/* 
+ *	@author Serge Rehem
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or jabber/mail me at jeti@jabber.org
+ *
+ *	Created on 14-nov-2003
+ */
+
+/*
+ *  This code is based on AES Interop Between PHP and Java (Part 1) post at  
+ *  http://propaso.com/blog/?cat=6                                           
+ */
+public class Crypto {
+
+	public static String decrypt(String cryptedPasswd, String secretKey) {
+		Cipher cipher;
+		try {
+			cipher = Cipher.getInstance("AES/CBC/NoPadding");
+			SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "AES");
+
+			IvParameterSpec ivSpec = new IvParameterSpec("@4321avaJtluafeD".getBytes());
+			
+			cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+			byte[] outText = cipher.doFinal(hexToBytes(cryptedPasswd));
+			return new String(outText).trim();
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	private static byte[] hexToBytes(String str) {
+		if (str == null) {
+			return null;
+		} else if (str.length() < 2) {
+			return null;
+		} else {
+			int len = str.length() / 2;
+			byte[] buffer = new byte[len];
+			for (int i = 0; i < len; i++) {
+				buffer[i] = (byte) Integer.parseInt(str.substring(i * 2,
+						i * 2 + 2), 16);
+			}
+			return buffer;
+		}
+	}
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/applet/JetiPrivatePreferencesExtension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/applet/JetiPrivatePreferencesExtension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/applet/JetiPrivatePreferencesExtension.java	(revision 3952)
@@ -0,0 +1,106 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ *
+ *	Created on 14-aug-2003
+ */
+ 
+package nu.fw.jeti.applet;
+
+
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQXExtension;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.util.Preferences;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class JetiPrivatePreferencesExtension extends Extension implements IQXExtension
+{
+    private Preferences preferences;
+	private String xmlVer;
+	public final static String XML_VERSION ="v1";
+
+	public JetiPrivatePreferencesExtension(){}
+	
+	public JetiPrivatePreferencesExtension(Preferences p)
+	{
+		preferences = p;
+		xmlVer = XML_VERSION;
+	}
+
+//	public JetiPrivatePreferencesExtension()
+//	{
+//		this.openGroups = openGroups;
+//		xmlVer = XML_VERSION;
+//	}
+	  
+    
+    public boolean isCorrectVersion()
+    {
+	    return XML_VERSION.equals(xmlVer);
+	}
+
+
+	public String getXmlVersion()
+    {
+	    return xmlVer;
+	}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		if (iq.getType().equals("result"))
+		{
+			
+		}
+		else if (iq.getType().equals("error"))
+		{
+			System.out.println(iq.getErrorDescription());
+		}
+	}
+	
+
+	public void appendToXML(StringBuffer retval)
+	{
+		retval.append("<jeti xmlns='jeti:prefs'");
+		if (preferences == null)
+		{ //short cut
+			retval.append("/>");
+			return;
+		}
+		appendAttribute(retval,"xmlVersion",xmlVer);
+		retval.append(">");
+		if(preferences != null)
+		{
+			preferences.appendToXML(retval);
+		}
+		retval.append("</jeti>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/applet/Jeti.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/applet/Jeti.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/applet/Jeti.java	(revision 3952)
@@ -0,0 +1,519 @@
+package nu.fw.jeti.applet;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JApplet;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import nu.fw.jeti.backend.LoginInfo;
+import nu.fw.jeti.backend.Start;
+import nu.fw.jeti.events.StatusChangeListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.UnknownJIDStatus;
+import nu.fw.jeti.jabber.elements.IQPrivate;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.InfoQueryBuilder;
+import nu.fw.jeti.jabber.elements.Presence;
+import nu.fw.jeti.jabber.elements.RosterBuilder;
+import nu.fw.jeti.jabber.elements.RosterItemBuilder;
+import nu.fw.jeti.ui.AddContact;
+import nu.fw.jeti.ui.JIDInput;
+import nu.fw.jeti.ui.StatusButton;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.JavaScriptServerExpresso;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+import nu.fw.jeti.util.ProxyIM;
+
+import netscape.javascript.JSObject;
+
+// Crypt Password
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+/* 
+ *	@author E.S. de Boer
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or jabber/mail me at jeti@jabber.org
+ *
+ *	Created on 14-nov-2003
+ */
+
+public class Jeti extends JApplet
+{
+	private Start start;
+	private static Jeti applet;
+	private URL exitURL;
+	private static String groupchatRoom;
+	private static String groupchatServer;
+	private Backend backend;
+	private boolean started = false;
+	private SecureMethodRunner secureMethodInvoker;
+	private Thread initIM;
+	public static JLabel from;
+	public static LoginInfo loginInfo;
+	public static String USERID;
+	public static String SERVEREXPRESSO;
+	public static String NAMECOMPANY;
+	public static String USEHTTPS;
+	public static String CNNAME;
+	public static List GROUPSUSER;
+	public static String SERVERJABBER;
+	public static String COUNTRY;
+	public static String LANGUAGE;
+	public static String JAVAPLUGINS;
+	
+	public static JSObject WINDOWAPPLET = null;	
+
+	/**
+	 * @Author	: Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+	 * @date	: 10/06/2009
+	 * @Brief	: A variavel DEBUG_APPLET_VIEWER = true, para usar o appletviewer
+	 */
+	public static boolean DEBUG_APPLET_VIEWER = false;
+
+	public void init()
+	{
+		splash();
+		loadApplet();
+	}
+	
+	public void loadApplet()
+	{
+		applet = this;
+		String keyCrypt = "DefaultJava1234@";		
+		
+		String	_mcrypt = applet.getParameter("FIELD17");
+				_mcrypt = _mcrypt.toLowerCase(); 
+
+		String server;
+		String portText;
+		boolean ssl;
+		String user;
+		String password;
+		String resource;
+		String host;
+		String serverExpresso;
+		String useHttps;
+		String nameCompany;
+		String cnName;
+		String country;
+		String language;
+		String javaPlugins;
+				
+		if ( _mcrypt.equals("true") )
+		{
+			server			= new String( Crypto.decrypt(applet.getParameter("FIELD12"), keyCrypt ) );
+			portText		= new String( Crypto.decrypt(applet.getParameter("FIELD10"), keyCrypt ) );
+			ssl				= Boolean.valueOf( Crypto.decrypt(applet.getParameter("FIELD13"), keyCrypt ) ).booleanValue();
+			user			= new String( Crypto.decrypt(applet.getParameter("FIELD16"), keyCrypt ) );
+			password		= new String( Crypto.decrypt(applet.getParameter("FIELD09"), keyCrypt ) );
+			resource		= new String( Crypto.decrypt(applet.getParameter("FIELD11"), keyCrypt ) );
+			host			= new String( Crypto.decrypt(applet.getParameter("FIELD05"), keyCrypt ) );
+			serverExpresso	= new String( Crypto.decrypt(applet.getParameter("FIELD03"), keyCrypt ) );
+			useHttps		= new String( Crypto.decrypt(applet.getParameter("FIELD14"), keyCrypt ) );
+			nameCompany		= new String( Crypto.decrypt(applet.getParameter("FIELD08"), keyCrypt ) );
+			cnName			= new String( Crypto.decrypt(applet.getParameter("FIELD01"), keyCrypt ) );
+			country			= new String( Crypto.decrypt(applet.getParameter("FIELD02"), keyCrypt ) );
+			language 		= new String( Crypto.decrypt(applet.getParameter("FIELD07"), keyCrypt ) );
+			javaPlugins 	= new String( applet.getParameter("FIELD06") );
+		}
+		else
+		{
+			server			= new String( applet.getParameter("FIELD12") );
+			portText		= new String( applet.getParameter("FIELD10") );
+			ssl				= Boolean.valueOf( applet.getParameter("FIELD13") ).booleanValue();
+			user			= new String( applet.getParameter("FIELD16") );
+			password		= new String( applet.getParameter("FIELD09") );
+			resource		= new String( applet.getParameter("FIELD11") );
+			host			= new String( applet.getParameter("FIELD05") );
+			serverExpresso	= new String( applet.getParameter("FIELD03") );
+			useHttps		= new String( applet.getParameter("FIELD14") );
+			nameCompany		= new String( applet.getParameter("FIELD08") );
+			cnName			= new String( applet.getParameter("FIELD01") );
+			country			= new String( applet.getParameter("FIELD02") );
+			language		= new String( applet.getParameter("FIELD07") );
+			javaPlugins		= new String( applet.getParameter("FIELD06") );
+		}
+		
+		/**
+		 * @Author : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+		 * @Date : 20/10/2008
+		 * @Description : - Nome Servidor IM-Jabber para Constant SERVERJABBER
+		 * 				  - Nome da Maquina Expresso para Constant SERVEREXPRESSO
+		 * 				  - Nome da Empresa para Constant NAMECOMPANY
+		 * 				  - Informa qual o tipo de protocolo a ser usado http ou https.
+		 * 				  - Passa ao java o cn do ldap do servidor expresso.
+		 * 				  - Carrega plugins para o Java.
+		 * 				  - uid expresso;		 					  
+		 */
+		
+		SERVERJABBER	= server;	
+		SERVEREXPRESSO	= serverExpresso;
+		NAMECOMPANY		= nameCompany;
+		USEHTTPS		= useHttps;
+		CNNAME			= cnName;
+		LANGUAGE		= language;
+		COUNTRY			= country;
+		JAVAPLUGINS		= javaPlugins;
+		USERID			= user;
+
+		// Reinit languages incase they changed
+		I18N.init(language, country);
+		
+		groupchatRoom = getParameter("GROUPCHATROOM");
+		groupchatServer = getParameter("GROUPCHATSERVER");
+		
+		Start.programURL = getCodeBase();
+		Start.applet = true;
+		final JPanel panel = new JPanel(new BorderLayout());
+		start = new Start( getCodeBase().toString(), panel );
+		
+		backend = start.getBackend();
+		backend.getMain().translate();
+
+		if( server !=null || resource != null || portText !=null )
+		{
+			int port = Integer.parseInt(portText);
+			
+			ProxyIM.setProxy("http://" + server);
+			String proxyServer = ProxyIM.getProxyServer();
+			String proxyPort   = String.valueOf(ProxyIM.getProxyPort());
+
+			loginInfo = new LoginInfo(server, host.trim(), user, password, resource, port, ssl, proxyServer, proxyPort);
+			backend.autoLogin(loginInfo, 2);
+
+			if( loginInfo != null )
+			{
+				secureMethodInvoker = new SecureMethodRunner(backend);
+				secureMethodInvoker.start();
+
+				backend.addExtensionHandler("jeti:prefs",new PreferencesHandler());
+				try
+				{
+					backend.addListener(StatusChangeListener.class,new StatusChangeListener()
+					{
+						public void connectionChanged(boolean online)
+						{
+							if( online )
+							{	
+								backend.send(new InfoQuery("get",new IQPrivate(new JetiPrivatePreferencesExtension())));
+								
+								if( !nu.fw.jeti.applet.Jeti.DEBUG_APPLET_VIEWER )
+									WINDOWAPPLET = JSObject.getWindow(applet);
+							}
+						}
+						public void ownPresenceChanged(int show, String status)	{}
+						public void exit(){}
+					});
+				}
+				catch(NoClassDefFoundError e)
+				{	
+					e.printStackTrace();
+				}
+				
+				new Popups(Jeti.this);
+				SwingUtilities.invokeLater(new Runnable()
+				{
+					public void run()
+					{
+						setContentPane(panel);
+						validate();
+					}
+				});
+				started = true;
+			}
+			else
+			{
+				started = false;
+			}
+		}
+	}
+	
+	private void splash()
+	{ 
+		/**
+		 * Splash - Inicializador - 
+		 * Modificado by Alexandre Correia - alexandrecorreia@celepar.pr.gov.br 
+		 */
+		
+       JLabel label = new JLabel(  "<html>" +
+  							  	   "<span style='margin-left:10px;font:20pt Verdana,Arial,serif'>Expresso IM </span>" +
+								   "<br/>" +
+								   "<span style='font:9pt Verdana,Arial,serif'> Jeti - http://www.jeti-im.org </span></html>",
+								   JLabel.CENTER );
+       
+       label.setBackground(new Color(255,255,255));
+       getContentPane().setBackground(new Color(255,255,255));
+       getContentPane().add(label,BorderLayout.CENTER);
+       
+       JPanel panel = new JPanel();
+       panel.setBackground(new Color(255,255,255));
+       from = new JLabel(NAMECOMPANY, JLabel.CENTER);
+       from.setBackground(new Color(255,255,255));
+       from.setFont(new java.awt.Font("Serif,Verdana,Arial",1,10));
+       panel.add(from);
+       getContentPane().add(panel,BorderLayout.SOUTH);
+	}
+	
+	public String getStatusMessages(int status)
+	{
+        List messages = Preferences.getStatusMessages(status);
+		return messages.toString();
+	}
+	
+	public String getGroupsToExpresso()
+	{
+		String[] Groups = applet.backend.getAllGroups();
+		String groups = "";
+		
+		for(int i = 0 ; i < Groups.length; i++ )
+			groups += Groups[i] + ";";
+		
+		return groups.toString();
+	}
+	
+	public void changeStatusfromExpresso(int status, String msgStatus)
+	{
+		switch (status)
+        {
+		    case Presence.AVAILABLE: 	 
+		    	StatusButton.changeStatusExpresso(Presence.AVAILABLE, msgStatus);
+		    	break;
+		    case Presence.FREE_FOR_CHAT: 
+		    	StatusButton.changeStatusExpresso(Presence.FREE_FOR_CHAT, msgStatus); 
+		    	break;
+		    case Presence.AWAY:
+		    	StatusButton.changeStatusExpresso(Presence.AWAY, msgStatus); 
+		    	break;
+		    case Presence.XA:            
+		    	StatusButton.changeStatusExpresso(Presence.XA, msgStatus); 
+		    	break;
+		    case Presence.DND:           
+		    	StatusButton.changeStatusExpresso(Presence.DND, msgStatus); 
+		    	break;
+		    case Presence.UNAVAILABLE:   
+		    	StatusButton.changeStatusExpresso(Presence.UNAVAILABLE, msgStatus); 
+		    	break;
+		    case Presence.INVISIBLE:     
+		    	StatusButton.changeStatusExpresso(Presence.INVISIBLE, msgStatus); 
+		    	break;
+        }
+	}
+	
+	public static void showURL(String url)
+	{
+		try
+		{
+			applet.getAppletContext().showDocument(new URL(url),"_blank");
+		}
+		catch (MalformedURLException e){}
+	}
+		
+	public void destroy()
+	{
+		try
+		{
+			if( secureMethodInvoker != null )
+				secureMethodInvoker.stopRunning();
+			start.close();
+		}
+		catch (Exception e) 
+		{
+			e.printStackTrace();
+		} 
+	}
+		
+	public static void exit()
+	{
+		applet.getAppletContext().showDocument(applet.exitURL);
+	}
+	
+	public boolean isReady()
+	{
+		System.out.println("ready " + started);
+		return started;
+	}
+	
+	public boolean isLoggedIn()
+	{
+		if( !started )
+			return false;
+		
+		return backend.isLoggedIn();
+	}
+	
+	public void openChat(String jid)
+	{
+		try
+		{
+			JID j = JID.checkedJIDFromString(jid);
+			secureMethodInvoker.addData(new Object[]{"openChat",j});
+		} 
+		catch (InstantiationException e)
+		{
+			e.printStackTrace();
+		}
+
+	}
+	
+	public void addContact(String jid, String name, String group)
+	{
+		/**
+		 * @Author(s) : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+		 * @Date : 24/04/2009
+		 * @Brief : Adiciona novos contatos e ja pede autorizacao.
+		 */
+		try
+		{
+			JID Jid = JID.checkedJIDFromString(jid);
+			backend.send(new Presence(Jid, "subscribe"));
+			RosterBuilder rb = new RosterBuilder();
+			RosterItemBuilder rib = new RosterItemBuilder();
+			rib.addGroup(group);
+			rib.jid = Jid;
+			rib.name = name;
+			rib.ask = "subscribe";
+			rib.subscription = "none";
+			rb.addItem(rib.build());
+			InfoQueryBuilder iqb = new InfoQueryBuilder();
+			iqb.addExtension(rb.build());
+			iqb.setType("set");
+			backend.send(iqb.build());
+		}
+		catch(Exception error)
+		{
+			error.printStackTrace();
+		}
+	}
+	
+	public void login(String username,String server,String password)
+	{
+		if( backend.isLoggedIn() )
+		{
+			JID j = backend.getMyJID();
+			
+			if( j.getDomain().equals(server) && j.getUser().equals(username) )
+				return;
+		}
+		secureMethodInvoker.addData(new Object[]{"login",username,server,password});
+	}
+	
+	public static Class getPlugin(String name)
+	{
+		try
+        {
+            return Class.forName("nu.fw.jeti.plugins."+name+".Plugin");
+		}
+        catch (ClassNotFoundException e)
+		{
+			System.err.println(MessageFormat.format(I18N.gettext("main.error.{0}_plugin_not_found"), new Object[]{name}));
+			return null;
+		}
+	}
+	
+	public static void getPrefPanel(String name,Map loadedPreferencePanels)
+	{
+		Class prefPanel = null;
+		try
+		{
+			prefPanel = Class.forName("nu.fw.jeti.plugins."+name+".PrefPanel");
+		}
+		catch (ClassNotFoundException e)
+		{
+			return;
+		}
+		loadedPreferencePanels.put(name,prefPanel);
+	}
+	
+	static class SecureMethodRunner extends Thread
+	{
+		private Backend backend;
+		private LinkedList queue = new LinkedList();
+		private volatile boolean isRunning=true;
+
+	    SecureMethodRunner(Backend backend)
+		{
+	    	this.backend = backend;
+		}
+		
+	    public void addData(Object method)
+	    {
+	    	synchronized(queue)
+			{
+				queue.addLast(method);
+				queue.notifyAll();
+			}
+	    }
+	    
+	    public void stopRunning()
+	    {
+	    	isRunning = false;
+			synchronized(queue){queue.notifyAll();}
+	    }
+
+	    public void run()
+		{
+			Object[] method;
+			while (isRunning)
+			{
+				synchronized (queue)
+				{
+					if (queue.isEmpty())
+					{
+						try
+						{
+							queue.wait();
+						}
+						catch (InterruptedException e)
+						{
+							//bug when thrown? called when interrupted
+							e.printStackTrace();
+							return;
+						}
+						continue;
+					}
+					method = (Object[]) queue.removeFirst();
+				}
+
+				String m = (String)method[0];
+				
+				if( m.equals("openChat") )
+					backend.getMain().startChat((JID)method[1]);
+			}
+		}
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/applet/LoadGroupChat.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/applet/LoadGroupChat.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/applet/LoadGroupChat.java	(revision 3952)
@@ -0,0 +1,78 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 26-okt-2005
+ */
+
+
+
+/*
+  Classe Comentada - Retirado o pacote java plugins 
+  Date : 31/03/2009
+
+
+package nu.fw.jeti.applet;
+
+import javax.swing.JMenu;
+import javax.swing.JPanel;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.Message;
+
+import nu.fw.jeti.plugins.groupchat.GroupchatSignin;
+import nu.fw.jeti.plugins.groupchat.GroupchatWindow;
+import nu.fw.jeti.plugins.groupchat.Plugin;
+import nu.fw.jeti.plugins.groupchat.elements.XMUCUserInvite;
+
+public class LoadGroupChat
+{
+	
+	  public static JPanel createGroupChatWindow(JID jid)
+	  {
+	  	return Plugin.createGroupChatWindow(jid);
+	  }
+	  
+	  public static JMenu[] getMenu(JPanel gw)
+	  {
+		  return ((GroupchatWindow)gw).getMenu();
+	  }
+
+	  public static void startGroupchat(JID jid,Backend backend)
+	  {
+		  Plugin.startGroupchat(jid,backend);
+	  }
+	  
+	  public static void signin(Backend backend, String server)
+	  {
+		  GroupchatSignin g = new GroupchatSignin(backend);
+		  g.setChatRoomServer(server);
+		  g.setVisible(true);
+	  }
+	  
+	  public static void invite(Backend backend, JID jid,JID roomJID)
+	  {
+		  backend.send(new Message(null,roomJID,new XMUCUserInvite(jid,null)));
+	  }
+
+
+}
+*/
Index: 3thparty/jmessenger/src/nu/fw/jeti/images/status.cfg
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/images/status.cfg	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/images/status.cfg	(revision 3952)
@@ -0,0 +1,9 @@
+default
+status/ask question.gif
+status/away away.png
+status/chat chat.png
+status/dnd dnd.png
+status/invisible invisible.png
+status/offline unavailable.png
+status/online available.png
+status/xa xa.png
Index: 3thparty/jmessenger/src/nu/fw/jeti/images/StatusIcons.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/images/StatusIcons.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/images/StatusIcons.java	(revision 3952)
@@ -0,0 +1,152 @@
+package nu.fw.jeti.images;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.Presence;
+
+public class StatusIcons
+{
+	private static Map statusIcons;
+
+	public StatusIcons()
+    {
+		if( statusIcons == null )
+		{
+			statusIcons = new HashMap();
+
+			String urlString = getClass().getResource("StatusIcons.class").toString();
+			try
+			{
+				urlString = urlString.substring(0,urlString.lastIndexOf("StatusIcons.class"));
+				readImages(new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("status.cfg"))),new URL(urlString));
+				readImages(new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("images.cfg"))),new URL(urlString));
+			}
+			catch(MalformedURLException e)
+			{
+				e.printStackTrace();
+			}
+			catch(NullPointerException e)
+			{
+				System.err.println("nu/fw/jeti/images/Status.cfg or images.cfg missing");
+			}
+		}
+	}
+	
+	public static ImageIcon getOfflineIcon()
+	{
+		return getStatusIcon(Presence.UNAVAILABLE ,"default");
+	}
+	
+	public static ImageIcon getStatusIcon(int show)
+	{
+		return getStatusIcon(show,"");
+	}
+
+	/** verander
+	 * calls getImageIcon(show) whith the default imageset
+	 * @param show
+	 * @return ImageIcon
+	 */
+	public static ImageIcon getImageIcon(String show)
+	{
+		return getStatusIcon(show,"images");
+	}
+
+	/**
+	 * returns a status icon
+	 * @param show one of chat,available,dnd,away,xa or unavailable
+	 * @param type which image set
+	 * @return ImageIcon
+	 */
+	public static ImageIcon getStatusIcon(int show,String type)
+	{
+		switch (show)
+		{
+			case Presence.FREE_FOR_CHAT: return  getStatusIcon("status/chat",type);
+			case Presence.AWAY: return  getStatusIcon("status/away",type);
+			case Presence.XA: return  getStatusIcon("status/xa",type);
+			case Presence.DND: return  getStatusIcon("status/dnd",type);
+			case Presence.UNAVAILABLE: return  getStatusIcon("status/offline",type);
+			case Presence.INVISIBLE: return  getStatusIcon("status/invisible",type);
+			case Presence.NONE: return  getStatusIcon("status/ask",type);
+			default: return getStatusIcon("status/online",type);
+		}	
+	}
+	
+	public static ImageIcon getStatusIcon(JIDStatus jidStatus)
+	{
+		return getStatusIcon(jidStatus.getShow(),jidStatus.getType());
+	}
+	
+	private static ImageIcon getStatusIcon(String show,String type)
+	{
+
+		if ( !statusIcons.containsKey(type) ) 
+			type = "default";
+		
+		ImageIcon icon = (ImageIcon)((Map)statusIcons.get(type)).get(show);
+		
+		if (icon == null)
+			icon = (ImageIcon)((Map)statusIcons.get("default")).get(show);
+		
+		return icon;
+	}
+
+	private void readImages(BufferedReader data,URL file)
+	{
+	    try
+		{
+			Map map = new HashMap();
+			statusIcons.put(data.readLine(),map);
+			while(true)
+			{
+				readSmilie(data,file,map);
+			}
+		}
+		catch(EOFException e)
+		{
+			//e.printStackTrace();
+		}
+		catch (IOException e2)
+		{
+		    //e2.printStackTrace();
+		}
+	}
+
+	private void readSmilie(BufferedReader data,URL file,Map current) throws IOException
+	{
+		StringBuffer buffer = new StringBuffer();
+		
+		while( true )
+		{
+			int in2 = data.read();
+			if( in2 == -1) throw new EOFException();
+			char ca =(char)in2;
+			if ( ca == ' ')
+			{
+				current.put(new String(buffer),readImage(data,file));
+				return;
+			}
+			buffer.append(ca);
+		}
+	}
+
+	private Icon readImage(BufferedReader data,URL file) throws IOException
+	{
+		return new ImageIcon( new URL(file + data.readLine()) );
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/images/images.cfg
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/images/images.cfg	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/images/images.cfg	(revision 3952)
@@ -0,0 +1,7 @@
+images
+arrowUp pijlomhoog.gif
+arrowDown pijlomlaag.gif
+jeti jeti.png
+keys keys.png
+multiple multiple.gif
+status/unknown question.gif
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XDataHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XDataHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XDataHandler.java	(revision 3952)
@@ -0,0 +1,150 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *
+ *	Created on 27-jul-2003
+ */
+
+package nu.fw.jeti.jabber.handlers;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.xml.sax.Attributes;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XDataBuilder;
+import nu.fw.jeti.jabber.elements.XDataFieldBuilder;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class XDataHandler extends ExtensionHandler
+{//add item and reported
+	private XDataBuilder builder;
+	private XDataFieldBuilder fieldBuilder;
+	private String label;
+	private String value;
+	private boolean inOption;
+	private boolean multiple;
+	private boolean reported;
+	private List items;
+	
+	public XDataHandler()
+	{
+		builder = new XDataBuilder();
+		fieldBuilder = new XDataFieldBuilder();
+	}
+
+	public void startHandling(Attributes attr)
+	{
+		inOption=false;
+		label=null;
+		value=null;
+		multiple = false;
+		reported=false;
+		builder.reset();
+		builder.type = attr.getValue("type");
+	}
+
+	public void startElement(String name,Attributes attr)
+	{
+		if(name.equals("reported"))
+		{
+			multiple = true;
+			reported=true;
+		}
+		if(name.equals("item"))items = new LinkedList();
+		if(name.equals("field"))
+		{
+			fieldBuilder.reset();
+			fieldBuilder.label =attr.getValue("label");
+			fieldBuilder.var = attr.getValue("var");
+			fieldBuilder.type = attr.getValue("type");
+		}
+		else if(name.equals("option"))
+		{
+			inOption = true;
+			label = attr.getValue("label");
+		}
+		//else if(!name.equals("title") || !name.equals("instructions")  ) nu.fw.jeti.util.Log.notParsedXML("roster " + name);
+	}
+
+	public void endElement(String name)
+	{
+		if(name.equals("field"))
+		{
+			if(reported)builder.addReportedField(fieldBuilder.build());
+			else if(multiple && items != null) items.add(fieldBuilder.build());
+			else builder.addField(fieldBuilder.build());
+		}
+		else if(name.equals("title"))
+		{
+			builder.title = getText();  
+		}
+		else if(name.equals("instructions"))
+		{
+			builder.addInstructions(getText());
+		}
+		else if(name.equals("desc"))
+		{
+			fieldBuilder.addDescription(getText()); 
+		}
+		else if(name.equals("required"))
+		{
+			fieldBuilder.required = true;
+		}
+		else if(name.equals("option"))
+		{
+			fieldBuilder.addOption(label,value);
+			inOption=false;
+			label=null;
+			value=null;
+		}
+		else if(name.equals("value"))
+		{
+			if(inOption == true) value = getText();
+			else fieldBuilder.addValue(getText()); 
+		}
+		else if (name.equals("reported"))
+		{
+			reported = false;
+		}
+		else if (name.equals("item"))
+		{
+			builder.addItem(items);
+		}
+		else nu.fw.jeti.util.Log.notParsedXML("XData " + name + getText());
+		clearCurrentChars();
+	}
+
+	public Extension build() throws InstantiationException 
+	{
+		Extension e = builder.build();
+		builder.reset();
+		return e;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQXOOBHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQXOOBHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQXOOBHandler.java	(revision 3952)
@@ -0,0 +1,59 @@
+package nu.fw.jeti.jabber.handlers;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQXOOB;
+import nu.fw.jeti.util.I18N;
+
+import org.xml.sax.Attributes;
+
+/**
+ * Created on 1-mrt-2003
+ * @author E.S. de Boer
+ *
+ */
+public class IQXOOBHandler extends ExtensionHandler
+{
+	private String url;
+	private String description;
+
+
+	public void startHandling(Attributes attr)
+	{
+		url = null;
+		description = null;
+	}
+
+	public void endElement(String name)
+	{
+		if (name.equals("url")) url = getText();
+		else if (name.equals("desc")) description  = getText();
+		else nu.fw.jeti.util.Log.notParsedXML("OOB" + name + getText());
+		clearCurrentChars();
+	}
+
+
+
+	public Extension build() throws InstantiationException
+	{
+		try
+		{
+			return new IQXOOB(new URL(url),description);
+		}
+		catch (MalformedURLException e)
+		{
+			throw new InstantiationException(I18N.gettext("main.error.invalid_url_in_OOB_packet")); 
+		}
+	}
+
+	
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/PresenceHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/PresenceHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/PresenceHandler.java	(revision 3952)
@@ -0,0 +1,47 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.*;
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class PresenceHandler extends PacketHandler
+{
+    public PresenceHandler()
+    {
+		super(new PresenceBuilder());
+    }
+
+	public void startHandling(Attributes attr)
+	{
+	    builder.reset();
+		super.startHandling(attr);
+		((PresenceBuilder)builder).type = attr.getValue("type");
+	}
+
+	public void endElement(String name)
+	{
+		super.endElement(name);
+		if("show".equals(name)) {
+            ((PresenceBuilder)builder).showAsString = getText();
+        } else if("status".equals(name)) {
+            ((PresenceBuilder)builder).status = getText();
+		} else if("priority".equals(name)) {
+            ((PresenceBuilder)builder).priorityAsString = getText();
+        } else if("error".equals(name)) {
+            // Do Nothing
+        } else {
+            nu.fw.jeti.util.Log.notParsedXML("presence " + name + getText());
+        }
+		clearCurrentChars();
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQPrivateHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQPrivateHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQPrivateHandler.java	(revision 3952)
@@ -0,0 +1,47 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQPrivate;
+import nu.fw.jeti.jabber.elements.IQXExtension;
+
+import org.xml.sax.Attributes;
+
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQPrivateHandler extends ExtensionHandler
+{
+	private Extension extension;
+
+	public void startHandling(Attributes attr)
+	{
+		reset();
+	}
+
+	private void reset()
+	{
+		extension = null;
+
+	}
+
+	public void addExtension(Extension extension)
+	{
+		this.extension = extension;
+	}
+
+	public Extension build()
+	{
+		Extension e = new IQPrivate((IQXExtension)extension);
+		reset();
+		return e;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/StreamErrorHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/StreamErrorHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/StreamErrorHandler.java	(revision 3952)
@@ -0,0 +1,82 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 18-sep-2005
+ */
+
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.Packet;
+import nu.fw.jeti.jabber.elements.StreamError;
+import nu.fw.jeti.jabber.elements.XMPPErrorTag;
+
+import org.xml.sax.Attributes;
+
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class StreamErrorHandler extends PacketHandler
+{
+	private XMPPErrorTag extension;
+	
+	public void startHandling(Attributes attr)
+	{
+		reset();
+	}
+
+	private void reset()
+	{
+		extension = null;
+	}
+	
+	public void startElement(String name,Attributes attr) {}
+
+	public void endElement(String name)
+	{
+			clearCurrentChars();
+	}
+
+	public void addExtension(Extension extension)
+	{
+		if(extension instanceof XMPPErrorTag)
+		{
+			XMPPErrorTag tag = (XMPPErrorTag)extension;
+			if(!"text".equals(tag.getError())) this.extension = tag;
+		}
+	}
+
+	public Packet build()
+	{
+		StreamError error = new StreamError(extension);
+		reset();
+		return error;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/PacketHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/PacketHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/PacketHandler.java	(revision 3952)
@@ -0,0 +1,77 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.backend.Connect;
+import nu.fw.jeti.jabber.elements.*;
+import org.xml.sax.Attributes;
+import nu.fw.jeti.jabber.*;
+import nu.fw.jeti.util.I18N;
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public abstract class PacketHandler extends BaseHandler
+{
+	protected PacketBuilder builder;
+
+	public PacketHandler() {}
+	
+	public PacketHandler(PacketBuilder builder)
+    {
+		this.builder = builder;
+    }
+
+	public void startHandling(Attributes attr)
+	{
+		builder.reset();
+		builder.setTo(JID.jidFromString(attr.getValue("to")));
+		builder.setFrom(JID.jidFromString(attr.getValue("from")));
+		builder.setId(attr.getValue("id"));
+	}
+
+	public void startElement(String name,Attributes attr)
+	{
+		if(name.equals("error"))
+		{
+			builder.setErrorType(attr.getValue("type"));
+			String code = attr.getValue("code");
+			try
+			{
+				builder.errorCode = Integer.parseInt(code);
+			}catch(NumberFormatException e){nu.fw.jeti.util.Log.notParsedXML(I18N.gettext("main.error.Illegal_errorcode") + " " +  code);}
+		}
+	}
+
+	public void endElement(String name)
+	{
+		if(name.equals("error"))
+		{
+			builder.errorDescription = getText();
+			clearCurrentChars();
+		}
+	}
+
+	public void addExtension(Extension extension)
+	{
+		builder.addExtension(extension);
+	}
+
+	public Packet build() throws InstantiationException
+	{
+		if(builder.from==null) builder.from = Connect.getMyJID();
+		Packet p = builder.build();
+		builder.reset();
+	    return p;
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQDiscoInfoHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQDiscoInfoHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQDiscoInfoHandler.java	(revision 3952)
@@ -0,0 +1,82 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.jabber.handlers;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.DiscoIdentity;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQBrowseBuilder;
+import nu.fw.jeti.jabber.elements.IQDiscoInfo;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ */
+// created on 6-aug-2004
+public class IQDiscoInfoHandler extends ExtensionHandler
+{
+	private String node;
+	private List features;
+	private List identities;
+
+	public IQDiscoInfoHandler(){}
+
+	public void startHandling(Attributes attr)
+	{
+		features=null;
+		identities=null;
+		node = attr.getValue("node");
+		//if(getName().equals("item")) parseStartElement(attr.getValue("category"),attr);
+		//else parseStartElement(getName(),attr);
+	}
+
+	public void startElement(String name,Attributes attr)
+	{
+		if (name.equals("feature"))
+		{
+			if(features==null)features = new LinkedList();
+			features.add(attr.getValue("var"));
+		}
+		else if(name.equals("identity"))
+		{
+			if(identities==null)identities = new LinkedList();
+			identities.add(new DiscoIdentity(attr.getValue("category"),attr.getValue("type"),attr.getValue("name")));
+		}
+	}
+
+	public Extension build()
+	{
+		return new IQDiscoInfo(node,identities,features);
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQXCapsHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQXCapsHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQXCapsHandler.java	(revision 3952)
@@ -0,0 +1,44 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQLast;
+import nu.fw.jeti.jabber.elements.IQXCaps;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ */
+public class IQXCapsHandler extends ExtensionHandler
+{
+	private String node;
+	private String ver;
+	private String ext;
+		
+	public void startHandling(Attributes attr)
+	{
+		reset();
+		node = attr.getValue("node");
+		ver= attr.getValue("ver");
+		ext = attr.getValue("ext");
+	}
+
+	private void reset()
+	{
+		node=ver=ext=null;
+	}
+
+	public Extension build()
+	{
+		Extension e = new IQXCaps(node,ver,ext); 
+		reset();
+		return e;
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/BaseHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/BaseHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/BaseHandler.java	(revision 3952)
@@ -0,0 +1,49 @@
+package nu.fw.jeti.jabber.handlers;
+import org.xml.sax.Attributes;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+abstract public class BaseHandler
+{
+	private StringBuffer currentChars = new StringBuffer();
+
+    public void characters(String text)
+	{// append text to buffer because parser likes to cut text
+		currentChars.append(text);
+	}
+
+	public String getText()
+	{//return text between elements
+	    return currentChars.toString().trim();
+	}
+	
+	public StringBuffer getUntrimmedText()
+	{//return text between elements
+	    return currentChars;
+	}
+
+	public void clearCurrentChars()
+	{
+	    currentChars = new StringBuffer();
+	}
+
+	abstract public void startHandling(Attributes attr);
+
+	//abstract public void build();
+
+	public void endElement(String name){clearCurrentChars();}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/ExtensionHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/ExtensionHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/ExtensionHandler.java	(revision 3952)
@@ -0,0 +1,65 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.util.Log;
+
+import org.xml.sax.Attributes;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public abstract class ExtensionHandler extends BaseHandler
+{
+	private ExtensionHandler extensionHandler;
+	private String name;//start tag name
+	private int depth;
+
+	public ExtensionHandler getParent()
+	{
+	    return extensionHandler;
+	}
+
+	public void setParent(ExtensionHandler handler)
+	{
+	    extensionHandler = handler;
+	}
+
+
+	public void setName(String name)
+	{
+	    this.name =name;
+	}
+
+	public String getName()
+	{
+	    return name;
+	}
+
+	public void up(){depth++;}
+
+	public void down(){depth--;}
+
+	public boolean isTop(){return depth == 0;}
+
+	public void addExtension(Extension extension)
+	{//overide for extension embedded in extension
+	    Log.notParsedXML("this extension does not support embedded extensions");
+	}
+
+	abstract public Extension build() throws InstantiationException;
+
+	public void startElement(String name,Attributes attr){}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XMessageEventHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XMessageEventHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XMessageEventHandler.java	(revision 3952)
@@ -0,0 +1,48 @@
+package nu.fw.jeti.jabber.handlers;
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XMessageEventBuilder;
+import org.xml.sax.Attributes;
+
+
+public class XMessageEventHandler extends ExtensionHandler
+{
+    private XMessageEventBuilder builder;
+
+    public XMessageEventHandler()
+    {
+		builder=new XMessageEventBuilder();
+    }
+
+    public void startHandling(Attributes attributes)
+	{
+		builder.reset();
+    }
+
+	public void endElement(String name)
+	{
+		if (name.equals("id")) builder.setID(getText());
+		//else if (name.equals("type"))
+		else builder.setType(name);
+		//else nu.fw.jeti.util.Log.notParsedXML("messageEvent " + name + getText());
+		clearCurrentChars();
+	}
+
+	public Extension build()
+	{
+		return builder.build();
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/RosterHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/RosterHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/RosterHandler.java	(revision 3952)
@@ -0,0 +1,73 @@
+package nu.fw.jeti.jabber.handlers;
+import nu.fw.jeti.jabber.elements.RosterBuilder;
+import nu.fw.jeti.jabber.elements.RosterItemBuilder;
+import org.xml.sax.Attributes;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class RosterHandler extends ExtensionHandler
+{
+	private RosterBuilder builder;
+	private RosterItemBuilder itemBuilder;
+
+    public RosterHandler()
+    {
+		builder = new RosterBuilder();
+		itemBuilder = new RosterItemBuilder();
+    }
+
+	public void startHandling(Attributes attr)
+	{
+	    builder.reset();
+	}
+
+	public void startElement(String name,Attributes attr)
+	{
+		if(name.equals("item"))
+		{
+			itemBuilder.reset();
+			//jid not checked because you can't delete it then?
+			itemBuilder.jid = JID.jidFromString(attr.getValue("jid"));
+			itemBuilder.subscription =attr.getValue("subscription");
+			itemBuilder.name = attr.getValue("name");
+			itemBuilder.ask = attr.getValue("ask");
+		}
+		else if (!name.equals("group")) nu.fw.jeti.util.Log.notParsedXML("roster " + name);
+	}
+
+	public void endElement(String name)
+	{
+		if(name.equals("item"))
+		{
+			try{
+		        builder.addItem(itemBuilder.build());
+			}catch (InstantiationException e) {nu.fw.jeti.util.Log.xmlParseException(e);}
+		}
+		else if (name.equals("group")) itemBuilder.addGroup(getText());
+		else nu.fw.jeti.util.Log.notParsedXML("Roster " + name + " " + getText());
+		clearCurrentChars();
+	}
+	
+	public Extension build()
+	{
+		Extension e = builder.build();
+		builder.reset();
+	    return e;
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQRegisterHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQRegisterHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQRegisterHandler.java	(revision 3952)
@@ -0,0 +1,68 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQRegister;
+import nu.fw.jeti.jabber.elements.XData;
+
+import org.xml.sax.Attributes;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQRegisterHandler extends ExtensionHandler
+{
+	private boolean remove=false;
+	private Map fields;
+	private XData xdata;//xdata
+
+	public void startHandling(Attributes attr)
+	{
+		reset();
+	}
+
+	private void reset()
+	{
+		remove=false;
+		fields =null;
+		xdata = null;
+	}
+
+	public void endElement(String name)
+	{//parse everything
+		if("remove".equals(name)) remove =true;
+		else
+		{
+			if(fields == null) fields = new LinkedHashMap(14);
+			fields.put(name,getText());
+		}
+		//else util.Log.notParsedXML("iq:auth " + name + getText());
+		clearCurrentChars();
+	}
+	
+	public void addExtension(Extension extension)
+	{
+		if(extension instanceof XData) xdata = (XData) extension;
+	}
+
+	public Extension build()
+	{
+		Extension e = null;
+		if(xdata != null)
+		{
+			e = new IQRegister(remove,fields,xdata);
+		}
+		else e = new IQRegister(remove,fields);
+		reset();
+		return e;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQVersionHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQVersionHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQVersionHandler.java	(revision 3952)
@@ -0,0 +1,67 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQVersion;
+import org.xml.sax.Attributes;
+
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2002 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+public class IQVersionHandler extends ExtensionHandler
+{
+	private String os;
+	private String name;
+	private String version;
+
+	public void startHandling(Attributes attr)
+	{
+	    reset();
+	}
+
+	private void reset()
+	{
+	    os=name=version=null;
+
+	}
+
+	public void endElement(String name)
+	{
+		if("os".equals(name)) os = getText();
+		else if("name".equals(name)) this.name = getText();
+		else if("version".equals(name)) version = getText();
+		else nu.fw.jeti.util.Log.notParsedXML("iq:auth " + name + getText());
+		clearCurrentChars();
+	}
+
+	public Extension build()
+	{
+		Extension e = new IQVersion(name,version ,os);
+		reset();
+	    return e;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/InfoQueryHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/InfoQueryHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/InfoQueryHandler.java	(revision 3952)
@@ -0,0 +1,35 @@
+package nu.fw.jeti.jabber.handlers;
+
+import org.xml.sax.Attributes;
+import nu.fw.jeti.jabber.elements.InfoQueryBuilder;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class InfoQueryHandler extends PacketHandler
+{
+
+    public InfoQueryHandler()
+    {
+		super(new InfoQueryBuilder());
+    }
+
+	public void startHandling(Attributes attr)
+	{
+	    builder.reset();
+		super.startHandling(attr);
+		((InfoQueryBuilder)builder).setType(attr.getValue("type"));
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/MessageHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/MessageHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/MessageHandler.java	(revision 3952)
@@ -0,0 +1,45 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.*;
+import org.xml.sax.Attributes;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class MessageHandler extends PacketHandler
+{
+    public MessageHandler()
+    {
+		super(new MessageBuilder());
+    }
+
+	public void startHandling(Attributes attr)
+	{
+	    builder.reset();
+		super.startHandling(attr);
+		((MessageBuilder)builder).type = attr.getValue("type");
+	}
+
+	public void endElement(String name)
+	{
+		super.endElement(name);
+		if(name.equals("error")){}
+		else if(name.equals("body")){((MessageBuilder)builder).body = getText();}
+		else if(name.equals("thread")){((MessageBuilder)builder).thread = getText();}
+		else if(name.equals("subject")){((MessageBuilder)builder).subject = getText();}
+		else nu.fw.jeti.util.Log.notParsedXML("message " + name + getText());
+		clearCurrentChars();
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/JetiPrivateRosterHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/JetiPrivateRosterHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/JetiPrivateRosterHandler.java	(revision 3952)
@@ -0,0 +1,63 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ *
+ *	Created on 15-nov-2003
+ */
+ 
+package nu.fw.jeti.jabber.handlers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.JetiPrivateRosterExtension;
+
+import org.xml.sax.Attributes;
+
+
+public class JetiPrivateRosterHandler extends ExtensionHandler
+{
+	private List tempList;
+	//private String xmlVersion;
+
+	public void startHandling(Attributes attributes)
+	{
+		tempList = new ArrayList(10);
+		//xmlVersion = attributes.getValue("xmlVersion");
+	}
+
+	public void startElement(String name,Attributes attributes)
+	{
+		tempList.add(name);
+	}
+
+
+	public Extension build()
+	{
+		return new JetiPrivateRosterExtension((String[])tempList.toArray(new String[] {}));
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/JetiPrivateHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/JetiPrivateHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/JetiPrivateHandler.java	(revision 3952)
@@ -0,0 +1,89 @@
+package nu.fw.jeti.jabber.handlers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.JetiExtensionBuilder;
+
+import org.xml.sax.Attributes;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+
+
+
+public class JetiPrivateHandler extends ExtensionHandler
+{
+	private JetiExtensionBuilder builder;
+
+	private List tempList;
+
+	public JetiPrivateHandler()
+	{
+		builder=new JetiExtensionBuilder();
+	}
+
+	public void startHandling(Attributes attributes)
+	{
+		builder.reset();
+		tempList = new ArrayList(10);
+		builder.reset();
+		//attributes.
+		for (int i = 0; i < attributes.getLength(); i++)
+		{
+			String name = attributes.getQName(i);
+			if(name.equals("xmlVersion")) builder.setXmlVersion(attributes.getValue(i));
+			else if(!name.equals("xmlns") && !attributes.getValue(i).equals("")) builder.put(name,attributes.getValue(i));
+		}
+	}
+
+	public void startElement(String name,Attributes attributes)
+	{// in attributes in volgede tags?
+		//we have no attributes, so all we do is make sure the buffer is
+		//reset for reading in character data.
+		if(!name.equals("status"))
+		{
+			tempList = new ArrayList(10);
+			//System.out.println("we hebben een pref start element");
+			for (int i = 0; i < attributes.getLength(); i++)
+			{
+				//String name2 = attributes.getName(i);
+				//System.out.println(name2  +" : " +attributes.getValue(i) );
+				tempList.add(attributes.getValue(i));
+				//if(!name.equals("xmlns")) builder.put(name,attributes.getValue(i));
+			}
+		}
+	}
+
+	public void endElement(String name)
+	{// element = tussen tags
+		//System.out.println("end element name" + name);
+		if(!name.equals("status"))
+		{
+			//Collections.reverse(tempList);
+			builder.putMessages(name,tempList);
+		}
+		//System.out.println(new String(elementChars));
+	}
+
+	public Extension build()
+	{
+		Extension e = builder.build();
+		builder.reset();
+		return e;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XDelayHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XDelayHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XDelayHandler.java	(revision 3952)
@@ -0,0 +1,55 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XDelay;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public class XDelayHandler extends ExtensionHandler
+{
+	private String stamp;
+	private JID from;
+	
+	public void startHandling(Attributes attr)
+	{
+		reset();
+		from = JID.jidFromString(attr.getValue("from"));
+		stamp = attr.getValue("stamp");
+	}
+
+
+
+	private void reset()
+	{
+		stamp=null;
+		from=null;
+
+	}
+
+//	public void endElement(String name)
+//	{
+//		if("from".equals(name)) from = JID.jidFromString(getText());
+//		else if("stamp".equals(name)) stamp = getText();
+//		else util.Log.notParsedXML("x:delay " + name + getText());
+//		clearCurrentChars();
+//	}
+
+	public Extension build()
+	{
+		Extension e = new XDelay(stamp,from);
+		reset();
+		return e;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/UnknownPacketHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/UnknownPacketHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/UnknownPacketHandler.java	(revision 3952)
@@ -0,0 +1,41 @@
+package nu.fw.jeti.jabber.handlers;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Log;
+import org.xml.sax.Attributes;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class UnknownPacketHandler extends PacketHandler
+{
+
+    public UnknownPacketHandler(){}
+
+	public void startHandling(Attributes attr)
+	{}
+
+	public void endElement(String name)
+	{
+		Log.notParsedXML(name);
+		Log.notParsedXML(getText());
+		clearCurrentChars();
+	}
+	
+	public void addExtension(Extension extension) {}
+
+	public nu.fw.jeti.jabber.elements.Packet build() throws InstantiationException
+	{throw new InstantiationException(I18N.gettext("main.error.Unknown_Packet"));}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQTimeHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQTimeHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQTimeHandler.java	(revision 3952)
@@ -0,0 +1,50 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQTime;
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQTimeHandler extends ExtensionHandler
+{
+	private String tz;
+	private String utc;
+	private String display;
+
+	public void startHandling(Attributes attr)
+	{
+	    reset();
+	}
+
+	private void reset()
+	{
+	    tz=utc=display=null;
+
+	}
+
+	public void endElement(String name)
+	{
+		if("tz".equals(name)) tz = getText();
+		else if("utc".equals(name)) utc = getText();
+		else if("display".equals(name)) display = getText();
+		else nu.fw.jeti.util.Log.notParsedXML("iq:time " + name + getText());
+		clearCurrentChars();
+	}
+
+	public Extension build()
+	{
+		Extension e = new IQTime(utc,tz,display);
+		reset();
+	    return e;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQBrowseHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQBrowseHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQBrowseHandler.java	(revision 3952)
@@ -0,0 +1,87 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQBrowseBuilder;
+
+import org.xml.sax.Attributes;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQBrowseHandler extends ExtensionHandler
+{
+	private IQBrowseBuilder iqBuilder;
+	private IQBrowseBuilder itemBuilder;
+	private IQBrowseBuilder currentBuilder;
+
+	public IQBrowseHandler()
+	{
+		iqBuilder = new IQBrowseBuilder();
+		itemBuilder = new IQBrowseBuilder();
+		currentBuilder = iqBuilder;
+	}
+
+	public void startHandling(Attributes attr)
+	{
+		iqBuilder.reset();
+		if(getName().equals("item")) parseStartElement(attr.getValue("category"),attr);
+		//backwards compatible:
+		//else if(getName().equals("service")) parseStartElement(attr.getValue("service"),attr);
+		//else if(getName().equals("conference")) parseStartElement(attr.getValue("conference"),attr);
+		//else if(getName().equals("user")) parseStartElement(attr.getValue("user"),attr);
+		else parseStartElement(getName(),attr);
+	}
+
+	private void parseStartElement(String category, Attributes attr)
+	{
+		String jid = attr.getValue("jid");
+		if(jid != null)	currentBuilder.setJID(JID.jidFromString(jid));
+		currentBuilder.setCategory(category);
+		currentBuilder.setType(attr.getValue("type"));
+		currentBuilder.setName(attr.getValue("name"));
+		currentBuilder.setVersion(attr.getValue("version"));
+	}
+
+	public void startElement(String name,Attributes attr)
+	{
+		if (name.equals("ns")) return;
+		currentBuilder = itemBuilder;
+		itemBuilder.reset();
+		if(name.equals("item"))	parseStartElement(attr.getValue("category"),attr);
+		else parseStartElement(name,attr);
+	}
+
+	public void endElement(String name)
+	{
+		if(name.equals("ns")) currentBuilder.addNamespace(getText());
+		else
+		{
+			iqBuilder.addItem(itemBuilder.build());
+			currentBuilder = iqBuilder;
+		}
+		//else util.Log.notParsedXML("IQBrowse " + name + getText());
+		clearCurrentChars();
+	}
+
+	public Extension build()
+	{
+        Extension e = null;
+        e = iqBuilder.build();
+        iqBuilder.reset();
+		return e;
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQAuthHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQAuthHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQAuthHandler.java	(revision 3952)
@@ -0,0 +1,55 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.*;
+import org.xml.sax.Attributes;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQAuthHandler extends ExtensionHandler
+{
+	private IQAuthBuilder builder;
+
+    public IQAuthHandler()
+    {
+		builder = new IQAuthBuilder();
+    }
+
+	public void startHandling(Attributes attr)
+	{
+	    builder.reset();
+	}
+
+	public void endElement(String name)
+	{
+		if("username".equals(name)) builder.username = getText();
+		else if("password".equals(name)) builder.password = getText();
+		else if("digest".equals(name)) builder.digest = getText();
+		else if("resource".equals(name)) builder.resource = getText();
+		else if("token".equals(name)) builder.zeroKToken = getText();
+		else if("sequence".equals(name)) builder.zeroKSequence = getText();
+		else if("hash".equals(name)) builder.zeroKHash = getText();
+		else nu.fw.jeti.util.Log.notParsedXML("iq:auth " + name + getText());
+		clearCurrentChars();
+	}
+
+	public Extension build()
+	{
+		Extension e = builder.build();
+		builder.reset();
+	    return e;
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/UnknownExtensionHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/UnknownExtensionHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/UnknownExtensionHandler.java	(revision 3952)
@@ -0,0 +1,77 @@
+package nu.fw.jeti.jabber.handlers;
+
+import org.xml.sax.Attributes;
+
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Log;
+import nu.fw.jeti.jabber.elements.Extension;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class UnknownExtensionHandler extends ExtensionHandler
+{
+	private StringBuffer xmlText = new StringBuffer();
+
+	public void startHandling(Attributes attr)
+	{
+		xmlText = new StringBuffer();
+		xmlText.append("<");
+		xmlText.append(getName());
+		if (attr != null) {
+			String aName = null;
+			for (int i = 0; i < attr.getLength(); i++) {
+				aName = attr.getQName(i);
+				xmlText.append(" ");
+				xmlText.append(aName+"=\""+attr.getValue(i)+"\"");
+			}
+		}
+		xmlText.append(">");
+	}
+
+	public void startElement(String name,Attributes attr)
+	{
+		xmlText.append("<");
+		xmlText.append(name);
+		if (attr != null) {
+			String aName = null;
+			for (int i = 0; i < attr.getLength(); i++) {
+				aName = attr.getQName(i);
+				xmlText.append(" ");
+				xmlText.append(aName+"=\""+attr.getValue(i)+"\"");
+			}
+		}
+		xmlText.append(">");
+	}
+
+	public void endElement(String name)
+	{
+		xmlText.append(getText());
+		xmlText.append("</");
+		xmlText.append(name);
+		xmlText.append(">");
+
+		clearCurrentChars();
+	}
+
+	public Extension build() throws InstantiationException
+	{
+		xmlText.append("</");
+		xmlText.append(getName());
+		xmlText.append(">");
+		Log.notParsedXML(xmlText.toString());
+		throw new InstantiationException(I18N.gettext("main.error.Unknown_Extension"));
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQLastHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQLastHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQLastHandler.java	(revision 3952)
@@ -0,0 +1,41 @@
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQLast;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public class IQLastHandler extends ExtensionHandler
+{
+	private String seconds;
+		
+	public void startHandling(Attributes attr)
+	{
+		reset();
+		seconds = attr.getValue("seconds");
+	}
+
+	private void reset()
+	{
+		seconds=null;
+	}
+
+	public Extension build()
+	{
+		Extension e = new IQLast(seconds); 
+		reset();
+		return e;
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQDiscoItemsHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQDiscoItemsHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/IQDiscoItemsHandler.java	(revision 3952)
@@ -0,0 +1,82 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.jabber.handlers;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.*;
+import nu.fw.jeti.jabber.elements.DiscoIdentity;
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.IQBrowseBuilder;
+import nu.fw.jeti.jabber.elements.IQDiscoInfo;
+import nu.fw.jeti.util.Log;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author E.S. de Boer
+ */
+// created on 7-aug-2004
+public class IQDiscoItemsHandler extends ExtensionHandler
+{
+	private String node;
+	private List items;
+
+	public IQDiscoItemsHandler(){}
+
+	public void startHandling(Attributes attr)
+	{
+		items=null;
+		node = attr.getValue("node");
+	}
+
+	public void startElement(String name,Attributes attr)
+	{
+		if(name.equals("item"))
+		{
+			if(items==null)items = new LinkedList();
+			try
+			{
+				JID jid = JID.checkedJIDFromString(attr.getValue("jid"));
+				items.add(new DiscoItem(jid,attr.getValue("name"),attr.getValue("node"),attr.getValue("action")));
+			} catch (InstantiationException e)
+			{
+				Log.xmlParseException(e);
+			}
+		}
+	}
+
+	public Extension build()
+	{
+		return new IQDiscoItems(node,items);
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XMPPErrorHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XMPPErrorHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/handlers/XMPPErrorHandler.java	(revision 3952)
@@ -0,0 +1,52 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.jabber.handlers;
+
+import nu.fw.jeti.jabber.elements.Extension;
+import nu.fw.jeti.jabber.elements.XMPPError;
+import nu.fw.jeti.jabber.elements.XMPPErrorTag;
+
+import org.xml.sax.Attributes;
+
+/**
+ * @author M Forssen
+ */
+public class XMPPErrorHandler extends ExtensionHandler
+{
+    public XMPPErrorHandler(){}
+
+    public void startHandling(Attributes attr) {
+    }
+
+    public Extension build()
+    {
+        return new XMPPErrorTag(getName());
+    }
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/XDataPanel.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/XDataPanel.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/XDataPanel.java	(revision 3952)
@@ -0,0 +1,383 @@
+// Created on 28-jul-2003
+package nu.fw.jeti.jabber;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.text.JTextComponent;
+
+import nu.fw.jeti.jabber.elements.XData;
+import nu.fw.jeti.jabber.elements.XDataBuilder;
+import nu.fw.jeti.jabber.elements.XDataField;
+import nu.fw.jeti.util.I18N;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class XDataPanel extends JPanel
+{
+	private boolean editable;
+	private List fields = new LinkedList(); 
+	private XData xdata;
+	private int current;
+		
+	public XDataPanel(XData xdata,final XDataCallback callback)
+	{
+		setLayout(new BorderLayout());
+		this.xdata = xdata;
+		if(xdata.getType().equals("form")) 
+		{
+			editable =true;
+			JPanel panel = new JPanel();
+			JButton btnOk  = new JButton();
+			I18N.setTextAndMnemonic("Send",btnOk);
+			btnOk.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent event)
+				{
+					sendForm(callback); 
+				}
+			});
+			panel.add(btnOk);
+			JButton btnCancel  = new JButton(I18N.gettext("Cancel"));
+			btnCancel.setMnemonic(KeyEvent.VK_ESCAPE);
+			btnCancel.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent event)
+				{
+					callback.cancelForm();  
+				}
+			});
+			panel.add(btnCancel);
+			add(panel,BorderLayout.SOUTH);
+		}
+		if(xdata.getInstructions() !=null)
+		{
+			JTextArea area = new JTextArea(xdata.getInstructions()); 
+			area.setEditable(false);
+			area.setBackground(SystemColor.control); 
+			add(area,BorderLayout.NORTH);
+		}
+		if(xdata.hasItems())
+		{
+			createMulti(xdata.getItems());
+		}
+		else if(xdata.hasFields())
+		{
+			add(new JScrollPane(createFieldsPanel(xdata.getFields())),BorderLayout.CENTER);
+		}
+		
+	}
+		
+	public Iterator getCurrentField()
+	{
+		if(xdata.hasItems())return ((List)xdata.getItems().get(current)).iterator();
+		return xdata.getFields();
+	}
+		
+	//TODO use reported to make fields
+	private JPanel createFieldsPanel(Iterator fields)
+	{
+		JPanel pnlFields = new JPanel(new GridBagLayout());
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.anchor = GridBagConstraints.LINE_START;
+        c.fill = GridBagConstraints.HORIZONTAL;
+        c.insets = new Insets(3, 5, 0, 3);
+		for(Iterator i = fields;i.hasNext();)
+		{
+			XDataField data = (XDataField)i.next();
+			parseField(data, pnlFields, c);
+		}
+        c.gridwidth = 1;
+        c.weighty = 1.0;
+        c.weightx = 1.0;
+        pnlFields.add(Box.createVerticalGlue(), c);
+        return pnlFields;
+	}
+	
+	private void createMulti(final List list)
+	{
+		if(list.size()>1)
+		{
+			JPanel panel = new JPanel();
+			final JButton btnNext = new JButton();
+			final JButton btnPrevious = new JButton();
+			I18N.setTextAndMnemonic("search.Previous", btnPrevious);
+			panel.add(btnPrevious,BorderLayout.EAST);
+			final JLabel title = new JLabel("1/" + list.size());
+			panel.add(title,BorderLayout.CENTER);
+			btnPrevious.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					if(current!=0)current--;
+					if(current==0)btnPrevious.setEnabled(false);
+					if(current==list.size()-2)btnNext.setEnabled(true);
+					title.setText(current + 1 + "/" + list.size());
+					gotoPostion(list);
+				}
+			});
+			I18N.setTextAndMnemonic("search.Next", btnNext);
+			btnNext.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					if(current<list.size()-1)current++;
+					if(current==list.size()-1)btnNext.setEnabled(false);
+					if(current==1)btnPrevious.setEnabled(true);
+					title.setText(current + 1 + "/" + list.size());
+					gotoPostion(list);
+				}
+			});
+			panel.add(btnNext,BorderLayout.EAST);
+			add(panel, BorderLayout.NORTH);
+		}
+		add(new JScrollPane(
+				createFieldsPanel(((List)list.get(0)).iterator()))
+				,BorderLayout.CENTER);
+//		JPanel panel = new JPanel();
+//		panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
+//		add(panel, BorderLayout.CENTER);
+//		makeForm((Map)list.get(0), panel);
+	}
+	
+	private void gotoPostion(List list)
+	{
+		//Map form =(Map) list.get(current);
+		//final JPanel panel = new JPanel();
+		//panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
+		final JPanel panel = createFieldsPanel(
+				((List)list.get(current)).iterator());
+		SwingUtilities.invokeLater(new Runnable()
+		{
+			public void run()
+			{
+				//remove(jPanel2);
+				//jPanel2 = panel;
+				add(panel, BorderLayout.CENTER);
+				validate();
+			}
+		});
+	}
+	
+	private void parseField(XDataField field,JPanel pnlFields, GridBagConstraints c)
+	{//change to implemnt check for required fields?
+		String type = field.getType();
+		String value = field.getValue();
+		String label = field.getLabel();
+		String description = field.getDescription();
+		boolean required = field.getRequired();
+		final String var = field.getVar(); 
+		if(type.equals("boolean"))
+		{
+			String label2;
+			if(label != null) label2 =field.getLabel();
+			else label2 = var; 
+			if(required) label2 = "*" + label2; 
+			JCheckBox chkBox = new JCheckBox(label2);
+			if("0".equals(value))chkBox.setSelected(false);
+			if("1".equals(value))chkBox.setSelected(true); 
+			chkBox.setEnabled(editable);
+			if(description !=null)chkBox.setToolTipText(description);
+			pnlFields.add(chkBox, c);
+			chkBox.setName(var);
+			fields.add(chkBox);
+		}
+		else if (type.equals("fixed"))
+		{
+			JLabel lbl = new JLabel(value);
+			lbl.setAlignmentX(0.0f);
+			if(description !=null) lbl.setToolTipText(description);
+			lbl.setAlignmentX(0.0f);
+			pnlFields.add(lbl, c);
+		}
+		else if (type.equals("list-multi"))
+		{//assmodel
+			addLabel(label,var,description, required, pnlFields, c);
+			JList list = new JList();
+			list.setEnabled(editable);
+			pnlFields.add(list, c);
+			list.setAlignmentX(0.0f);
+			list.setName(var);
+			fields.add(list);
+		}
+		else if (type.equals("list-single")) 
+		{//addcombo
+			addLabel(label,var,description, required,pnlFields, c);
+			if(field.hasOptions())
+			{ 
+				Object[] values = new Object[field.getOptionsSize()];
+				int tel=0;
+				int index = 0;
+				for (Iterator i = field.getOptionsIterator(); i.hasNext();tel++)
+				{
+					Object[] obj = (Object[]) i.next();
+					values[tel] = new LabelValue(obj);
+					if(((String)obj[1]).equals(value)) index = tel;
+										
+				}
+				JComboBox cmbBox = new JComboBox(values);
+				cmbBox.setSelectedIndex(index);
+				cmbBox.setEditable(editable);
+				cmbBox.setAlignmentX(0.0f);
+				pnlFields.add(cmbBox, c);
+				cmbBox.setName(var); 	
+				fields.add(cmbBox);
+			}
+		}
+		else if (type.equals("text-multi"))
+		{
+			addLabel(label,var,description, required,pnlFields, c);
+			final JTextArea area = new JTextArea(value, 2, 20);
+            JScrollPane scrollPane = new JScrollPane(area);
+			area.setEditable(editable);
+			area.setAlignmentX(0.0f);
+			pnlFields.add(scrollPane, c);
+			area.setName(var);
+			fields.add(area);
+		}
+		else if (type.equals("text-private"))
+		{
+			addLabel(label,var,description, required,pnlFields, c);
+			final JPasswordField pass = new JPasswordField(value);
+			pass.setEditable(editable);
+			pass.setAlignmentX(0.0f);
+			pnlFields.add(pass, c);
+			pass.setName(var);
+			fields.add(pass);
+		}
+		else if (type.equals("text-single"))
+		{
+			addLabel(label,var,description, required, pnlFields, c);
+			final JTextField text = new JTextField(value);
+			text.setEditable(editable);
+			text.setAlignmentX(0.0f);
+			pnlFields.add(text, c);
+			text.setName(var); 
+			fields.add(text);
+		}
+		else if (type.equals("jid-multi"))
+		{
+			addLabel(label,var,description, required, pnlFields, c);
+		}
+		else if (type.equals("jid-single"))
+		{
+			addLabel(label,var,description, required,pnlFields, c);
+		}
+		else if (type.equals("hidden"))
+		{
+			fields.add(new Object[]{var,value}); 
+		}
+	}
+	
+	private void addLabel(String label,String var,String description,
+                          boolean required,JPanel panel, GridBagConstraints c)
+	{
+		String label2;
+		if(label != null) label2 =label;
+		else label2 = var;
+		if(required) label2 = "*" + label2;
+		JLabel lbl = new JLabel(label2);
+		if(description !=null) lbl.setToolTipText(description);
+		lbl.setAlignmentX(0.0f);
+		panel.add(lbl, c); 
+	}
+	
+	private void sendForm(XDataCallback callback)
+	{
+		XDataBuilder builder = new XDataBuilder(); 
+		builder.type ="submit"; 
+		for (Iterator iter = fields.iterator(); iter.hasNext();)
+		{
+			Object element = iter.next();
+			String var = null;
+			String value = null;
+			if(element instanceof Object[])
+			{
+				var =(String)((Object[])element)[0];
+				value =(String)((Object[])element)[1];
+				if(value != null)
+				{
+					builder.addField(new XDataField(var,value));
+				}
+			}
+			else
+			{
+				JComponent component = (JComponent)element;
+				var = component.getName();
+				if(component instanceof JCheckBox)
+				{
+					  value = String.valueOf(((JCheckBox)component).isSelected()?1:0);  
+				}
+				else if(component instanceof JTextComponent)
+				{
+					value = ((JTextComponent)component).getText(); 
+				}
+				else if(component instanceof JComboBox)
+				{
+					value =((LabelValue)((JComboBox)component).getSelectedItem()).value; 
+				}
+				if(value != null)
+				{
+					builder.addField(new XDataField(var,value));
+				}
+				if(component instanceof JList)
+				{
+					JList list = ((JList)component);
+					if (!list.isSelectionEmpty())
+					{
+						Object[] values = list.getSelectedValues();
+						for (int i = 0; i < values.length; i++)
+						{
+							builder.addField(new XDataField(var,((LabelValue)values[i]).value));
+						}
+					} 
+		 		}
+			}
+		}
+		try
+		{
+			callback.sendForm(builder.build());
+		}
+		catch (InstantiationException e)
+		{
+			e.printStackTrace();
+		}
+	}
+}
+
+ class LabelValue
+{
+	String label;
+	String value;
+	
+	LabelValue(Object[] obj)
+	{
+		value = (String)obj[1];
+		if(obj[0]==null)label= value;
+		else label =(String) obj[0];
+	}
+	
+	public String toString()
+	{
+		return label;
+	}
+}
+
+
+
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/JID.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/JID.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/JID.java	(revision 3952)
@@ -0,0 +1,221 @@
+package nu.fw.jeti.jabber;
+
+import nu.fw.jeti.util.I18N;
+
+
+/**
+ *
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public final class JID
+{
+	private String domain;
+	private String user;
+	private String resource;
+	
+	private String domainPreped;
+	private String userPreped;
+	private String resourcePreped;
+
+    public JID(String server)
+    {
+//		if(server == null || server.equals("")) throw new NullPointerException(I18N.gettext("main.error.Server_has_no_value"));
+//		if(Utils.isAtleastJava(6)){
+//			domainPreped = JIDPrep.namePrep(server);
+//		}
+//		else
+    	domainPreped = server.toLowerCase();
+		this.domain = server;
+	}
+
+	public JID(String user,String server)
+	{
+	    this(server);
+	    if(user == null) return;
+//	    if(Utils.isAtleastJava(6)){
+//			userPreped = JIDPrep.nodePrep(user);
+//		}
+//	    else
+	    userPreped = user.toLowerCase();
+		this.user = user;
+	}
+
+	public JID(String user,String server,String resource)
+	{
+		this(user,server);
+//		if(Utils.isAtleastJava(6)){
+//			resourcePreped = JIDPrep.resourcePrep(resource);
+//			
+//		}
+//		else
+			resourcePreped = resource;
+		this.resource = resource;
+	}
+
+	public JID()
+	{
+
+	}
+	
+	public static JID jidFromString(String jid)
+	{
+	    if(jid == null || jid.equals("")) return null;
+		String node=null, domain=null, resource=null;
+		int loc = jid.indexOf("@");
+		if (loc != -1)
+		{
+		  node = jid.substring(0, loc);
+		  jid = jid.substring(loc + 1);
+		}
+		loc = jid.indexOf("/");
+		if (loc == -1) domain = jid;
+		else
+		{
+		  domain = jid.substring(0, loc);
+		  resource = jid.substring(loc + 1);
+		}
+		if(domain ==null) return null;
+		return new JID(node,domain,resource);
+	}
+
+	public static JID checkedJIDFromString(String jid) throws InstantiationException
+	{
+	    if(jid == null || jid.equals("")) return null;
+		String node=null, domain=null, resource=null;
+		int loc = jid.indexOf("@");
+		if (loc != -1)
+		{
+		  node = jid.substring(0, loc);
+		  jid = jid.substring(loc + 1);
+		}
+		loc = jid.indexOf("/");
+		if (loc == -1) domain = jid;
+		else
+		{
+		  domain = jid.substring(0, loc);
+		  resource = jid.substring(loc + 1);
+		}
+		if (node != null && node.length() > 255) throw new InstantiationException (I18N.gettext("main.error.Username_>_255_Characters"));
+		if(domain.indexOf("@") != -1) throw new InstantiationException (I18N.gettext("main.error.Server_or_Username_contains_a_'@'"));
+		if(!isValidUser(node)) throw new InstantiationException(I18N.getTextWithAmp("main.error.Username_contains_illegal_chars_(see_english_translation)"));
+		if(!isValidServer(domain)) throw new InstantiationException(I18N.gettext("main.error.Server_must_start_with_a_letter_(see_english_translation)"));
+		return new JID(node,domain,resource);
+	}
+
+	public static boolean isValidUser(String user)
+	{
+		if(user == null) return true;
+		int len = user.length();
+		if (len > 255) return false;
+		char c;
+		for(int i=0; i<len; i++)
+		{
+			c = user.charAt(i);
+			if (c <= ' ') return false;
+			if (c == ':') return false;
+			if (c == '@') return false;
+			if (c == '"') return false;
+			if (c == '>') return false;
+			if (c == '<') return false;
+			if (c == '/') return false;
+			if (c == '\'') return false;
+			if (c == '&') return false;
+			if (c == '\u077F') return false;
+			if (c == '\u0FFE') return false;
+			if (c == '\u0FFF') return false;
+		}
+		return true;
+	}
+
+	//check dns name
+	public static boolean isValidServer(String server)
+	{
+		if(server == null || server.equals("")) return false;
+		int len = server.length();
+		//if (len > 255) return false;
+		//first leter must be alphanumeric
+		if(!Character.isLetterOrDigit(server.charAt(0)))return false;
+		char c;
+		for(int i=0; i<len; i++)
+		{
+			c = server.charAt(i);
+			if(!(Character.isLetterOrDigit(c) || c=='.' || c=='-')) return false;
+		}
+		return true;
+	}
+
+	public String getUser(){return user;}
+
+	public String getDomain(){return domain;}
+
+	public String getResource(){return resource;}
+
+	public String toString()
+	{
+		StringBuffer jid = new StringBuffer();
+		if (user != null){
+			jid.append(user);
+			jid.append("@");
+		}
+		jid.append(domain);
+		if (resource != null){
+			jid.append("/");
+			jid.append(resource);
+		}
+		return jid.toString();
+	}
+
+	public String toStringNoResource()
+	{
+		StringBuffer jid = new StringBuffer();
+		if (user != null){
+			jid.append(user);
+			jid.append("@");
+		}
+		jid.append(domain);
+		return jid.toString();
+	}
+
+
+	
+	/**
+	 * equals doesn't look at resources
+	 * @param jid
+	 * @return boolean
+	 */
+	public boolean equals(JID jid){
+		if (jid == null) return false;
+        return equalsNode(jid) && domainPreped.equals(jid.domainPreped);
+    }
+
+	private boolean equalsNode(JID jid){
+		if (userPreped == null ^ jid.userPreped == null ) return false;
+		if(userPreped == null) return true;
+		return userPreped.equals(jid.userPreped);
+	}
+
+	/**
+	 * equals doesn't look at resources
+	 * @param jid
+	 * @return boolean
+	 */
+	public boolean equals(Object o){
+		if(!(o instanceof JID)) return false;
+		return equals((JID)o);
+	}
+
+	public int hashCode()
+	{
+		int hash = 0;
+		if(userPreped != null) hash = userPreped.hashCode();
+		return hash ^ domainPreped.hashCode();
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/Backend.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/Backend.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/Backend.java	(revision 3952)
@@ -0,0 +1,495 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/im me at jeti@jabber.org
+ */
+package nu.fw.jeti.jabber;
+
+import java.awt.Container;
+import java.awt.Frame;
+import java.awt.Window;
+import java.net.InetAddress;
+import java.util.*;
+
+import javax.swing.JFrame;
+
+import nu.fw.jeti.backend.*;
+import nu.fw.jeti.backend.roster.Roster;
+import nu.fw.jeti.backend.roster.Server;
+import nu.fw.jeti.events.*;
+import nu.fw.jeti.jabber.elements.IQXCaps;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.Message;
+import nu.fw.jeti.jabber.elements.Packet;
+import nu.fw.jeti.jabber.elements.StreamError;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.ui.Jeti;
+
+/**
+ * @author E.S. de Boer
+  */
+
+public class Backend
+{
+    private Map eventListeners;
+	private Map presenceListeners;
+	private Map messageListeners;	
+	private int identifier;
+	private Connect connect;
+	private Roster roster;
+	private Server server;
+	private Jeti main;
+	private JFrame mainFrame;
+	private Container mainWindow;
+	private Start start;
+	private IQTimerQueue iqTimerQueue;
+	private Handlers handlers;
+	private CapabilitiesCache capabilitiesCache;
+	
+	
+    public Backend(Start start)
+    {
+		eventListeners = new HashMap(10);
+		iqTimerQueue = new IQTimerQueue();
+		handlers = new Handlers();
+		connect = new Connect(this,iqTimerQueue,handlers);
+		server =new Server(this);
+		roster = new Roster(this,server);
+		capabilitiesCache = new CapabilitiesCache(this);
+		this.start = start;
+    }
+   
+	public void setMain(Jeti main,Window window, JFrame frame)
+	{
+		this.main = main;
+		this.mainWindow = window;
+		this.mainFrame = frame;
+	}
+
+	public Container getMainWindow()
+	{//TODO bit of a hack, think of something nicer
+		if( mainWindow == null )
+		{
+			mainWindow=main.getTopLevelAncestor();
+		}
+		return mainWindow;
+	}
+	
+	public JFrame getMainFrame()
+	{
+		return mainFrame;
+	}
+	
+	public Jeti getMain(){return main;}
+/*
+	public void setBackend(JabberBackend backend)
+	{
+	    this.backend = backend;
+	}
+*/
+	public void login(LoginInfo loginInfo)
+	{
+		connect.login(loginInfo);
+	}
+	
+	public void autoLogin(LoginInfo loginInfo,int tries)
+	{
+		connect.autoLogin(loginInfo,tries);
+	}
+	
+	public void abortLogin()
+	{
+		connect.abort();
+	}
+
+	public JID getMyJID()
+	{
+		return Connect.getMyJID();
+	}
+
+	/*
+	 * Este método recupera a inteface de rede local, usada para transferir arquivo.
+	 * Previne erro no caso do cliente ter múltiplas interface de rede.
+	 * 
+	 * emmanuel.ferro@serpro.gov.br em 08/10/2010
+	 * 
+	 */
+	public InetAddress getLocalIP()
+	{
+		return connect.getSocket().getLocalAddress();
+	}
+
+	/**
+	 * Checks if the password matches the password used to login on this account
+	 * @param password the password to check
+	 * @return true if the password matches the password of this account
+	 */
+	public boolean isPasswordValid(String password)
+	{
+		return connect.isPasswordValid(password);
+	}
+
+	public synchronized String getIdentifier()
+	{
+		return "JETI_" + identifier++;
+	}
+
+	public String createThread()
+	{
+		return  Integer.toHexString("JETI".hashCode()) + Long.toHexString(System.currentTimeMillis());
+	}
+
+	public String getAccountInfo()
+	{
+		return connect.getAccountInfo();
+	}
+	
+	public Map getAvailableTransports()
+	{
+		return server.getAvailableTransports();
+	}
+
+	public void changeStatus(int show,String status)
+	{
+		connect.changeStatus(show, status);
+	}
+
+	public void rosterLoaded()
+	{
+		connect.connected();
+	}
+
+	public void disconnect()
+	{
+		connect.disconnect();
+	}
+	
+	public boolean isLoggedIn()
+	{
+		return connect.isLoggedIn();
+	}
+
+	public void streamError(StreamError error)
+	{
+		connect.streamError(error);
+	}
+
+	public void exit()
+	{
+		if(!Start.applet)
+		{
+			System.out.println("This window will close in one minute");
+			Timer t = new Timer(true);
+			t.schedule(new TimerTask()
+			{
+				public void run()
+				{
+					System.exit(0);
+				}
+			},10000);
+		}
+				
+		//end connection
+		connect.exit();
+		
+		Frame[] frames = Frame.getFrames();
+		for(int i =0;i<frames.length;i++)
+		{//remove all opened jeti frames
+			String name = frames[i].getClass().getName();
+			System.out.println(name);
+			if(name.startsWith("nu.fw.jeti"))frames[i].dispose();
+			//frames[i].dispose();
+			if(frames[i].isDisplayable())System.out.println(name);
+		}
+		
+		capabilitiesCache.save();
+		//unload plugins
+		start.exit();
+		//remove references'
+//		start=null;
+//		connect=null;
+//		roster=null;
+//		server=null;
+//		main=null;
+//		mainFrame=null;
+//		mainWindow =null;
+		synchronized (this)
+		{
+			eventListeners.clear();
+			if(presenceListeners!=null)presenceListeners.clear();
+			if(messageListeners!=null)messageListeners.clear();
+		}
+	}
+
+	/**
+	 *	send packets
+	 *	please send messages that need to be logged with sendMessage
+	 *  @param packet
+	 */
+	public void send(Packet packet)
+	{
+		connect.send(packet);
+	}
+	
+	/**
+	 * Sends a infoquery and gives the result to listener
+	 * @param query the infoquery to send
+	 * @param listener the IQResultListener wich listens for the result
+	 * @param timeout not yet implemented 
+	 */
+	public void send(InfoQuery query, IQResultListener listener,int timeout)
+	{
+		iqTimerQueue.add(query,listener,timeout);
+		send(query);
+	}
+
+	/**
+	 * needed to log own send messages
+	 * @param message
+	 */
+	public void sendMessage(Message message)
+	{
+		//if(isr
+		for(Iterator j = getListeners(OwnMessageListener.class);j.hasNext();)
+		{
+			((OwnMessageListener)j.next()).sendMessage(message);
+		}
+		connect.send(message);
+	}
+
+//	public void browse(JID jid, BrowseListener listener)
+//	{
+//		connect.browse(jid,listener);
+//	}
+	
+	public void getItems(JID jid, DiscoveryListener listener, boolean useCache)
+	{
+		connect.getItems(jid,listener,useCache);
+	}
+	
+	public void getItems(JID jid, DiscoveryListener listener)
+	{
+		connect.getItems(jid,listener);
+	}
+	
+	public void getInfo(JID jid, DiscoveryListener listener)
+	{
+		connect.getInfo(jid,listener);
+	}
+	
+	public void getItems(JID jid,String node, DiscoveryListener listener)
+	{
+		connect.getItems(jid,node,listener);
+	}
+	
+	public void getInfo(JID jid,String node, DiscoveryListener listener)
+	{
+		connect.getInfo(jid,node,listener);
+	}
+	
+	/**
+	 * gets the current Jabber status of the user
+	 * @return the current status
+	 */
+	public int getStatus()
+	{
+		return connect.getStatus();
+	}
+	
+	
+//	//muc hack
+//	public void browseNotCached(JID jid, BrowseListener listener)
+//	{
+//		connect.browseNotCached(jid,listener);
+//	}
+
+	public static JIDStatus getJIDStatus(JID jid)
+	{
+		return Roster.getJIDStatus(jid);
+	}
+
+	public String[] getAllGroups()
+	{
+		return roster.getAllGroups();
+	}
+
+	/**
+	 * makes a new account
+	 * (the current connection will be logged off)
+	 * @param name Username (may be null)
+	 * @param password User password (may be null)
+	 */
+	public void newAccount(String server,int port,String name,String password)
+	{
+		disconnect();
+		new NewAccount(server,port,handlers,name,password);
+	}
+	
+	public void setCapability(JID jid, IQXCaps caps){
+		capabilitiesCache.setCapability(jid, caps);
+	}
+	
+	/**
+	 * Has JID jid capability capability
+	 * @param jid
+	 * @param capability
+	 * @return true if the jid has the capability
+	 */
+	public boolean hasCapability(JID jid, String capability){
+		return capabilitiesCache.hasCapability(jid, capability);
+	}
+	
+	public void addCapability(String capability,String feature){
+		connect.addCapability(capability, feature);
+	}
+	
+	public void removeCapability(String capability,String feature){
+		connect.removeCapability(capability, feature);
+	}
+	
+	
+	
+	public void addExtensionHandler(String namespace,ExtensionHandler handler)
+	{
+		handlers.addExtensionHandler(namespace,handler);
+	}
+	
+	public void removeExtensionHandler(String namespace)
+	{
+		handlers.removeExtensionHandler(namespace);
+	}
+	
+	/**
+	 * Add a listener for a specific presence
+	 * @param jid The JID of which the presence is to be monitored (resource is not considered) 
+	 * @param listener The class that is interested in presence events
+	 */
+	public synchronized void addPresenceListener(JID jid, PresenceListener listener)
+	{
+		 if(presenceListeners == null)presenceListeners = new HashMap(); 
+		 presenceListeners.put(jid,listener); 
+	}
+	
+	/**
+	 * removes a presencelistener
+	 * @param jid The JID of which presence is monitored
+	 */
+	public synchronized void removePresenceListener(JID jid)
+	{
+		if(presenceListeners==null)return;
+		presenceListeners.remove(jid);
+		if (presenceListeners.isEmpty()) presenceListeners = null;
+	}
+	
+	/**
+	 * Gets the presenceListener which is monitoring this JID 
+	 * @param jid The JID of which the presenceListener is requested
+	 * @return PresenceListener
+	 */
+	public synchronized PresenceListener getPresenceListener(JID jid)
+	{
+		if(presenceListeners == null) return null;
+		return (PresenceListener)presenceListeners.get(jid);
+	}
+	
+	/**
+	 * Add a listener for messages to this JID
+	 * @param jid The JID of which the message is to be monitored (resource is not considered) 
+	 * @param listener The class that is interested in message events
+	 */
+	public synchronized void addMessageListener(JID jid, MessageListener listener)
+	{
+		 if(messageListeners == null)messageListeners = new HashMap(); 
+		 messageListeners.put(jid,listener); 
+	}
+	
+	/**
+	 * removes a messagelistener
+	 * @param jid The JID of which messages are monitored
+	 */
+	public synchronized void removeMessageListener(JID jid)
+	{
+		if(messageListeners==null)return;
+		messageListeners.remove(jid);
+		if (messageListeners.isEmpty()) messageListeners = null;
+	}
+	
+	/**
+	 * Gets the MessageListener which is monitoring this JID 
+	 * @param jid The JID of which the messageListener is requested
+	 * @return MessageListener
+	 */
+	public synchronized MessageListener getMessageListener(JID jid)
+	{
+		if(messageListeners == null) return null;
+		return (MessageListener)messageListeners.get(jid);
+	}
+	
+	public synchronized void addListener(Class type, JETIListener listener)
+	{//class meegeven?
+		LinkedList list = (LinkedList) eventListeners.get(type.getName());
+		if(list == null)
+		{
+			list = new LinkedList();
+		    eventListeners.put(type.getName(),list);
+		}
+		list.add(listener);
+	}
+
+	public synchronized void removeListener(Class type, JETIListener listener)
+	{
+		LinkedList list = (LinkedList) eventListeners.get(type.getName());
+		if(list == null) return;
+		list.remove(listener);
+		if(list.isEmpty()) eventListeners.remove(type.getName());//good idea?
+	}
+
+	public synchronized Iterator getListeners(final Class type)
+	{
+		LinkedList listeners = (LinkedList)eventListeners.get(type.getName());
+		if(listeners == null)
+		{//no listeners so return a empty iterator, if happens use isRegisteredListener(class type)
+			return new Iterator()
+			{//empty iterator
+				public Object next()
+				{
+					throw new NoSuchElementException();
+				}
+				public boolean hasNext()
+				{
+					//System.out.println(type + " listener not registerd");
+					//throw new RuntimeException();
+					return false;
+				}
+				public void remove()
+				{
+					throw new UnsupportedOperationException();
+				}
+			};
+		}
+		return new LinkedList(listeners).iterator();//no concurrentmodexcep
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/JIDStatus.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/JIDStatus.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/JIDStatus.java	(revision 3952)
@@ -0,0 +1,164 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 9-jan-2005
+ */
+
+package nu.fw.jeti.jabber;
+
+import java.util.List;
+
+import javax.swing.ImageIcon;
+
+/**
+ * Keeps a JID and the information about that JID together.
+ * The JIDStatus is implemented in three classes
+ * <ul>
+ * <li>{@link nu.fw.jeti.backend.roster.PrimaryJIDStatus}</li>
+ * <li>{@link nu.fw.jeti.backend.roster.NormalJIDStatus}</li>
+ * <li>{@link nu.fw.jeti.backend.roster.ResourceJIDStatus}</li>
+ * </ul>
+ * These classes are part of the roster and are used to store and sort
+ * information about the nickname and resources. This abstract class makes the most
+ * used methods of those classes available, so the implementing classes are rarely needed.
+ * The JIDStatussen available in the roster can be obtained by {@link Backend#getJIDStatus(JID)}
+ * If you need your own jidstatus you can use {@link UnknownJIDStatus}
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public abstract class JIDStatus implements Comparable
+{
+	protected ImageIcon avatar;
+	protected boolean online = false;
+	protected int show;
+	protected String status;
+	protected JID completeJID;
+	
+	/**
+	 * get users Avatar, only works when an avatar plugin is enabled
+	 * @return users avatar or null when none found
+	 */
+	public ImageIcon getAvatar()
+	{
+		return avatar;
+	}
+	
+	
+	/**
+	 * Gets the JID associated with this JIDStatus.
+	 * @return the JID whitout resource associated with this JIDStatus.
+	 */
+	public abstract  JID getJID();
+
+	/**
+	 * Gets the JID with resource associated with this JIDStatus.
+	 * @return If there is a known resource the JID with resource,
+	 *         else the JID without the resource.
+	 */
+	public JID getCompleteJID(){return completeJID;}
+
+	/**
+	 * Returns true when the user is online.
+	 * @return true when the user is online.
+	 */
+	public boolean isOnline()
+	{
+		return online;
+	}
+
+	/**
+	 * Gets the Show status of this JIDStatus.
+	 * one of "chat", "available", "away", "dnd", "xa" or "unavailable".
+	 * @return The show status of this JIDStatus.
+	 */
+	public int getShow()
+	{
+		return show;
+	}
+
+	/**
+	 * Gets the status of this JIDStatus, status is the user defined
+	 * message (status can be null).
+	 * @return The status of this JIDStatus.
+	 */
+	public String getStatus()
+	{
+		return status;
+	}
+
+	/**
+	 * Gets the nickname of this JIDStatus.
+	 * If the nickname is unknown this method returns the
+	 * JID assiocated with this JIDStatus.
+	 * @return The nickname
+	 */
+	public abstract String getNick();
+
+	/**
+	 * Gets the type of the transport this JIDStatus is registerd with.
+	 * initialy this is "jabber". The type is browsed for the registerd transports,
+	 * if the browse succeeded the type is changed in the type of the transport, when
+	 * the browse fails the type is changed to "unknown".
+	 * @return The type of this JIDStatus.
+	 */
+	public abstract String getType();
+
+	
+	/**
+	 * Gets the subscription type of this JIDStatus
+	 * can be null, "none" , "to", "from", or "both".
+	 * @return The subscription type of this JIDStatus.
+	 */
+	public abstract String getSubscription();
+
+	/**
+	 * Gets the waiting status of this JIDStatus
+	 * can be null, "subscribe" or "unsubscribe".
+	 * @return The waiting status of this JIDStatus.
+	 */
+	public abstract String getWaiting();
+
+	/**
+	 *
+	 * @return copy of the groups.
+	 */
+	public abstract List getGroupsCopy();
+
+	/**
+	 * Counts the groups this JIDStatus is in.
+	 * @return The number of groups this JIDStatus is in.
+	 */
+	abstract public int groupCount();
+
+	/**
+	 * Returns true when this JIDStatus is in group.
+	 * @param group The group that is tested.
+	 * @return true when this JIDStatus is in group.
+	 */
+	abstract public boolean isGroupPresent(String group);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/UnknownJIDStatus.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/UnknownJIDStatus.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/UnknownJIDStatus.java	(revision 3952)
@@ -0,0 +1,140 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 9-jan-2005
+ */
+
+package nu.fw.jeti.jabber;
+
+import java.util.List;
+
+import nu.fw.jeti.backend.roster.Roster;
+import nu.fw.jeti.backend.roster.SecondaryJIDStatus;
+import nu.fw.jeti.jabber.elements.Presence;
+
+/**
+ * @author E.S. de Boer
+ */
+
+ //stores jid + status
+
+//inconsistent with equals to make consistant add jid + equals if comletejids are same
+public class UnknownJIDStatus extends SecondaryJIDStatus
+{
+	private JID jid;
+	private String nickname;
+	private String type;
+	
+	
+	public UnknownJIDStatus(JID jid)
+	{
+		this(jid,null);
+	}
+	
+	/**
+	 * Make jidStatus from jid and nickname 
+	 * @param jid
+	 * @param nickname
+	 */
+	public UnknownJIDStatus(JID jid,String nickname)
+	{//call update??
+		this.jid = jid;
+		completeJID = jid;
+		this.nickname = nickname; 
+		if(nickname == null || nickname.equals(""))
+		{
+		   this.nickname = jid.getUser();
+		}
+		show = Presence.NONE;
+		JIDStatus server = Roster.getJIDStatus(new JID(jid.getDomain()));
+		if(server == null) type =null;
+		else type = server.getType();
+	}
+		
+	public String toString()
+	{//jid
+		return nickname;
+	}
+
+	public JID getJID(){return jid;}
+
+		
+	public void setStatus(String status)
+	{
+		this.status = status;
+	}
+
+	public String getNick()
+	{
+		return nickname;
+	}
+
+	public void setNick(String nick)
+	{
+		nickname = nick;
+	}
+
+	public String getType()
+	{
+		if(type == null) return "jabber";
+		return type;
+	}
+
+	public void setType(String type)
+	{
+		//System.out.println(type);
+		this.type = type;
+	}
+
+	public String getSubscription(){return null;}
+
+	public String getWaiting()
+	{
+		return null;
+	}
+	
+	public int groupCount()
+	{
+		return 0;
+	}
+
+	public List getGroupsCopy()
+	{
+		return null;
+	}
+
+	public boolean isGroupPresent(String group)
+	{
+		return false;
+	}
+
+	public int compareTo(Object o)
+	{
+		return nickname.compareTo(((JIDStatus)o).getNick());
+	}
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXRoster.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXRoster.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXRoster.java	(revision 3952)
@@ -0,0 +1,93 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+
+import nu.fw.jeti.events.CompleteRosterListener;
+import nu.fw.jeti.jabber.Backend;
+
+
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQXRoster extends Extension implements IQExtension, XExtension
+{//iq + x roster
+	private LinkedList items;
+
+	public IQXRoster(){}//extension tag for get
+
+	public IQXRoster(RosterItem item)
+	{
+		items = new LinkedList();
+		items.add(item);
+	}
+
+    public IQXRoster(RosterBuilder rb)
+    {
+		items=rb.getItems();
+    }
+
+	public Iterator getItems()
+	{
+		if (items==null)
+		{
+			return new Iterator()
+			{//empty iterator
+				public Object next()
+				{
+					throw new NoSuchElementException();
+				}
+				public boolean hasNext()
+				{
+					return false;
+				}
+				public void remove()
+				{
+					throw new UnsupportedOperationException();
+				}
+			};
+		}
+		return items.iterator();
+	}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		for (Iterator j = backend.getListeners(CompleteRosterListener.class); j.hasNext();)
+		{
+			((CompleteRosterListener) j.next()).rosterReceived(iq,this);
+		}
+	}
+
+
+	public void appendToXML(StringBuffer xml)
+    {/** @todo x */
+        xml.append("<query xmlns=\"jabber:iq:roster\"");
+		if(items ==null)
+		{ //short cut
+		    xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		for(Iterator i = items.iterator();i.hasNext();)
+		{
+		    ((RosterItem)i.next()).appendToXML(xml);
+		}
+		xml.append("</query>");
+    }
+}
+
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Presence.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Presence.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Presence.java	(revision 3952)
@@ -0,0 +1,172 @@
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.jabber.*;
+import nu.fw.jeti.util.I18N;
+
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class Presence extends Packet
+{
+	public final static int NONE = 0;
+	public final static int FREE_FOR_CHAT = 1;
+	public final static int AVAILABLE = 2;
+	public final static int AWAY = 3;
+	public final static int DND = 4;
+	public final static int XA = 5;
+	public final static int INVISIBLE = 6;
+	public final static int UNAVAILABLE = 7;
+	 
+	
+	private int show;
+	private String status;
+	private int priority;
+	private String type;
+	
+	public Presence(JID to,String type)
+	{
+	    super(to);
+		this.type = type;
+	}
+	
+	public Presence(JID to,int show,String status,XExtension extension)
+	{
+		super(to,(Extension)extension);
+		setShow(show);
+		this.status = status;
+	}
+
+	public Presence(JID to,int show, String status)
+	{
+	    super(to);
+		this.show = show;
+		this.status = status;
+		setShow(show);
+	}
+
+	public Presence(int show, String status,int priority,XExtension extension)
+	{
+		super((Extension)extension);
+		setShow(show);
+		this.status = status;
+		this.priority = priority;
+	}
+	
+	private void setShow(int show)
+	{
+		switch (show) {
+	        case INVISIBLE:   type = "invisible";   break;
+	        case UNAVAILABLE: type = "unavailable"; break;
+	        default:          type = "available";   break;
+	    }
+		this.show = show;
+	}
+//	public Presence(int show, String status,int priority,XExtension extension)
+//	{
+//		super(presence.getTo(),(Extension)extension);
+//		this,type = presence.getType();
+//		this.show = presence.getShow();
+//		this.status = presence.getStatus();
+//		this.priority = priority;
+//	}
+
+    protected Presence(PresenceBuilder pb)
+	{
+		super(pb);
+		show = pb.show;
+		status = pb.status;
+		priority = pb.priority;
+		type = pb.type;
+	}
+
+	public int getShow()
+	{
+		return show;
+	}
+	
+	/**
+	 * converts the constant status to a readable version
+	 * @param show
+	 * @return the long version of the presence
+	 */
+	public static String toLongShow(int show)
+	{ 
+		return I18N.gettext(getI18NKey(show));
+//		switch(show)
+//		{
+//			case Presence.FREE_FOR_CHAT:return I18N.gettext("main.presence.Free_for_Chat");
+//			case Presence.AWAY: return I18N.gettext("main.presence.Away");
+//			case Presence.XA: return I18N.gettext("main.presence.Extended_Away");
+//			case Presence.DND: return I18N.gettext("main.presence.Do_not_Disturb");
+//			case Presence.NONE: return I18N.gettext("main.presence.Unknown");
+//			case Presence.UNAVAILABLE: return I18N.gettext("main.presence.Unavailable");
+//			case Presence.INVISIBLE: return I18N.gettext("main.presence.Invisible");
+//			default: return I18N.gettext("main.presence.Available");
+//		}
+	}
+	
+	/**
+	 * converts the constant status to a readable version
+	 * @param show
+	 * @return the long version of the presence
+	 */
+	public static String getI18NKey(int show)
+	{ 
+		switch(show)
+		{
+			case Presence.FREE_FOR_CHAT:return "main.presence.Free_for_Chat";
+			case Presence.AWAY: return "main.presence.Away";
+			case Presence.XA: return "main.presence.Extended_Away";
+			case Presence.DND: return "main.presence.Do_not_Disturb";
+			case Presence.NONE: return "main.presence.Unknown";
+			case Presence.UNAVAILABLE: return "main.presence.Unavailable";
+			case Presence.INVISIBLE: return "main.presence.Invisible";
+			default: return "main.presence.Available";
+		}
+	}
+	
+
+	public String getStatus()
+	{
+		return status;
+	}
+
+	public int getPriorety()
+	{
+		return priority;
+	}
+
+	public String getType(){return type;}
+
+	public void appendToXML(StringBuffer xml)
+    {//make short cut?
+        xml.append("<presence");
+		appendBaseAttributes(xml);
+		if(!type.equals("available")) appendAttribute(xml,"type",type);
+		xml.append(">");
+		if(! (show == AVAILABLE || show == UNAVAILABLE))
+		{
+			switch (show)
+			{
+				case Presence.FREE_FOR_CHAT: appendElement(xml,"show","chat"); break;
+				case Presence.AWAY: appendElement(xml,"show","away"); break;
+				case Presence.XA: appendElement(xml,"show","xa"); break;
+				case Presence.DND: appendElement(xml,"show","dnd"); break;
+			}	
+		}
+		appendElement(xml,"status",status);
+		if(priority!=0)	appendElement(xml,"priority",Integer.toString(priority));
+		if("error".equals(type)) appendError(xml);
+		appendExtensions(xml);
+		xml.append("</presence>");
+    }
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQPrivate.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQPrivate.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQPrivate.java	(revision 3952)
@@ -0,0 +1,50 @@
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.jabber.Backend;
+
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQPrivate extends Extension implements IQExtension
+{
+	private IQXExtension extension;
+
+	// public IQPrivate(){}
+
+	public IQPrivate(IQXExtension extension)
+	{
+		this.extension = extension;
+	}
+
+//	public Extension getExtension()
+//	{
+//		return extension;
+//	}
+
+	public void execute(InfoQuery iq, Backend backend)
+	{
+		if(extension!=null)extension.execute(iq,backend);
+	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<query xmlns=\"jabber:iq:private\"");
+		if (extension == null)
+		{ //short cut
+			xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		((Extension)extension).appendToXML(xml);
+		xml.append("</query>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/RosterItemBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/RosterItemBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/RosterItemBuilder.java	(revision 3952)
@@ -0,0 +1,64 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.util.I18N;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class RosterItemBuilder
+{
+	private List groups;
+	public String name;
+	public String subscription;
+	public String ask;
+	public JID jid;
+
+	public void reset()
+	{
+		name=null;
+		subscription =null;
+		ask=null;
+		jid =null;
+		groups =null;
+	}
+
+	public void addGroup(String group)
+	{
+		if(groups == null) groups = new LinkedList();
+		groups.add(group);
+	}
+
+	public List getGroups()
+	{
+		return groups;
+	}
+
+	public RosterItem build() throws InstantiationException
+	{
+		if(jid == null) throw new InstantiationException(I18N.gettext("main.error.No_JID_found"));
+		if(subscription !=null && !(subscription.equals("none") ||
+									subscription.equals("from") ||
+									subscription.equals("to") ||
+									subscription.equals("both") ||
+									subscription.equals("remove")))
+									subscription = null;
+		if(ask != null && !(ask.equals("subscribe") || ask.equals("unsubscribe"))) ask =null;
+		return new RosterItem(this);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQExtension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQExtension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQExtension.java	(revision 3952)
@@ -0,0 +1,23 @@
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.jabber.Backend;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface IQExtension
+{
+	public void execute(InfoQuery iq,Backend backend);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDataBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDataBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDataBuilder.java	(revision 3952)
@@ -0,0 +1,124 @@
+/* 
+*	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+*
+*  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
+*
+*	For questions, comments etc, 
+*	use the website at http://jeti.jabberstudio.org
+*  or mail/IM me at jeti@jabber.org
+*
+*	Created on 26-jul-2003
+*/
+
+package nu.fw.jeti.jabber.elements;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import nu.fw.jeti.util.I18N;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class XDataBuilder
+{
+	private String instructions; //multi add line end
+	public String title;
+	public String type;
+	private List fields;
+	private List reportedFields;
+	private List items;
+
+	public XDataBuilder()
+	{
+		reset();
+	}
+	
+	public void reset()
+	{
+		instructions = null;
+		title = null;
+		type = null;
+		fields = null;
+		reportedFields = null;
+		items = null;
+	}
+	
+	/**
+	 * adds instructions (multiple calls wil be added with an added end of line)
+	 * @param instructions
+	 */
+	public void addInstructions(String instructions)
+	{
+		if(this.instructions == null) this.instructions = instructions; 
+		else
+		{
+			this.instructions = this.instructions + "\n" + instructions;
+		}
+	}
+
+	public String getInstructions()
+	{
+		return instructions;
+	}
+	
+	public void addField(XDataField field)
+	{
+		if(fields== null) fields = new LinkedList();
+		fields.add(field);  
+	}
+	
+	public void addReportedField(XDataField field)
+	{
+		if(reportedFields== null) reportedFields = new LinkedList();
+		reportedFields.add(field);  
+	}
+	
+	public void addItem(List item)
+	{
+		if(items==null)items = new LinkedList();
+		items.add(item);
+	}
+
+	public List getFields()
+	{
+		return fields;
+	}
+	
+	public List getReported()
+	{
+		return reportedFields;
+	}
+	
+	public List getItems()
+	{
+		return items;
+	}
+	
+	public XData build() throws InstantiationException
+	{
+		if (type==null) throw new InstantiationException(I18N.gettext("main.error.No_type"));
+		else if(type.equals("cancel") || type.equals("form") || type.equals("result") || type.equals("submit"));
+		else throw new InstantiationException("Wrong type"); 
+		return new XData(this);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoIdentity.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoIdentity.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoIdentity.java	(revision 3952)
@@ -0,0 +1,55 @@
+// Created on 4-aug-2004
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.backend.XMLData;
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * @author E.S. de Boer
+ * The identities used in the disco spec
+ * @see nu.fw.jeti.jabber.elements.IQDiscoInfo 
+ */
+public class DiscoIdentity extends XMLData 
+{
+	private String name;
+	private String category;
+	private String type;
+	
+	public DiscoIdentity(String category,String type,String name)
+	{
+		this.name = name;
+		this.category = category;
+		this.type = type;
+	}
+	
+	public String getName()
+	{
+		return name;
+	}
+
+	public String getCategory()
+	{
+		return category;
+	}
+
+	public String getType()
+	{
+		return type;
+	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<identity");
+		appendAttribute(xml, "category", category);
+		appendAttribute(xml, "type", type);
+		appendAttribute(xml, "name", name);
+		xml.append("/>");
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/PresenceBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/PresenceBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/PresenceBuilder.java	(revision 3952)
@@ -0,0 +1,74 @@
+package nu.fw.jeti.jabber.elements;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class PresenceBuilder extends PacketBuilder
+{
+	public String showAsString;
+	public int show;
+	public String status;
+	public String priorityAsString;
+	public int priority;
+	public String type;
+
+    public PresenceBuilder()
+    {
+		reset();
+    }
+
+	public void reset()
+	{
+		super.reset();
+	    show = Integer.MAX_VALUE;
+	    showAsString = null;
+		status=null;
+		priorityAsString=null;
+		priority = 0;
+	}
+
+	public Packet build() throws InstantiationException
+	{
+		//if(priority < -128 || priority >127) throw new InstantiationException("priority out of bounds (-128, 127");
+		if(priority==0)
+		{
+			try{
+				priority =Integer.parseInt(priorityAsString);
+			}catch (NumberFormatException e){priority=0;}
+		}
+		if(type==null) type = "available";
+		else
+		{
+			if(!(type.equals("unavailable") ||
+				      type.equals("subscribe")||
+					  type.equals("subscribed")||
+					  type.equals("unsubscribe")||
+					  type.equals("unsubscribed")||
+					  type.equals("probe")||
+					  type.equals("error")))
+					  type ="available";
+		}
+		if(type.equals("unavailable")) show = Presence.UNAVAILABLE;
+		else if(show == Integer.MAX_VALUE)
+		{
+			if("away".equals(showAsString)) show = Presence.AWAY; 
+			else if("dnd".equals(showAsString)) show = Presence.DND;
+			else if("xa".equals(showAsString)) show = Presence.XA;
+			else if("chat".equals(showAsString)) show = Presence.FREE_FOR_CHAT;
+			else
+			{
+				if (type.equals("available")) show = Presence.AVAILABLE;
+				else show = Presence.UNAVAILABLE; 
+			}
+		}
+		return new Presence(this);
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Extension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Extension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Extension.java	(revision 3952)
@@ -0,0 +1,20 @@
+package nu.fw.jeti.jabber.elements;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public abstract class Extension extends nu.fw.jeti.backend.XMLData
+{
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDataFieldBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDataFieldBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDataFieldBuilder.java	(revision 3952)
@@ -0,0 +1,117 @@
+// Created on 27-jul-2003
+package nu.fw.jeti.jabber.elements;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class XDataFieldBuilder
+{
+	private String desc;//multi add line end
+	public String label;
+	public String var;
+	public String type;
+	private List options;
+	public boolean required=false;
+	private String value; //multi add line end
+	
+	public void reset()
+	{
+		desc = null;
+		label = null;
+		var=null;
+		type=null;
+		options = null;
+		required=false;
+		value=null;
+	}
+
+	/**
+	 * adds a description (multiple calls wil be added with an added end of line)
+	 * @param description
+	 */
+	public void addDescription(String description)
+	{
+		if(desc == null) desc = description; 
+		else
+		{
+			desc = desc + "\n" + description;
+		}
+	}
+
+	public String getDescription()
+	{
+		return desc;
+	}
+	
+	/**
+	 * adds a value (multiple calls wil be added with an added end of line)
+	 * @param value
+	 */
+	public void addValue(String value)
+	{
+		if(this.value == null) this.value = value; 
+		else
+		{
+			this.value = this.value + "\n" + value;
+		}
+	}
+
+	public String getValue()
+	{
+		return value;
+	}
+	
+	/**
+	 * adds an option
+	 * @param value
+	 */
+	public void addOption(String value)
+	{
+		addOption(null,value);
+	}
+	
+	/**
+	 * adds an option
+	 * @param label optional label
+	 * @param value
+	 */
+	public void addOption(String label, String value)
+	{
+		if(options == null) options = new LinkedList(); 
+		options.add(new String[]{label,value});
+	}
+
+	/**
+	 * returns options as a List containing String[]
+	 * with on position 0 the label and on position 1 the value
+	 * @return List
+	 */
+	public List getOptions()
+	{
+		return options;	
+	}
+	
+	public XDataField build()
+	{
+		checkType();
+		return new XDataField(this);
+	}
+	
+	private void checkType() 
+	{
+		if(type == null) type = "text-single";
+		else if(type.equals("boolean") || type.equals("fixed") || type.equals("hidden") || type.equals("jid-multi")|| type.equals("jid-single")|| type.equals("list-multi")|| type.equals("list-single")|| type.equals("text-multi")|| type.equals("text-private")|| type.equals("text-single"));
+		else  type = "text-single";
+	}
+	
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/PacketBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/PacketBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/PacketBuilder.java	(revision 3952)
@@ -0,0 +1,72 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.util.LinkedList;
+import nu.fw.jeti.jabber.*;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public abstract class PacketBuilder
+{
+	public JID to;
+	public JID from;
+	public String id;
+	public int errorCode;
+	public String errorDescription;
+	private String type;
+	public XMPPError xmppError;
+	private LinkedList extensions;
+
+	public void reset()
+	{
+	    to = null;
+		from = null;
+		id = null;
+		extensions = null;
+		errorCode = 0;
+		xmppError=null;
+		type=null;
+		errorDescription = null;
+	}
+
+	public void setTo(JID to){this.to =to;}
+	public void setFrom(JID from){this.from =from;}
+	public void setId(String id){this.id =id;}
+	public void setErrorType(String type){this.type =type;}
+	public void setErrorCode(int errorCode){this.errorCode =errorCode;}
+	public void setErrorDescription(String errorDescription){this.errorDescription = errorDescription;}
+
+	public void addExtension(Extension extension)
+	{
+		if(extension == null) return;
+		if(extension instanceof XMPPErrorTag)
+		{
+			if(xmppError==null)xmppError=new XMPPError(type,errorCode);
+			xmppError.addError((XMPPErrorTag)extension);
+		}
+		else
+		{
+		    if(extensions == null) extensions = new LinkedList();
+			extensions.add(extension);
+		}
+	}
+
+	public LinkedList getExtensions()
+	{
+	    return extensions;
+	}
+
+	abstract public Packet build() throws InstantiationException;
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQRegister.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQRegister.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQRegister.java	(revision 3952)
@@ -0,0 +1,99 @@
+package nu.fw.jeti.jabber.elements;
+import java.util.Iterator;
+import java.util.Map;
+
+import nu.fw.jeti.events.RegisterListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQRegister extends Extension implements IQExtension
+{
+	private boolean remove =false;
+	private Map fields;
+	private XData xdata;
+	private String password;
+	private String username;
+
+
+	public IQRegister(){}
+	
+	public IQRegister(String username,String password)
+	{
+		this.password = password;
+		this.username = username;
+	}
+	
+	public IQRegister(XData xdata){this.xdata = xdata;}
+
+	public IQRegister(boolean remove,Map fields,XData xdata)
+	{
+		this.remove = remove;
+		this.fields =fields;
+		this.xdata = xdata;
+	}
+
+	public IQRegister(boolean remove,Map fields)
+	{
+		this.remove = remove;
+		this.fields =fields;
+	}
+
+	public boolean getRemove(){return remove;}
+
+	public Map getFields(){return fields;}
+	
+	public XData getXData(){return xdata;}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		if (iq.getType().equals("result"))
+		{
+			for (Iterator j = backend.getListeners(RegisterListener.class); j.hasNext();)
+			{
+				((RegisterListener) j.next()).register(this,iq.getID());
+			}
+
+		}
+		else if (iq.getType().equals("error"))
+		{
+			Popups.errorPopup(iq.getErrorDescription(), I18N.gettext("main.error.Register_Error"));
+		}
+	}
+
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<query xmlns=\"jabber:iq:register\"");
+		if(username==null && fields == null && !remove && xdata == null)
+		{ //short cut
+			xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		if(username!=null)
+		{
+			appendElement(xml,"username",username);
+			appendElement(xml,"password",password);
+		}
+		else
+		{
+			appendElement(xml,fields);
+			appendElement(xml,"remove",remove);
+			if(xdata != null) xdata.appendToXML(xml);
+		}
+		xml.append("</query>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMessageEvent.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMessageEvent.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMessageEvent.java	(revision 3952)
@@ -0,0 +1,57 @@
+package nu.fw.jeti.jabber.elements;
+
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class XMessageEvent extends Extension implements XExtension
+{
+    /** id is the id from the message that this event is a reply to */
+    private String id;
+
+    /** type holds a string describing the type of the event (composing/offline/deliverd/displayed) */
+    private String type;
+
+	public XMessageEvent(String type,String id)
+	{
+		this.id=id;
+		this.type=type;
+	}
+
+    public XMessageEvent(XMessageEventBuilder builder)
+    {
+		id=builder.getID();
+		type=builder.getType();
+	}
+
+	public String getType()
+    {
+		return type;
+    }
+
+	public String getID()
+	{
+		return id;
+	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<x xmlns=\"jabber:x:event\">");
+		if(type!=null) xml.append("<"+type + "/>");
+		if(id!=null) xml.append("<id>"+ id + "</id>");
+		//retval.append('>');
+		xml.append("</x>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQVersion.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQVersion.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQVersion.java	(revision 3952)
@@ -0,0 +1,90 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.text.MessageFormat;
+
+import javax.swing.JOptionPane;
+
+import nu.fw.jeti.backend.Start;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+
+
+/**
+ * <p>Copyright: Copyright (c) 2002</p>
+ *  @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQVersion extends Extension implements IQExtension
+{
+	private String os;
+	private String name;
+	private String version;
+
+	public IQVersion(){}
+
+	public IQVersion(String name, String version,String os)
+    {
+		this.os = os;
+		this.name = name;
+		this.version = version;
+    }
+
+	public String getOS(){return os;}
+
+	public String getName(){return name;}
+
+	public String getVersion(){return version;}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		String type = iq.getType();
+		if (type.equals("get"))
+		{
+			if(Preferences.getBoolean("jeti", "privacy", false)) return;
+			String tag = "";
+			if(Start.applet)tag = "A";
+			else if(Start.webstart)tag = "W";
+			IQVersion version = new IQVersion("JETI" +Start.OS2, Start.VERSION + tag + " - " + Start.BUILD_DATE, System.getProperty("os.name") + " " +System.getProperty("os.version"));
+			backend.send(new InfoQuery(iq.getFrom(),"result", iq.getID(),version));
+		}
+		else if (type.equals("result"))
+		{
+			versionPopup(iq.getFrom().toString(), getName(), getVersion(), getOS());
+			//System.out.println(iq.getFrom().toStringNoResource() + " " +  getName() + " " +  getVersion() + " " + getOS());
+		}
+		else if (type.equals("error"))
+		{
+			Popups.errorPopup(iq.getErrorDescription(), I18N.gettext("main.error.Version_Error"));
+		}
+		
+	}
+	
+	private void versionPopup(String jid,String name,String version,String os)
+	{
+		Popups.popup(MessageFormat.format(I18N.gettext("main.popup.{0}_uses_{1},_version_{2},_On_{3}"),new Object[]{jid,name,version,os}),I18N.gettext("main.popup.Version"),JOptionPane.INFORMATION_MESSAGE);
+	}
+		
+	public void appendToXML(StringBuffer xml)
+    {
+        xml.append("<query xmlns=\"jabber:iq:version\"");
+		if(name == null && version == null && os ==null)
+		{ //short cut
+		    xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		appendElement(xml,"name",name);
+		appendElement(xml,"version",version);
+		appendElement(xml,"os",os);
+		xml.append("</query>");
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/ExtensionBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/ExtensionBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/ExtensionBuilder.java	(revision 3952)
@@ -0,0 +1,25 @@
+package nu.fw.jeti.jabber.elements;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface ExtensionBuilder
+{
+
+	public Extension build();
+
+	public void reset();
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Message.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Message.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Message.java	(revision 3952)
@@ -0,0 +1,145 @@
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.jabber.*;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class Message extends Packet
+{
+	private String body;
+	private String thread;
+	private String subject;
+	private String type;
+
+	public Message(MessageBuilder mb)
+	{
+		super(mb);
+		body = mb.body;
+		thread = mb.thread;
+		subject = mb.subject;
+		type = mb.type;
+	}
+
+	/**
+	 * normal message
+	 * @param body
+	 * @param to
+	 */
+	public Message(String body,String subject, JID to)
+	{
+		super(to);
+		type = "normal";
+		this.body = body;
+		this.subject = subject;
+	}
+	
+	/**
+	 * normal message
+	 * @param body
+	 * @param to
+	 */
+	public Message(String body, JID to,XExtension extension)
+	{
+		super(to, (Extension) extension);
+		type = "normal";
+		this.body = body;
+	}
+	
+	
+	/**
+	 * groupchat message
+	 * @param to
+	 * @param body
+	 */
+	public Message(JID to,String body,XExtension extension)
+	{
+		super(to, (Extension) extension);
+		type = "groupchat";
+		this.body = body;
+	}
+	
+	/**
+	 * groupchat message
+	 * @param to
+	 * @param body
+	 */
+	public Message(JID to,String body)
+	{
+		super(to);
+		type = "groupchat";
+		this.body = body;
+	}
+
+	public Message(String body, JID to, String thread)
+	{
+		super(to);
+		type = "chat";
+		this.body = body;
+		this.thread = thread;
+	}
+
+	public Message(String body, JID to, String id, String thread, XExtension extension)
+	{
+		super(to, id, (Extension) extension);
+		type = "chat";
+		this.body = body;
+		this.thread = thread;
+	}
+
+	public Message(String body, JID to, String id,XExtension extension)
+	{
+		super(to, id, (Extension) extension);
+		type = "chat";
+		this.body = body;
+	}
+		
+	public String getSubject()
+	{
+		return subject;
+	}
+
+	public String getThread()
+	{
+		return thread;
+	}
+
+	public String getBody()
+	{
+		return body;
+	}
+
+	public String getType()
+	{
+		return type;
+	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		//make short cut?
+		xml.append("<message");
+		appendBaseAttributes(xml);
+		if (!type.equals("normal"))
+			appendAttribute(xml, "type", type);
+		xml.append(">");
+		appendElement(xml, "thread", thread);
+		appendElement(xml, "subject", subject);
+		appendElement(xml, "body", body);
+		if ("error".equals(type))
+			appendError(xml);
+		appendExtensions(xml);
+		xml.append("</message>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMessageEventBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMessageEventBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMessageEventBuilder.java	(revision 3952)
@@ -0,0 +1,67 @@
+package nu.fw.jeti.jabber.elements;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class XMessageEventBuilder implements ExtensionBuilder
+{
+	//remove this
+	
+	/** id is the id from the message that this event is a reply to */
+	private String id;
+
+	/** type holds a string describing the type of the event (composing/offline/deliverd/displayed) */
+	private String type;
+
+	/** construct a new XMessageEventBuilder object */
+	public XMessageEventBuilder()
+	{
+		reset();
+	}
+
+	/** reset the builder to a default state, for reuse */
+	public void reset()
+	{
+		id=null;
+		type=null;
+	}
+
+	public String getType()
+	{
+		return type;
+	}
+
+	public void setType(String type)
+	{
+		this.type=type;
+	}
+
+
+	public String getID()
+	{
+		return id;
+	}
+
+	public void setID(String txt)
+	{
+		id=txt;
+	}
+
+	public Extension build()
+	{
+		//if(id == null) throw new InstantiationException("ID may not be null");
+		return new XMessageEvent(this);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQTime.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQTime.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQTime.java	(revision 3952)
@@ -0,0 +1,127 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import javax.swing.JOptionPane;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+
+/**
+  * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQTime  extends Extension implements IQExtension
+{
+	private String tz;
+	private String utc;
+	private String display;
+
+	public IQTime(){}
+
+	public IQTime(String utc, String tz,String display)
+    {
+		this.tz = tz;
+		this.utc = utc;
+		this.display = display;
+    }
+
+	public String getTZ(){return tz;}
+
+	public String getUTC(){return utc;}
+
+	public String getDisplay(){return display;}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		String type = iq.getType();
+		if (type.equals("get")) {
+			if(Preferences.getBoolean("jeti", "privacy", false)) return;
+			sendTime(iq.getFrom(), iq.getID(),backend);
+		} else if (type.equals("result")) {
+			timePopup(iq.getFrom().toString(), getDisplay(),
+                      getUTC(),getTZ());
+		} else if (type.equals("error")) {
+			Popups.errorPopup(iq.getErrorDescription(), I18N.gettext("main.error.Time_Error"));
+		}
+	}
+
+	private void timePopup(String jid, String display, String time,
+                           String timezone) {
+		String info = MessageFormat.format(
+            I18N.gettext("main.popup.Local_time_for_{0}_is"),
+            new Object[]{jid});
+        String timeFormatted = null;
+        if (time.length() > 0 && timezone.length() > 0) {
+            try {
+                DateFormat peerFormat =
+                    new java.text.SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+                peerFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+                Date peerDate = peerFormat.parse(time);
+                DateFormat localFormat = DateFormat.getDateTimeInstance();
+                localFormat.setTimeZone(TimeZone.getTimeZone(timezone));
+                timeFormatted = localFormat.format(peerDate);
+            } catch (ParseException e) {
+                // Do Nothing
+            }
+        }
+        if (timeFormatted == null) {
+            timeFormatted = display;
+        }
+        if (timezone.length() > 0) {
+            timeFormatted += "\n" +
+                MessageFormat.format(
+                    I18N.gettext("main.popup.{0}_is_in_timezone_{1}"),
+                    new Object[]{jid,timezone});
+        }
+		Popups.popup(info + "\n" + timeFormatted,
+                     jid + ' ' +  I18N.gettext("main.popup.Time"),
+                     JOptionPane.INFORMATION_MESSAGE);
+	}
+	
+	private void sendTime(JID to, String id,Backend backend)
+	{
+		Date date = new Date();
+		DateFormat dateFormat =
+            new java.text.SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
+        TimeZone tz = dateFormat.getTimeZone();
+		String tzName = dateFormat.getTimeZone().getDisplayName(
+            tz.inDaylightTime(date), TimeZone.SHORT);
+		dateFormat.setCalendar(
+            Calendar.getInstance(TimeZone.getTimeZone("UTC")));
+		IQTime time =
+            new IQTime(dateFormat.format(date), tzName, date.toString());
+		backend.send(new InfoQuery(to,"result",id,time));
+	}
+
+	public void appendToXML(StringBuffer xml)
+    {
+        xml.append("<query xmlns=\"jabber:iq:time\"");
+		if(tz == null && utc == null && display ==null)
+		{ //short cut
+		    xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		appendElement(xml,"utc",utc);
+		appendElement(xml,"tz",tz);
+		appendElement(xml,"display",display);
+		xml.append("</query>");
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/InfoQueryBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/InfoQueryBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/InfoQueryBuilder.java	(revision 3952)
@@ -0,0 +1,45 @@
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.util.I18N;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class InfoQueryBuilder extends PacketBuilder
+{
+	private String type;
+
+	public InfoQueryBuilder()
+    {
+		reset();
+    }
+
+	//public void reset(){reset;}
+
+	public void setType(String type){this.type =type;}
+
+	public String getType(){return type;}
+
+	public Packet build() throws InstantiationException
+	{
+		if(type == null) throw new InstantiationException(I18N.gettext("main.error.type_must_be_specified"));
+		if(!(type.equals("get") || type.equals("set") || type.equals("result") || type.equals("error")))
+		{
+		    throw new InstantiationException(I18N.gettext("main.error.wrong_type") + ": " + type);
+		}
+
+		return new InfoQuery(this);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMPPErrorTag.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMPPErrorTag.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMPPErrorTag.java	(revision 3952)
@@ -0,0 +1,65 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 24-okt-2004
+ */
+ 
+package nu.fw.jeti.jabber.elements;
+
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class XMPPErrorTag extends Extension
+{
+	private String xmlns;
+	private String error;
+	
+	public XMPPErrorTag (String error)
+	{
+		 this.error = error;
+		 xmlns="urn:ietf:params:xml:ns:xmpp-stanzas";
+	}
+	
+	public XMPPErrorTag (String error,String xmlns)
+	{
+		this.error = error;
+		this.xmlns = xmlns;
+	}
+	
+	public String getError()
+	{
+		return error;
+	}
+	
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<" + error+" xmlns='"+ xmlns +"'/>");
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/MessageBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/MessageBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/MessageBuilder.java	(revision 3952)
@@ -0,0 +1,52 @@
+package nu.fw.jeti.jabber.elements;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class MessageBuilder extends PacketBuilder
+{
+	public String body;
+	public String thread;
+	public String subject;
+	public String type;
+
+   	public void reset()
+	{
+		super.reset();
+	    body = null;
+		thread=null;
+		subject=null;
+	}
+	
+	public void addXExtension(XExtension extension)
+	{
+		if(extension == null) return;
+	    addExtension((Extension)extension);
+	}
+
+	public Packet build()
+	{
+		if(type==null) type = "normal";
+		else
+		{
+		    if(!(type.equals("headline")||
+			     type.equals("chat")||
+			     type.equals("groupchat")||
+			     type.equals("error")))
+				 type = "normal";
+		}
+		return new Message(this);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/RosterItem.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/RosterItem.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/RosterItem.java	(revision 3952)
@@ -0,0 +1,84 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.util.List;
+
+import nu.fw.jeti.backend.XMLData;
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class RosterItem extends XMLData
+{
+	private List groups;
+	private String name;
+	private String subscription;
+	private String ask;
+	private JID jid;
+
+	public RosterItem(JID jid, String name,String subscription, String ask, List groups)
+	{
+		this.name = name;
+		this.subscription = subscription;
+		this.ask = ask;
+		this.jid = jid;
+		this.groups = groups;
+
+	}
+
+	public RosterItem(RosterItemBuilder ib)
+	{
+		name = ib.name;
+		subscription = ib.subscription;
+		ask = ib.ask;
+		jid = ib.jid;
+		groups = ib.getGroups();
+
+	}
+	
+	public String getName(){return name;}
+
+	public String getSubscription(){return subscription;}
+
+	public String getAsk(){return ask;}
+
+	public JID getJID(){return jid;}
+
+	public List getGroups()
+	{
+		return groups;
+	}
+
+	public void appendToXML(StringBuffer xml)
+    {
+        xml.append("<item");
+		appendAttribute(xml,"jid",jid);
+		appendAttribute(xml,"name",name);
+		appendAttribute(xml,"subscription",subscription);
+		appendAttribute(xml,"ask",ask);
+		if(groups ==null)
+		{ //short cut
+		    xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		for(int i=0;i < groups.size();i++)
+		{
+			appendElement(xml,"group",groups.get(i));
+		}
+		xml.append("</item>");
+    }
+	
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQDiscoItems.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQDiscoItems.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQDiscoItems.java	(revision 3952)
@@ -0,0 +1,124 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *
+ *	Created on 26-apr-2003
+ */
+
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Iterator;
+import java.util.List;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * @author E.S. de Boer
+ * implements the http://jabber.org/protocol/disco#items'/ spec
+ */
+public class IQDiscoItems extends Extension implements IQExtension, DiscoveryItem
+{
+	private String node;
+	private List items;
+
+	public IQDiscoItems()
+	{}
+
+	public IQDiscoItems(String node)
+	{
+		this.node = node;
+	}
+	
+	public IQDiscoItems(String node,List items)
+	{
+		this.node = node;
+		this.items = items;
+	}
+
+	public String getNode()
+	{
+		return node;
+	}
+		
+	public Iterator getItems()
+	{
+		if (items == null)return null;
+		return items.iterator();
+	}
+	
+	public int getSize()
+	{
+		if (items == null)return 0;
+		return items.size();
+	}
+	
+
+	public boolean hasItems()
+	{
+		return items != null;
+	}
+	
+	//---------------------------discoveryInfo implementation, returns null--------------
+	public JID getJID()
+	{
+		return null;
+	}
+
+	public String getName()
+	{
+		return null;
+	}
+	
+	//----------------------------------------------------------------------------
+	
+	
+	
+	public void execute(InfoQuery iq,Backend backend){}
+
+	
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<query xmlns= \"http://jabber.org/protocol/disco#items\"");
+		appendAttribute(xml, "node", node);
+		if (items == null)
+		{ //short cut
+			xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		if (items != null)
+		{
+			for (Iterator i = items.iterator(); i.hasNext();)
+			{
+				((DiscoItem) i.next()).appendToXML(xml);
+			}
+		}
+		xml.append("</query>");
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXExtension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXExtension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXExtension.java	(revision 3952)
@@ -0,0 +1,43 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 30-mrt-2004
+ */
+ 
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.jabber.Backend;
+
+/**
+ * @author Eric
+ */
+
+public interface IQXExtension
+{
+	public void execute(InfoQuery iq,Backend backend);
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/JetiExtensionBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/JetiExtensionBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/JetiExtensionBuilder.java	(revision 3952)
@@ -0,0 +1,89 @@
+package nu.fw.jeti.jabber.elements;
+
+/**
+ * Title:        im
+ * Description:
+ * Copyright:    Copyright (c) 2001
+ * Company:
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+import java.util.*;
+
+
+public class JetiExtensionBuilder implements ExtensionBuilder
+{
+
+	private Map entries;
+	private Map messages;
+	private String xmlVersion;
+
+
+	public JetiExtensionBuilder()
+	{
+		reset();
+	}
+
+	public void reset()
+	{
+		entries=new HashMap();
+		messages=new HashMap();
+	}
+
+	public void put(String name, String value)
+	{
+		//if (value==null)            entries.remove(name);
+		//System.out.println(name + " : " + value);
+		entries.put(name,value);
+	}
+
+	public void putMessages(String name, List value)
+	{
+		//if (value==null)            entries.remove(name);
+		//System.out.println(name + " : " + value);
+		messages.put(name,value);
+	}
+
+	public void addMap (Map map)
+	{
+		//if (value==null)            entries.remove(name);
+		entries =map;
+	}
+
+
+	public void addMapMessages(Map mapMessages)
+	{
+		messages = mapMessages;
+	}
+
+	public Map getMap()
+			{ return entries; }
+
+
+	public Map getMapMessages()
+	{
+		return messages;
+	}
+
+	public String getXmlVersion()
+	{
+		return xmlVersion;
+	}
+
+	public void setXmlVersion(String xmlVersion)
+	{
+		this.xmlVersion = xmlVersion;
+	}
+
+	public Extension build()
+	{
+		return new JetiPrivateExtension(this);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XData.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XData.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XData.java	(revision 3952)
@@ -0,0 +1,129 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *
+ *	Created on 26-jul-2003
+ */
+
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author E.S. de Boer
+ * X-data
+ */
+public class XData extends Extension implements XExtension
+{
+	private String instructions; //multi add line end
+	private String title;
+	private String type;
+	private List fields;
+	private List reported;
+	private List items;
+	
+	public XData(){}
+	
+	public XData(String type){this.type = type;}
+	
+	public XData(XDataBuilder builder)
+	{
+		instructions = builder.getInstructions();
+		title = builder.title;
+		type = builder.type;
+		fields = builder.getFields();
+		items = builder.getItems();
+		reported = builder.getReported();
+	}
+	
+	public String getType()
+	{
+		return type;
+	}
+	
+	public String getInstructions()
+	{
+		return instructions;
+	}
+	
+	public String getTitle()
+	{
+		return title;
+	}
+		
+	public boolean hasFields()
+	{
+		return fields != null; 
+		
+	}
+
+	public Iterator getFields()
+	{
+		return fields.iterator();
+	}
+	
+	public XDataField getField(int index)
+	{
+		return (XDataField)fields.get(index);
+	}
+	
+	public boolean hasItems()
+	{
+		return items!=null;
+	}
+
+	public Iterator getReported()
+	{
+		return reported.iterator();
+	}
+
+	public List getItems()
+	{
+		return new LinkedList(items);
+	}
+
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<x xmlns='jabber:x:data'");
+		appendAttribute(xml,"type",type);
+		xml.append(">");
+		if(instructions !=null) xml.append("<instructions>"+ instructions + "</instructions>");
+		if(title != null)xml.append("<title>" + title + "</title>");
+		if(fields !=null)
+		{
+			for(Iterator i = fields.iterator();i.hasNext();)
+			{
+				((XDataField)i.next()).appendToXML(xml);
+			}
+		}
+		xml.append("</x>");
+	}
+}
+
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXOOB.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXOOB.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXOOB.java	(revision 3952)
@@ -0,0 +1,75 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.net.URL;
+import java.util.Iterator;
+
+
+
+import nu.fw.jeti.events.ErrorListener;
+import nu.fw.jeti.events.OOBListener;
+import nu.fw.jeti.jabber.Backend;
+
+/**
+ * Created on 1-mrt-2003
+ * @author E.S. de Boer
+ *
+ */
+public class IQXOOB extends Extension implements IQExtension
+{
+	String description;
+	URL url;
+
+	public IQXOOB(URL url,String description)
+	{
+		this.description = description;
+		this.url = url;
+	}
+
+	public URL getURL()
+	{
+		return url;
+	}
+
+	public String getDescription()
+	{
+		return description;
+	}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		if (iq.getType().equals("set"))
+		{
+			for (Iterator j = backend.getListeners(OOBListener.class); j.hasNext();)
+			{
+				((OOBListener) j.next()).oob(iq.getFrom(),iq.getID(),this);
+			}
+			
+		}
+		else if (iq.getType().equals("error"))
+		{
+			for (Iterator j = backend.getListeners(ErrorListener.class); j.hasNext();)
+			{
+				((ErrorListener) j.next()).error(iq.getErrorCode(),iq.getErrorDescription());
+			}
+		}
+	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		/** @todo x */
+		xml.append("<query xmlns=\"jabber:iq:oob\">");
+		if (url != null)
+			xml.append("<url>" + url  + "</url>");
+		if (description != null)
+			xml.append("<desc>" + description + "</desc>");
+		xml.append("</query>");
+	}
+
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQBrowseBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQBrowseBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQBrowseBuilder.java	(revision 3952)
@@ -0,0 +1,85 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQBrowseBuilder
+{
+	private List namespaces;
+	private String name;
+	private String type;
+	private String category;
+	private JID jid;
+	private String version;
+	private List childItems;
+
+	public void reset()
+	{
+		name=null;
+		type =null;
+		category=null;
+		jid =null;
+		namespaces =null;
+		childItems = null;
+	}
+
+	public String getName(){return name;}
+
+	public void setName(String name){this.name =name;}
+
+	public String getType(){return type;}
+
+	public void setType(String type){this.type = type;}
+
+	public String getVersion(){return version;}
+
+	public void setVersion(String version){this.version = version;}
+
+	public String getCategory(){return category;}
+
+	public void setCategory(String category){this.category =category;}
+
+	public JID getJID(){return jid;}
+
+	public void setJID(JID jid){this.jid = jid;}
+
+	public List getItems(){return childItems;}
+
+	public void addItem(IQBrowse item)
+	{
+		if(childItems == null) childItems = new ArrayList();
+		childItems.add(item);
+	}
+
+	public List getNamespaces()
+	{
+		return namespaces;
+	}
+
+	public void addNamespace(String namespace)
+	{
+		if(namespaces  == null) namespaces = new LinkedList();
+		namespaces.add(namespace);
+	}
+
+	public IQBrowse build()
+	{
+		//if(jid == null) throw new InstantiationException("jid may not be null");
+		return new IQBrowse(this);
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQAuthBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQAuthBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQAuthBuilder.java	(revision 3952)
@@ -0,0 +1,48 @@
+package nu.fw.jeti.jabber.elements;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQAuthBuilder implements ExtensionBuilder
+{
+	public String username;
+	public String password;
+	public String digest;
+	public String resource;
+	public String zeroKHash;
+	public String zeroKToken;
+	public String zeroKSequence;
+
+    public IQAuthBuilder()
+    {
+		reset();
+    }
+
+	public void reset()
+	{
+		username = null;
+		password = null;
+		digest = null;
+		resource = null;
+		zeroKHash =null;
+		zeroKSequence =null;
+		zeroKToken =null;
+	}
+
+	public Extension build()
+	{
+	    return new IQAuth(this);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XExtension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XExtension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XExtension.java	(revision 3952)
@@ -0,0 +1,16 @@
+package nu.fw.jeti.jabber.elements;
+
+/*
+ * @author E.S. de Boer
+ */
+
+public interface XExtension
+{
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDataField.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDataField.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDataField.java	(revision 3952)
@@ -0,0 +1,148 @@
+// Created on 28-jul-2003
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class XDataField extends Extension
+{
+
+		private String desc;//multi add line end
+		private List options; //more 2 each
+		private boolean required=false;
+		private String value; //multi add line end
+		private String label;
+		private String var;
+		private String type; 
+	 
+//		 <xs:enumeration value='boolean'/>
+//		 <xs:enumeration value='fixed'/>
+//		 <xs:enumeration value='hidden'/>
+//		 <xs:enumeration value='jid-multi'/>
+//		 <xs:enumeration value='jid-single'/>
+//		 <xs:enumeration value='list-multi'/>
+//		 <xs:enumeration value='list-single'/>
+//		 <xs:enumeration value='text-multi'/>
+//		 <xs:enumeration value='text-private'/>
+//		 <xs:enumeration value='text-single'/>
+			
+		public XDataField(String var, String value)
+		{
+			 this.var = var;
+			 this.value = value;
+		}
+		
+		public XDataField(String var, String value,String type)
+		{
+			 this.var = var;
+			 this.value = value;
+			 this.type = type;
+		}
+				 
+		public XDataField(XDataFieldBuilder builder)
+		{
+			desc = builder.getDescription();
+			required =builder.required;
+			label = builder.label;
+			var = builder.var;
+			type = builder.type;
+			options = builder.getOptions();
+			value = builder.getValue();
+		}
+		
+		public String getDescription()
+		{
+			return desc;
+		}
+		
+		public boolean hasOptions()
+		{
+			return options!=null; 
+		}
+		
+		public int getOptionsSize()
+		{
+			return options.size();  
+		}
+		
+		/**
+		 * returns options as array of Object[] the label is the first element
+		 * the value the second  
+		 * @return Object[]
+		 */
+		public Object[] getOptions()
+		{
+			return options.toArray(); 
+		}
+		
+		/**
+		 * returns options as Object[] the label is the first element
+		 * the value the second  
+		 * @return Iterator
+		 */
+		public Iterator getOptionsIterator()
+		{
+			return options.iterator(); 
+		}
+		
+		public boolean getRequired()
+		{
+			return required; 
+		}
+		
+		public String getValue()
+		{
+			return value;
+		}
+		
+		public String getLabel()
+		{
+			return label;
+		}
+		
+		public String getVar()
+		{
+			return var;
+		}
+		
+		public String getType()
+		{
+			return type;
+		}
+
+		public void appendToXML(StringBuffer xml)
+		{
+			xml.append("<field");
+			appendAttribute(xml,"label",label);
+			appendAttribute(xml,"var",var);
+			appendAttribute(xml,"type",type);
+			xml.append(">");
+			if(desc !=null) xml.append("<desc>" +desc+ "</desc>"); //addmultiline
+			if(value !=null) xml.append("<value>"+ value + "</value>");//addmultiline
+			if(required == true) xml.append("<required/>");
+			if(options !=null)
+			{
+				for(Iterator i=options.iterator();i.hasNext();)
+				{
+					xml.append("<option");
+					String[] option = (String[])i.next(); 
+					if(option[0] != null) appendAttribute(xml,"label",option[0]);
+					xml.append(">");
+					if(option[1] != null) xml.append("<value>" + option[1] + "</value>");
+					xml.append("</option>");
+				}
+			}
+			xml.append("</field>");
+		}
+	}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/StreamError.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/StreamError.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/StreamError.java	(revision 3952)
@@ -0,0 +1,48 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 18-sep-2005
+ */
+
+package nu.fw.jeti.jabber.elements;
+
+
+public class StreamError extends Packet
+{
+	XMPPErrorTag error;
+	
+	public StreamError(XMPPErrorTag error)
+	{
+		this.error=error;
+	}
+	
+	public XMPPErrorTag getError()
+	{
+		return error;
+	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<stream:error xmlns:stream='http://etherx.jabber.org/streams'>");
+		if(error!=null)error.appendToXML(xml);
+		xml.append("</stream:error>");
+	}
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Packet.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Packet.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/Packet.java	(revision 3952)
@@ -0,0 +1,172 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.util.LinkedList;
+import java.util.Iterator;
+
+import nu.fw.jeti.jabber.*;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.backend.*;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public abstract class Packet extends XMLData
+{
+	private JID to;
+	private JID from;
+	protected String id; //protected so infoquery can set the id if unset
+	private int errorCode;
+	private String errorDescription;
+	private XMPPError xmppError;
+	private LinkedList extensions;
+
+	public Packet(){}
+
+	public Packet(JID to)
+	{
+		this.to = to;
+	}
+	
+	public Packet(Extension ex)
+	{
+		if(ex!=null)
+		{
+		    extensions = new LinkedList();
+		    extensions.add(ex);
+		}
+	}
+
+    public Packet(JID to,Extension ex)
+	{
+		this(ex);
+		this.to = to;
+	}
+
+	public Packet(JID to,String id,Extension ex)
+	{
+		this(to);
+		this.id = id;
+		if(ex!=null)
+		{
+		    extensions = new LinkedList();
+		    extensions.add(ex);
+		}
+	}
+	
+	public Packet(JID to,String id,String error,int errorCode)
+	{
+		this(to);
+		this.id = id;
+		this.errorDescription = error;
+		this.errorCode = errorCode; 
+	}
+	
+	public Packet(JID to,String id,XMPPError error)
+	{
+		this(to);
+		this.id = id;
+		xmppError = error; 
+	}
+
+	protected Packet(PacketBuilder pb)
+	{
+		to = pb.to;
+		from = pb.from;
+		id = pb.id;
+		errorCode = pb.errorCode;
+		errorDescription = pb.errorDescription;
+		extensions = pb.getExtensions();
+		xmppError = pb.xmppError;
+	}
+
+	public IQExtension getIQExtension()
+	{
+		if(extensions == null)return null;
+	    return (IQExtension)extensions.getFirst();
+	}
+
+	public XMPPError getXMPPError()
+	{
+		return xmppError;
+	}
+	
+	public Iterator getExtensions()
+	{
+		if(extensions == null) return null;
+	    return extensions.iterator();
+	}
+
+	public boolean hasExtensions(){return extensions != null;}
+
+	public String getID(){return id;}
+
+	public JID getTo(){return to;}
+
+	public JID getFrom(){return from;}
+
+	public int getErrorCode(){return errorCode;}
+
+	public String getErrorDescription() {
+       if (xmppError!=null && errorDescription.length() == 0) {
+           StringBuffer desc = new StringBuffer();
+            for(Iterator i = xmppError.getXMPPErrors(); i.hasNext();) {
+                if (desc.length() > 0) {
+                    desc.append(", ");
+                }
+                XMPPErrorTag et = (XMPPErrorTag)i.next();
+                desc.append(I18N.gettext("main.error.xmpp." + et.getError()));
+            }
+            if (desc.length() > 0) {
+                return desc.toString();
+            }
+            else return I18N.gettext("main.error.Unknown_error");
+        } else {
+            return errorDescription;
+        }
+    }
+
+	protected void appendBaseAttributes(StringBuffer xml)
+	{
+		appendAttribute(xml,"to",to);
+		appendAttribute(xml,"from",from);
+		appendAttribute(xml,"id", id);
+
+	}
+
+	protected void appendError(StringBuffer xml)
+	{
+		if(xmppError==null)
+		{
+			xml.append("<error");
+			appendAttribute(xml,"code",String.valueOf(errorCode));
+			if(errorDescription == null)
+			{
+				xml.append( "/>");
+				return;
+			}
+			xml.append(">");
+			escapeString(xml,errorDescription);
+			xml.append("</error>");
+		}
+		else xmppError.appendToXML(xml);
+	}
+
+	protected void appendExtensions(StringBuffer xml)
+	{
+		if(extensions !=null)
+		{
+			for(Iterator i = extensions.iterator();i.hasNext();)
+			{
+				((Extension)i.next()).appendToXML(xml);
+			}
+		}
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQDiscoInfo.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQDiscoInfo.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQDiscoInfo.java	(revision 3952)
@@ -0,0 +1,146 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+
+//Created on 6-aug-2004
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import nu.fw.jeti.jabber.Backend;
+
+/**
+ * @author E.S. de Boer
+ * implements the http://jabber.org/protocol/disco#info'/ spec
+ */
+public class IQDiscoInfo extends Extension implements IQExtension, DiscoveryInfo
+{
+	private String node;
+	private List identities;
+	private List features;
+	
+
+	public IQDiscoInfo()
+	{}
+	
+	public IQDiscoInfo(String node)
+	{
+		this.node = node;
+	}
+
+	public IQDiscoInfo(String node,List identities,List features)
+	{
+		this.node = node;
+		this.identities = identities;
+		this.features = features;
+	}
+
+	public String getNode()
+	{
+		return node;
+	}
+
+	public Iterator getIdentities()
+	{
+		if (identities == null)return null;
+		return identities.iterator();
+	}
+	
+	public List getFeatures()
+	{
+		return Collections.unmodifiableList(features);
+	}
+	
+	public boolean hasFeatures()
+	{
+		return features!=null;
+	}
+	
+	//discovery implementations
+	//use the first identity, browse can not have more identities
+	public String getName()
+	{
+		if (identities == null)return null;
+		return ((DiscoIdentity)identities.get(0)).getName();
+	}
+
+	//discovery implementations
+	//use the first identity, browse can not have more identities
+	public String getCategory()
+	{
+		if (identities == null)return null;
+		return ((DiscoIdentity)identities.get(0)).getCategory();
+	}
+
+	//discovery implementations
+	//use the first identity, browse can not have more identities
+	public String getType()
+	{
+		if (identities == null)return null;
+		return ((DiscoIdentity)identities.get(0)).getType();
+	}
+	
+
+//	public boolean hasChildItems()
+//	{
+//		return identities != null;
+//	}
+	
+	public void execute(InfoQuery iq,Backend backend){}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<query xmlns= 'http://jabber.org/protocol/disco#info'");
+		appendAttribute(xml, "node", node);
+		if (identities == null && features==null)
+		{ //short cut
+			xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		if (identities != null)
+		{
+			for (Iterator i = identities.iterator(); i.hasNext();)
+			{
+				((DiscoIdentity) i.next()).appendToXML(xml);
+			}
+		}
+		if(features!=null)
+		{
+			for (Iterator i = features.iterator(); i.hasNext();)
+			{
+				xml.append("<feature var='" + i.next() +"'/>");
+			}
+		}
+		xml.append("</query>");
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXCaps.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXCaps.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQXCaps.java	(revision 3952)
@@ -0,0 +1,71 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 21-feb-2007
+ */
+
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.jabber.Backend;
+
+/**
+  * @author E.S. de Boer
+ */
+
+public class IQXCaps  extends Extension implements IQExtension, XExtension{
+	private String node;
+	private String ver;
+	private String ext;
+
+	public IQXCaps(String node, String ver, String ext) {
+		this.node = node;
+		this.ver = ver;
+		this.ext = ext;
+	}
+
+	public String getExt() {
+		return ext;
+	}
+
+	public String getNode() {
+		return node;
+	}
+
+	public String getVer() {
+		return ver;
+	}
+	
+	public void execute(InfoQuery iq,Backend backend){}
+
+	public void appendToXML(StringBuffer xml)  {
+        xml.append("<c xmlns='http://jabber.org/protocol/caps'");
+		appendAttribute(xml,"node",node);
+		appendAttribute(xml,"ver",ver);
+		appendAttribute(xml,"ext",ext);
+		xml.append("/>");
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoItem.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoItem.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoItem.java	(revision 3952)
@@ -0,0 +1,76 @@
+// Created on 26-apr-2003
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Iterator;
+
+import nu.fw.jeti.backend.XMLData;
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * @author E.S. de Boer
+ * The items used in the disco spec
+ * @see nu.fw.jeti.jabber.elements.IQDiscoItems 
+ */
+public class DiscoItem extends XMLData implements DiscoveryItem
+{
+	private JID jid;
+	private String name;
+	private String node;
+	private String action;
+
+	public DiscoItem()
+	{}
+
+	public DiscoItem(JID jid,String name,String node,String action)
+	{
+		this.jid = jid;
+		this.name = name;
+		this.node = node;
+		this.action = action;
+	}
+
+	
+	public JID getJID()
+	{
+		return jid;
+	}
+
+	public String getName()
+	{
+		return name;
+	}
+
+	public String getNode()
+	{
+		return node;
+	}
+
+	public String getAction()
+	{
+		return action;
+	}
+	
+	//---------------DiscoveryItem------------------------
+	public Iterator getItems(){return null;}
+	
+	public int getSize() {return 0;}
+	
+	public boolean hasItems(){return false;}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<item");
+		appendAttribute(xml, "jid", jid);
+		appendAttribute(xml, "name", name);
+		appendAttribute(xml, "node", node);
+		appendAttribute(xml, "action", action);
+		xml.append("/>");
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/InfoQuery.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/InfoQuery.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/InfoQuery.java	(revision 3952)
@@ -0,0 +1,82 @@
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * Info/Query, or IQ, is a simple request-response mechanism. Just as HTTP is a request-response medium,
+ * the iq element enables an entity to make a request of, and receive a response from, another entity.
+ * The data content of the request and response is defined by the namespace declaration of a
+ * direct child element of the iq element.
+ * @see <a href=http://www.jabber.org/ietf/draft-miller-xmpp-core-00.html#iq>xmpp-core</a>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class InfoQuery extends Packet
+{
+	private static int ids=0;
+	private String type;
+	private static String ID_PREFIX = "Jeti_" + Math.random() +"_";
+
+	public InfoQuery(String type,IQExtension ex)
+    {
+		this(null,type,ID_PREFIX + ids++,ex);
+    }
+
+    public InfoQuery(JID to,String type,IQExtension ex)
+    {
+		this(to,type,ID_PREFIX + ids++,ex);
+    }
+
+	public InfoQuery(JID to,String type,String id,IQExtension ex)
+    {
+		super(to,id,(Extension)ex);
+		if(super.id==null) super.id = ID_PREFIX + ids++;
+		this.type = type;
+    }
+    
+//	public InfoQuery(JID to,String type,String id,IQExtension ex,String error,int errorCode)
+//	{//TODO remove 
+//		super(to,id,error,errorCode);
+//		type = "error";
+//	}
+	
+	public InfoQuery(JID to,String id,XMPPError error)
+	{
+		super(to,id,error);
+		if(super.id==null) super.id = ID_PREFIX + ids++; 
+		type = "error";
+	}
+
+
+	protected InfoQuery(InfoQueryBuilder iqb)
+	{
+	    super(iqb);
+	    if(id==null) id = ID_PREFIX + ids++;
+		type = iqb.getType();
+	}
+
+	public String getType(){return type;}
+
+    public void appendToXML(StringBuffer xml)
+    {
+        xml.append("<iq");
+		appendBaseAttributes(xml);
+		appendAttribute(xml,"type",type);
+		if(getExtensions() == null && !"error".equals(getType()))
+		{//short cut
+		    xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		if("error".equals(type)) appendError(xml);
+		appendExtensions(xml);
+		xml.append("</iq>");
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/JetiPrivateRosterExtension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/JetiPrivateRosterExtension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/JetiPrivateRosterExtension.java	(revision 3952)
@@ -0,0 +1,107 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ *
+ *	Created on 21-nov-2003
+ */
+ 
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.jabber.Backend;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class JetiPrivateRosterExtension extends Extension implements IQXExtension
+{
+    private String[] openGroups ;
+	private String xmlVer;
+	public final static String XML_VERSION ="v1";
+
+	public JetiPrivateRosterExtension(){}
+
+	public JetiPrivateRosterExtension(String[] openGroups)
+	{
+		this.openGroups = openGroups;
+		xmlVer = XML_VERSION;
+	}
+	
+	public JetiPrivateRosterExtension(String[] openGroups, String xmlVersion)
+	{
+		this.openGroups = openGroups;
+		xmlVer = xmlVersion;
+	}
+
+    public String[] getOpenGroups()
+    {
+	    return openGroups;
+    }
+    
+    public boolean isCorrectVersion()
+    {
+	    return XML_VERSION.equals(xmlVer);
+	}
+
+
+	public String getXmlVersion()
+    {
+	    return xmlVer;
+	}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		if (iq.getType().equals("result"))
+		{
+			backend.getMain().openGroups(this);
+		}
+		else if (iq.getType().equals("error"))
+		{
+			System.err.println(iq.getErrorDescription());
+		}
+	}
+	
+
+	public void appendToXML(StringBuffer retval)
+	{
+		retval.append("<jeti xmlns='jeti:rosterprefs'");
+		if (openGroups == null)
+		{ //short cut
+			retval.append("/>");
+			return;
+		}
+		appendAttribute(retval,"xmlVersion",xmlVer);
+		retval.append(">");
+		if(openGroups != null)
+		{
+			for (int i=0;i<openGroups.length;i++)
+			{
+				appendElement(retval,openGroups[i],true);
+			}
+		}
+		retval.append("</jeti>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/JetiPrivateExtension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/JetiPrivateExtension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/JetiPrivateExtension.java	(revision 3952)
@@ -0,0 +1,124 @@
+package nu.fw.jeti.jabber.elements;
+
+
+import java.util.*;
+
+import nu.fw.jeti.events.PreferenceListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+
+
+/**
+ * Title:        im
+ * Description:
+ * Copyright:    Copyright (c) 2001
+ * Company:
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class JetiPrivateExtension extends Extension implements IQXExtension
+{
+    private Map map;
+    private Map messages ;
+	private String xmlVer;
+	public final static String XML_VERSION ="v1";
+
+	public JetiPrivateExtension(){}
+
+	public JetiPrivateExtension(Map map,Map messages)
+	{
+		this.messages  = messages;
+		this.map = map;
+		xmlVer = XML_VERSION;
+	}
+
+	public JetiPrivateExtension(JetiExtensionBuilder builder)
+    {
+		map = Collections.unmodifiableMap(builder.getMap());
+		messages = Collections.unmodifiableMap(builder.getMapMessages());
+		xmlVer = builder.getXmlVersion();
+		if (xmlVer == null) xmlVer = "v1";//empty private space then newest version
+    }
+
+    public Map getMap()
+    {
+	    return map;
+    }
+
+	 public Map getMessages()
+    {
+	    return messages;
+    }
+
+	public String getXmlVersion()
+    {
+	    return xmlVer;
+	}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		if (iq.getType().equals("result"))
+		{
+			Preferences.load(this);
+			for (Iterator j = backend.getListeners(PreferenceListener.class); j.hasNext();)
+			{
+				((PreferenceListener) j.next()).preferencesChanged();
+			}
+		}
+		else if (iq.getType().equals("error"))
+		{
+			System.out.println("Preferences load error");
+		}
+	}
+
+	public void appendToXML(StringBuffer retval)
+	{
+		retval.append("<j2m xmlns=\"j2m:prefs\"");
+		if (map == null && messages == null)
+		{ //short cut
+			retval.append("/>");
+			return;
+		}
+
+		appendAttribute(retval,"xmlVersion",xmlVer);
+		if(map != null)
+		{
+			for (Iterator i=map.entrySet().iterator(); i.hasNext(); )
+			{
+				Map.Entry e = (Map.Entry) i.next();
+				appendAttribute(retval,(String)e.getKey(),(String)e.getValue());
+			}
+		}
+		retval.append(">");
+
+		if(messages != null)
+		{
+			retval.append("<status>");
+			for (Iterator i=messages.entrySet().iterator(); i.hasNext(); )
+			{
+				Map.Entry e = (Map.Entry) i.next();
+				{
+					retval.append("<"+ e.getKey());
+					List tempList = (List)e.getValue();
+					for(int tel=0;tel<tempList.size();tel++)
+					{
+						appendAttribute(retval,"s"+ tel,(String)tempList.get(tel));
+					}
+					retval.append("/>");
+				}
+			}
+			retval.append("</status>");
+		}
+		retval.append("</j2m>");
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoveryInfo.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoveryInfo.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoveryInfo.java	(revision 3952)
@@ -0,0 +1,57 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 7-aug-2004
+ */
+ 
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Iterator;
+import java.util.List;
+
+
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public interface DiscoveryInfo
+{
+	public String getName();
+
+	public String getType();
+
+	public String getCategory();
+
+	/**
+	 * @return an unmodifyable list of features of this IQ
+	 */
+	public List getFeatures();
+	
+	public boolean hasFeatures();
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/RosterBuilder.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/RosterBuilder.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/RosterBuilder.java	(revision 3952)
@@ -0,0 +1,51 @@
+package nu.fw.jeti.jabber.elements;
+import java.util.LinkedList;
+
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class RosterBuilder implements ExtensionBuilder
+{
+	private LinkedList items;
+
+    public RosterBuilder()
+    {
+		reset();
+    }
+
+	public void reset()
+	{
+		items = null;
+	}
+
+	public void addItem(RosterItem item)
+	{
+	    if(items ==null) items = new LinkedList();
+		items.add(item);
+	}
+
+	public LinkedList getItems()
+	{
+	    return items;
+	}
+
+	public Extension build()
+	{
+	    return new IQXRoster(this);
+	}
+
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoveryItem.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoveryItem.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/DiscoveryItem.java	(revision 3952)
@@ -0,0 +1,57 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 7-aug-2004
+ */
+ 
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Iterator;
+
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public interface DiscoveryItem
+{
+
+	public JID getJID();
+	
+
+	public String getName();
+	
+	public Iterator getItems();
+	
+	public boolean hasItems();
+
+	public int getSize();
+	//public String getNode();
+	
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDelay.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDelay.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XDelay.java	(revision 3952)
@@ -0,0 +1,64 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import nu.fw.jeti.jabber.JID;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public class XDelay extends Extension implements XExtension
+{
+	private String stamp;
+	private JID from;
+	private static DateFormat dateFormat = new java.text.SimpleDateFormat("yyyyMMdd'T'hh:mm:ss");
+
+	public XDelay(){}
+
+	public XDelay(String timeStamp, JID from)
+	{
+		this.from = from;
+		this.stamp = timeStamp;
+		dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+	}
+
+	public String getTimeStamp(){return stamp;}
+	
+	public Date getDate()
+	{
+		try
+		{
+			return dateFormat.parse(stamp);
+		} catch (java.text.ParseException e1)
+		{
+			e1.printStackTrace();
+		}
+		return null;
+	}
+
+	public JID getFrom(){return from;}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<x xmlns=\"jabber:x:delay\"");
+//		if(tz == null && utc == null && display ==null)
+//		{ //short cut
+//			xml.append("/>");
+//			return;
+//		}
+		appendAttribute(xml,"from",from);
+		appendAttribute (xml,"stamp",stamp);
+		xml.append('>');
+		xml.append("</x>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQAuth.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQAuth.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQAuth.java	(revision 3952)
@@ -0,0 +1,70 @@
+package nu.fw.jeti.jabber.elements;
+
+import nu.fw.jeti.jabber.Backend;
+
+
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQAuth extends Extension implements IQExtension
+{
+	/** @todo add zeroK */
+	private String username;
+	private String password;
+	private String digest;
+	private String resource;
+//	private String zeroKHash;
+//	private String zeroKToken;
+//	private String zeroKSequence;
+
+	public IQAuth(String username, String password,String resource)
+    {
+		this.username = username;
+		this.password = password;
+		this.resource = resource;
+    }
+
+	protected IQAuth(IQAuthBuilder iqab)
+	{
+	    username = iqab.username;
+		password = iqab.password;
+		digest = iqab.digest;
+		resource = iqab.resource;
+	}
+
+	public boolean hasDigest()
+	{
+		return digest != null;
+	}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		
+	}
+
+	public void appendToXML(StringBuffer xml)
+    {
+        xml.append("<query xmlns=\"jabber:iq:auth\">");
+		appendElement(xml,"username",username);
+		appendElement(xml,"password",password);
+		appendElement(xml,"digest",digest);
+		appendElement(xml,"resource",resource);
+		//appendChild(retval,"hash",ZeroKHash);
+		//appendChild(retval,"token",ZeroKToken);
+		//appendChild(retval,"sequence",   ((sequence==-1)?null:new Integer(sequence).toString()));
+		xml.append("</query>");
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQBrowse.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQBrowse.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQBrowse.java	(revision 3952)
@@ -0,0 +1,132 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class IQBrowse extends Extension implements IQExtension, DiscoveryItem, DiscoveryInfo
+{
+	private List namespaces;
+	private String name;
+	private String type;
+	private String category;
+	private String version;
+	private JID jid;
+	private List childItems;
+
+	public IQBrowse()
+	{
+	}
+
+	public IQBrowse(JID jid)
+	{
+		this.jid = jid;
+	}
+
+
+	public IQBrowse(IQBrowseBuilder ib)
+	{
+		name = ib.getName();
+		jid = ib.getJID();
+		category = ib.getCategory();
+		type = ib.getType();
+		version = ib.getVersion();
+		namespaces = ib.getNamespaces();
+		childItems = ib.getItems();
+	}
+
+
+	public String getName(){return name;}
+
+	public String getType(){return type;}
+
+	public String getCategory(){return category;}
+
+	public JID getJID(){return jid;}
+
+	public Iterator getItems()
+	{
+		if(childItems == null) return null;
+		return childItems.iterator();
+	}
+		
+	public int getSize()
+	{
+		if(childItems == null) return 0;
+		return childItems.size();
+	}
+
+	public boolean hasItems(){return childItems != null;}
+
+	public List getFeatures()
+	{
+		return Collections.unmodifiableList(namespaces);
+	}
+	
+//	public StringArray getNamespaces()
+//	{
+//		return namespaces;
+//	}
+
+	public boolean hasFeatures(){return namespaces != null;}
+
+	public void appendToXMLNoIQ(StringBuffer xml)
+	{
+		xml.append("<item");
+		toXML(xml);
+	}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		
+	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<item xmlns='jabber:iq:browse'");
+		toXML(xml);
+	}
+
+	private void toXML(StringBuffer xml)
+	{
+		appendAttribute(xml,"jid",jid);
+		appendAttribute(xml,"category",category);
+		appendAttribute(xml,"type",type);
+		appendAttribute(xml,"name",name);
+		appendAttribute(xml,"version",version);
+		if(namespaces == null && childItems == null)
+		{ //short cut
+			xml.append("/>");
+			return;
+		}
+		xml.append('>');
+		if(namespaces !=null)
+		{
+			for(Iterator i = namespaces.iterator();i.hasNext();)
+			{
+				appendElement(xml,"ns",i.next());
+			}
+		}
+		if(childItems != null)
+		{
+			  for(Iterator i = childItems.iterator();i.hasNext();)
+			  {
+				  ((IQBrowse)i.next()).appendToXMLNoIQ(xml);
+			  }
+		}
+		xml.append("</item>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQLast.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQLast.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/IQLast.java	(revision 3952)
@@ -0,0 +1,80 @@
+package nu.fw.jeti.jabber.elements;
+
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.util.Calendar;
+
+import javax.swing.JOptionPane;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public class IQLast extends Extension implements IQExtension
+{
+	private String seconds;
+	
+	public IQLast(){}
+	
+	public IQLast(String seconds)
+	{
+		this.seconds = seconds;
+	}
+
+	public String getSeconds(){return seconds;}
+	
+	public void execute(InfoQuery iq,Backend backend)
+	{
+		if (iq.getType().equals("get"))
+		{
+			//TODO idle time??
+			//sendTime(infoQuery.getFrom(),infoQuery.getID());
+		}
+		else if (iq.getType().equals("result"))
+		{
+			lastSeenPopup(iq.getFrom().toStringNoResource(), getSeconds());
+		}
+		else if (iq.getType().equals("error"))
+		{
+			Popups.errorPopup(iq.getErrorDescription(), I18N.gettext("main.error.Last_Seen_Error"));
+		}
+	}
+	
+	private void lastSeenPopup(String jid,String seconds)
+	{
+		int second = 0;
+		try{
+			second = Integer.parseInt(seconds);
+		}
+		catch (NumberFormatException e)
+		{
+			Popups.popup(MessageFormat.format(I18N.gettext("main.popup.{0}_was_last_seen_{1}_seconds_ago"),new Object[]{jid,seconds})  ,"main.popup.Last_Seen",JOptionPane.INFORMATION_MESSAGE);	
+		}
+		Calendar calendar = Calendar.getInstance();
+		//calendar.clear();   
+		calendar.add(Calendar.SECOND,-second);
+			 
+		DateFormat dateFormat = DateFormat.getDateTimeInstance();
+		Popups.popup(MessageFormat.format(I18N.gettext("main.popup.{0}_was_last_seen_on_{1}"),new Object[]{jid, dateFormat.format(calendar.getTime())}),I18N.gettext("main.popup.Last_Seen"),JOptionPane.INFORMATION_MESSAGE);
+		//JOptionPane.showMessageDialog(main,display  +"\n Time: " + time + "\n Timezone: " + timezone ,jid + "Time",javax.swing.JOptionPane.INFORMATION_MESSAGE);
+
+	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		xml.append("<query xmlns=\"jabber:iq:last\"");
+		appendAttribute(xml,"seconds",seconds);
+		xml.append("/>");
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMPPError.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMPPError.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/elements/XMPPError.java	(revision 3952)
@@ -0,0 +1,89 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 23-okt-2004
+ */
+ 
+package nu.fw.jeti.jabber.elements;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class XMPPError extends Extension
+{
+	private int errorCode;
+	private String type;
+	private List errors = new LinkedList();
+	
+	public XMPPError(String type, int errorCode)
+	{
+		this.errorCode= errorCode;
+		this.type=type;
+	}
+	
+	public void addError(XMPPErrorTag error)
+	{
+		 errors.add(error);
+	}
+	
+	public String getType()
+	{
+		return type;
+	}
+	
+	public int code()
+	{
+		return errorCode;
+	}
+
+    public Iterator getXMPPErrors() {
+        return errors.iterator();
+    }
+    
+    public XMPPErrorTag getFirstXMPPError() {
+        return (XMPPErrorTag)errors.get(0);
+    }
+
+	public void appendToXML(StringBuffer xml)
+	{
+	    xml.append("<error");
+		appendAttribute(xml,"code",String.valueOf(errorCode));
+		appendAttribute(xml,"type",type);
+		xml.append(">");
+		for(Iterator i = errors.iterator();i.hasNext();)
+		{
+			((XMPPErrorTag)i.next()).appendToXML(xml);
+		}
+		xml.append("</error>");
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/jabber/XDataCallback.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/jabber/XDataCallback.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/jabber/XDataCallback.java	(revision 3952)
@@ -0,0 +1,22 @@
+// Created on 29-jul-2003
+package nu.fw.jeti.jabber;
+
+import nu.fw.jeti.jabber.elements.XData;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public interface XDataCallback
+{
+	void sendForm(XData  xdata);
+	
+	void cancelForm();
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/languages/languages.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/languages/languages.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/languages/languages.java	(revision 3952)
@@ -0,0 +1,207 @@
+package nu.fw.jeti.languages;
+
+public interface languages
+{
+	public static final Object[][] language_pt_BR =
+	{  
+		{ "Abort" , "&Abortar"},
+		{ "Apply" , "&Aplicar"},
+		{ "Cancel" , "Cancelar"},
+		{ "Close" , "&Fechar"},
+		{ "Loading" , "Carregando"},
+		{ "initMessages.Available", "Disponível" },
+		{ "initMessages.Be_right_back", "Volto Logo"},
+		{ "initMessages.Busy", "Ocupado"},
+		{ "initMessages.Extend_Away", "Ausente" },
+		{ "initMessages.Free_for_Chat", "Livre p/ Chat"},		
+		{ "initMessages.Gone_home", "Em casa"},
+		{ "initMessages.In_a_meeting", "Em reunião"},
+		{ "initMessages.On_vacation", "Férias"},
+		{ "initMessages.On_the_phone", "No telefone"},
+		{ "initMessages.Working", "Trabalhando"},
+		{ "main.about.About_JETI" , "Sobre Jeti"},
+		{ "main.about.Version" , "Jeti Versão"},
+		{ "main.AddContact.Add_Contact" , "Adicionar contatos"},
+		{ "main.AddContact.Address" , "Endereço"},
+		{ "main.AddContact.Group" , "&Grupo"},
+		{ "main.AddContact.Nickname" , "&Apelido"},
+		{ "main.AddContact.Other" , "Outro"},
+		{ "main.AddContact.Screen_Name" , "Apelido na Tela"},
+		{ "main.AddContact.Select_Service" , "&Selecionar serviço:"},
+		{ "main.ChangePassword.Change_Password" , "Trocar senha"},
+		{ "main.ChangePassword.New_Password" , "Nova senha"},
+		{ "main.ChangePassword.Old_Password" , "Senha antiga"},
+		{ "main.ChangePassword.Password_not_equal" , "Senha não confere"},
+		{ "main.chat.{0}_(offline)" , "{0} (desconectado)"},
+		{ "main.chat.{0}_says_{1}" , "{0} diz: {1}"},
+		{ "main.chat.changed_status_to" , "trocou status para:"},
+		{ "main.chat.Chat_started_on" , "Chat iniciou em: "},
+		{ "main.chat.is_currently_offline" , " está desconectado"},
+		{ "main.chat.menu" , "Menu"},
+		{ "main.chat.menu.Close" , "Fechar aba"},
+		{ "main.chat.menu.Convert_to_tab" , "Converter aba em janela"},
+		{ "main.chat.menu.Move_to_Window" , "Move para janela"},
+		{ "main.chat.menu.To_front_on_new_message" , "Para frente numa nova mensagem"},
+		{ "main.chat.with_status_message" , "com status de mensagem:"},
+		{ "main.chat.You_are_about_to_close_{0}_tabs_Are_you_sure_you_want_to_close_them?" , "Você vai fechar {0} abas. Tem certeza que deseja fechá-las?"}, 
+		{ "main.connect.proxy.Authentication_for", "Autenticação para"},
+		{ "main.connect.proxy.Authentication_required", "Autenticação requerida"},
+		{ "main.connect.proxy.Cancel", "Cancel"},
+		{ "main.connect.proxy.Ok", "Ok"},
+		{ "main.connect.proxy.Password", "Password"},
+		{ "main.connect.proxy.The_proxy_is_requesting_a_username_and_password", "O proxy esta solicitando um usuário e senha"},
+		{ "main.connect.proxy.Username", "Username"},
+		{ "main.error.{0}_contains_no_plugins" , "{0} não contêm plugins"},
+		{ "main.error.{0}_is_not_valid" , "{0} não é válido"},
+		{ "main.error.{0}_plugin_failed_to_load" , "{0} plugin falhou ao carregar"},
+		{ "main.error.{0}_plugin_not_found" , "{0} plugin não encontrado"},
+		{ "main.error.Error" , "Erro"},
+		{ "main.error.Error_in_chat" , "Erro no bate-papo:"},
+		{ "main.error.Illegal_errorcode" , "Código de erro ilegal"},
+		{ "main.error.invalid_server" , "servidor inválido"},
+		{ "main.error.invalid_url_in_OOB_packet" , "url inválida no pacote OOB"},
+		{ "main.error.Last_Seen_Error" , "Último erro"},
+		{ "main.error.No_JID_found" , "JID não encontrado"},
+		{ "main.error.No_services" , "Sem serviço"},
+		{ "main.error.No_type" , "Sem tipo"},
+		{ "main.error.Register_Error" , "Erro no registro"},
+		{ "main.error.Register_Services" , "Registrar serviços"},
+		{ "main.error.registration_aborted,_code_{0}" , "registro abortado, código {0}."},
+		{ "main.error.Server_doesn't_support_browse" , "Servidor não suporta navegação"},
+		{ "main.error.Server_has_no_value" , "Servidor não tem valor"},
+		{ "main.error.Server_must_start_with_a_letter_(see_english_translation)" , "Servidor precisa começar com uma letra ou conter [A-Z], [a-z], [0-9], '.' or '-'"},
+		{ "main.error.Server_or_Username_contains_a_'@'" , "Servidor ou usuário contém @"},
+		{ "main.error.Service_error" , "Erro de serviço"},
+		{ "main.error.Time_Error" , "Erro de tempo"},
+		{ "main.error.type_must_be_specified" , "tipo precisa ser informado"},
+		{ "main.error.Unknown_error" , "erro desconhecido"},
+		{ "main.error.Unknown_Extension" , "extensão desconhecida"},
+		{ "main.error.Unknown_Packet" , "pacote desconhecido"},
+		{ "main.error.User_{0}_could_not_be_found" , "Usuário {0} não foi encontrado"},
+		{ "main.error.Username_>_255_Characters" , "Usuário > 255 caracteres"},
+		{ "main.error.Username_contains_illegal_chars_(see_english_translation)" , "Usuário contêm caracteres inválidos"},
+		{ "main.error.Version_Error" , "Erro na versão"},
+		{ "main.error.Wrong_contact_name" , "Nome de contato inválido"},
+		{ "main.error.Wrong_Jabber_Identifier" , "Identificador Jabber inválido (JID)"},
+		{ "main.error.wrong_type" , "tipo errado"},
+		{ "main.log.Clear_Log" , "&Texto puro"},
+		{ "main.log.Errors" , "Erros"},
+		{ "main.log.For_security_reasons_it_is_not_possible_to_change_passwords,_please_use_the_jeti_functions_for_that" , "Por razões de segurança, não é possível trocar sua senha, use as funções do Jeti para isso."},
+		{ "main.log.For_security_reasons_it_is_not_possible_to_remove_accounts,_please_use_the_jeti_functions_for_that" , "Por razões de segurança, não é possível remover contas, use as funções do Jeti para isso."},
+		{ "main.log.JETI_Log" , "Jeti Log"},
+		{ "main.log.Please_wait_loading_log" , "Por favor aguarde, carregando log"},
+		{ "main.log.Refresh" , "&Atualizar"},
+		{ "main.log.Send_XML" , "Enviar XML"},
+		{ "main.log.Send_XML_to_the_Jabber_server,_use_this_at_your_own_risk" , "Enviar XML para o servidor Jabber, use isso por sua conta própria."},
+		{ "main.log.XML" , "XML"},
+		{ "main.log.XML_Errors" , "XML Erros"},
+		{ "main.loginstatus.Could_not_login_with_SHA" , "Não poder carregar login com SHA"},
+		{ "main.loginstatus.Could_not_open_input_because" , "Não pode abrir entrada porque"},
+		{ "main.loginstatus.Could_not_open_output_because" , "Não pode abrir saída porque"}, 
+		{ "main.loginstatus.Lost_Input" , "Entrada perdida"},
+		{ "main.loginstatus.Not_logged_in_because" , "Não conectado porque"},
+		{ "main.loginstatus.Server_{0}_could_not_be_found" , "Servidor {0} não pode ser encontrado."},
+		{ "main.main.jetimenu.About" , "Sobre Jeti"},
+		{ "main.main.jetimenu.Add_Contact" , "Adicionar Contato"},
+		{ "main.main.jetimenu.Chat" , "&Bate-papo"},
+		{ "main.main.jetimenu.Exit" , "Sair"},
+		{ "main.main.jetimenu.Show_Log" , "Mostrar Jeti &Log"},
+		{ "main.main.List_Contacts" , "Lista de Contatos"},
+		{ "main.main.Offline", "Desconectado" },
+		{ "main.main.Online",  "Conectado" },
+		{ "main.main.presencebutton.Available", "&Conectado"},
+		{ "main.main.presencebutton.Away","&Ausente"},
+		{ "main.main.presencebutton.DND","&Não pertube"},
+		{ "main.main.presencebutton.FreeforChat","&Livre para bate-papo"},
+		{ "main.main.presencebutton.Invisible", "&Invisível"},
+		{ "main.main.presencebutton.Offline", "&Desconectado"},
+		{ "main.main.presencebutton.XA", "&Ausência prolongada"},
+		{ "main.main.roster.Friends" , "Contatos"},
+		{ "main.main.roster.Servers" , "Servidores"},
+		{ "main.main.roster.Status" , "Status:"},
+		{ "main.main.roster.Subscription" , "Descrição:"},
+		{ "main.main.roster.Unfiled" , "Sem Grupo"},
+		{ "main.main.roster.Waiting_Status" , "Aguardando Status:"},
+		{ "main.main.rostermenu.Add_to_Group" , "Adicionar no grupo"},
+		{ "main.main.rostermenu.Change_Group" , "Trocar Grupo"},
+		{ "main.main.rostermenu.Chat" , "&Bate-papo"},
+		{ "main.main.rostermenu.Edit_Registration" , "Editar registro"},
+		{ "main.main.rostermenu.Group" , "&Grupo"},
+		{ "main.main.rostermenu.Invisible" , "&Invisível"},
+		{ "main.main.rostermenu.Last_Seen" , "Último acesso"},
+		{ "main.main.rostermenu.Local_Time" , "Local &Data"},
+		{ "main.main.rostermenu.Local_Version" , "Local &Versão"},
+		{ "main.main.rostermenu.Log_Off" , "Desconectar"},
+		{ "main.main.rostermenu.Log_On" , "Conectar"},
+		{ "main.main.rostermenu.Message" , "&Mensagem"},
+		{ "main.main.rostermenu.Remove" , "Remover"},
+		{ "main.main.rostermenu.Remove_from_group" , "&Remover do grupo"},
+		{ "main.main.rostermenu.Rename" , "&Renomear"},
+		{ "main.main.rostermenu.Set_Invisible" , "Ficar invisível"},
+		{ "main.main.rostermenu.Set_Visible" , "Ficar visível"},
+		{ "main.main.rostermenu.Subscribe_from" , "Autorizar"},
+		{ "main.main.rostermenu.Subscriptions" , "Autorizações"},
+		{ "main.main.rostermenu.Unsubscribe_from" , "Cancelar autorização"},
+		{ "main.main.statusmenu.Change_message" , "&Trocar mensagem"},
+		{ "main.main.statusmenu.Manage_messages" , "&Gerenciar mensagens"},
+		{ "main.main.statusmenu.Status_message" , "Mensagem de Status:"},
+		{ "main.manageservices.Change_Server" , "&Trocar servidor"},
+		{ "main.manageservices.Manage_Services" , "&Gerenciar serviços"},
+		{ "main.manageservices.Register" , "&Registrar"},
+		{ "main.manageservices.Server" , "&Servidor"},
+		{ "main.pluginsInfo.has_no_remove" , "não foi removido"},
+		{ "main.pluginsInfo.Plugin_removing", "Plugin removido"},
+		{ "main.pluginsInfo.plugins_unloaded", "plugins unloaded"},		
+		{ "main.popup.{0}_is_in_the_last_group,_remove_completely?" , "{0} é o último grupo, remover completamente?"},
+		{ "main.popup.{0}_is_in_timezone_{1}" , "{0} fuso-horário {1}"},
+		{ "main.popup.{0}_not_found" , "{0} não encontrado."},
+		{ "main.popup.{0}_not_found_until_the_end_of_this_file,_start_again_from_the_beginning?" , "{0} não encontrado o final no arquivo, começar novamente no início do arquivo?"},
+		{ "main.popup.{0}_uses_{1},_version_{2},_On_{3}" , "{0} use: {1}, versão: {2}, On: {3}"},
+		{ "main.popup.{0}_wants_to_subscribe_to_your_presence" , "{0} quer registrar sua presença"},
+		{ "main.popup.{0}_was_last_seen_{1}_seconds_ago" , "{0} foi visto {1} segundos atrás."},
+		{ "main.popup.{0}_was_last_seen_on_{1}" , "{0} foi visto em {1}"},
+		{ "main.popup.Add_{0}_to" , "Adicionar {0} para :"},
+		{ "main.popup.Add_to_roster" , "Adicionar para roster"},
+		{ "main.popup.Change_{0}_from_{1}_to" , "Trocar {0} para {1} to :"},
+		{ "main.popup.Deny_subscription" , "Negar registrar"},
+		{ "main.popup.Enter_the_text_to_search_for" , "Entre o texto de pesquisa para"}, 
+		{ "main.popup.Find" , "Procurar"},
+		{ "main.popup.Last_Seen" , "Visto em"},
+		{ "main.popup.Local_time_for_{0}_is" , "Hora local para {0} é"},
+		{ "main.popup.logged_in_as_{0}_on_server_{1}_with_resource_{2}" , "Logado como: {0}non server: {1}nwith resource: {2}\nJID "  ,"\"{0}@{1}/{2}\""},
+		{ "main.popup.Message" , "Mensagem"},
+		{ "main.popup.Really_remove_{0}_from_all_groups_?" , "Realmente remover {0} todos os grupos?"},
+		{ "main.popup.Really_remove_{0}?_All_JIDs_in_this_group_will_be_removed!" , "Realmente remover {0}? Todos JIDs neste grupo serão removidos!"},
+		{ "main.popup.Remove_Group" , "Remover Grupo"},
+		{ "main.popup.Rename_{0}_to" , "Renomear {0} to:"},
+		{ "main.popup.Reply" , "Responder"},
+		{ "main.popup.Subject" , "Informação"},
+		{ "main.popup.Subscription_request" , "Requer assinatura"},
+		{ "main.popup.Time" , "Tempo"},
+		{ "main.popup.Version" , "Versão"},
+		{ "main.presence.Available" , "Disponível"},
+		{ "main.presence.Away" , "Ausente"},
+		{ "main.presence.Do_not_Disturb" , "Não Perturbe"},
+		{ "main.presence.Extended_Away" , "Ausência Prolongada"},
+		{ "main.presence.Free_for_Chat" , "Bate-papo"},
+		{ "main.presence.Invisible" , "Invisível"},
+		{ "main.presence.Unavailable" , "Indisponível"},
+		{ "main.presence.Unknown" , "Desconhecido"},
+		{ "main.register.Instructions" , "Instruções"},
+		{ "main.register.Password" , "Senha"},
+		{ "main.register.Register" , "Registar"},
+		{ "main.register.registration_succeded" , "registrado com sucesso."},
+		{ "main.register.Username" , "Usuário"},
+		{ "main.statusmessages.Add" , "&Adicionar"},
+		{ "main.statusmessages.Manage_Status_Messages" , "Gerenciar Mensagens"},
+		{ "main.statusmessages.Remove" , "&Remover"},
+		{ "Name" , "Nome"},
+		{ "OK" , "&OK"},
+		{ "Organization" , "Organização"},
+		{ "Remove" , "&Remover"},
+		{ "Retry" , "&Tentar"},
+		{ "Save" , "Salvar"},
+		{ "Send" , "&Enviar"},
+	};
+	
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/languages/languagesPlugins.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/languages/languagesPlugins.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/languages/languagesPlugins.java	(revision 3952)
@@ -0,0 +1,71 @@
+package nu.fw.jeti.languages;
+
+public interface languagesPlugins
+{
+	/**
+	 *  Loag Lang PT_BR for plugins
+	 */
+	
+	public static final Object[][] languagePlugins_pt_BR =
+	{
+		{ "callVoip.Call_Voip", "Call Voip"},
+		{ "callVoip.Calling" , "Ligando para" },		
+		{ "callVoip.Dialing_for_business_phone", "Discar para telefone comercial"},
+		{ "callVoip.Status", "Status"},
+		{ "filetransfer.{0}_already_exist,_overwrite?" , "{0} já existe, sobrescrever?"},
+		{ "filetransfer.{0}_could_not_be_openend_in_write_mode" , "{0} não pode ser aberto em modo de escrita."},
+		{ "filetransfer.{0}_did_not_accept_the_file" , "{0} não pode aceitar o arquivo"},
+		{ "filetransfer.{0}_does_not_exist" , "{0} não existe"},
+		{ "filetransfer.{0}_does_not_support_file_transfer_or_is_offline" , "{0} não suporta transferência de arquivos ou está offline."},
+		{ "filetransfer.{0}_is_not_online" , "{0} não esta online."},
+		{ "filetransfer.{0}_is_not_readable" , "{0} não esta legível."},
+		{ "filetransfer.Abort" , "Abortar"},
+		{ "filetransfer.automatic" , "automático"},
+		{ "filetransfer.behind_firewall_or_NAT" , "atrás de um firewall ou  NAT"},
+		{ "filetransfer.Can_not_transfer_directories" , "não pode transferir diretórios"},
+		{ "filetransfer.Close" , "Fechar"},
+		{ "filetransfer.close_download_windows_when_download_ready" , "Fechar a janela quando terminar o download."},
+		{ "filetransfer.Description" , "&Descrição"},
+		{ "filetransfer.Download" , "Download"},
+		{ "filetransfer.File_Error" , "Erro no arquivo"},
+		{ "filetransfer.File_Name" , "&Nome do arquivo"},
+		{ "filetransfer.File_Size" , "Tamanho do arquivo"},
+		{ "filetransfer.File_Transfer" , "Transferência de arquivo"},
+		{ "filetransfer.From" , "De"},
+		{ "filetransfer.ip" , "ip"},
+		{ "filetransfer.No_compatible_transfer_protocols_found" , "Sem protocolos compatíveis para transferência"},
+		{ "filetransfer.No_recipient_specified" , "Destinatário não especificado."},
+		{ "filetransfer.Opening_connection" , "Estabelecendo conexão"},
+		{ "filetransfer.port" , "porta"},
+		{ "filetransfer.Problem_during_file_transfer,_transfer_aborted" , "Problemas durante a transferência, transferência abortada."},
+		{ "filetransfer.Recipient?" , "Destinatário?"},
+		{ "filetransfer.Send" , "&Enviar"},
+		{ "filetransfer.Send_file_to" , "Mandar arquivo para"},
+		{ "filetransfer.To" , "Para"},
+		{ "filetransfer.Transfer_complete" , "transferência completa"},
+		{ "filetransfer.Transfer_File" , "Transferir arquivo"},
+		{ "filetransfer.Transferring" , "Transferindo"},
+		{ "filetransfer.URL" , "&URL"},
+		{ "xhtml.{0}_does_not_support_formatted_messages" , "{0} não suporta mensagens formatadas" },
+		{ "xhtml.background" , "fundo" },
+		{ "xhtml.Bold" , "Negrito" },
+		{ "xhtml.BoldItalic" , "Negrito/Itálico" },
+		{ "xhtml.Choose_Font" , "Escolher fonte" },
+		{ "xhtml.Color" , "Cor" },
+		{ "xhtml.Font" , "Fonte" },
+		{ "xhtml.Font_Family" , "Família de Fonte" },
+		{ "xhtml.foreground" , "primeiro plano" },
+		{ "xhtml.Formats_messages" , "Formartar mensagens" },
+		{ "xhtml.Italic" , "Itálico" },
+		{ "xhtml.Plain" , "Plano" },
+		{ "xhtml.Preview" , "Pre-vizualizar" },
+		{ "xhtml.Select_Color" , "Selecionar cor" },
+		{ "xhtml.Size" , "Tamanho" },
+		{ "xhtml.Style" , "Estilo" },
+		{ "xhtml.Transfer_File" , "Transferência de arquivo" },
+		{ "xhtml.The_font_used_when_sending_messages" , "A fonte usada para mandar mensagem" },
+		{ "xhtml.The_quick_brown_fox_jumped_over_the_lazy_dogs" , "A ligeira raposa ultrapassou os lobos preguiçosos" }, 
+		{ "xhtml.Underline" , "Sublinhado" }
+	};
+
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/Input.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/Input.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/Input.java	(revision 3952)
@@ -0,0 +1,161 @@
+package nu.fw.jeti.backend;
+
+
+import java.io.*;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import nu.fw.jeti.plugins.PluginsInfo;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class Input extends Thread
+{
+	private InputSource in;
+	private ConnectionPacketReceiver backend;
+	private JabberHandler jH;
+	private boolean closing = false;
+
+    public Input(InputStream stream,ConnectionPacketReceiver backend,JabberHandler jH)
+    {
+		try{
+			if(PluginsInfo.isPluginLoaded("xmpp"))
+			{
+				in = new InputSource(new XMPPInputStream(new InputStreamReader(stream,"UTF-8")));
+			}
+			else
+			{
+				in = new InputSource(new InputStreamReader(stream,"UTF8"));
+				//in = new InputSource(new LogInputStream(new InputStreamReader(stream,"UTF8")));
+			}
+		}catch(UnsupportedEncodingException e){e.printStackTrace();}
+		this.backend = backend;
+		this.jH = jH;
+		start();
+    }
+        
+    public void disconnect(){
+    	try {
+    		System.out.println("closing " + in.getCharacterStream());
+    		closing = true;
+			in.getCharacterStream().close();
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+    }
+        
+	public void run()
+	{
+		try{
+			
+			/** @todo look at parsers */
+
+//direct, higer mem?
+//SAXParser parser =new org.apache.crimson.jaxp.SAXParserFactoryImpl().newSAXParser();
+
+
+			SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+		   	parser.parse(in,jH);
+		}
+		catch(ParserConfigurationException pce){pce.printStackTrace();}
+		catch (SAXParseException spe)
+		{
+			Exception e = spe.getException();
+			if(e != null) e.printStackTrace();
+			else System.err.println(spe.getMessage());
+		}
+		catch(SAXException se)
+		{
+			if("end xmlparser".equals(se.getMessage())) {
+				closing = true;
+				return;
+			}
+			Exception e = se.getException();
+			if(e != null) e.printStackTrace();
+			else System.err.println(se.getMessage());
+		}
+		catch(IOException e)
+		{
+            // No need to do anything here.
+			System.err.println(e.getMessage());
+		}
+		catch (UnsupportedOperationException e)	{
+			if("end xmlparser".equals(e.getMessage())) {
+				closing = true;
+				return;
+			}
+			System.err.println(e.getMessage());
+		}
+		finally{
+			System.out.println(in.getCharacterStream() + "closed");
+			if(!closing) backend.inputDeath();
+		}
+        
+	}
+	
+//	//prints inputstream
+//	class LogInputStream extends FilterReader
+//	{
+//		LogInputStream(Reader reader)
+//		{
+//			 super(reader);
+//		}
+//
+//		public int read(char[] cbuf, int off, int len) throws IOException
+//		{
+//			int readed = super.read(cbuf, off, len);
+//			if(readed == -1) System.out.println("end of stream detected readed");
+//			else System.out.println(new String(cbuf, off,readed).trim());
+//			return readed;
+//		}
+//
+//		
+//		public int read(char[] cbuf) throws IOException
+//		{
+//			int readed = super.read(cbuf);
+//			System.out.println(new String(cbuf).trim());
+//			return readed;
+//		}
+//	
+//		public void close()
+//		{//stop close of stream from parser
+//			System.out.println("close stream");
+//		}
+//	}
+}
+
+class XMPPInputStream extends FilterReader
+{
+	XMPPInputStream(Reader reader)
+	{
+		 super(reader);
+	}
+	
+	//stop close of stream from parser, 
+	//needed to prevent stopping xml parser to close the socket
+	public void close()
+	{
+		System.out.println("close stream" + this);
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/Handlers.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/Handlers.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/Handlers.java	(revision 3952)
@@ -0,0 +1,90 @@
+package nu.fw.jeti.backend;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import nu.fw.jeti.jabber.handlers.*;
+import nu.fw.jeti.plugins.PluginsInfo;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class Handlers
+{
+	private Map handlers;
+	private Map xmppHandlers;
+	private InfoQueryHandler iqHandler;
+	private PresenceHandler presenceHandler;
+	private MessageHandler messageHandler;
+	private StreamErrorHandler streamErrorHandler;
+
+    public Handlers()
+    {
+		handlers = new HashMap(10);
+		iqHandler = new InfoQueryHandler();
+		presenceHandler  = new PresenceHandler();
+		messageHandler = new MessageHandler();
+		streamErrorHandler = new StreamErrorHandler();
+		handlers.put("jabber:iq:auth",new IQAuthHandler());
+		handlers.put("jabber:iq:roster",new RosterHandler());
+		handlers.put("jabber:iq:time",new IQTimeHandler());
+		handlers.put("jabber:iq:version",new IQVersionHandler());
+		handlers.put("jabber:iq:register",new IQRegisterHandler());
+		handlers.put("j2m:prefs",new JetiPrivateHandler());
+		handlers.put("jeti:rosterprefs",new JetiPrivateRosterHandler());
+		handlers.put("jabber:iq:private",new IQPrivateHandler());
+		handlers.put("jabber:x:event",new XMessageEventHandler());
+		handlers.put("jabber:x:delay",new XDelayHandler());
+		handlers.put("jabber:iq:browse",new IQBrowseHandler());
+		handlers.put("jabber:iq:last",new IQLastHandler());
+		handlers.put("jabber:iq:oob",new IQXOOBHandler());
+		handlers.put("jabber:x:data",new XDataHandler());
+		handlers.put("http://jabber.org/protocol/disco#info",new IQDiscoInfoHandler());
+		handlers.put("http://jabber.org/protocol/disco#items",new IQDiscoItemsHandler());
+		handlers.put("http://jabber.org/protocol/caps",new IQXCapsHandler());
+		handlers.put("urn:ietf:params:xml:ns:xmpp-stanzas",new XMPPErrorHandler());
+		handlers.put("urn:ietf:params:xml:ns:xmpp-streams",new XMPPErrorHandler());
+		handlers.put("unknown",new UnknownExtensionHandler());
+    }
+    
+    public void loadExtraHandlers(Map extraHandlers)
+    {
+    	if(PluginsInfo.isPluginLoaded("xmpp"))xmppHandlers = extraHandlers;
+    }
+
+	public PacketHandler getPacketHandler(String namespace)
+	{//iq message & presence
+		if ("iq".equals(namespace)) return iqHandler;
+		else if ("presence".equals(namespace)) return presenceHandler;
+		else if("message".equals(namespace)) return messageHandler;
+		else if ("stream:error".equals(namespace)) return streamErrorHandler;
+		else if(xmppHandlers!=null)
+		{
+			return (PacketHandler)xmppHandlers.get(namespace);
+		}
+		else return null;
+	}
+
+	public synchronized ExtensionHandler getExtensionHandler(String namespace)
+	{
+		return (ExtensionHandler) handlers.get(namespace);
+	}
+	
+	public synchronized void addExtensionHandler(String namespace,ExtensionHandler handler)
+	{
+		handlers.put(namespace,handler);
+	}
+	
+	public synchronized void removeExtensionHandler(String namespace)
+	{
+		handlers.remove(namespace);
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/Output.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/Output.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/Output.java	(revision 3952)
@@ -0,0 +1,192 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/im me at jeti@jabber.org
+ *  
+ *  created 2001
+ */
+
+package nu.fw.jeti.backend;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.net.Socket;
+import java.util.LinkedList;
+
+import nu.fw.jeti.jabber.elements.Packet;
+import nu.fw.jeti.plugins.PluginsInfo;
+import nu.fw.jeti.util.Preferences;
+
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class Output extends Thread
+{
+	private Writer out;
+	private LinkedList queue = new LinkedList();
+	private volatile boolean isRunning=true;
+	private volatile boolean closeSocketOnExit=true;
+	private Socket socket;
+	public static final int KEEP_ALIVE = 60000;
+	private KeepAlivePacket keepAlivePacket =  new KeepAlivePacket();
+	private ConnectionPacketReceiver backend;
+	private volatile boolean authenticated = false;
+	private String host;
+	
+    public Output(Socket socket,String host,ConnectionPacketReceiver backend,OutputStream stream) throws IOException
+    {
+		this.socket = socket;
+		this.backend = backend;
+		this.host = host;
+		try{
+		    out = new BufferedWriter(new OutputStreamWriter(stream,"UTF8"));
+		}catch (UnsupportedEncodingException e){e.printStackTrace();}
+		writeHeader();
+		start();
+	}
+    
+    public void writeHeader() throws IOException
+    {
+    	//send stream init tag
+		out.write("<?xml version='1.0' encoding='UTF-8'?>");
+	 	out.write("<stream:stream xmlns:stream='http://etherx.jabber.org/streams' ");
+		out.write("xmlns='jabber:client' ");
+		if(PluginsInfo.isPluginLoaded("xmpp"))out.write("version='1.0' ");
+		out.write("to='");
+		out.write(host);
+		String lang = Preferences.getString("jeti","language",null);
+		if(lang!=null)
+		{
+			String country = Preferences.getString("jeti","country",null);
+			if(country!=null)lang+="-"+country;
+			out.write("' xml:lang='");
+			out.write(lang);
+		}
+		out.write("'>");
+		out.flush();
+    }
+
+    public void send(Packet p)
+    {
+		if(isRunning)
+		{
+			synchronized(queue)
+			{
+				queue.addLast(p);
+				queue.notifyAll();
+			}
+		}
+	}
+
+	public void disconnect(boolean closeSocket)
+	{
+		this.closeSocketOnExit = closeSocket;
+	    isRunning = false;
+		synchronized(queue){queue.notifyAll();}
+	}
+	
+	public void setAuthenticated()
+	{
+		authenticated = true;
+	}
+
+    public final void run()
+    {
+    	Packet packet;
+		while ((isRunning) || (!queue.isEmpty()))
+		{
+			synchronized(queue)
+			{
+				if (queue.isEmpty())
+				{
+					if(isRunning)
+					{
+						try
+						{
+							queue.wait(KEEP_ALIVE);
+						}
+						catch(InterruptedException e)
+						{
+							e.printStackTrace();
+							return;
+						}
+						if(authenticated && queue.isEmpty()) queue.add(keepAlivePacket);
+					}
+					continue;
+				}
+				else packet =(Packet) queue.removeFirst();
+			}
+			//send packet
+			String xml = packet.toString();
+			try
+			{
+				nu.fw.jeti.util.Log.sendXML(xml);
+				out.write(xml);
+				out.flush();
+			}
+			catch(IOException e)
+			{
+				if(!socket.isClosed())
+				{	
+					nu.fw.jeti.util.Log.notSend(xml);
+					if(isRunning) backend.outputDeath();//only reconnect if output thread in running status
+	                try{
+	                	System.out.println("closing socket");
+	                    socket.close();
+	                } catch (IOException ex){ }
+				}
+				return;
+			}
+		}
+		if(closeSocketOnExit && !socket.isClosed())
+		{	
+			try
+			{//close stream
+				out.write("</stream:stream>");
+				out.flush();
+				System.out.println("closing socket");
+				socket.close();
+				
+			}
+			catch(Exception e)
+			{e.printStackTrace();}
+		}
+    }
+}
+
+class KeepAlivePacket extends Packet
+{
+    public void appendToXML(StringBuffer xml)
+    {
+        xml.append(' ');
+    }
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/XExecutableExtension.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/XExecutableExtension.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/XExecutableExtension.java	(revision 3952)
@@ -0,0 +1,23 @@
+// Created on 14-okt-2003
+package nu.fw.jeti.backend;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.Packet;
+import nu.fw.jeti.jabber.elements.XExtension;
+
+/**
+ * @author E.S. de Boer
+ * Exectutable Xextension
+ * this extension can be executed to allow Xextension in plugins
+ */
+public interface XExecutableExtension extends XExtension
+{
+	public void execute(Packet packet,final Backend backend);
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/NewAccount.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/NewAccount.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/NewAccount.java	(revision 3952)
@@ -0,0 +1,126 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/im me at jeti@jabber.org
+ *  
+ *  created 2001
+ */
+
+package nu.fw.jeti.backend;
+
+import java.io.*;
+import java.net.*;
+
+import nu.fw.jeti.backend.Connect.DummySSLSocketFactory;
+import nu.fw.jeti.jabber.elements.*;
+import nu.fw.jeti.ui.RegisterWindow;
+
+import nu.fw.jeti.jabber.*;
+
+/**
+ * @author E.S. de Boer
+ * 
+ */
+public class NewAccount implements ConnectionPacketReceiver
+{
+	private String server;
+	private String username;
+	private String password;
+	private Output output;
+	private String registerId="";
+	private RegisterWindow registerWindow;
+
+	public NewAccount(String server,int port,Handlers handlers,String username,String password)
+	{
+		this.password = password;
+		this.username = username;
+		try{
+		    connect(server,port,handlers);
+		}catch (IOException e){e.printStackTrace();}
+	}
+
+	public void connect(String host, int port,Handlers handlers) throws IOException
+	{
+		if(port==5222)port=5223;
+		JabberHandler jH = new JabberHandler(this,handlers);
+		//Socket socket = new Socket(host,port);
+		Socket socket = new DummySSLSocketFactory().createSocket(host,port);
+		new Input(socket.getInputStream(),this,jH);
+		output = new Output(socket,host,this,socket.getOutputStream());
+		server = host;
+	}
+
+	public void connected(String connectionID,String xmppVersion)
+	{
+		send(new InfoQuery(new JID(server),"get",new IQRegister()));
+	}
+
+	public void receivePackets(Packet packet)
+	{
+		if(registerId.equals(packet.getID()))
+		{
+			InfoQuery query = (InfoQuery) packet;
+			if(query.getType().equals("result"))
+			{
+			    registerWindow.login();
+				output.disconnect(true);
+			}
+			//maak mooi
+			else registerWindow.error(packet.getErrorDescription());
+		}
+		else if(packet instanceof InfoQuery)
+		{
+		    IQExtension extension = packet.getIQExtension();
+			if(extension instanceof IQRegister)
+			{
+				new RegisterWindow(this,(IQRegister)extension,username,password).setVisible(true);
+			}
+
+		}
+	}
+
+	public void sendRegister(IQRegister register,RegisterWindow window)
+	{
+		registerWindow = window;
+		registerId = "JetiRegister_"+ new java.util.Date().getTime();
+		send(new InfoQuery(null,"set",registerId,register));
+	}
+
+
+	public void inputDeath()
+	{//verbeter
+		System.out.println("input death");
+	}
+
+	public void outputDeath()
+	{//verbeter
+		System.out.println("input death");
+	}
+
+	public void send(Packet packet)
+	{
+		output.send(packet);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/Discovery.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/Discovery.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/Discovery.java	(revision 3952)
@@ -0,0 +1,249 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.backend;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import nu.fw.jeti.events.DiscoveryListener;
+import nu.fw.jeti.events.StatusChangeListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.*;
+
+/**
+ * @author E.S. de Boer
+ */
+//created on 6-8-2004
+public class Discovery implements DiscoveryListener, StatusChangeListener
+{
+	private Backend backend;
+	private Map infoCache = new HashMap();
+	private Map itemCache = new HashMap();
+	private Map itemRequests = new HashMap(5);
+	private Map infoRequests = new HashMap(10);
+	private Map timeoutTimers = new HashMap(20);
+	private Timer timer = new Timer(true);
+	
+	private int idCount;
+
+    public Discovery(Backend backend)
+    {
+		this.backend = backend;
+    }
+    	
+	public void getItems(JID jid,DiscoveryListener listener) {
+        getItems(jid, listener, true);
+    }
+
+	public void getItems(JID jid,DiscoveryListener listener, boolean useCache)
+    {
+		if (listener == null) listener = this;
+		DiscoveryItem item = null;
+        if (!useCache || null == (item = (DiscoveryItem)itemCache.get(jid)))
+		{//first try disco
+			String id = "Jeti_Disco_" + idCount++;
+			InfoQuery iq = new InfoQuery(jid,"get",id,new IQDiscoItems());
+			timeout(iq);
+			itemRequests.put(id,listener);
+			backend.send(iq);
+		} else {
+            listener.discoveryItemResult(jid,item);
+        }
+	}
+	
+	public void getInfo(JID jid,DiscoveryListener listener)
+	{
+		if (listener == null) listener = this;
+		DiscoveryInfo info = (DiscoveryInfo)infoCache.get(jid);
+		if(info == null)
+		{//first try disco
+			String id = "Jeti_Disco_" + idCount++;
+			InfoQuery iq =new InfoQuery(jid,"get",id,new IQDiscoInfo());
+			timeout(iq);
+			infoRequests.put(id,listener);
+			backend.send(iq);
+		}
+		else listener.discoveryInfoResult(jid,info);
+	}
+	
+	//no cache TODO add no browse if node query error
+	public void getItems(JID jid,String node,DiscoveryListener listener)
+	{
+		if (listener == null) return;//no cache so empty listener is useless
+		//DiscoveryItem item = (DiscoveryItem)itemCache.get(jid);
+		//if(item == null)
+		{
+			String id = "Jeti_Disco_" + idCount++;
+			InfoQuery iq = new InfoQuery(jid,"get",id,new IQDiscoItems(node));
+			timeout(iq);
+			itemRequests.put(id,listener);
+			backend.send(iq);
+		}
+		//else listener.discoveryItemResult(jid,item);
+	}
+	
+	public void getInfo(JID jid,String node,DiscoveryListener listener)
+	{
+		if (listener == null) listener = this;
+		DiscoveryInfo info = (DiscoveryInfo)infoCache.get(jid);
+		if(info == null)
+		{//first try disco
+			String id = "Jeti_Disco_" + idCount++;
+			InfoQuery iq =new InfoQuery(jid,"get",id,new IQDiscoInfo(node));
+			timeout(iq);
+			infoRequests.put(id,listener);
+			backend.send(iq);
+		}
+		else listener.discoveryInfoResult(jid,info);
+	}
+	
+	public void discoveryInfoResult(JID jid,String id,IQDiscoInfo info)
+	{
+		TimerTask t =(TimerTask) timeoutTimers.remove(id);
+		if(t!=null)t.cancel();
+		infoCache.put(jid,info);
+		DiscoveryListener d = (DiscoveryListener)infoRequests.remove(id);
+		if(d!=null) d.discoveryInfoResult(jid,info);
+	}
+	
+	public void discoveryItemResult(JID jid,String id,IQDiscoItems item)
+	{
+		TimerTask t =(TimerTask) timeoutTimers.remove(id);
+		if(t!=null)t.cancel();
+		itemCache.put(jid,item);
+		DiscoveryListener d =(DiscoveryListener)itemRequests.remove(id);
+		if(d!=null)d.discoveryItemResult(jid,item);
+	}
+	
+
+//	public void browseResult(JID jid,String id,IQBrowse item)
+//	{
+//		TimerTask t =(TimerTask) timeoutTimers.remove(id);
+//		if(t!=null)t.cancel();
+//		if(itemRequests.containsKey(id))
+//		{
+//			itemCache.put(item.getJID(),item);
+//			DiscoveryListener d =(DiscoveryListener)itemRequests.remove(id);
+//			if(d!=null)d.discoveryItemResult(jid,item);
+//		}
+//		else if(infoRequests.containsKey(id))
+//		{
+//			infoCache.put(item.getJID(),item);
+//			DiscoveryListener d = (DiscoveryListener)infoRequests.remove(id);
+//			if(d!=null)d.discoveryInfoResult(jid,item);
+//		}
+//	}
+
+	public void discoError(String id,JID jid)
+	{//no disco so try browse
+		TimerTask t =(TimerTask) timeoutTimers.remove(id);
+		if(t!=null)t.cancel();
+//		String newID = "Jeti_Browse_" + idCount++;
+//		InfoQuery iq =new InfoQuery(jid,"get",newID,new IQBrowse());
+//		backend.send(iq);
+//		timeout(iq);
+//		Object o = infoRequests.remove(id);
+//		if(o!=null)infoRequests.put(newID,o);
+//		else
+//		{
+//			o=itemRequests.remove(id);
+//			if(o!=null)itemRequests.put(newID,o);
+//		}
+//	}
+//	
+//	public void browseError(String id,JID jid)
+//	{
+//		TimerTask t =(TimerTask) timeoutTimers.remove(id);
+//		if(t!=null)t.cancel();
+		Object o = infoRequests.remove(id);
+		//change to disco
+		if(o!=null)
+		{
+			DiscoveryInfo info = new IQBrowse(jid);
+			if (infoCache.get(jid) == null)	infoCache.put(jid,info);//if nothing in cache cache = empty browse (maby server browse did succeed)
+			((DiscoveryListener)o).discoveryInfoResult(jid,info);
+		}
+		else
+		{
+			o = itemRequests.remove(id);
+			if(o!=null)
+			{
+				DiscoveryItem item = new IQBrowse(jid);
+				if (itemCache.get(jid) == null)	itemCache.put(jid,item);//if nothing in cache cache = empty browse (maby server browse did succeed)
+				((DiscoveryListener)o).discoveryItemResult(jid,item);
+			}
+		}
+	}
+	
+	public void discoveryInfoResult(JID jid,DiscoveryInfo info){}
+	
+	public void discoveryItemResult(JID jid,DiscoveryItem item){}
+	
+	//times out a disco or browse request
+	private void timeout(final InfoQuery query)
+	{
+		TimerTask t = new TimerTask()
+		{
+			public void run()
+			{
+				System.out.println("timeout " + query);
+//				if(query.getIQExtension() instanceof IQBrowse)
+//				{//browse
+//					browseError(query.getID(),query.getTo());
+//				}
+//				else
+				{//disco
+					discoError(query.getID(),query.getTo());
+				}
+			}
+		};
+		timer.schedule(t,60000);
+		timeoutTimers.put(query.getID(),t);
+	}
+
+	//-------------------status events-------------\
+	public void connectionChanged(boolean online)
+	{//clear cache when offline
+		if (!online)
+		{
+			infoRequests = new HashMap(10);
+			itemRequests = new HashMap(5);
+			infoCache = new HashMap();
+			itemCache = new HashMap();
+		}
+	}
+
+	public void ownPresenceChanged(int show, String status){}
+
+	public void exit(){}
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/ConnectionPacketReceiver.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/ConnectionPacketReceiver.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/ConnectionPacketReceiver.java	(revision 3952)
@@ -0,0 +1,23 @@
+package nu.fw.jeti.backend;
+
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface ConnectionPacketReceiver extends PacketReceiver
+{
+	void connected(String connectionId,String xmppVersion);
+
+	public void outputDeath();
+	
+	public void inputDeath();
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/CapabilitiesCache.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/CapabilitiesCache.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/CapabilitiesCache.java	(revision 3952)
@@ -0,0 +1,181 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2007 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ */
+
+//created on Mar 11, 2007
+package nu.fw.jeti.backend;
+
+import java.io.BufferedWriter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+
+import nu.fw.jeti.events.DiscoveryListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.DiscoItem;
+import nu.fw.jeti.jabber.elements.DiscoveryInfo;
+import nu.fw.jeti.jabber.elements.DiscoveryItem;
+import nu.fw.jeti.jabber.elements.IQDiscoInfo;
+import nu.fw.jeti.jabber.elements.IQXCaps;
+import nu.fw.jeti.jabber.handlers.IQDiscoInfoHandler;
+import nu.fw.jeti.util.Log;
+import nu.fw.jeti.util.PreferencesHandler;
+
+
+public class CapabilitiesCache implements DiscoveryListener {
+	private Map capabilities;
+	private Map cache;
+	private Backend backend;
+
+	
+	public CapabilitiesCache(Backend backend)
+	{
+		try
+		{
+			this.backend = backend;
+			capabilities = new HashMap();
+			cache = new HashMap();
+		}
+		catch(Exception e){}
+	}
+	
+	public void setCapability(JID jid, IQXCaps caps){
+		String cap = caps.getNode() + "#" + caps.getVer();
+		addCap(jid, cap);
+		if(caps.getExt() != null){
+			String[] extensions = caps.getExt().split(" ");
+			for (int i = 0; i < extensions.length; i++) {
+				cap = caps.getNode() +"#"+ extensions[i];
+				addCap(jid, cap);
+			}
+		}
+	}
+
+	private void addCap(JID jid, String cap) {
+		if(cache.containsKey(cap)){
+			getJIDCapabilities(jid).addCapabilities((IQDiscoInfo)cache.get(cap));
+		}
+		else {
+			backend.getInfo(jid, cap, this);
+		}
+	}
+
+
+	private Capabilities getJIDCapabilities(JID jid) {
+		Capabilities jidCapabilities = null;
+		if(capabilities.containsKey(jid)){
+			jidCapabilities = (Capabilities) capabilities.get(jid);
+		}
+		else {
+			jidCapabilities = new Capabilities();
+			capabilities.put(jid,jidCapabilities);
+		}
+		return jidCapabilities;
+	}
+	
+	public void discoveryInfoResult(JID jid, DiscoveryInfo info){
+		if(!info.hasFeatures())return;
+		IQDiscoInfo disco = (IQDiscoInfo)info;
+		cache.put(disco.getNode(), disco);
+		getJIDCapabilities(jid).addCapabilities(disco);
+	}
+	
+	public void discoveryItemResult(JID jid, DiscoveryItem item){}
+	
+	
+	/**
+	 * Has JID jid capability capability
+	 * @param jid
+	 * @param capability
+	 */
+	public boolean hasCapability(JID jid, String capability){
+		return getJIDCapabilities(jid).hasCapability(capability);
+	}	
+	
+	public void save()
+	{
+		StringBuffer xml = new StringBuffer();
+		xml.append("<?xml version='1.0'?>");
+		xml.append("<queries>");
+		for (Iterator iter = cache.values().iterator(); iter.hasNext();) {
+			
+			((IQDiscoInfo)iter.next()).appendToXML(xml);
+			xml.append("\n");
+		}
+		xml.append("</queries>");
+		System.out.println(xml.toString());
+		try
+		{
+			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
+					new FileOutputStream(Start.path + "capabilitiesCache.xml"), "UTF8"));
+			writer.write(xml.toString());
+			writer.close();
+		}
+		catch (IOException ex2)
+		{
+			ex2.printStackTrace();
+		}
+	}
+	
+	static class DiscoInfoHandler extends DefaultHandler
+    {
+         private Map queries;
+         private IQDiscoInfoHandler discoHandler;
+	
+        public DiscoInfoHandler(Map queries)
+        {
+            this.queries = queries;
+            discoHandler = new IQDiscoInfoHandler();
+        }
+	    
+        public void startElement(String uri, String localName, String qName, Attributes attr) 
+        {
+        	if(qName.equals("queries")); //do nothing top level
+        	else if(qName.equals("query"))	
+            {
+                discoHandler.startHandling(attr);
+            }
+            else discoHandler.startElement(qName, attr);
+        }
+        
+        public void endElement(String uri, String localName, String qName){
+        	if(qName.equals("query")){
+	        	IQDiscoInfo info = (IQDiscoInfo) discoHandler.build();
+	        	queries.put(info.getNode(),info);
+        	}
+        }
+	}
+	
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/OwnCapabilities.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/OwnCapabilities.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/OwnCapabilities.java	(revision 3952)
@@ -0,0 +1,136 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2007 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ */
+
+//created on Feb 21, 2007
+package nu.fw.jeti.backend;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.LinkLoopException;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.DiscoIdentity;
+import nu.fw.jeti.jabber.elements.IQDiscoInfo;
+import nu.fw.jeti.jabber.elements.IQXCaps;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+
+/**
+ * @author eric
+ *
+ */
+public class OwnCapabilities {
+	private IQXCaps caps;
+	private List features;
+	private List coreFeatures;
+	private Map capabilities;
+	boolean changed = true;
+	private Backend backend;
+	
+	public OwnCapabilities(Backend backend){
+		this.backend = backend;
+		features = new ArrayList();
+		coreFeatures = new ArrayList();
+		capabilities = new HashMap();
+		addCapability("http://jabber.org/protocol/disco#info");
+		//addCapability("jabber:iq:last");
+		addCapability("jabber:iq:oob");
+		addCapability("jabber:iq:time");
+		addCapability("jabber:iq:version");
+		addCapability("jabber:x:event");
+	}
+			
+	public void addCapability(String feature){
+		coreFeatures.add(feature);
+		features.add(feature);
+	}
+			
+	
+	public void addCapability(String capability,String feature){
+		features.add(feature);
+		Object cap = capabilities.get(capability);
+		if(cap !=null){
+			((List)cap).add(feature);
+		}
+		else {
+			List list = new LinkedList();
+			list.add(feature);
+			capabilities.put(list,feature);
+		}
+		changed = true;
+	}
+	
+	public void removeCapability(String capability,String feature){
+		features.remove(feature);
+		capabilities.remove(capability);
+		changed = true;
+	}
+	
+	private void createCaps(){
+		StringBuffer buffer = new StringBuffer();
+		for (Iterator i = capabilities.keySet().iterator(); i.hasNext();) {
+			buffer.append(i.next());
+			if(i.hasNext())buffer.append(' ');
+		}
+		caps = new IQXCaps("http://jeti.jabberstudio.org/caps",
+							Start.VERSION,buffer.toString());
+	}
+		
+	/**
+	 * answers disco#info requests with capabilities info
+	 * @param iq the Infoquery packet
+	 * @param info The disco#info packet
+	 */
+	public void answerInfoRequest(InfoQuery iq, IQDiscoInfo info){
+		List identities = new LinkedList();
+		identities.add(new DiscoIdentity("client","pc",null));
+		IQDiscoInfo returnInfo = null;
+		String node = info.getNode(); 
+		if(("http://jeti.jabberstudio.org/caps#"+Start.VERSION).equals(node)){
+			returnInfo = new IQDiscoInfo(info.getNode(),identities,coreFeatures);
+		}
+		else if(node!=null && node.startsWith("http://jeti.jabberstudio.org/caps#")){
+			List list =(List) capabilities.get(node.substring(node.lastIndexOf('#')+1,node.length()));
+			if(list!=null) returnInfo = new IQDiscoInfo(node,identities,list);
+		}
+		else returnInfo = new IQDiscoInfo(node,identities,features);
+		
+		backend.send(new InfoQuery(iq.getFrom(),"result",iq.getID(),returnInfo));
+    }
+	
+	/**
+	 * gets a capabilties packet with all correct attributes set
+	 * @return the capabilities packet
+	 */
+	public IQXCaps getCaps(){
+		if(changed){
+			createCaps();
+			changed = false;
+		}
+		return caps;
+	}
+	
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/URLClassloader.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/URLClassloader.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/URLClassloader.java	(revision 3952)
@@ -0,0 +1,36 @@
+package nu.fw.jeti.backend;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * @author E.S. de Boer
+ *
+ * 
+ */
+public class URLClassloader extends URLClassLoader
+{
+	public URLClassloader(URL[] urls,ClassLoader parent)
+	{
+		super(urls,parent); 
+	}
+
+	public void addURL(URL url)
+	{
+		super.addURL(url);
+	}
+	
+//	public Class loadClass(String name) throws ClassNotFoundException 
+//	{
+//		System.out.println(name);
+//		return super.loadClass(name);
+//	}
+
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/IQTimerQueue.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/IQTimerQueue.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/IQTimerQueue.java	(revision 3952)
@@ -0,0 +1,66 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 20-okt-2004
+ */
+ 
+package nu.fw.jeti.backend;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import nu.fw.jeti.events.IQResultListener;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+//TODO add timeout, add muc and disco timeout to this (priority queue with timer)
+public class IQTimerQueue
+{
+	private Map iqs = new HashMap(25);
+	
+	public void add(InfoQuery query, IQResultListener listener,int timeout)
+	{
+		if(query.getID()==null)
+		{
+			System.err.println("query id is null" + query);
+			return;
+		}
+		iqs.put(query.getID(),listener);
+	}
+	
+	public IQResultListener getInfoQueryListener(String id)
+	{
+		return (IQResultListener)iqs.remove(id);
+	}
+	
+	public void clear() {iqs.clear();}
+	
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/JabberHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/JabberHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/JabberHandler.java	(revision 3952)
@@ -0,0 +1,226 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *
+ *  created 2001
+ */
+
+package nu.fw.jeti.backend;
+
+import nu.fw.jeti.jabber.elements.Packet;
+import nu.fw.jeti.jabber.handlers.ExtensionHandler;
+import nu.fw.jeti.jabber.handlers.PacketHandler;
+import nu.fw.jeti.jabber.handlers.UnknownPacketHandler;
+import nu.fw.jeti.util.Log;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class JabberHandler extends DefaultHandler
+{
+	private int depth;//current depth in xml doc
+	private PacketHandler packetHandeler;
+	private ExtensionHandler extensionHandler;
+	private Handlers handlers;
+	private PacketReceiver packetReceiver;
+	private StringBuffer completeXML = new StringBuffer();
+	
+    public JabberHandler(PacketReceiver connect,Handlers handlers)
+    {
+		this.handlers =handlers;
+		this.packetReceiver = connect;
+    }
+
+	public void changePacketReceiver(PacketReceiver reciever)
+	{
+	    packetReceiver = reciever;
+	}
+
+	public void startDocument()
+	{
+	   //System.out.println("<?xml version='1.0' encoding='UTF-8'?>");
+	}
+
+	public void endDocument(){ System.out.println("end document");}
+
+
+
+	public void startElement(String namespaceURI,
+							String sName, // simple name
+							String qName, // qualified name
+							Attributes attrs)
+							throws SAXException
+	{
+		try{
+			switch(depth++)
+			{
+				case 0:
+					if("stream:stream".equals(qName))
+					{
+						String sessionID = attrs.getValue("id");
+						String xmppVersion = attrs.getValue("version");
+						//connected only when connecting so cast to connect make interface for register?
+						((ConnectionPacketReceiver)packetReceiver).connected(sessionID,xmppVersion);
+						//no connection id on sun server so don't throw the error
+						//if(sessionID == null) throw new SAXException("session id = null");
+						return;
+					}
+					else if(qName.equals("stream:error")) break;
+					throw new SAXException("No stream:stream element");
+				case 1://iq message and presence
+					completeXML = new StringBuffer();//new packet so clear stringbuffer
+					//if(qName.equals("stream:error")) break;
+					packetHandeler = handlers.getPacketHandler(qName);
+					extensionHandler = null;
+					if(packetHandeler == null)
+					{//unknown handler
+						Log.notParsedXML(qName + " is not a message, presence or iq packet");
+						packetHandeler = new UnknownPacketHandler();
+					}
+					packetHandeler.startHandling(attrs);
+					break;
+				default:
+					String xmlns = attrs.getValue("xmlns");
+					if(xmlns !=null)
+					{//if contains xmlns new extension //extension in extension?
+						ExtensionHandler tempExtensionHandler = handlers.getExtensionHandler(xmlns);
+						if(tempExtensionHandler == null)
+						{// unknown extension handler
+							tempExtensionHandler = handlers.getExtensionHandler("unknown");
+						}
+						if (extensionHandler != null) tempExtensionHandler.setParent(extensionHandler);//extension in extension
+						extensionHandler = tempExtensionHandler;
+						extensionHandler.setName(qName);
+						extensionHandler.startHandling(attrs);
+					}
+					else if(extensionHandler !=null)
+					{//no new extension
+						extensionHandler.up();
+						extensionHandler.startElement(qName,attrs);
+					}
+					else packetHandeler.startElement(qName, attrs);
+			}
+			completeXML.append("<");
+			completeXML.append(qName);
+			if (attrs != null) {
+				String aName = null;
+				for (int i = 0; i < attrs.getLength(); i++) {
+					aName = attrs.getQName(i);
+					completeXML.append(" ");
+					completeXML.append(aName+"=\""+attrs.getValue(i)+"\"");
+				}
+			}
+			completeXML.append(">");
+	   } catch (Exception e){throw new SAXException(e);}
+	}
+
+	public void endElement(String namespaceURI,
+						   String sName, // simple name
+						   String qName  // qualified name
+							)
+							throws SAXException
+	{
+		try{
+			completeXML.append("</");
+			completeXML.append(qName);
+		    completeXML.append(">");
+			switch(--depth){
+			case 0:   // We're back at the end of the root
+			  break;
+			case 1:// The Packet is finished
+				Log.completeXML(completeXML);
+				/*
+				if(qName.equals("stream:error"))
+				{
+					Packet packet = packetHandeler.build();
+					Log.xmlPacket(packet);
+					packetReceiver.streamError(packet);
+					break;
+				}
+				*/
+				try{
+					Packet packet = packetHandeler.build();
+					Log.xmlPacket(packet);
+					packetReceiver.receivePackets(packet);
+				}catch(InstantiationException e){Log.xmlParseException(e);}
+				catch (UnsupportedOperationException e)
+				{//only way to stop xml parser
+					if("end xmlparser".equals(e.getMessage())) {throw new SAXException("end xmlparser");}
+				}
+				catch(Exception e)
+				{
+					e.printStackTrace();
+				}
+				break;
+			default:  // Move back up the tree
+				if(extensionHandler != null)
+				{//in extension
+					//if(extensionHandler.getName().equals(qName))//name matched start tag so close extension
+					if(extensionHandler.isTop())
+					{
+						ExtensionHandler parent = extensionHandler.getParent();
+						if(parent != null)
+						{//extension has parent add this extension to parent
+							try{
+								parent.addExtension(extensionHandler.build());
+							}catch(InstantiationException e){Log.xmlParseException(e);}
+							extensionHandler.setParent(null);//clear parent
+							extensionHandler = parent;
+						}
+						else
+						{//add extension to packet
+							try{
+								packetHandeler.addExtension(extensionHandler.build());
+							}catch(InstantiationException e){Log.xmlParseException(e);}
+							extensionHandler = null;
+						}
+					}// element end inside a extension
+					else
+					{
+						extensionHandler.down();
+						extensionHandler.endElement(qName);
+					}
+				}// element end inside a packet
+				else packetHandeler.endElement(qName);
+			}
+		} catch (Exception e){throw new SAXException(e);}
+	}
+
+	public void characters(char buf[], int offset, int len)	throws SAXException
+	{
+		try{
+			String text = new String(buf, offset,len); //.trim();
+			if(extensionHandler != null) extensionHandler.characters(text);
+			else if (packetHandeler!=null)  packetHandeler.characters(text);
+			completeXML.append(text.trim());
+		} catch (Exception e){throw new SAXException(e);}
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/LoginInfo.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/LoginInfo.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/LoginInfo.java	(revision 3952)
@@ -0,0 +1,274 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.backend;
+
+import java.net.ProxySelector;
+import java.net.URL;
+
+import com.sun.java.browser.net.ProxyInfo;
+import com.sun.java.browser.net.ProxyService;
+
+import nu.fw.jeti.util.Utils;
+
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class LoginInfo
+{
+	public final static int NO_PROXY=0;
+	public final static int SOCKS_PROXY=1;
+	public final static int HTTP_PROXY=2;
+	public final static int HTTP_BIND=3;
+		
+	private String server;
+    private String host;
+	private int port;
+	private String username;
+	private String resource;
+	private String password;
+	private boolean ssl=false;
+	private int proxyType=NO_PROXY;
+	private String proxyServer;
+	private String proxyPort;
+	private String proxyUsername;
+	private String proxyPassword; 
+	private int priority;
+
+	//applet
+    public LoginInfo(String server, String host, String username,String password,
+    		String resource,int port,boolean ssl,int priority)
+    {
+    	if(username!=null && username.indexOf("@")!=-1 )
+    	{
+    		this.host = server;
+    		int atPos = username.indexOf('@');
+    		this.username = username.substring(0,atPos);
+    		this.server = username.substring(atPos+1,username.length());
+    	}
+    	else
+    	{
+    		this.server = server;
+            this.host = host;
+    		this.username = username;
+    	}
+
+    	this.ssl = ssl;
+		this.password = password;
+		if(password != null && password.equals("")) this.password =null;
+		this.resource = resource;
+		if(resource != null && resource.equals("")) this.resource ="Jeti";
+		this.port = port;
+		if(port==0)port=5222;
+		this.priority = priority;
+    }
+
+    public LoginInfo(String server, String host, String username,
+                     String password, String resource,int port, boolean ssl,int priority,
+                     int proxy, String proxyServer, String proxyUsername,
+                     String proxyPassword, String proxyPort )
+    {
+        this(server,host,username,password,resource,port,ssl,priority);
+		this.proxyType = proxy;
+		this.proxyServer = proxyServer;
+		this.proxyPort = proxyPort;
+		this.proxyUsername = proxyUsername ;
+		this.proxyPassword = proxyPassword;
+    }
+
+    public LoginInfo(String server, String host, String username, String password, String resource,
+    				 int port, boolean ssl, String proxyServer, String proxyPort)
+    {
+    	this.server = server;
+    	this.host = host;
+    	this.username = username;
+    	this.password = password;
+    	this.resource = resource;
+    	this.port = port;
+    	this.ssl = ssl;
+    	this.proxyServer = proxyServer;
+    	this.proxyPort = proxyPort;
+    }
+    
+	/**
+	 * Returns the port.
+	 * @return String
+	 */
+	public int getPort()
+	{
+		return port;
+	}
+
+	/**
+	 * Returns the resource.
+	 * @return String
+	 */
+	public String getResource()
+	{
+		return resource;
+	}
+
+	/**
+	 * Returns the server.
+	 * @return String
+	 */
+	public String getServer()
+	{
+		return server;
+	}
+
+	/**
+     * Returns the host to connect to.
+     * @return String
+     */
+    public String getHost()
+    {
+        return host;
+    }
+
+    /**
+	 * Returns the ssl.
+	 * @return boolean
+	 */
+	public boolean isSSl() {
+		return ssl;
+	}
+	
+	public int getPriority()
+	{
+		return priority;
+	}
+
+	/**
+	 * Returns the username.
+	 * @return String
+	 */
+	public String getUsername() {
+		return username;
+	}
+
+	/**
+	 * Returns the password.
+	 * @return String
+	 */
+	public String getPassword()
+	{
+		return password;
+	}
+
+	/**
+	 * Sets the password.
+	 * @param password The password to set
+	 */
+	public void setPassword(String password)
+	{
+		this.password = password;
+	}
+
+	public void setProxyPassword(String proxyPassword)
+	{
+		this.proxyPassword = proxyPassword;
+	}
+	
+	/**
+	 * Returns the proxyPassword.
+	 * @return String
+	 */
+	public String getProxyPassword()
+	{
+		return proxyPassword;
+	}
+
+	/**
+	 * Returns the proxyPort.
+	 * @return int
+	 */
+	public String getProxyPort()
+	{
+		return proxyPort;
+	}
+
+	/**
+	 * Returns the proxyServer.
+	 * @return String
+	 */
+	public String getProxyServer()
+	{
+		return proxyServer;
+	}
+
+	/**
+	 * Set the proxyUsername;
+	 * 
+	 */
+	
+	public void setProxyUsername(String proxyUsername)
+	{
+		this.proxyUsername = proxyUsername;
+	}
+	
+	/**
+	 * Returns the proxyUsername.
+	 * @return String
+	 */
+	public String getProxyUsername()
+	{
+		return proxyUsername;
+	}
+	
+	/**
+	 * returns the type of proxy used
+	 * @return proxy type
+	 */
+	public int getProxyType()
+	{
+		return proxyType;
+	}
+
+	/**
+	 * returns the used proxy
+	 * @param proxy the proxy type to test
+	 * @return true if the proxy is used
+	 */
+	public boolean useProxy(int proxy)
+	{
+		return proxyType == proxy;
+	}
+	
+	public boolean isHTTPProxy()
+	{
+		//System.out.println( "( " + proxyType + "==" + LoginInfo.HTTP_PROXY + " ) = " + useProxy(LoginInfo.HTTP_PROXY));
+		
+		if( useProxy(LoginInfo.HTTP_PROXY) ) 
+			return true;
+		else
+			return false;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/Jabber.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/Jabber.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/Jabber.java	(revision 3952)
@@ -0,0 +1,383 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.backend;
+
+import java.text.MessageFormat;
+import java.util.Iterator;
+
+import javax.swing.JOptionPane;
+
+import nu.fw.jeti.events.IQResultListener;
+import nu.fw.jeti.events.MessageEventListener;
+import nu.fw.jeti.events.MessageListener;
+import nu.fw.jeti.events.PresenceListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.*;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Log;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.ServerExpresso;
+
+
+/**
+ * distributes the xml packets received from the server
+ * @author E.S. de Boer
+ */
+
+public class Jabber implements PacketReceiver
+{
+	private Backend backend;
+	private Discovery discovery;
+	private IQTimerQueue iqTimerQueue;
+	private OwnCapabilities capabilities;
+	public String NameContact;
+	public String NameOrganization;
+
+	public Jabber(Backend backend,OwnCapabilities capabilities, Discovery browse, IQTimerQueue timerQueue)
+	{
+		iqTimerQueue = timerQueue;
+		this.backend = backend;
+		this.discovery = browse;
+		this.capabilities = capabilities;
+	}
+
+	public void receivePackets(Packet packet)
+	{
+		if (packet instanceof StreamError) backend.streamError((StreamError)packet);
+		if (packet instanceof InfoQuery) infoQuery((InfoQuery) packet);
+		else if (packet instanceof Message) message((Message) packet);
+		else if (packet instanceof Presence) presence((Presence) packet);
+	}
+	
+	/*------------------------------InfoQuery------------------------*/
+	private void infoQuery(InfoQuery infoQuery)
+	{
+		IQResultListener iqrListener = iqTimerQueue.getInfoQueryListener(infoQuery.getID());
+		if(iqrListener!=null)
+		{
+			iqrListener.iqResult(infoQuery);
+		}
+		else
+		{
+			if (infoQuery.hasExtensions())
+			{
+				IQExtension extension = infoQuery.getIQExtension();
+				if (extension instanceof IQDiscoInfo)
+				{//move to execute
+					if (infoQuery.getType().equals("result"))
+					{
+						discovery.discoveryInfoResult(infoQuery.getFrom(), infoQuery.getID(), (IQDiscoInfo) extension);
+					}
+					else if (infoQuery.getType().equals("get"))
+					{
+						capabilities.answerInfoRequest(infoQuery,(IQDiscoInfo) extension);
+					}
+					else if (infoQuery.getType().equals("error"))
+					{
+						Log.xmlReceivedError("Disco error " + infoQuery.getErrorCode() + " " + infoQuery.getErrorDescription() + " from " + infoQuery.getFrom());
+						discovery.discoError(infoQuery.getID(), infoQuery.getFrom());
+					}
+				}
+				else if (extension instanceof IQDiscoItems)
+				{//move to execute
+					if (infoQuery.getType().equals("result"))
+					{
+						discovery.discoveryItemResult(infoQuery.getFrom(), infoQuery.getID(), (IQDiscoItems) extension);
+					}
+					else if (infoQuery.getType().equals("error"))
+					{
+						Log.xmlReceivedError("Disco error " + infoQuery.getErrorCode() + " " + infoQuery.getErrorDescription() + " from " + infoQuery.getFrom());
+						discovery.discoError(infoQuery.getID(), infoQuery.getFrom());
+					}
+				}
+				else if (extension instanceof IQBrowse)
+				{
+					/*if (infoQuery.getType().equals("result"))
+					{
+						browse.browseResult(infoQuery.getFrom(), infoQuery.getID(), (IQBrowse) extension);
+					}
+					else if (infoQuery.getType().equals("error"))
+					{
+						Log.xmlReceivedError("Browse error " + infoQuery.getErrorCode() + " " + infoQuery.getErrorDescription() + " from " + infoQuery.getFrom());
+						browse.browseError(infoQuery.getID(), infoQuery.getFrom());
+					}*/
+				}
+				else extension.execute(infoQuery, backend);
+			} else if (infoQuery.getType().equals("set")) {
+                XMPPError err = new XMPPError("cancel", 501);
+                err.addError(new XMPPErrorTag("feature-not-implemented"));
+                backend.send(new InfoQuery(infoQuery.getFrom(), 
+                                           infoQuery.getID(), err));
+            }
+			//TODO change Jingle code to register iq listeners instead of this
+			else if(infoQuery.getType().equals("result") && infoQuery.getID().indexOf("jj") == 0  ){
+				for(Iterator i = backend.getListeners(IQResultListener.class); i.hasNext();){
+					((IQResultListener)i.next()).iqResult(infoQuery);
+				}
+			}
+		}
+	}
+	
+	/*-------------------------------Message-------------------------*/
+	private void message(Message message)
+	{
+		MessageListener messageListener = backend.getMessageListener(message.getFrom());
+		if(messageListener != null)
+		{
+			messageListener.message(message); 
+ 			return;
+		}
+		
+		if (message.getBody() != null || message.getType().equals("groupchat"))
+		{
+			for (Iterator j = backend.getListeners(MessageListener.class); j.hasNext();)
+			{
+				((MessageListener) j.next()).message(message);
+			}
+		}
+		if (message.hasExtensions())
+		{
+			for (Iterator i = message.getExtensions(); i.hasNext();)
+			{
+				Extension extension = (Extension) i.next();
+				if (extension instanceof XExecutableExtension)
+				{
+					((XExecutableExtension)extension).execute(message, backend);					
+				}
+				
+				else if (extension instanceof XMessageEvent)
+				{//make executable?
+					//goes wrong with groupchat
+					//System.out.println( "exts" + extension);
+					if (message.getBody() == null)
+					{
+						//no body so composing event
+						for (Iterator j = backend.getListeners(MessageEventListener.class); j.hasNext();)
+						{
+							((MessageEventListener) j.next()).onComposing(message.getFrom(), message.getThread(), (XMessageEvent) extension);
+						}
+					}
+					else
+					{
+						//body so request to sent events //goes wrong with groupchat
+						for (Iterator j = backend.getListeners(MessageEventListener.class); j.hasNext();)
+						{
+							((MessageEventListener) j.next()).requestComposing(message.getFrom(), message.getID(), message.getThread());
+						}
+					}
+				}
+			}
+		}
+	}
+
+	//----------------------------------Presence----------------------------------------------------\\
+	private void presence(Presence presence)
+	{
+		//presence for groupchat eg
+		PresenceListener presenceListener = backend.getPresenceListener(presence.getFrom());
+		if(presenceListener != null)
+		{
+			presenceListener.presenceChanged(presence); 
+ 			return;
+		}
+		
+		String type = presence.getType();
+		if(!"error".equals(type))
+		{
+			if(presence.hasExtensions())
+			{
+				for(Iterator i = presence.getExtensions();i.hasNext();)
+				{
+					Object o = i.next();
+					if(o instanceof IQXCaps)
+					{
+						backend.setCapability(presence.getFrom(),(IQXCaps)o);
+						break;
+					}
+				}
+			}
+		}
+				
+		if (type.equals("available") || type.equals("unavailable"))
+		{
+			for (Iterator j = backend.getListeners(PresenceListener.class); j.hasNext();)
+			{
+				((PresenceListener) j.next()).presenceChanged(presence);
+			}
+		}
+		else if ("subscribe".equals(type))
+		{
+			JID from = presence.getFrom();
+			if (from.getUser() == null)
+			{
+				//server
+				sendSubscribed(from, "subscribed", presence.getID());
+				if (backend.getJIDStatus(presence.getFrom()) == null)
+				{
+					backend.send(new Presence(from, "subscribe"));
+					IQXRoster roster = new IQXRoster(new RosterItem(from, null, null, "subscribe", null));
+					backend.send(new InfoQuery("set", roster));
+				}
+			} else {
+                fireOnSubscriptionRequestEvent(from, presence.getID());
+            }
+		}
+		else if ("unsubscribed".equals(type))
+		{
+            
+			/**
+			 * @DATE : 14/10/2008
+			 * @author : Alexandre Correia - alexandrecorreia@im.pr.gov.br
+			 * @Description : Trecho Comentado : 
+			 * Este trecho faz com que se receba uma notificacao quando o seu
+			 * convite nao foi aceito/rejeitado.
+			 */
+			//Popups.messagePopup(presence.getFrom() + "unsuscribed you from his presence", "Unsubscribed");
+			
+		}
+		else if ("error".equals(type))
+		{
+            Log.xmlReceivedError("Presence error "
+                                  + presence.getErrorCode() + " "
+                                  + presence.getErrorDescription()
+                                  + " from " + presence.getFrom());
+         }
+	}
+
+	private void fireOnSubscriptionRequestEvent(final JID from, final String id)
+	{
+		/**
+		 * @Author : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+		 * @Date : 13/10/2008
+		 * @Description : Passa para o Servidor Expresso o uid para retornar
+		 * o cn e a organizacao do requerente, informacao que sera
+		 * mostrada, pedindo autorizacao.
+		 */
+		
+		JIDStatus jidStatus = backend.getJIDStatus(from);
+		String cName = new String();
+		cName = from.toString();
+		ServerExpresso JidName = new ServerExpresso();	
+		String ReturnComplete = JidName.NameCN(cName);
+		
+		NameContact = ReturnComplete.substring(0, ReturnComplete.indexOf(";"));
+		NameOrganization = ReturnComplete.substring(ReturnComplete.indexOf(";") + 1 ,ReturnComplete.length());
+		
+		if (jidStatus != null)
+		{
+			Popups.OptionChoosed choose = new Popups.OptionChoosed()
+			{
+				public void optionChoosed(int option)
+				{
+					String type = "subscribed";
+					if (option == javax.swing.JOptionPane.YES_OPTION)
+						type = "unsubscribed";
+					sendSubscribed(from, type, id);
+				}
+			};
+
+			Object[] options = { I18N.gettext("main.popup.Deny_subscription"), I18N.gettext("OK") };
+			Popups.showOptionDialog(
+				//MessageFormat.format(I18N.gettext("main.popup.{0}_wants_to_subscribe_to_your_presence"),new Object[]{jidStatus.getNick()}),
+				MessageFormat.format(I18N.gettext("main.popup.{0}_wants_to_subscribe_to_your_presence"),new Object[]{NameContact + "\n" + NameOrganization + " , "}),				
+				I18N.gettext("main.popup.Subscription_request"),
+				javax.swing.JOptionPane.YES_NO_OPTION,
+				javax.swing.JOptionPane.QUESTION_MESSAGE,
+				null,
+				options,
+				options[1],
+				choose);
+		}
+		else
+		{
+			Popups.OptionChoosed choose = new Popups.OptionChoosed()
+			{	
+				public void optionChoosed(int option)
+				{
+					String type = "subscribed";
+					if ( option == javax.swing.JOptionPane.NO_OPTION )
+					{
+						/**
+						 * @Author : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br 
+						 * @Date : 17/10/2008
+						 * @Description : Passa para a janela o Nome do Contato consultado no Ldap Expresso.
+						 */
+						//add to roster  //frame to addcontact ipv null??
+						//new nu.fw.jeti.ui.AddContact(from, null, backend).setVisible(true);
+						new nu.fw.jeti.ui.AddContact(from, null, backend, NameContact, NameOrganization).setVisible(true);						
+					}
+					else
+						type = "unsubscribed";
+					sendSubscribed(from, type, id);
+
+					//					no more no filed (distracts users)
+					//					else if (option == javax.swing.JOptionPane.CANCEL_OPTION || option == javax.swing.JOptionPane.CLOSED_OPTION)
+					//					{//add to not filed make different
+					//					    //subscribe(from,null,"Friends");
+					//						IQXRoster roster = new IQXRoster(new RosterItem(from,null,null,null,null));
+					//						backend.send(new InfoQuery("set",roster));
+					//					}
+				}
+			};
+			Object[] options = { I18N.gettext("main.popup.Deny_subscription"), I18N.gettext("main.popup.Add_to_roster") };
+			Popups.showOptionDialog(
+				//MessageFormat.format(I18N.gettext("main.popup.{0}_wants_to_subscribe_to_your_presence"),new Object[]{from}),
+				MessageFormat.format(I18N.gettext("main.popup.{0}_wants_to_subscribe_to_your_presence"), new Object[]{NameContact + "\n" + NameOrganization + " , "}),				
+				I18N.gettext("main.popup.Subscription_request"),
+				javax.swing.JOptionPane.YES_NO_OPTION,
+				javax.swing.JOptionPane.QUESTION_MESSAGE,
+				null,
+				options,
+				options[1],
+				choose);
+		}
+	}
+
+	private void sendSubscribed(JID from, String type, String id)
+	{
+		//id so need builder
+		try
+		{
+			PresenceBuilder pb = new PresenceBuilder();
+			pb.type = type;
+			pb.setId(id);
+			pb.setTo(from);
+			backend.send(pb.build());
+		}
+		catch (InstantiationException e)
+		{
+			e.printStackTrace();
+		}
+	}
+
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
+
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/Capabilities.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/Capabilities.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/Capabilities.java	(revision 3952)
@@ -0,0 +1,40 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2007 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ */
+
+//created on Mar 11, 2007
+package nu.fw.jeti.backend;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import nu.fw.jeti.jabber.elements.IQDiscoInfo;
+
+public class Capabilities {
+	List capabilities = new ArrayList();
+		
+	public void addCapabilities(IQDiscoInfo info){
+		capabilities.addAll(info.getFeatures());
+	}
+	
+	public boolean hasCapability(String capabilties){
+		return capabilties.contains(capabilties);
+	}
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/Connect.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/Connect.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/Connect.java	(revision 3952)
@@ -0,0 +1,1331 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/im me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.backend;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
+import java.util.Iterator;
+
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+import javax.swing.border.EmptyBorder;
+
+import nu.fw.jeti.events.DiscoveryListener;
+import nu.fw.jeti.events.LoginListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.DiscoveryInfo;
+import nu.fw.jeti.jabber.elements.DiscoveryItem;
+import nu.fw.jeti.jabber.elements.IQAuth;
+import nu.fw.jeti.jabber.elements.IQAuthBuilder;
+import nu.fw.jeti.jabber.elements.IQExtension;
+import nu.fw.jeti.jabber.elements.IQPrivate;
+import nu.fw.jeti.jabber.elements.IQXRoster;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.JetiPrivateExtension;
+import nu.fw.jeti.jabber.elements.JetiPrivateRosterExtension;
+import nu.fw.jeti.jabber.elements.Packet;
+import nu.fw.jeti.jabber.elements.Presence;
+import nu.fw.jeti.jabber.elements.PresenceBuilder;
+import nu.fw.jeti.jabber.elements.StreamError;
+import nu.fw.jeti.plugins.PluginsInfo;
+import nu.fw.jeti.plugins.XMPP;
+import nu.fw.jeti.util.Digest;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Log;
+import nu.fw.jeti.util.Utils;
+
+/**
+ * @author E.S. de Boer
+ */
+
+//TODO stop packet start spul en de andere dingen in aparte class??
+//TODO improve error reporting, make it translatable
+ //class voor connectie
+
+public class Connect implements ConnectionPacketReceiver
+{
+	private Output output;
+	private Input input;
+	private String authenticationId="yytr";
+	private JabberHandler jabberHandler;
+	private LoginInfo loginInfo;
+	private Backend backend;
+	private static JID myJID = new JID("test","test","test");
+	private boolean authenticated = false;
+	private boolean reconnecting = false;
+	private long latestConnected=0;
+	private int show;
+	private String status;
+	private String connectionID;
+	private Discovery discovery;
+	private OwnCapabilities capabilities;
+	private Socket socket;//socket needed to close if abort
+	private Thread connectThread;//login thread
+	private volatile boolean abort = false;//abort login
+	private IQTimerQueue iqTimerQueue;
+	private Handlers handlers;
+	private XMPP xmpp;
+	
+	// windowProxy
+	private JButton buttonCancel;
+	private JButton buttonOk;
+	private JDialog dialog;
+	private JLabel infoLabel;
+	private JLabel lblUsername;
+	private JLabel lblPassword;
+	private JPanel buttonPanel;
+	private JPanel panelMain;
+	private JPasswordField txtPassword;
+	private JOptionPane optionPane;
+	private JTextField txtUsername;
+    
+	public Connect(Backend backend,IQTimerQueue timerQueue,Handlers handlers)
+	{
+		this.backend = backend;
+		iqTimerQueue = timerQueue;
+		this.handlers = handlers;
+		discovery = new Discovery(backend) ;
+		capabilities = new OwnCapabilities(backend);
+	}
+
+	public void addCapability(String capability,String feature)
+	{
+		capabilities.addCapability(capability, feature);
+	}
+	
+	public void removeCapability(String capability,String feature)
+	{
+		capabilities.removeCapability(capability, feature);
+	}
+	
+	public int getStatus()
+	{
+		return show;
+	}
+	
+	public void getItems(JID jid, DiscoveryListener listener, boolean useCache)
+	{
+		discovery.getItems(jid,listener,useCache);
+	}
+	
+	public void getItems(JID jid, DiscoveryListener listener)
+	{
+		discovery.getItems(jid,listener);
+	}
+	
+	public void getInfo(JID jid, DiscoveryListener listener)
+	{
+		discovery.getInfo(jid,listener);
+	}
+	
+	public void getItems(JID jid,String node, DiscoveryListener listener)
+	{
+		discovery.getItems(jid,node,listener);
+	}
+	
+	public void getInfo(JID jid,String node, DiscoveryListener listener)
+	{
+		discovery.getInfo(jid,node,listener);
+	}
+	
+	public boolean isLoggedIn()
+	{
+		return authenticated;
+	}
+	
+	public boolean isPasswordValid(String password)
+	{
+		return loginInfo.getPassword().equals(password);
+	}
+
+    public void login(LoginInfo info)
+    {
+    	autoLogin(info,2);
+    }
+    
+    public void autoLogin(LoginInfo info,final int tries)
+	{
+		abort = false;
+		loginInfo = info;
+		connectThread = new Thread()
+		{
+			int tel = 0;
+			
+			public void run()
+			{
+				boolean connected = false;
+				int triesc	= 0; 
+				int posIn	= 0;
+				int posFi	= 0;
+				
+				String hosts = loginInfo.getHost();
+				
+				if( hosts.indexOf(" ") != -1 )
+				{
+					hosts = hosts + " ";
+					
+					for( int i = 0; i < hosts.length(); i++ )
+					{
+						String w = String.valueOf(hosts.charAt(i));
+						
+						if( w.equals(" ") )
+						{
+							posIn = posFi;
+							posFi = i;
+							
+							String hostConnect = hosts.substring(posIn, posFi).trim();
+							
+							while( tel < tries )
+							{
+								if( tel == 0)
+								{
+									connected = connect( hostConnect, false );
+								}
+								else
+								{
+									connected = connect( hostConnect, true );
+								}
+
+								if( connected )
+								{
+									i = hosts.length();
+									break;
+								}	
+								
+								try { Thread.sleep(600);}
+								catch (InterruptedException e){}
+								tel++;
+							}	
+							
+								
+							tel = 0;
+						}
+					}
+				}
+				else
+				{
+					
+					while( tel < tries )
+					{
+						if( tel == 0)
+						{
+							connected = connect( hosts, false );
+						}
+						else
+						{
+							connected = connect( hosts, true );
+						}
+
+						if( connected )
+						{
+							break;
+						}	
+						
+						try { Thread.sleep(600);}
+						catch (InterruptedException e){}
+						tel++;
+					}	
+				}
+
+				if( !connected )
+				{
+					interrupt();
+	        		loginInfo.setProxyUsername(null);
+	        		loginInfo.setProxyPassword(null);
+					System.out.println("***** Login Failed *****");					
+				}
+			}
+		};
+		
+		connectThread.start();
+	}
+    
+    public void abort()
+    {
+		abort = true;
+		
+		if( socket != null )
+		{
+			try
+			{
+				socket.close();
+			}
+			catch (IOException e)
+			{
+				e.printStackTrace();
+			}
+		}
+    	connectThread.interrupt();
+		disconnect(); 
+    }
+    
+    public boolean isAborted()
+    {
+    	return abort;
+    }
+
+	synchronized private boolean connect( String host, boolean proxy )
+	{	
+		latestConnected = System.currentTimeMillis();
+		
+		if( loginInfo == null )
+			return true;
+
+		if( authenticated )
+			disconnect();//clear old connection
+		
+		jabberHandler = new JabberHandler(this,handlers);
+		
+		if( xmpp == null && PluginsInfo.isPluginLoaded("xmpp") )
+		{
+			xmpp = (XMPP)PluginsInfo.newPluginInstance("xmpp");
+			handlers.loadExtraHandlers(xmpp.getXMPPHandlers());
+		}
+				
+		try
+		{   		
+            
+			if (host == null || host.length() == 0)
+            {
+            	if(loginInfo.useProxy(LoginInfo.NO_PROXY) && PluginsInfo.isPluginLoaded("xmpp"))
+            	{
+            		host = xmpp.resolveXMPPDomain(loginInfo.getServer());
+            	}
+            	else host = loginInfo.getServer();
+            }
+                       
+		   	if( loginInfo.isSSl() ) 
+	       	{
+		   		if( proxy )
+		   	   	{
+	   				//System.out.println("CONNECT COM PROXY - SEGURA : " + host);
+		   			Socket tunnel = createHTTPTunel(host); 
+	       			socket = new DummySSLSocketFactory().createSocket(tunnel, host, loginInfo.getPort(),true);
+	       		}
+		   		else
+		   		{
+		   			//System.out.println("CONNECT SEM PROXY - SEGURA : " + host);
+		   			socket = new DummySSLSocketFactory().createSocket(host,loginInfo.getPort());
+		   		}
+	       	}
+	       	else 
+	       	{
+	       		if( proxy )
+	       		{
+	       			//System.out.println("CONNECT COM PROXY - NÃO SEGURA : " + host);
+	       			socket = createHTTPTunel(host);
+	       		}
+	       		else
+	       		{
+	       			//System.out.println("CONNECT SEM PROXY - NÃO SEGURA : " + host);
+	       			socket = new Socket( host,loginInfo.getPort() );
+	       		}
+	       	}
+        }
+		catch (UnknownHostException ex){ return false; }
+		catch (IOException ex){ return false; }
+
+		if( abort )
+			return false;
+		
+		try
+		{
+			input = new Input(socket.getInputStream(), this, jabberHandler);
+        }
+		catch (IOException ex){ return false; }
+		
+		if( abort )
+			return false;
+		
+	    try
+	    {
+			output = new Output(socket, loginInfo.getServer(), this, socket.getOutputStream());
+        }
+	    catch (IOException ex){ return false; }
+		
+        return true;
+	}
+	
+	String host;
+	
+	public boolean startTls(ConnectionPacketReceiver cpr)
+	{
+		if(abort)
+			return false;
+		
+		try
+		{   		
+           //TODO stop output
+			output.disconnect(false);
+			input.disconnect();
+            socket = new DummySSLSocketFactory().createSocket(socket,host,loginInfo.getPort(),true);
+            jabberHandler = new JabberHandler(cpr,handlers);
+          
+            try
+            {
+	            input = new Input(socket.getInputStream(),this,jabberHandler);
+	        }
+            catch (IOException ex)
+	        {
+				return false;
+	        }
+	        
+            try
+            {
+		    	output = new Output(socket,loginInfo.getServer(),this,socket.getOutputStream());
+	        }
+            catch (IOException ex)
+	        {
+				//sendLoginError(I18N.gettext("main.loginstatus.Could_not_open_output_because") + " " +ex.getMessage());
+				return false;
+	        }
+	                    
+	        if(abort)
+	        	return false;
+	       
+		}
+		catch(IOException e){}
+		
+		return true;
+	}
+	
+	//used by compression plugin
+	public Socket getSocket()
+	{
+		return socket;
+	}
+	
+	public boolean startCompressed(ConnectionPacketReceiver cpr,InputStream inputStream,OutputStream outputStream)
+	{
+		jabberHandler = new JabberHandler(cpr,handlers);
+		if ( abort )
+			return false;
+	
+		output.disconnect(false);
+		Input input2 = input;
+		input = new Input(inputStream,this,jabberHandler);
+        try
+        {
+	    	output = new Output(socket,loginInfo.getServer(),this,outputStream);
+        }
+        catch (IOException ex)
+        {
+			sendLoginError(I18N.gettext("main.loginstatus.Could_not_open_output_because") + " " +ex.getMessage());
+			ex.printStackTrace();
+			return false;
+        }
+                    
+        if ( abort )
+        	return false;
+        input2.disconnect();
+        //FIXME Using thread.stop because throwing an exception to stop the sax parser in the input thread fails
+        input2.stop();
+        //throw new RuntimeException();
+
+		return true;
+	}
+	
+	public void startSasl(ConnectionPacketReceiver cpr)
+	{
+		if( abort )
+			return;
+		
+		//kill old input and send new stream
+		//input.disconnect();
+		jabberHandler = new JabberHandler(cpr,handlers);
+		
+		try
+		{
+			input = new Input(socket.getInputStream(),this,jabberHandler);
+		}
+		catch (IOException e)
+		{
+			e.printStackTrace();
+		}
+		
+		try
+		{
+			output.writeHeader();
+		}
+		catch (IOException e)
+		{
+			e.printStackTrace();
+		}
+		
+		throw new UnsupportedOperationException("end xmlparser");
+	}
+	
+	private Socket createHTTPTunel(String host) throws IOException
+	{
+		String tunnelHost = loginInfo.getProxyServer();
+	    int tunnelPort = Integer.valueOf(loginInfo.getProxyPort()).intValue();
+	    String typeProxy = typeProxy(tunnelHost, tunnelPort, host, loginInfo.getPort());
+
+		if( typeProxy.indexOf("Proxy-Authenticate: Digest") != -1 )
+		{
+			// Proxy Username and Password
+			if( loginInfo.getProxyPassword() == null && loginInfo.getProxyPassword() == null)
+				windowProxy(tunnelHost);
+
+			return doTunnelHandshakeDigest(host, loginInfo.getPort());
+		}
+		else if( typeProxy.indexOf("Proxy-Authenticate: Basic") != -1 )
+		{
+			// Proxy Username and Password
+			if( loginInfo.getProxyPassword() == null && loginInfo.getProxyPassword() == null)
+				windowProxy(tunnelHost);
+
+			Socket tunnel = new Socket(tunnelHost, tunnelPort);
+			doTunnelHandshake(tunnel, host, loginInfo.getPort());
+			return tunnel;
+		}
+		else if( typeProxy.indexOf("HTTP/1.0 200") != -1 )
+		{
+			Socket tunnel = new Socket(tunnelHost, tunnelPort);
+			PrintWriter writeConnection = new PrintWriter( tunnel.getOutputStream(), true ); 
+			BufferedReader readConnection = new BufferedReader(new InputStreamReader(tunnel.getInputStream()));
+
+			String msg = "CONNECT " + host + ":" + loginInfo.getPort() + " HTTP/1.1\r\n" +
+			 			 "User-Agent: " + sun.net.www.protocol.http.HttpURLConnection.userAgent + "\r\n" + 
+			 			 "Proxy-Connection: Keep-Alive\r\n";
+			
+			writeConnection.println(msg);
+			String line = "";
+			
+			while((line = readConnection.readLine()) != null )
+			{
+				if( line.indexOf("HTTP/1.0 200") != -1 )
+					break;
+			}
+			
+			return tunnel;
+		}
+
+		return new Socket();
+	}
+
+	private void windowProxy(String tunnelHost)
+	{
+		panelMain = new JPanel();
+		panelMain.setBackground(Color.ORANGE);
+		panelMain.setPreferredSize(new Dimension(400,120));
+		panelMain.setLayout(new GridBagLayout());
+		GridBagConstraints gbc = new GridBagConstraints();
+
+		// Espaco entre elementos;
+		gbc.insets = new Insets(2, 2, 2, 2);
+	    gbc.gridy = 0; // row
+	    gbc.gridx = 0; // col
+	    gbc.gridwidth = 2; 
+	    gbc.anchor = GridBagConstraints.WEST;
+	    
+	    infoLabel = new JLabel(I18N.gettext("main.connect.proxy.Authentication_for") + " " + tunnelHost + " :");
+	    infoLabel.setFont(new Font("SansSerif",Font.BOLD, 14));
+	    infoLabel.setBorder(new EmptyBorder(0, 0, 5, 0));
+	    panelMain.add(infoLabel, gbc);
+
+	    // Label Username
+	    gbc.gridy = 1; // row
+	    gbc.gridx = 0; // col
+	    gbc.gridwidth = 1;
+	    lblUsername = new JLabel(I18N.gettext("main.connect.proxy.Username")+":");
+	    panelMain.add(lblUsername, gbc);
+
+	    // JTextField do Username
+	    gbc.gridy = 1; // row
+	    gbc.gridx = 1; // col
+	    txtUsername = new JTextField(20);
+	    panelMain.add(txtUsername, gbc);
+
+	    // Label Password
+	    gbc.gridy = 2; // row
+	    gbc.gridx = 0; // col
+	    lblPassword = new JLabel(I18N.gettext("main.connect.proxy.Password")+":");
+	    panelMain.add(lblPassword, gbc);    
+
+	    // JPasswordField		    
+	    gbc.gridy = 2; // row
+	    gbc.gridx = 1; // col
+	    txtPassword = new JPasswordField(20);
+	    panelMain.add(txtPassword, gbc);
+
+	    // Buttons
+	    gbc.gridy = 3; // linha
+	    gbc.gridx = 1; // coluna
+	    gbc.gridwidth = 2; // duas células na linha
+	    gbc.anchor = GridBagConstraints.WEST;
+	    buttonPanel	= new JPanel();
+	    buttonCancel = new JButton(I18N.gettext("main.connect.proxy.Cancel"));
+	    buttonOk = new JButton(I18N.gettext("main.connect.proxy.Ok"));
+	    
+	    // Event click Ok
+		buttonOk.addActionListener(new java.awt.event.ActionListener()
+		{
+            public void actionPerformed(ActionEvent e)
+            {
+            	String Username = txtUsername.getText().toString();
+            	String Password = txtPassword.getText().toString();
+            	
+            	if( !Username.trim().equals("") && !Password.trim().equals("") )
+            	{	 
+            		loginInfo.setProxyUsername(Username);
+            		loginInfo.setProxyPassword(Password);
+            		dialog.setVisible(false);
+            	}
+            }
+		});
+
+	    // Event click Cancel
+		buttonCancel.addActionListener(new java.awt.event.ActionListener()
+		{
+            public void actionPerformed(ActionEvent e)
+            {
+        		loginInfo.setProxyUsername(null);
+        		loginInfo.setProxyPassword(null);
+            	dialog.setVisible(false);
+            }
+		});
+		
+		buttonPanel.setBackground(Color.ORANGE);
+		buttonPanel.add(buttonOk);
+		buttonPanel.add(buttonCancel);
+	    panelMain.add(buttonPanel, gbc);
+		
+	    optionPane = new JOptionPane();
+	    optionPane.setMessage(I18N.gettext("main.connect.proxy.The_proxy_is_requesting_a_username_and_password") + "!");
+	    optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
+	    optionPane.setOptions(new Object[] {panelMain});
+	    dialog = optionPane.createDialog(null, I18N.gettext("main.connect.proxy.Authentication_required"));
+	    dialog.setVisible(true);
+	}
+	
+	private String typeProxy(String proxyServer, int proxyPort, String host, int port) throws IOException
+	{
+		Socket connection = new Socket(proxyServer, proxyPort);
+		PrintWriter writeConnection = new PrintWriter( connection.getOutputStream(), true ); 
+		BufferedReader readConnection = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+		String _return = "";
+		String line = "";
+		int count = 0;
+		
+		String msg = "CONNECT " + host + ":" + port + " HTTP/1.1\r\n" +
+					 "User-Agent: " + sun.net.www.protocol.http.HttpURLConnection.userAgent + "\r\n" + 
+					 "Proxy-Connection: Keep-Alive\r\n";
+
+		writeConnection.println(msg);
+		
+		while((line = readConnection.readLine()) != null )
+		{
+			if( line.indexOf("Proxy") != -1 )
+				_return += line + "\n";
+			
+			if( line.indexOf("Proxy-Connection: close") != -1 || count == 12 )
+				break;
+
+			if( line.indexOf("HTTP/1.0 200") != -1 )
+			{
+				_return += line + "\n";
+				break;
+			}
+			count++;
+		}
+		
+		
+		writeConnection.close();
+		readConnection.close();
+		connection.close();
+
+		return _return;
+	}
+
+	// Proxy Authorization: Basic	
+	private void doTunnelHandshake(Socket tunnel, String host, int port) throws IOException
+	{
+		OutputStream out = tunnel.getOutputStream();
+		
+		String msg = "CONNECT " + host + ":" + port + " HTTP/1.0\n"
+				+ "User-Agent: "
+				+ sun.net.www.protocol.http.HttpURLConnection.userAgent;
+		
+		if ( loginInfo.getProxyUsername() != null && loginInfo.getProxyPassword() != null )
+		{
+			//add basic authentication header for the proxy
+			sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
+			String encodedPassword = enc.encode((loginInfo.getProxyUsername() + ":" + loginInfo.getProxyPassword()).getBytes());
+			msg = msg + "\nProxy-Authorization: Basic " + encodedPassword;
+	    }
+		
+		msg = msg + "\nContent-Length: 0"
+				  + "\nPragma: no-cache"
+		          + "\r\n\r\n";
+
+		byte b[];
+		try
+		{
+			/*
+			 * We really do want ASCII7 -- the http protocol doesn't change
+			 * with locale.
+			 */
+			b = msg.getBytes("ASCII7");
+		} 
+		catch (UnsupportedEncodingException ignored)
+		{
+			/*
+			 * If ASCII7 isn't there, something serious is wrong, but
+			 * Paranoia Is Good (tm)
+			 */
+			b = msg.getBytes();
+		}
+		out.write(b);
+		out.flush();
+
+		/*
+		 * We need to store the reply so we can create a detailed error
+		 * message to the user.
+		 */
+		byte reply[] = new byte[200];
+		int replyLen = 0;
+		int newlinesSeen = 0;
+		boolean headerDone = false; /* Done on first newline */
+		InputStream in = tunnel.getInputStream();
+
+		while ( newlinesSeen < 2 )
+		{
+			int i = in.read();
+			if (i < 0) { throw new IOException("Unexpected EOF from proxy"); }
+			if (i == '\n')
+			{
+				headerDone = true;
+				++newlinesSeen;
+			} else if (i != '\r')
+			{
+				newlinesSeen = 0;
+				if (!headerDone && replyLen < reply.length)
+				{
+					reply[replyLen++] = (byte) i;
+				}
+			}
+		}
+
+		/*
+		 * Converting the byte array to a string is slightly wasteful in the
+		 * case where the connection was successful, but it's insignificant
+		 * compared to the network overhead.
+		 */
+		String replyStr;
+		try
+		{
+			replyStr = new String(reply, 0, replyLen, "ASCII7");
+		}
+		catch (UnsupportedEncodingException ignored)
+		{
+			replyStr = new String(reply, 0, replyLen);
+		}
+
+		/*
+		 * We check for Connection Established because our proxy returns
+		 * HTTP/1.1 instead of 1.0
+		 */
+		
+		// if (!replyStr.startsWith("HTTP/1.0 200")) {
+		if (replyStr.toLowerCase().indexOf("200 connection established") == -1)
+		{
+			String tunnelHost = System.getProperty("http.proxyHost");
+			String tunnelPort = System.getProperty("http.proxyPort");
+			throw new IOException("Unable to tunnel through " + tunnelHost
+					+ ":" + tunnelPort + ".  Proxy returns \"" + replyStr + "\"");
+		}
+		
+		/* tunneling Handshake was successful! */
+	}
+
+	//Proxy-Authenticate Digest
+	private Socket doTunnelHandshakeDigest(String host, int port) throws IOException
+	{
+		Digest hashProxy	= new Digest();
+		String response		= "";
+		String nonce		= "";
+		String cnonce		= "ALEC290908" + String.valueOf(System.currentTimeMillis());
+		String qop			= "";
+		String uri			= "/";
+		String username		= loginInfo.getProxyUsername();
+		String password		= loginInfo.getProxyPassword();
+		String realm		= "";
+		String line			= "";
+
+		// Dados Proxy
+		String proxyServer = loginInfo.getProxyServer();
+		int proxyPort = Integer.valueOf(loginInfo.getProxyPort()).intValue();
+
+		// PRIMEIRA PARTE
+		Socket tunnel = new Socket(proxyServer, proxyPort);
+		PrintWriter writeTunnel = new PrintWriter(tunnel.getOutputStream(), true);
+		BufferedReader readTunnel = new BufferedReader(new InputStreamReader(tunnel.getInputStream()));		
+
+		String msg = "CONNECT " + host + ":" + port + " HTTP/1.1\r\n" +
+					 "User-Agent: " + sun.net.www.protocol.http.HttpURLConnection.userAgent + "\r\n" +
+					 "Proxy-Connection: Keep-Alive\r\n";
+
+		writeTunnel.println(msg);
+		
+		while((line = readTunnel.readLine()) != null )
+		{
+			// Get nonce;
+			if ( line.indexOf("nonce=\"") != -1 )
+			{
+	    		nonce = line.substring(line.indexOf("nonce=\"") + 7);
+	    		nonce = nonce.substring(0,nonce.indexOf("\","));
+			}	
+
+			// Get realm;
+			if ( line.indexOf("realm=\"") != -1 )
+			{
+	    		realm = line.substring(line.indexOf("realm=\"") + 7);
+	    		realm = realm.substring(0,realm.indexOf("\","));
+			}
+			
+			// Get qop;
+			if ( line.indexOf("qop=\"") != -1 )
+			{
+				qop = line.substring(line.indexOf("qop=\"") + 5);
+				qop = qop.substring(0,qop.indexOf("\","));
+			}
+		}
+
+		writeTunnel.close();
+		readTunnel.close();
+		tunnel.close();
+		
+		response = hashProxy.hashMD5(nonce, cnonce, qop, "CONNECT:"+uri, username, password, realm);
+		line = "";
+		
+		// SEGUNDA PARTE
+		tunnel		= new Socket(proxyServer, proxyPort);
+		writeTunnel	= new PrintWriter(tunnel.getOutputStream(), true);
+		readTunnel	= new BufferedReader(new InputStreamReader(tunnel.getInputStream()));		
+
+		msg = "CONNECT " + host + ":" + port + " HTTP/1.1\r\n" +
+			  "Proxy-Authorization: Digest username=\""+username+"\", "+
+		      "realm=\""+realm+"\", nonce=\""+nonce+"\", uri=\""+uri+"\", " +
+		      "cnonce=\""+cnonce+"\", nc=00000001, qop=\""+qop+"\", response=\""+response+"\"\r\n" +
+			  "User-Agent: " + sun.net.www.protocol.http.HttpURLConnection.userAgent + "\r\n" +
+		      "Proxy-Connection: Keep-Alive\r\n";
+
+		int count = 0;
+		writeTunnel.println(msg);
+		
+		while((line = readTunnel.readLine().trim()) != null )
+		{
+			if( line.indexOf("HTTP/1.0 200 OK") != -1 )
+				break;
+			
+			if( line.indexOf("HTTP/1.0 503") != -1 )
+				break;
+			
+			if( count == 1 )
+				break;
+			
+			count++;
+		}
+		
+		/* tunneling Handshake was successful! */		
+		return tunnel;
+	}
+	
+	private void sendLoginMessage(String message)
+	{
+		for(Iterator j = backend.getListeners(LoginListener.class);j.hasNext();)
+		{
+			((LoginListener)j.next()).loginMessage(message);
+		}
+	}
+	
+	private void sendLoginStatus(int count)
+	{
+		/*for(Iterator j = backend.getListeners(LoginListener.class);j.hasNext();)
+		{
+			((LoginListener)j.next()).loginStatus(count);
+		}*/
+	}
+	
+	public void sendLoginError(String message)
+	{
+		for(Iterator j = backend.getListeners(LoginListener.class);j.hasNext();)
+		{
+			((LoginListener)j.next()).loginError(message);
+		}
+	}
+	
+	public void sendUnauthorized()
+	{
+		for(Iterator j = backend.getListeners(LoginListener.class);j.hasNext();)
+		{
+			((LoginListener)j.next()).unauthorized();
+		}
+	}
+
+	synchronized public void connected(String connectionID,String xmppVersion)
+	{
+		if(!abort)
+		{
+			if(xmppVersion!=null && PluginsInfo.isPluginLoaded("xmpp"))
+			{
+				ConnectionPacketReceiver cpr = xmpp.getConnectionPacketReceiver(loginInfo,this);
+				jabberHandler.changePacketReceiver(cpr);
+				cpr.connected(connectionID, xmppVersion);
+			}
+			else
+			{
+				jabberHandler.changePacketReceiver(this);
+				// Este atributo é usado na transferência de arquivos
+				// socket = null; //clear socket reference
+				this.connectionID = connectionID;
+				// TODO remove lowercase if filetransfer bug fixed
+				output.send(new InfoQuery(null,"get",new IQAuth(loginInfo.getUsername().toLowerCase(),null,null)));
+			}
+		}
+		
+	}
+
+	public void authenticate(IQAuth iqAuth)
+	{
+		if(!abort)
+		{
+			authenticationId = "Jeti_Auth_" + new java.util.Date().getTime();
+			
+			if(iqAuth.hasDigest())
+			{
+				MessageDigest sha = null;
+				try {
+				  sha = MessageDigest.getInstance("SHA");
+				} catch (Exception ex){
+				  Log.error(I18N.gettext("main.loginstatus.Could_not_login_with_SHA"));
+				  // TODO remove lowercase if filetransfer bug fixed
+				  output.send(new InfoQuery(null,"set",authenticationId,new IQAuth(loginInfo.getUsername().toLowerCase(),loginInfo.getPassword() ,loginInfo.getResource())));
+				  return;
+				}
+
+				sha.update(connectionID.getBytes());
+				String digest = Utils.toString(sha.digest(loginInfo.getPassword().getBytes()));
+				IQAuthBuilder iqab =  new IQAuthBuilder();
+				iqab.digest = digest;
+				// TODO remove lowercase if filetransfer bug fixed
+				iqab.username = loginInfo.getUsername().toLowerCase();
+				iqab.resource = loginInfo.getResource();
+				output.send(new InfoQuery(null,"set",authenticationId,(IQExtension)iqab.build()));
+			}
+			else
+			{
+				if(!loginInfo.isSSl())
+				{
+					int option = JOptionPane.showConfirmDialog(null,
+							I18N.gettext("main.loginstatus.Sending_password_as_plain_text_over_an_unencrypted_connection,_continue?"),"Plain text",JOptionPane.YES_NO_OPTION);
+					if (option == JOptionPane.NO_OPTION)
+					{
+						sendLoginError("Sending password in plain not allowed");
+						return;
+					}
+				}
+//				TODO remove lowercase if filetransfer bug fixed
+				output.send(new InfoQuery(null,"set",authenticationId,new IQAuth(loginInfo.getUsername().toLowerCase(),loginInfo.getPassword() ,loginInfo.getResource())));
+			}
+		}
+	}
+
+	public void authenticated(InfoQuery infoQuery)
+	{
+		if(!abort)
+		{
+			if(infoQuery.getType().equals("error"))
+			{
+				if(infoQuery.getErrorCode() == 401)
+				{
+					sendUnauthorized();		
+				}
+				else
+				{
+					sendLoginError(I18N.gettext("main.loginstatus.Not_logged_in_because") + " " + infoQuery.getErrorDescription());	
+				}	
+				return;
+			}
+			//TODO remove lowercase if filetransfer bug fixed
+			JID jid = new JID(loginInfo.getUsername().toLowerCase(),loginInfo.getServer() ,loginInfo.getResource());
+			authenticated(jid);
+		}
+	}
+
+	public void authenticated(JID jid)
+	{
+		myJID = jid;
+		iqTimerQueue.clear();//remove iqs from previous connections
+		jabberHandler.changePacketReceiver(new Jabber(backend,capabilities,discovery,iqTimerQueue));
+		authenticated = true;
+        reconnecting = false;
+		output.setAuthenticated(); 
+
+		//TODO increase timeout
+		getItems(new JID(loginInfo.getServer()), new DiscoveryListener()
+		{
+			public void discoveryItemResult(JID jid, DiscoveryItem item)
+			{
+				//cache disco items for this server
+				if (item.hasItems())
+				{
+					for(Iterator i = item.getItems();i.hasNext();)
+					{
+						DiscoveryItem di = (DiscoveryItem)i.next();
+						backend.getInfo(di.getJID(),null);
+					}
+				}
+			}
+			public void discoveryInfoResult(JID jid, DiscoveryInfo info) {}
+		});
+		output.send(new InfoQuery("get",new IQPrivate(new JetiPrivateExtension())));
+		output.send(new InfoQuery("get",new IQXRoster()));
+		if(show == Presence.NONE)
+		{
+			show = Presence.AVAILABLE;
+		}
+		latestConnected = System.currentTimeMillis();
+		
+	}
+	
+	public void connected()
+	{
+		sendStatus();
+		send(new InfoQuery("get",new IQPrivate(new JetiPrivateRosterExtension())));
+		for( Iterator j = backend.getListeners(nu.fw.jeti.events.StatusChangeListener.class) ; j.hasNext(); )
+		{
+			//online
+			((nu.fw.jeti.events.StatusChangeListener)j.next()).connectionChanged(true);
+		}
+	}
+
+	public void receivePackets(Packet packet)
+	{
+		if (packet instanceof StreamError) streamError((StreamError)packet);
+		else if(authenticationId.equals(packet.getID())) authenticated((InfoQuery)packet);
+		else if(packet instanceof InfoQuery)
+		{
+		    IQExtension extension = packet.getIQExtension();
+			if(extension instanceof IQAuth)
+			{
+				if(((InfoQuery)packet).getType().equals("error"))
+				{
+					sendLoginError(packet.getErrorDescription());
+				}
+				else authenticate((IQAuth) extension);
+			}
+		}
+	}
+
+	public void inputDeath()
+	{
+		try
+		{
+			if( socket != null ) socket.close();
+		}
+		catch (IOException e)
+		{
+			e.printStackTrace();
+		}
+		
+		if((authenticated || reconnecting))
+		{
+			authenticated = false;
+            if (reconnecting) {
+                try {
+                    Thread.sleep(10000);
+                } catch (InterruptedException e) {};
+            }
+            reconnecting = true;
+			output.disconnect(true);
+			for(Iterator j = backend.getListeners(nu.fw.jeti.events.StatusChangeListener.class);j.hasNext();)
+			{
+				//offline
+				((nu.fw.jeti.events.StatusChangeListener)j.next()).connectionChanged(false);
+			}
+		}
+		
+	}
+
+	public void streamError(StreamError error)
+	{
+		//only when logging in
+		if(authenticated)
+		{
+			authenticated = false;
+			output.disconnect(true);
+			for(Iterator j = backend.getListeners(nu.fw.jeti.events.StatusChangeListener.class);j.hasNext();)
+			{
+				//offline
+				((nu.fw.jeti.events.StatusChangeListener)j.next()).connectionChanged(false);
+			}
+		}
+		sendLoginError(error.getErrorDescription());
+	}
+
+	public void outputDeath()
+	{
+		authenticated = false;
+		for(Iterator j = backend.getListeners(nu.fw.jeti.events.StatusChangeListener.class);j.hasNext();)
+		{
+			//offline
+			((nu.fw.jeti.events.StatusChangeListener)j.next()).connectionChanged(false);
+		}
+	}
+
+	public static JID getMyJID()
+	{
+	    return myJID;
+	}
+
+	public boolean getOnline()
+	{
+	    return authenticated;
+	}
+
+	public void disconnect()
+	{
+		if( authenticated )
+		{
+			send(new Presence(myJID, "unavailable"));
+			authenticated = false;
+			output.disconnect(true);
+		}
+		output = null;
+		for(Iterator j = backend.getListeners(nu.fw.jeti.events.StatusChangeListener.class);j.hasNext();)
+		{
+			//offline
+			((nu.fw.jeti.events.StatusChangeListener)j.next()).connectionChanged(false);
+		}
+	}
+
+	public void exit()
+	{
+		if(authenticated)
+		{
+			send(new nu.fw.jeti.jabber.elements.Presence(myJID,"unavailable"));
+			authenticated = false;
+			output.disconnect(true);
+		}
+		output = null;
+		for(Iterator j = backend.getListeners(nu.fw.jeti.events.StatusChangeListener.class);j.hasNext();)
+		{//exit
+			((nu.fw.jeti.events.StatusChangeListener)j.next()).exit();
+		}
+	}
+
+	public void send(Packet packet)
+	{
+		if(authenticated) output.send(packet);
+		else nu.fw.jeti.util.Log.notSend(packet.toString());
+	}
+	
+	public void sendWhileConnecting(Packet packet)
+	{
+		output.send(packet);
+	}
+
+	public String getAccountInfo()
+	{
+		return MessageFormat.format(I18N.gettext("main.popup.logged_in_as_{0}_on_server_{1}_with_resource_{2}"),new Object[]{loginInfo.getUsername(),loginInfo.getServer(),loginInfo.getResource()});
+	}
+
+	public void sendStatus()
+	{
+		if( authenticated )
+		{
+			PresenceBuilder pb = new PresenceBuilder();
+			pb.show = show;
+			pb.status = status;
+			pb.priority = loginInfo.getPriority();
+			pb.addExtension(capabilities.getCaps());
+
+			try
+			{
+				send(pb.build());
+			} catch (InstantiationException e)
+			{
+				e.printStackTrace();
+			}
+			for(Iterator j = backend.getListeners(nu.fw.jeti.events.StatusChangeListener.class);j.hasNext();)
+			{
+				((nu.fw.jeti.events.StatusChangeListener)j.next()).ownPresenceChanged(show,status);
+			}
+		}
+	}
+	
+	public void changeStatus(int show,String status)
+	{
+		this.show = show;
+		this.status = status;
+		sendStatus();
+	}
+
+	public static class DummySSLSocketFactory extends SSLSocketFactory
+	{
+		private SSLSocketFactory factory;
+		
+		public DummySSLSocketFactory()
+		{
+			try
+			{
+				SSLContext sslcontent = SSLContext.getInstance("TLS");
+				sslcontent.init(null, // KeyManager not required
+								new TrustManager[] {new DummyTrustManager()},
+								new java.security.SecureRandom());
+				factory = sslcontent.getSocketFactory();
+			}
+			catch (NoSuchAlgorithmException e)
+			{
+				e.printStackTrace();
+			}
+			catch (KeyManagementException e)
+			{
+				e.printStackTrace();
+			}
+		}
+	
+		public Socket createSocket(Socket socket, String s, int i,boolean flag)	throws IOException
+		{
+			return factory.createSocket(socket, s, i, flag);
+		}
+
+		public Socket createSocket(InetAddress inaddr, int i,InetAddress inaddr2, int j) throws IOException
+		{
+			return factory.createSocket(inaddr, i, inaddr2, j);
+		}
+
+		public Socket createSocket(InetAddress inaddr, int i) throws IOException
+		{
+			return factory.createSocket(inaddr, i);
+		}
+
+		public Socket createSocket(String s, int i, InetAddress inaddr,	int j)	throws IOException
+		{
+			return factory.createSocket(s, i, inaddr, j);
+		}
+
+		public Socket createSocket(String s, int i)	throws IOException
+		{
+		  
+		  /**
+	       * register a callback for handshaking completion event
+	       */
+			
+			Socket so = factory.createSocket(s, i);
+	        ((SSLSocket)so).addHandshakeCompletedListener(new HandshakeCompletedListener()
+				{
+					public void handshakeCompleted(HandshakeCompletedEvent event)
+					{
+						System.out.println("** Handshake finished! **");
+						//System.out.println("\t CipherSuite:" + event.getCipherSuite());
+						//System.out.println("\t SessionId " + event.getSession());
+						//System.out.println("\t PeerHost " + event.getSession().getPeerHost());
+					}
+				}
+	        );
+	        
+	        return so;
+		}
+		
+		public String[] getDefaultCipherSuites()
+		{
+			return factory.getSupportedCipherSuites();
+		}
+
+		public String[] getSupportedCipherSuites()
+		{
+			return factory.getSupportedCipherSuites();
+		}
+	}
+
+	/**
+	 * Trust manager which accepts certificates without any validation
+	 * except date validation.
+	 */
+	private static class DummyTrustManager implements X509TrustManager
+	{
+
+		public void checkClientTrusted(X509Certificate[] chain, String authType) {	}
+
+		public void checkServerTrusted(X509Certificate[] chain, String authType)
+		{
+			try
+			{
+				chain[0].checkValidity();
+			}
+			catch (CertificateExpiredException e){}
+			catch (CertificateNotYetValidException e){}
+		}
+
+		public X509Certificate[] getAcceptedIssuers()
+		{
+			return new X509Certificate[0];
+		}
+	}
+ }
+
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/XMLData.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/XMLData.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/XMLData.java	(revision 3952)
@@ -0,0 +1,254 @@
+/*
+ *   License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "License").  You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the License.  You
+ * may obtain a copy of the License at http://www.jabber.com/license/ or at
+ * http://www.opensource.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ *   Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 2000 Jabber.com, Inc.  All Rights Reserved.  Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1999-2000 David Waite
+ *
+ *   Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ *   Changes
+ *   renamed/added methods
+ */
+package nu.fw.jeti.backend;
+
+import java.util.Map;
+import java.util.Iterator;
+
+/**
+ * an XMLData object is the root class of a hierarchy of objects, all which
+ * serialize themselves to XML data.
+ *
+ * @author  David Waite <a href="mailto:dwaite@jabber.com">
+ *                      <i>&lt;dwaite@jabber.com&gt;</i></a>
+ * @author  Author: mass
+ * @author  Author: E.S. de Boer
+ * @version Revision: 2
+ */
+public abstract class XMLData
+{
+	/**
+	 * <code>toString</code> is a serializer for the data contained in the
+	 * object to an equivalent snippet of XML stream.
+	 *
+	 * @return a <code>String</code> value which contains the XML
+	 * representation of this object
+     */
+	public String toString()
+    {
+		StringBuffer retval=new StringBuffer();
+		appendToXML(retval);
+		return new String(retval);
+	}
+
+    /**
+     * <code>appendToXML</code> appends the XML representation of the
+     * current packet data to the specified <code>StringBuffer</code>.
+     *
+     * @param xml The <code>StringBuffer</code> to append to
+     */
+    public abstract void appendToXML(StringBuffer xml);
+
+    /**
+     * <code>appendElement</code> adds the XML for a child 'element' to a
+     * StringBuffer, as a helper to appendItem.
+     *
+     * @param xml a <code>StringBuffer</code> value
+     * @param name a <code>String</code> value
+     * @param value a <code>String</code> value
+     *
+     * @return a <code>bool</code> which is true if the element was generated.
+     *         This is used for any shortcuts in the outputted XML
+     */
+    protected static final boolean appendElement(StringBuffer xml,String name,String value)
+    {
+        if (value==null) return false;
+        xml.append( '<' );
+        xml.append(name);
+        if(value.equals(""))
+		{//shortcut
+			xml.append("/>" );
+			return true;
+		}
+		xml.append( '>' );
+        escapeString(xml, value);
+        xml.append( "</" );
+        xml.append(name);
+        xml.append( '>' );
+	    return true;
+    }
+    
+    
+	/**
+		 * <code>appendElement</code> adds the XML for a child 'element' to a
+		 * StringBuffer, as a helper to appendItem.
+		 *
+		 * @param xml a <code>StringBuffer</code> value
+		 * @param name a <code>String</code> value
+		 * @param value a <code>String</code> value
+		 *
+		 * @return a <code>bool</code> which is true if the element was generated.
+		 *         This is used for any shortcuts in the outputted XML
+		 */
+		protected static final boolean appendElement(StringBuffer xml,String name,Object value)
+		{
+			if (value==null) return false;
+			return appendElement(xml,name,value.toString()); 
+		}
+
+    /**
+     * <code>appendElement</code> outputs an element if needed. If value=false
+     * it does not output the tag.
+     *
+     * @param xml a <code>StringBuffer</code> value
+     * @param name a <code>String</code> value
+     * @param value a <code>boolean</code> value
+     *
+     * @return a <code>bool</code> which is true if the element was generated.
+     *         This is used for any shortcuts in the outputted XML
+     */
+    protected static final boolean appendElement(StringBuffer xml,String name,boolean value)
+    {
+        if (value==false)return false;
+        xml.append( '<' );
+        xml.append(name);
+        xml.append( "/>" );
+	    return true;
+    }
+
+	/**
+	 * <code>appendElement</code> adds a the entry's of a map to the stingbuffer
+	 *
+	 * @param xml a <code>StringBuffer</code> value
+	 * @param map a <code>Map</code> value
+	 *
+	 * @return a <code>bool</code> which is true if the element was generated.
+	 *         This is used for any shortcuts in the outputted XML
+	 */
+	protected static final boolean appendElement(StringBuffer xml,Map map)
+	{
+		if (map == null) return false;
+		for (Iterator i=map.entrySet().iterator(); i.hasNext(); )
+		{
+			Map.Entry e = (Map.Entry) i.next();
+			String name = (String)e.getKey();
+			String value =(String)e.getValue();
+
+			if (value==null) continue;
+			xml.append( '<' );
+			xml.append(name);
+			if(value.equals(""))
+			{//shortcut
+				xml.append("/>" );
+				continue;
+			}
+			xml.append( '>' );
+			escapeString(xml, value);
+			xml.append( "</" );
+			xml.append(name);
+			xml.append( '>' );
+		}
+		return false;
+    }
+
+    /**
+     * <code>appendAttribute</code> outputs an attribute if needed. If value=null
+     * it will not output the attribute. Note that it outputs in the format:<p>
+     * &qout;&nbsp;<i>attrib</i>=<i>value</i>&qout;, or it puts a proceeding
+     * space in front of the attribute, and no trailing space. This is for
+     * optimizing the XML generation.
+     *
+     * @param xml a <code>StringBuffer</code> value
+     * @param name a <code>String</code> value
+     * @param value a <code>boolean</code> value
+     *
+     * @return a <code>bool</code> which is true if the attribute was
+     * generated. This is used for any shortcuts in the outputted XML
+     */
+    protected static final boolean appendAttribute(StringBuffer xml,String name,String value)
+    {
+		if (value==null || value.length()<1) return false;
+		xml.append( ' ' );
+		xml.append(name );
+		xml.append("=\"");
+		escapeString(xml,value);
+		xml.append("\"");
+		return true;
+    }
+
+    /**
+     * <code>appendAttribute</code> outputs an attribute if needed. If value=null
+     * it will not output the attribute. Note that it outputs in the format:<p>
+     * &qout;&nbsp;<i>attrib</i>=<i>value</i>&qout;, or it puts a proceeding
+     * space in front of the attribute, and no trailing space. This is for
+     * optimizing the XML generation.
+     *
+     * @param xml a <code>StringBuffer</code> value
+     * @param name a <code>String</code> value
+     * @param value a <code>Object</code> value, which supports toString()
+     *
+     * @return a <code>bool</code> which is true if the attribute was
+     * generated. This is used for any shortcuts in the outputted XML
+     */
+    protected static final boolean appendAttribute(StringBuffer xml,String name,Object value)
+    {
+		if (value==null) return false;
+	    return appendAttribute(xml,name,value.toString());
+    }
+
+    public static final void escapeString(StringBuffer xml, String data)
+    {
+    	char c;
+		for (int i=0;i<data.length();i++)
+		{
+			int d = data.charAt(i);
+			if(d>=32 || d==9 ||d==10 || d==13)
+			{
+				switch(c=data.charAt(i))
+				{
+					case '&':
+					xml.append("&amp;");
+					break;
+					case '<':
+					xml.append("&lt;");
+					break;
+					case '>':
+					xml.append("&gt;");
+					break;
+					case '\'':
+					xml.append("&apos;");
+					break;
+					case '\"':
+					xml.append("&quot;");
+					break;
+					default:
+					xml.append(c);
+		        }
+			}
+	    }
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/XMLDataFile.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/XMLDataFile.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/XMLDataFile.java	(revision 3952)
@@ -0,0 +1,138 @@
+package nu.fw.jeti.backend;
+
+import java.util.Map;
+
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2002 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+public abstract class XMLDataFile
+{
+	int tabDepth=0;
+
+    public XMLDataFile()
+    {
+    }
+
+	private void addTabs(StringBuffer xml)
+	{
+		xml.append("\r\n");
+		for(int i =0;i<tabDepth;i++)
+		{
+			xml.append("    ");
+		}
+	}
+
+	/**
+	 * <code>toString</code> is a serializer for the data contained in the
+	 * object to an equivalent snippet of XML stream.
+	 *
+	 * @return a <code>String</code> value which contains the XML
+	 * representation of this object
+	 */
+	public String toString()
+	{
+		StringBuffer retval=new StringBuffer();
+		appendToXML(retval);
+		return new String(retval);
+	}
+
+	/**
+	* <code>appendToXML</code> appends the XML representation of the
+	* current packet data to the specified <code>StringBuffer</code>.
+	*
+	* @param xml The <code>StringBuffer</code> to append to
+	*/
+    public abstract void appendToXML(StringBuffer xml);
+
+	protected final void appendHeader(StringBuffer xml)
+	{
+	   xml.append("<?xml version=\"1.0\"?>");
+    }
+
+	protected final void appendOpenTag(StringBuffer xml,String name)
+	{
+		//xml.append("\r\n");
+		addTabs(xml);
+		tabDepth++;
+		xml.append(name);
+	}
+
+	protected final void appendCloseTag(StringBuffer xml,String name)
+	{
+		//xml.append("\r\n");
+		tabDepth--;
+		addTabs(xml);
+		xml.append(name);
+
+	}
+
+	protected final void appendCloseSymbol(StringBuffer xml)
+	{
+		tabDepth--;
+		xml.append("/>");
+	}
+
+	protected final boolean appendElement(StringBuffer xml,String name,String value)
+	{
+	   //xml.append("\r\n");
+	   addTabs(xml);
+	   boolean result = HelpXMLData.appendElement(xml,name,value);
+	   //xml.append('\n');
+	   return result;
+	}
+
+   protected final boolean appendElement(StringBuffer xml,String name,boolean value)
+   {
+	   //xml.append("\r\n");
+	   addTabs(xml);
+	   return HelpXMLData.appendElement(xml,name,value);
+   }
+
+   protected final boolean appendElement(StringBuffer xml,Map map)
+   {
+	   //xml.append("\r\n");
+	   addTabs(xml);
+	   return HelpXMLData.appendElement(xml,map);
+   }
+
+   protected final boolean appendAttribute(StringBuffer xml,String name,String value)
+   {
+	  return HelpXMLData.appendAttribute(xml,name,value);
+   }
+
+   protected final boolean appendAttribute(StringBuffer xml,String name,Object value)
+   {
+	   return HelpXMLData.appendAttribute(xml,name,value);
+   }
+
+   abstract static class HelpXMLData extends XMLData
+   {
+
+   }
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/PacketReceiver.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/PacketReceiver.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/PacketReceiver.java	(revision 3952)
@@ -0,0 +1,21 @@
+package nu.fw.jeti.backend;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public interface PacketReceiver
+{
+	public void receivePackets(nu.fw.jeti.jabber.elements.Packet packet);
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/Start.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/Start.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/Start.java	(revision 3952)
@@ -0,0 +1,233 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.backend;
+
+import java.awt.*;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Properties;
+
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import nu.fw.jeti.images.StatusIcons;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.plugins.PluginsInfo;
+import nu.fw.jeti.ui.Jeti;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Log;
+import nu.fw.jeti.util.Popups;
+import nu.fw.jeti.util.Preferences;
+
+import org.xml.sax.SAXException;
+/**
+ * @author E.S. de Boer
+ */
+
+public class Start 
+{
+	public static final String VERSION = "0.7.7";
+	public final static String OS2 = "";
+	public static String BUILD_DATE;
+	public static String BUILD_NUM;
+	public static URL programURL; //url where the plugins are change to plugins url can use always also if local
+	public static URL dataURL; //url where the data is stored
+	public static String pluginPath; //plugin path
+	public static String path; //local path
+	public static boolean applet=false;
+	public static boolean webstart=false;
+	private SAXParser parser;
+	private static Splash splash;
+	private PluginsInfo pluginsInfo;
+	private Backend backend;
+	
+	public Start(String urlString, Container container)
+	{
+		try
+		{
+			parser = SAXParserFactory.newInstance().newSAXParser();
+		}
+		catch (FactoryConfigurationError ex)
+		{
+			ex.printStackTrace();
+		}
+		catch (SAXException ex)
+		{
+			ex.printStackTrace();
+		}
+		catch (ParserConfigurationException ex)
+		{
+			ex.printStackTrace();
+		}
+		
+		if ( urlString == null) 
+			urlString = getPath();
+		
+		if ( System.getProperty("file.separator").equals("/") )
+			pluginPath = urlString.substring(5);
+		else 
+			pluginPath = urlString.substring(6);
+		
+		pluginPath = pluginPath.replace('/', File.separatorChar);
+		
+		/**
+		 * Se o programurl for null, applet ou network tb usa program path como data path
+		 */
+		
+		dataURL = programURL;
+		path = pluginPath;
+
+		try
+		{
+			Properties p = new Properties();
+			p.load(getClass().getResource("version.properties").openStream());
+			BUILD_DATE = p.getProperty("buildDate");
+			BUILD_NUM = p.getProperty("buildNum");
+		} 
+		catch (Exception e){}
+		
+		backend = new Backend(this);
+		new Preferences( backend, parser );
+		new StatusIcons();
+		Jeti jeti = new Jeti(backend,container);
+		new Popups( backend.getMainWindow() );
+		pluginsInfo = new PluginsInfo(backend, this);
+		jeti.init();
+		if( container != null)
+			container.add(jeti);
+		else 
+			backend.getMainWindow().setVisible(true);
+	}
+	
+	public void setSplashText(String text) 
+	{
+		nu.fw.jeti.applet.Jeti.from.setText(I18N.gettext("Loading") + " " + text + "...");
+	}
+
+	private String getPath()
+	{
+		URL url = this.getClass().getResource("Start.class");
+		//System.out.println(url);
+		String urlString = null;
+
+		try
+		{ 
+			//remove %20 from program files etc
+			urlString = URLDecoder.decode(url.toString(), "UTF8"); //encode if to url? probaly not
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+		}
+
+		if (url.getProtocol().equals("jar"))
+		{
+			// Strip off JarURL-specific syntax.
+			urlString = urlString.substring(4);
+			urlString = urlString.substring(0, urlString.lastIndexOf("!"));
+			urlString = urlString.substring(0, urlString.lastIndexOf("/") + 1);
+		}
+		else urlString = urlString.substring(0, urlString.lastIndexOf("/jeti") + 1);
+		return urlString;
+	}
+
+	public static void remoteLoad(URL url, String path)
+	{
+		Start.pluginPath = path;
+		Start.programURL = url;
+		start(pluginPath,null);
+	}
+	
+	public static void main(String[] args)
+	{
+		if(args.length>0)path = args[0];
+		start(null,null);
+	}
+	
+	public static void start(String path,Container container)
+	{
+		Frame frame = new Frame();
+		splash = new Splash(frame);
+		new Start(path,container);
+		splash.dispose();
+		frame.dispose();
+		splash=null;
+	}
+	
+	public Backend getBackend()
+	{
+		return backend;
+	}
+	
+	public void close()
+	{
+		if( backend != null ) backend.getMain().close();
+	}
+	
+	public void exit()
+	{
+		pluginsInfo.exit();
+		nu.fw.jeti.applet.Jeti.exit();
+		backend = null;
+	}
+
+    static class Splash extends Window
+    {
+        Label from;
+        public Splash(Frame frame)
+        { 
+            super(frame);
+            Label label = new Label("Jeti" + OS2 + " is Loading"+"...");
+            label.setFont(new java.awt.Font("Serif", 1, 40)); 
+            label.setBackground(new Color(150,150,255));
+            add(label,BorderLayout.CENTER);
+            Panel panel = new Panel();
+            panel.setBackground(new Color(255,255,150));
+            from = new Label("(c) 2001-2008 E.S. de Boer");
+            from.setBackground(new Color(255,255,150));
+            panel.add(from);
+            Label version = new Label("Version: " + Start.VERSION);
+            version.setBackground(new Color(255,255,150));
+            panel.add(version);
+            add(panel,BorderLayout.SOUTH); 
+            pack();
+            setLocationRelativeTo(null);
+            setVisible(true);
+        }
+    }
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/ResourceJIDStatus.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/ResourceJIDStatus.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/ResourceJIDStatus.java	(revision 3952)
@@ -0,0 +1,129 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created in 2002
+ */
+
+package nu.fw.jeti.backend.roster;
+
+
+import java.util.List;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+
+
+/**
+  * @author E.S. de Boer
+  */
+
+public class ResourceJIDStatus extends SecondaryJIDStatus implements Cloneable
+{
+	private NormalJIDStatus normalJIDStatus;
+
+	public ResourceJIDStatus(JID jid,NormalJIDStatus normalJIDStatus)
+	{
+		this.completeJID = jid;
+		this.normalJIDStatus = normalJIDStatus;
+	}
+
+	void update(JID jid,int show, String status,boolean online)
+	{
+		this.online = online;
+		this.show = show;
+		this.status = status;
+		this.completeJID = jid;
+	}
+
+	public String toString()
+	{//jid
+		return completeJID.toString();
+	}
+	
+	public JIDStatus normalJIDStatus()
+	{
+		return normalJIDStatus; 
+	}
+
+	public JID getJID(){return normalJIDStatus.getJID();}
+
+	
+
+	public String getNick()
+	{
+		return normalJIDStatus.getNick();
+	}
+
+	public String getType()
+	{
+		return normalJIDStatus.getType();
+	}
+
+	public String getSubscription(){return normalJIDStatus.getSubscription();}
+
+	public String getWaiting()
+	{
+		return normalJIDStatus.getWaiting();
+	}
+
+	public List getGroupsCopy(){return normalJIDStatus.getGroupsCopy();}
+
+	public int groupCount(){return normalJIDStatus.groupCount();}
+
+	public boolean isGroupPresent(String group){return normalJIDStatus.isGroupPresent(group);}
+
+	public int compareTo(Object o)
+	{//compare jid
+		//if(nickname==null) return -1;
+		//return nickname.compareTo(((JIDStatus2)o).nickname);
+		if(!(o instanceof ResourceJIDStatus)) return normalJIDStatus.compareTo(this,(JIDStatus)o);
+		ResourceJIDStatus resourceJIDStatus = (ResourceJIDStatus)o;
+		if(this.equals(resourceJIDStatus)) return 0;
+		int result = (normalJIDStatus.compareTo(this,resourceJIDStatus));
+		if(result == 0)
+		{
+			return completeJID.getResource().compareTo(resourceJIDStatus.getCompleteJID().getResource());
+		}
+		return result;
+	}
+
+
+
+	public Object clone()
+	{//shallow copy
+		try
+		{
+			return super.clone();
+		}
+		catch (CloneNotSupportedException ex)
+		{
+			ex.printStackTrace();
+			return null;
+		}
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/SecondaryJIDStatus.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/SecondaryJIDStatus.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/SecondaryJIDStatus.java	(revision 3952)
@@ -0,0 +1,61 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created in 2002
+ */
+
+package nu.fw.jeti.backend.roster;
+
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.Presence;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public abstract class SecondaryJIDStatus extends JIDStatus
+{
+	private String msnNick;
+		
+	public void updatePresence(Presence presence)
+	{
+		online = presence.getType().equals("available");
+		show = presence.getShow();
+		status = presence.getStatus();
+		completeJID = presence.getFrom();
+	}
+	
+	public String getMSNNick()
+	{
+		return msnNick;
+	}
+	
+	public void setMSNNick(String nick)
+	{
+		msnNick = nick;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/NormalJIDStatus.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/NormalJIDStatus.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/NormalJIDStatus.java	(revision 3952)
@@ -0,0 +1,413 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *    
+ */
+
+package nu.fw.jeti.backend.roster;
+
+import java.util.*;
+
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.Presence;
+import nu.fw.jeti.jabber.elements.RosterItem;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+/**
+ * Copyright:    Copyright (c) 2001
+ * @author E.S. de Boer
+ */
+
+ //stores jid + status
+
+//inconsistent with equals to make consistant add jid + equals if comletejids are same
+public class NormalJIDStatus extends SecondaryJIDStatus
+{
+	private JID jid;
+	private String nickname;
+	private String subscription;
+	private String waiting;
+	private List groups;
+	private ArrayList resources;
+	private String resource;
+	private String type;
+	private boolean forcedOffline=false;//if transport loggedoff
+	
+	public NormalJIDStatus(RosterItem item)
+	{//call update??
+		jid = item.getJID();
+		completeJID = jid;
+		show = Presence.UNAVAILABLE;
+		resource = completeJID.getResource();
+		JIDStatus server = Roster.getJIDStatus(new JID(jid.getDomain()));
+		if(server == null) type =null;
+		else type = server.getType();
+		update(item);
+	}
+	
+	//add to special transport jid?
+	public boolean isForcedOffline()
+	{
+		return forcedOffline;
+	}
+	
+	public void setForcedOffline(boolean forced)
+	{
+		forcedOffline = forced;
+	}
+	
+	//TODO REMOVE
+	/**
+	 * Make jidStatus from jid and nickname (used by groupchat) 
+	 * @param jid
+	 * @param nickname
+	 */
+	public NormalJIDStatus(JID jid,String nickname)
+	{//call update??
+		this.jid = jid;
+		completeJID = jid;
+		this.nickname = nickname; 
+		if(nickname == null || nickname.equals(""))
+		{
+		   this.nickname = jid.toString();
+		}
+		show = Presence.UNAVAILABLE;
+		resource = completeJID.getResource();
+		//System.out.println("jidstatus made");
+		JIDStatus server = Roster.getJIDStatus(new JID(jid.getDomain()));
+		if(server == null) type =null;
+		else type = server.getType();
+	}
+
+	public void update(RosterItem item)
+	{
+		nickname = item.getName();
+		if(nickname == null || nickname.equals(""))
+		{
+			nickname = jid.toString();
+		}
+		subscription = item.getSubscription();
+		waiting = item.getAsk();
+		if(item.getGroups()==null)
+		{
+			groups = new LinkedList(); 
+			groups.add(I18N.gettext("main.main.roster.Unfiled"));
+		}
+		else groups = item.getGroups();
+	}
+
+	public void updatePresence(Presence presence)
+	{//resources
+		super.updatePresence(presence);
+		resource = completeJID.getResource();
+	}
+
+	public void sortSecondary()
+	{//swap resources ,highest in normal
+		if(resources == null) return;
+		Collections.sort(resources);
+		if(this.compareTo(resources.get(0)) > 0)
+		{
+			//System.out.println("swap");
+			ResourceJIDStatus resourceJIDStatus = (ResourceJIDStatus) resources.get(0);
+			ResourceJIDStatus tempJIDStatus =(ResourceJIDStatus) resourceJIDStatus.clone();
+			resourceJIDStatus.update(completeJID,show,status,online);
+			completeJID = tempJIDStatus.getCompleteJID();
+			resource = completeJID.getResource();
+			show = tempJIDStatus.getShow();
+			status = tempJIDStatus.getStatus();
+			online = tempJIDStatus.isOnline();
+			Collections.sort(resources);
+		}
+	}
+
+	public String toString()
+	{//jid
+		if("msn".equals(type)&& Preferences.getBoolean("jeti","showRealNick", false))
+		{
+			if(getMSNNick()!=null)return getMSNNick();
+			if(status!=null && jid.getUser()!=null)
+			{
+				 return status;
+			}
+			return nickname;
+		}
+		return nickname;
+	}
+
+	public JID getJID(){return jid;}
+
+		
+	public void setStatus(String status)
+	{
+		this.status = status;
+	}
+
+	public String getNick()
+	{
+		return nickname;
+	}
+
+	public void setNick(String nick)
+	{
+		nickname = nick;
+	}
+
+	public String getType()
+	{
+		if(type == null) return "jabber";
+		return type;
+	}
+
+	public void setType(String type)
+	{
+		//System.out.println(type);
+		this.type = type;
+	}
+
+
+//	public StringArray getNamespaces()
+//	{
+//		return namespaces;
+//	}
+//
+//	public void setNamespaces(StringArray namespaces)
+//	{
+//		this.namespaces = namespaces;
+//	}
+
+
+	public String getSubscription(){return subscription;}
+
+	public String getWaiting()
+	{
+		return waiting;
+	}
+	
+	public JIDStatus normalJIDStatus()
+	{
+		return this;
+	}
+
+	public SecondaryJIDStatus getSecondaryJIDStatus(String resource)
+	{
+		if(this.resource == null)
+		{
+			this.resource = resource;
+			return this;
+		}
+		if(resource.equals(this.resource)) return this;
+		if(resources !=null)
+		{
+			for(Iterator i = resources.iterator();i.hasNext();)
+			{
+				SecondaryJIDStatus secondary = (SecondaryJIDStatus)i.next();
+				if(resource.equals(secondary.getCompleteJID().getResource())) return secondary;
+			}
+		}
+		return null;
+	}
+
+	public ResourceJIDStatus addSecondaryJIDStatus(JID jid)
+	{
+		ResourceJIDStatus resource = new ResourceJIDStatus(jid,this);
+		if(resources == null) resources = new ArrayList(4);
+		resources.add(resource);
+		return resource;
+	}
+	
+	
+	public Iterator getSecondaryJIDStatussen()
+	{
+		if(resources ==null)
+		{
+			return new Iterator()
+			{
+				public boolean hasNext()
+				{
+					return false;
+				}
+				
+				public Object next(){throw new NoSuchElementException();}
+				
+				public void remove(){}
+			};
+		}
+		else
+		{
+			return new Iterator()
+			{
+				private int count = resources.size();
+							
+				public boolean hasNext()
+				{
+					return count > 0;
+				}
+				
+				public Object next()
+				{
+					count--;
+					if(count<0)throw new NoSuchElementException();
+					return resources.get(count); 
+				}
+				
+				public void remove(){}
+				
+			};
+		}
+	}
+	
+	public ResourceJIDStatus removeResource(String resource)
+	{
+		//System.out.println(resource + " mee");
+		//System.out.println(this.resource + " eigen");
+		
+		if(resource == null) return null; //no resource so remove nothing
+		if(resource.equals(this.resource))
+		{
+			//System.out.println(resource + " equal"    );
+			if(resources != null)
+			{
+				ResourceJIDStatus resourceJIDStatus =(ResourceJIDStatus) resources.remove(0);
+				completeJID = resourceJIDStatus.getCompleteJID();
+				this.resource =    completeJID.getResource();
+				show = resourceJIDStatus.getShow();
+				status = resourceJIDStatus.getStatus();
+				online = resourceJIDStatus.isOnline();
+
+				if(resources.isEmpty()) resources = null;
+				return resourceJIDStatus ;
+			}
+			else this.resource = null;
+			completeJID = jid;
+			return null;//resource does not change
+		}
+		if(resources==null) return null;// removing something that not exists
+	  	for(Iterator i = resources.iterator();i.hasNext();)
+		{
+			ResourceJIDStatus resourceJIDStatus = (ResourceJIDStatus)i.next();
+			if(resourceJIDStatus.getCompleteJID().getResource().equals(resource))
+			{
+				resources.remove(resourceJIDStatus);
+				if(resources.isEmpty()) resources = null;
+				return resourceJIDStatus;
+			}
+		}
+		System.out.println("error"); //hier hoort ie niet te komen
+		return null;
+	}
+
+	public int groupCount()
+	{
+		if(groups == null) return 0;
+		return groups.size();
+	}
+
+	public List getGroupsCopy()
+	{
+		return new LinkedList(groups);
+	}
+
+	public Iterator getGroups()
+	{
+		return groups.iterator();
+	}
+
+	public boolean isGroupPresent(String group)
+	{
+		return groups.contains(group);
+	}
+	
+/*
+	public boolean equals(Object object)
+	{
+		if (object instanceof JIDStatus)
+		{
+			JIDStatus temp = (JIDStatus)object;
+			return temp.getCompleteJID().equals(completeJID);
+		}
+		else return false;
+	}
+	
+	public int hashCode()
+	{
+		System.out.println("hash");
+		return completeJID.hashCode();  
+	}
+*/	
+
+	public int compareTo(Object o)
+	{//compare jid
+		return compareTo(this,(JIDStatus)o);
+	}
+
+	public int compareTo(JIDStatus jidStatus1,JIDStatus jidStatus)
+	{
+		if(jidStatus1.equals(jidStatus)) return 0;
+		if(jidStatus1.getShow() == jidStatus.getShow())
+		{
+			if(jidStatus1.getJID().getDomain().equals(nu.fw.jeti.backend.Connect.getMyJID().getDomain()))
+			{//is this jid own server?
+				if(jidStatus.getJID().getDomain().equals(nu.fw.jeti.backend.Connect.getMyJID().getDomain()))
+				{//other jid has the same server
+					return (jidStatus1.getJID().toStringNoResource().compareTo(jidStatus.getJID().toString()));
+				}
+				return -1;
+			}
+			//check if other jid has the server you are logged in to
+			if(jidStatus.getJID().getDomain().equals(nu.fw.jeti.backend.Connect.getMyJID().getDomain())) return 1;
+			//check servertypes
+			if(jidStatus1.getType().equals(jidStatus.getType()))
+			{
+				return (jidStatus1.getJID().toStringNoResource().compareTo(jidStatus.getJID().toString()));
+			}
+			if (getTypeRank(jidStatus1.getType()) < getTypeRank(jidStatus.getType())) return -1;
+			return 1;
+		}
+		if(jidStatus1.getShow() < jidStatus.getShow()) return -1;
+		return 1;
+	}
+
+//	private int getShowRank(String show)
+//	{
+//		if(show.equals("chat")) return 0;
+//		if(show.equals("available")) return 1;
+//		if(show.equals("away")) return 2;
+//		if(show.equals("dnd")) return 3;
+//		if(show.equals("xa")) return 4;
+//		return 5;
+//	}
+
+	private int getTypeRank(String type)
+	{
+		if(type.equals("jabber")) return 0;
+		if(type.equals("msn")) return 1;
+		if(type.equals("icq")) return 2;
+		return 3;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/Server.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/Server.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/Server.java	(revision 3952)
@@ -0,0 +1,470 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.backend.roster;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.swing.SwingUtilities;
+
+import nu.fw.jeti.backend.Discovery;
+import nu.fw.jeti.events.*;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.DiscoveryInfo;
+import nu.fw.jeti.jabber.elements.DiscoveryItem;
+import nu.fw.jeti.jabber.elements.Presence;
+import nu.fw.jeti.util.I18N;
+
+
+/**
+ * Class that controls the transports/sever from the roster.
+ * when a new transport is added this class wil lookup information
+ * about the transport (with Browse) and will give every item in the roster
+ * registered with that transport the right type.
+ * Changes in the server roster are broadcasted using the ServerListener
+ * @see ServerListener
+ * @see Discovery
+ * @see Roster
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+//primarys wel/niet?? what about resources?
+public class Server implements DiscoveryListener
+{
+	private JIDStatusTree jidTree = new JIDStatusTree();//serverrostertree
+	private Backend backend;
+	private boolean fireReady = false;//is the server roster displayed?
+	private Map jidStatussen;//reference to rosters jidstatus cache
+	private Map availableTransports;
+
+	public Server(Backend backend)
+	{
+		this.backend = backend;
+		//backend.addListener(DiscoveryListener.class,this);
+		availableTransports = new HashMap(5);
+	}
+
+	public void addJIDStatussen(Map jidStatussen)
+	{//adds jidstatussen from roster
+		this.jidStatussen = jidStatussen;
+		//System.out.println("jidstatussen added");
+	}
+
+	public void clear()
+	{//reset server
+		fireReady = false;
+		availableTransports.clear();
+		if(jidTree.existGroup(I18N.gettext("main.main.roster.Servers")))
+		{
+			jidTree.removeGroup(jidTree.getGroup(I18N.gettext("main.main.roster.Servers")));
+		}
+	}
+
+	public void fire()
+	{//fire after complete server reset
+		for(Iterator j = backend.getListeners(ServerListener.class);j.hasNext();)
+		{
+			((RosterListener)j.next()).rosterReplaced(jidTree);
+		}
+		fireReady = true;
+	}
+
+
+	public void addServerNoFire(JIDStatus jidStatus)
+	{
+		JIDStatusGroup jidGroup = jidTree.getGroup(I18N.gettext("main.main.roster.Servers"));
+		PrimaryJIDStatus primary = new PrimaryJIDStatus(jidStatus.getNick(),jidStatus);
+		jidGroup.addPrimaryJIDStatus(primary);
+		//primary.addJIDStatus(jidStatus);
+		//backend.browse(JID.jidFromString(jidStatus.getJID().toStringNoResource()) ,this);
+		backend.getInfo(JID.jidFromString(jidStatus.getJID().toStringNoResource()) ,this);
+	}
+
+	public void addServer(String nick, JIDStatus jidStatus)
+	{
+		addServerNoBrowse(nick,jidStatus);
+		//backend.browse(JID.jidFromString(jidStatus.getJID().toStringNoResource()),this);
+		backend.getInfo(JID.jidFromString(jidStatus.getJID().toStringNoResource()),this);
+	}
+
+	private void addServerNoBrowse(final String nick,final JIDStatus jidStatus)
+	{//when nick changes after browse
+		Runnable updateAComponent = new Runnable() {
+			public void run()
+			{
+				String group = I18N.gettext("main.main.roster.Servers");
+				JIDStatusGroup jidGroup;
+				if(!jidTree.existGroup(group))
+				{
+					jidGroup = jidTree.getGroup(group);
+					int index = jidTree.indexOfGroup(jidGroup);
+					for(Iterator j = backend.getListeners(ServerListener.class);j.hasNext();)
+					{//fire group make
+						((RosterListener)j.next()).groupAdded(jidGroup,index);
+					}
+				}
+				else jidGroup = jidTree.getGroup(group);
+				PrimaryJIDStatus primary = jidGroup.searchPrimaryJIDStatus(nick);
+				//if(primary == null)
+				{//server no primarys
+					primary = new PrimaryJIDStatus(nick,jidStatus);
+					jidGroup.addPrimaryJIDStatus(primary);
+					//primary.addJIDStatus(jidStatus);
+					//jidGroup.sort();
+					int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+					for(Iterator j = backend.getListeners(ServerListener.class);j.hasNext();)
+					{//fire add
+						((RosterListener)j.next()).primaryAdded(jidGroup,primary,index);
+					}
+				}
+			}
+		};
+		if(SwingUtilities.isEventDispatchThread())
+		{
+			updateAComponent.run();
+		}
+		else
+		{
+			try
+			{
+				SwingUtilities.invokeAndWait(updateAComponent);
+			} catch (InterruptedException e)
+			{
+				e.printStackTrace();
+			} catch (InvocationTargetException e)
+			{
+				e.printStackTrace();
+			}
+		}
+	}
+
+	public void removeServer(final String nick,final JIDStatus jidStatus)
+	{//als primary gebruik veranderen als in roster
+		Runnable updateAComponent = new Runnable() {
+			public void run()
+			{
+				JIDStatusGroup jidGroup = jidTree.getGroup(I18N.gettext("main.main.roster.Servers"));
+				PrimaryJIDStatus primary = jidGroup.searchPrimaryJIDStatus(nick);
+				if(primary.removeJIDStatus(jidStatus)) //primary onzin kan weg
+				{
+					//if(primary.isEmpty())
+					{
+						int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+						jidGroup.removePrimaryJIDStatus(primary);
+						for(Iterator j = backend.getListeners(ServerListener.class);j.hasNext();)
+						{//fire delete
+							((RosterListener)j.next()).primaryDeleted(jidGroup,primary,index);
+						}
+						//fire remove
+						if(jidGroup.size() < 1)
+						{//fire group remove
+							int index2 = jidTree.indexOfGroup(jidGroup);
+							jidTree.removeGroup(jidGroup);
+							for(Iterator j = backend.getListeners(ServerListener.class);j.hasNext();)
+							{//fire group delete
+								((RosterListener)j.next()).groupDeleted(jidGroup,index2);
+							}
+						}
+					}
+	//				else
+	//				{//fire update
+	//					int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+	//					for(Iterator j = backend.getListeners(ServerListener.class);j.hasNext();)
+	//					{
+	//						((RosterListener)j.next()).primaryUpdated(jidGroup,primary,index);
+	//					}
+	//				}
+				}
+			}
+		};
+		if(SwingUtilities.isEventDispatchThread())
+		{
+			updateAComponent.run();
+		}
+		else
+		{
+			try
+			{
+				SwingUtilities.invokeAndWait(updateAComponent);
+			} catch (InterruptedException e)
+			{
+				e.printStackTrace();
+			} catch (InvocationTargetException e)
+			{
+				e.printStackTrace();
+			}
+		}
+	}
+
+	private void changeNickServer(String nick, NormalJIDStatus jidStatus)
+	{//changes nick from server  nick + oldjidstatus
+		String group = I18N.gettext("main.main.roster.Servers");
+		//System.out.println("update");
+		JIDStatusGroup jidGroup = jidTree.getGroup(group);
+		PrimaryJIDStatus primary = jidGroup.searchPrimaryJIDStatus(jidStatus.getNick());
+		if(primary.removeJIDStatus(jidStatus)) //primary onzin kan weg
+		{
+			//if(primary.isEmpty())
+			{
+				//int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+				jidGroup.removePrimaryJIDStatus(primary);
+			}
+		}
+		jidStatus.setNick(nick);
+//		if(!jidTree.existGroup(group))
+//		{
+//			jidGroup = jidTree.getGroup(group);
+//			//int index = jidTree.indexOfGroup(jidGroup);
+//		}
+//		else 
+		jidGroup = jidTree.getGroup(group);
+		//JIDPrimaryStatus primary = jidGroup.searchPrimaryJIDStatus(nick);
+		//if(primary == null)
+		{//server no primarys
+			primary = new PrimaryJIDStatus(nick,jidStatus);
+			jidGroup.addPrimaryJIDStatus(primary);
+			//primary.addJIDStatus(jidStatus);
+			//jidGroup.sort();
+			//int index = jidGroup.indexOfJIDPrimaryStatus(primary);
+		}
+	}
+	
+	//------------------------------Presence event------------------------------------\\
+
+	public void showChange(JIDStatus jidstatus)
+	{//als primary gebruik veranderen als in roster
+		//System.out.println("server show change");
+		//for(Iterator i = jidstatus.getGroups();i.hasNext();)
+		{//new jidstatus per group?
+			final JIDStatusGroup jidGroup = jidTree.getGroup(I18N.gettext("main.main.roster.Servers"));
+			final PrimaryJIDStatus primary = jidGroup.searchPrimaryJIDStatus(jidstatus.getNick());
+			if(primary!=null)
+			{//no status change after remove
+				if(!jidstatus.isOnline() && !((NormalJIDStatus)jidstatus).isForcedOffline())new AutoReconnect(backend,jidstatus.getJID());
+				Runnable updateAComponent = new Runnable() {
+					public void run()
+					{
+						int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+						for(Iterator j = backend.getListeners(ServerListener.class);j.hasNext();)
+						{
+							((RosterListener)j.next()).primaryUpdated(jidGroup,primary,index);
+						}
+					}
+				};
+				try
+				{
+					SwingUtilities.invokeAndWait(updateAComponent);
+				} catch (InterruptedException e)
+				{
+					e.printStackTrace();
+				} catch (InvocationTargetException e)
+				{
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+
+	public void discoveryItemResult(JID jid, DiscoveryItem browseItem){}
+	
+	public void discoveryInfoResult(JID jid, DiscoveryInfo browseItem)
+	{
+		//System.out.println("browse" + jid + browseItem);
+		NormalJIDStatus jidStatus = (NormalJIDStatus) backend.getJIDStatus(jid);
+		if(jidStatus == null)
+		{
+			System.out.println(jid + " is unknown jidstatus");
+			return;
+		}
+		try
+		{//if transport has no nick/ jid as nick set nick to name of browseitem
+			if(browseItem.getName() != null)
+			{
+				JID nick = JID.checkedJIDFromString(jidStatus.getNick());
+				if(nick == null || nick.equals(jid))
+				{
+					if(fireReady)
+					{
+						removeServer(jidStatus.getNick(),jidStatus);
+						jidStatus.setNick(browseItem.getName());
+						addServerNoBrowse(browseItem.getName(), jidStatus);
+					}
+					else changeNickServer(browseItem.getName(), jidStatus);
+				}
+			}
+		}
+		catch (Exception e)
+		{
+			//nick already correct}
+		}
+		String type = browseItem.getType();
+		if(type == null) type = "unknown";
+		availableTransports.put(type, jidStatus);
+		if(jidStatus.getType().equals("jabber") && !type.equals("jabber"))
+		{//add type to jidstatusen in roster
+			updateTypes(jidStatus, type);
+		}
+		//jidStatus.setNamespaces(browseItem.getNamespaces());
+	}
+
+	
+	
+	/*
+	public void browseResult(IQBrowse browseItem)
+	{
+		//System.out.println("browse");
+		NormalJIDStatus jidStatus = (NormalJIDStatus) Backend.getJIDStatus(browseItem.getJID());
+		if(jidStatus == null)
+		{
+			System.out.println(browseItem.getJID() + " is unknown jidstatus");
+			return;
+		}
+		try
+		{//if transport has no nick/ jid for nick set nick to name of browseitem
+			if(browseItem.getName() != null)
+			{
+				JID nick = JID.checkedJIDFromString(jidStatus.getNick());
+				if(nick == null || nick.equals(browseItem.getJID()))
+				{
+					if(fireReady)
+					{
+						removeServer(jidStatus.getNick(),jidStatus);
+						jidStatus.setNick(browseItem.getName());
+						addServerNoBrowse(browseItem.getName(), jidStatus);
+					}
+					else changeNickServer(browseItem.getName(), jidStatus);
+				}
+			}
+		}
+		catch (Exception e){}
+		String type = browseItem.getType();
+		availableTransports.put(type, jidStatus);
+		if(type == null) type = "unknown";
+		if(jidStatus.getType().equals("jabber") && !type.equals("jabber"))
+		{//add type to jidstatusen in roster
+			updateTypes(jidStatus, type);
+		}
+		jidStatus.setNamespaces(browseItem.getNamespaces());
+	}
+	*/
+
+	private void updateTypes(NormalJIDStatus jidStatus, String type)
+	{
+	   jidStatus.setType(type);
+	   //System.out.println("type changed");
+	   String server = jidStatus.getJID().getDomain();
+	   //System.out.println(server);
+	   for(Iterator i = jidStatussen.values().iterator();i.hasNext();)
+	   {
+		   NormalJIDStatus ji =(NormalJIDStatus) i.next();
+		   //System.out.println(ji);
+		   if(ji.getJID().getDomain().equals(server))
+		   {//server van j == changed server
+			   ji.setType(type);
+			   /*
+			   for(Iterator i2 = ji.getGroups();i2.hasNext();)
+			  {//new jidstatus per group?
+				  JIDGroup2 jidGroup = jidTree.getGroup((String)i2.next());
+				  JIDPrimaryStatus primary = jidGroup.searchPrimaryJIDStatus(ji.getNick());
+				  int index = jidGroup.indexOfJIDPrimaryStatus(primary);
+				  for(Iterator j = backend.getListeners(RosterModelListener.class);j.hasNext();)
+				  {
+					  ((RosterModelListener)j.next()).primaryUpdated(jidGroup,primary,index);
+				  }
+			}
+			   */
+		   }
+	   }
+    }
+	
+	public Map getAvailableTransports()
+	{
+		return availableTransports;
+	}
+}	
+	
+	class AutoReconnect extends Thread implements PresenceListener , StatusChangeListener
+	{
+		private Backend backend;
+		private volatile boolean connect = true;
+		private JID jid;
+		
+		
+		public AutoReconnect(Backend backend,JID jid)
+		{
+			this.backend = backend;
+			this.jid = jid;
+			backend.addListener(PresenceListener.class, this);
+			backend.addListener(StatusChangeListener.class, this);
+			start();
+		}
+		
+		public void ownPresenceChanged(int show, String status)
+		{}
+		
+		public void connectionChanged(boolean online)
+		{//stop reconnecting when gone offline
+			if(!online)exit();
+		}
+		
+		public void exit()
+		{
+			connect = false;
+			interrupt();
+		}
+		
+		public void presenceChanged(Presence presence)
+		{
+			if(presence.getShow()!=Presence.UNAVAILABLE)exit();
+		}
+				
+		public void run()
+		{
+			boolean connected=false;
+			while(connect)
+			{
+				backend.send(new Presence(jid,backend.getStatus(),null));
+				if(connected) break;
+				try
+				{
+					Thread.sleep(600000);
+				}
+				catch (InterruptedException e){}
+			}
+			backend.removeListener(PresenceListener.class, this);
+			backend.removeListener(StatusChangeListener.class, this);
+		}
+	}
+	
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/JIDStatusGroup.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/JIDStatusGroup.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/JIDStatusGroup.java	(revision 3952)
@@ -0,0 +1,180 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.backend.roster;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import nu.fw.jeti.jabber.JIDStatus;
+
+/**
+ * Title:        im
+ * Description:
+ * Copyright:    Copyright (c) 2001
+ * Company:
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class JIDStatusGroup implements Comparable
+{
+//	synchronized because used in event thread & jabberinput thread
+	private String name;
+	private List members;
+	private int onlines;
+
+	public JIDStatusGroup(String name)
+	{
+		this.name = name;
+		members = new ArrayList(16);
+	}
+
+	public synchronized int size()
+	{
+		return members.size();
+	}
+	
+//	public boolean isOffline()
+//	{
+//		for(Iterator i = members.iterator();i.hasNext();)
+//		{
+//			if(((JIDStatus)i.next()).isOnline()) return false;		
+//		}
+//		return true;
+//	}
+	
+	public synchronized void addOnline() {onlines++;}
+	
+	public synchronized void removeOnline() {onlines--;}
+	
+	public synchronized int getOnlines() {return onlines;}
+			
+	public Iterator iterator()
+	{
+		return new ArrayList(members).iterator();
+	}
+
+	/*
+	public void addJIDStatus(JIDStatus2 jid)
+	{
+		members.add(jid);
+		Collections.sort(members);
+	}
+	*/
+	
+	public synchronized void addPrimaryJIDStatus(PrimaryJIDStatus jid)
+	{
+		members.add(jid);
+		Collections.sort(members);
+	}
+/*
+	public void sort()
+	{
+	    Collections.sort(members);
+	}
+*/
+	public synchronized Object getPrimaryJIDStatus(int index)
+	{
+		return members.get(index);
+	}
+
+	/*
+	public  JIDStatus searchJID(JID jid)
+	{
+		for(int tel =0; tel<members.size();tel++)
+		{
+			if (((JIDStatus)members.get(tel)).getJID().equals(jid)) return (JIDStatus)members.get(tel);
+		}
+		return null;
+	}
+	*/
+
+	public synchronized PrimaryJIDStatus searchPrimaryJIDStatus(String nickname)
+	{
+		for(int tel =0; tel<members.size();tel++)
+		{
+			PrimaryJIDStatus temp = (PrimaryJIDStatus)members.get(tel);
+			if (temp.getNick().equals(nickname)) return temp;
+		}
+		return null;
+	}
+
+	/*
+	public boolean remove(JID jid)
+	{
+		for(int tel =0; tel<members.size();tel++)
+		{
+			if (((JIDStatus)members.get(tel)).getJID().equals(jid))
+			{
+			     members.remove(tel);
+				 return true;
+			}
+		}
+		return false;
+	}
+	*/
+
+	public synchronized void removePrimaryJIDStatus(PrimaryJIDStatus primary)
+	{
+		members.remove(primary);
+	}
+
+	public synchronized int indexOfPrimaryJIDStatus(Object jidPrimaryStatus)
+	{
+	    return members.indexOf(jidPrimaryStatus);
+	}
+
+	public String getName()
+	{
+		return name;
+	}
+
+	public String toString()
+	{
+		return name;
+	}
+
+	public boolean equals(Object object)
+	{//als name is gelijk object is gelijk
+		if(!(object instanceof JIDStatusGroup)) return false;
+		return name.equals(((JIDStatusGroup)object).name);
+	}
+	
+	public int hashCode()
+	{
+		return name.hashCode();
+	}
+
+	public int compareTo(Object o)
+	{
+		return name.compareTo(((JIDStatusGroup)o).name);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/JIDStatusTree.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/JIDStatusTree.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/JIDStatusTree.java	(revision 3952)
@@ -0,0 +1,83 @@
+package nu.fw.jeti.backend.roster;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Title:        im
+ * Description:
+ * Copyright:    Copyright (c) 2001
+ * Company:
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class JIDStatusTree
+{
+	//synchronized because used in event thread & jabberinput thread
+	
+	private List root;
+
+	public JIDStatusTree()
+	{
+		root = new ArrayList(8);
+	}
+
+
+	//get group of maak als hij nog niet bestaat
+	/**
+	 * Searches group and returns the found group 
+	 * or makes a new group if group does not exist
+	 * @param group  Group to get
+	 * @return JIDStatusGroup 
+	 */
+	public synchronized JIDStatusGroup getGroup(String group)
+	{
+		int index = root.indexOf(new JIDStatusGroup(group));
+		if(index == -1)
+		{
+			JIDStatusGroup jidGroup = new JIDStatusGroup(group);
+			root.add(jidGroup);
+			Collections.sort(root);
+			return jidGroup;
+		}
+		return (JIDStatusGroup)root.get(index);
+	}
+
+	
+	public synchronized boolean existGroup(String group)
+	{
+		return (root.indexOf(new JIDStatusGroup(group))!=-1) ;
+	}
+
+	public synchronized void removeGroup(JIDStatusGroup jidGroup)
+	{
+		root.remove(jidGroup);
+	}
+
+	public synchronized String[] getGroups()
+	{
+		String[] groups = new String[root.size()];
+		for(int tel =0; tel<root.size();tel++)
+		{
+			groups[tel] =((JIDStatusGroup)root.get(tel)).getName();
+		}
+		return groups;
+	}
+
+	public synchronized int indexOfGroup(Object group)
+	{
+	    return root.indexOf(group);
+	}
+
+	public synchronized int getSize(){return root.size();}
+
+	public synchronized Object get(int index){return root.get(index);}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/PrimaryJIDStatus.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/PrimaryJIDStatus.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/PrimaryJIDStatus.java	(revision 3952)
@@ -0,0 +1,253 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ */
+
+package nu.fw.jeti.backend.roster;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import nu.fw.jeti.jabber.JIDStatus;
+
+/**
+ * <p>Copyright: Copyright (c) 2001</p>
+ * @author E.S. de Boer
+ */
+
+public class PrimaryJIDStatus implements Comparable //implements JIDStatus
+{//contains secondary jidstatussen (resource + same nick)
+	private JIDStatus primaryJIDStatus;
+	private List jidStatussen;
+	private String nick;
+	private boolean online=false;
+
+
+    public PrimaryJIDStatus(String nick,JIDStatus primaryJIDStatus)
+    {
+		this.nick = nick;
+		this.primaryJIDStatus = primaryJIDStatus;
+    }
+
+//	public boolean isEmpty()
+//	{
+//		return isEmpty;
+//	}
+
+	public boolean hasMultiple()
+	{
+		return jidStatussen != null;
+	}
+
+	public boolean isAJIDstatusOffline()
+	{
+		if(!primaryJIDStatus.isOnline()) return true;
+		if(jidStatussen == null) return false;
+		for(Iterator i = jidStatussen.iterator();i.hasNext();)
+		{
+			if(!((JIDStatus)i.next()).isOnline()) return true;
+		}
+		return false;
+	}
+	
+	public boolean multipleJIDstatusOnline()
+	{
+		if(!primaryJIDStatus.isOnline()) return false;
+		if(jidStatussen == null) return false;
+		for(Iterator i = jidStatussen.iterator();i.hasNext();)
+		{
+			if(((JIDStatus)i.next()).isOnline()) return true;
+		}
+		return false;
+	}
+
+	public void addJIDStatus(JIDStatus jidStatus)
+	{
+//		if(primaryJIDStatus == null)
+//		{
+//			primaryJIDStatus = jidStatus;
+//			//return true;
+//		}
+//		else
+//		{
+			if(jidStatussen == null) jidStatussen = new ArrayList(8);
+			jidStatussen.add(jidStatus);
+		    Collections.sort(jidStatussen);
+			//return false;
+		//}
+	}
+
+	public boolean removeJIDStatus(JIDStatus jidStatus)
+	{//returns true if primaryJIDStatus is now empty
+	    if(primaryJIDStatus == jidStatus)
+		{
+			if(jidStatussen == null)
+			{
+				//primaryJIDStatus = null;
+				return true;
+			}
+			else if(jidStatussen.size() == 1)
+			{
+			    primaryJIDStatus = (JIDStatus)jidStatussen.get(0);
+				jidStatussen =null;
+			}
+			else
+			{//jidstatussen sorted so first will be new primary
+				primaryJIDStatus = (JIDStatus)jidStatussen.remove(0);
+			}
+			return false;
+		}
+		jidStatussen.remove(jidStatus);
+		if (jidStatussen.size() == 0) jidStatussen = null;
+		return false;
+	}
+
+	public void updatePresence(SecondaryJIDStatus jidStatus)
+	{
+		if(jidStatussen != null) Collections.sort(jidStatussen);
+		if(jidStatus == primaryJIDStatus)
+		{//check of secondery jids better then primary
+			if(jidStatussen == null) return;
+
+			//jidstatussen sorted so only first checked
+			checkVolgorde((JIDStatus) getJIDStatus(0));
+			/*
+			for(Iterator i = jidStatussen.iterator();i.hasNext();)
+			{
+				JIDStatus2 temp = (JIDStatus2)i.next();
+				checkVolgorde(jidGroup, temp, backend);
+			}
+			*/
+		}
+	  	else checkVolgorde(jidStatus);
+	}
+	
+	public void updateOnline()
+	{
+		online = primaryJIDStatus.isOnline();
+	}
+
+    private void checkVolgorde(JIDStatus jidStatus)
+    {
+        if(primaryJIDStatus.compareTo(jidStatus) > 0)
+        {
+            int index = jidStatussen.indexOf(jidStatus);
+            jidStatussen.set(index,primaryJIDStatus);
+		    primaryJIDStatus = jidStatus;
+			Collections.sort(jidStatussen);//sort new list
+        }
+    }
+
+	public JIDStatus getJIDPrimaryStatus()
+	{
+//		if(primaryJIDStatus == null)
+//		{//jidstatus is removed from group before from gui so use a dummy until removed from gui
+//			//System.out.println("Primary jids null");
+//		 	return new NormalJIDStatus(new JID("test","test") ,nick); 
+//		}
+		return primaryJIDStatus;
+	}
+	
+	public Object getJIDStatus(int index)
+	{
+		return jidStatussen.get(index);
+	}
+
+	public int indexOfJIDStatus(Object jidStatus)
+	{
+		return jidStatussen.indexOf(jidStatus);
+	}
+
+	public int size()
+	{
+		if(jidStatussen == null) return 0;
+		return jidStatussen.size();
+	}
+
+	public Iterator getOtherJidStatussen()
+	{
+		return new ArrayList(jidStatussen).iterator();
+	}
+
+	public String toString()
+	{
+	    //return nick;//
+	    return primaryJIDStatus.toString();
+	}
+
+	public int compareTo(Object o)
+	{//compare jid
+		//if(nickname==null) return -1;
+	    return nick.compareTo(((PrimaryJIDStatus)o).nick);
+	}
+
+	//-------------------JIStatus implementation--------------------\\
+
+//	public JID getJID(){return primaryJIDStatus.getJID();}
+//
+//	public JID getCompleteJID(){return primaryJIDStatus.getCompleteJID();}
+//
+	public boolean isOnline()
+	{
+		return online;
+	}
+//
+//	public int getShow()
+//	{
+//		return primaryJIDStatus.getShow();
+//	}
+//
+//	public String getStatus()
+//	{
+//		return primaryJIDStatus.getStatus();
+//	}
+//
+	public String getNick()
+	{
+		return nick;
+	}
+//
+//	public String getType()
+//	{
+//		return primaryJIDStatus.getType();
+//	}
+//
+//	public String getSubscription(){return primaryJIDStatus.getSubscription();}
+//
+//	public String getWaiting()
+//	{
+//		return primaryJIDStatus.getWaiting();
+//	}
+//
+//	public StringArray getGroupsCopy(){return primaryJIDStatus.getGroupsCopy();}
+//
+//	public int groupCount(){return primaryJIDStatus.groupCount();}
+//
+//	public boolean isGroupPresent(String group){return primaryJIDStatus.isGroupPresent(group);}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/Roster.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/Roster.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/backend/roster/Roster.java	(revision 3952)
@@ -0,0 +1,700 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at jeti@jabberstudio.org 
+ *  created 2001
+ */
+
+package nu.fw.jeti.backend.roster;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.SwingUtilities;
+
+import nu.fw.jeti.events.CompleteRosterListener;
+import nu.fw.jeti.events.PresenceListener;
+import nu.fw.jeti.events.RosterListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.IQXRoster;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.Presence;
+import nu.fw.jeti.jabber.elements.RosterItem;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+/**
+ * Class that controls the Roster. JIDS whithout a username
+ * are assumed to be a transport/server and are transfered to Server.
+ * The items in the roster are sorted stored.
+ * Changes in the roster are broadcasted using the RosterListener
+ * @see Server
+ * @see RosterListener
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class Roster implements CompleteRosterListener, PresenceListener
+{
+	private JIDStatusTree jidTree;
+	private static Map jidStatussen = new HashMap(32); //default 20 different?;//fast access to tree
+	private Backend backend;
+	private boolean first; //server hack
+	private Server server;
+	
+	public Roster(Backend backend, Server server)
+	{
+		this.server = server;
+		this.backend = backend;
+		backend.addListener(CompleteRosterListener.class, this);
+		backend.addListener(PresenceListener.class, this);
+	}
+
+	//-------------------roster events--------------------------------------\\
+	public void rosterReceived(final InfoQuery infoquery, final IQXRoster roster)
+	{
+		Runnable updateAComponent = new Runnable() {
+			public void run()
+			{
+				if (infoquery.getType().equals("result"))
+				{
+					//complete new roster
+					completeRoster(roster);
+				}
+				else if (infoquery.getType().equals("set"))
+				{
+					//add result
+					for (Iterator i = roster.getItems(); i.hasNext();)
+					{
+						RosterItem item = (RosterItem) i.next();
+						JID jid = item.getJID();
+						String nick = item.getName();
+						if (nick == null) nick = jid.toString();
+						NormalJIDStatus oldJIDStatus = (NormalJIDStatus) jidStatussen.get(jid);
+						if ("remove".equals(item.getSubscription()))
+						{
+							removeFromRoster(oldJIDStatus, item, jid);
+						}
+						else if (oldJIDStatus == null)
+						{
+							//group must be present (no unfiled) except when server
+							if (item.getGroups() != null || jid.getUser() == null) addToRoster(nick, item, jid);
+						}
+						else
+						{
+							changeItem(oldJIDStatus, nick, item, jid);
+						}
+					}
+				}
+			}
+		};
+		try
+		{
+			SwingUtilities.invokeAndWait(updateAComponent);
+		} catch (InterruptedException e)
+		{
+			e.printStackTrace();
+		} catch (InvocationTargetException e)
+		{
+			e.printStackTrace();
+		}
+	}
+
+	private void completeRoster(IQXRoster roster)
+	{
+		//add complete roster
+		//System.out.println("result");
+		jidStatussen = new HashMap(32); //default 20 different?
+		server.addJIDStatussen(jidStatussen);
+		jidTree = new JIDStatusTree();
+		server.clear();
+		for (Iterator i = roster.getItems(); i.hasNext();)
+		{
+			RosterItem item = (RosterItem) i.next();
+			JIDStatus jidStatus = new NormalJIDStatus(item);
+			jidStatussen.put(item.getJID(), jidStatus);
+			if (item.getJID().getUser() == null)
+			{
+				//server /transport
+				server.addServerNoFire(jidStatus);
+			}
+			else
+			{
+				if (item.getGroups() == null)
+					addJIDStatus(I18N.gettext("main.main.roster.Unfiled"), jidStatus);
+				else
+				{
+					for (Iterator j = item.getGroups().iterator(); j.hasNext();)
+					{
+						//new jidstatus per group?
+						addJIDStatus((String) j.next(), jidStatus);
+					}
+				}
+			}
+		}
+		backend.rosterLoaded();
+		server.fire();
+		for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+		{
+			((RosterListener) j.next()).rosterReplaced(jidTree);
+		}
+	}
+
+	private void removeFromRoster(NormalJIDStatus oldJIDStatus, RosterItem item, JID jid)
+	{
+		if (oldJIDStatus == null)
+			return; //jidstatus unknown
+		String oldNick = oldJIDStatus.getNick();
+		if (item.getJID().getUser() == null)
+		{
+			//server /transport
+			server.removeServer(oldNick, oldJIDStatus);
+		}
+		else
+		{
+			if (oldJIDStatus.groupCount() < 1)
+			{
+				//unknown group
+				removeJIDStatus(I18N.gettext("main.main.roster.Unfiled"), oldNick, oldJIDStatus);
+			}
+			else
+			{
+				//known groups
+				for (Iterator j = oldJIDStatus.getGroups(); j.hasNext();)
+				{
+					//remove
+					removeJIDStatus((String) j.next(), oldNick, oldJIDStatus);
+				}
+			}
+		}
+		jidStatussen.remove(jid); //remove from cache
+	}
+
+	private void addToRoster(String nick, RosterItem item, JID jid)
+	{
+		if (!first && (item.getGroups() == null || item.getName() == null))
+		{
+			//server sends first a rosterset without nickname and groups
+			//we don't need that because it generates a new group or new nicknames
+			first = true; //first time sended
+			return;
+		}
+		first = false;
+		//System.out.println("addnew");
+		NormalJIDStatus jidStatus = new NormalJIDStatus(item);
+		jidStatussen.put(jid, jidStatus);
+		if (jid.getUser() == null)
+		{
+			//server /transport
+			server.addServer(nick, jidStatus);
+		}
+		else if (item.getGroups() == null)
+		{
+			//no groups only when complete roster
+			addJIDStatusFire(I18N.gettext("main.main.roster.Unfiled"), nick, jidStatus);
+		}
+		else
+		{
+			for (Iterator j = item.getGroups().iterator(); j.hasNext();)
+			{
+				//new jidstatus per group?
+				addJIDStatusFire((String) j.next(), nick, jidStatus);
+			}
+		}
+	}
+
+	private void changeItem(NormalJIDStatus oldJIDStatus, String nick, RosterItem item, JID jid)
+	{
+		//group nickname or other change to rosteritem
+		if (!nick.equals(oldJIDStatus.getNick()))
+		{
+			//nick changes
+			nickChanges(oldJIDStatus, nick, item, jid);
+		}
+		if (jid.getUser() == null) //no else because change nick & change groups could be at same time
+		{
+			//server doesn't care about groups
+			oldJIDStatus.update(item);
+		}
+		else if (item.getGroups() == null)
+		{
+			//heeft 0 groups
+			if (oldJIDStatus.groupCount() < 1)
+				oldJIDStatus.update(item); //had 0 heeft 0 overbodig?
+			else
+			{
+				if (!oldJIDStatus.isGroupPresent(I18N.gettext("main.main.roster.Unfiled")))
+				{
+					//heeft 0 had meer
+					for (Iterator j = oldJIDStatus.getGroups(); j.hasNext();)
+					{
+						//remove
+						removeJIDStatus((String) j.next(), nick, oldJIDStatus);
+					}
+					addJIDStatusFire(I18N.gettext("main.main.roster.Unfiled"), nick, oldJIDStatus); //add to unfiled
+					oldJIDStatus.update(item);
+				}
+			}
+		}
+		else
+		{
+			changeGroups(oldJIDStatus, nick, item);
+		}
+	}
+
+	private void nickChanges(NormalJIDStatus oldJIDStatus, String nick, RosterItem item, JID jid)
+	{
+		//nickname changed
+		String oldNick = oldJIDStatus.getNick();
+		if (jid.getUser() == null)
+		{
+			//server /transport
+			server.removeServer(oldNick, oldJIDStatus);
+			oldJIDStatus.update(item);
+			server.addServer(nick, oldJIDStatus);
+		}
+		else
+		{
+			//nickname is different so new primarys
+			//System.out.println("remove old nicknames");
+			//remove old
+			if (oldJIDStatus.groupCount() < 1)
+			{
+				//unknown group
+				removeJIDStatus(I18N.gettext("main.main.roster.Unfiled"), oldNick, oldJIDStatus);
+			}
+			else
+			{
+				//known groups
+				for (Iterator j = oldJIDStatus.getGroups(); j.hasNext();)
+				{
+					//remove
+					removeJIDStatus((String) j.next(), oldNick, oldJIDStatus);
+				}
+			}
+			oldJIDStatus.update(item);
+			//addnew
+			if (item.getGroups() == null)
+			{
+				addJIDStatusFire(I18N.gettext("main.main.roster.Unfiled"), nick, oldJIDStatus);
+			}
+			else
+			{
+				for (Iterator j = item.getGroups().iterator(); j.hasNext();)
+				{
+					//new jidstatus per group?
+					addJIDStatusFire((String) j.next(), nick, oldJIDStatus);
+				}
+			}
+		}
+		
+	}
+
+	private void changeGroups(NormalJIDStatus oldJIDStatus, String nick, RosterItem item)
+	{
+		/*
+		if(oldJIDStatus.groupCount() < 1)//overbodig??
+		{//had 0 heeft meer
+			removeJIDStatus("Unfiled",nick,oldJIDStatus);
+			for(Iterator j = item.getGroups().iterator();j.hasNext();)
+			{//new jidstatus per group?
+				addJIDStatusFire((String) j.next(),nick,oldJIDStatus);
+			}
+			oldJIDStatus.update(item);
+		}
+		else
+		*/
+		//{//heeft x had y
+		List oldGroups = oldJIDStatus.getGroupsCopy();
+		for (Iterator j = item.getGroups().iterator(); j.hasNext();)
+		{
+			//new jidstatus per group?
+			String newGroup = (String) j.next();
+			if (oldGroups.contains(newGroup))
+				oldGroups.remove(newGroup);
+			else
+				addJIDStatusFire(newGroup, nick, oldJIDStatus);
+		}
+		for (Iterator j = oldGroups.iterator(); j.hasNext();)
+		{
+			//remove old groups
+			removeJIDStatus((String) j.next(), nick, oldJIDStatus);
+		}
+		oldJIDStatus.update(item);
+		//}
+	}
+
+	//---------------------add //remove //change methods--------------------------------------
+	private void addJIDStatus(String group, JIDStatus jidStatus)
+	{
+		//add jidstatus for complete roster the roster will be completely refreshed so no fires per jidstatus
+		JIDStatusGroup jidGroup = jidTree.getGroup(group);
+		PrimaryJIDStatus primary = jidGroup.searchPrimaryJIDStatus(jidStatus.getNick());
+		if (primary == null)
+		{
+			primary = new PrimaryJIDStatus(jidStatus.getNick(),jidStatus);
+			jidGroup.addPrimaryJIDStatus(primary);
+		}
+		else primary.addJIDStatus(jidStatus);
+		//jidGroup.sort();
+	}
+
+	private void addJIDStatusFire(String group, String nick, NormalJIDStatus normalJIDStatus)
+	{
+	//	System.out.println("add");
+		JIDStatusGroup jidGroup;
+		if (!jidTree.existGroup(group))
+		{
+			jidGroup = jidTree.getGroup(group);
+			int index = jidTree.indexOfGroup(jidGroup);
+			for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+			{
+				//fire group make
+				 ((RosterListener) j.next()).groupAdded(jidGroup, index);
+			}
+		}
+		else jidGroup = jidTree.getGroup(group);
+		PrimaryJIDStatus primary = jidGroup.searchPrimaryJIDStatus(nick);
+		for (Iterator i = normalJIDStatus.getSecondaryJIDStatussen(); i.hasNext();)
+		{//add secondary jidstatussen
+			SecondaryJIDStatus jidStatus = (SecondaryJIDStatus) i.next();
+			// bug if primary was offline but now becomes online
+			// then odd results so now do things slower but consistent 
+			//(add is rare so no problem)
+			primary = addJIDStatus(nick, jidGroup, primary, jidStatus);
+		}
+		primary = addJIDStatus(nick, jidGroup, primary, normalJIDStatus);
+		primary.updatePresence(normalJIDStatus);
+		boolean oldOnline = primary.isOnline(); 
+		primary.updateOnline();
+		if(!oldOnline && primary.isOnline())
+		{//add online
+			jidGroup.addOnline();
+			for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+			{
+				((RosterListener) j.next()).groupUpdated(jidGroup, jidTree.indexOfGroup(jidGroup));
+			}	
+		}
+				
+	//	{//fire update if changed (faster)	
+	//		int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+			for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+			{
+				((RosterListener) j.next()).jidStatussenUpdated(jidGroup,primary);
+				//primaryUpdated(jidGroup, primary,index);
+			}
+	//	}
+				
+		
+		/*
+		else if(primary.addJIDStatus(jidStatus))
+		{ //fire update
+			int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+			for(Iterator j = backend.getListeners(RosterListener.class);j.hasNext();)
+			{
+				((RosterListener)j.next()).primaryUpdated(jidGroup,primary,index);
+			}
+		}
+		else
+		{//primary doesn't change
+			int index = primary.indexOfJIDStatus(jidStatus);
+			for(Iterator j = backend.getListeners(RosterListener.class);j.hasNext();)
+			{
+				((RosterListener)j.next()).jidStatusAdded(jidGroup,primary,jidStatus,index);
+			}
+		}
+		*/
+		//primary.updatePresence(secondary);
+
+		/*
+		for(Iterator j = backend.getListeners(RosterListener.class);j.hasNext();)
+		{
+			((RosterListener)j.next()).jidStatussenUpdated(jidGroup,primary);
+		}
+		*/
+	}
+	private PrimaryJIDStatus addJIDStatus(String nick, JIDStatusGroup jidGroup, PrimaryJIDStatus primary, SecondaryJIDStatus jidStatus)
+	{
+		if (primary == null)
+		{
+			primary = new PrimaryJIDStatus(nick,jidStatus);
+			jidGroup.addPrimaryJIDStatus(primary);
+			//primary.addJIDStatus(jidStatus);
+			int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+			for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+			{//fire add primary
+				 ((RosterListener) j.next()).primaryAdded(jidGroup, primary, index);
+			}
+		}
+		else
+		{
+			primary.addJIDStatus(jidStatus);
+//			int index = primary.indexOfJIDStatus(jidStatus);
+//			for(Iterator j = backend.getListeners(RosterListener.class);j.hasNext();)
+//			{//fire add jidstatus
+//				((RosterListener)j.next()).jidStatusAdded(jidGroup,primary,jidStatus,index);
+//				
+//			}
+		}
+//		primary.updatePresence(jidStatus);
+		return primary;
+	}
+
+	private void removeJIDStatus(String group, String nick, NormalJIDStatus jidStatus)
+	{
+		//System.out.println("remove");
+		JIDStatusGroup jidGroup = jidTree.getGroup(group);
+		PrimaryJIDStatus primary = jidGroup.searchPrimaryJIDStatus(nick);
+		boolean oldOnline = primary.isOnline();
+		for (Iterator i = jidStatus.getSecondaryJIDStatussen(); i.hasNext();)
+		{
+			SecondaryJIDStatus secondaryJIDStatus = (SecondaryJIDStatus) i.next();
+			if (removeJIDStatus2(secondaryJIDStatus, jidGroup, primary))
+			{
+				if(oldOnline)
+				{//gone offline
+					jidGroup.removeOnline();
+					for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+					{
+						((RosterListener) j.next()).groupUpdated(jidGroup, jidTree.indexOfGroup(jidGroup));
+					}
+				}
+				return;
+			}
+		}
+		boolean deleted = removeJIDStatus2(jidStatus, jidGroup, primary);
+		if(!deleted) primary.updateOnline();
+		if(((deleted && oldOnline) || (oldOnline && !primary.isOnline()))
+            && -1 != jidTree.indexOfGroup(jidGroup))
+		{//gone offline
+			jidGroup.removeOnline();
+			for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+			{
+				((RosterListener) j.next()).groupUpdated(jidGroup, jidTree.indexOfGroup(jidGroup));
+			}
+		}
+		//fire update
+		for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+		{
+			((RosterListener) j.next()).jidStatussenUpdated(jidGroup, primary);
+		}
+	}
+
+	private boolean removeJIDStatus2(JIDStatus jidStatus, JIDStatusGroup jidGroup, PrimaryJIDStatus primary)
+	{
+		//return true if primary deleted
+		//int jidStatusIndex =0;
+		//if(primary.hasMultiple()) jidStatusIndex = primary.indexOfJIDStatus(jidStatus);
+		if (primary.removeJIDStatus(jidStatus))
+		{
+			//if (primary.isEmpty())
+			{
+				int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+				jidGroup.removePrimaryJIDStatus(primary);
+				for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+				{
+					//fire delete
+					 ((RosterListener) j.next()).primaryDeleted(jidGroup, primary, index);
+				}
+				//fire remove
+				if (jidGroup.size() < 1)
+				{
+					//fire group remove
+					int index2 = jidTree.indexOfGroup(jidGroup);
+					jidTree.removeGroup(jidGroup);
+					for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+					{
+						//fire group delete
+						 ((RosterListener) j.next()).groupDeleted(jidGroup, index2);
+					}
+				}
+				return true;
+			}
+			//			else
+			//			{//fire update
+			//				for(Iterator j = backend.getListeners(RosterListener.class);j.hasNext();)
+			//				{
+			//					((RosterListener)j.next()).jidStatussenUpdated(jidGroup,primary);
+			//				}
+			//			}
+		}
+		//		else
+		//		{//primary doesn't change
+		//			for(Iterator j = backend.getListeners(RosterListener.class);j.hasNext();)
+		//			{
+		//				((RosterListener)j.next()).jidStatusDeleted(jidGroup,primary,jidStatus,jidStatusIndex);
+		//			}
+		//		}
+		return false;
+	}
+
+	//------------------------------Prescence event------------------------------------\\
+
+	public void presenceChanged(final Presence presence)
+	{
+		JID jid = presence.getFrom();
+		final NormalJIDStatus jidStatus = (NormalJIDStatus) jidStatussen.get(jid);
+		if (jidStatus == null)
+		{
+			return;
+		}
+		if (jid.getUser() == null)
+		{
+			//server, server has no multiple resources
+			jidStatus.updatePresence(presence);
+			server.showChange(jidStatus);
+			return;
+		}
+		SecondaryJIDStatus secondary = jidStatus.getSecondaryJIDStatus(jid.getResource());
+		boolean seconderyAdded = false;
+		ResourceJIDStatus remove = null;
+		if (presence.getShow() == Presence.UNAVAILABLE)
+		{
+			remove = jidStatus.removeResource(jid.getResource());
+			if (remove == null)
+			{
+				//no remove so no resource so update presence to unavailable 
+				secondary.updatePresence(presence);
+			}
+		}
+		else
+		{
+			if (secondary == null)
+			{
+				//new resource
+				secondary = jidStatus.addSecondaryJIDStatus(jid);
+				seconderyAdded = true;
+			}
+			else if (secondary.getShow() == presence.getShow())
+			{//show hasn't changed
+				if(secondary.getType().equals("msn") && secondary.getStatus()!=null
+						&&  Preferences.getBoolean("jeti","showRealNick", false))
+				{//should status be visible?  old msn transport hack, remove when new nick mechanism
+					if(!secondary.getStatus().equals(presence.getStatus()))
+					{//status has changed
+						final SecondaryJIDStatus secondary2 = secondary;
+						Runnable updateAComponent = new Runnable() {
+							public void run()
+							{
+								secondary2.updatePresence(presence);
+								for (Iterator i = jidStatus.getGroups(); i.hasNext();)
+								{//update tree part status is in, replace by update only the changed part?
+									JIDStatusGroup jidGroup = jidTree.getGroup((String) i.next());
+									PrimaryJIDStatus primary = jidGroup.searchPrimaryJIDStatus(jidStatus.getNick());
+									for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+									{
+										((RosterListener) j.next()).jidStatussenUpdated(jidGroup, primary);
+									}
+								}
+							}
+						};
+						SwingUtilities.invokeLater(updateAComponent);
+					}
+				}
+				secondary.updatePresence(presence);
+				return;
+			}
+			secondary.updatePresence(presence);
+			//show has changed so update
+			 jidStatus.sortSecondary();
+		}
+		
+		final SecondaryJIDStatus secondary2 = secondary; 
+		final boolean seconderyAdded2 = seconderyAdded;
+		final ResourceJIDStatus remove2=remove;
+		
+		Runnable updateAComponent = new Runnable() {
+			public void run()
+			{
+//				change
+				for (Iterator i = jidStatus.getGroups(); i.hasNext();)
+				{
+					//new jidstatus per group?
+					JIDStatusGroup jidGroup = jidTree.getGroup((String) i.next());
+					PrimaryJIDStatus primary = jidGroup.searchPrimaryJIDStatus(jidStatus.getNick());
+					if (seconderyAdded2) primary.addJIDStatus(secondary2);
+					if (remove2 != null) primary.removeJIDStatus(remove2);
+					else if (secondary2 != null) primary.updatePresence(secondary2);
+					boolean oldOnline = primary.isOnline();
+					primary.updateOnline();
+					boolean newOnline = primary.isOnline();
+					if(oldOnline && !newOnline)
+					{//gone offline
+						jidGroup.removeOnline();
+						for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+						{
+							((RosterListener) j.next()).groupUpdated(jidGroup, jidTree.indexOfGroup(jidGroup));
+						}
+					}
+					else if(!oldOnline && newOnline)
+					{//gone online
+						jidGroup.addOnline();
+						for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+						{
+							((RosterListener) j.next()).groupUpdated(jidGroup, jidTree.indexOfGroup(jidGroup));
+						}	
+					}
+					//int index = jidGroup.indexOfPrimaryJIDStatus(primary);
+					for (Iterator j = backend.getListeners(RosterListener.class); j.hasNext();)
+					{
+						((RosterListener) j.next()).jidStatussenUpdated(jidGroup, primary);
+					}
+				}
+			}
+		};
+		try
+		{
+			SwingUtilities.invokeAndWait(updateAComponent);
+		} catch (InterruptedException e)
+		{
+			e.printStackTrace();
+		} catch (InvocationTargetException e)
+		{
+			e.printStackTrace();
+		}
+	}
+
+	public static JIDStatus getJIDStatus(JID jid)
+	{
+		return (JIDStatus) jidStatussen.get(jid);
+	}
+	
+	public static Iterator getJIDStatussen()
+	{
+		return jidStatussen.values().iterator();
+	}
+
+	public String[] getAllGroups()
+	{
+		if (jidTree==null) return new String[]{ I18N.gettext("main.main.roster.Friends") };
+		return jidTree.getGroups();
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/ChatSplitPane.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/ChatSplitPane.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/ChatSplitPane.java	(revision 3952)
@@ -0,0 +1,976 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2003 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *
+ *	Created on 28-apr-2003
+ */
+
+package nu.fw.jeti.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.List;
+
+import javax.swing.Timer;
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.text.*;
+import javax.swing.border.*;
+import javax.swing.ImageIcon;
+
+import nu.fw.jeti.events.ChatEndedListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.*;
+import nu.fw.jeti.plugins.*;
+import nu.fw.jeti.util.Base64;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+import nu.fw.jeti.util.JavaScriptServerExpresso;
+import nu.fw.jeti.util.ServerExpresso;
+
+/**
+ * @Author : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+ * @Date : 05/11/2008
+ * @Brief : classe focusWindow, faz com que o titulo fique piscando.
+ */
+
+class focusWindow implements ActionListener
+{
+	private boolean flag = false;
+	private JFrame window;
+	private String title;
+	private Timer timer = new Timer(1000, this);
+	private String name;
+	
+	public focusWindow()
+	{
+		timer.start();
+	}
+	
+	public void load(JFrame window, String name)
+	{
+		this.window = window;
+		this.title = window.getTitle();
+		this.name = name;
+		
+	}
+    public void actionPerformed(ActionEvent evt)
+    {
+    	if( window.getExtendedState() == window.ICONIFIED )
+    	{
+	    	if(flag)
+	    		window.setTitle("## " + this.name + " fala...");
+	    	else
+	    		window.setTitle(this.title);
+	    	flag = !flag;
+    	}
+    	else
+    	{
+    		timer.stop();
+    		window.setTitle(this.title);
+    	}
+    }
+}
+
+/**
+ * class defining basic feature of a chat window
+ * used by groupchatwindow and chatwindow
+ * @author E.S. de Boer
+ *
+ */
+
+//Do not add text to txtUitvoer outside this class, otherwise the text will 
+//not scroll to the end
+
+public class ChatSplitPane extends JSplitPane 
+{
+	private JID contactJID;
+	private String me;
+	private Backend backend;
+	private JScrollPane scrlInvoer = new JScrollPane();
+	private JTextPane txtInvoer = new JTextPane();
+	private JScrollPane scrlUitvoer = new JScrollPane();
+	private JTextPane txtUitvoer = new ToolTipTextpane();
+	private String contactName;
+	private String thread;
+	private boolean enterSends = false;
+	private boolean showTimestamp = false;
+	private Emoticons emoticons;
+	private JPanel pnlBottom = new JPanel();
+	private BorderLayout borderLayout1 = new BorderLayout();
+	private JPanel pnlControl = new JPanel();
+	private JPopupMenu popupMenu = new JPopupMenu();
+	private JPopupMenu popupSelectedMenu = new JPopupMenu();
+	private String composingID;
+	private boolean typing = false;
+	private FormattedMessage xhtml;
+	private boolean groupChat;
+	private Spell spell;
+	private Translator translator;
+	private Translator links;
+	private Translator xmppuri;
+	private Translator colormessages;
+	private Notifiers titleTimer;
+	private Notifiers titleFlash;
+	private SimpleAttributeSet colorAttributeSet = new SimpleAttributeSet();
+	private boolean toFrontOnNewMessage = false;
+	private static DateFormat dateFormat = DateFormat.getTimeInstance();
+	DateFormat shortDate= DateFormat.getDateInstance(DateFormat.SHORT);
+	DateFormat shortTime= DateFormat.getTimeInstance(DateFormat.SHORT);
+	private volatile boolean scrolls=true;
+	private volatile boolean systemScroll=true;
+	private Date date = new Date();
+	private ImageIcon avatar;
+
+	
+	class ToolTipTextpane extends JTextPane
+	{
+		//Tooltip on textpane, show time of message
+		public String getToolTipText(MouseEvent e)
+		{
+			int location = txtUitvoer.viewToModel(e.getPoint());
+			StyledDocument doc = (StyledDocument) txtUitvoer.getDocument();
+			AttributeSet set = doc.getCharacterElement(location).getAttributes();
+			return (String) set.getAttribute("time");	
+		}
+	}
+	
+	/**
+	 * Groupchat splitpane constructor 
+	 * @param backend
+	 * @param to
+	 * @param toName
+	 * @param me
+	 * @param thread
+	 * @param groupChat
+	 * @param type
+	 * @param menu
+	 * @param frame
+	 * 
+	 */
+	public ChatSplitPane(Backend backend, JID to,String toName,String me,JMenu menu,JFrame frame)
+	{
+		groupChat = true; 
+		contactJID = to;
+        contactName = toName; 
+		this.backend = backend;
+		this.me = me;
+		toFrontOnNewMessage = Preferences.getBoolean("groupchat", "toFrontOnNewMessage", false);
+		init("groupchat",menu);
+		setParentFrame(frame);
+	}
+	
+	/**
+	 * 1-1 chat splitpane constructor
+	 * @param backend
+	 * @param to
+	 * @param me
+	 * @param thread
+	 * @param menu
+	 * @param frame
+	 */
+	public ChatSplitPane(Backend backend, JIDStatus jidStatus,String me,String thread,JMenu menu,JFrame frame)
+	{
+		this.groupChat = false; 
+		contactJID = jidStatus.getCompleteJID();
+        contactName = jidStatus.getNick(); 
+		avatar = jidStatus.getAvatar();
+		this.backend = backend;
+		this.me = me;
+		this.thread = thread;
+		toFrontOnNewMessage = Preferences.getBoolean("jeti", "toFrontOnNewMessage", false);
+		init(jidStatus.getType(),menu);
+		setParentFrame(frame);
+	}
+	
+	/**
+	 * Add a menu to the menu invoked when the user presses on 
+	 * the right mousebutton and there is text selected
+	 * @param submenu The menu to add
+	 */
+	public void addToSelectedTextPopupMenu(JMenu submenu)
+	{
+		popupSelectedMenu.add(submenu);
+	}
+	
+	public String getSelectedText()
+	{
+		String text = txtUitvoer.getSelectedText();
+		if(text!=null) return text;
+		else return txtInvoer.getSelectedText();
+	}
+	
+	public void init(String type,JMenu menu)
+	{
+		JCheckBoxMenuItem chkItem = new JCheckBoxMenuItem(I18N.gettext("main.chat.menu.To_front_on_new_message"));
+		chkItem.setSelected(toFrontOnNewMessage);
+		chkItem.addActionListener(new ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				toFrontOnNewMessage= ((JCheckBoxMenuItem)e.getSource()).isSelected();
+			}
+		});
+		
+		menu.add(chkItem);
+		
+		if (PluginsInfo.isPluginLoaded("emoticons"))
+		{
+			initEmoticons(type,menu);
+		}
+				
+		Translate translate=null;
+		
+		if (PluginsInfo.isPluginLoaded("translate"))
+		{
+			translate = (Translate) PluginsInfo.newPluginInstance("translate");
+			translate.init(this,txtInvoer);
+			txtUitvoer.addMouseListener(new MouseAdapter()
+			{
+				public void mouseClicked(MouseEvent e)
+				{	
+					if(SwingUtilities.isRightMouseButton(e))
+					{
+						if(txtUitvoer.getSelectedText()!=null)
+						{
+							popupSelectedMenu.show(e.getComponent(), e.getX(), e.getY());
+						}
+					}
+				}
+			});
+		}
+		
+		if (PluginsInfo.isPluginLoaded("spell"))
+		{
+			spell = (Spell) PluginsInfo.newPluginInstance("spell");
+			spell.addChangeDictoryMenuEntry(menu);
+		}
+		
+		if (PluginsInfo.isPluginLoaded("links"))
+		{
+			links = (Translator) PluginsInfo.newPluginInstance("links");
+			links.init(txtUitvoer);
+		}
+		if (PluginsInfo.isPluginLoaded("xmppuri"))
+		{
+			xmppuri = (Translator) PluginsInfo.newPluginInstance("xmppuri");
+			xmppuri.init(txtUitvoer);
+		}
+		if (PluginsInfo.isPluginLoaded("colormessages"))
+		{
+			colormessages = (Translator) PluginsInfo.newPluginInstance("colormessages");
+		}
+		if (PluginsInfo.isPluginLoaded("titlescroller"))
+		{
+			titleTimer = (Notifiers) PluginsInfo.newPluginInstance("titlescroller");
+		}
+		if (PluginsInfo.isPluginLoaded("titleflash"))
+		{
+			titleFlash = (Notifiers) PluginsInfo.newPluginInstance("titleflash");
+		}
+		if(PluginsInfo.isPluginLoaded("windowsutils"))
+		{
+			NativeUtils util =(NativeUtils)PluginsInfo.newPluginInstance("windowsutils");
+			util.addChatMenus(menu,this);
+		}
+		
+		//translator = PluginsInfo.getTranslator();
+		if(emoticons != null || spell != null || translate!=null)  
+		{	
+			txtInvoer.addMouseListener(new MouseAdapter()
+			{
+				public void mousePressed(MouseEvent e)
+				{
+					if (SwingUtilities.isRightMouseButton(e))
+					{
+						if(txtInvoer.getSelectedText()!=null)
+						{
+							popupSelectedMenu.show(e.getComponent(), e.getX(), e.getY());
+						}
+						else if(spell != null)
+						{
+							if(!spell.rightClick(txtInvoer,e) && emoticons != null) popupMenu.show(e.getComponent(), e.getX(), e.getY());
+						}
+						else popupMenu.show(e.getComponent(), e.getX(), e.getY());
+					}
+				}
+			});
+		}
+		
+		ToolTipManager.sharedInstance().registerComponent(txtUitvoer);
+			
+		enterSends = Preferences.getBoolean("jeti","enterSends",true);
+		showTimestamp = Preferences.getBoolean("jeti","showTimestamp",true);
+		
+		try
+		{
+			jbInit();
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+		}
+		if (PluginsInfo.isPluginLoaded("xhtml"))
+		{
+			//String contactJid = contactJID.toString();
+			xhtml = (FormattedMessage) PluginsInfo.newPluginInstance("xhtml");
+			xhtml.initXHTML(this, txtInvoer, pnlControl, contactJID, contactName);
+		}
+		
+		String t = "";
+		//if(!groupChat)	
+		//notify();
+		if (thread == null)t = "no id - ";
+		Document doc = txtUitvoer.getDocument();
+		try
+		{
+			SimpleAttributeSet sas = new SimpleAttributeSet();
+			StyleConstants.setForeground(sas, Color.gray);
+			doc.insertString(doc.getLength(), t + I18N.gettext("main.chat.Chat_started_on") + " " + DateFormat.getDateTimeInstance().format(new Date()), sas);
+		}
+		catch (BadLocationException e)
+		{
+			e.printStackTrace();
+		}
+		
+		//solves bug iconofied frame then no system message and wrong layout
+		//pack();
+		
+		setBorder(null);		
+		setSize(300, 350);
+	}
+	
+	public JTextPane getTextInput()
+	{
+		return txtInvoer;
+	}
+
+	
+	public void setParentFrame(JFrame frame)
+	{
+		if (titleTimer !=null)
+		{
+			titleTimer.init(frame,contactName);
+		}
+		if (titleFlash != null)
+		{
+			titleFlash.init(frame, contactName);
+		}
+	}
+	
+	private void initEmoticons(String type,JMenu menu)
+	{
+		//showEmoticons = true;
+		emoticons = (Emoticons) PluginsInfo.newPluginInstance("emoticons");
+		try
+		{
+			emoticons.init(txtUitvoer, pnlControl, txtInvoer, popupMenu,type,menu);
+		}
+		catch (IllegalStateException ex)
+		{
+			ex.printStackTrace(); 
+			//showEmoticons = false;
+			PluginsInfo.unloadPlugin("emoticons");
+			emoticons = null;
+			return;
+		}
+	}
+	
+	public void close()
+	{
+		if(groupChat)contactJID = new JID(contactJID.getUser(),contactJID.getDomain(),me);
+		for(Iterator j = backend.getListeners(ChatEndedListener.class);j.hasNext();)
+		{//exit
+			((ChatEndedListener)j.next()).chatEnded(contactJID);
+		}
+	}
+	
+//	public boolean compareJID(JID jid)
+//	{
+//		return from.equals(jid);
+//	}
+//	
+//	public String getThread()
+//	{
+//		return thread;
+//	}
+
+	public void appendMessage(Message message,String name)
+	{
+		if (name == null)
+		{
+			appendSystemMessage(message.getBody());
+		}
+		else if(name.equals(me))
+		{
+			showMessage(message,new Color(156, 23, 23),Preferences.getString("jeti", "ownName", me));
+		}
+		else
+		{
+			if(titleTimer!=null )
+			{
+                titleTimer.start(
+                    MessageFormat.format(
+                        I18N.gettext("main.chat.{0}_says_{1}"),
+                        new Object[] {name,message.getBody()}));
+            }
+			
+			if(titleFlash!=null)
+			{
+                titleFlash.start("");
+            }
+			
+			if(!groupChat)contactJID = message.getFrom();
+			
+			showMessage(message, new Color(17, 102, 6), name);
+		}
+	}
+	
+	public  void appendSystemMessage(final String message)
+	{
+		SwingUtilities.invokeLater(new Runnable()
+		{
+			public void run()
+			{
+				final Document doc = txtUitvoer.getDocument();
+				final Point  viewPos = scrlUitvoer.getViewport().getViewPosition();
+				final boolean scroll = scrolls;//cache scroll value because insertstring will update it
+				systemScroll=true;
+				SimpleAttributeSet sas = new SimpleAttributeSet();
+				StyleConstants.setForeground(sas, Color.gray);
+				String timeStamp;
+				if (showTimestamp) 	timeStamp = formatTime(new Date()) + " ";
+				else timeStamp = "";
+				try
+				{
+					doc.insertString(doc.getLength(), "\n"+ timeStamp + message, sas);
+				}
+				catch (BadLocationException e)
+				{
+					e.printStackTrace();
+				}
+				
+				if(scroll)
+				{
+					txtUitvoer.setCaretPosition(doc.getLength());
+					scrolls=true;
+				}
+				else
+				{
+					scrlUitvoer.getViewport().setViewPosition(viewPos);
+				}
+			}
+		});
+	}
+	
+	private void showMessage(final Message message,final Color color,final String name)
+	{//two threads can write to display (typing & receiving)
+			
+		SwingUtilities.invokeLater(new Runnable()
+		{
+			public void run()
+			{
+				String text = message.getBody();
+		        Date tempdelay =null;
+				List tempWordList=null;
+				date.setTime(System.currentTimeMillis());
+				
+				// Loop through extensions
+				if (message.hasExtensions())
+				{
+					for (Iterator i = message.getExtensions(); i.hasNext();)
+					{
+						Extension extension = (Extension) i.next();
+						if (extension instanceof XDelay)
+						{
+							tempdelay = ((XDelay) extension).getDate();
+						}
+						else if(xhtml!=null) 
+						{
+							if(tempWordList==null) tempWordList = xhtml.getWordList(extension);
+						}
+					}
+				}
+				if (xhtml!=null && !name.equals(me) && !groupChat) {
+		            xhtml.useXHTML(tempWordList!=null, name);
+		        }
+				
+				if (tempWordList==null) {
+					tempWordList = createWordList(text);
+		        }
+
+		        // Mark eventual weblinks (if links extension is active)
+				if(links!=null) {
+		            links.translate(tempWordList);
+		        }
+				
+				if(xmppuri!=null) {
+		            xmppuri.translate(tempWordList);
+		        }
+				if(groupChat) {//me replace with nick
+					for(Iterator i = tempWordList.iterator();i.hasNext();) {
+						Word w = (Word) i.next();
+						if(w.word.equals("/me")) {
+		                    w.word = name;
+		                }
+					}
+				}
+
+				// Handle error messages
+				if (message.getType().equals("error")) {
+		            insertError(tempWordList, message);
+				}
+
+				// Insert eventual emoticons
+				if (emoticons != null) {
+		            emoticons.insertEmoticons(tempWordList);
+		        }
+
+				//TODO make a new translator thing for here and below
+				if (translator != null) translator.translate(tempWordList);
+
+				final Date delay = tempdelay;
+				final List wordList = tempWordList;
+				
+				
+				Point  viewPos = scrlUitvoer.getViewport().getViewPosition();
+				boolean scroll = scrolls;//cache scroll value because insertstring will update it
+				systemScroll=true;
+				Document doc = txtUitvoer.getDocument();
+				try
+				{
+					doc.insertString(doc.getLength(),"\n",null);
+		            if (delay != null) 
+		            {
+		            	SimpleAttributeSet sas = new SimpleAttributeSet();
+		                StyleConstants.setForeground(sas, Color.darkGray);
+		                doc.insertString(doc.getLength(), formatTime(delay) + " ", sas);
+		            }
+					else if (showTimestamp) 
+					{// Eventual timestamp
+		                SimpleAttributeSet sas = new SimpleAttributeSet();
+		                StyleConstants.setForeground(sas, Color.gray);
+		                doc.insertString(doc.getLength(), formatTime(date) + " ", sas);
+		            }
+					
+		            StyleConstants.setForeground(colorAttributeSet, color);
+		
+		            // Print originator if not error
+		            if (!message.getType().equals("error")) {
+						colorAttributeSet.addAttribute("time",dateFormat.format(date));
+						doc.insertString(doc.getLength(),name+ ": ",colorAttributeSet);
+					}
+					
+		            // Insert words from wordlist
+					for(Iterator i = wordList.iterator();i.hasNext();) {
+						Word w = (Word) i.next();
+						doc.insertString(doc.getLength(),w.toString(),
+		                                 w.getAttributes());
+					}
+		
+					if (scroll)	{
+		                txtUitvoer.setCaretPosition(doc.getLength());
+		                scroll=true;
+		            } else {
+						scrlUitvoer.getViewport().setViewPosition(viewPos);
+					}
+				}
+				catch (BadLocationException e)
+				{
+					e.printStackTrace();
+				}
+
+				/**
+				 * @Author : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+				 * @Date : 04/11/2008
+				 * @Brief : Trecho de codigo abaixo, faz com que a janela fique piscando caso esta esteja
+				 * minimizada na barra de tarefas. Criada a classe focusWindow.
+				 */
+
+				JFrame frame = (JFrame)getTopLevelAncestor();
+
+				if(toFrontOnNewMessage)
+				{
+					if(!getTopLevelAncestor().isFocusOwner())
+					{
+						if(getTopLevelAncestor() instanceof JFrame)
+						{
+							if( frame.getExtendedState() == Frame.ICONIFIED )
+							{
+								frame.setState(Frame.NORMAL);
+							}
+							frame.toFront();
+						}
+						else txtInvoer.requestFocus();
+					}
+				}
+				
+				if( frame.getExtendedState() == Frame.ICONIFIED )
+				{
+					frame.setVisible(true);
+					focusWindow focusW = new focusWindow();
+					focusW.load(frame, name);
+				}
+			}
+		});
+	}
+	
+	/**
+     * Format a timestamp for display.
+     */
+    private String formatTime(Date date) {
+		String result = "";
+		
+		if((System.currentTimeMillis() -date.getTime())> 600000)
+		{//message is older then 10 min
+			Calendar cal = Calendar.getInstance();
+			cal.set(Calendar.HOUR, 0);
+		    cal.set(Calendar.MINUTE, 0);
+		    cal.set(Calendar.SECOND, 0);
+		    cal.set(Calendar.AM_PM, Calendar.AM);
+		    if(date.before(cal.getTime()))
+		    {//print date if it is the previous day
+		    	result += shortDate.format(date)+ " ";
+		    }
+		}
+		result+="["+shortTime.format(date)+"]";
+		return result;
+    }
+
+    // Insert error message into wordList
+    private void insertError(List wordList, Message message) {
+        String error;
+        boolean replace = false;
+
+        switch (message.getErrorCode()) {
+        case 404:
+            error = MessageFormat.format(
+                I18N.gettext("main.error.User_{0}_could_not_be_found"),
+                new Object[] {message.getFrom()});
+            replace = true;
+            break;
+        default:
+            error = I18N.gettext("main.error.Error_in_chat");
+            break;
+        }
+
+        // Create list of error words and make them red
+        List errorWords = createWordList(error);
+        SimpleAttributeSet sas = new SimpleAttributeSet();
+        StyleConstants.setForeground(sas, Color.red);
+        for(Iterator i = errorWords.iterator();i.hasNext();) {
+            ((Word)i.next()).addAttributes(sas);
+        }
+        
+        if (!replace) {
+            errorWords.addAll(wordList);
+        }
+        wordList.clear();
+        wordList.addAll(errorWords);
+	}
+
+    //TODO move to util??
+	public static List createWordList(String text)
+	{
+		List wordList = new ArrayList();
+		StringBuffer temp = new StringBuffer();
+		for(int i = 0;i<text.length();i++)
+		{//split text up in words
+			char c = text.charAt(i);
+			switch (c)
+			{
+				case ' ': 	addWordFromTemp(temp, wordList);
+							wordList.add(new Word(" "));
+							temp = new StringBuffer(); break;
+				case '\n': 	addWordFromTemp(temp, wordList);
+							wordList.add(new Word("\n"));
+							temp = new StringBuffer(); break;
+				case '\t': 	addWordFromTemp(temp, wordList); 
+							wordList.add(new Word("\t"));
+							temp = new StringBuffer();break;
+				default: temp.append(c);
+			}
+		}
+		addWordFromTemp(temp, wordList);
+		return wordList;
+	}
+	
+	private static void addWordFromTemp(StringBuffer temp, List wordList)
+	{
+		if(temp.length()>0)wordList.add(new Word(temp));
+	}
+
+	private String getPhotoExpresso(String Jid)
+	{
+		ServerExpresso JidPhoto = new ServerExpresso();
+		return JidPhoto.PhotoUid(Jid);
+	}
+	
+	private void jbInit() throws Exception
+	{
+		Color c = UIManager.getColor("TextPane.foreground");
+		txtInvoer.setForeground(c);
+		txtUitvoer.setForeground(c);
+		setResizeWeight(0.9);
+		setOrientation(JSplitPane.VERTICAL_SPLIT);
+		setBottomComponent(pnlBottom);
+		scrlInvoer.setPreferredSize(new Dimension(100, 100));
+
+		txtInvoer.addKeyListener(new java.awt.event.KeyAdapter()
+		{
+			public void keyPressed(KeyEvent e)
+			{
+				txtInvoer_keyPressed(e);
+			}
+			
+			public void keyReleased(KeyEvent e)
+			{
+				if(spell!=null)spell.keyReleased(e,txtInvoer);
+			}
+		});
+		txtUitvoer.setEditable(false);
+		txtUitvoer.getDocument().putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" );
+		txtInvoer.getDocument().putProperty( DefaultEditorKit.EndOfLineStringProperty, "\n" );
+		addFocusListener(new FocusAdapter()
+		{
+			public void focusGained(FocusEvent e)
+			{
+				txtInvoer.requestFocusInWindow();
+			}
+		});
+
+		/**
+		 * @Author : Rodrigo Souza
+		 * @Date : 15/10/2008
+		 * @Description : Trecho de codigo abaixo ajusta as dimensoes da foto (avatar)
+		 *				  e demais paineis dentro da janela de conversa.
+		 */
+
+		pnlBottom.setLayout(borderLayout1);
+
+		JPanel text = new JPanel();
+		text.setLayout(new BoxLayout(text,BoxLayout.PAGE_AXIS));
+		text.add(pnlControl);
+		text.add(scrlInvoer);
+		pnlBottom.add(text, BorderLayout.CENTER);
+
+		/**
+		 * @Author : Alexandre Luiz Correia - alexandrecorreia@celepar.pr.gov.br
+		 * @Date : 10/09/2008
+		 * @Description : Avatar - Carrega as Photos do Ldap
+		 */
+
+		// Border
+		Border raisedetched;
+		raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
+
+		// String
+		String userJid = contactJID.toString();
+		
+		/**
+		 *  @Author : Alexandre Correia / Rodrigo Souza
+		 *  @Date : 04/03/2009
+		 *  @Description : Conecta com a classe ServerExpresso(nu.fw.jeti.util.ServerExpresso) no Expresso e passa 
+		 *  a variavel userJid(Ex.: teste@nome_maquina), recebendo uma string em base64.
+		 */ 
+
+		String imgUser = getPhotoExpresso(userJid);
+		byte[] data = Base64.decode(imgUser);
+		ImageIcon icon = new ImageIcon(data);
+		JLabel avatar = new JLabel(icon);
+
+		avatar.setBorder(raisedetched);
+		pnlBottom.add(avatar, BorderLayout.EAST);
+
+		add(pnlBottom, JSplitPane.BOTTOM);
+		add(scrlUitvoer, JSplitPane.TOP);
+
+		scrlUitvoer.getViewport().add(txtUitvoer, null);
+		scrlInvoer.getViewport().add(txtInvoer, null);
+		scrlUitvoer.getVerticalScrollBar().getModel().addChangeListener(new ChangeListener()
+		{
+			public void stateChanged(ChangeEvent e)
+			{
+				BoundedRangeModel model =(BoundedRangeModel)e.getSource();
+				if(!model.getValueIsAdjusting())
+				{
+					if(!systemScroll)
+					{//only update if scroll is performed by the user
+						if(model.getMaximum()-model.getExtent()-model.getValue()>10)
+						{
+							scrolls=false;
+						}
+						else scrolls=true;
+					}
+					systemScroll=false;
+				}
+			}
+		});
+
+		setOneTouchExpandable(true);		
+		setDividerLocation(170);
+	}
+
+	public void send()
+	{
+		if (txtInvoer.getText().length()==0)return;
+		boolean sendXHTML = false;
+		XExtension html = null;
+		XExtension ownHtml = null;
+		List wordList=null;
+		if(xhtml != null)wordList = xhtml.makeWordListFromDocument();
+		if(wordList==null)wordList=createWordList(txtInvoer.getText());
+		StringBuffer temp = new StringBuffer();
+		//TODO Translator
+		//if (translator != null) translator.translate(wordList);
+		
+		for(Iterator i = wordList.iterator();i.hasNext();)
+		{
+			temp.append(i.next());
+		}
+		String text = temp.toString();
+		if(colormessages!=null)
+		{
+			colormessages.translate(wordList);
+		}
+		if(xhtml != null)
+		{	
+			html = xhtml.getXHTMLExtension(wordList);
+			//ownwordlist will be edited so make deep copy
+			ArrayList tempList = new ArrayList();
+			try 
+			{
+				for(Iterator i = wordList.iterator();i.hasNext();)
+				{
+					tempList.add(((Word)i.next()).clone());
+				}
+			}
+			catch (CloneNotSupportedException e)
+			{
+				e.printStackTrace();
+			}
+			ownHtml = xhtml.getXHTMLExtension(tempList);
+			sendXHTML = xhtml.sendXML();
+		}
+		Message message = null;
+		if(groupChat)
+		{	
+			if(html!=null) message = new Message(contactJID,text ,html);
+			else message = new Message(contactJID,text);
+		}
+		else
+		{
+			MessageBuilder b = new MessageBuilder();
+			b.type = "chat";
+			b.setTo(contactJID);
+			b.setId(backend.getIdentifier());
+			b.thread = thread;
+			b.addXExtension(new XMessageEvent("composing", null));
+			b.body = text;
+			if(sendXHTML)b.addXExtension(html);
+
+			message =((Message) b.build());
+			showMessage(new Message(text, contactJID, null,thread,ownHtml),new Color(156, 23, 23), "Eu ( " + nu.fw.jeti.applet.Jeti.CNNAME +" ) ");
+		}
+		backend.sendMessage(message);
+		
+		txtInvoer.getHighlighter().removeAllHighlights();
+		txtInvoer.setText("");
+		txtInvoer.requestFocus();
+		typing = false;
+	}
+	
+	public void composingID(String id)
+	{
+		composingID = id;
+	}
+
+	void txtInvoer_keyPressed(KeyEvent e)
+	{
+		/**
+		 * Author(s): Alexandre Correia / Rodrigo Souza
+		 * Date : 02/03/2009
+		 * Brief : Limpa o tempo do Javascript
+		 */
+		
+		JavaScriptServerExpresso cleanSt = new nu.fw.jeti.util.JavaScriptServerExpresso();
+		cleanSt.cleanStatus();
+		
+		if(!groupChat)
+		{
+			if (txtInvoer.getText().length() < 2 && typing)
+			{
+				typing = false;
+				if (composingID != null)
+				{
+					//send not composing
+					backend.send(new Message(null, contactJID, null, new XMessageEvent(null, composingID)));
+				}
+			}
+			else if (txtInvoer.getText().length() > 0 && !typing)
+			{
+				typing = true;
+				if (composingID != null)
+				{
+					//send composing
+					 backend.send(new Message(null, contactJID, null, new XMessageEvent("composing", composingID)));
+				}
+			}
+		}
+		if (e.getKeyCode() == KeyEvent.VK_ENTER)
+		{
+			if (enterSends)
+			{
+				if ((e.getModifiers() == InputEvent.SHIFT_MASK) || (e.getModifiers() == InputEvent.CTRL_MASK))
+				{
+					Document doc = txtInvoer.getDocument();
+					try
+					{
+						doc.insertString(txtInvoer.getCaretPosition(), "\n", null);
+					}
+					catch (BadLocationException e3)
+					{e3.printStackTrace();}
+				}
+				else 
+				{
+					send();
+					e.consume();
+				}
+			}
+			else if ((e.getModifiers() == InputEvent.SHIFT_MASK) || (e.getModifiers() == InputEvent.CTRL_MASK))
+			{
+				send();
+			}
+		}
+	}	
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/AboutWindow.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/AboutWindow.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/AboutWindow.java	(revision 3952)
@@ -0,0 +1,126 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.ui;
+
+
+import java.awt.SystemColor;
+import java.awt.event.ActionEvent;
+
+import javax.swing.*;
+
+import nu.fw.jeti.backend.Start;
+import nu.fw.jeti.plugins.PluginsInfo;
+import nu.fw.jeti.util.I18N;
+
+/*2001
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class AboutWindow extends JFrame
+{   
+    public AboutWindow()
+    {
+        try
+        {
+            jbInit();
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+    private void jbInit() throws Exception
+    {
+		setTitle(I18N.gettext("main.about.About_JETI"));
+		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+		getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS));
+		
+		// Title 
+		JLabel label = new JLabel("JETI"+Start.OS2);
+		label.setFont(new java.awt.Font("Serif", 1, 35));
+        label.setAlignmentX((float) 0.5);
+		getContentPane().add(label);
+        
+		// Version
+		label = new JLabel(I18N.gettext("main.about.Version") + " " + Start.VERSION);
+        label.setAlignmentX((float) 0.5);
+        getContentPane().add(label);
+               
+		label = new JLabel("http://www.jeti-im.org");
+		label.setAlignmentX(0.5f);
+		getContentPane().add(label);
+	    
+		label = new JLabel(" ");
+		label.setAlignmentX(0.5f);
+		getContentPane().add(label);
+		
+		label = new JLabel(".:: Customizado por CELEPAR ::.");
+		label.setFont(new java.awt.Font("Serif",1,15));
+		label.setAlignmentX(0.5f);
+		getContentPane().add(label);
+		
+		label = new JLabel("Cia de Inform\u00E1tica do Paran\u00E1");
+		label.setFont(new java.awt.Font("Serif",1,10));
+		label.setAlignmentX(0.5f);	
+		getContentPane().add(label);
+		
+		JTextPane textPane = new JTextPane(); 
+		textPane.setBackground(SystemColor.control);
+		textPane.setEnabled(false);
+		textPane.setFont(new java.awt.Font("Dialog", 1, 12));
+		textPane.setOpaque(false);
+		textPane.setEditable(false);
+		textPane.setText(PluginsInfo.getAbout());
+		textPane.setCaretPosition(0); 
+		JScrollPane scrollPane = new JScrollPane(textPane); 
+		scrollPane.setBorder(null);
+        getContentPane().add(scrollPane);
+		
+		JButton button = new JButton();
+        button.setAlignmentX((float) 0.5);
+        I18N.setTextAndMnemonic("Close",button);
+        button.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                jButton1_actionPerformed(e);
+            }
+        });
+		getRootPane().setDefaultButton(button);
+		getContentPane().add(button);
+		setSize(300,250);
+		setLocationRelativeTo(null);
+    }
+
+    void jButton1_actionPerformed(ActionEvent e)
+    {
+		this.dispose();
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/models/RosterTreeModel.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/models/RosterTreeModel.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/models/RosterTreeModel.java	(revision 3952)
@@ -0,0 +1,224 @@
+package nu.fw.jeti.ui.models;
+
+import java.util.LinkedList;
+
+import javax.swing.SwingUtilities;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+import nu.fw.jeti.backend.roster.JIDStatusGroup;
+import nu.fw.jeti.backend.roster.JIDStatusTree;
+import nu.fw.jeti.backend.roster.PrimaryJIDStatus;
+import nu.fw.jeti.events.RosterListener;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.util.I18N;
+
+/**
+ * A TreeModel implementation of the roster. If used as the
+ * TreeModel of a JTree the JTree will display the roster.
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class RosterTreeModel implements TreeModel, RosterListener 
+{//use invokeandwait because removing from roster will go wrong otherwise
+	private LinkedList treeModelListeners = new LinkedList();
+	private JIDStatusTree tree;
+	
+	//------------------ events------------------\\
+	public void rosterReplaced(final JIDStatusTree jidTree)
+	{
+		tree = jidTree;
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree});
+		for (int i = 0; i < treeModelListeners.size(); i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeStructureChanged(e);
+		}
+	}
+
+	public void groupAdded(JIDStatusGroup group,int index)
+	{
+        int len = treeModelListeners.size();
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree},new int[] {index}, new Object[]{group});
+		for (int i = 0; i < len; i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeNodesInserted(e);
+		}
+	}
+
+
+	public void groupDeleted(JIDStatusGroup group, int index)
+	{
+		int len = treeModelListeners.size();
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree},new int[] {index}, new Object[]{group});
+		for (int i = 0; i < len; i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeNodesRemoved(e);
+		}
+	}
+	
+	//TODO synchronization?
+	public void groupUpdated(JIDStatusGroup group,int index)
+	{
+		int len = treeModelListeners.size();
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree},new int[] {index}, new Object[]{group});
+		for (int i = 0; i < len; i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeNodesChanged(e);
+		}
+	}
+
+	public void primaryAdded(JIDStatusGroup jidGroup, PrimaryJIDStatus primary, int index)
+	{
+        int len = treeModelListeners.size();
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree, jidGroup},new int[] {index}, new Object[]{primary});
+		for (int i = 0; i < len; i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeNodesInserted(e);
+		}
+	}
+
+	public void primaryDeleted(JIDStatusGroup jidGroup,PrimaryJIDStatus primary, int index)
+	{
+		int len = treeModelListeners.size();
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree, jidGroup},new int[] {index}, new Object[]{primary});
+		for (int i = 0; i < len; i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeNodesRemoved(e);
+		}
+	}
+
+	public void primaryUpdated(JIDStatusGroup jidGroup,PrimaryJIDStatus primary, int index)
+	{
+		int len = treeModelListeners.size();
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree, jidGroup},new int[] {index}, new Object[]{primary});
+		for (int i = 0; i < len; i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeNodesChanged(e);
+		}
+	}
+	
+	public void jidStatusAdded(JIDStatusGroup jidGroup,PrimaryJIDStatus primary, JIDStatus jidStatus, int index)
+	{
+		int len = treeModelListeners.size();
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree,jidGroup, primary},new int[] {index}, new Object[]{jidStatus});
+		for (int i = 0; i < len; i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeNodesInserted(e);
+		}
+	}
+
+	public void jidStatusDeleted(JIDStatusGroup jidGroup,PrimaryJIDStatus primary, JIDStatus jidStatus, int index)
+	{
+		int len = treeModelListeners.size();
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree, jidGroup ,primary},new int[] {index}, new Object[]{jidStatus});
+		for (int i = 0; i < len; i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeNodesRemoved(e);
+		}
+	}
+
+	public void jidStatussenUpdated(JIDStatusGroup jidGroup,PrimaryJIDStatus primary)
+	{
+		int len = treeModelListeners.size();
+		TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree,jidGroup, primary});
+		for (int i = 0; i < len; i++) {
+			((TreeModelListener)treeModelListeners.get(i)).treeStructureChanged(e);
+		}
+	}
+	
+	public void add()
+	{//error message
+		if(tree!=null && !tree.existGroup(I18N.gettext("main.error.Error")))
+		{//cannont use groupAdded because that needs invokeAndWait for the roster to work
+		//this doesn't work with invokeAndWait because an error fired from the eventdispatch
+		//thread causes an deadlock   
+			final JIDStatusGroup group = tree.getGroup(I18N.gettext("main.error.Error"));
+			Runnable updateAComponent = new Runnable() {
+			public void run()
+				{
+					int len = treeModelListeners.size();
+					TreeModelEvent e = new TreeModelEvent(this,new Object[] {tree},new int[] {tree.indexOfGroup(group)}, new Object[]{group});
+					for (int i = 0; i < len; i++) {
+						((TreeModelListener)treeModelListeners.get(i)).treeNodesInserted(e);
+					}
+				}
+			};
+			try{
+				SwingUtilities.invokeLater(updateAComponent);
+			}catch (Exception e)
+			{//not on error thread to prevent endless recursion 
+				System.out.println(e.getMessage()); 
+			}
+		}
+	}
+	
+	public void remove()
+	{
+		if(tree!=null && tree.existGroup(I18N.gettext("main.error.Error")))
+		{
+			JIDStatusGroup group = tree.getGroup(I18N.gettext("main.error.Error"));
+			int index = tree.indexOfGroup(group);
+			tree.removeGroup(group);
+			groupDeleted(group,index);
+		}
+	}
+
+	//-------------------------------Treemodel implementation---------------------------
+
+	public Object getRoot()
+	{
+		return tree;
+	}
+
+	public Object getChild(Object parent, int index)
+	{
+		if(parent instanceof JIDStatusTree) return tree.get(index);  //root node
+		else if (parent instanceof JIDStatusGroup) return ((JIDStatusGroup)parent).getPrimaryJIDStatus(index);
+		else return ((PrimaryJIDStatus)parent).getJIDStatus(index);
+
+	}
+
+	public int getChildCount(Object parent)
+	{
+		if(parent instanceof JIDStatusTree) return tree.getSize();  //root node
+		if(parent instanceof JIDStatusGroup) return ((JIDStatusGroup)parent).size();
+		if(parent instanceof PrimaryJIDStatus) return ((PrimaryJIDStatus)parent).size();
+		System.out.println("no parent");
+		return 0; //hoort niet voor te komen
+	}
+
+	public boolean isLeaf(Object node)
+	{
+		if(node instanceof JIDStatusTree) return false;
+		if(node instanceof JIDStatusGroup) return false;
+		if(node instanceof PrimaryJIDStatus) return false;
+		return true;
+	}
+
+	public void valueForPathChanged(TreePath path, Object newValue)
+	{
+		/**@todo: Implement this javax.swing.tree.TreeModel method*/
+		throw new java.lang.UnsupportedOperationException("Method valueForPathChanged() not yet implemented");
+	}
+
+	public int getIndexOfChild(Object parent, Object child)
+	{//kijk na
+		if(parent instanceof JIDStatusTree) return ((JIDStatusTree)parent).indexOfGroup(child);
+		if(parent instanceof JIDStatusGroup) return ((JIDStatusGroup)parent).indexOfPrimaryJIDStatus(child);
+		else return ((PrimaryJIDStatus)parent).indexOfJIDStatus(child);
+	}
+
+	/**
+	 * @see javax.swing.tree.TreeModel#addTreeModelListener(javax.swing.event.TreeModelListener)
+	 */
+	public void addTreeModelListener(TreeModelListener l)
+	{
+		treeModelListeners.add(l);
+	}
+
+	public void removeTreeModelListener(TreeModelListener l)
+	{
+		treeModelListeners.remove(l);
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/models/ListTableModel.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/models/ListTableModel.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/models/ListTableModel.java	(revision 3952)
@@ -0,0 +1,206 @@
+package nu.fw.jeti.ui.models;
+
+import javax.swing.table.*;
+import java.util.*;
+
+
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2002 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+public class ListTableModel extends AbstractTableModel
+{
+	private String[] columnNames;
+	private List plugins;
+
+    public ListTableModel(String[] columnNames,List plugins)
+    {
+		this.columnNames = columnNames;
+		this.plugins = plugins;
+    }
+
+	public void reload(List list)
+	{
+		plugins = list;
+		fireTableDataChanged();
+	}
+
+	public List getPlugins()
+	{
+		return plugins;
+	}
+
+/*
+ public synchronized void remove(int row)
+ {
+	//System.out.println(row);
+  data.remove(row);
+  fireTableDataChanged();
+ }
+
+
+	public  Object getRow(int row)
+	{
+			return data.get(row);
+	}
+
+	public void delete(Object list)
+	{
+		data.remove(data.indexOf(list));
+		fireTableDataChanged();
+	}
+
+
+ public void sort(int column, boolean asc)
+ {
+  ascending =asc;
+  if (data.isEmpty())return;
+  if ((((List)data.get(0)).get(column)).getClass() == String.class)
+  {
+   sortStrings(column);
+  }
+  else if ((((List)data.get(0)).get(column)).getClass() == Double.class)
+  {
+   sortDoubles(column);
+  }
+  else if ((((List)data.get(0)).get(column)).getClass() == Boolean.class)
+  {
+   sortBooleans(column);
+  }
+  else
+  {
+   sortIntegers(column);
+  }
+ }
+
+ public void sortIntegers(int column)
+ {
+  final int col = column;
+  Collections.sort(data, new Comparator()
+  {
+  public int compare(Object o1, Object o2)
+  {
+   int result = ((Integer)((List)o1).get(col)).compareTo(((Integer)((List)o2).get(col)));
+   if (result != 0)
+   {
+	return ascending ? result : -result;
+   }
+	return 0;
+   }
+  });
+ }
+
+ public void sortDoubles(int column)
+ {
+  final int col = column;
+  Collections.sort(data, new Comparator()
+  {
+   public int compare(Object o1, Object o2)
+   {
+	int result = ((Double)((List)o1).get(col)).compareTo(((Double)((List)o2).get(col)));
+	if (result != 0)
+	{
+	 return ascending ? result : -result;
+	}
+	return 0;
+   }
+  });
+ }
+
+ public void sortStrings(int column)
+ {
+  final int col = column;
+  Collections.sort(data, new Comparator()
+  {
+   public int compare(Object o1, Object o2)
+   {
+	int result = ((String)((List)o1).get(col)).compareTo(((String)((List)o2).get(col)));
+	if (result != 0)
+	{
+	 return ascending ? result : -result;
+	}
+	return 0;
+   }
+  });
+ }
+
+ public void sortBooleans(int column)
+ {
+  final int col = column;
+  Collections.sort(data, new Comparator()
+  {
+   public int compare(Object o1, Object o2)
+   {
+	int result =0;
+	if ((((List)o1).get(col)).equals(((List)o2).get(col))) return 0;
+	if (((Boolean)((List)o1).get(col)).booleanValue()) result=-1;
+	else result=1;
+	return ascending ? result : -result;
+   }
+  });
+ }
+ */
+
+	public boolean isCellEditable(int row, int col)
+	{
+	   if (col == 1)  return true;
+	   if (col == 3)  return true;
+	   return false;
+	}
+
+	public void setValueAt(Object value, int row, int col)
+	{
+		((Object[])plugins.get(row))[col] = value;
+		fireTableCellUpdated(row, col);
+	}
+
+	public Class getColumnClass(int col)
+	{
+		return ((Object[])plugins.get(0))[col].getClass();
+	}
+
+	public String getColumnName(int col)
+	{
+		return columnNames[col];
+	}
+
+	//required methods by abstractTableModel
+	public int getColumnCount()
+	{
+		return columnNames.length;
+	}
+
+	public int getRowCount()
+	{
+		return plugins.size();
+	}
+
+	public Object getValueAt(int row, int col)
+	{
+		return ((Object[])plugins.get(row))[col];
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/StatusMessagesWindow.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/StatusMessagesWindow.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/StatusMessagesWindow.java	(revision 3952)
@@ -0,0 +1,358 @@
+package nu.fw.jeti.ui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.JToggleButton;
+
+import nu.fw.jeti.jabber.elements.Presence;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+import nu.fw.jeti.util.Utils;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class StatusMessagesWindow extends JFrame
+{
+	//status messages editor
+//	public final static int CHAT=0;
+//	public final static int AVAILABLE=1;
+//	public final static int DND=2;
+//	public final static int AWAY=3;
+//	public final static int XA=4;
+    private JPanel jPanel1 = new JPanel();
+    private JToggleButton btnChat = new JToggleButton();
+    private JToggleButton btnAvailable = new JToggleButton();
+    private JToggleButton btnDnD = new JToggleButton();
+    private JToggleButton btnAway = new JToggleButton();
+    private JToggleButton btnXA = new JToggleButton();
+    private JPanel jPanel2 = new JPanel();
+    private JButton btnOK = new JButton();
+    private JButton btnCancel = new JButton();
+    private JPanel jPanel3 = new JPanel();
+    private JPanel jPanel4 = new JPanel();
+    private JButton btnAdd = new JButton();
+    private JButton btnRemove = new JButton();
+    private JScrollPane jScrollPane1 = new JScrollPane();
+    private JPanel[] messagePanels = new JPanel[5];
+    //private JButton btnUp = new JButton();
+	private JButton btnUp = new javax.swing.plaf.basic.BasicArrowButton(SwingConstants.SOUTH);
+	private JButton btnDown = new javax.swing.plaf.basic.BasicArrowButton(SwingConstants.NORTH);
+    //private JButton btnDown = new JButton();
+	private int selectedStatus = Presence.AVAILABLE;
+	private JTextField selectedTextField;
+
+    public StatusMessagesWindow()
+    {
+		for(int tel=0;tel<5;tel++)
+		{
+			JPanel tempPanel = new JPanel();
+		    //tempPanel.setLayout(new VerticalLayout(0,VerticalLayout.BOTH));
+			tempPanel.setLayout(new BoxLayout(tempPanel,BoxLayout.Y_AXIS));
+
+			List tempList = nu.fw.jeti.util.Preferences.getStatusMessages(tel+1);
+		    for(int i=0;i<tempList.size();i++)
+			{
+				JTextField tempTextField =new JTextField((String)tempList.get(i));
+				tempTextField.addFocusListener(new java.awt.event.FocusAdapter()
+				{
+					public void focusGained(FocusEvent e)
+					{
+						selectedTextField =(JTextField)e.getSource();
+					}
+				});
+				tempPanel.add(tempTextField);
+			}
+			messagePanels[tel] = tempPanel;
+		}
+
+        try
+        {
+            jbInit();
+			pack();
+			setLocationRelativeTo(null);
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+//	private String getStatus(int status)
+//	{
+//		switch (status)
+//		{
+//		    case 0: return "chat";
+//			case 1: return "available";
+//			case 3: return "dnd";
+//			case 2: return "away";
+//			default: return "xa";
+//		}
+//
+//	}
+
+    private void jbInit() throws Exception
+    {
+		setTitle(I18N.gettext("main.statusmessages.Manage_Status_Messages"));
+		//setIconImage(nu.fw.jeti.images.StatusIcons.getImageIcon("jeti").getImage());
+        Dimension dim = new Dimension(Integer.MAX_VALUE, 27);
+	    btnChat.setMaximumSize(dim);
+        //btnChat.setPreferredSize(new Dimension(121, 27));
+        btnChat.setText(I18N.gettext("main.presence.Free_for_Chat"));
+        btnChat.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                messageButtons_actionPerformed(e);
+            }
+        });
+        btnAvailable.setMaximumSize(new Dimension(dim));
+        //btnAvailable.setPreferredSize(new Dimension(121, 27));
+        btnAvailable.setSelected(true);
+        btnAvailable.setText(I18N.gettext("main.presence.Available"));
+        btnAvailable.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                messageButtons_actionPerformed(e);
+            }
+        });
+        btnDnD.setMaximumSize(dim);
+        //btnDnD.setPreferredSize(new Dimension(121, 27));
+        btnDnD.setText(I18N.gettext("main.presence.Do_not_Disturb"));
+        btnDnD.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                messageButtons_actionPerformed(e);
+            }
+        });
+        btnAway.setMaximumSize(dim);
+        //btnAway.setPreferredSize(new Dimension(121, 27));
+        btnAway.setText(I18N.gettext("main.presence.Away"));
+        btnAway.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                messageButtons_actionPerformed(e);
+            }
+        });
+        btnXA.setText(I18N.gettext("main.presence.Extended_Away"));
+        btnXA.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                messageButtons_actionPerformed(e);
+            }
+        });
+		//jPanel1.setLayout(new VerticalLayout());
+		jPanel1.setLayout(new BoxLayout(jPanel1,BoxLayout.Y_AXIS));
+        //btnOK.setMnemonic('O');
+        btnOK.setText(I18N.gettext("OK"));
+        getRootPane().setDefaultButton(btnOK); 
+        btnOK.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                btnOK_actionPerformed(e);
+            }
+        });
+         
+        Action cancelAction = new AbstractAction(I18N.gettext("Cancel"))
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				dispose();
+			}
+		};
+		Utils.addCancelButton(this, btnCancel, cancelAction);
+             
+        jPanel3.setLayout(new BorderLayout());
+        //btnAdd.setMnemonic('A'); 
+        //btnAdd.setText(I18N.gettext("main.statusmessages.Add"));
+        I18N.setTextAndMnemonic("main.statusmessages.Add",btnAdd);
+        btnAdd.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                btnAdd_actionPerformed(e);
+            }
+        });
+        //btnRemove.setMnemonic('R');
+        //btnRemove.setText(I18N.gettext("main.statusmessages.Remove"));
+        I18N.setTextAndMnemonic("main.statusmessages.Remove",btnRemove);
+        btnRemove.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                btnRemove_actionPerformed(e);
+            }
+        });
+
+        //btnUp.setIcon(StatusIcons.getImageIcon("arrowUp"));
+        btnUp.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                btnUp_actionPerformed(e);
+            }
+        });
+        //btnDown.setIcon(StatusIcons.getImageIcon("arrowDown"));
+        btnDown.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                btnDown_actionPerformed(e);
+            }
+        });
+        this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+        this.getContentPane().add(jPanel1, BorderLayout.WEST);
+        jPanel1.add(btnChat, null);
+        jPanel1.add(btnAvailable, null);
+        jPanel1.add(btnDnD, null);
+        jPanel1.add(btnAway, null);
+        jPanel1.add(btnXA, null);
+        this.getContentPane().add(jPanel2, BorderLayout.SOUTH);
+        jPanel2.add(btnOK, null);
+        jPanel2.add(btnCancel, null);
+        this.getContentPane().add(jPanel3, BorderLayout.CENTER);
+        jPanel3.add(jPanel4,  BorderLayout.SOUTH);
+        jPanel4.add(btnAdd, null);
+        jPanel4.add(btnRemove, null);
+        jPanel4.add(btnUp, null);
+        jPanel4.add(btnDown, null);
+        jPanel3.add(jScrollPane1, BorderLayout.CENTER);
+
+        jScrollPane1.getViewport().add(messagePanels[selectedStatus-1]);
+
+    }
+
+    void messageButtons_actionPerformed(ActionEvent e)
+    {
+		btnChat.setSelected(false);
+		btnAvailable.setSelected(false);
+		btnDnD.setSelected(false);
+		btnAway.setSelected(false);
+		btnXA.setSelected(false);
+		JToggleButton toggleButton = (JToggleButton)e.getSource();
+		toggleButton.setSelected(true);
+		String buttonText = toggleButton.getText();
+		if(buttonText.equals(I18N.gettext("main.presence.Free_for_Chat"))) selectedStatus=Presence.FREE_FOR_CHAT;
+		else if(buttonText.equals(I18N.gettext("main.presence.Available"))) selectedStatus=Presence.AVAILABLE;
+		else if(buttonText.equals(I18N.gettext("main.presence.Do_not_Disturb"))) selectedStatus=Presence.DND;
+		else if(buttonText.equals(I18N.gettext("main.presence.Away"))) selectedStatus=Presence.AWAY;
+		else selectedStatus=Presence.XA;
+		jScrollPane1.getViewport().add(messagePanels[selectedStatus-1]);
+		jScrollPane1.validate();
+
+    }
+
+    void btnAdd_actionPerformed(ActionEvent e)
+    {
+		JTextField tempTextField =new JTextField();
+			tempTextField.addFocusListener(new java.awt.event.FocusAdapter()
+			{
+				public void focusGained(FocusEvent e)
+				{
+					selectedTextField =(JTextField)e.getSource();
+				}
+			});
+		messagePanels[selectedStatus-1].add(tempTextField);
+		messagePanels[selectedStatus-1].validate();
+		jScrollPane1.validate();
+    }
+
+    void btnRemove_actionPerformed(ActionEvent e)
+    {
+		//if(messagePanels[selectedStatus].co
+		if(selectedTextField !=null)
+		{
+			messagePanels[selectedStatus-1].remove(selectedTextField);
+		    messagePanels[selectedStatus-1].validate();
+		    messagePanels[selectedStatus-1].updateUI() ;
+		}
+    }
+
+    void btnUp_actionPerformed(ActionEvent e)
+    {
+		JPanel  tempPanel =messagePanels[selectedStatus-1];
+		java.awt.Component[] components =  tempPanel.getComponents();
+		for(int tel=0;tel<components.length -1;tel++)
+		{
+		    if(components[tel].equals(selectedTextField))
+			{
+				JTextField tempTextField = (JTextField)components[tel+1];
+				String message = selectedTextField.getText();
+				selectedTextField.setText(tempTextField.getText());
+				tempTextField.setText(message);
+				selectedTextField = tempTextField;
+				tempPanel.validate();
+				return;
+			}
+		}
+	}
+
+    void btnDown_actionPerformed(ActionEvent e)
+    {
+		JPanel  tempPanel =messagePanels[selectedStatus-1];
+		java.awt.Component[] components =  tempPanel.getComponents();
+		for(int tel=1;tel<components.length;tel++)
+		{
+		    if(components[tel].equals(selectedTextField))
+			{
+				JTextField tempTextField = (JTextField)components[tel-1];
+				String message = selectedTextField.getText();
+				selectedTextField.setText(tempTextField.getText());
+				tempTextField.setText(message);
+				selectedTextField = tempTextField;
+				tempPanel.validate();
+				return;
+			}
+		}
+    }
+
+    void btnOK_actionPerformed(ActionEvent e)
+    {
+		for(int tel=0;tel<5;tel++)
+		{
+			java.awt.Component[] components = messagePanels[tel].getComponents();
+			List tempList = new ArrayList(components.length);
+			//for(int i=components.length-1;i>=0;i--)
+			for(int i=0;i<components.length;i++)
+			{
+				tempList.add(((JTextField)components[i]).getText());
+			}
+			//main.savePreference(getStatus(tel),tempList);
+			Preferences.saveStatusMessages(tel+1,tempList);
+		}
+		Preferences.saveToServer();  
+		StatusButton.reloadMessages();
+		this.dispose();
+    }
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/AddContact.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/AddContact.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/AddContact.java	(revision 3952)
@@ -0,0 +1,183 @@
+package nu.fw.jeti.ui;
+
+import java.awt.*; 
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+
+import javax.swing.*;
+
+import javax.swing.JOptionPane;
+import javax.swing.border.Border;
+import javax.swing.border.EtchedBorder;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.InfoQueryBuilder;
+import nu.fw.jeti.jabber.elements.Presence;
+import nu.fw.jeti.jabber.elements.RosterBuilder;
+import nu.fw.jeti.jabber.elements.RosterItemBuilder;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Utils;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class AddContact extends JDialog
+{
+	private Box panel1 = Box.createVerticalBox();
+	private JLabel jLabel2 = new JLabel();
+	private JTextField txtNick = new JTextField();
+	private JLabel jLabel3 = new JLabel();
+	private JComboBox cmbGroup;
+	private JPanel jPanel1 = new JPanel();
+	private JButton btnCancel = new JButton();
+	private JButton btnOK = new JButton();
+	private Backend backend;
+	
+	private JIDInput jidInput;
+	private String nameContact;
+	private String nameOrganization;
+
+	public AddContact(Backend backend)
+	{
+		super(backend.getMainFrame(), I18N.gettext("main.AddContact.Add_Contact"), false);
+		this.backend = backend;
+		jidInput = new JIDInput(backend);
+		jbInit();
+		pack();
+		setLocationRelativeTo(backend.getMainFrame());
+	}
+	
+	public AddContact(JID jid, String nick,String group, Backend backend)
+	{
+		//xmppuri
+		super(backend.getMainFrame(), I18N.gettext("main.AddContact.Add_Contact"), false);
+		this.backend = backend;
+		jidInput = new JIDInput(jid,backend);
+		txtNick.setText(nick);
+		jbInit();
+		if(group!=null)cmbGroup.setSelectedItem(group);
+		pack();
+		setLocationRelativeTo(backend.getMainFrame());
+	}
+
+	public AddContact(JID jid, JFrame frame, Backend backend, String name, String org)
+	{
+		super(frame, I18N.gettext("main.AddContact.Add_Contact"), false);
+		this.backend = backend;
+		jidInput = new JIDInput(jid,backend);
+		nameContact = name;
+		nameOrganization = org;
+		name = name.substring(name.indexOf(": ") + 2);
+		txtNick.setText(name.substring(0, name.indexOf(" ")));
+		jbInit();
+		pack();
+		setLocationRelativeTo(frame);
+	}
+
+	void jbInit()
+	{
+		String[] groups = backend.getAllGroups();
+		if (groups.length == 0) groups = new String[] { I18N.gettext("main.main.roster.Friends") };
+		getRootPane().setDefaultButton(btnOK);
+
+		txtNick.setHorizontalAlignment(SwingConstants.LEFT);
+		cmbGroup = new JComboBox(groups);
+		cmbGroup.setAlignmentX(0.0f);
+		cmbGroup.setEditable(true);
+		jPanel1.setAlignmentX(0.0f);
+		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+		panel1.add(Box.createHorizontalGlue());
+		I18N.setTextAndMnemonic("main.AddContact.Nickname",jLabel2);
+		jLabel2.setLabelFor(txtNick);
+		jLabel3.setHorizontalAlignment(SwingConstants.LEFT);
+		I18N.setTextAndMnemonic("main.AddContact.Group",jLabel3);
+		jLabel3.setLabelFor(cmbGroup);
+		Action cancelAction = new AbstractAction(I18N.gettext("Cancel"))
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				dispose();
+			}
+		};
+		Utils.addCancelButton(this, btnCancel, cancelAction);
+		
+		btnOK.setText(I18N.gettext("OK"));
+		btnOK.addActionListener(new java.awt.event.ActionListener()
+		{
+
+			public void actionPerformed(ActionEvent e)
+			{
+				if ( !txtNick.getText().trim().equals("") )
+					btnOK_actionPerformed(e);
+				else
+					JOptionPane.showMessageDialog(null, "Informe um apelido.");
+			}
+		});
+		
+		/**
+		 * 
+		 * @Author: Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+		 * @Date : 17/10/2008
+		 * @Description : Criado o JLabel Component para mostrar o nome do contato ao inves do uid.
+		 * 
+		 */
+
+		JLabel contact = new JLabel();
+		contact.setBorder(BorderFactory.createEmptyBorder(5, 0, 2, 0));
+		contact.setText(nameContact);
+		contact.setAlignmentX(0.0f);
+
+		JLabel organization = new JLabel();
+		organization.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0)); 
+		organization.setText(nameOrganization);
+		organization.setAlignmentX(0.0f);
+
+		getContentPane().add(panel1, BorderLayout.CENTER);
+		panel1.add(contact);
+		panel1.add(organization);
+		panel1.add(jLabel2);
+		panel1.add(txtNick);
+		panel1.add(jLabel3);
+		panel1.add(cmbGroup);
+		panel1.add(jPanel1);
+		jPanel1.add(btnOK);
+		
+	}
+
+	void btnOK_actionPerformed(ActionEvent e)
+	{
+		JID contact = jidInput.createJID();
+		if (contact == null) return;
+		backend.send(new Presence(contact, "subscribe"));
+		try
+		{
+			String nick = txtNick.getText();
+			if (nick.equals("")) nick = contact.getUser();
+			//still empty then use domain
+			if (nick == null) nick = contact.getDomain();
+			RosterBuilder rb = new RosterBuilder();
+			RosterItemBuilder rib = new RosterItemBuilder();
+			rib.addGroup((String) cmbGroup.getSelectedItem());
+			rib.jid = contact;
+			rib.name = nick;
+			rib.ask = "subscribe";
+			rb.addItem(rib.build());
+			InfoQueryBuilder iqb = new InfoQueryBuilder();
+			iqb.addExtension(rb.build());
+			iqb.setType("set");
+			backend.send(iqb.build());
+		} catch (InstantiationException e2)
+		{
+			e2.printStackTrace();
+		}
+		this.dispose();
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/ChatWindow.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/ChatWindow.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/ChatWindow.java	(revision 3952)
@@ -0,0 +1,426 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.ui;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.swing.*;
+import javax.swing.plaf.basic.BasicSplitPaneDivider;
+import javax.swing.plaf.basic.BasicSplitPaneUI;
+
+import nu.fw.jeti.images.StatusIcons;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.UnknownJIDStatus;
+import nu.fw.jeti.jabber.elements.*;
+import nu.fw.jeti.plugins.RosterMenuListener;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+
+import javax.swing.JOptionPane;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class ChatWindow extends JPanel
+{
+	private JPanel jPanel1 = new JPanel();
+	private ChatSplitPane chatSplitPane;
+	private JButton jButton1 = new JButton();
+	private Backend backend;
+	private String thread;
+	private JLabel lblComposing = new JLabel();
+	private JIDStatus jidStatus;
+	private JFrame window;
+	private JMenu menu;
+	private ChatWindows chatWindows;
+
+	
+	public ChatWindow(Backend backend,ChatWindows chatWindows,JFrame frame, JIDStatus jidStatus, String thread)
+	{
+		this.backend = backend;
+		this.chatWindows=chatWindows;
+		//this.tabs = tabs;
+		//toName = jidStatus.getNick();
+		//to = jidStatus.getCompleteJID();
+		//type = jidStatus.getType();
+		this.jidStatus = jidStatus;	
+		//int status = jidStatus.getShow();
+		this.thread = thread;
+		
+		makeMenu();
+		
+		if( frame==null )
+		{
+			makeWindow();
+			frame = window;
+		}
+		
+		String me = backend.getMyJID().getUser();
+
+		chatSplitPane = new ChatSplitPane(backend,jidStatus,me,thread,menu,frame);
+		
+		init();
+	}
+
+	private void init() 
+	{
+		lblComposing.setIcon(StatusIcons.getImageIcon("keys"));
+		lblComposing.setVisible(false);
+		
+		I18N.setTextAndMnemonic("Send",jButton1);
+		jButton1.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				jButton1_actionPerformed(e);
+			}
+		});
+		setLayout(new BorderLayout());
+		add(jPanel1, BorderLayout.SOUTH);
+		
+		jPanel1.setLayout(new GridLayout(1,3));
+		jPanel1.add(lblComposing);
+		jPanel1.add(jButton1);
+		jPanel1.add(Box.createHorizontalStrut(0));
+		add(chatSplitPane , BorderLayout.CENTER);
+		addFocusListener(new FocusAdapter()
+		{
+			public void focusGained(FocusEvent e)
+			{
+				chatSplitPane.requestFocusInWindow();
+			}
+		});
+		
+		/**
+		 * @Author : Rodrigo Souza - rodsouza@celepar.pr.gov.br
+		 * @Date : 31/10/2008
+		 * @Brief : Comentado trecho abaixo, pois o mesmo dimensionava o componente splitPane 
+		 * deixando a foto (avatar) pequeno.
+		 */
+		
+		//chatSplitPane.setDividerLocation(Preferences.getInteger("jeti","chatDivider",170));
+		
+		if( window!=null )
+			window.setVisible(true);
+	}
+	
+	private void makeMenu()
+	{
+		/**
+		 * 
+		 * @Author : Alexandre Correia -alexandrecorreia@celepar.pr.gov.br 
+		 * @Description : Desabilitado no menu o item Local Data e Local Versao
+		 *  
+		 */
+		
+		menu = new JMenu();
+		I18N.setTextAndMnemonic("main.chat.menu", menu);
+		
+		/*JMenuItem item = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Local_Time", item, true);
+		item.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				//backend.send(new InfoQuery(jidStatus.getCompleteJID(), "get", new IQTime()));
+
+			}
+		});
+		menu.add(item);
+
+		item = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Local_Version", item,true);
+		item.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				//backend.send(new InfoQuery(jidStatus.getCompleteJID(), "get", new IQVersion()));
+			}
+		});
+		
+		menu.add(item);
+
+		Map menuItems = backend.getMain().getRosterMenuItems();
+		if (menuItems != null)
+		{
+			for (Iterator i = menuItems.entrySet().iterator(); i.hasNext();)
+			{
+				Map.Entry entry = (Map.Entry) i.next();
+				item = new JMenuItem((String) entry.getKey());
+				final RosterMenuListener listener = (RosterMenuListener) entry.getValue();
+				item.addActionListener(new java.awt.event.ActionListener()
+				{
+					public void actionPerformed(ActionEvent e)
+					{
+						listener.actionPerformed(jidStatus, null);
+					}
+				});
+				menu.add(item);
+			}
+		}*/
+	}
+
+	public void addSplitBarListener(ComponentListener listener)
+	{
+		BasicSplitPaneDivider divider = 
+			((BasicSplitPaneUI)chatSplitPane.getUI()).getDivider();
+		divider.addComponentListener(listener);
+	}
+	
+	public void removeSplitBarListener(ComponentListener listener)
+	{
+		BasicSplitPaneDivider divider = 
+			((BasicSplitPaneUI)chatSplitPane.getUI()).getDivider();
+		divider.removeComponentListener(listener);
+	}
+	
+	public void setDividerLocation(int location)
+	{
+		chatSplitPane.setDividerLocation(location);
+	}
+	
+	public void setParentFrame(JFrame frame)
+	{
+		chatSplitPane.setParentFrame(frame);
+	}
+	
+	private void makeWindow()
+	{
+		window = new JFrame();
+		setIcon(StatusIcons.getStatusIcon(jidStatus));
+		if(jidStatus instanceof UnknownJIDStatus)setTitle(jidStatus.getCompleteJID().toString());
+		else setTitle(jidStatus.getNick());
+		window.addWindowListener(new WindowAdapter()
+		{
+			public void windowClosing(WindowEvent e) 
+			{
+				Preferences.putInteger("jeti","chatPosX",window.getX());
+				Preferences.putInteger("jeti","chatPosY",window.getY());
+				Preferences.putInteger("jeti","chatHeight",window.getHeight());
+				Preferences.putInteger("jeti","chatWidth",window.getWidth());
+				Preferences.putInteger("jeti","chatDivider",chatSplitPane.getDividerLocation());
+				chatWindows.removeChatWindow(ChatWindow.this);
+				exit();
+			}
+		});
+		
+		/**
+		 * @Author : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+		 * @Date : 03/11/2008
+		 * @Description : Funcao abaixo comentada, tira o foco das outras janelas.
+		 */
+		
+		/*window.addWindowFocusListener(new WindowAdapter()
+		{
+			public void windowGainedFocus(WindowEvent e)
+			{
+				ChatWindow.this.requestFocusInWindow();
+			}
+		});*/
+
+		JMenuBar menuBar = new JMenuBar();
+		menuBar.add(menu);
+		final JMenuItem item = new JMenuItem();
+		I18N.setTextAndMnemonic("main.chat.menu.Convert_to_tab", item,true);
+		item.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				menu.remove(item);
+				chatWindows.convertToTab(ChatWindow.this,jidStatus);
+				window.dispose();
+			}
+		});
+		menu.add(item);
+		window.setJMenuBar(menuBar);
+		window.setContentPane(this);
+		window.setSize(Preferences.getInteger("jeti","chatWidth",300),Preferences.getInteger("jeti","chatHeight",350));
+	}
+		
+	public void changeToWindow()
+	{
+		makeWindow();
+		chatSplitPane.setDividerLocation(Preferences.getInteger("jeti","chatDivider",200));
+		chatSplitPane.setParentFrame(window);
+		window.setVisible(true);
+	}
+	
+	public void setLocationOnScreen(int posX,int posY)
+	{
+		window.setLocation(posX,posY);
+	}
+	
+	public void setLocationOnScreen(ChatWindow oldWindow)
+	{
+		if(oldWindow.window!=null)
+		{
+			window.setLocation(oldWindow.window.getX()+50
+					,oldWindow.window.getY()+50);
+		}
+	}	
+	public void exit()
+	{
+		chatSplitPane.close();
+		if(window!=null)window.dispose();
+	}
+
+	
+//	public boolean compareJID(JID jid)
+//	{
+//		return jidStatus.getJID().equals(jid);
+//	}
+
+	public String getThread()
+	{
+		return thread;
+	}
+
+	public void appendMessage(Message message)
+	{
+		String name;
+		
+		if (jidStatus.getJID().equals(message.getFrom()))
+			name = jidStatus.getNick();
+
+		else
+			name = message.getFrom().getUser();
+
+		Runnable updateAComponent = new Runnable()
+		{
+			public void run()
+			{
+				//lblComposing.setText("");
+				lblComposing.setVisible(false);
+			}
+		};
+		SwingUtilities.invokeLater(updateAComponent);
+		
+		chatSplitPane.appendMessage(message,name);
+	}
+
+	public void appendPresenceChange(final Presence presence)
+	{
+		Runnable updateAComponent = new Runnable()
+		{
+			public void run()
+			{
+				lblComposing.setVisible(false);
+				int show = presence.getShow();
+				String status = presence.getStatus();
+				setIcon(StatusIcons.getStatusIcon(jidStatus));
+				
+				String text;
+				if (show == Presence.UNAVAILABLE)
+				{
+					text = " " + I18N.gettext("main.chat.is_currently_offline");
+					setTitle(MessageFormat.format(I18N.gettext("main.chat.{0}_(offline)"),new Object[]{jidStatus.getNick()}));
+				}
+				else
+				{
+					text = " " + I18N.gettext("main.chat.changed_status_to") + " " + Presence.toLongShow(show);
+					if (status != null)
+					{
+						text += '\n' + I18N.gettext("main.chat.with_status_message") + " " + status;
+					}
+					if(jidStatus instanceof UnknownJIDStatus)setTitle(jidStatus.getCompleteJID().toString());
+					else setTitle(jidStatus.getNick());
+				}
+				chatSplitPane.appendSystemMessage(jidStatus.getNick() + text);
+			}
+		};
+		SwingUtilities.invokeLater(updateAComponent);
+	}
+	
+	public JIDStatus getJIDStatus()
+	{
+		return jidStatus;
+	}
+
+	public void composingID(String id)
+	{
+		if (id == null)id = "";
+		chatSplitPane.composingID(id);
+	}
+
+	public void composing(String type)
+	{
+		if (type != null)
+		{
+			Runnable updateAComponent = new Runnable()
+			{
+				public void run()
+				{
+					lblComposing.setVisible(true);
+				}
+			};
+			SwingUtilities.invokeLater(updateAComponent);
+		}
+		else
+		{
+			Runnable updateAComponent = new Runnable()
+			{
+				public void run()
+				{
+					lblComposing.setVisible(false);
+				}
+			};
+			SwingUtilities.invokeLater(updateAComponent);
+		}
+	}
+
+	void jButton1_actionPerformed(ActionEvent e)
+	{
+		chatSplitPane.send(); 
+	}
+		
+	private void setTitle(String title)
+	{
+		if(window!=null)window.setTitle(title);
+		else chatWindows.setTitleAt(this,title);
+	}
+	
+	private void setIcon(ImageIcon icon)
+	{
+		if(window!=null) window.setIconImage(icon.getImage());
+		else chatWindows.setIconAt(this,icon);
+	}
+	
+	public JMenu getMenu()
+	{
+		return menu;
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/ChangePassword.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/ChangePassword.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/ChangePassword.java	(revision 3952)
@@ -0,0 +1,144 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  created 24-4-2005
+ */
+
+package nu.fw.jeti.ui;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+
+import javax.swing.*;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.IQRegister;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Utils;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class ChangePassword extends JDialog
+{
+	//JPanel panel1 = new JPanel();
+	private Box panel1 = Box.createVerticalBox();
+	private JLabel jLabel1 = new JLabel();
+	private JPasswordField txtOldPassword = new JPasswordField();
+	private JLabel jLabel2 = new JLabel();
+	private JPasswordField txtNewPassword1 = new JPasswordField();
+	private JPasswordField txtNewPassword2 = new JPasswordField();
+	private JLabel jLabel3 = new JLabel();
+	private JPanel jPanel1 = new JPanel();
+	private JButton btnCancel = new JButton();
+	private JButton btnOK = new JButton();
+	private Backend backend;
+	
+
+	public ChangePassword(Backend backend)
+	{
+		super(backend.getMainFrame(), I18N.gettext("main.ChangePassword.Change_Password"), false);
+		this.backend = backend;
+		jbInit();
+		pack();
+		setLocationRelativeTo(backend.getMainFrame());
+	}
+
+	
+	void jbInit()
+	{
+		I18N.setTextAndMnemonic("main.ChangePassword.Old_Password",jLabel1);
+		
+		txtOldPassword.setHorizontalAlignment(SwingConstants.LEADING);
+		txtNewPassword1.setHorizontalAlignment(SwingConstants.LEFT);
+		txtNewPassword2.setHorizontalAlignment(SwingConstants.LEFT);
+		
+		jPanel1.setAlignmentX(0.0f);
+		this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+		panel1.add(Box.createHorizontalGlue());
+		
+		I18N.setTextAndMnemonic("main.ChangePassword.New_Password",jLabel2);
+		jLabel2.setLabelFor(txtNewPassword1);
+		jLabel3.setHorizontalAlignment(SwingConstants.LEFT);
+
+		I18N.setTextAndMnemonic("main.ChangePassword.New_Password",jLabel3);
+		jLabel3.setLabelFor(txtNewPassword2);
+		jLabel1.setHorizontalAlignment(SwingConstants.LEFT);
+			
+		Action cancelAction = new AbstractAction(I18N.gettext("Cancel"))
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				dispose();
+			}
+		};
+		Utils.addCancelButton(this, btnCancel, cancelAction);
+	
+		btnOK.setText(I18N.gettext("OK"));
+		btnOK.addActionListener(new java.awt.event.ActionListener()
+		{
+
+			public void actionPerformed(ActionEvent e)
+			{
+				btnOK_actionPerformed(e);
+			}
+		});
+		getContentPane().add(panel1, BorderLayout.CENTER);
+		panel1.add(jLabel1);
+		panel1.add(txtOldPassword);
+		panel1.add(jLabel2);
+		panel1.add(txtNewPassword1);
+		panel1.add(jLabel3);
+		panel1.add(txtNewPassword2);
+		panel1.add(jPanel1);
+		jPanel1.add(btnOK);
+		jPanel1.add(btnCancel);
+	}
+
+	void btnOK_actionPerformed(ActionEvent e)
+	{
+		if(txtNewPassword1.getText().equals(" ") 
+				|| !txtNewPassword1.getText().equals(txtNewPassword2.getText()))
+		{
+			JOptionPane.showMessageDialog(this, I18N.gettext("main.ChangePassword.Password_not_equal"));
+		}
+		else
+		{
+			if(backend.isPasswordValid(txtOldPassword.getText()))
+			{
+				//TODO add iq listener that changes password in profile if set
+				backend.send(new InfoQuery(new JID(backend.getMyJID().getDomain())
+						,"set",new IQRegister(
+						backend.getMyJID().getUser(),txtNewPassword1.getText())));
+				this.dispose();
+			}
+		}
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/LogWindow.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/LogWindow.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/LogWindow.java	(revision 3952)
@@ -0,0 +1,242 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/im me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.ui;
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.*;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.Packet;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Log;
+import nu.fw.jeti.util.Utils;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class LogWindow extends JFrame
+{
+
+	private JPanel jPanel1 = new JPanel();
+	private JButton btnClose = new JButton();
+	private JButton btnRefresh = new JButton();
+	private JTabbedPane jTabbedPane1 = new JTabbedPane();
+	private JTextArea txtXML = new JTextArea();
+	private JScrollPane jScrollPane1 = new JScrollPane();
+	private JTextArea txtXMLErrors = new JTextArea();
+	private JScrollPane jScrollPane2 = new JScrollPane();
+	private JTextArea txtErrors = new JTextArea();
+	private JScrollPane jScrollPane3 = new JScrollPane();
+	private int xml;
+	private int xmlErrors;
+	private int errors;
+	private Backend backend;
+
+	public LogWindow(Backend backend)
+	{
+		this.backend = backend;
+		try
+		{
+			jbInit();
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+		}
+		txtXML.setText(I18N.gettext("main.log.Please_wait_loading_log"));
+		Thread t= new Thread()
+		{
+			public void run()
+			{
+				//try{
+					StringBuffer buffer = new StringBuffer();
+					List temp = Log.getXML();
+					for (Iterator i = temp.iterator(); i.hasNext();)
+					{
+						buffer.append(i.next());
+						buffer.append("\n");
+				//		sleep(2);
+					}
+					txtXML.setText(buffer.toString());
+					buffer = new StringBuffer();
+					xml = temp.size();
+					temp = Log.getXMLErrors();
+					for (Iterator i = temp.iterator(); i.hasNext();)
+					{
+						buffer.append(i.next());
+						buffer.append("\n");
+					//	sleep(2);
+					}
+					txtXMLErrors.setText(buffer.toString());
+					xmlErrors = temp.size();
+					temp = Log.getErrors();
+					buffer = new StringBuffer();
+					for (Iterator i = temp.iterator(); i.hasNext();)
+					{
+						buffer.append(i.next());
+						buffer.append("\n");
+					//	sleep(2);
+					}
+					txtErrors.setText(buffer.toString());
+					errors = temp.size();
+				//}catch(InterruptedException e){}
+			}
+		};
+		t.setPriority(Thread.NORM_PRIORITY);
+		t.start();
+	}
+	
+	private void jbInit() throws Exception
+	{
+		jTabbedPane1.setBorder(null);
+		//setIconImage(nu.fw.jeti.images.StatusIcons.getImageIcon("jeti").getImage());
+		setTitle(I18N.gettext("main.log.JETI_Log"));
+		btnClose.setText(I18N.gettext("Close"));
+		Action cancelAction = new AbstractAction(I18N.gettext("Close"))
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				dispose();
+			}
+		};
+		Utils.addCancelButton(this, btnClose, cancelAction);		
+		//btnRefresh.setText(I18N.gettext("main.log.Refresh"));  
+		I18N.setTextAndMnemonic("main.log.Refresh",btnRefresh);
+		btnRefresh.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				btnRefresh_actionPerformed(e);
+			}
+		});
+		JButton btnClear = new JButton();
+		I18N.setTextAndMnemonic("main.log.Clear_Log",btnClear);
+		btnClear.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				Log.clear();
+				xml=0;
+				txtXML.setText("");
+				btnRefresh_actionPerformed(e);
+			}
+		});
+		//JButton btnSend = new JButton(I18N.gettext("main.log.Send_XML"));
+		JButton btnSend = new JButton();
+		I18N.setTextAndMnemonic("main.log.Send_XML",btnSend);
+		this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+		this.getContentPane().add(jPanel1, BorderLayout.SOUTH);
+		//jPanel1.add(btnSend);
+		jPanel1.add(btnClear, null);
+		jPanel1.add(btnRefresh, null);
+		jPanel1.add(btnClose, null);
+		//send xml window
+		JPanel pnlSendXML = new JPanel(new BorderLayout());
+		pnlSendXML.add(new JLabel(I18N.gettext("main.log.Send_XML_to_the_Jabber_server,_use_this_at_your_own_risk")), BorderLayout.NORTH);
+		final JTextArea txtXMLSend = new JTextArea();
+		//txtXMLSend.
+		pnlSendXML.add(new JScrollPane(txtXMLSend),BorderLayout.CENTER);
+		btnSend.addActionListener(new ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				if(txtXMLSend.getText().indexOf("password")!=-1)
+				{
+					JOptionPane.showMessageDialog(LogWindow.this,I18N.gettext("main.log.For_security_reasons_it_is_not_possible_to_change_passwords,_please_use_the_jeti_functions_for_that"));
+				}
+				else if(txtXMLSend.getText().indexOf("remove")!=-1)
+				{
+					JOptionPane.showMessageDialog(LogWindow.this,I18N.gettext("main.log.For_security_reasons_it_is_not_possible_to_remove_accounts,_please_use_the_jeti_functions_for_that"));
+				}
+				else if(!txtXMLSend.getText().equals(""))
+				{
+					backend.send(new Packet()
+					{
+						public void appendToXML(StringBuffer xml)
+						{
+							xml.append(txtXMLSend.getText());
+							txtXMLSend.setText("");
+						}
+					});
+				}
+			}
+		});
+		pnlSendXML.add(btnSend,BorderLayout.SOUTH);
+		getContentPane().add(jTabbedPane1, BorderLayout.CENTER);
+		//TODO mnemonics on tab
+		jTabbedPane1.add(jScrollPane1, I18N.gettext("main.log.XML"));
+		jTabbedPane1.add(jScrollPane2, I18N.gettext("main.log.XML_Errors"));
+		jTabbedPane1.add(jScrollPane3, I18N.gettext("main.log.Errors"));
+		jTabbedPane1.add(pnlSendXML, I18N.gettext("main.log.Send_XML"));
+		jScrollPane3.getViewport().add(txtErrors, null);
+		jScrollPane2.getViewport().add(txtXMLErrors, null);
+		jScrollPane1.getViewport().add(txtXML, null);
+		setSize(400, 400);
+		setLocationRelativeTo(null);
+	}
+
+	void btnRefresh_actionPerformed(ActionEvent e)
+	{
+		Thread t = new Thread()
+		{
+			public void run()
+			{
+				List temp = Log.getXML();
+				StringBuffer buffer = new StringBuffer();
+				for (Iterator i = temp.listIterator(xml); i.hasNext();)
+				{
+					buffer.append(i.next());
+					buffer.append("\n");
+				}
+				xml = temp.size();
+				txtXML.append(buffer.toString());
+				temp = Log.getXMLErrors();
+				for (Iterator i = temp.listIterator(xmlErrors); i.hasNext();)
+				{
+					txtXMLErrors.append((String) i.next() + "\n");
+				}
+				xmlErrors = temp.size();
+				temp = Log.getErrors();
+				for (Iterator i = temp.listIterator(errors); i.hasNext();)
+				{
+					txtErrors.append((String) i.next() + "\n");
+				}
+				errors = temp.size();
+			}
+		};
+		t.setPriority(Thread.NORM_PRIORITY);
+		t.start();
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/GroupDialog.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/GroupDialog.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/GroupDialog.java	(revision 3952)
@@ -0,0 +1,195 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/im me at jeti@jabberstudio.org
+ */
+
+package nu.fw.jeti.ui;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.text.MessageFormat;
+import java.util.List;
+
+import javax.swing.*;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.IQXRoster;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.RosterItem;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Utils;
+
+/**
+ *  
+ * @author E.S. de Boer
+ */
+
+public class GroupDialog extends JDialog
+{
+	private JPanel btnPanel = new JPanel();
+	private JLabel lbltekst = new JLabel();
+	private JComboBox cmbGroup;
+	private JButton btnCancel = new JButton();
+	private JButton btnOK = new JButton();
+	private String[] groups;
+	//private String operation;
+	private Backend backend;
+	private String currentGroup;
+	private JIDStatus jidStatus;
+
+	public GroupDialog(JIDStatus jidStatus, Backend backend)
+	{
+		super(backend.getMainFrame(), I18N.gettext("main.main.rostermenu.Add_to_Group"), false);
+		groups = backend.getAllGroups();
+		String[] temp = new String[groups.length - 1];
+		int i = 0;
+		for (int tel = 0; tel < groups.length; tel++)
+		{//remove group wich contains jid
+			if (!jidStatus.isGroupPresent(groups[tel]))
+			{
+				temp[i] = groups[tel];
+				i++;
+			}
+		}
+		groups = new String[i];
+		System.arraycopy(temp, 0, groups, 0, i);
+		init(jidStatus, backend);
+		lbltekst.setText(MessageFormat.format(I18N.gettext("main.popup.Add_{0}_to"), new Object[] { jidStatus.getNick()}));
+		pack();
+		setLocationRelativeTo(null);
+	}
+
+	private void init(JIDStatus jidStatus, Backend backend)
+	{
+		this.backend = backend;
+		this.jidStatus = jidStatus;
+		try
+		{
+			jbInit();
+		} catch (Exception ex)
+		{
+			ex.printStackTrace();
+		}
+	}
+
+	public GroupDialog(String group, JIDStatus jidStatus, Backend backend)
+	{
+		super(backend.getMainFrame(), I18N.gettext("main.main.rostermenu.Change_Group"), false);
+		groups = backend.getAllGroups();
+		String[] temp = new String[groups.length - 1];
+		int i = 0;
+		for (int tel = 0; tel < groups.length; tel++)
+		{//remove group wich contains jid
+			if (!jidStatus.isGroupPresent(groups[tel]))
+			{
+				temp[i] = groups[tel];
+				i++;
+			}
+		}
+		groups = new String[i];
+		System.arraycopy(temp, 0, groups, 0, i);
+
+		currentGroup = group;
+		init(jidStatus, backend);
+		lbltekst.setText(MessageFormat.format(I18N.gettext("main.popup.Change_{0}_from_{1}_to"), new Object[] { jidStatus.getNick(),
+				group}));
+		pack();
+		setLocationRelativeTo(null);
+	}
+
+	void jbInit() throws Exception
+	{
+		this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+		getRootPane().setDefaultButton(btnOK);
+
+		cmbGroup = new JComboBox(groups);
+		cmbGroup.setAlignmentX((float) 0.0);
+		cmbGroup.setEditable(true);
+
+		Action cancelAction = new AbstractAction(I18N.gettext("Cancel"))
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				dispose();
+			}
+		};
+		Utils.addCancelButton(this, btnCancel, cancelAction);
+
+		btnOK.setText(I18N.gettext("OK"));
+		btnOK.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				btnOK_actionPerformed(e);
+			}
+		});
+
+        getContentPane().setLayout(new GridBagLayout());
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridwidth = GridBagConstraints.REMAINDER;
+        c.anchor = GridBagConstraints.LINE_START;
+        c.fill = GridBagConstraints.BOTH;
+        c.insets = new Insets(3, 3, 0, 3);
+        c.weightx = 1.0;
+
+        getContentPane().add(lbltekst, c);
+        getContentPane().add(cmbGroup, c);
+        btnPanel.add(btnOK, null);
+        btnPanel.add(btnCancel, null);
+        getContentPane().add(btnPanel, c);
+	}
+
+	void btnOK_actionPerformed(ActionEvent e)
+	{
+		String group = (String) cmbGroup.getSelectedItem();
+		if (!group.equals(" "))
+		{
+			List groups = jidStatus.getGroupsCopy();
+			if (currentGroup != null)
+			{
+				//backend.changeGroup(jidStatus,currentGroup,group);
+				groups.remove(currentGroup);
+				groups.add(group);
+				IQXRoster roster = new IQXRoster(new RosterItem(jidStatus.getJID(), jidStatus.getNick(), null, null, groups));
+				backend.send(new InfoQuery("set", roster));
+			} else
+			{
+				if (!jidStatus.isGroupPresent(group))
+				{
+					groups.add(group);
+					IQXRoster roster = new IQXRoster(new RosterItem(jidStatus.getJID(), jidStatus.getNick(), null, null, groups));
+					backend.send(new InfoQuery("set", roster));
+					//backend.addGroup(jidStatus,group);
+				}
+			}
+		}
+		this.dispose();
+	}
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/RegisterWindow.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/RegisterWindow.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/RegisterWindow.java	(revision 3952)
@@ -0,0 +1,400 @@
+package nu.fw.jeti.ui;
+
+import java.awt.BorderLayout;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.WindowEvent;
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.swing.*;
+
+import nu.fw.jeti.backend.NewAccount;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.XDataCallback;
+import nu.fw.jeti.jabber.XDataPanel;
+import nu.fw.jeti.jabber.elements.IQRegister;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.XData;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Utils;
+
+//TODO add xdata to register constructor, update languages
+/**
+ * Title:        im
+ * Description:
+ * Copyright:    Copyright (c) 2001
+ * Company:
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class RegisterWindow extends JFrame implements XDataCallback 
+{
+    private JPanel jPanel1 = new JPanel();
+    private JPanel jPanel2 = new JPanel();
+    private JButton btnOK = new JButton();
+    private JButton btnCancel = new JButton();
+	private JPasswordField txtPassword;
+	private JPanel pnlPassword = new JPanel();
+    private JLabel jLabel3 = new JLabel();
+	private JPanel pnlUsername = new JPanel();
+    private JLabel jLabel2 = new JLabel();
+	private JTextField txtUsername;
+	private JPanel pnlInstructions = new JPanel();
+    private JTextArea txtInstructions = new JTextArea();
+    private JLabel jLabel1 = new JLabel();
+	private String key;
+	private Map map;
+	private Backend backend;
+	private NewAccount backend2;
+	private boolean newAccount;
+	private JID from;
+	private JCheckBox checkBox = new JCheckBox(I18N.gettext("Remove"));
+	//private String id;
+	public static final int STRUT_SIZE = 5;
+
+
+	public RegisterWindow(NewAccount backend,IQRegister register,String username,String password)
+    {
+    	newAccount = true;
+		Map forms = register.getFields();
+		//setIconImage(nu.fw.jeti.images.StatusIcons.getImageIcon("jeti").getImage());
+		setTitle(I18N.gettext("main.register.Register"));
+		//I18N.setTextAndMnemonic("Remove",checkBox);
+		//checkBox.setText("Remove");
+		map = new LinkedHashMap(15);
+		this.backend2 = backend;
+		key =(String) forms.remove("key");
+        try
+        {
+            if(forms.containsKey("instructions"))
+			{
+				String instructions = (String)forms.remove("instructions");
+			    txtInstructions.setText(instructions);
+				txtInstructions.setEnabled(false);
+			    jLabel1.setText(I18N.gettext("main.register.Instructions"));
+				jLabel1.setPreferredSize(new Dimension(80, 17));
+				pnlInstructions.setLayout(new BorderLayout());
+				pnlInstructions.add(jLabel1, BorderLayout.WEST  );
+				pnlInstructions.add(txtInstructions, BorderLayout.CENTER);
+				/*
+				java.awt.FontMetrics font = getFontMetrics((Font)UIManager.get("TextArea.font"));
+				int naamLengte = (font.stringWidth(message));
+				if(naamLengte > 400)
+				{//message is to long enable autowrap
+					font.getHeight();
+					txtInstructions.setPreferredSize(new Dimension(400    ))
+					txtInstructions.setLineWrap(true);
+					return;
+				}
+				*/
+
+				jPanel2.add(Box.createVerticalStrut(STRUT_SIZE));
+				jPanel2.add(pnlInstructions);
+
+			}
+			if(forms.containsKey("password"))
+			{
+				//txtPassword = new JPasswordField((String)forms.remove("password"));
+				forms.remove("password");
+				txtPassword = new JPasswordField(password);
+			    //txtPassword.setText();
+			}
+			if(forms.containsKey("username"))
+			{
+				//txtUsername = new JTextField((String)forms.remove("username"));
+				forms.remove("username");
+				txtUsername = new JTextField(username);
+			}
+			jbInit();
+			if(forms.containsKey("registered"))
+			{
+				forms.remove("registered");
+				checkBox.setEnabled(true);
+			}
+			for (Iterator i=forms.entrySet().iterator(); i.hasNext(); ) {
+			    Map.Entry e = (Map.Entry) i.next();
+			    //System.out.println(e.getKey() + ": " + e.getValue());
+
+				JTextField text;
+				//if(((String)e.getKey()).equals("password")) text = new JPasswordField();
+			    text = new JTextField();
+
+				//text.setPreferredSize(new Dimension(200, 21));
+				text.setText((String)e.getValue());
+
+				JPanel panel = new JPanel();
+				panel.setLayout(new BorderLayout());
+				map.put(e.getKey(),text);
+				JLabel label = new JLabel((String)e.getKey());
+				label.setPreferredSize(new Dimension(80, 17));
+				panel.add(label, BorderLayout.WEST);
+				panel.add(text,BorderLayout.CENTER);
+				jPanel2.add(Box.createVerticalStrut(STRUT_SIZE));
+				jPanel2.add(panel);
+		    }
+			jPanel2.add(checkBox);
+	    }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+        }
+		pack();
+		setLocationRelativeTo(null);
+	}
+
+	//TODO remove id
+	public RegisterWindow(Backend backend,IQRegister register,JID jid,String id)
+	{
+		from = jid;
+		//this.id = id;
+		this.backend = backend;
+		XData xData =register.getXData(); 
+		if(xData !=null)
+		{
+			//setIconImage(nu.fw.jeti.images.StatusIcons.getImageIcon("jeti").getImage());
+			if(xData.getTitle() !=null) setTitle(xData.getTitle());
+			else setTitle(I18N.gettext("main.register.Register"));
+			setContentPane(new XDataPanel(xData,this));
+			this.addWindowListener(new java.awt.event.WindowAdapter()
+			{
+				public void windowClosing(WindowEvent e)
+				{
+					cancelForm();
+				}
+			});
+			pack();
+			setLocationRelativeTo(null);
+			setVisible(true);  
+		}
+		else if(register.getFields()!=null)
+		{
+			oldRegister(register);
+		}
+	}
+	
+	private void oldRegister(IQRegister register)
+	{
+		newAccount = false;
+		Map forms = register.getFields();
+		//setIconImage(nu.fw.jeti.images.StatusIcons.getImageIcon("jeti").getImage());
+		setTitle(I18N.gettext("main.register.Register"));
+		map = new LinkedHashMap(15);
+
+		key =(String) forms.remove("key");
+
+		try
+		{
+			if(forms.containsKey("instructions"))
+			{
+				String instructions = (String)forms.remove("instructions");
+				txtInstructions.setText(instructions);
+				txtInstructions.setEnabled(false);
+				jLabel1.setText(I18N.gettext("main.register.Instructions"));
+				jLabel1.setPreferredSize(new Dimension(80, 17));
+				pnlInstructions.setLayout(new BorderLayout());
+				pnlInstructions.add(jLabel1, BorderLayout.WEST  );
+				pnlInstructions.add(txtInstructions, BorderLayout.CENTER);
+				/*
+				java.awt.FontMetrics font = getFontMetrics((Font)UIManager.get("TextArea.font"));
+				int naamLengte = (font.stringWidth(message));
+				if(naamLengte > 400)
+				{//message is to long enable autowrap
+					font.getHeight();
+					txtInstructions.setPreferredSize(new Dimension(400    ))
+					txtInstructions.setLineWrap(true);
+					return;
+				}
+				*/
+
+				jPanel2.add(Box.createVerticalStrut(STRUT_SIZE));
+				jPanel2.add(pnlInstructions);
+
+			}
+			if(forms.containsKey("password"))
+			{
+				txtPassword = new JPasswordField((String)forms.remove("password"));
+				//txtPassword.setText();
+			}
+			if(forms.containsKey("username"))
+			{
+				txtUsername = new JTextField((String)forms.remove("username"));
+			}
+			jbInit();
+			if(forms.containsKey("registered"))
+			{
+				forms.remove("registered");
+				checkBox.setEnabled(true);
+			}
+			for (Iterator i=forms.entrySet().iterator(); i.hasNext(); ) {
+				Map.Entry e = (Map.Entry) i.next();
+			//	System.out.println(e.getKey() + ": " + e.getValue());
+
+				JTextField text;
+				//if(((String)e.getKey()).equals("password")) text = new JPasswordField();
+				text = new JTextField();
+
+				//text.setPreferredSize(new Dimension(200, 21));
+				text.setText((String)e.getValue());
+
+				JPanel panel = new JPanel();
+				panel.setLayout(new BorderLayout());
+				map.put(e.getKey(),text);
+				JLabel label = new JLabel((String)e.getKey());
+				label.setPreferredSize(new Dimension(80, 17));
+				panel.add(label, BorderLayout.WEST);
+				panel.add(text,BorderLayout.CENTER);
+				jPanel2.add(Box.createVerticalStrut(STRUT_SIZE));
+				jPanel2.add(panel);
+			}
+			jPanel2.add(checkBox);
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+		pack();
+		setLocationRelativeTo(null);
+		setVisible(true);
+	}
+
+
+    private void jbInit() throws Exception
+    {
+		getRootPane().setDefaultButton(btnOK);
+		I18N.setTextAndMnemonic("OK",btnOK);
+        btnOK.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                btnOK_actionPerformed(e);
+            }
+        });
+        Action cancelAction = new AbstractAction(I18N.gettext("Cancel"))
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				btnCancel_actionPerformed(e);
+			}
+		};
+        btnCancel.setAction(cancelAction);
+        Utils.addCancelButton(this, btnCancel, cancelAction);
+        
+        //txtInstructions.setPreferredSize(new Dimension(200, 17));
+		checkBox.setEnabled(false);
+
+        //txtUsername.setPreferredSize(new Dimension(200, 21));
+        //txtPassword.setPreferredSize(new Dimension(200, 21));
+
+		if(txtUsername!=null)
+		{
+			jLabel2.setPreferredSize(new Dimension(80, 17));
+			jLabel2.setText(I18N.gettext("main.register.Username"));
+			pnlUsername.setLayout(new BorderLayout());
+			pnlUsername.add(jLabel2, BorderLayout.WEST);
+			pnlUsername.add(txtUsername, BorderLayout.CENTER);
+			jPanel2.add(Box.createVerticalStrut(STRUT_SIZE));
+			jPanel2.add(pnlUsername);
+		}
+		if(txtPassword!=null)
+		{
+			jLabel3.setPreferredSize(new Dimension(80, 17));
+			jLabel3.setText(I18N.gettext("main.register.Password"));
+			pnlPassword.setLayout(new BorderLayout());
+			pnlPassword.add(jLabel3, BorderLayout.WEST);
+			pnlPassword.add(txtPassword, BorderLayout.CENTER);
+			jPanel2.add(Box.createVerticalStrut(STRUT_SIZE));
+			jPanel2.add(pnlPassword);
+		}
+
+        //jPanel2.setLayout(new VerticalLayout(5,VerticalLayout.BOTH));
+		jPanel2.setLayout(new BoxLayout(jPanel2,BoxLayout.Y_AXIS));
+		this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+        this.getContentPane().add(jPanel2, BorderLayout.CENTER);
+		jPanel1.add(btnOK, null);
+        jPanel1.add(btnCancel, null);
+        this.getContentPane().add(jPanel1,  BorderLayout.SOUTH);
+		this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+    }
+
+    void btnCancel_actionPerformed(ActionEvent e)
+    {
+		this.dispose();
+    }
+
+    void btnOK_actionPerformed(ActionEvent e)
+    {
+			setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+			btnOK.setEnabled(false);
+			btnCancel.setEnabled(false);
+
+			LinkedHashMap tmap = new LinkedHashMap(16);
+			if(txtUsername!=null)
+			{
+				tmap.put("username",txtUsername.getText().trim());
+			}
+			if(txtPassword!=null)
+			{
+				tmap.put("password",txtPassword.getText());
+			}
+			for (Iterator i=map.entrySet().iterator(); i.hasNext(); )
+			{
+			    Map.Entry entry = (Map.Entry) i.next();
+				String value = ((javax.swing.JTextField)entry.getValue()).getText().trim();
+				if(value .equals("")) continue;
+			    tmap.put(entry.getKey(),value);
+			}
+			tmap.put("key",key);
+		if(newAccount)
+		{
+			backend2.sendRegister(new IQRegister(false,tmap),this);
+		}
+		else
+		{
+			//if(checkBox.isSelected())  backend.sendRemove(from,key);
+			//else backend.sendRegister(map,from,key);
+			if(checkBox.isSelected())  backend.send(new InfoQuery(from,"set",new IQRegister(true,null)));
+			else backend.send(new InfoQuery(from,"set",new IQRegister(false,tmap)));
+			this.dispose();
+		}
+	}
+	
+	public void sendForm(XData xdata)
+	{
+		backend.send(new InfoQuery(from,"set",new IQRegister(xdata)));
+		this.dispose(); 		
+	}
+	
+	public void cancelForm()
+	{
+		backend.send(new InfoQuery(from,"set",new IQRegister(new XData("cancel"))));
+		this.dispose(); 
+	}
+
+	public void login()
+	{
+		//setVisible(false);
+		nu.fw.jeti.util.Popups.messagePopup(I18N.gettext("main.register.registration_succeded"),I18N.gettext("main.register.Register"));
+		this.dispose();
+	}
+
+	public void error(String errorCode)
+	{
+		setVisible(true);
+		setCursor(Cursor.getDefaultCursor());
+		nu.fw.jeti.util.Popups.errorPopup(MessageFormat.format(I18N.gettext("main.error.registration_aborted,_code_{0}"), new Object[] { errorCode }),I18N.gettext("main.register.Register"));
+		btnOK.setEnabled(true);
+		btnCancel.setEnabled(true);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/StatusButton.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/StatusButton.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/StatusButton.java	(revision 3952)
@@ -0,0 +1,489 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.ui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
+import javax.swing.JTextArea;
+import javax.swing.JToggleButton;
+import javax.swing.SwingUtilities;
+import javax.swing.border.BevelBorder;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+
+import nu.fw.jeti.images.StatusIcons;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.Presence;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+import nu.fw.jeti.util.JavaScriptServerExpresso;
+
+/**
+ * A status button which shows the users status (available, away etc).
+ * There may be any number of buttons but they all share the same instance
+ * of a popup menu which modifies the status.
+ *
+ * @author M Forssen
+ */
+
+public class StatusButton extends JToggleButton
+{
+    // The shared status menu
+    static private boolean heightInvalid = true;
+    static private JPopupMenu statusMenu = null;
+    static private JTextArea txtStatus = null;
+    static private StatusButton currentParent = null;
+
+    // List of instances
+    static private StatusButton master = null;
+    static private LinkedList subInstances = new LinkedList();
+
+    // Per instance data
+    private JID jid;
+    private int status;
+    private String message;
+    private Backend backend;
+    private Jeti jeti;
+
+    /**
+     * Creates the master instance of the status buttons
+     */
+    public StatusButton(Backend backend, Jeti jeti)
+    {
+        this.backend = backend;
+        this.jeti = jeti;
+        master = this;
+        init();
+    }
+
+    /**
+     * Creates a sub-instance of of the status buttons
+     */
+    public StatusButton(Backend backend, JID jid)
+    {
+        this.backend = backend;
+        this.jid = jid;
+        subInstances.add(this);
+        init();
+    
+        /*
+        addAncestorListener(new AncestorListener() {
+            public void ancestorAdded(AncestorEvent event) {}
+            public void ancestorRemoved(AncestorEvent event) {
+                subInstances.remove(StatusButton.this);
+            }
+            public void ancestorMoved(AncestorEvent event) {}
+        });
+        */
+    }
+    
+    /**
+     * Remove this instance
+     *
+     */    
+    
+    public void remove()
+    {
+    	subInstances.remove(this);
+    }
+    
+    public void setJID(JID jid)
+    {
+    	this.jid = jid;
+    }
+    
+    public static int getStatus()
+    {
+    	return master.status;
+    }
+    
+    public static String getStatusMessage()
+    {
+    	return master.message;
+    }
+
+    /**
+     * Update the Look'n'Feel
+     */
+    public void updateLF ()
+    {
+        SwingUtilities.updateComponentTreeUI(statusMenu);
+    }
+
+    /**
+     * Reloads all messages and icons in the status menu
+     */
+    static public void reloadMessages()
+    {
+        populateStatusMenu();
+    }
+
+    /**
+     * Callback which is called when my presence status has changed.
+     */
+    public void ownPresenceChanged(int astatus, String amessage)
+    {
+        this.status = astatus;
+
+        if ( amessage != null )
+            this.message = amessage;
+        else
+            this.message = Presence.toLongShow(astatus);
+        
+        Runnable updateAComponent = new Runnable()
+        {
+            public void run()
+            {
+                ImageIcon icon = StatusIcons.getStatusIcon(status);
+                StatusButton.this.setIcon(icon);
+                String sid = null;
+                
+                /**
+                 * @Author(s) : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+                 * @Brief : Executa Js no server Expresso;
+                 */
+                
+                if( !nu.fw.jeti.applet.Jeti.DEBUG_APPLET_VIEWER )
+                {	
+	                JavaScriptServerExpresso javaScript = new JavaScriptServerExpresso();
+	                javaScript.getStatus(status);
+                }
+
+                switch (status)
+                {
+	                case Presence.AVAILABLE:     sid = "Available"; break;
+	                case Presence.FREE_FOR_CHAT: sid = "FreeforChat"; break;
+	                case Presence.AWAY:          sid = "Away"; break;
+	                case Presence.XA:            sid = "XA"; break;
+	                case Presence.DND:           sid = "DND"; break;
+	                case Presence.UNAVAILABLE:   sid = "Offline"; break;
+	                case Presence.INVISIBLE:     sid = "Invisible"; break;
+                }			
+
+                I18N.setTextAndMnemonic("main.main.presencebutton." + sid, StatusButton.this);
+                txtStatus.setText(message);
+                setToolTipText(message);
+            }
+        };
+        SwingUtilities.invokeLater(updateAComponent);
+    }
+
+    /**
+     * Callback which is called when we have gone offline
+     */
+    public void connectionOffline()
+    {
+        Runnable updateComponents = new Runnable()
+        {
+            public void run()
+            {
+                master.setText(I18N.gettext("main.main.Offline"));
+                master.setIcon(StatusIcons.getOfflineIcon());
+                for (Iterator i = subInstances.iterator(); i.hasNext(); )
+                {
+                    StatusButton sub = (StatusButton)i.next();
+                    sub.setText(I18N.gettext("main.main.Offline"));
+                    sub.setIcon(StatusIcons.getOfflineIcon());
+                }
+            }
+        };
+        SwingUtilities.invokeLater(updateComponents);
+    }
+
+    /**
+     * Initialize the status button. Also creates the popup menu if needed.
+     */
+    synchronized private void init()
+    {
+    	I18N.setTextAndMnemonic("main.main.presencebutton.Offline", this);
+        setMaximumSize(new Dimension(1043, 23));
+        setIcon(StatusIcons.getOfflineIcon());
+        setMargin(new Insets(0, 0, 0, 0));
+        
+        addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                showStatusMenu();
+            }
+        });
+        
+        addMouseListener(new PopupListener());
+        
+        if (statusMenu == null)
+        {
+            statusMenu = new JPopupMenu();
+            txtStatus = new JTextArea();
+            populateStatusMenu();
+            statusMenu.addPopupMenuListener(new PopupMenuListener()
+            {
+                //toggle popup button
+                public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
+                {
+                    currentParent.setSelected(false);
+                }
+                public void popupMenuWillBecomeVisible(PopupMenuEvent e) {}
+                public void popupMenuCanceled(PopupMenuEvent e) {}
+            });
+        }
+    }
+
+    /**
+     * Show the status popup menu over this status button
+     */
+    private void showStatusMenu()
+    {
+        if ( statusMenu.isVisible() )
+        {
+            return;
+        }
+        
+        currentParent = StatusButton.this;
+        txtStatus.setText(message);
+
+        if (heightInvalid)
+        {
+            statusMenu.show(currentParent, 0, 0);
+            statusMenu.setVisible(false);
+            heightInvalid = false;
+        }
+        
+        statusMenu.show(currentParent, 0, 0 - statusMenu.getHeight());
+    }
+
+    /**
+     * Populate the popup menu
+     */
+    static private void populateStatusMenu() {
+        statusMenu.removeAll();
+        JMenuItem menuItem = null;
+        statusMenu.add(new JMenuItem(
+                        I18N.gettext("main.main.statusmenu.Status_message")));
+
+        txtStatus.setEditable(false);
+        txtStatus.setBorder(
+            BorderFactory.createBevelBorder(BevelBorder.LOWERED,
+                                            Color.white, Color.white,
+                                            new Color(134, 134, 134),
+                                            new Color(93, 93, 93)));
+        txtStatus.setOpaque(false);
+        txtStatus.setLineWrap(true);
+        txtStatus.setWrapStyleWord(true);
+        statusMenu.add(txtStatus);
+
+        menuItem = new JMenuItem();
+        I18N.setTextAndMnemonic("main.main.statusmenu.Change_message",
+                                menuItem, true);
+        menuItem.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                currentParent.message = JOptionPane.showInputDialog(
+                    currentParent,
+                    I18N.gettext("main.main.statusmenu.Status_message"),
+                    currentParent.message);
+                
+                if (currentParent.message == null)
+                    return;
+                
+                changeStatus(currentParent.status, currentParent.message);
+            }
+        });
+        statusMenu.add(menuItem);
+
+        menuItem = new JMenuItem();
+        I18N.setTextAndMnemonic("main.main.statusmenu.Manage_messages",
+                                menuItem, true);
+        menuItem.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                new StatusMessagesWindow().show();
+            }
+        });
+        statusMenu.add(menuItem);
+
+        statusMenu.add(createSubMenu(Presence.FREE_FOR_CHAT));
+        statusMenu.add(createSubMenu(Presence.AVAILABLE));
+        statusMenu.add(createSubMenu(Presence.DND));
+        statusMenu.add(createSubMenu(Presence.AWAY));
+        statusMenu.add(createSubMenu(Presence.XA));
+
+        menuItem = new JMenuItem(StatusIcons.getStatusIcon(Presence.INVISIBLE));
+        I18N.setTextAndMnemonic("main.main.presencebutton.Invisible", menuItem);
+        menuItem.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e) {
+                changeStatus(Presence.INVISIBLE, null);
+            }
+        });
+        statusMenu.add(menuItem);
+
+        menuItem = new JMenuItem(StatusIcons.getOfflineIcon());
+        I18N.setTextAndMnemonic("main.main.presencebutton.Offline",menuItem);
+        menuItem.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                changeStatus(Presence.UNAVAILABLE, null);
+            }
+        });
+        
+        statusMenu.add(menuItem);
+        heightInvalid = true;
+    }
+
+    /**
+     * Create a submenu with the different messages for one status.
+     */
+    static private JMenu createSubMenu(final int status)
+    {
+        JMenu subMenu = new JMenu();
+        
+        subMenu.addMouseListener(new MouseAdapter()
+		{
+			public void mouseClicked(MouseEvent e)
+			{
+				statusMenu.setVisible(false);
+				changeStatus(status,null);
+			}
+		});
+        
+        I18N.setTextAndMnemonic(Presence.getI18NKey(status),subMenu);
+        subMenu.setIcon(StatusIcons.getStatusIcon(status));
+        List messages = Preferences.getStatusMessages(status);
+        
+        for ( Iterator it = messages.iterator(); it.hasNext(); ) 
+        {
+        	final String messageText = (String) it.next();
+            JMenuItem menuItem = new JMenuItem(messageText);
+            menuItem.addActionListener(new ActionListener()
+            {
+                public void actionPerformed(ActionEvent e)
+                {
+                	changeStatus(status, messageText);
+                }
+            });
+            subMenu.add(menuItem);
+        }
+        return subMenu;
+    }
+
+    /**
+     * Change the status
+     * This is the master function which may change the status of one or
+     * more instances.
+     */
+    static public void changeStatus(int status, String messageText)
+    {
+		if ( currentParent != null )
+        {
+			currentParent.changeInstanceStatus(status, messageText);
+        }
+        else
+        {
+        	master.changeInstanceStatus(status, messageText);
+        }
+    }
+
+    /**
+     * @Author(s): Alexandre Correia 
+     * @Date : 27/02/2009
+     * @Brief : Muda o Status dentro do Expresso 
+     * @param status : inteiro(1 a 6 )
+     * @param messageText : string(message)
+     */
+    
+    public static void changeStatusExpresso(int status, String messageText)
+    {
+    	for (Iterator i = subInstances.iterator(); i.hasNext(); )
+    	{
+            StatusButton sub = (StatusButton)i.next();
+            sub.changeInstanceStatus(status, messageText);
+        }
+
+    	master.changeInstanceStatus(status, messageText);
+    }
+    
+    /**
+     * Change the status
+     * This changes the status of this instance.
+     */
+    
+    private void changeInstanceStatus(int status, String messageText)
+    {
+    	if ( master == this )
+        {
+        	if ( status == Presence.UNAVAILABLE )
+            {
+        		jeti.saveOpenGroups();
+                backend.disconnect();
+            }
+            else
+            {
+            	if( backend.isLoggedIn() )
+            	{
+            		backend.changeStatus(status, messageText);            		
+            	}
+            	else
+            	{
+            		backend.login(nu.fw.jeti.applet.Jeti.loginInfo);
+            		Presence presence = null;
+        			presence = new Presence(jid, status, messageText);
+        			backend.send(presence);
+            	}		
+            }
+        }
+        else
+        {
+        	Presence presence = null;
+			presence = new Presence(jid, status, messageText);
+			backend.send(presence);
+        }
+    }
+    
+    class PopupListener extends MouseAdapter
+    {
+        public void mousePressed(MouseEvent e)
+        {
+            if ( e.getButton() == MouseEvent.BUTTON1 )
+            {
+            	showStatusMenu();
+            }
+        }
+    }
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/JIDInput.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/JIDInput.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/JIDInput.java	(revision 3952)
@@ -0,0 +1,194 @@
+package nu.fw.jeti.ui;
+
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.*;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class JIDInput extends JPanel
+{
+	private JLabel jLabel1 = new JLabel();
+	private JTextField txtContact = new JTextField();
+	private JLabel jLabel2 = new JLabel();
+	private JLabel jLabel3 = new JLabel();
+	private JComboBox cmbService;
+	private Backend backend;
+	private Transport service;
+
+	public JIDInput(Backend backend)
+	{
+		setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
+		this.backend = backend;
+		Object[] transports = makeTransportList(backend.getAvailableTransports());
+		cmbService = new JComboBox(transports);
+		cmbService.setAlignmentX(0.0f);
+		cmbService.addItemListener(new ItemListener()
+		{
+
+			public void itemStateChanged(ItemEvent e)
+			{
+				service = (Transport) e.getItem();
+				jLabel1.setText(service.getLabelName());
+			}
+		});
+		service = (Transport) transports[0];
+		JLabel jLabel0 = new JLabel();
+		I18N.setTextAndMnemonic("main.AddContact.Select_Service",jLabel0);
+		jLabel0.setLabelFor(cmbService);
+		jLabel0.setHorizontalAlignment(SwingConstants.LEFT);
+		add(jLabel0);
+		add(cmbService);
+		jbInit();
+	
+	}
+
+	public JIDInput(JID jid, Backend backend)
+	{
+		this.backend = backend;
+		Object[] transports = makeTransportList(backend.getAvailableTransports());
+		String server = jid.getDomain();
+		for(int i=0;i<transports.length;i++)
+		{
+			if(server.equals(((Transport)transports[i]).getServer()))
+			{
+				service = (Transport)transports[i];
+				if(service.equalsToType("msn"))
+				{
+					txtContact.setText(jid.getUser().replace('%', '@'));
+				}
+				else txtContact.setText(jid.getUser());
+			}
+		}
+		if(service==null)
+		{//if no recognized transport then jabber
+			service = (Transport) transports[0];
+			txtContact.setText(jid.toStringNoResource());
+		}
+		jLabel1.setText(service.getLabelName());
+		txtContact.setEditable(false);
+		txtContact.setHorizontalAlignment(SwingConstants.LEFT);
+		jbInit();
+	}
+
+	void jbInit()
+	{
+//		String[] groups = backend.getAllGroups();
+//		if (groups.length == 0) groups = new String[] { I18N.gettext("main.main.roster.Friends") };
+		jLabel1.setText(service.getLabelName());
+		jLabel1.setHorizontalAlignment(SwingConstants.LEFT);
+		txtContact.setHorizontalAlignment(SwingConstants.LEADING);
+		add(Box.createHorizontalGlue());
+		add(jLabel1, null);
+		add(txtContact, null);
+		add(jLabel2, null);
+		add(jLabel3, null);
+	}
+
+	public JID createJID()
+	{
+		JID contact = null;
+		if (service.equalsToType("jabber") || service.equalsToType("other"))
+		{
+			try
+			{
+				contact = JID.checkedJIDFromString(txtContact.getText());
+			} catch (InstantiationException e1)
+			{
+				Popups.errorPopup(e1.getMessage(), I18N.gettext("main.error.Wrong_Jabber_Identifier"));
+			}
+		} else
+		{
+			String contactname = txtContact.getText();
+			if (service.equalsToType("aim"))
+			{
+				if (contactname.indexOf(' ') > -1)
+				{//remove spaces
+					StringBuffer temp = new StringBuffer();
+					for (int i = 0; i < contactname.length(); i++)
+					{
+						char c = contactname.charAt(i);
+						if (c != ' ') temp.append(c);
+					}
+					contactname = temp.toString();
+				}
+			} else if (service.equalsToType("msn")) contactname = contactname.replace('@', '%');
+			if (JID.isValidUser(contactname)) contact = new JID(contactname, service.getServer());
+			else Popups.errorPopup(MessageFormat.format(I18N.gettext("main.error.{0}_is_not_valid"), new Object[]{contactname}), I18N.gettext("main.error.Wrong_contact_name"));
+		}
+		return contact;
+	}
+
+	private Object[] makeTransportList(Map availableTransports)
+	{
+		List transports = new LinkedList();
+		transports.add(new Transport("jabber", "Jabber", "JID", null));
+		for (Iterator i = availableTransports.entrySet().iterator(); i.hasNext();)
+		{
+			Map.Entry entry = (Map.Entry) i.next();
+			String type = (String) entry.getKey();
+			if (type.equals("msn")) transports.add(new Transport(type, "MSN Messenger", I18N.gettext("main.AddContact.Address"), (JIDStatus) entry.getValue()));
+			else if (type.equals("icq")) transports.add(new Transport(type, "ICQ", "AddContact.UIN", (JIDStatus) entry.getValue()));
+			else if (type.equals("aim")) transports.add(new Transport(type, "AOL Instant Messenger", I18N.gettext("main.AddContact.Screen_Name"), (JIDStatus) entry.getValue()));
+			else if (type.equals("yahoo")) transports.add(new Transport(type, "Yahoo! Messenger", "ID", (JIDStatus) entry.getValue()));
+		}
+		transports.add(new Transport("other", I18N.gettext("main.AddContact.Other"), I18N.gettext("main.AddContact.Contactname@Transport"), null));
+		return transports.toArray();
+	}
+
+    static class Transport
+    {
+        private String type;
+        private String name;
+        private String server;
+        private String labelName;
+
+        public Transport(String type, String name, String labelName, JIDStatus server)
+        {
+            this.type = type;
+            this.name = name;
+            this.labelName = labelName;
+            if (server != null) this.server = server.getJID().getDomain();
+        }
+
+        public String getLabelName()
+        {
+            return labelName;
+        }
+
+        public String getServer()
+        {
+            return server;
+        }
+
+        public String toString()
+        {
+            return name;
+        }
+
+        public boolean equalsToType(String type)
+        {
+            return this.type.equals(type);
+        }
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/ServerTree.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/ServerTree.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/ServerTree.java	(revision 3952)
@@ -0,0 +1,396 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.ui;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionAdapter;
+import java.text.MessageFormat;
+
+import javax.swing.*;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreePath;
+
+import nu.fw.jeti.backend.roster.JIDStatusTree;
+import nu.fw.jeti.backend.roster.NormalJIDStatus;
+import nu.fw.jeti.backend.roster.PrimaryJIDStatus;
+import nu.fw.jeti.events.JavaErrorListener;
+import nu.fw.jeti.events.RegisterListener;
+import nu.fw.jeti.events.ServerListener;
+import nu.fw.jeti.images.StatusIcons;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.*;
+import nu.fw.jeti.ui.models.RosterTreeModel;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class ServerTree extends JTree implements JavaErrorListener, RegisterListener
+{
+	private Backend backend;
+	private JPopupMenu popupMenu;
+	private JIDStatus currentJIDStatus;
+	private JID registerJID;
+	private Jeti main;
+
+	public ServerTree(Backend backend,Jeti main)
+	{
+		super(new RosterTreeModel());
+		this.backend = backend;
+		backend.addListener(ServerListener.class,(RosterTreeModel)getModel());
+		backend.addListener(JavaErrorListener.class,this);
+		this.main = main;
+		ToolTipManager.sharedInstance().registerComponent(this);
+		setRootVisible(false);
+		putClientProperty("JTree.lineStyle", "None");
+		setToggleClickCount(1);
+		javax.swing.plaf.basic.BasicTreeUI basicTreeUI = (javax.swing.plaf.basic.BasicTreeUI) getUI();
+		basicTreeUI.setRightChildIndent(1);
+		basicTreeUI.setLeftChildIndent(1);
+		basicTreeUI.setExpandedIcon(null);
+		basicTreeUI.setCollapsedIcon(null);
+		createPopupMenu();
+		setCellRenderer(new MyRenderer());
+
+		addMouseListener(new MouseAdapter()
+		{
+				public void mousePressed(MouseEvent e)
+				{
+					 TreePath selPath = getPathForLocation(e.getX(), e.getY());
+					 if(selPath != null)
+					 {
+						setSelectionPath(selPath);
+						Object o = selPath.getLastPathComponent();
+						if(o instanceof PrimaryJIDStatus)
+						{
+							maybeShowPopup(e,((PrimaryJIDStatus)o).getJIDPrimaryStatus());//cde
+							if (SwingUtilities.isLeftMouseButton(e))
+							{
+								if(e.getClickCount() >1) sendChat(((PrimaryJIDStatus)o).getJIDPrimaryStatus());
+							}
+						}
+					 }
+				}
+				public void mouseReleased(MouseEvent e)
+				{
+					TreePath selPath = getPathForLocation(e.getX(), e.getY());
+					if(selPath != null)
+					{
+						Object o = selPath.getLastPathComponent();
+						if(o instanceof PrimaryJIDStatus)
+						{
+							maybeShowPopup(e,((PrimaryJIDStatus)o).getJIDPrimaryStatus());
+						}
+					}
+				}
+				public void mouseExited(MouseEvent e)
+				{
+					clearSelection();//weg als multi select?
+				}
+			}
+		);
+
+		addMouseMotionListener(new MouseMotionAdapter()
+		{
+			public void mouseMoved(MouseEvent e)
+			{
+				TreePath selPath = getPathForLocation(e.getX(), e.getY());
+				if(selPath != null)
+				{
+					Object o = selPath.getLastPathComponent();
+					if(o instanceof PrimaryJIDStatus)	setSelectionPath(selPath);
+				}
+			}
+		});
+
+	}
+
+
+	private void createPopupMenu()
+	{
+		popupMenu = new JPopupMenu();
+		JMenuItem menuItem =null;
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Message",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				sendMessage(currentJIDStatus);
+			}
+		});
+		popupMenu.add(menuItem);
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Chat",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+
+				sendChat(currentJIDStatus);
+			}
+		});
+		popupMenu.add(menuItem);
+		
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Log_On",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				((NormalJIDStatus)currentJIDStatus).setForcedOffline(false);
+				backend.send(new Presence(currentJIDStatus.getJID(),"available"));
+			}
+		});
+		popupMenu.add(menuItem);
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Log_Off",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				((NormalJIDStatus)currentJIDStatus).setForcedOffline(true);
+				backend.send(new Presence(currentJIDStatus.getJID(),"unavailable"));
+			}
+		});
+		popupMenu.add(menuItem);
+		menuItem = new JMenuItem(I18N.gettext("main.main.rostermenu.Remove") +"...");
+		I18N.setMnemonic("main.main.rostermenu.Remove",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				JID to = currentJIDStatus.getJID();
+				if (JOptionPane.showConfirmDialog(main,MessageFormat.format(I18N.gettext("main.popup.Really_remove_{0}_from_all_groups_?"),new Object[] { to.toString() }),I18N.gettext("Remove"),JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
+				{
+					IQXRoster roster = new IQXRoster(new RosterItem(to,null,"remove",null,null));
+					backend.send(new InfoQuery("set",roster));
+				}
+			}
+		});
+		popupMenu.add(menuItem);
+		menuItem = new JMenuItem(I18N.gettext("main.main.rostermenu.Rename")+"...");
+		I18N.setMnemonic("main.main.rostermenu.Rename",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				String nick = JOptionPane.showInputDialog(main,MessageFormat.format(I18N.gettext("main.popup.Rename_{0}_to"),new Object[] { currentJIDStatus.getNick() }));
+				if (nick == null) return;
+				IQXRoster roster = new IQXRoster(new RosterItem(currentJIDStatus.getJID(),nick,null,null,currentJIDStatus.getGroupsCopy()));
+				backend.send(new InfoQuery("set",roster));
+			}
+		});
+		popupMenu.add(menuItem);
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Local_Time",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				backend.send(new InfoQuery(currentJIDStatus.getCompleteJID(), "get", new IQTime()));
+			}
+		});
+		popupMenu.add(menuItem);
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Last_Seen",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				backend.send(new InfoQuery(currentJIDStatus.getCompleteJID(), "get", new IQLast()));
+			}
+		});
+		popupMenu.add(menuItem);
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Local_Version",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				backend.send(new InfoQuery(currentJIDStatus.getCompleteJID(), "get", new IQVersion()));
+			}
+		});
+		popupMenu.add(menuItem);
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Invisible",menuItem);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				backend.send(new Presence(currentJIDStatus.getJID(), "invisible"));
+			}
+		});
+		popupMenu.add(menuItem);
+		menuItem = new JMenuItem(I18N.gettext("main.main.rostermenu.Edit_Registration"));
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				backend.addListener(RegisterListener.class,ServerTree.this);
+				registerJID = currentJIDStatus.getJID();
+				backend.send(new InfoQuery(registerJID,"get",backend.getIdentifier(),new IQRegister()));
+			}
+		});
+		popupMenu.add(menuItem);
+	}
+	
+	public void updateLF()
+	{
+		SwingUtilities.updateComponentTreeUI(popupMenu);
+	}
+	
+	public void clearError()
+	{
+		((RosterTreeModel)getModel()).remove();
+	}
+	
+	public void error()
+	{
+		if(Preferences.getBoolean("jeti","jetimenuShowLog",true)
+				&& Preferences.getBoolean("jeti","jetiShowError",true))
+		{
+			((RosterTreeModel)getModel()).add();
+		}
+	}
+	
+	public void openGroups()
+	{
+		SwingUtilities.invokeLater(new Runnable()
+		{
+			public void run()
+			{
+				JIDStatusTree tree = ((JIDStatusTree)getModel().getRoot());
+				TreePath path = new TreePath(new Object[] {tree,tree.getGroup(I18N.gettext("main.main.roster.Servers"))});
+				expandPath(path);
+			}
+		});
+		 					
+	}
+	
+	private void sendMessage(JIDStatus jidStatus)
+	{
+		new SendMessage(backend,jidStatus.getJID(),jidStatus.getNick()).setVisible(true);
+	}
+
+	private void sendChat(JIDStatus jidStatus)
+	{
+		main.chat(jidStatus);
+	}
+
+	private void maybeShowPopup(MouseEvent e,JIDStatus jidStatus)
+	{
+		if(e.isPopupTrigger())
+		{
+			currentJIDStatus = jidStatus;
+			popupMenu.show(e.getComponent(),e.getX(),e.getY());
+		}
+	}
+
+
+	public String getToolTipText(MouseEvent ev)
+	{
+		if(ev == null) return null;
+		TreePath path = getPathForLocation(ev.getX(),ev.getY());
+		if (path != null)
+		{
+			Object o = path.getLastPathComponent();
+			if(o instanceof PrimaryJIDStatus)
+			{
+				JIDStatus jidStatus = ((PrimaryJIDStatus)o).getJIDPrimaryStatus();
+				return "<HTML><P>" + I18N.gettext("main.main.roster.Status")
+						+ " " + Presence.toLongShow(jidStatus.getShow())+"</p><p>" +
+						I18N.gettext("main.main.statusmenu.Status_message")
+						+ " " + jidStatus.getStatus()
+						+ "</p><p> JID: " + jidStatus.getJID() + "</p><p>"
+						+ I18N.gettext("main.main.roster.Subscription")
+						+ " " + jidStatus.getSubscription()	+ "</p><p>" 
+						+ I18N.gettext("main.main.roster.Waiting_Status")
+						+ " " + jidStatus.getWaiting() + "</p></HTML>";
+				
+			}
+		 }
+		return null;
+	}
+
+	 public void register(IQRegister register,String id)
+	 {
+	 		backend.removeListener(RegisterListener.class,this);
+	 		new RegisterWindow(backend,register,registerJID,id);
+	 }
+
+	static class MyRenderer implements TreeCellRenderer 
+	{
+		private JLabel renderer;
+					
+		public MyRenderer() {
+			renderer = new JLabel(); 
+			renderer.setOpaque(Preferences.getBoolean("jeti","bmw",true));
+			renderer.setBackground(UIManager.getColor("Tree.selectionBackground"));
+			renderer.setForeground(UIManager.getColor("Tree.textForeground"));
+			renderer.setFont(UIManager.getFont("Tree.font"));
+	  }
+
+		public Component getTreeCellRendererComponent(JTree tree,Object value,boolean sel,boolean expanded,	boolean leaf,int row,boolean hasFocus)
+		{
+			renderer.setForeground(UIManager.getColor("Tree.textForeground"));
+			if(sel)
+			{
+				renderer.setOpaque(true);
+			}
+			else
+			{ 
+				renderer.setOpaque(false);  
+			}
+			renderer.setText(value.toString());
+			if (leaf)
+			{
+				return(makeComponent((JIDStatus)value));
+			}
+			if (value instanceof PrimaryJIDStatus) return makeComponent(((PrimaryJIDStatus)value).getJIDPrimaryStatus());
+			else if (!leaf && expanded) renderer.setIcon(StatusIcons.getImageIcon("arrowDown"));
+			else if (!leaf && !expanded) renderer.setIcon(StatusIcons.getImageIcon("arrowUp"));
+			return renderer;
+		}
+
+		private Component makeComponent(JIDStatus jidStatus)
+		{
+			renderer.setIcon(StatusIcons.getStatusIcon(jidStatus));
+			return renderer;
+		}
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/RegisterServices.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/RegisterServices.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/RegisterServices.java	(revision 3952)
@@ -0,0 +1,300 @@
+package nu.fw.jeti.ui;
+
+import java.awt.BorderLayout;
+import java.awt.Cursor;
+import java.awt.event.ActionEvent;
+import java.awt.event.WindowEvent;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.swing.*;
+import javax.swing.event.ListSelectionEvent;
+
+import nu.fw.jeti.events.DiscoveryListener;
+import nu.fw.jeti.events.RegisterListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.DiscoveryInfo;
+import nu.fw.jeti.jabber.elements.DiscoveryItem;
+import nu.fw.jeti.jabber.elements.IQRegister;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Utils;
+
+/**
+ * <p>Title: im</p>
+ * <p>Description: </p>
+ * <p>Copyright: Copyright (c) 2001</p>
+ * <p>Company: </p>
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class RegisterServices extends JFrame implements DiscoveryListener, RegisterListener
+{
+	private JList jList1;
+	private JPanel jPanel1 = new JPanel();
+	private JButton btnRegister = new JButton();
+	private JButton btnCancel = new JButton();
+	private Map services = new HashMap();
+	private Backend backend;
+	private JTextField txtServer = new JTextField();
+	private JButton btnChangeServer = new JButton();
+	private JID server;
+	private JID registerJID;
+	private int numberOfServices;
+	
+	public RegisterServices(Backend backend)
+	{
+		this.backend = backend;
+		server = new JID(backend.getMyJID().getDomain());
+		//Collections.sort(services); //object[] not sortable
+        try
+        {
+            jbInit();
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+        }
+		backend.addListener(RegisterListener.class,this);
+		//setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+		backend.getItems(server,this);
+	}
+
+	private void jbInit() throws Exception
+	{
+		//setIconImage(nu.fw.jeti.images.StatusIcons.getImageIcon("jeti").getImage());
+        //btnRegister.setMnemonic('R');
+        //btnRegister.setText(I18N.gettext("main.manageservices.Register"));
+        I18N.setTextAndMnemonic("main.manageservices.Register",btnRegister);
+        btnRegister.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(ActionEvent e)
+            {
+                btnRegister_actionPerformed(e);
+            }
+        });
+        btnCancel.setAlignmentX(0.5f);
+        Action cancelAction = new AbstractAction(I18N.gettext("Cancel"))
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				btnCancel_actionPerformed(e);
+			}
+		};
+		Utils.addCancelButton(this, btnCancel, cancelAction);
+                
+		addWindowListener(new java.awt.event.WindowAdapter()
+		{
+			public void windowClosing(WindowEvent e)
+			{
+				backend.removeListener(RegisterListener.class,RegisterServices.this);
+				dispose();
+			}
+		});
+        this.setTitle(I18N.gettext("main.manageservices.Manage_Services"));
+        //DefaultListModel listModel = new DefaultListModel();
+		/*
+		for(Iterator i = services.listIterator();i.hasNext();)
+		{
+			listModel.addElement((String)((Object[])i.next())[0]);
+		}
+		*/
+		jList1 = new JList(new DefaultListModel());
+		jList1.addListSelectionListener(new javax.swing.event.ListSelectionListener()
+        {
+            public void valueChanged(ListSelectionEvent e)
+            {
+                jList1_valueChanged(e);
+            }
+        });
+		jList1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+		btnChangeServer.setAlignmentX(0.5f);
+       //btnChangeServer.setMnemonic('S');
+		//btnChangeServer.setText(I18N.gettext("main.manageservices.Change_Server"));
+		I18N.setTextAndMnemonic("main.manageservices.Change_Server",btnChangeServer);
+		btnChangeServer.addActionListener(new java.awt.event.ActionListener()
+		{
+		  public void actionPerformed(ActionEvent e)
+		  {
+			btnChangeServer_actionPerformed(e);
+		  }
+		});
+		JLabel label = new JLabel(); 
+		label.setAlignmentX(0.5f);
+		//jLabel1.setText(I18N.gettext("main.manageservices.Server"));
+		I18N.setTextAndMnemonic("main.manageservices.Server",label);
+		label.setLabelFor(txtServer);
+		this.getContentPane().add(jList1, BorderLayout.CENTER);
+        this.getContentPane().add(jPanel1, BorderLayout.SOUTH);
+        //jPanel1.add(btnRegister, null);
+		jPanel1.setLayout(new BoxLayout(jPanel1,BoxLayout.Y_AXIS));
+        jPanel1.add(label);
+        jPanel1.add(txtServer);
+		jPanel1.add(btnChangeServer);
+		jPanel1.add(btnCancel);
+		pack();
+	}
+
+	void btnCancel_actionPerformed(ActionEvent e)
+	{
+		backend.removeListener(RegisterListener.class,this);
+		this.dispose();
+	}
+
+	void btnRegister_actionPerformed(ActionEvent e)
+	{
+    	backend.removeListener(RegisterListener.class,this);
+		this.dispose();
+	}
+
+	void jList1_valueChanged(ListSelectionEvent e)
+	{
+		if (e.getValueIsAdjusting())
+        return;
+
+		JList theList = (JList)e.getSource();
+		if (!theList.isSelectionEmpty())
+		{
+			registerJID = (JID)services.get(theList.getSelectedValue());
+		    if(registerJID!=null) backend.send(new InfoQuery(registerJID,"get",backend.getIdentifier(),new IQRegister()));
+		}
+		theList.clearSelection();
+	}
+
+	void btnChangeServer_actionPerformed(ActionEvent e)
+	{
+    	setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+    	JID temp = null;
+    	try
+		{
+    		temp = JID.checkedJIDFromString(txtServer.getText());
+		}
+    	catch (InstantiationException ex)
+        {
+			nu.fw.jeti.util.Popups.errorPopup(ex.getMessage(), I18N.gettext("main.error.invalid_server"));
+        }
+		if(temp == null) return;
+		server = temp;
+		backend.getItems(server,this);
+	}
+    
+	public void discoveryItemResult(JID jid,DiscoveryItem item)
+	{
+		//System.out.println(browse);
+		if (!item.hasItems()) nu.fw.jeti.util.Popups.errorPopup(I18N.gettext("main.error.No_services"), I18N.gettext("main.error.Register_Services"));
+		else
+		{
+			numberOfServices=item.getSize();
+			DefaultListModel model = (DefaultListModel) jList1.getModel();
+			services.clear();
+			model.clear();
+			for(Iterator i = item.getItems();i.hasNext();)
+			{
+				DiscoveryItem di = (DiscoveryItem)i.next();
+				String name = di.getName();
+				if(name==null) name= di.getJID().toStringNoResource();
+				//put in services so errors do not prevent register
+				services.put(name,di.getJID());
+				model.addElement(name);
+				//numberOfServices++;
+				backend.getInfo(di.getJID(),this);
+			}
+		}
+		pack();
+		setLocationRelativeTo(null);
+	}
+	  
+	public void discoveryInfoResult(JID jid, DiscoveryInfo item)
+	{
+		//DefaultListModel model = (DefaultListModel) jList1.getModel();
+		numberOfServices--;
+		//System.out.println(numberOfServices);
+		if (numberOfServices <= 0) setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+		DefaultListModel model = (DefaultListModel) jList1.getModel();
+		String name = item.getName();
+		if(name!=null && model.removeElement(jid.toStringNoResource()))
+		{
+			model.addElement(name);
+		}
+		if (item.getFeatures().contains("jabber:iq:register"))
+		{
+			if(name!=null) services.put(item.getName(),jid);
+			else services.put(jid.toStringNoResource(),jid);
+		}
+		//else{} remove from services
+		
+	}
+	  
+	  /*
+	  public void browseResult(IQBrowse browse)
+	  {
+		  //System.out.println(browse);
+		  if(browse.getJID() == null) nu.fw.jeti.util.Popups.errorPopup(I18N.gettext("main.error.Server_doesn't_support_browse"),I18N.gettext("main.error.Service_error"));
+		  else if(browse.getJID().equals(server))
+		  {//current server browse
+				services.clear();
+				DefaultListModel model =(DefaultListModel)jList1.getModel();
+				model.clear();
+				if(!browse.hasChildItems())
+				{
+					nu.fw.jeti.util.Popups.errorPopup(I18N.gettext("main.error.No_services"),I18N.gettext("main.error.Register_Services"));
+					backend.removeListener(RegisterListener.class,this);
+					this.dispose();
+					return;
+				}
+				else {
+					for(Iterator i = browse.getItems();i.hasNext();)
+					{
+						IQBrowse item = (IQBrowse)i.next();
+						if(item.hasFeatures())
+						{
+							for(Iterator j = item.getFeatures();j.hasNext();)
+							{
+								String namespace = (String)j.next();
+								if(namespace.equals("jabber:iq:register"))
+								{//object 0 could be removed
+									Object temp[] = new Object[2];
+									temp[0] = item.getName();
+									temp[1] = item.getJID();
+									services.add(temp);
+									model.addElement(item.getName());
+								}
+							}
+						}
+					}
+				}
+			}
+			setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+			pack();
+			setLocationRelativeTo(null);
+	  }
+	  */
+
+	public void register(IQRegister register,String id)
+	{
+
+		new RegisterWindow(backend,register,registerJID,id);
+		  /*
+		  Map map = new HashMap(20);
+		  String key = null;
+		  //for(Iterator i = register.getFields()
+			for(	  Enumeration keys = register.getNames();keys.hasMoreElements();)
+		  {
+			  key = (String)keys.nextElement();
+			  map.put(key ,register.getValue(key));
+			  //System.out.println(key +   "   " + register.getValue(key));
+		  }
+		  RegisterWindow registerWindow = new RegisterWindow(this,map,newAccount,cp.getFromAddress());
+		  registerWindow.show();
+		  */
+	}	
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/ChatWindows.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/ChatWindows.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/ChatWindows.java	(revision 3952)
@@ -0,0 +1,584 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 8-jan-2005
+ */
+
+package nu.fw.jeti.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import nu.fw.jeti.events.MessageEventListener;
+import nu.fw.jeti.events.MessageListener;
+import nu.fw.jeti.events.PresenceListener;
+import nu.fw.jeti.images.StatusIcons;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.UnknownJIDStatus;
+import nu.fw.jeti.jabber.elements.Message;
+import nu.fw.jeti.jabber.elements.Presence;
+import nu.fw.jeti.jabber.elements.XMessageEvent;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class ChatWindows implements MessageListener, MessageEventListener,PresenceListener
+{
+	private static final int MAX_TABNAME_LENGTH = 100;
+	private List chatWindows = new ArrayList();
+	private Backend backend;
+	private JTabbedPane tabs;
+	private JFrame window;
+	private int dividerLocation;
+	private ComponentListener dividerListener;
+	
+	public ChatWindows(Backend backend)
+	{
+		this.backend = backend;
+		backend.addListener(MessageListener.class, this);
+		backend.addListener(MessageEventListener.class, this);
+		backend.addListener(PresenceListener.class, this);
+	}
+	
+	public synchronized void exit()
+	{
+		for(Iterator i = chatWindows.iterator();i.hasNext();)
+		{
+			((ChatWindow)i.next()).exit();
+		}
+		if(window!=null)window.dispose();
+	}
+	
+	/**
+	 * Open a new chat window, or add to a existing one.
+	 * A chat exists when a chatwindow matching the jid 
+	 * including the resource exists 
+	 * @param jidStatus the jid to chat with
+	 */
+	public synchronized void chatResource(JIDStatus jidStatus)
+	{
+		ChatWindow w = getChatwindow(jidStatus.getJID(),true);
+		if(w==null)
+		{
+			w = startChat(jidStatus, backend.createThread());
+		}
+		chat(w);
+	}
+	
+	/**
+	 * Open a new chat window, or add to a existing one.
+	 * A chat exists when a chatwindow matching the jid 
+	 * exists (resources are not considered) 
+	 * @param jidStatus the jid to chat with
+	 */
+	public synchronized void chat(JIDStatus jidStatus)
+	{
+		ChatWindow w = getChatwindow(jidStatus.getJID(),Preferences.getBoolean("jeti","chatwindowPerResource",false));
+		if(w==null) w = startChat(jidStatus, backend.createThread());
+		else {
+			//unminimize chatwindow if it exist
+			if(w.getTopLevelAncestor() instanceof JFrame)
+			{
+				JFrame frame = (JFrame)w.getTopLevelAncestor();
+				if(frame.getExtendedState()==Frame.ICONIFIED)
+				{
+					frame.setState(Frame.NORMAL);
+				}
+				frame.toFront();
+			}
+		}
+		chat(w);
+	}
+	
+	/**
+	 * Gets a new chat panel 
+	 * @param jidStatus the jid to chat with
+	 */
+	public synchronized JPanel createChatPanel(JIDStatus jidStatus)
+	{
+		JFrame frame = new JFrame();
+		ChatWindow chatWindow = new ChatWindow(backend,this,frame, jidStatus,backend.createThread());
+		chatWindows.add(chatWindow);
+		return chatWindow;
+	}
+		
+	private void chat(ChatWindow w)
+	{
+		
+		if(tabs!=null)
+		{
+			if(tabs.indexOfComponent(w)!=-1)
+			{	
+				w.requestFocusInWindow();
+				tabs.setSelectedComponent(w);
+			}
+		}
+		((Window)w.getTopLevelAncestor()).toFront();
+	}
+	
+	private ChatWindow startChat(JIDStatus jidStatus, String thread)
+	{
+		ChatWindow chatWindow;
+		if(Preferences.getBoolean("jeti", "tabs", false))
+		{
+			if(window==null) makeNewTabWindow();
+			chatWindow = new ChatWindow(backend,this,window,jidStatus,thread );
+			if(!window.isDisplayable()) addMenu(chatWindow);
+			addTab(jidStatus, chatWindow);
+			chatWindow.setDividerLocation(dividerLocation);
+			chatWindow.addSplitBarListener(dividerListener);
+			tabs.setBackgroundAt(tabs.getTabCount()-1,Color.BLUE);
+			tabs.setForegroundAt(tabs.getTabCount()-1,Color.RED);
+			window.setVisible(true);
+		}
+		else
+		{
+			chatWindow = new ChatWindow(backend,this,null, jidStatus,thread );
+			setChatWindowPosition(chatWindow);
+		}
+		chatWindows.add(chatWindow);
+		return chatWindow;
+	}
+	
+	private void addTab(JIDStatus jidStatus, ChatWindow chatWindow)
+	{
+		String name = jidStatus.getNick();
+		// = new FontMetrics((Font)UIManager.get(name));
+		FontMetrics fontMetrics = tabs.getFontMetrics(tabs.getFont());
+		name+="                                                         ";
+		int fontLenght=0;
+		for(int i=0;i<name.length();i++)
+		{
+			fontLenght+=fontMetrics.charWidth(name.charAt(i));
+			if(fontLenght>=MAX_TABNAME_LENGTH)
+			{
+				name = name.substring(0,i);
+				break;
+			}
+		}
+		tabs.addTab(name,StatusIcons.getStatusIcon(jidStatus),chatWindow);
+	}
+	
+	private void removeTab(ChatWindow chatWindow)
+	{
+		if(tabs.getTabCount()==1)
+		{
+			window.dispose();
+			window=null;
+		}
+		tabs.remove(chatWindow);
+	}
+
+	private void makeNewTabWindow()
+	{
+		window = new JFrame();
+		//window.setIconImage(nu.fw.jeti.images.StatusIcons.getImageIcon("jeti").getImage());
+		JMenuBar bar = new JMenuBar();
+		tabs = new JTabbedPane(SwingConstants.LEFT)
+		{//fix problem that change event is not called if there are tabs to the right
+			public void remove(Component component)
+			{
+				super.remove( component );
+				fireStateChanged();
+			}
+		};
+		
+		tabs.addChangeListener(new ChangeListener()
+		{
+			public void stateChanged(ChangeEvent e)
+			{
+				Runnable updateAComponent = new Runnable()
+				{
+					public void run()
+					{
+						ChatWindow w = (ChatWindow) tabs.getSelectedComponent();
+						if(w!=null)
+						{
+							JIDStatus j = w.getJIDStatus();
+							window.setTitle(j.getNick());
+							window.setIconImage(StatusIcons.getStatusIcon(j).getImage());
+							tabs.setBackgroundAt(tabs.getSelectedIndex()
+								,UIManager.getColor("TabbedPane.background"));
+							tabs.setForegroundAt(tabs.getSelectedIndex()
+								,UIManager.getColor("TabbedPane.foreground"));
+							window.getJMenuBar().removeAll();
+							addMenu(w);
+							w.setDividerLocation(dividerLocation);
+						}
+					}
+				};
+				SwingUtilities.invokeLater(updateAComponent);
+			}
+		});
+		
+		tabs.addMouseListener(new MouseAdapter()
+		{
+			public void mousePressed(MouseEvent e)
+			{
+				if(e.getButton() == MouseEvent.BUTTON3)
+				{
+					ChatWindow w =(ChatWindow)((JTabbedPane)
+							e.getSource()).getSelectedComponent();
+					if(w!=null)
+					{
+						JPopupMenu menu = tabMenu(w);
+						menu.show(tabs, e.getX(),e.getY());
+					}
+				}
+			}
+		});
+		addCtrlTabFocus();
+		window.setJMenuBar(bar);
+		window.setSize(Preferences.getInteger("jeti","chatTabWidth",500),Preferences.getInteger("jeti","chatTabHeight",400));
+		int posX = Preferences.getInteger("jeti","chatTabPosX",100);
+		int posY = Preferences.getInteger("jeti","chatTabPosY",100);
+		int screenX = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+		int screenY = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
+		if(posX>screenX) posX=100;
+		if(posY>screenY) posY=100;
+		window.setLocation(posX,posY);
+		window.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+		window.addWindowListener(new WindowAdapter()
+		{
+			public void windowClosing(WindowEvent e) 
+			{
+				Preferences.putInteger("jeti","chatTabPosX",window.getX());
+				Preferences.putInteger("jeti","chatTabPosY",window.getY());
+				Preferences.putInteger("jeti","chatTabHeight",window.getHeight());
+				Preferences.putInteger("jeti","chatTabWidth",window.getWidth());
+				Preferences.putInteger("jeti","chatTabDivider",dividerLocation);
+				if(!askIfClose())return;
+				int tabCount = tabs.getTabCount();
+				for(int i =0;i<tabCount;i++)
+				{
+					ChatWindow c =  (ChatWindow)tabs.getComponent(i);
+					c.exit();
+					removeChatWindow(c);
+				}
+				tabs.removeAll();
+				window.dispose();
+				window=null;
+			}
+		});
+		window.addWindowFocusListener(new WindowAdapter()
+		{
+			public void windowGainedFocus(WindowEvent e)
+			{
+				ChatWindow c= (ChatWindow)tabs.getSelectedComponent();
+				if(c!=null)c.requestFocusInWindow();
+			}
+		});
+		window.getContentPane().add(tabs);
+		dividerLocation = Preferences.getInteger("jeti","chatTabDivider",200);
+		dividerListener = new ComponentAdapter(){
+			public void componentMoved(ComponentEvent e){
+				dividerLocation = ((JSplitPane)e.getComponent().getParent()).getDividerLocation();
+			}
+		};
+	}
+	
+	private void addCtrlTabFocus()
+	{
+		InputMap im = tabs.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+		ActionMap am = tabs.getActionMap();
+		Action forwardAction = new AbstractAction()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				 int idx = tabs.getSelectedIndex();
+				 if (idx != -1) {
+					 idx = (idx + 1) % tabs.getTabCount();
+					 //tabs.requestFocusInWindow();
+					 tabs.setSelectedIndex(idx);
+					 focusTab();
+				 }
+			}
+		};
+		KeyStroke forwardStroke = KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK);
+		im.put(forwardStroke, "myForwardAction");
+		am.put("myForwardAction", forwardAction);
+		
+		Action backwardAction = new AbstractAction()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				 int idx = tabs.getSelectedIndex();
+				 idx = (idx - 1);
+				 if (idx < 0) idx = tabs.getTabCount()-1; 
+				 tabs.setSelectedIndex(idx);
+				 focusTab();
+			}
+		};
+		KeyStroke backwardStroke = KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.SHIFT_DOWN_MASK + InputEvent.CTRL_DOWN_MASK);
+		im.put(backwardStroke, "myBackwardAction");
+		am.put("myBackwardAction", backwardAction);
+		
+	}
+	
+	private void focusTab()
+	{
+		SwingUtilities.invokeLater(new Runnable() {
+			public void run() {
+				tabs.getSelectedComponent().requestFocusInWindow();
+			}
+		});
+	}
+	
+	public boolean askIfClose()
+	{
+		if(tabs==null)return true;
+		int tabCount = tabs.getTabCount();
+		if(tabCount>1)
+		{
+			int answer = JOptionPane.showConfirmDialog(window,MessageFormat.format(I18N.gettext("main.chat.You_are_about_to_close_{0}_tabs_Are_you_sure_you_want_to_close_them?")
+				,new Object[]{new Integer(tabCount)}),""
+				,JOptionPane.YES_NO_OPTION);
+			if(answer==JOptionPane.NO_OPTION)return false;
+		}
+		return true;
+	}
+	
+	private void addMenu(final ChatWindow chatWindow)
+	{
+		final JMenu menu = chatWindow.getMenu();
+		if(menu.getItem(menu.getItemCount()-1).getName()==null)
+		{
+			final JMenuItem menuMoveToWindow = new JMenuItem();
+			final JMenuItem menuClose = new JMenuItem();
+			I18N.setTextAndMnemonic("main.chat.menu.Move_to_Window",menuMoveToWindow,true);
+			menuMoveToWindow.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					menu.remove(menuMoveToWindow);
+					menu.remove(menuClose);
+					removeTab(chatWindow);
+					chatWindow.removeSplitBarListener(dividerListener);
+					chatWindow.changeToWindow();
+					setChatWindowPosition(chatWindow);
+				}
+			});
+			menu.add(menuMoveToWindow);
+			I18N.setTextAndMnemonic("main.chat.menu.Close",menuClose,true);
+			menuClose.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					chatWindow.removeSplitBarListener(dividerListener);
+					chatWindow.exit();
+					chatWindows.remove(chatWindow);
+					removeTab(chatWindow);
+				}
+			});
+			menuClose.setName("close");
+			menu.add(menuClose);
+		}
+		window.getJMenuBar().add(menu);
+	}
+
+	private JPopupMenu tabMenu(final ChatWindow chatWindow)
+	{
+		JPopupMenu groupPopupMenu = new JPopupMenu();
+		JMenuItem menuItem = null;
+
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.chat.menu.Close",menuItem,true);
+		menuItem.addActionListener(new ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				chatWindow.removeSplitBarListener(dividerListener);
+				chatWindow.exit();
+				chatWindows.remove(chatWindow);
+				removeTab(chatWindow);
+			}
+		});
+		groupPopupMenu.add(menuItem);
+		return groupPopupMenu;
+	}
+	
+	public void convertToTab(ChatWindow cw,JIDStatus jidStatus)
+	{
+		if(window==null)makeNewTabWindow();
+		if(!window.isDisplayable()) addMenu(cw);
+		addTab(jidStatus,cw);
+		cw.setDividerLocation(dividerLocation);
+		cw.addSplitBarListener(dividerListener);
+		cw.setParentFrame(window);
+		window.setVisible(true);
+	}
+		
+	public synchronized void presenceChanged(Presence presence)
+	{
+		ChatWindow cw = getChatwindow(presence.getFrom()
+				,Preferences.getBoolean("jeti","chatwindowPerResource",false));
+		if(cw!=null) cw.appendPresenceChange(presence);
+	}
+	
+	private void beep()
+	{
+		if (Preferences.getBoolean("jeti","beep",true))
+			java.awt.Toolkit.getDefaultToolkit().beep();
+	}
+	
+	public void setTitleAt(ChatWindow cw, String title)
+	{
+		int index = tabs.indexOfComponent(cw);
+		tabs.setTitleAt(index, title);
+		if(tabs.getSelectedIndex()==index)
+		{
+			window.setTitle(title);
+		}
+	}
+	
+	public void setIconAt(ChatWindow cw, ImageIcon icon)
+	{
+		int index = tabs.indexOfComponent(cw);
+		tabs.setIconAt(index, icon);
+		if(tabs.getSelectedIndex()==index)
+		{
+			window.setIconImage(icon.getImage());
+		}
+	}
+	
+//	---------------------message event---------------------------
+	public synchronized void message(Message message)
+	{
+		JID from = message.getFrom();
+		if (message.getType().equals("chat") || (message.getType().equals("error") && message.getThread() != null))
+		{
+			beep();
+			ChatWindow chatWindow;
+			JIDStatus jidStatus = backend.getJIDStatus(message.getFrom());
+			String server = from.getDomain();
+			if(server.indexOf("conference")>-1 ||
+				server.indexOf("private")>-1 ||
+				server.indexOf("groupchat")>-1)
+			{//private messages from groupchat should have a window per resource
+				chatWindow = getChatwindow(from,true);
+				jidStatus = new UnknownJIDStatus(from,from.getResource());
+			}
+			else chatWindow = getChatwindow(from,Preferences.getBoolean("jeti","chatwindowPerResource",false));
+			if(tabs!=null)
+			{
+				int index = tabs.indexOfComponent(chatWindow);
+				if(index!=-1) 
+				{
+					if(tabs.getSelectedIndex()!=index)
+					{
+						tabs.setBackgroundAt(index,Color.BLUE);
+						tabs.setForegroundAt(index,Color.RED);
+					}
+					tabs.setToolTipTextAt(index,message.getBody());
+				}
+			}
+			if (chatWindow == null)
+			{
+				if (jidStatus == null)	chatWindow = startChat(new UnknownJIDStatus(from),message.getThread()); // ,"images",true,"unknown",e.getThread());
+				else chatWindow = startChat(jidStatus, message.getThread());
+			}
+			chatWindow.appendMessage(message);
+		}
+		else if(message.getType().equals("groupchat"))
+		{
+        	if(from.getResource()!=null) beep();
+		}
+		else
+		{
+			beep();
+			new SendMessage(backend, message);
+		}
+	}
+	
+	private ChatWindow getChatwindow(JID jid,boolean matchResources)
+	{
+		String resource =jid.getResource();
+		ChatWindow cw=null;
+		for (int tel = 0; tel < chatWindows.size(); tel++)
+		{
+			JID cwJID = ((ChatWindow)chatWindows.get(tel)).getJIDStatus().getCompleteJID();
+			if (cwJID.equals(jid))
+			{
+				if(resource!=null && resource.equals(cwJID.getResource()))
+				{
+					return  (ChatWindow) chatWindows.get(tel);
+				}
+				else if(!matchResources) cw = (ChatWindow) chatWindows.get(tel);
+			}
+		}
+		return cw;
+	}
+	
+	private void setChatWindowPosition(ChatWindow chatWindow)
+	{
+		if(chatWindows.isEmpty())
+		{
+			int posX = Preferences.getInteger("jeti","chatPosX",100);
+			int posY = Preferences.getInteger("jeti","chatPosY",100);
+			int screenX = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+			int screenY = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
+			if(posX>screenX) posX=100;
+			if(posY>screenY) posY=100;
+			chatWindow.setLocationOnScreen(posX,posY);
+		}
+		else
+		{
+			ChatWindow oldWindow = (ChatWindow)chatWindows.get(chatWindows.size()-1);
+			chatWindow.setLocationOnScreen(oldWindow);
+		}
+	}
+
+	public synchronized void removeChatWindow(ChatWindow chatWindow)
+	{
+		chatWindows.remove(chatWindow);
+	}
+
+	public synchronized void onComposing(JID from, String thread, XMessageEvent messageEvent)
+	{
+		ChatWindow chatWindow = null;
+		{
+			chatWindow = getChatwindow(from,Preferences.getBoolean("jeti","chatwindowPerResource",false));
+			if(chatWindow!=null)chatWindow.composing(messageEvent.getType());
+		}
+	}
+	
+	public synchronized void requestComposing(JID from, String id, String thread)
+	{
+		ChatWindow chatWindow = null;
+		{
+			chatWindow = getChatwindow(from,Preferences.getBoolean("jeti","chatwindowPerResource",false));
+			if(chatWindow!=null)chatWindow.composingID(id);
+		}
+	}
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/Jeti.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/Jeti.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/Jeti.java	(revision 3952)
@@ -0,0 +1,809 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.ui;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowEvent;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.text.DefaultEditorKit;
+import javax.swing.text.JTextComponent;
+import javax.swing.tree.TreeModel;
+
+import nu.fw.jeti.backend.Start;
+import nu.fw.jeti.backend.roster.JIDStatusGroup;
+import nu.fw.jeti.backend.roster.PrimaryJIDStatus;
+import nu.fw.jeti.events.PreferenceListener;
+import nu.fw.jeti.events.PresenceListener;
+import nu.fw.jeti.events.RosterListener;
+import nu.fw.jeti.events.StatusChangeListener;
+import nu.fw.jeti.images.StatusIcons;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.UnknownJIDStatus;
+import nu.fw.jeti.jabber.elements.*;
+import nu.fw.jeti.plugins.PluginsInfo;
+import nu.fw.jeti.plugins.RosterMenuListener;
+import nu.fw.jeti.ui.models.RosterTreeModel;
+import nu.fw.jeti.util.*;
+
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class Jeti extends JPanel implements PresenceListener, StatusChangeListener, PreferenceListener
+{
+	private RosterTree onlinePanel;
+	//private RosterTree offlinePanel;
+	private JPopupMenu popupMenu;
+    private JMenu popdownMenu;
+	private JToggleButton btnJeti;
+    private StatusButton btnStatus;
+	private JPanel pnlMenuButtons;
+	private JPanel empty = new JPanel();
+	private JScrollPane jScrollPaneTop;
+	private JPanel pnlRoster;
+	private JSplitPane jSplitPane1;
+	private JScrollPane scrollPaneBottom;
+	private ServerTree serverPanel;
+	private int status;
+	private String message = "";
+	private List menuItems;
+	private JetiFrame jetiFrame;
+	private JetiDialog jetiDialog;
+	private boolean heightInvalid=true;
+	private Map rosterMenuItems= new HashMap(10);
+	private ChatWindows chatWindows;
+	private Image backgroundImage;
+
+	private Backend backend;
+
+    private static final String TITLE = "Jeti"+Start.OS2;
+
+	public Jeti(Backend backend,Container container)
+	{
+
+		if(container!=null)backend.setMain(this,null,null);
+		else
+		{
+			jetiFrame = new JetiFrame();
+			backend.setMain(this,jetiFrame,jetiFrame);
+			if(Preferences.getBoolean("jeti","showNotInTaskbar",false))
+			{
+				jetiFrame.setIconImage(StatusIcons.getOfflineIcon().getImage());
+				jetiDialog = new JetiDialog(jetiFrame);
+				backend.setMain(this,jetiDialog,null);
+			}
+		}
+		this.backend = backend;
+				
+		backend.addListener(PresenceListener.class, this);
+		backend.addListener(StatusChangeListener.class, this);
+		backend.addListener(PreferenceListener.class, this);
+
+		RosterTreeModel model = new RosterTreeModel();
+		backend.addListener(RosterListener.class, model);
+		
+		// Exibe a lista de Contato
+		TreeModel onlineModel = model;
+
+		if (UIManager.getLookAndFeel().isNativeLookAndFeel())
+		{
+			System.out.println("using mac hack");
+		    onlineModel = new TreeModelFilter(model, new OnlineSelector());
+		}
+		
+	    onlinePanel = new RosterTree(backend, this, true, onlineModel);
+		
+		serverPanel = new ServerTree(backend, this);
+		chatWindows = new ChatWindows(backend);
+		refreshBackgroundImage();
+	}
+
+	public void init()
+	{
+		setLayout(new BorderLayout());
+		btnStatus = new StatusButton(backend, this);
+		pnlMenuButtons = new JPanel();
+		jScrollPaneTop = new JScrollPane();
+		pnlRoster = new JPanel();
+		jSplitPane1 = new JSplitPane();
+		scrollPaneBottom = new JScrollPane();
+		boolean opaque = Preferences.getBoolean("jeti","bmw",true);
+		pnlRoster.setBackground(new Color(255,255,255));
+		onlinePanel.setBackground(new Color(255,255,255));
+		serverPanel.setBackground(new Color(255,255,255));
+		jScrollPaneTop.setOpaque(opaque);
+		jScrollPaneTop.getViewport().setOpaque(opaque);
+		scrollPaneBottom.setOpaque(opaque);
+		scrollPaneBottom.getViewport().setOpaque(opaque);
+		empty.setBackground(new Color(255,255,255));
+		Font bold =(UIManager.getFont("TitledBorder.font").deriveFont(Font.BOLD));
+		scrollPaneBottom.setMinimumSize(new Dimension(22, 0));
+		jScrollPaneTop.getViewport().add(pnlRoster, null);
+		pnlMenuButtons.setLayout(new BoxLayout(pnlMenuButtons, BoxLayout.X_AXIS));
+		
+		if (Preferences.getBoolean("jeti","menutop",false))
+		{
+            createJetiMenu();
+        }
+		
+		pnlMenuButtons.add(btnStatus, null);
+		
+		add(jScrollPaneTop, BorderLayout.CENTER);
+		add(pnlMenuButtons, java.awt.BorderLayout.SOUTH);
+
+		pnlRoster.setLayout(new BorderLayout());
+		pnlRoster.add(onlinePanel, BorderLayout.CENTER);
+		
+
+		if(jetiDialog!=null)jetiDialog.init(this);
+		else if(jetiFrame!=null)jetiFrame.init(this);
+		
+		int divLoc = Preferences.getInteger("jeti","dividerLocation",-10);
+		if(divLoc == -10) jSplitPane1.setDividerLocation(0.75);
+		else jSplitPane1.setDividerLocation(divLoc);
+		
+		initMenu();
+		
+		updateLF();
+	}
+
+    private void createJetiMenu()
+    {
+        JMenuBar menuBar = new JMenuBar();
+        popdownMenu = new JMenu(TITLE);
+        popdownMenu.setMnemonic('J');
+        menuBar.add(popdownMenu);
+        jetiFrame.setJMenuBar(menuBar);
+    }
+
+    private void createBtnJeti()
+    {
+		btnJeti = new JToggleButton(TITLE);
+		btnJeti.setMnemonic('J');
+        btnJeti.setMaximumSize(new Dimension(500, 23));
+		btnJeti.setMargin(new Insets(0, 0, 0, 0));
+		btnJeti.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+		
+				if (heightInvalid)
+				{
+				  	popupMenu.show(Jeti.this, 0, 0);
+				  	popupMenu.setVisible(false);
+		            heightInvalid = false;
+		        }
+				popupMenu.show(Jeti.this, btnJeti.getX(), btnJeti.getY() + pnlMenuButtons.getY() - popupMenu.getHeight() );
+			}
+		});
+        popupMenu = new JPopupMenu();
+        popupMenu.addPopupMenuListener(new PopupMenuListener()
+        {
+        
+            public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
+            {
+                btnJeti.setSelected(false);
+            }
+            public void popupMenuWillBecomeVisible(PopupMenuEvent e)
+            {}
+            public void popupMenuCanceled(PopupMenuEvent e)
+            {}
+        });
+    }
+
+	public void close()
+	{
+		if(chatWindows.askIfClose())
+		{
+			saveOpenGroups();
+			Container w = backend.getMainWindow();
+			if(w!=null && w instanceof Window)((Window)w).dispose();
+			System.out.println("Exiting, closing connection please wait....");
+			//todo disconnect backend.disconnect(); and exit instead of only exit
+			backend.exit();
+			backend=null;
+		}
+	}
+
+	private void startChat(final boolean message)
+	{
+		final JDialog dialog = new JDialog(backend.getMainFrame());
+		if(message)dialog.setTitle(I18N.gettext("main.main.jetimenu.Message"));
+		else dialog.setTitle(I18N.gettext("main.main.jetimenu.Chat"));
+		final JIDInput jidInput = new JIDInput(backend);
+		JButton btnOK = new JButton(I18N.gettext("OK"));
+		btnOK.addActionListener(new ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				JID jid  = jidInput.createJID(); 
+				if(jid==null)return;
+				if(message) new SendMessage(backend,jid,jid.getUser()).setVisible(true);
+				else startChat(jid);
+				dialog.dispose();
+			}
+		});
+		JButton btnCancel = new JButton();
+		dialog.getRootPane().setDefaultButton(btnOK);
+		dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+		
+		Action cancelAction = new AbstractAction(I18N.gettext("Cancel"))
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				dialog.dispose();
+			}
+		};
+		Utils.addCancelButton(dialog, btnCancel, cancelAction);
+		Box panel1 = Box.createVerticalBox();
+		panel1.add(Box.createHorizontalGlue());
+		JPanel jPanel1 = new JPanel();
+		jPanel1.add(btnOK);
+		jPanel1.add(btnCancel);
+		panel1.add(jidInput);
+		panel1.add(jPanel1);
+		dialog.getContentPane().add(panel1, BorderLayout.CENTER);
+		dialog.pack();
+		dialog.setLocationRelativeTo(this);
+		dialog.setVisible(true);
+	}
+	
+	public void startChat(JID jid)
+	{
+		if (jid == null) 
+			return;
+		chatWindows.chat(new UnknownJIDStatus(jid));
+	}
+	
+	public void startChatResource(JIDStatus jidstatus)
+	{
+		chatWindows.chatResource(jidstatus);
+	}
+	
+	public void chat(JIDStatus jidStatus)
+	{
+		chatWindows.chat(jidStatus);
+	}
+	
+	public JPanel createChatPanel(JIDStatus jidStatus)
+	{
+		return chatWindows.createChatPanel(jidStatus);
+	}
+	
+	public void addToMenu(JMenuItem menuItem)
+	{
+		if( menuItems == null )
+			menuItems = new LinkedList();
+		menuItems.add(menuItem);
+		initMenu();
+	}
+	
+	public Map getRosterMenuItems()
+	{
+		return rosterMenuItems; 
+	}
+	
+	public void removeFromMenu(JMenuItem menuItem)
+	{
+		menuItems.remove(menuItem);
+		if(menuItems.isEmpty()) menuItems = null;
+		initMenu();
+	}
+
+	public void addToRosterMenu(String name, RosterMenuListener listener)
+	{
+		onlinePanel.addToMenu(name, listener);
+		//offlinePanel.addToMenu(name, listener);
+		rosterMenuItems.put(name,listener);
+	}
+	
+	public void removeFromRosterMenu(String name)
+	{
+		onlinePanel.removeFromMenu(name);
+		//offlinePanel.removeFromMenu(name);
+		rosterMenuItems.remove(name);
+	}
+	
+	public void addToOnlineRosterMenu(String name, RosterMenuListener listener)
+	{
+		onlinePanel.addToMenu(name, listener);
+		rosterMenuItems.put(name,listener);
+	}
+	
+	public void removeFromOnlineRosterMenu(String name)
+	{
+		onlinePanel.removeFromMenu(name);
+		rosterMenuItems.remove(name);
+	}
+	
+	public void addToOfflineRosterMenu(String name, RosterMenuListener listener)
+	{
+		//offlinePanel.addToMenu(name, listener);
+	}
+	
+	public void removeFromOfflineRosterMenu(String name)
+	{
+		//offlinePanel.removeFromMenu(name);
+	}
+
+    private void addMenuItem(JMenuItem item)
+    {
+        if (popupMenu != null)
+        {
+            popupMenu.add(item);
+        }
+        else
+        {
+            popdownMenu.add(item);
+        }
+    }
+
+	private void initMenu()
+	{
+		//JETI menu
+        if ( popdownMenu == null && popupMenu == null )
+            return;
+
+        if ( popdownMenu != null )
+            popdownMenu.removeAll();
+        else
+            popupMenu.removeAll();
+		
+        JMenuItem menuItem = null;
+		JMenu subMenu = null;
+		
+		if(Preferences.getBoolean("jeti","jetimenuShowMessage",true))
+		{
+			menuItem = new JMenuItem(I18N.gettext("main.main.jetimenu.Message")+"...");
+			I18N.setMnemonic("main.main.jetimenu.Message",menuItem);  
+			menuItem.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					startChat(true);  
+				}
+			});
+	        addMenuItem(menuItem);
+		}
+		
+		if(Preferences.getBoolean("jeti","jetimenuShowChat",true))
+		{
+			menuItem = new JMenuItem(I18N.gettext("main.main.jetimenu.Chat")+"...");
+			I18N.setMnemonic("main.main.jetimenu.Chat",menuItem); 
+			menuItem.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					startChat(false);
+				}
+			});
+	        addMenuItem(menuItem);
+		}
+		
+		if(Preferences.getBoolean("jeti","jetimenuShowAddContact",true))
+		{
+			menuItem = new JMenuItem(I18N.gettext("main.main.jetimenu.Add_Contact")+"...");
+			I18N.setMnemonic("main.main.jetimenu.Add_Contact",menuItem); 
+			menuItem.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					new AddContact(backend).setVisible(true);
+				}
+			});
+	        addMenuItem(menuItem);
+		}
+		
+		//add plugin menus
+		if( menuItems != null )
+		{
+			for(Iterator i = menuItems.iterator();i.hasNext();)
+			{
+				JMenuItem item = (JMenuItem)i.next();
+                addMenuItem(item);
+			}
+		}
+		
+		menuItem = new JMenuItem();
+		
+		if(Preferences.getBoolean("jeti","jetimenuShowLog",true))
+		{
+	        I18N.setTextAndMnemonic("main.main.jetimenu.Show_Log",menuItem,true);
+			menuItem.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					serverPanel.clearError();
+					new LogWindow(backend).setVisible(true);
+				}
+			});
+			addMenuItem(menuItem);
+		}
+        
+        menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.jetimenu.About",menuItem,true);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				new AboutWindow().setVisible(true);
+			}
+		});
+        addMenuItem(menuItem);
+        if(Preferences.getBoolean("jeti","jetimenuShowExit",true))
+        {
+			final JMenuItem menuItem2 = new JMenuItem(I18N.gettext("main.main.jetimenu.Exit"));
+			I18N.setMnemonic("main.main.jetimenu.Exit",menuItem2);
+			menuItem2.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					//menuItem2.removeActionListener(this);
+					close();
+				}
+			});
+	        addMenuItem(menuItem2);
+        }
+		heightInvalid=true;
+	}
+
+	protected void saveOpenGroups()
+	{
+		List groups = onlinePanel.getOpenGroups();
+		if(groups != null)	backend.send(new InfoQuery("set",new IQPrivate(new JetiPrivateRosterExtension((String[]) groups.toArray(new String[] {})))));
+	}
+	
+	public void openGroups(final JetiPrivateRosterExtension extension)
+	{
+		SwingUtilities.invokeLater(new Runnable()
+		{
+		
+			public void run()
+			{
+				onlinePanel.openGroups(extension);
+				serverPanel.openGroups();
+			}
+		});
+	}
+	
+	public void updateLF()
+	{
+        if (popupMenu != null)
+        {
+            SwingUtilities.updateComponentTreeUI(popupMenu);
+        }
+        
+        if (popdownMenu != null)
+        {
+            SwingUtilities.updateComponentTreeUI(popdownMenu);
+        }
+        btnStatus.updateLF();
+		onlinePanel.updateLF();
+		//offlinePanel.updateLF();
+		serverPanel.updateLF();
+	}
+	
+	public void changeOFFlinePanel(boolean show)
+	{
+		if(show)
+		{ 
+			pnlRoster.remove(empty);
+			//pnlRoster.add(offlinePanel, BorderLayout.CENTER);
+		}
+		else 
+		{
+			//pnlRoster.remove(offlinePanel);
+			pnlRoster.add(empty, BorderLayout.CENTER);
+		}
+		pnlRoster.validate();
+		pnlRoster.repaint();
+	}
+	
+	
+	/**
+	 *  translate the main display if another locale is choosen
+	 */
+	public void translate()
+	{
+		preferencesChanged();
+		onlinePanel.createPopupMenu();
+		onlinePanel.setBorder(new TitledBorder(BorderFactory.createEmptyBorder(), I18N.gettext("main.main.List_Contacts")));
+	}
+	
+	public void refreshBackgroundImage()
+	{
+		String image = Preferences.getString("jeti", "backgroundImage", null);
+		
+		if(image != null)
+		{
+			if(Start.applet)
+			{
+				try
+				{
+					URL url= new URL(image);
+					backgroundImage = new ImageIcon(url).getImage();
+				}
+				catch(MalformedURLException e)
+				{
+					e.printStackTrace();
+				}
+			}
+			else backgroundImage = new ImageIcon(image).getImage();
+		}
+		repaint();
+	}
+
+	public void preferencesChanged()
+	{
+		initMenu(); //remake options
+		StatusButton.reloadMessages();
+	}
+	
+	/*--------------------Status Change events----------------------------------*/
+	public void presenceChanged(Presence presence)
+	{
+		JIDStatus jidStatus = backend.getJIDStatus(presence.getFrom());
+		String nick = null;
+		if (jidStatus != null) nick = jidStatus.getNick();
+		if (nick == null) nick = presence.getFrom().getUser();
+		if(jetiFrame!=null) jetiFrame.initTimer(nick,Presence.toLongShow(presence.getShow())); 
+		//beep();
+		//chatWindows.presenceChanged(presence);
+	}
+	
+	public void ownPresenceChanged(int astatus, String amessage)
+	{
+		this.status = astatus;
+		this.message = amessage;
+        btnStatus.ownPresenceChanged(status, message);
+        Runnable updateAComponent = new Runnable()
+        {
+            public void run()
+            {
+                if( jetiFrame != null )
+                {
+                    ImageIcon icon = StatusIcons.getStatusIcon(status);
+                    jetiFrame.setIconImage(icon.getImage());
+                }
+            }
+        };
+        SwingUtilities.invokeLater(updateAComponent);
+	}
+	
+	public void connectionChanged(boolean online)
+	{
+		if ( online )
+		{
+			if( jetiFrame != null)
+				jetiFrame.setTitle(TITLE);
+			onlinePanel.setVisible(true);
+			serverPanel.setVisible(true);
+		}
+		else
+		{
+			btnStatus.connectionOffline();
+            Runnable updateAComponent = new Runnable() {
+                public void run() {
+                	onlinePanel.setVisible(false);
+                	serverPanel.setVisible(false);
+                    if(jetiFrame!=null) {
+						jetiFrame.setTitle(I18N.gettext("main.main.Offline"));
+						jetiFrame.setIconImage(
+                            StatusIcons.getOfflineIcon().getImage());
+                    }
+                }
+            };
+            SwingUtilities.invokeLater(updateAComponent);
+		}
+	}
+
+	public void exit()
+	{
+		//if size of main window changed save
+		Window w = jetiDialog == null ? (Window)jetiFrame : (Window)jetiDialog;
+		
+		if( w != null )
+		{
+			Preferences.putInteger("jeti","height",w.getHeight());
+			Preferences.putInteger("jeti","width",w.getWidth());
+	 		Preferences.putInteger("jeti","posX",w.getX());
+			Preferences.putInteger("jeti","posY",w.getY());
+			Preferences.putInteger("jeti","dividerLocation",jSplitPane1.getDividerLocation());
+			Preferences.save();
+		}
+		chatWindows.exit();
+	}
+	
+	public void paint (Graphics g)
+	{
+		super.paint(g);
+		if(backgroundImage!=null)
+		{
+			if(Preferences.getBoolean("jeti","backgroundImageScaled",true))
+			{
+				g.drawImage(backgroundImage, 0, 0,getWidth(),getHeight(), null);
+			}
+			else
+			{
+				g.drawImage(backgroundImage, 0, 0,null);
+			}
+		}
+		paintChildren(g);
+	}
+
+	static {
+		JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP).addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 1),new DefaultEditorKit.PasteAction());
+		JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP).addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 2),new DefaultEditorKit.CopyAction());
+		JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP).addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 1),new DefaultEditorKit.CutAction());
+		Action findAction = new FindAction();
+		JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP).addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F, 2),findAction);
+		JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP).addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0),findAction);
+	}
+	
+	//only for filetransfer drag&drop
+	public RosterTree getOnlinePanel()
+	{
+        return onlinePanel;
+    }
+
+static class JetiDialog extends JDialog
+{
+	public JetiDialog(JFrame frame)
+	{
+		super(frame);
+	}
+
+	public void init(final Jeti jeti)
+	{
+		setContentPane(jeti);
+		this.setTitle(TITLE);
+		if (PluginsInfo.isPluginLoaded("systemtray")) setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
+		else
+		{
+			addWindowListener(new java.awt.event.WindowAdapter()
+			{
+				public void windowClosing(WindowEvent e)
+				{
+					setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+					jeti.close();
+				}
+			});
+		}
+		
+		int heigth = Preferences.getInteger("jeti","height",0);
+		int width = Preferences.getInteger("jeti","width",0);
+		if( heigth ==0 || width == 0) pack();
+		else setSize(width,heigth);
+		int x = Preferences.getInteger("jeti","posX",50);
+		int y = Preferences.getInteger("jeti","posY",50);
+		int screenX = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+		int screenY = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
+		if(x>screenX) x=50;
+		if(y>screenY) y=50;
+		setLocation(x,y);
+	}
+}
+
+
+static class JetiFrame extends JFrame
+{
+	private TitleTimer timer = new TitleTimer(this, TITLE);
+	
+	
+	public void init(final Jeti jeti)
+	{
+		setContentPane(jeti);
+		setIconImage(StatusIcons.getOfflineIcon().getImage());
+		this.setTitle(TITLE);
+		if (PluginsInfo.isPluginLoaded("systemtray")) setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
+		else
+		{
+			setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+			addWindowListener(new java.awt.event.WindowAdapter()
+			{
+				public void windowClosing(WindowEvent e)
+				{
+					jeti.close();
+				}
+			});
+		}
+		
+		int heigth = Preferences.getInteger("jeti","height",0);
+		int width = Preferences.getInteger("jeti","width",0);
+		if( heigth ==0 || width == 0) pack();
+		else setSize(width,heigth);
+		int x = Preferences.getInteger("jeti","posX",50);
+		int y = Preferences.getInteger("jeti","posY",50);
+		int screenX = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
+		int screenY = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
+		if(x>screenX) x=50;
+		if(y>screenY) y=50;
+		setLocation(x,y);
+	}
+	
+	public void initTimer(String nick, String status)
+	{
+		timer.init(nick,status);
+	}
+}
+
+	static class OnlineSelector implements TreeModelSelector
+	{
+        public boolean isVisible(Object o)
+        {
+			if (o instanceof JIDStatus)
+			{
+                return ((JIDStatus)o).isOnline();
+            }
+			else if (o instanceof PrimaryJIDStatus)
+			{
+                return ((PrimaryJIDStatus)o).getJIDPrimaryStatus().isOnline();
+            }
+			else if (o instanceof JIDStatusGroup)
+			{
+				return (((JIDStatusGroup)o).getOnlines() > 0);
+			}
+            return false;
+        }
+    }
+
+    static  class OfflineSelector implements TreeModelSelector
+    {
+        public boolean isVisible(Object o)
+        {
+			if (o instanceof JIDStatus)
+			{
+                return !((JIDStatus)o).isOnline();
+            }
+			else if (o instanceof PrimaryJIDStatus)
+			{
+                return ((PrimaryJIDStatus)o).isAJIDstatusOffline();
+            }
+			else if (o instanceof JIDStatusGroup)
+			{
+				return true;
+            }
+            return true;
+        }
+    }
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/.cvsignore
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/.cvsignore	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/.cvsignore	(revision 3952)
@@ -0,0 +1,4 @@
+GroupchatSignin.jav
+GroupchatWindow.jav
+Kopie van ChatWindow.jav
+PopupPanel.jav
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/SendMessage.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/SendMessage.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/SendMessage.java	(revision 3952)
@@ -0,0 +1,280 @@
+package nu.fw.jeti.ui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.text.Document;
+
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.Message;
+import nu.fw.jeti.plugins.PluginsInfo;
+import nu.fw.jeti.plugins.Translator;
+import nu.fw.jeti.plugins.Word;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Popups;
+
+/**
+ * Title:        im
+ * Description:
+ * Copyright:    Copyright (c) 2001
+ * Company:
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class SendMessage extends JFrame
+{
+    private static final int MIN_WIDTH  = 300;
+    private static final int MIN_HEIGHT = 200;
+	private JButton jButton1 = new JButton();
+	private Backend  backend;
+	private JID jid;
+	private String user;
+    private JScrollPane jScrollPane1 = new JScrollPane();
+    private JTextPane jTextArea1 = new JTextPane();
+    private JTextField txtSubject = new JTextField();
+    //private boolean enterSends;
+
+	public SendMessage(Backend backend,JID jid, String user)
+	{//send
+		this.backend = backend;
+		this.jid = jid;
+	//	jTextArea1.setLineWrap(true);
+		//enterSends = Preferences.getBoolean("jeti","enterSends",true);
+		setTitle(user);
+		try
+		{
+			jbInit();
+			jButton1.setText(I18N.gettext("Send"));
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+		jTextArea1.addKeyListener(new java.awt.event.KeyAdapter()
+		{
+			public void keyPressed(KeyEvent e)
+			{
+				txtInvoer_keyPressed(e);
+			}
+		});
+        fixSize();
+	}
+	
+	public SendMessage(Backend backend,JIDStatus jidstatus,String subject,String text)
+	{//xmppuri
+		this(backend,jidstatus.getJID(),jidstatus.getNick());
+		txtSubject.setText(subject);
+		jTextArea1.setText(text);
+	}
+	
+	public SendMessage(Backend backend,JID jid, String user,String title)
+	{//reply
+		this(backend,jid,user);
+		txtSubject.setText("Re: " + title);
+	}
+	
+	public SendMessage(Backend backend,Message messageElement)
+	{//receive
+		this.backend = backend;
+
+        if (messageElement.getType().equals("error")) {
+            handleError(messageElement);
+            return;
+        }
+
+		getRootPane().setDefaultButton(jButton1);
+		JIDStatus jidStatus = backend.getJIDStatus(messageElement.getFrom());
+		jid = messageElement.getFrom();
+		if (jidStatus == null || jidStatus.getNick() == null ) {
+            if (jid.getResource() != null) {
+                user = jid.getUser() + "/" + jid.getResource();
+            } else {
+                user = jid.getUser();
+            }
+		} else {
+            user = jidStatus.getNick();
+        }
+		String subject = messageElement.getSubject(); 
+		if (subject == null) {
+            setTitle(user);
+		} else {
+            setTitle(user +" | " + subject);
+        }
+
+		try
+		{
+			jbInit();
+			//jButton1.setMnemonic('R');
+			I18N.setTextAndMnemonic("main.popup.Reply",jButton1);
+			String message = messageElement.getBody();
+			if (PluginsInfo.isPluginLoaded("links"))
+			{
+				//jTextArea1.setDocument(new DefaultStyledDocument());
+				Translator links = (Translator) PluginsInfo.newPluginInstance("links");
+				links.init(jTextArea1);
+				List wordList = ChatSplitPane.createWordList(message);
+	            links.translate(wordList);
+	            // Insert words from wordlist
+	            Document doc = jTextArea1.getDocument();
+				for(Iterator i = wordList.iterator();i.hasNext();) {
+					Word w = (Word) i.next();
+					doc.insertString(doc.getLength(),w.toString(),
+	                                 w.getAttributes());
+				}
+			}
+			else jTextArea1.setText(message);
+			jTextArea1.setEditable(false);
+			txtSubject.setText(subject);
+			txtSubject.setEditable(false);
+			if(message.indexOf(System.getProperty("line.separator")) == -1)
+			{// if enters message is expected to be well formated, otherwise format
+				int naamLengte = (getFontMetrics((Font)UIManager.get("TextArea.font"))).stringWidth(message);
+				//System.out.println(naamLengte);
+				if(naamLengte > 400)
+				{//message is to long enable autowrap
+					setSize(400,300);
+	//				jTextArea1.setLineWrap(true);
+					setVisible(true);
+					return;
+				}
+			}
+		}
+		catch(Exception e)
+		{
+			e.printStackTrace();
+		}
+		pack();
+		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+		if(getHeight() > screenSize.getHeight() || getWidth() > screenSize.getWidth())
+		{
+			setSize((int)screenSize.getWidth()-50,(int)screenSize.getHeight()-50);
+		}
+        fixSize();
+        setVisible(true);
+	}
+
+	private void jbInit() throws Exception
+	{
+		//setIconImage(nu.fw.jeti.images.StatusIcons.getImageIcon("jeti").getImage());
+		//getRootPane().setDefaultButton(jButton1);
+		jButton1.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				jButton1_actionPerformed(e);
+			}
+		});
+        this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+        //jScrollPane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ //       jTextArea1.setWrapStyleWord(true);
+        this.getContentPane().add(jScrollPane1, BorderLayout.CENTER);
+        jScrollPane1.getViewport().add(jTextArea1, null);
+		//    this.getContentPane().add(jTextArea1, BorderLayout.CENTER);
+
+		this.getContentPane().add(jButton1, BorderLayout.SOUTH);
+		JPanel panel = new JPanel(new BorderLayout());
+		panel.add(new JLabel(I18N.gettext("main.popup.Subject")), BorderLayout.NORTH);
+		panel.add(txtSubject, BorderLayout.CENTER);
+		panel.add(new JLabel(I18N.gettext("main.popup.Message")), BorderLayout.SOUTH);
+		this.getContentPane().add(panel,BorderLayout.NORTH);
+		setLocationRelativeTo(null);
+	}
+
+    private void fixSize()
+    {
+        Dimension d = getSize();
+
+        if (d.height < MIN_HEIGHT) {
+            d.height = MIN_HEIGHT;
+        }
+        if (d.width < MIN_WIDTH) {
+            d.width = MIN_WIDTH;
+        }
+        if (d != getSize()) {
+            setSize(d);
+        }
+    }
+
+	void jButton1_actionPerformed(ActionEvent e)
+	{
+		if(jButton1 .getText().equals(I18N.gettext("Send")))
+		{
+			String subject =txtSubject.getText();
+			if(subject.equals("")) subject = null;
+		    backend.sendMessage(new Message(jTextArea1.getText(),subject,jid));
+		    this.dispose();
+		}
+		else new SendMessage(backend ,jid,user,txtSubject.getText()).show();
+	}
+	
+	void txtInvoer_keyPressed(KeyEvent e)
+	{
+		if (e.getKeyCode() == KeyEvent.VK_ENTER)
+		{
+//			if (enterSends)
+//			{
+//				if ((e.getModifiers() == KeyEvent.SHIFT_MASK) || (e.getModifiers() == KeyEvent.CTRL_MASK))
+//				{
+//					//txtInvoer.setText(txtInvoer.getText() +"\n");
+//					Document doc = jTextArea1.getDocument();
+//					try
+//					{
+//						doc.insertString(jTextArea1.getCaretPosition(), "\n", null);
+//					}
+//					catch (BadLocationException e3)
+//					{}
+//				}
+//				else
+//				{
+//					//send();
+//					jButton1.doClick();
+//					e.consume();
+//				}
+//			}
+//			else
+			{
+				if ((e.getModifiers() == InputEvent.SHIFT_MASK) || (e.getModifiers() == InputEvent.CTRL_MASK))
+				{
+					//send();
+					jButton1.doClick();
+				}
+			}
+		}
+	}	
+
+	private void handleError(Message message) {
+        String error;
+
+        switch (message.getErrorCode()) {
+        case 404:
+            error = MessageFormat.format(
+                I18N.gettext("main.error.User_{0}_could_not_be_found"),
+                new Object[] {message.getFrom()});
+            break;
+        default:
+            error = I18N.gettext("main.error.Error_in_chat")
+                + " " + message.getBody();
+            break;
+        }
+        Popups.errorPopup(error,I18N.gettext("main.error.Error"));
+    }
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/ui/RosterTree.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/ui/RosterTree.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/ui/RosterTree.java	(revision 3952)
@@ -0,0 +1,922 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at jeti@jabberstudio.org 
+ *  created 2001
+ */
+
+package nu.fw.jeti.ui;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.*;
+import java.text.MessageFormat;
+import java.util.*;
+
+import javax.swing.*;
+import javax.swing.Timer;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+import nu.fw.jeti.backend.roster.JIDStatusGroup;
+import nu.fw.jeti.backend.roster.JIDStatusTree;
+import nu.fw.jeti.backend.roster.PrimaryJIDStatus;
+import nu.fw.jeti.images.StatusIcons;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.JIDStatus;
+import nu.fw.jeti.jabber.elements.*;
+import nu.fw.jeti.plugins.PluginsInfo;
+import nu.fw.jeti.plugins.RosterMenuListener;
+import nu.fw.jeti.util.I18N;
+import nu.fw.jeti.util.Preferences;
+import nu.fw.jeti.util.TreeExpander;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class RosterTree extends JTree
+{
+	private Backend backend;
+	private JPopupMenu popupMenu;
+	private JPopupMenu groupPopupMenu;
+	private JIDStatus currentJIDStatus;
+	private JIDStatusGroup currentJIDStatusGroup2;
+	private JIDStatusGroup currentJIDStatusGroup;
+	private boolean onlineTree;
+	private Jeti main;
+	private String currentGroup;
+	private Map menuItems; //added menus
+    private TreeExpander treeExpander;
+
+    /**
+     * @param type True for an online tree
+     */
+	public RosterTree( Backend backend, Jeti main, boolean onlineTree, TreeModel model )
+	{
+		super(model);
+		this.onlineTree = onlineTree;
+		this.backend = backend;
+		this.main = main;
+
+		ToolTipManager.sharedInstance().registerComponent(this);
+
+		setRootVisible(false);
+		setToggleClickCount(0);//set expanding on mouseclicks of because
+							   // detection needed for single or double click
+
+		javax.swing.plaf.basic.BasicTreeUI basicTreeUI = (javax.swing.plaf.basic.BasicTreeUI) getUI();
+		basicTreeUI.setRightChildIndent(1);
+		basicTreeUI.setLeftChildIndent(1);
+		basicTreeUI.setExpandedIcon(null);
+		basicTreeUI.setCollapsedIcon(null);
+		putClientProperty("JTree.lineStyle", "None");
+
+		createPopupMenu();
+		createGroupPopupMenu();
+		if (UIManager.getLookAndFeel().isNativeLookAndFeel())
+		{
+			setCellRenderer(new MacRenderer());
+            if (onlineTree)
+            {
+                treeExpander = new TreeExpander(this, model);
+            }
+ 		}
+		else setCellRenderer(new MyRenderer());
+
+		addMouseListener(new MouseAdapter()
+		{
+			//save tree path for single click event
+			TreePath lastTreePath;
+			
+			//timer needed to check if double or single mouseclick
+			Timer timer = new Timer(300, new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					if (isExpanded(lastTreePath)) collapsePath(lastTreePath);
+					else expandPath(lastTreePath);
+					timer.stop();
+				}
+			});
+
+			public void mousePressed(MouseEvent e)
+			{
+				TreePath selPath = getPathForLocation(e.getX(), e.getY());
+				if (selPath != null)
+				{
+					setSelectionPath(selPath);
+					Object o = selPath.getLastPathComponent();
+					if (o instanceof JIDStatusGroup)
+					{//group
+						maybeShowGroupPopup(e, (JIDStatusGroup) o);
+						if (SwingUtilities.isLeftMouseButton(e))
+						{
+							if (isExpanded(selPath)) collapsePath(selPath);
+							else expandPath(selPath);
+						}
+					}
+					else
+					{
+						JIDStatusGroup group = null;
+						JIDStatus jidStatus = null;
+						if (o instanceof PrimaryJIDStatus)
+						{
+							group = ((JIDStatusGroup) selPath.getPathComponent(selPath.getPathCount() - 2));
+							jidStatus = ((PrimaryJIDStatus) o).getJIDPrimaryStatus();
+						}
+						else if (o instanceof JIDStatus)
+						{
+							group = ((JIDStatusGroup) selPath.getPathComponent(selPath.getPathCount() - 3));
+							jidStatus = (JIDStatus)o;
+						}
+						else return;
+						maybeShowPopup(e, jidStatus, group);//cde
+						
+						if (SwingUtilities.isLeftMouseButton(e))
+						{
+							lastTreePath = selPath;
+							//check if double or single mouseclick, needed
+							// because double click= 2 single clicks
+							if (timer.isRunning())
+							{
+								timer.stop();
+								sendChat(jidStatus);
+							}
+							else
+							{
+								timer.restart();
+							}
+						}
+					}
+				}
+			}
+
+			public void mouseReleased(MouseEvent e)
+			{
+				TreePath selPath = getPathForLocation(e.getX(), e.getY());
+				if (selPath != null)
+				{
+					Object o = selPath.getLastPathComponent();
+					if (o instanceof JIDStatusGroup)
+					{//group
+						maybeShowGroupPopup(e, (JIDStatusGroup) o);
+					}
+					if (o instanceof PrimaryJIDStatus)
+					{
+						JIDStatusGroup group = ((JIDStatusGroup) selPath.getPathComponent(selPath.getPathCount() - 2));
+						maybeShowPopup(e, ((PrimaryJIDStatus) o).getJIDPrimaryStatus(), group);
+					}
+					else if (o instanceof JIDStatus)
+					{
+						JIDStatusGroup group = ((JIDStatusGroup) selPath.getPathComponent(selPath.getPathCount() - 3));
+						maybeShowPopup(e, (JIDStatus) o, group);
+					}
+				}
+			}
+
+			public void mouseExited(MouseEvent e)
+			{
+				clearSelection();//weg als multi select?
+			}
+		});
+
+		addMouseMotionListener(new MouseMotionAdapter()
+		{
+			public void mouseMoved(MouseEvent e)
+			{
+				TreePath selPath = getPathForLocation(e.getX(), e.getY());
+				if (selPath != null)
+				{
+					//Object o = selPath.getLastPathComponent();
+					//if (o instanceof PrimaryJIDStatus || o instanceof JIDStatus) 
+					setSelectionPath(selPath);
+				}
+			}
+		});
+
+	}
+	
+	public void updateLF()
+	{
+		SwingUtilities.updateComponentTreeUI(popupMenu);
+		SwingUtilities.updateComponentTreeUI(groupPopupMenu);
+	}
+		
+	private void createGroupPopupMenu()
+	{
+		groupPopupMenu = new JPopupMenu();
+		JMenuItem menuItem = null;
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Remove",menuItem,true);
+		menuItem.addActionListener(new java.awt.event.ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				if (JOptionPane.showConfirmDialog(main, MessageFormat.format(I18N.gettext("main.popup.Really_remove_{0}?_All_JIDs_in_this_group_will_be_removed!"), new Object[] {currentGroup}),
+						I18N.gettext("main.popup.Remove_Group"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
+				{
+					for (Iterator i = currentJIDStatusGroup.iterator(); i.hasNext();)
+					{
+						PrimaryJIDStatus primary = (PrimaryJIDStatus) i.next();
+						if (primary.hasMultiple())
+						{
+							for (Iterator j = primary.getOtherJidStatussen(); j.hasNext();)
+							{
+								groupRemove((JIDStatus) j.next());
+							}
+						}
+						groupRemove(primary.getJIDPrimaryStatus());
+					}
+				}
+			}
+
+			private void groupRemove(JIDStatus jidStatus)
+			{
+				JID to = jidStatus.getJID();
+				if (jidStatus.groupCount() == 1)
+				{
+					if (JOptionPane.showConfirmDialog(main,
+							MessageFormat.format(I18N.gettext(
+							"main.popup.{0}_is_in_the_last_group,_remove_completely?")
+							, new Object[] {to.toString()})
+							, I18N.gettext("main.main.rostermenu.Remove")
+							, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)	{
+						IQXRoster roster = new IQXRoster(new RosterItem(to, null, "remove", null, null));
+						backend.send(new InfoQuery("set", roster));
+					}
+				}
+				else
+				{
+					List groups = jidStatus.getGroupsCopy();
+					groups.remove(currentGroup);
+					IQXRoster roster = new IQXRoster(new RosterItem(to, jidStatus.getNick(), null, null, groups));
+					backend.send(new InfoQuery("set", roster));
+				}
+			}
+		});
+
+		groupPopupMenu.add(menuItem);
+		menuItem = new JMenuItem();
+		I18N.setTextAndMnemonic("main.main.rostermenu.Rename",menuItem,true);
+		menuItem.addActionListener(new ActionListener()
+		{
+			public void actionPerformed(ActionEvent e)
+			{
+				String groupName = JOptionPane.showInputDialog(main, MessageFormat.format(I18N.gettext("main.popup.Rename_{0}_to"),new Object[] {currentGroup}));
+				if (groupName == null) return;
+				for (Iterator i = currentJIDStatusGroup.iterator(); i.hasNext();)
+				{
+					PrimaryJIDStatus primary = (PrimaryJIDStatus) i.next();
+					if (primary.hasMultiple())
+					{
+						for (Iterator j = primary.getOtherJidStatussen(); j.hasNext();)
+						{
+							renameGroup(groupName, (JIDStatus) j.next());
+						}
+					}
+					renameGroup(groupName, primary.getJIDPrimaryStatus());
+				}
+			}
+
+			private void renameGroup(String groupName, JIDStatus jidStatus)
+			{
+				List groups = jidStatus.getGroupsCopy();
+				groups.remove(currentGroup);
+				groups.add(groupName);
+				IQXRoster roster = new IQXRoster(new RosterItem(jidStatus.getJID(), jidStatus.getNick(), null, null, groups));
+				backend.send(new InfoQuery("set", roster));
+			}
+		});
+		groupPopupMenu.add(menuItem);
+
+	}
+
+	public void createPopupMenu()
+	{
+		popupMenu = new JPopupMenu();
+		JMenuItem menuItem = null;
+		JMenu subMenu = null;
+		menuItem = new JMenuItem();
+		if(Preferences.getBoolean("jeti","rostermenuShowMessage",true))
+		{
+			I18N.setTextAndMnemonic("main.main.rostermenu.Message",menuItem,true);
+			menuItem.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					sendMessage(currentJIDStatus);
+				}
+			});
+			popupMenu.add(menuItem);
+		}
+		
+		if(Preferences.getBoolean("jeti","rostermenuShowChat",true))
+		{
+			menuItem = new JMenuItem();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Chat",menuItem,true);
+			menuItem.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					sendChat(currentJIDStatus);
+				}
+			});
+			popupMenu.add(menuItem);
+		}
+		
+		if(Preferences.getBoolean("jeti","rostermenuSubscriptions",true))
+		{
+			subMenu = new JMenu(I18N.gettext("main.main.rostermenu.Subscriptions"));
+			menuItem = new JMenuItem(I18N.gettext("main.main.rostermenu.Subscribe_from"));
+			menuItem = new JMenuItem();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Subscribe_from",menuItem);
+			menuItem.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					backend.send(new Presence(currentJIDStatus.getJID(), "subscribe"));
+				}
+			});
+			subMenu.add(menuItem);
+			menuItem = new JMenuItem(I18N.gettext("main.main.rostermenu.Unsubscribe_from"));
+			menuItem = new JMenuItem();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Unsubscribe_from",menuItem);
+			menuItem.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					backend.send(new Presence(currentJIDStatus.getJID(), "unsubscribe"));
+				}
+			});
+			subMenu.add(menuItem);
+			popupMenu.add(subMenu);
+		}
+		if(Preferences.getBoolean("jeti","rostermenuRemove",true))
+		{
+			menuItem = new JMenuItem();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Remove",menuItem,true);
+			menuItem.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					/**
+					 * @Author - Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+					 * @Date - 14/10/2008
+					 * Criada a variavel String nickName para mostrar o nickname do usuario ao inves do UID
+					 *  
+					 */
+					
+					JID to = currentJIDStatus.getJID();
+					String nickName = currentJIDStatus.getNick();
+					if (JOptionPane.showConfirmDialog(main, MessageFormat.format(I18N.gettext("main.popup.Really_remove_{0}_from_all_groups_?"),
+													new Object[] { nickName }),I18N.gettext("main.main.rostermenu.Remove"),
+															JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
+					{
+						IQXRoster roster = new IQXRoster(new RosterItem(to, null, "remove", null, null));
+						backend.send(new InfoQuery("set", roster));
+					}
+				}
+			});
+			popupMenu.add(menuItem);
+		}
+		if(Preferences.getBoolean("jeti","rostermenuRename",true))
+		{
+			menuItem = new JMenuItem();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Rename",menuItem,true);
+			menuItem.addActionListener(new ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					String nick = JOptionPane.showInputDialog(main, MessageFormat.format(I18N.gettext("main.popup.Rename_{0}_to"),new Object[] {currentJIDStatus.getNick() }));
+					if (nick == null) return;
+					IQXRoster roster = new IQXRoster(new RosterItem(currentJIDStatus.getJID(), nick, null, null, currentJIDStatus.getGroupsCopy()));
+					backend.send(new InfoQuery("set", roster));
+				}
+			});
+			popupMenu.add(menuItem);
+		}
+		if(Preferences.getBoolean("jeti","rostermenuGroup",true))
+		{
+			subMenu = new JMenu();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Group",subMenu);
+			menuItem = new JMenuItem();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Change_Group",
+	                                menuItem,true);
+			menuItem.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					new GroupDialog(currentGroup, currentJIDStatus, backend).setVisible(true);
+				}
+			});
+			subMenu.add(menuItem);
+			menuItem = new JMenuItem();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Add_to_Group",
+	                                menuItem, true);
+			menuItem.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					new GroupDialog(currentJIDStatus, backend).setVisible(true);
+				}
+			});
+			subMenu.add(menuItem);
+			menuItem = new JMenuItem();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Remove_from_group",
+	                                menuItem,true);
+			menuItem.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					JID to = currentJIDStatus.getJID();
+					if (currentJIDStatus.groupCount() == 1)
+					{
+						if (JOptionPane.showConfirmDialog(main
+							, MessageFormat.format(
+							I18N.gettext("main.popup.{0}_is_in_the_last_group,_remove_completely?")
+							,new Object[]{to.toString() })
+							, I18N.gettext("main.main.rostermenu.Remove")
+							, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
+						{
+							IQXRoster roster = new IQXRoster(new RosterItem(to, null, "remove", null, null));
+							backend.send(new InfoQuery("set", roster));
+						}
+					}
+					else
+					{
+						List groups = currentJIDStatus.getGroupsCopy();
+						groups.remove(currentGroup);
+						IQXRoster roster = new IQXRoster(new RosterItem(to, currentJIDStatus.getNick(), null, null, groups));
+						backend.send(new InfoQuery("set", roster));
+					}
+				}
+			});
+			subMenu.add(menuItem);
+			popupMenu.add(subMenu);
+		}
+        if (onlineTree) {
+        	if(Preferences.getBoolean("jeti","rostermenuLocalTime",true)
+        			&& !Preferences.getBoolean("jeti", "privacy", false))
+        	{
+	            menuItem = new JMenuItem();
+	            I18N.setTextAndMnemonic("main.main.rostermenu.Local_Time",
+	                                    menuItem, true);
+	            menuItem.addActionListener(new java.awt.event.ActionListener()
+	            {
+	                public void actionPerformed(ActionEvent e)
+	                {
+	                    backend.send(new InfoQuery(currentJIDStatus.getCompleteJID(), "get", new IQTime()));
+	                    
+	                }
+	            });
+
+	            popupMenu.add(menuItem);
+        	}
+        	if(Preferences.getBoolean("jeti","rostermenuLocalVersion",true)
+        			&& !Preferences.getBoolean("jeti", "privacy", false))
+        	{
+	            menuItem = new JMenuItem();
+	            I18N.setTextAndMnemonic("main.main.rostermenu.Local_Version",
+	                                    menuItem, true);
+	            menuItem.addActionListener(new java.awt.event.ActionListener()
+	            {
+	                public void actionPerformed(ActionEvent e)
+	                {
+	                    backend.send(new InfoQuery(currentJIDStatus.getCompleteJID(), "get", new IQVersion()));
+	                }
+	            });
+	            popupMenu.add(menuItem);
+        	}
+        }
+        else
+        {
+        	if(Preferences.getBoolean("jeti","rostermenuLastSeen",true))
+        	{
+	            menuItem = new JMenuItem();
+	            I18N.setTextAndMnemonic("main.main.rostermenu.Last_Seen",menuItem,true);
+	            menuItem.addActionListener(new java.awt.event.ActionListener()
+	            {
+	                public void actionPerformed(ActionEvent e)
+	                {
+	                    backend.send(new InfoQuery(currentJIDStatus.getJID(), "get", new IQLast()));
+	                }
+	            });
+	            popupMenu.add(menuItem);
+        	}
+        }
+        if(Preferences.getBoolean("jeti","rostermenuInvisible",true))
+        {
+			final JMenuItem menuItem3 = new JMenuItem();
+			I18N.setTextAndMnemonic("main.main.rostermenu.Set_Invisible",menuItem3);
+			menuItem3.addActionListener(new java.awt.event.ActionListener()
+			{
+				public void actionPerformed(ActionEvent e)
+				{
+					if(menuItem3.getText().equals(I18N.gettext("main.main.rostermenu.Set_Invisible")))
+					{
+						I18N.setTextAndMnemonic("main.main.rostermenu.Set_Visible",menuItem3);
+						backend.send(new Presence(currentJIDStatus.getJID(), "invisible"));
+					}
+					else
+					{
+						I18N.setTextAndMnemonic("main.main.rostermenu.Set_Invisible",menuItem3);				
+						backend.send(new Presence(currentJIDStatus.getJID(),"available"));
+					}
+				}
+			});
+			popupMenu.add(menuItem3);
+        }
+		if (menuItems != null)
+		{
+			for (Iterator i = menuItems.entrySet().iterator(); i.hasNext();)
+			{
+				Map.Entry entry = (Map.Entry) i.next();
+				JMenuItem item = new JMenuItem((String) entry.getKey());
+				final RosterMenuListener listener = (RosterMenuListener) entry.getValue();
+				item.addActionListener(new java.awt.event.ActionListener()
+				{
+					public void actionPerformed(ActionEvent e)
+					{
+						listener.actionPerformed(currentJIDStatus, currentJIDStatusGroup2);
+					}
+				});
+				popupMenu.add(item);
+			}
+		}
+	}
+
+	public void addToMenu(String name, RosterMenuListener listener)
+	{
+		if (menuItems == null) menuItems = new HashMap(10);
+		menuItems.put(name, listener);
+		if (popupMenu != null) createPopupMenu();
+	}
+
+	public void removeFromMenu(String name)
+	{
+		if (menuItems == null) return;
+		menuItems.remove(name);
+		if (menuItems.isEmpty()) menuItems = null;
+		if (popupMenu != null) createPopupMenu();
+	}
+
+	public List getOpenGroups()
+	{
+		List tempList = new LinkedList();
+		JIDStatusTree tree = ((JIDStatusTree) getModel().getRoot());
+		
+		if (tree == null)
+			return null;
+		
+		for (int i = 0; i < tree.getSize(); i++)
+		{
+			TreePath path = new TreePath(new Object[] { tree, tree.get(i)});
+			if (isExpanded(path))
+			{
+                tempList.add(tree.get(i).toString());
+            }
+		}
+		
+		return tempList;
+
+	}
+
+	public void openGroups(JetiPrivateRosterExtension extension)
+	{
+		String[] groups = extension.getOpenGroups();
+		JIDStatusTree tree = ((JIDStatusTree) getModel().getRoot());
+		if (groups == null) return;
+		for (int i = 0; i < groups.length; i++)
+		{
+			if(tree.existGroup(groups[i]))
+			{
+				TreePath path = new TreePath(new Object[] { tree, tree.getGroup(groups[i])});
+                if (treeExpander != null) {
+                    treeExpander.expand(path);
+                } else {
+                    expandPath(path);
+                }
+			}
+		}
+
+	}
+
+	private void sendMessage(JIDStatus jidStatus)
+	{
+		new SendMessage(backend, jidStatus.getJID(), jidStatus.getNick()).setVisible(true);
+	}
+
+	private void sendChat(JIDStatus jidStatus)
+	{
+		main.chat(jidStatus);
+	}
+
+	private void maybeShowPopup(MouseEvent e, JIDStatus jidStatus, JIDStatusGroup group)
+	{
+		if (e.isPopupTrigger())
+		{
+			currentJIDStatus = jidStatus;
+			currentGroup = group.getName();
+			currentJIDStatusGroup2 = group;
+			popupMenu.show(e.getComponent(), e.getX(), e.getY());
+		}
+	}
+
+	private void maybeShowGroupPopup(MouseEvent e, JIDStatusGroup jidStatusGroup)
+	{
+		if (e.isPopupTrigger())
+		{
+			currentJIDStatusGroup = jidStatusGroup;
+			currentGroup = jidStatusGroup.getName();
+			groupPopupMenu.show(e.getComponent(), e.getX(), e.getY());
+		}
+	}
+	
+	JIDStatus tooltipjids;
+
+	public String getToolTipText(MouseEvent ev)
+	{
+		if ( ev == null) return null;
+		TreePath path = getPathForLocation(ev.getX(), ev.getY());
+		if ( path != null )
+		{
+			JIDStatus jidStatus = null;
+			Object o = path.getLastPathComponent();
+			if (o instanceof PrimaryJIDStatus)
+			{
+				jidStatus = ((PrimaryJIDStatus) o).getJIDPrimaryStatus();
+
+			}
+			else if (o instanceof JIDStatus)
+			{
+				jidStatus = ((JIDStatus) o);
+			}
+			else
+			{
+                return null;
+            }
+			
+			String statusMsg;
+            
+			if (jidStatus.getStatus() != null)
+            {
+                statusMsg = jidStatus.getStatus();
+            } 
+            else
+            {
+                statusMsg = "";
+            }
+                        
+            String waitingStatus = jidStatus.getWaiting();
+            
+            if ( jidStatus.getWaiting() != null )
+            {
+                waitingStatus = I18N.gettext("main.main.roster.Waiting_Status") + " " + jidStatus.getWaiting();
+            }
+            else
+            {
+                waitingStatus = "";
+            }
+            
+            String	htmlReturn = "";
+	            	htmlReturn  = "<html>";
+	            	htmlReturn += "<table border='0' cellpadding='0' cellspacing='0' bgcolor='#ffffff' width='300'>";
+	            	htmlReturn += "<tr>";	
+		            htmlReturn += "<td valign='top' style='margin-left:5px'>";
+		            htmlReturn += "<b>Nick:</b> " + jidStatus.getNick() + "<br/>";
+		            htmlReturn += "<b>" + I18N.gettext("main.main.roster.Status") + "</b> " + Presence.toLongShow(jidStatus.getShow()) + "<br/>";
+		            htmlReturn += "<b>" + I18N.gettext("main.main.statusmenu.Status_message") +  "</b> " + statusMsg + "<br/>";
+		            htmlReturn += "<b>" + I18N.gettext("main.main.roster.Subscription") + "</b> " + jidStatus.getSubscription() + "<br/>";
+		            htmlReturn += "<b>" + waitingStatus + "<b/>";
+		            htmlReturn += "</td>";
+		            htmlReturn += "</tr>";
+		            htmlReturn += "<tr>";
+		            htmlReturn += "<td align='right'><font style='font-size:10pt;font-weight:bold'>" + nu.fw.jeti.applet.Jeti.NAMECOMPANY + "</td>";
+		            htmlReturn += "</tr>";
+		            htmlReturn += "</table>";            
+		            htmlReturn += "</html>";
+            					  
+            return htmlReturn;		
+		}
+		return null;
+	}
+	
+	public JToolTip createToolTip()
+	{
+		return super.createToolTip();
+	}
+	
+	class MyRenderer extends DefaultTreeCellRenderer //implements TreeCellRenderer
+	{
+		//TODO setpreferred size on mac (not on the render) to fix not 0
+		private JLabel renderer;
+		private Component fill = Box.createRigidArea(new Dimension(0, 0));
+
+		public MyRenderer()
+		{
+			
+			renderer = new JLabel();
+			renderer.setFont(UIManager.getFont("Tree.font"));
+		}
+
+		public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
+		{
+			
+			String text = value.toString();
+			renderer.setText(text);
+
+			if (sel)
+			{
+				renderer.setOpaque(true);
+				renderer.setForeground(UIManager.getColor("Tree.selectionForeground"));
+				renderer.setBackground(UIManager.getColor("Tree.selectionBackground"));
+			} 
+			else
+			{
+				renderer.setForeground(UIManager.getColor("Tree.textForeground"));
+				renderer.setOpaque(Preferences.getBoolean("jeti", "bmw", true));
+				renderer.setBackground(UIManager.getColor("Tree.background"));
+				//renderer.setOpaque(false);
+			}
+			
+			if (leaf)
+			{ 
+				return (makeComponent((JIDStatus) value));
+			}
+			
+			if (value instanceof PrimaryJIDStatus)
+			{
+				PrimaryJIDStatus ps = (PrimaryJIDStatus) value;
+				JIDStatus jidsStatus = ps.getJIDPrimaryStatus();
+				if (ps.hasMultiple())
+				{
+					if (onlineTree)
+					{
+						if (ps.multipleJIDstatusOnline())
+							renderer.setForeground(new Color(0, 0, 190));
+						return makeComponent(jidsStatus);
+					}
+					if (ps.isAJIDstatusOffline())
+					{
+						// if offline tree and a jidStatus in primary is offline
+						// show
+						renderer.setIcon(StatusIcons.getImageIcon("multiple"));
+						return renderer;
+					}
+					return fill;
+				}
+				return makeComponent(jidsStatus);
+			} 
+			else if (value instanceof JIDStatusGroup)
+			{
+				JIDStatusGroup group = (JIDStatusGroup) value;
+
+				/*if ( onlineTree )
+				{
+					if (group.getOnlines() == 0)
+						return fill;*/
+					
+					text = group.toString() + " ( " + group.getOnlines() + " / " + group.size() + " ) ";
+					renderer.setText(text);
+				//}
+
+				if ( expanded ) 
+					renderer.setIcon(StatusIcons.getImageIcon("arrowDown"));
+				else
+					renderer.setIcon(StatusIcons.getImageIcon("arrowUp"));
+			}
+			return renderer;
+		}
+
+		private Component makeComponent(JIDStatus jidStatus)
+		{
+			/**
+			 * Monta a lista de Conectado e Desconectado
+			 * Esta parte esta comentada para montar apenas um ramo com todos os grupos e seus usuarios ( online / offline )
+			 */
+
+			/*if (jidStatus.isOnline())
+			{
+				if (!onlineTree)
+					return fill; // leeg want offline tree
+				renderer.setIcon(StatusIcons.getStatusIcon(jidStatus));
+			} 
+			else
+			{
+				if (onlineTree) 
+					return fill; //leeg want online tree
+				renderer.setIcon(StatusIcons.getStatusIcon(jidStatus));
+			}*/
+			
+			renderer.setIcon(StatusIcons.getStatusIcon(jidStatus));
+			return renderer;
+		}
+	}
+
+
+	class MacRenderer implements TreeCellRenderer 
+	{
+		//special render for Mac because it doesn't support 0 sized components in the tree
+		private JLabel renderer;
+
+		public MacRenderer()
+		{
+			renderer = new JLabel();
+			renderer.setFont(UIManager.getFont("Tree.font"));
+		}
+
+		public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus)
+		{
+			renderer.setText(value.toString());
+			if (sel)
+			{
+				renderer.setOpaque(true);
+				renderer.setForeground(UIManager
+						.getColor("Tree.selectionForeground"));
+				renderer.setBackground(UIManager.getColor("Tree.selectionBackground"));
+			} else
+			{
+				renderer.setForeground(UIManager.getColor("Tree.textForeground"));
+				renderer.setOpaque(Preferences.getBoolean("jeti", "bmw", true));
+				renderer.setBackground(UIManager.getColor("Tree.background"));
+			}
+			if (leaf) { return (makeComponent((JIDStatus) value)); }
+			if (value instanceof PrimaryJIDStatus)
+			{
+				PrimaryJIDStatus ps = (PrimaryJIDStatus) value;
+				JIDStatus jidsStatus = ps.getJIDPrimaryStatus();
+				if (ps.hasMultiple())
+				{
+					if (onlineTree) {
+						if (ps.multipleJIDstatusOnline()) {
+                            renderer.setForeground(new Color(0, 0, 190));
+                        }
+						return makeComponent(jidsStatus);
+					}
+					if (ps.isAJIDstatusOffline())
+					{//if offline tree and a jidStatus in primary is offline
+					 // show
+						renderer.setIcon(StatusIcons.getImageIcon("multiple"));
+					}
+                    return renderer;
+				}
+				return makeComponent(jidsStatus);
+			}
+			else if (value instanceof JIDStatusGroup)
+			{
+				JIDStatusGroup group = (JIDStatusGroup) value;
+				if (onlineTree) {
+					renderer.setText(group.toString() + " ("
+                                     + group.getOnlines() + ")");
+				}
+				if (expanded) {
+                    renderer.setIcon(StatusIcons.getImageIcon("arrowDown"));
+				} else {
+                    renderer.setIcon(StatusIcons.getImageIcon("arrowUp"));
+                }
+			}
+			return renderer;
+		}
+
+		private Component makeComponent(JIDStatus jidStatus)
+		{
+            int show;
+			if (jidStatus.isOnline()) {
+				show = jidStatus.getShow();
+			} else {
+                show = Presence.UNAVAILABLE;
+			}
+            renderer.setIcon(StatusIcons.getStatusIcon(show,
+                                                       jidStatus.getType()));
+			return renderer;
+		}
+	}
+	
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/PreferencesHandler.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/PreferencesHandler.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/PreferencesHandler.java	(revision 3952)
@@ -0,0 +1,117 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+
+package nu.fw.jeti.util;
+
+import org.xml.sax.helpers.*;
+import org.xml.sax.*;
+import java.util.*;
+
+/**
+  * @author E.S. de Boer
+ */
+
+public class PreferencesHandler extends DefaultHandler
+{
+	
+	private Map preferences;
+	//private Map messages;
+	private StringBuffer text= new StringBuffer();
+    //private String show = "available";
+
+	public PreferencesHandler(Map preferences)
+	{
+		this.preferences = preferences; 
+       // this.messages = messages;
+	}
+
+	public void startElement(String namespaceURI,
+							String sName, // simple name
+							String qName, // qualified name
+							Attributes attrs)							
+	{
+		if(qName.equals("plugin"))
+		{
+			String type = attrs.getValue("type");
+			String name = attrs.getValue("name");
+			Object[] temp = new Object[6];
+			temp[0] = name;
+			temp[1] = Boolean.valueOf(attrs.getValue("enabled"));
+			temp[3] = attrs.getValue("transport");
+
+            List l = Preferences.getPlugable(type);
+            for (int i=0; i<l.size(); i++) {
+                Object[] t = (Object[])l.get(i);
+                if (name.equals(t[0])) {
+                    l.remove(i--);
+                }
+            }
+			l.add(temp);
+		}
+		else if(qName.equals("preference"))
+		{
+			String key = attrs.getValue("key");
+			String value = attrs.getValue("value");
+			preferences.put(key,value);
+		}
+		/*
+		else if(qName.equals("messages"))
+		{
+			show = attrs.getValue("show");
+            if (show == null) {
+                show = "available";
+            }
+		}
+		else if(qName.equals("message"))
+		{
+			String text = attrs.getValue("text");
+            List list = (List)messages.get(show);
+            if (list == null) {
+                list = new ArrayList(10);
+            }
+            list.add(text);
+            messages.put(show, list);
+		}
+		*/
+	}
+
+
+	public void endElement(String namespaceURI,
+						   String sName, // simple name
+						   String qName  // qualified name
+							)
+	{
+        text = new StringBuffer();
+	}
+
+	public void characters(char buf[], int offset, int Len)
+	{
+		   text.append(buf, offset, Len);
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/ServerExpresso.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/ServerExpresso.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/ServerExpresso.java	(revision 3952)
@@ -0,0 +1,210 @@
+/**
+ * @Authors : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br 
+ * 			  Rodrigo Souza - rodsouza@celepar.pr.gov.br
+ * @Date : 29/10/2008
+ * @Description : Conecta com o servidor do Expresso (http ou https), 
+ * faz um post para receber os dados nome e organizacao do contato.
+ */
+
+package nu.fw.jeti.util;
+
+
+import java.io.*;
+import java.net.*;
+import javax.net.ssl.*;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+
+class MyHostnameVerifier implements HostnameVerifier
+{
+
+	public boolean verify(String hostname, SSLSession session)
+	{
+		return true;
+    }
+}
+
+public class ServerExpresso
+{
+
+	private void acceptSSL()
+ 	{
+ 		// Create a trust manager that does not validate certificate chains
+        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
+            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+                return null;
+            }
+ 
+            public void checkClientTrusted(
+                    java.security.cert.X509Certificate[] certs, String authType) {
+            }
+ 
+            public void checkServerTrusted(
+                    java.security.cert.X509Certificate[] certs, String authType) {
+            }
+        } };
+ 
+        // Install the all-trusting trust manager
+        try {
+            SSLContext sc = SSLContext.getInstance("SSL");
+ 
+            sc.init(null, trustAllCerts, new java.security.SecureRandom());
+            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+        }
+        catch (Exception e){}    	
+    }
+	
+	private String connectHttp(String typeProtocol, String NameServer, String Param)
+	{
+		String returnResult = "";
+		String line;
+		
+		try
+		{
+			URLConnection conn = (URLConnection)(new URL( typeProtocol + "://" + NameServer + "inc/webservice.php")).openConnection();
+        	conn.setDoOutput(true);
+	        OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
+	        wr.write(Param);
+	        wr.flush();
+	    
+	        // Get the response
+	        BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+
+	        while ( (line = rd.readLine()) != null )
+	        {
+	        	if( line.trim() != "" )
+	        		returnResult += line;
+	        }
+
+	        wr.close();
+	        rd.close();
+	        
+		}
+		catch(Exception e)
+		{
+			returnResult = "Erro : " + e ;	
+		}
+		
+		return returnResult;
+	}
+
+	private String connectHttps(String typeProtocol, String NameServer, String Param)
+	{
+		String returnResult = "";
+		String line;
+
+        try
+        {
+	        this.acceptSSL();
+	    	HttpsURLConnection conn = (HttpsURLConnection)(new URL(typeProtocol + "://" + NameServer + "inc/webservice.php")).openConnection();
+	    	conn.setHostnameVerifier(new MyHostnameVerifier());
+	    	conn.setDoOutput(true);
+
+	    	OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
+	        wr.write(Param);
+	        wr.flush();
+	    
+	        // Get the response
+	        BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+
+	        while ( (line = rd.readLine()) != null )
+	        {
+	        	if( line.trim() != "" )
+	        		returnResult += line;
+	        }
+
+	        wr.close();
+	        rd.close();
+	        
+	       	conn.disconnect();
+        }
+        catch(Exception e)
+        {
+        	returnResult = "Erro : " + e ;
+        }
+
+       	return returnResult;
+	}
+	
+	public String NameCN(String Jid)
+	{
+		return this.connectServerExpresso(Jid);
+	}
+	
+	private String connectServerExpresso(String Jid)
+	{
+
+		String cnName = "";
+
+        // Construct data
+        String data = "jid=" + Jid;		        
+        
+        // Type Charset
+        CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
+        String charsetType = decoder.charset().toString();
+        
+		if ( charsetType.equals("UTF-8"))
+			data += "&charset=1";
+		else
+			data += "&charset=2";
+		
+        // Name Server Expresso
+        String serverExpresso = nu.fw.jeti.applet.Jeti.SERVEREXPRESSO;
+        
+        // Type Protocol Http / Https
+        String typeProtocol = nu.fw.jeti.applet.Jeti.USEHTTPS;
+        
+        if( typeProtocol.equals("http") )
+        	cnName = this.connectHttp(typeProtocol, serverExpresso, data);
+        else
+        	cnName = this.connectHttps(typeProtocol, serverExpresso, data);
+
+		return cnName;
+	}
+	
+	public String PhotoUid(String Jid)
+	{
+		String photo = "";
+		String data = "javaPhoto=" + Jid;
+		
+        // Name Server Expresso
+        String serverExpresso = nu.fw.jeti.applet.Jeti.SERVEREXPRESSO;
+        
+        // Type Protocol Http / Https
+        String typeProtocol = nu.fw.jeti.applet.Jeti.USEHTTPS;
+        
+        if( typeProtocol.equals("http") )
+        {
+        	photo = this.connectHttp(typeProtocol, serverExpresso, data);
+        }
+        else
+        {
+        	photo = this.connectHttps(typeProtocol, serverExpresso, data);
+        }
+        
+        return photo;
+	}
+	
+	public String connectCallVoip(String jidFrom, String jidTo)
+	{
+		String callVoip = "";
+		String data = "javaVoipFrom=" + jidFrom + "&javaVoipTo=" + jidTo;
+
+		// Name Server Expresso
+        String serverExpresso = nu.fw.jeti.applet.Jeti.SERVEREXPRESSO;
+        
+        // Type Protocol Http / Https
+        String typeProtocol = nu.fw.jeti.applet.Jeti.USEHTTPS;
+        
+        if( typeProtocol.equals("http") )
+        {
+        	callVoip = this.connectHttp(typeProtocol, serverExpresso, data);
+        }
+        else
+        {
+        	callVoip = this.connectHttps(typeProtocol, serverExpresso, data);
+        }
+        
+        return callVoip;
+	}
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/Utils.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/Utils.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/Utils.java	(revision 3952)
@@ -0,0 +1,83 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 17-jun-2005
+ */
+
+package nu.fw.jeti.util;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import javax.swing.*;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class Utils
+{
+	public static String toString(byte[] bytes)
+	{
+		StringBuffer buf = new StringBuffer(bytes.length * 2);
+		for(int i = 0; i < bytes.length; i++)
+		{
+			int hex = bytes[i];
+			if (hex < 0) hex = 256 + hex;
+			if (hex >=16) buf.append(Integer.toHexString(hex));
+			else
+			{
+				buf.append('0');
+				buf.append(Integer.toHexString(hex));
+			}
+		}
+		return buf.toString().toLowerCase();
+	}
+	
+	public static void addCancelButton(RootPaneContainer rpc,JButton button,Action cancelAction)
+	{
+		button.setAction(cancelAction);
+		KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
+		JLayeredPane layeredPane = rpc.getLayeredPane();
+		layeredPane.getActionMap().put("cancel", cancelAction);
+		layeredPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(stroke, "cancel");
+	}
+	
+	/**
+	 * Is the JRE Jeti runs on at least version version
+	 * @param version the minimum version needed
+	 * 				  version can be 5 or 6
+	 */
+	public static boolean isAtleastJava(int version)
+	{
+		String versionString = System.getProperty("java.version");
+		if (versionString.startsWith("1.4"))
+		{
+			return false;
+		}
+		if (versionString.startsWith("1.5"))
+		{
+			if(version==5) return true;
+			else return false;
+		}
+		return true;
+	}
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/Preferences.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/Preferences.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/Preferences.java	(revision 3952)
@@ -0,0 +1,399 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.util;
+
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+
+import javax.xml.parsers.SAXParser;
+
+import nu.fw.jeti.backend.Start;
+import nu.fw.jeti.backend.XMLDataFile;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.IQPrivate;
+import nu.fw.jeti.jabber.elements.InfoQuery;
+import nu.fw.jeti.jabber.elements.JetiPrivateExtension;
+import nu.fw.jeti.jabber.elements.Presence;
+
+import org.xml.sax.SAXException;
+
+/**
+ * @author E.S. de Boer
+ * 2001
+ */
+
+public class Preferences extends XMLDataFile
+{
+	//private static Map map;
+	private static Map mapMessages = new HashMap();
+	private static Backend backend;
+    private static Map plugable = new HashMap();
+    private static Map preferences = new HashMap();
+    private static Map defaults = new HashMap();
+
+	public Preferences()
+	{}
+
+	public Preferences(Map p)
+	{ 
+		//copy instead of put all?
+		preferences.putAll(p);
+	}
+	
+	public Preferences(Backend backend, SAXParser parser)
+	{
+		Preferences.backend = backend;
+		InputStream data = null;
+
+		try	
+		{
+			if ( Start.applet )
+			{
+				URL url = new URL(Start.dataURL + "default.xml");
+				data = url.openStream();
+			}
+			addPreferences(parser, data, defaults);
+		}
+		catch(IOException ex){}
+
+		// Initialize presence messages if none have been specified
+        if ( mapMessages.isEmpty())
+        {
+            initMessages(mapMessages);
+        }		
+	}
+
+    private void addPreferences(SAXParser parser, InputStream data,Map store)
+    {
+        try
+        {
+            parser.parse(data,new PreferencesHandler(store));
+        }
+        catch (SAXException ex)
+        {
+            ex.printStackTrace();
+        }
+        catch (IOException ex)
+        {
+            ex.printStackTrace();
+        }
+    }
+
+	private static Map initMessages(Map mapMessages)
+	{
+		/**
+		 * @Author(s) : Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+		 * @description : Adicionado as traducoes para os items do menu ( presenca )
+		 */
+		
+		List tempList = new ArrayList(10);
+			tempList.add(I18N.gettext("initMessages.Available"));
+			tempList.add(I18N.gettext("initMessages.Free_for_Chat"));
+			mapMessages.put("chat", tempList);
+			mapMessages.put("available", tempList);
+			tempList = new ArrayList(10);
+			tempList.add(I18N.gettext("initMessages.In_a_meeting"));
+			tempList.add(I18N.gettext("initMessages.Busy"));
+			tempList.add(I18N.gettext("initMessages.Working"));
+			mapMessages.put("dnd", tempList);
+			tempList = new ArrayList(10);
+			tempList.add(I18N.gettext("initMessages.On_the_phone"));
+			tempList.add(I18N.gettext("initMessages.Be_right_back"));
+			mapMessages.put("away", tempList);
+			tempList = new ArrayList(10);
+			tempList.add(I18N.gettext("initMessages.On_vacation"));
+			tempList.add(I18N.gettext("initMessages.Gone_home"));
+			tempList.add(I18N.gettext("initMessages.Extend_Away"));
+			mapMessages.put("xa", tempList);
+		
+		return mapMessages;
+	}
+	
+	/**
+	 * Adds a deafult preference, which cannot be overriden byt the user
+	 * and is not saved to the preferences file of the user
+	 * @param prefixKey prefix and key in prefix.key formant
+	 * @param value The value of the preference
+	 */
+	public static void addDefaultPreferences(String prefixKey,String value)
+	{
+		defaults.put(prefixKey,value);
+	}
+
+	public static void load(JetiPrivateExtension jetiExtension)
+	{
+		mapMessages.putAll(jetiExtension.getMessages());
+	}
+
+	/**
+	 * saves status messages on a server.
+	 * @param key
+	 * @param value
+	 */
+	public static void saveStatusMessages(int key, List value)
+	{
+		mapMessages.put(convertPresenceKey(key), value);
+	}
+	
+	/**
+	 * saves preferences (saved with put) to server
+	 */
+	public static void saveToServer()
+	{
+		backend.send(new InfoQuery("set", new IQPrivate(new JetiPrivateExtension(null, mapMessages))));
+	}
+	
+	/**
+	 * loads status messages from server
+	 * @param key
+	 * @return
+	 */
+	public static List getStatusMessages(int key)
+	{
+		return (List) mapMessages.get(convertPresenceKey(key));
+	}
+
+	private static String convertPresenceKey(int key)
+	{
+		String stringKey;
+		switch(key)
+		{
+			case Presence.FREE_FOR_CHAT: stringKey="chat"; break;
+			case Presence.AWAY: stringKey="away"; break;
+			case Presence.XA: stringKey="xa"; break;
+			case Presence.DND: stringKey="dnd"; break;
+			default: stringKey="available";
+		}
+		return stringKey;
+	}
+
+	/**
+	 * gets a boolean preference
+	 * @param prefix a prefix so there are no name clashes
+	 * @param def the value to be returned in the event that this preference has no value associated with key or the associated value cannot be interpreted as an boolean 	
+	 * @param key  key whose associated value is to be returned as an boolean.
+	 * @return boolean the boolean value represented by the string associated with key, or def if the associated value does not exist. 
+	 */
+	public static boolean getBoolean(String prefix,String key,boolean def)
+	{
+		String value = get(prefix,key);
+		if(value==null) return def; 
+		return Boolean.valueOf(value).booleanValue();
+	}
+	
+	/**
+	* Method saves a boolean preference
+	* @param prefix a prefix so there are no name clashes
+	* @param key
+	* @param value
+	*/
+	public static void putBoolean(String prefix, String key, boolean value)
+	{
+		preferences.put(prefix + "." + key,String.valueOf(value));
+	}
+	
+	/**
+	 * gets a integer preference
+	 * @param prefix a prefix so there are no name clashes
+	 * @param def the value to be returned in the event that this preference has no value associated with key or the associated value cannot be interpreted as an int 	
+	 * @param key  key whose associated value is to be returned as an boolean.
+	 * @return int the integer value represented by the string associated with key, or def if the associated value does not exist. 
+	 */
+	public static int getInteger(String prefix,String key,int def)
+	{
+		String value = get(prefix,key);
+		if(value==null) return def; 
+		try{
+			return Integer.parseInt(value);
+		} catch (NumberFormatException e)
+		{
+			return def; 
+		}
+	}
+
+	/**
+	* Method saves a integer preference
+	* @param prefix a prefix so there are no name clashes
+	* @param key
+	* @param value
+	*/
+	public static void putInteger(String prefix, String key,int value)
+	{
+		preferences.put(prefix + "." + key,String.valueOf(value));
+	}
+		
+	/**
+	* Method put saves a string preference.
+	* @param prefix a prefix so there are no name clashes
+	* @param key
+	* @param value
+	*/
+	public static void putString(String prefix, String key, String value)
+	{
+		preferences.put(prefix + "." + key, value);
+	}
+
+	/**
+	 * Method getPreference gets a string preference.
+	 * @param prefix a prefix so there are no name clashes
+	* @param def the value to be returned in the event that this preference has no value associated with key or the associated value cannot be interpreted as an boolean 	
+	 * @param key  key whose associated value is to be returned as an boolean.
+	 * @return boolean the boolean value represented by the string associated with key, or def if the associated value does not exist. 
+	 */
+	public static String getString(String prefix, String key,String def)
+	{
+		String value = get(prefix,key);
+		if(value==null) return def; 
+		return value;
+	}
+
+	/**
+	 * Method getPreference gets a preference from file.
+	 * first tries defaults to get a default value that 
+	 * can't be overriden by the users preferences file
+	 * @param prefix a prefix so there are no name clashes
+	 * @param key
+	 * @param value
+	 */
+	private static String get(String prefix, String key)
+	{
+		String defaultValue = (String)defaults.get(prefix + "." + key);
+		if(defaultValue!=null)return defaultValue;
+		return (String) preferences.get(prefix + "." + key);
+	}
+
+	public static List getPlugins()
+	{
+		//put in new class?
+		return getPlugable("plugins");
+	}
+
+	public static List getTranslatedPlugins()
+	{
+		//put in new class?
+		List temp = new ArrayList();
+		for (Iterator i = getPlugins().iterator(); i.hasNext();)
+		{
+			Object[] tempArray = new Object[6];
+			Object[] plugins = (Object[])i.next();
+			tempArray[0]= plugins[0];
+			tempArray[1]= plugins[1];
+			tempArray[2]= I18N.gettext((String)plugins[2]);
+			tempArray[3]= plugins[3];
+			tempArray[4]= plugins[4];
+			tempArray[5]= plugins[5];
+			temp.add(tempArray);
+		}
+		return temp;
+	}
+
+	public static List getPlugable(String name)
+	{
+		List list = (List) plugable.get(name);
+		if (list == null)
+		{
+			list = new ArrayList();
+			plugable.put(name, list);
+		}
+		return list;
+	}
+
+	public static List getPlugableCopy(String name)
+	{
+		List temp = new ArrayList();
+		for (Iterator i = getPlugable(name).iterator(); i.hasNext();)
+		{
+			Object[] tempArray = new Object[6];
+			System.arraycopy(i.next(), 0, tempArray, 0, 6);
+			temp.add(tempArray);
+		}
+		return temp;
+	}
+	
+	/**
+	 * Saves preferences to disk (in preferences.xml)
+	 */
+	public static void save()
+	{
+		StringBuffer xml = new StringBuffer();
+		new Preferences().appendToXML(xml);
+		xml.insert(0,"<?xml version='1.0'?>");
+		//System.out.println(xml.toString());
+		try
+		{
+			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
+					new FileOutputStream(Start.path + "preferences.xml"), "UTF8"));
+			writer.write(xml.toString());
+			writer.close();
+		}
+		catch (IOException ex2)
+		{
+			ex2.printStackTrace();
+		}
+	}
+
+	public void appendToXML(StringBuffer xml)
+	{
+		//appendHeader(xml);
+		appendOpenTag(xml, "<preferences>");
+		for (Iterator i = preferences.entrySet().iterator(); i.hasNext();)
+		{
+			Map.Entry entry = (Map.Entry) i.next();
+			String key = (String) entry.getKey();
+			String value = (String) entry.getValue();
+			if(value!=null)
+			{//only preference with a value, to reset old ones
+				appendOpenTag(xml, "<preference");
+				appendAttribute(xml, "key", key);
+				appendAttribute(xml, "value", value);
+				appendCloseSymbol(xml);
+			}
+		}
+		appendOpenTag(xml, "<plugins>");
+		for (Iterator i = plugable.entrySet().iterator(); i.hasNext();)
+		{
+			Map.Entry entry = (Map.Entry) i.next();
+			String type = (String) entry.getKey();
+			for (Iterator j = ((List) entry.getValue()).iterator(); j.hasNext();)
+			{
+				Object[] temp = (Object[]) j.next();
+				appendOpenTag(xml, "<plugin");
+				appendAttribute(xml, "type", type);
+				appendAttribute(xml, "name", temp[0]);
+				appendAttribute(xml, "enabled", temp[1]);
+				appendAttribute(xml, "transport", temp[3]);
+				appendCloseSymbol(xml);
+			}
+		}
+		appendCloseTag(xml, "</plugins>");
+		appendCloseTag(xml, "</preferences>");
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/ProxyIM.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/ProxyIM.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/ProxyIM.java	(revision 3952)
@@ -0,0 +1,59 @@
+/**
+ * @author Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+ * @ ProxyName e ProxyPort 
+ */
+
+package nu.fw.jeti.util;
+
+import java.util.Iterator;
+import java.util.List;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URI;
+
+public class ProxyIM
+{
+	private static String proxyServer;
+	private static int proxyPort;
+
+	public static void setProxy(String url)
+	{
+		try
+		{
+			System.setProperty("java.net.useSystemProxies","true");
+			List l = ProxySelector.getDefault().select(new URI(url));
+			
+			for (Iterator iter = l.iterator(); iter.hasNext(); )
+			{
+				Proxy proxy = (Proxy) iter.next();
+				InetSocketAddress addr = (InetSocketAddress)proxy.address();
+
+				if( addr == null )
+				{
+					proxyServer = "";
+					proxyPort = 0;
+				}
+				else
+				{
+					proxyServer = addr.getHostName().toString();
+					proxyPort = addr.getPort();
+				}
+			}
+		}
+		catch (Exception e)
+		{
+			e.printStackTrace();
+		}
+	}
+	
+	public static String getProxyServer()
+	{
+		return proxyServer;
+	}
+
+	public static int getProxyPort()
+	{
+		return proxyPort;
+	}
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/FindAction.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/FindAction.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/FindAction.java	(revision 3952)
@@ -0,0 +1,118 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 27-feb-2004
+ */
+ 
+package nu.fw.jeti.util;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.text.MessageFormat;
+
+import javax.swing.AbstractAction;
+import javax.swing.JOptionPane;
+import javax.swing.KeyStroke;
+import javax.swing.text.JTextComponent;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class FindAction extends AbstractAction
+{
+	private String searchString = null;
+	private int startPoint = 0;
+	private int endPoint = 0;
+	
+	public FindAction(){
+		super(I18N.gettext("main.popup.Find"));
+		putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_F));
+		putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_F, ActionEvent.CTRL_MASK));
+	}
+
+	public void actionPerformed(ActionEvent e)
+	{
+		if (e != null) 
+		{
+			Object o = e.getSource();
+			if (o instanceof JTextComponent) 
+			{
+				JTextComponent text = (JTextComponent) o;
+				if(e.getModifiers() == 0 )
+				{ 
+					//find Next
+					if (searchString==null) askSearchString(text);
+					if (searchString!=null)search(text,text.getCaretPosition());
+				}
+				else
+				{	
+					askSearchString(text);
+					if (searchString!=null)search(text,0);
+				}
+			}
+	    }
+	}
+	
+	private void askSearchString(JTextComponent text)
+	{
+		searchString = JOptionPane.showInputDialog(text.getTopLevelAncestor(), I18N.gettext("main.popup.Enter_the_text_to_search_for"),text.getSelectedText());
+	}
+	
+
+	private void search(JTextComponent text, int startPositionIn)
+	{
+		String fulltext = text.getText();
+
+		//get the substring based on the requested start position
+		fulltext = ( fulltext ).substring(startPositionIn);
+
+		int searchStringLength = searchString.length();
+		// so long as we can find the text in the file, go ahead and highlight it
+		// if we can't show the dialog
+		if(fulltext.indexOf(searchString) > -1)
+		{
+			startPoint = (fulltext.indexOf(searchString) ) + startPositionIn;
+			endPoint = startPoint + searchStringLength;
+			text.requestFocus();
+			text.setCaretPosition(startPoint);
+			text.moveCaretPosition(endPoint);
+		}
+		else if (startPositionIn!=0)
+		{
+			if(JOptionPane.showConfirmDialog(text.getTopLevelAncestor(),MessageFormat.format(I18N.gettext("main.popup.{0}_not_found_until_the_end_of_this_file,_start_again_from_the_beginning?"),new Object[]{searchString})) == JOptionPane.OK_OPTION)
+			{
+				search(text, 0);
+			}
+		}
+		else
+		{
+			JOptionPane.showMessageDialog(text.getTopLevelAncestor(),MessageFormat.format(I18N.gettext("main.popup.{0}_not_found"),new Object[]{searchString}));
+		}
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/JOptionPaneNonModal.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/JOptionPaneNonModal.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/JOptionPaneNonModal.java	(revision 3952)
@@ -0,0 +1,286 @@
+package nu.fw.jeti.util;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+
+import nu.fw.jeti.util.Popups;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener; 
+
+
+/**
+ * @author student
+ *
+ * To change this generated comment edit the template variable "typecomment":
+ * Window>Preferences>Java>Templates.
+ * To enable and disable the creation of type comments go to
+ * Window>Preferences>Java>Code Generation.
+ */
+public class JOptionPaneNonModal
+{
+
+//		
+//
+//		/**
+//		 * Brings up a dialog with the options <i>Yes</i>,
+//		 * <i>No</i> and <i>Cancel</i>; with the
+//		 * title, <b>Select an Option</b>.
+//		 *
+//		 * @param parentComponent determines the <code>Frame</code> in which the
+//		 *			dialog is displayed; if <code>null</code>,
+//		 *			or if the <code>parentComponent</code> has no
+//		 *			<code>Frame</code>, a 
+//		 *                  default <code>Frame</code> is used
+//		 * @param message   the <code>Object</code> to display
+//		 * @return an integer indicating the option selected by the user
+//		 * @exception HeadlessException if
+//		 *   <code>GraphicsEnvironment.isHeadless</code> returns
+//		 *   <code>true</code>
+//		 * @see java.awt.GraphicsEnvironment#isHeadless
+//		 */
+//		public static int showConfirmDialog(Component parentComponent,
+//			Object message) throws HeadlessException {
+//			return showConfirmDialog(parentComponent, message,
+//									 UIManager.getString("OptionPane.titleText"),
+//				JOptionPane.YES_NO_CANCEL_OPTION);
+//		}
+//
+//		/**
+//		 * Brings up a dialog where the number of choices is determined
+//		 * by the <code>optionType</code> parameter.
+//		 * 
+//		 * @param parentComponent determines the <code>Frame</code> in which the
+//		 *			dialog is displayed; if <code>null</code>,
+//		 *			or if the <code>parentComponent</code> has no
+//		 *			<code>Frame</code>, a 
+//		 *                  default <code>Frame</code> is used
+//		 * @param message   the <code>Object</code> to display
+//		 * @param title     the title string for the dialog
+//		 * @param optionType an int designating the options available on the dialog:
+//		 *                  <code>YES_NO_OPTION</code>, or
+//		 *			<code>YES_NO_CANCEL_OPTION</code>
+//		 * @return an int indicating the option selected by the user
+//		 * @exception HeadlessException if
+//		 *   <code>GraphicsEnvironment.isHeadless</code> returns
+//		 *   <code>true</code>
+//		 * @see java.awt.GraphicsEnvironment#isHeadless
+//		 */
+//		public static int showConfirmDialog(Component parentComponent,
+//			Object message, String title, int optionType)
+//			throws HeadlessException {
+//			return showConfirmDialog(parentComponent, message, title, optionType,
+//			JOptionPane.QUESTION_MESSAGE);
+//		}
+//
+//		/**
+//		 * Brings up a dialog where the number of choices is determined
+//		 * by the <code>optionType</code> parameter, where the
+//		 * <code>messageType</code>
+//		 * parameter determines the icon to display.
+//		 * The <code>messageType</code> parameter is primarily used to supply
+//		 * a default icon from the Look and Feel.
+//		 *
+//		 * @param parentComponent determines the <code>Frame</code> in
+//		 *			which the dialog is displayed; if <code>null</code>,
+//		 *			or if the <code>parentComponent</code> has no
+//		 *			<code>Frame</code>, a 
+//		 *                  default <code>Frame</code> is used.
+//		 * @param message   the <code>Object</code> to display
+//		 * @param title     the title string for the dialog
+//		 * @param optionType an integer designating the options available
+//		 *			on the dialog: <code>YES_NO_OPTION</code>,
+//		 *			or <code>YES_NO_CANCEL_OPTION</code>
+//		 * @param messageType an integer designating the kind of message this is; 
+//		 *                  primarily used to determine the icon from the pluggable
+//		 *                  Look and Feel: <code>ERROR_MESSAGE</code>,
+//		 *			<code>INFORMATION_MESSAGE</code>, 
+//		 *                  <code>WARNING_MESSAGE</code>,
+//		 *                  <code>QUESTION_MESSAGE</code>,
+//		 *			or <code>PLAIN_MESSAGE</code>
+//		 * @return an integer indicating the option selected by the user
+//		 * @exception HeadlessException if
+//		 *   <code>GraphicsEnvironment.isHeadless</code> returns
+//		 *   <code>true</code>
+//		 * @see java.awt.GraphicsEnvironment#isHeadless
+//		 */
+//		public static int showConfirmDialog(Component parentComponent,
+//			Object message, String title, int optionType, int messageType)
+//			throws HeadlessException {
+//			return showConfirmDialog(parentComponent, message, title, optionType,
+//									messageType, null);
+//		}
+//
+//		/**
+//		 * Brings up a dialog with a specified icon, where the number of 
+//		 * choices is determined by the <code>optionType</code> parameter.
+//		 * The <code>messageType</code> parameter is primarily used to supply
+//		 * a default icon from the look and feel.
+//		 *
+//		 * @param parentComponent determines the <code>Frame</code> in which the
+//		 *			dialog is displayed; if <code>null</code>,
+//		 *			or if the <code>parentComponent</code> has no
+//		 *			<code>Frame</code>, a 
+//		 *			default <code>Frame</code> is used
+//		 * @param message   the Object to display
+//		 * @param title     the title string for the dialog
+//		 * @param optionType an int designating the options available on the dialog:
+//		 *                  <code>YES_NO_OPTION</code>,
+//		 *			or <code>YES_NO_CANCEL_OPTION</code>
+//		 * @param messageType an int designating the kind of message this is, 
+//		 *                  primarily used to determine the icon from the pluggable
+//		 *                  Look and Feel: <code>ERROR_MESSAGE</code>,
+//		 *			<code>INFORMATION_MESSAGE</code>, 
+//		 *                  <code>WARNING_MESSAGE</code>,
+//		 *                  <code>QUESTION_MESSAGE</code>,
+//		 *			or <code>PLAIN_MESSAGE</code>
+//		 * @param icon      the icon to display in the dialog
+//		 * @return an int indicating the option selected by the user
+//		 * @exception HeadlessException if
+//		 *   <code>GraphicsEnvironment.isHeadless</code> returns
+//		 *   <code>true</code>
+//		 * @see java.awt.GraphicsEnvironment#isHeadless
+//		 */
+//		public static int showConfirmDialog(Component parentComponent,
+//			Object message, String title, int optionType,
+//			int messageType, Icon icon) throws HeadlessException {
+//			return showOptionDialog(parentComponent, message, title, optionType,
+//									messageType, icon, null, null);
+//		}
+
+
+
+
+	public static void showOptionDialog(Component parentComponent,
+			Object message, String title, int optionType, int messageType,
+			Icon icon,final Object[] options, Object initialValue,final Popups.OptionChoosed   choosed)
+			throws HeadlessException 
+	{
+		final JOptionPane   pane = new JOptionPane(message, messageType,
+													   optionType, icon,
+													   options, initialValue);
+
+		pane.setInitialValue(initialValue);
+		//	pane.setComponentOrientation(((parentComponent == null) ?
+		//	getRootFrame() : parentComponent).getComponentOrientation());
+
+		int style = styleFromMessageType(messageType);
+		//JDialog dialog = pane.createDialog(parentComponent, title, style);
+
+
+		final JDialog dialog;
+
+		//Window window = JOptionPane.getWindowForComponent(parentComponent);
+		if (parentComponent instanceof Dialog) {
+			dialog = new JDialog((Dialog)parentComponent, title, true);
+		}
+		else if (parentComponent instanceof Frame) {
+			dialog = new JDialog((Frame)parentComponent , title, false);	
+		} else {
+			dialog = new JDialog(new JFrame() , title, false);
+		}
+		Container             contentPane = dialog.getContentPane();
+		contentPane.setLayout(new BorderLayout());
+		contentPane.add(pane, BorderLayout.CENTER);
+		dialog.setResizable(false);
+		if (JDialog.isDefaultLookAndFeelDecorated()) {
+			boolean supportsWindowDecorations = 
+			UIManager.getLookAndFeel().getSupportsWindowDecorations();
+			if (supportsWindowDecorations) {
+				dialog.setUndecorated(true);
+				pane.getRootPane().setWindowDecorationStyle(style);
+			}
+		}
+		dialog.pack();
+		dialog.setLocationRelativeTo(parentComponent);
+		dialog.addWindowListener(new WindowAdapter() {
+			private boolean gotFocus = false;
+			public void windowClosing(WindowEvent we) {
+				pane.setValue(null);
+				returnOption(pane.getValue(),options,choosed);
+			}
+			public void windowGainedFocus(WindowEvent we) {
+				// Once window gets focus, set initial focus
+				if (!gotFocus) {
+					pane.selectInitialValue();
+					gotFocus = true;
+				}
+			}
+		});
+		dialog.addComponentListener(new ComponentAdapter() {
+				public void componentShown(ComponentEvent ce) {
+				// reset value to ensure closing works properly
+					pane.setValue(JOptionPane.UNINITIALIZED_VALUE);
+				}
+		});
+		pane.addPropertyChangeListener(new PropertyChangeListener() {
+			public void propertyChange(PropertyChangeEvent event) {
+				// Let the defaultCloseOperation handle the closing
+				// if the user closed the window without selecting a button
+				// (newValue = null in that case).  Otherwise, close the dialog.
+				if(dialog.isVisible() && event.getSource() == pane &&
+				   (event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY)) &&
+					event.getNewValue() != null &&
+			event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE) {
+					 
+					//System.out.println(event.getNewValue()    );
+					returnOption(pane.getValue(),options,choosed);
+					dialog.dispose();	  
+				}
+								
+				
+			}
+		});
+		pane.selectInitialValue();
+		dialog.show();
+	}
+		
+	private static void returnOption(Object selectedValue, Object[] options,Popups.OptionChoosed choosed)
+	{
+		//System.out.println(selectedValue );
+		if(choosed == null) return;//messagedialog does not use choosed
+		if(selectedValue == null)	choosed.optionChoosed(JOptionPane.CLOSED_OPTION);
+		else if(options == null) {
+			if(selectedValue instanceof Integer)
+			choosed.optionChoosed(((Integer)selectedValue).intValue());
+			else choosed.optionChoosed(JOptionPane.CLOSED_OPTION);
+		}
+		else
+		{
+			for(int counter = 0, maxCounter = options.length;counter < maxCounter; counter++) 
+			{
+				if(options[counter].equals(selectedValue))
+				{
+					choosed.optionChoosed(counter);
+					return;
+				}
+			}
+			choosed.optionChoosed(JOptionPane.CLOSED_OPTION);
+		}
+	}
+
+	private static int styleFromMessageType(int messageType) {
+		   switch (messageType) {
+		   case JOptionPane.ERROR_MESSAGE:
+			   return JRootPane.ERROR_DIALOG;
+		   case JOptionPane.QUESTION_MESSAGE:
+			   return JRootPane.QUESTION_DIALOG;
+		   case JOptionPane.WARNING_MESSAGE:
+			   return JRootPane.WARNING_DIALOG;
+		   case JOptionPane.INFORMATION_MESSAGE:
+			   return JRootPane.INFORMATION_DIALOG;
+		   case JOptionPane.PLAIN_MESSAGE:
+		   default:
+			   return JRootPane.PLAIN_DIALOG;
+		   }
+	   }
+}
+
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/Digest.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/Digest.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/Digest.java	(revision 3952)
@@ -0,0 +1,49 @@
+/**
+ * @author Alexandre Correia - alexandrecorreia@celepar.pr.gov.br
+ * @ Calculo do hash para autenticacao Digest
+ */
+package nu.fw.jeti.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class Digest
+{
+	private byte[] gHash(String frase, String algoritmo)
+	{
+		try
+		{
+			MessageDigest md = MessageDigest.getInstance(algoritmo);
+			md.update(frase.getBytes());
+			return md.digest();
+		}
+		catch (NoSuchAlgorithmException e)
+		{
+			return null;
+		}
+	}
+	
+	private String stringHexa(byte[] bytes)
+	{
+		StringBuilder s = new StringBuilder();
+		for (int i = 0; i < bytes.length; i++)
+		{
+			int parteAlta = ((bytes[i] >> 4) & 0xf) << 4;
+			int parteBaixa = bytes[i] & 0xf;
+			if (parteAlta == 0) s.append('0');
+			s.append(Integer.toHexString(parteAlta | parteBaixa));
+		}
+		
+		return s.toString();
+	}
+
+	public String hashMD5(String nonce, String cnonce, String qop, String uri, String user, String password, String realm)
+	{
+		String HA1 = stringHexa(gHash( user + ":" + realm + ":" + password, "MD5"));
+		String nc = "00000001";
+		String HA2 = stringHexa(gHash(uri, "MD5"));
+		String DigestPROXY = HA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + HA2;
+
+		return stringHexa(gHash(DigestPROXY, "MD5"));
+	}
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/I18N.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/I18N.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/I18N.java	(revision 3952)
@@ -0,0 +1,203 @@
+/**
+ * @Author(s) - Alexndre Correia
+ * Created on - 30/06/2009
+ */
+
+package nu.fw.jeti.util;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.util.*;
+
+import javax.swing.AbstractButton;
+import javax.swing.JLabel;
+import nu.fw.jeti.languages.*;
+
+public class I18N implements languages, languagesPlugins
+{
+	private static HashMap messagesLang;
+	private static HashMap messagesLangPlugins;
+	
+	private static String getEncoding()
+	{
+		CharsetDecoder decoder = Charset.defaultCharset().newDecoder();
+        String charsetType = decoder.charset().toString();
+        
+        return charsetType;
+	}
+
+	public static String getText(String type, String messageID)
+	{
+		return getText(messageID);
+	}
+	
+	public static String gettext(String messageID)
+	{
+		return getText(messageID);
+	}
+	
+	private static String getText( String messageID )
+	{
+		if ( messageID == null )
+			return null;
+		
+		String translation = getTextWithAmp(messageID);
+		//remove &
+		int i = translation.indexOf("&");
+		
+		if ( i > -1 )
+			translation = translation.substring(0, i) + translation.substring(i + 1, translation.length());
+
+		return translation;
+	}
+
+	public static String getTextWithAmp(String messageID)
+	{ 
+		return I18N.getText_WithAmp(messageID);
+	}
+	
+	private static String getText_WithAmp( String messageID )
+	{
+		String translation = (String)messagesLang.get(messageID);
+		
+		if( translation == null )
+		{
+			translation = (String)messagesLangPlugins.get(messageID);
+			
+			if( translation == null )
+			{
+				int dotPos = messageID.lastIndexOf('.');
+				
+				if ( dotPos > 0 )
+					messageID = messageID.substring(dotPos+1);
+				
+				return messageID.replace('_',' ');
+			}
+		}
+
+		// Encoding UTF-8/ISO-8859-1		
+		try
+		{
+
+			translation = new String(translation.getBytes("UTF-8"), "UTF-8");
+
+		}catch(Exception e){}
+
+		return translation;
+	}
+
+	public static void setTextAndMnemonic(String messageID,JLabel label)
+	{
+        setTextAndMnemonic(messageID, label, false);
+    }
+
+	public static void setTextAndMnemonic(String messageID, JLabel label, boolean addDots )
+	{
+		String mnemonicText = I18N.getTextWithAmp(messageID);
+		String translation=mnemonicText;
+		int pos = mnemonicText.indexOf("&");
+		
+		if (pos > -1)
+			translation = mnemonicText.substring(0, pos) + mnemonicText.substring(pos + 1, mnemonicText.length());
+        
+		if (addDots)
+            translation += "...";
+		
+		label.setText(translation);
+		pos = ( (int)mnemonicText.indexOf("&") >= 0 ) ? ((int)mnemonicText.indexOf("&")) + 1 : -1;
+		
+		if ( pos > -1)
+			label.setDisplayedMnemonic(mnemonicText.charAt(pos));
+		
+		if ( pos > 0 )
+			label.setDisplayedMnemonicIndex(pos-1);
+	}
+	
+	public static void setMnemonic(String messageID, JLabel label)
+	{
+		String translation = I18N.getTextWithAmp(messageID);
+		int pos = ( (int)translation.indexOf("&") >= 0 ) ? ((int)translation.indexOf("&")) + 1 : -1;
+		
+		if ( pos > -1)
+			label.setDisplayedMnemonic(translation.charAt(pos));
+		
+		if ( pos > 1)
+			label.setDisplayedMnemonicIndex(pos-1);
+	}
+	
+	public static void setMnemonic(String messageID, AbstractButton button)
+	{
+		String translation = I18N.getTextWithAmp(messageID);
+		int pos = ( (int)translation.indexOf("&") >= 0 ) ? ((int)translation.indexOf("&")) + 1 : -1;
+		
+		if ( pos >- 1) 
+			button.setMnemonic(translation.charAt(pos));
+		if ( pos > 1)
+			button.setDisplayedMnemonicIndex(pos-1);
+	}
+
+	public static void setTextAndMnemonic(String messageID,AbstractButton button)
+	{
+        setTextAndMnemonic(messageID, button, false);
+    }
+
+	public static void setTextAndMnemonic(String messageID,AbstractButton button, boolean addDots)
+	{
+		String mnemonicText = getTextWithAmp(messageID);
+		String translation=mnemonicText;
+		
+		int pos = mnemonicText.indexOf("&");
+		
+		if (pos > -1)
+			translation = mnemonicText.substring(0, pos) + mnemonicText.substring(pos + 1, mnemonicText.length());
+        
+		if (addDots)
+            translation += "...";
+		
+        button.setText(translation);
+        
+		pos = ( (int)mnemonicText.indexOf("&") >= 0 ) ? ((int)mnemonicText.indexOf("&")) + 1 : -1;
+
+		if( pos > -1)
+			button.setMnemonic(mnemonicText.charAt(pos));
+
+		if( pos > 0 )
+			button.setDisplayedMnemonicIndex(pos-1);
+		
+	}
+
+	private static void loadLanguages(Locale myLocale)
+	{
+		if ( myLocale.toString().equals("pt_BR"))
+		{
+			for( int i = 0; i < language_pt_BR.length; i++ )
+				messagesLang.put(language_pt_BR[i][0].toString(), language_pt_BR[i][1].toString());
+			
+			for( int i = 0 ; i < languagePlugins_pt_BR.length; i++)
+				messagesLangPlugins.put(languagePlugins_pt_BR[i][0].toString(), languagePlugins_pt_BR[i][1].toString());
+		}
+	}
+	
+	public static void init(String myLanguage, String myCountry)
+	{
+		messagesLang = new HashMap();
+		messagesLangPlugins = new HashMap();
+
+		// Preferences
+		Locale myLocale   = null;
+
+		if( !myLanguage.equals("") && !myCountry.equals("") )
+			myLocale = new Locale(myLanguage, myCountry);
+		else
+			myLocale = new Locale("en", "US");
+		
+		I18N.loadLanguages(myLocale);
+	}
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/QueryServers.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/QueryServers.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/QueryServers.java	(revision 3952)
@@ -0,0 +1,128 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2004 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk or Jabber at jeti@jabber.org
+ *
+ *	Created on 8-aug-2004
+ */
+ 
+package nu.fw.jeti.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import nu.fw.jeti.backend.Start;
+import nu.fw.jeti.jabber.JID;
+import nu.fw.jeti.jabber.elements.DiscoItem;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * @author E.S. de Boer
+ *
+ */
+public class QueryServers
+{
+	private static List servers;
+		
+	public static List getServers()
+	{
+		if(servers==null) readServerXML();
+		return servers;
+	}
+	
+	private static void readServerXML()
+	{
+		SAXParser parser=null;
+		try{parser = SAXParserFactory.newInstance().newSAXParser();}
+		catch (FactoryConfigurationError ex){ex.printStackTrace();}
+		catch (SAXException ex){ex.printStackTrace();}
+		catch (ParserConfigurationException ex){ex.printStackTrace();}
+		InputStream  data = null;
+		try
+		{
+			data = (new URL(Start.dataURL + "servers.xml")).openStream();
+		}
+		catch (IOException ex)
+		{
+			//ex.printStackTrace();
+			data = QueryServers.class.getResourceAsStream("/servers.xml");
+		}
+		List items = new LinkedList();
+		if(data != null)
+		{
+			try
+			{
+				parser.parse(data,new DiscoItemsHandler(items));
+			}
+			catch (SAXException ex)
+			{
+				ex.printStackTrace();
+			}
+			catch (IOException ex)
+			{
+				ex.printStackTrace();
+			}
+		}
+		servers = items;
+	}
+	
+
+    static class DiscoItemsHandler extends DefaultHandler
+    {
+         private List items;
+	
+        public DiscoItemsHandler(List items)
+        {
+            this.items = items;
+        }
+	    
+        public void startElement(String uri, String localName, String qName, Attributes attr) 
+        {
+            if(qName.equals("item"))
+            {
+                if(items==null)items = new LinkedList();
+                try
+                {
+                    JID jid = JID.checkedJIDFromString(attr.getValue("jid"));
+                    items.add(new DiscoItem(jid,attr.getValue("name"),attr.getValue("node"),attr.getValue("action")));
+                } catch (InstantiationException e)
+                {
+                    Log.xmlParseException(e);
+                }
+            }
+        }
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/TreeModelSelector.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/TreeModelSelector.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/TreeModelSelector.java	(revision 3952)
@@ -0,0 +1,20 @@
+package nu.fw.jeti.util;
+
+/**
+ * Determines if a node in a tree shoudl be shown or not.
+ * Must only look at the node when determining status. Must NOT change
+ * depending on some external circumstance.
+ *
+ * @author Martin Forssen
+ */
+
+public interface TreeModelSelector {
+    boolean isVisible(Object o);
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/Log.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/Log.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/Log.java	(revision 3952)
@@ -0,0 +1,243 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail me at eric@jeti.tk
+ */
+
+package nu.fw.jeti.util;
+import java.io.*;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import nu.fw.jeti.events.JavaErrorListener;
+import nu.fw.jeti.jabber.Backend;
+import nu.fw.jeti.jabber.elements.Packet;
+
+/**
+ * @author E.S. de Boer
+ */
+
+public class Log
+{
+	private static Date date = new Date();
+	private static DateFormat dateFormat = DateFormat.getTimeInstance();
+	private static LinkedList xml = new LinkedList();
+	private static LinkedList xmlErrors = new LinkedList();
+	private static volatile LinkedList errors = new LinkedList();
+	private static Backend backend;
+	private static PrintStream diskLog = null;
+	
+	public Log(Backend backend) 
+	{
+		Log.backend= backend; 
+		//pipe err stream to log screen
+
+		catchExceptions();
+		 if (Preferences.getBoolean("jeti","debugToFile",false)) {
+            String name = Preferences.getString("jeti","debugFile", "out.log");
+            try {
+                diskLog = new PrintStream(new FileOutputStream(name, true));
+            } catch (IOException e) {
+                System.err.println("Failed to open debug log '" + name + "': " + e);
+            }
+        }
+	}
+
+	private void catchExceptions()
+	{
+		try
+		{
+			Thread t = new Thread()
+			{
+				PipedInputStream piNormal = new PipedInputStream();
+				PipedOutputStream poNormal = new PipedOutputStream(piNormal);
+				public void run()
+				{
+                    try {
+                    	System.setErr(new PrintStream(poNormal, true));
+                    } catch (Exception e) {System.out.println("Error logging setup failed");}
+					try
+					{
+						BufferedReader bin = new BufferedReader(new InputStreamReader(piNormal));
+						String output = null;
+						while ((output = bin.readLine()) != null)
+						{
+							System.out.println(output);
+							synchronized(errors)
+							{
+								errors.add(gettime() + " " + output);
+							}
+//							//do only once? ipv every line 
+							try
+							{//try catch because exceptions thrown here cause deadlock
+								for(Iterator j = backend.getListeners(JavaErrorListener.class);j.hasNext();)
+								{//show error in ui
+									((JavaErrorListener)j.next()).error(); 
+								}
+							}
+							catch(Exception e)
+							{
+								System.out.println(e.getMessage());	
+							}
+						}
+						bin.close();
+						piNormal.close();
+					}
+					catch (IOException ioe)
+					{
+						//write end dead then set system err again
+						try
+						{
+							piNormal.close();
+							poNormal.close();
+						}
+						catch (IOException e)
+						{
+							System.out.println("foutje");
+						}
+						catchExceptions();   
+					}
+				}
+			};
+			t.setDaemon(true);
+			t.start();
+		}catch (IOException i){System.out.println("foutje");}
+	}
+	
+	private static String gettime()
+	{
+		date.setTime(System.currentTimeMillis());
+		return dateFormat.format(date);
+	}
+
+    private static void loggedAdd(LinkedList list, String line) {
+        list.add(line);
+        if (diskLog != null) {
+            diskLog.println(line);
+        }
+    }
+    
+    public static void clear()
+	{
+    	synchronized(xml)
+		{
+    		xml.clear();
+		}
+	}
+
+	//***********xml log*******************\\
+	public static void xmlPacket(Packet packet)
+	{
+		synchronized(xml)
+		{
+			loggedAdd(xml, gettime() + " - p: " + packet);
+		}
+	}
+
+	public static void completeXML(StringBuffer message)
+	{
+		synchronized(xml)
+		{
+			loggedAdd(xml, gettime() + " - c: " + message);
+		}
+	}
+
+	public static void sendXML(String message)
+	{
+		synchronized(xml)
+		{
+			loggedAdd(xml, gettime() + " +  : " + message);
+		}
+	}
+
+	public static List getXML()
+	{
+		synchronized(xml)
+		{
+			return (List) xml.clone();
+		}
+	}
+
+	//*********xml errors***********\\
+	public static void notParsedXML(String message)
+	{
+		synchronized(xmlErrors)
+		{
+			xmlErrors.add(gettime() + " not parsed: " + message);
+		}
+	}
+
+	public static void xmlParseException(Exception e)
+	{
+		synchronized(xmlErrors)
+		{
+			xmlErrors.add(gettime() + " " + e.getMessage());
+		}
+	}
+
+	public static void xmlReceivedError(String message)
+	{
+		synchronized(xmlErrors)
+		{
+			xmlErrors.add(gettime() + " " + message);
+		}
+	}
+
+	public static List getXMLErrors()
+	{
+		synchronized(xmlErrors)
+		{
+			return (List) xmlErrors.clone();
+		}
+	}
+
+	//***********errors***************\\
+	public static void notSend(String message)
+	{
+		synchronized(errors)
+		{
+			errors.add(gettime() + " not Send " + message);
+		}
+	}
+
+	public static void error(String message)
+	{
+		synchronized(errors)
+		{
+			errors.add(gettime() + " error " + message);
+		}
+	}
+
+	public static List getErrors()
+	{
+		synchronized(errors)
+		{
+			return (List) errors.clone();
+		}
+	}
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/SwingWorker.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/SwingWorker.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/SwingWorker.java	(revision 3952)
@@ -0,0 +1,138 @@
+package nu.fw.jeti.util;
+
+import javax.swing.SwingUtilities;
+
+/**
+ * This is the 3rd version of SwingWorker (also known as
+ * SwingWorker 3), an abstract class that you subclass to
+ * perform GUI-related work in a dedicated thread.  For
+ * instructions on and examples of using this class, see:
+ * 
+ * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
+ *
+ * Note that the API changed slightly in the 3rd version:
+ * You must now invoke start() on the SwingWorker after
+ * creating it.
+ */
+public abstract class SwingWorker {
+    private Object value;  // see getValue(), setValue()
+
+    /** 
+     * Class to maintain reference to current worker thread
+     * under separate synchronization control.
+     */
+    private static class ThreadVar {
+        private Thread thread;
+        ThreadVar(Thread t) { thread = t; }
+        synchronized Thread get() { return thread; }
+        synchronized void clear() { thread = null; }
+    }
+
+    private ThreadVar threadVar;
+
+    /** 
+     * Get the value produced by the worker thread, or null if it 
+     * hasn't been constructed yet.
+     */
+    protected synchronized Object getValue() { 
+        return value; 
+    }
+
+    /** 
+     * Set the value produced by worker thread 
+     */
+    private synchronized void setValue(Object x) { 
+        value = x; 
+    }
+
+    /** 
+     * Compute the value to be returned by the <code>get</code> method. 
+     */
+    public abstract Object construct();
+
+    /**
+     * Called on the event dispatching thread (not on the worker thread)
+     * after the <code>construct</code> method has returned.
+     */
+    public void finished() {
+    }
+
+    /**
+     * A new method that interrupts the worker thread.  Call this method
+     * to force the worker to stop what it's doing.
+     */
+    public void interrupt() {
+        Thread t = threadVar.get();
+        if (t != null) {
+            t.interrupt();
+        }
+        threadVar.clear();
+    }
+
+    /**
+     * Return the value created by the <code>construct</code> method.  
+     * Returns null if either the constructing thread or the current
+     * thread was interrupted before a value was produced.
+     * 
+     * @return the value created by the <code>construct</code> method
+     */
+    public Object get() {
+        while (true) {  
+            Thread t = threadVar.get();
+            if (t == null) {
+                return getValue();
+            }
+            try {
+                t.join();
+            }
+            catch (InterruptedException e) {
+                Thread.currentThread().interrupt(); // propagate
+                return null;
+            }
+        }
+    }
+
+
+    /**
+     * Start a thread that will call the <code>construct</code> method
+     * and then exit.
+     */
+    public SwingWorker() {
+        final Runnable doFinished = new Runnable() {
+           public void run() { finished(); }
+        };
+
+        Runnable doConstruct = new Runnable() { 
+            public void run() {
+                try {
+                    setValue(construct());
+                }
+                finally {
+                    threadVar.clear();
+                }
+
+                SwingUtilities.invokeLater(doFinished);
+            }
+        };
+
+        Thread t = new Thread(doConstruct);
+        threadVar = new ThreadVar(t);
+    }
+
+    /**
+     * Start the worker thread.
+     */
+    public void start() {
+        Thread t = threadVar.get();
+        if (t != null) {
+            t.start();
+        }
+    }
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/TitleTimer.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/TitleTimer.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/TitleTimer.java	(revision 3952)
@@ -0,0 +1,86 @@
+//Created on 14-sep-2003
+package nu.fw.jeti.util;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JFrame;
+import javax.swing.Timer;
+
+/**
+ * Title:        im
+ * Description:
+ * Copyright:    Copyright (c) 2001
+ * Company:
+ * @author E.S. de Boer
+ * @version 1.0
+ */
+
+public class TitleTimer implements ActionListener
+{
+	private Timer timer;
+	private String first;
+	private String second;
+	private int teller;
+	private int max = 9;
+	private JFrame frame;
+	private String title;
+
+
+	public TitleTimer(JFrame frame, String title)
+	{
+		this.frame = frame;
+		this.title =title;
+		timer = new Timer(1200,this);
+	}
+
+	public void setTitle(String title)
+	{
+	    this.title = title;
+	}
+
+	public void init(String first, String second)
+	{
+		frame.setTitle(first);
+		timer.stop();
+		this.first = first;
+		this.second = second;
+		teller =0;
+		timer.restart();
+	}
+
+	public void stop()
+	{
+		timer.stop();
+	}
+	
+	public void actionPerformed(ActionEvent e)
+	{
+		if(teller > max)
+		{
+			teller =0;
+			timer.stop();
+			frame.setTitle(title);
+		}
+		else
+		{
+			if(teller%2==0)
+			{
+				frame.setTitle(first);
+			}
+			else
+			{
+				frame.setTitle(second);
+			}
+			teller++;
+		}
+		//System.out.println("timer");
+	}
+
+}
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/Base64.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/Base64.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/Base64.java	(revision 3952)
@@ -0,0 +1,655 @@
+package nu.fw.jeti.util;
+
+/**
+ * Encodes and decodes to and from Base64 notation.
+ *
+ * <p>
+ * Change Log:
+ * </p>
+ * <ul>
+ *  <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added
+ *   some convenience methods for reading and writing to and from files.</li>
+ *  <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems
+ *   with other encodings (like EBCDIC).</li>
+ *  <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the
+ *   encoded data was a single byte.</li>
+ *  <li>v2.0 - I got rid of methods that used booleans to set options. 
+ *   Now everything is more consolidated and cleaner. The code now detects
+ *   when data that's being decoded is gzip-compressed and will decompress it
+ *   automatically. Generally things are cleaner. You'll probably have to
+ *   change some method calls that you were making to support the new
+ *   options format (<tt>int</tt>s that you "OR" together).</li>
+ *  <li>v1.5.1 - Fixed bug when decompressing and decoding to a             
+ *   byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>.      
+ *   Added the ability to "suspend" encoding in the Output Stream so        
+ *   you can turn on and off the encoding if you need to embed base64       
+ *   data in an otherwise "normal" stream (like an XML file).</li>  
+ *  <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself.
+ *      This helps when using GZIP streams.
+ *      Added the ability to GZip-compress objects before encoding them.</li>
+ *  <li>v1.4 - Added helper methods to read/write files.</li>
+ *  <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li>
+ *  <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream
+ *      where last buffer being read, if not completely full, was not returned.</li>
+ *  <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li>
+ *  <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li>
+ * </ul>
+ *
+ * <p>
+ * I am placing this code in the Public Domain. Do with it as you will.
+ * This software comes with no guarantees or warranties but with
+ * plenty of well-wishing instead!
+ * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
+ * periodically to check for updates or to contribute improvements.
+ * </p>
+ *
+ * @author Robert Harder
+ * @author rob@iharder.net
+ * @version 2.1
+ */
+public class Base64
+{
+    
+/* ********  P U B L I C   F I E L D S  ******** */   
+    
+    
+    /** No options specified. Value is zero. */
+    public final static int NO_OPTIONS = 0;
+    
+    /** Specify encoding. */
+    public final static int ENCODE = 1;
+    
+    
+    /** Specify decoding. */
+    public final static int DECODE = 0;
+    
+    
+    /** Specify that data should be gzip-compressed. */
+    public final static int GZIP = 2;
+    
+    
+    /** Don't break lines when encoding (violates strict Base64 specification) */
+    public final static int DONT_BREAK_LINES = 8;
+    
+    
+/* ********  P R I V A T E   F I E L D S  ******** */  
+    
+    
+    /** Maximum line length (76) of Base64 output. */
+    private final static int MAX_LINE_LENGTH = 76;
+    
+    
+    /** The equals sign (=) as a byte. */
+    private final static byte EQUALS_SIGN = (byte)'=';
+    
+    
+    /** The new line character (\n) as a byte. */
+    private final static byte NEW_LINE = (byte)'\n';
+    
+    
+    /** Preferred encoding. */
+    private final static String PREFERRED_ENCODING = "UTF-8";
+    
+    
+    /** The 64 valid Base64 values. */
+    private final static byte[] ALPHABET;
+    private final static byte[] _NATIVE_ALPHABET = /* May be something funny like EBCDIC */
+    {
+        (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+        (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+        (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 
+        (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+        (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+        (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+        (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 
+        (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+        (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 
+        (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
+    };
+    
+    /** Determine which ALPHABET to use. */
+    static
+    {
+        byte[] __bytes;
+        try
+        {
+            __bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes( PREFERRED_ENCODING );
+        }   // end try
+        catch (java.io.UnsupportedEncodingException use)
+        {
+            __bytes = _NATIVE_ALPHABET; // Fall back to native encoding
+        }   // end catch
+        ALPHABET = __bytes;
+    }   // end static
+    
+    
+    /** 
+     * Translates a Base64 value to either its 6-bit reconstruction value
+     * or a negative number indicating some other meaning.
+     **/
+    private final static byte[] DECODABET =
+    {   
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,                 // Decimal  0 -  8
+        -5,-5,                                      // Whitespace: Tab and Linefeed
+        -9,-9,                                      // Decimal 11 - 12
+        -5,                                         // Whitespace: Carriage Return
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 14 - 26
+        -9,-9,-9,-9,-9,                             // Decimal 27 - 31
+        -5,                                         // Whitespace: Space
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,              // Decimal 33 - 42
+        62,                                         // Plus sign at decimal 43
+        -9,-9,-9,                                   // Decimal 44 - 46
+        63,                                         // Slash at decimal 47
+        52,53,54,55,56,57,58,59,60,61,              // Numbers zero through nine
+        -9,-9,-9,                                   // Decimal 58 - 60
+        -1,                                         // Equals sign at decimal 61
+        -9,-9,-9,                                      // Decimal 62 - 64
+        0,1,2,3,4,5,6,7,8,9,10,11,12,13,            // Letters 'A' through 'N'
+        14,15,16,17,18,19,20,21,22,23,24,25,        // Letters 'O' through 'Z'
+        -9,-9,-9,-9,-9,-9,                          // Decimal 91 - 96
+        26,27,28,29,30,31,32,33,34,35,36,37,38,     // Letters 'a' through 'm'
+        39,40,41,42,43,44,45,46,47,48,49,50,51,     // Letters 'n' through 'z'
+        -9,-9,-9,-9                                 // Decimal 123 - 126
+        /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
+        -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
+    };
+    
+    // I think I end up not using the BAD_ENCODING indicator.
+    //private final static byte BAD_ENCODING    = -9; // Indicates error in encoding
+    private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
+    private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
+
+    
+    /** Defeats instantiation. */
+    private Base64(){}
+    
+    
+    
+/* ********  E N C O D I N G   M E T H O D S  ******** */    
+    
+    
+    
+    
+    /**
+     * Encodes up to three bytes of the array <var>source</var>
+     * and writes the resulting four Base64 bytes to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying 
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 3 for
+     * the <var>source</var> array or <var>destOffset</var> + 4 for
+     * the <var>destination</var> array.
+     * The actual number of significant bytes in your array is
+     * given by <var>numSigBytes</var>.
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param numSigBytes the number of significant bytes in your array
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+     * @return the <var>destination</var> array
+     * @since 1.3
+     */
+    private static byte[] encode3to4( 
+     byte[] source, int srcOffset, int numSigBytes,
+     byte[] destination, int destOffset )
+    {
+        //           1         2         3  
+        // 01234567890123456789012345678901 Bit position
+        // --------000000001111111122222222 Array position from threeBytes
+        // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
+        //          >>18  >>12  >> 6  >> 0  Right shift necessary
+        //                0x3f  0x3f  0x3f  Additional AND
+        
+        // Create buffer with zero-padding if there are only one or two
+        // significant bytes passed in the array.
+        // We have to shift left 24 in order to flush out the 1's that appear
+        // when Java treats a value as negative that is cast from a byte to an int.
+        int inBuff =   ( numSigBytes > 0 ? ((source[ srcOffset     ] << 24) >>>  8) : 0 )
+                     | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
+                     | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
+
+        switch( numSigBytes )
+        {
+            case 3:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = ALPHABET[ (inBuff       ) & 0x3f ];
+                return destination;
+                
+            case 2:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>>  6) & 0x3f ];
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+                
+            case 1:
+                destination[ destOffset     ] = ALPHABET[ (inBuff >>> 18)        ];
+                destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
+                destination[ destOffset + 2 ] = EQUALS_SIGN;
+                destination[ destOffset + 3 ] = EQUALS_SIGN;
+                return destination;
+                
+            default:
+                return destination;
+        }   // end switch
+    }   // end encode3to4
+    
+    
+    
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source )
+    {
+        return encodeBytes( source, 0, source.length, NO_OPTIONS );
+    }   // end encodeBytes
+    
+
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param options Specified options
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int options )
+    {   
+        return encodeBytes( source, 0, source.length, options );
+    }   // end encodeBytes
+    
+    
+    /**
+     * Encodes a byte array into Base64 notation.
+     * Does not GZip-compress data.
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @since 1.4
+     */
+    public static String encodeBytes( byte[] source, int off, int len )
+    {
+        return encodeBytes( source, off, len, NO_OPTIONS );
+    }   // end encodeBytes
+    
+    
+
+    /**
+     * Encodes a byte array into Base64 notation.
+     * <p>
+     * Valid options:<pre>
+     *   GZIP: gzip-compresses object before encoding it.
+     *   DONT_BREAK_LINES: don't break lines at 76 characters
+     *     <i>Note: Technically, this makes your encoding non-compliant.</i>
+     * </pre>
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
+     * <p>
+     * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code>
+     *
+     *
+     * @param source The data to convert
+     * @param off Offset in array where conversion should begin
+     * @param len Length of data to convert
+     * @param options Specified options
+     * @see Base64#GZIP
+     * @see Base64#DONT_BREAK_LINES
+     * @since 2.0
+     */
+    public static String encodeBytes( byte[] source, int off, int len, int options )
+    {
+        // Isolate options
+        int dontBreakLines = ( options & DONT_BREAK_LINES );
+        int gzip           = ( options & GZIP   );
+        
+        // Compress?
+        if( gzip == GZIP )
+        {
+        	/*
+            java.io.ByteArrayOutputStream  baos  = null;
+            java.util.zip.GZIPOutputStream gzos  = null;
+            Base64.OutputStream            b64os = null;
+            
+    
+            try
+            {
+                // GZip -> Base64 -> ByteArray
+                baos = new java.io.ByteArrayOutputStream();
+                b64os = new Base64.OutputStream( baos, ENCODE | dontBreakLines );
+                gzos  = new java.util.zip.GZIPOutputStream( b64os ); 
+            
+                gzos.write( source, off, len );
+                gzos.close();
+            }   // end try
+            catch( java.io.IOException e )
+            {
+                e.printStackTrace();
+                return null;
+            }   // end catch
+            finally
+            {
+                try{ gzos.close();  } catch( Exception e ){}
+                try{ b64os.close(); } catch( Exception e ){}
+                try{ baos.close();  } catch( Exception e ){}
+            }   // end finally
+
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( baos.toByteArray(), PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( baos.toByteArray() );
+            }   // end catch
+            */
+        	return null;
+        }   // end if: compress
+        
+        // Else, don't compress. Better not to use streams at all then.
+        else
+        {
+            // Convert option to boolean in way that code likes it.
+            boolean breakLines = dontBreakLines == 0;
+            
+            int    len43   = len * 4 / 3;
+            byte[] outBuff = new byte[   ( len43 )                      // Main 4:3
+                                       + ( (len % 3) > 0 ? 4 : 0 )      // Account for padding
+                                       + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines      
+            int d = 0;
+            int e = 0;
+            int len2 = len - 2;
+            int lineLength = 0;
+            for( ; d < len2; d+=3, e+=4 )
+            {
+                encode3to4( source, d+off, 3, outBuff, e );
+
+                lineLength += 4;
+                if( breakLines && lineLength == MAX_LINE_LENGTH )
+                {   
+                    outBuff[e+4] = NEW_LINE;
+                    e++;
+                    lineLength = 0;
+                }   // end if: end of line
+            }   // en dfor: each piece of array
+
+            if( d < len )
+            {
+                encode3to4( source, d+off, len - d, outBuff, e );
+                e += 4;
+            }   // end if: some padding needed
+
+            
+            // Return value according to relevant encoding.
+            try
+            {
+                return new String( outBuff, 0, e, PREFERRED_ENCODING );
+            }   // end try
+            catch (java.io.UnsupportedEncodingException uue)
+            {
+                return new String( outBuff, 0, e );
+            }   // end catch
+            
+        }   // end else: don't compress
+        
+    }   // end encodeBytes
+    
+
+    
+    
+    
+/* ********  D E C O D I N G   M E T H O D S  ******** */
+    
+    
+    /**
+     * Decodes four bytes from array <var>source</var>
+     * and writes the resulting bytes (up to three of them)
+     * to <var>destination</var>.
+     * The source and destination arrays can be manipulated
+     * anywhere along their length by specifying 
+     * <var>srcOffset</var> and <var>destOffset</var>.
+     * This method does not check to make sure your arrays
+     * are large enough to accomodate <var>srcOffset</var> + 4 for
+     * the <var>source</var> array or <var>destOffset</var> + 3 for
+     * the <var>destination</var> array.
+     * This method returns the actual number of bytes that 
+     * were converted from the Base64 encoding.
+     * 
+     *
+     * @param source the array to convert
+     * @param srcOffset the index where conversion begins
+     * @param destination the array to hold the conversion
+     * @param destOffset the index where output will be put
+     * @return the number of decoded bytes converted
+     * @since 1.3
+     */
+    private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
+    {
+        // Example: Dk==
+        if( source[ srcOffset + 2] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset    ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
+            
+            destination[ destOffset ] = (byte)( outBuff >>> 16 );
+            return 1;
+        }
+        
+        // Example: DkL=
+        else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
+        {
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6 );
+            
+            destination[ destOffset     ] = (byte)( outBuff >>> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>>  8 );
+            return 2;
+        }
+        
+        // Example: DkLE
+        else
+        {
+            try{
+            // Two ways to do the same thing. Don't know which way I like best.
+            //int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] << 24 ) >>>  6 )
+            //              | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
+            //              | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
+            //              | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
+            int outBuff =   ( ( DECODABET[ source[ srcOffset     ] ] & 0xFF ) << 18 )
+                          | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
+                          | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) <<  6)
+                          | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF )      );
+
+            
+            destination[ destOffset     ] = (byte)( outBuff >> 16 );
+            destination[ destOffset + 1 ] = (byte)( outBuff >>  8 );
+            destination[ destOffset + 2 ] = (byte)( outBuff       );
+
+            return 3;
+            }catch( Exception e){
+                System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset     ] ]  ) );
+                System.out.println(""+source[srcOffset+1]+  ": " + ( DECODABET[ source[ srcOffset + 1 ] ]  ) );
+                System.out.println(""+source[srcOffset+2]+  ": " + ( DECODABET[ source[ srcOffset + 2 ] ]  ) );
+                System.out.println(""+source[srcOffset+3]+  ": " + ( DECODABET[ source[ srcOffset + 3 ] ]  ) );
+                return -1;
+            }   //e nd catch
+        }
+    }   // end decodeToBytes
+    
+    
+    
+    
+    /**
+     * Very low-level access to decoding ASCII characters in
+     * the form of a byte array. Does not support automatically
+     * gunzipping or any other "fancy" features.
+     *
+     * @param source The Base64 encoded data
+     * @param off    The offset of where to begin decoding
+     * @param len    The length of characters to decode
+     * @return decoded data
+     * @since 1.3
+     */
+    public static byte[] decode( byte[] source, int off, int len )
+    {
+        int    len34   = len * 3 / 4;
+        byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
+        int    outBuffPosn = 0;
+        
+        byte[] b4        = new byte[4];
+        int    b4Posn    = 0;
+        int    i         = 0;
+        byte   sbiCrop   = 0;
+        byte   sbiDecode = 0;
+        for( i = off; i < off+len; i++ )
+        {
+            sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
+            sbiDecode = DECODABET[ sbiCrop ];
+            
+            if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
+            {
+                if( sbiDecode >= EQUALS_SIGN_ENC )
+                {
+                    b4[ b4Posn++ ] = sbiCrop;
+                    if( b4Posn > 3 )
+                    {
+                        outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
+                        b4Posn = 0;
+                        
+                        // If that was the equals sign, break out of 'for' loop
+                        if( sbiCrop == EQUALS_SIGN )
+                            break;
+                    }   // end if: quartet built
+                    
+                }   // end if: equals sign or better
+                
+            }   // end if: white space, equals sign or better
+            else
+            {
+                System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
+                return null;
+            }   // end else: 
+        }   // each input character
+                                   
+        byte[] out = new byte[ outBuffPosn ];
+        System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 
+        return out;
+    }   // end decode
+    
+    
+    
+    
+    /**
+     * Decodes data from Base64 notation, automatically
+     * detecting gzip-compressed data and decompressing it.
+     *
+     * @param s the string to decode
+     * @return the decoded data
+     * @since 1.4
+     */
+    public static byte[] decode( String s )
+    {   
+        byte[] bytes;
+        try
+        {
+            bytes = s.getBytes( PREFERRED_ENCODING );
+        }   // end try
+        catch( java.io.UnsupportedEncodingException uee )
+        {
+            bytes = s.getBytes();
+        }   // end catch
+		//</change>
+        
+        // Decode
+        bytes = decode( bytes, 0, bytes.length );
+        
+        
+        // Check to see if it's gzip-compressed
+        // GZIP Magic Two-Byte Number: 0x8b1f (35615)
+        if( bytes != null && bytes.length >= 4 )
+        {
+            
+            int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);       
+            if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) 
+            {
+                java.io.ByteArrayInputStream  bais = null;
+                java.util.zip.GZIPInputStream gzis = null;
+                java.io.ByteArrayOutputStream baos = null;
+                byte[] buffer = new byte[2048];
+                int    length = 0;
+
+                try
+                {
+                    baos = new java.io.ByteArrayOutputStream();
+                    bais = new java.io.ByteArrayInputStream( bytes );
+                    gzis = new java.util.zip.GZIPInputStream( bais );
+
+                    while( ( length = gzis.read( buffer ) ) >= 0 )
+                    {
+                        baos.write(buffer,0,length);
+                    }   // end while: reading input
+
+                    // No error? Get new bytes.
+                    bytes = baos.toByteArray();
+
+                }   // end try
+                catch( java.io.IOException e )
+                {
+                    // Just return originally-decoded bytes
+                }   // end catch
+                finally
+                {
+                    try{ baos.close(); } catch( Exception e ){}
+                    try{ gzis.close(); } catch( Exception e ){}
+                    try{ bais.close(); } catch( Exception e ){}
+                }   // end finally
+
+            }   // end if: gzipped
+        }   // end if: bytes.length >= 2
+        
+        return bytes;
+    }   // end decode
+
+
+    
+
+        
+}   // end class Base64
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/TableSorter.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/TableSorter.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/TableSorter.java	(revision 3952)
@@ -0,0 +1,491 @@
+package nu.fw.jeti.util;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.table.*;
+
+/**
+ * TableSorter is a decorator for TableModels; adding sorting 
+ * functionality to a supplied TableModel. TableSorter does 
+ * not store or copy the data in its TableModel; instead it maintains 
+ * a map from the row indexes of the view to the row indexes of the 
+ * model. As requests are made of the sorter (like getValueAt(row, col)) 
+ * they are passed to the underlying model after the row numbers 
+ * have been translated via the internal mapping array. This way, 
+ * the TableSorter appears to hold another copy of the table 
+ * with the rows in a different order.
+ * <p>
+ * TableSorter registers itself as a listener to the underlying model,  
+ * just as the JTable itself would. Events revieved from the model  
+ * are examined, sometimes manipulated (typically widened), and then 
+ * passed on to the TableSorter's listeners (typically the JTable). 
+ * If a change to the model has invalidated the order of TableSorter's 
+ * rows, a note of this is made and the sorter will resort the 
+ * rows the next time a value is requested. 
+ * <p>
+ * When the tableHeader property is set, either by using the 
+ * setTableHeader() method or the two argument constructor, the 
+ * table header may be used as a complete UI for TableSorter. 
+ * The default renderer of the tableHeader is decorated with a renderer 
+ * that indicates the sorting status of each column. In addition, 
+ * a mouse listener is installed with the following behavior: 
+ * <ul>
+ * <li>
+ * Mouse-click: Clears the sorting status of all other columns 
+ * and advances the sorting status of that column through three 
+ * values: {NOT_SORTED, ASCENDING, DESCENDING} (then back to 
+ * NOT_SORTED again). 
+ * <li>
+ * SHIFT-mouse-click: Clears the sorting status of all other columns 
+ * and cycles the sorting status of the column through the same 
+ * three values, in a different order: {NOT_SORTED, DESCENDING, ASCENDING}.  
+ * <li>
+ * CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except 
+ * that the changes to the column do not cancel the statuses of columns 
+ * that are already sorting - giving a way to initiate a compound 
+ * sort.  
+ * </ul>
+ * 
+ * @author Philip Milne
+ * @author Brendon McLean 
+ * @author Dan van Enckevort
+ * @author Parwinder Sekhon
+ * 
+ */
+
+ /* 
+ *  This is a, long overdue, rewrite of a class of the same name that 
+ *  first appeared in the swing table demos in 1997. This version was 
+ *  last updated on: 25:2:2004 
+ */
+public class TableSorter extends AbstractTableModel {
+    protected TableModel tableModel;
+
+    public static final int DESCENDING = -1;
+    public static final int NOT_SORTED = 0;
+    public static final int ASCENDING = 1;
+
+    private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED);
+    
+    public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() {
+        public int compare(Object o1, Object o2) {
+            return ((Comparable) o1).compareTo(o2);
+        }
+    };
+    public static final Comparator LEXICAL_COMPARATOR = new Comparator() {
+        public int compare(Object o1, Object o2) {
+            return o1.toString().compareTo(o2.toString());
+        }
+    };
+
+    private Row[] viewToModel;
+    private int[] modelToView;
+
+    private JTableHeader tableHeader;
+    private MouseListener mouseListener;
+    private TableModelListener tableModelListener;
+    private Map columnComparators = new HashMap();
+    private List sortingColumns = new ArrayList();
+
+    public TableSorter() {
+        this.mouseListener = new MouseHandler();
+        this.tableModelListener = new TableModelHandler();
+    }
+
+    public TableSorter(TableModel tableModel) {
+        this();
+        setTableModel(tableModel);
+    }
+
+    public TableSorter(TableModel tableModel, JTableHeader tableHeader) {
+        this();
+        setTableHeader(tableHeader);
+        setTableModel(tableModel);
+    }
+
+    private void clearSortingState() {
+        viewToModel = null;
+        modelToView = null;
+    }
+
+    public TableModel getTableModel() {
+        return tableModel;
+    }
+
+    public void setTableModel(TableModel tableModel) {
+        if (this.tableModel != null) {
+            this.tableModel.removeTableModelListener(tableModelListener);
+        }
+
+        this.tableModel = tableModel;
+        if (this.tableModel != null) {
+            this.tableModel.addTableModelListener(tableModelListener);
+        }
+
+        clearSortingState();
+        fireTableStructureChanged();
+    }
+
+    public JTableHeader getTableHeader() {
+        return tableHeader;
+    }
+
+    public void setTableHeader(JTableHeader tableHeader) {
+        if (this.tableHeader != null) {
+            this.tableHeader.removeMouseListener(mouseListener);
+            TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();
+            if (defaultRenderer instanceof SortableHeaderRenderer) {
+                this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer);
+            }
+        }
+        this.tableHeader = tableHeader;
+        if (this.tableHeader != null) {
+            this.tableHeader.addMouseListener(mouseListener);
+            this.tableHeader.setDefaultRenderer(
+                    new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer()));
+        }
+    }
+
+    public boolean isSorting() {
+        return sortingColumns.size() != 0;
+    }
+
+    private Directive getDirective(int column) {
+        for (int i = 0; i < sortingColumns.size(); i++) {
+            Directive directive = (Directive)sortingColumns.get(i);
+            if (directive.column == column) {
+                return directive; 
+            }
+        }
+        return EMPTY_DIRECTIVE; 
+    }
+
+    public int getSortingStatus(int column) {
+        return getDirective(column).direction;
+    }
+
+    private void sortingStatusChanged() {
+        clearSortingState();
+        fireTableDataChanged();
+        tableHeader.repaint();
+    }
+
+    public void setSortingStatus(int column, int status) {
+        Directive directive = getDirective(column); 
+        if (directive != EMPTY_DIRECTIVE) {
+            sortingColumns.remove(directive); 
+        }
+        if (status != NOT_SORTED) { 
+            sortingColumns.add(new Directive(column, status));
+        }
+        sortingStatusChanged();
+    }
+
+    protected Icon getHeaderRendererIcon(int column, int size) {
+        Directive directive = getDirective(column);
+        if (directive == EMPTY_DIRECTIVE) {
+            return null; 
+        } 
+        return new Arrow(directive.direction == DESCENDING, size, sortingColumns.indexOf(directive)); 
+    }
+    
+    private void cancelSorting() {
+        sortingColumns.clear();
+        sortingStatusChanged();
+    }
+
+    public void setColumnComparator(Class type, Comparator comparator) {
+        if (comparator == null) {
+            columnComparators.remove(type);
+        } else {
+            columnComparators.put(type, comparator);
+        }
+    }
+
+    protected Comparator getComparator(int column) {
+        Class columnType = tableModel.getColumnClass(column);
+        Comparator comparator = (Comparator) columnComparators.get(columnType);
+        if (comparator != null) {
+            return comparator;
+        }
+        if (Comparable.class.isAssignableFrom(columnType)) {
+            return COMPARABLE_COMAPRATOR;
+        }
+        return LEXICAL_COMPARATOR;
+    }
+
+    private Row[] getViewToModel() {
+        if (viewToModel == null) {
+            int tableModelRowCount = tableModel.getRowCount();
+            viewToModel = new Row[tableModelRowCount];
+            for (int row = 0; row < tableModelRowCount; row++) {
+                viewToModel[row] = new Row(row);
+            }
+
+            if (isSorting()) {
+                Arrays.sort(viewToModel);
+            }
+        }
+        return viewToModel;
+    }
+
+    public int modelIndex(int viewIndex) {
+        return getViewToModel()[viewIndex].modelIndex;
+    }
+    
+    private int[] getModelToView() {
+        if (modelToView == null) {
+            int n = getViewToModel().length;
+            modelToView = new int[n];
+            for (int i = 0; i < n; i++) {
+                modelToView[modelIndex(i)] = i;
+            }
+        }
+        return modelToView;
+    }
+
+    // TableModel interface methods 
+
+    public int getRowCount() {
+        return (tableModel == null) ? 0 : tableModel.getRowCount();
+    }
+
+    public int getColumnCount() {
+        return (tableModel == null) ? 0 : tableModel.getColumnCount();
+    }
+
+    public String getColumnName(int column) {
+        return tableModel.getColumnName(column);
+    }
+
+    public Class getColumnClass(int column) {
+        return tableModel.getColumnClass(column);
+    }
+
+    public boolean isCellEditable(int row, int column) {
+        return tableModel.isCellEditable(modelIndex(row), column);
+    }
+
+    public Object getValueAt(int row, int column) {
+        return tableModel.getValueAt(modelIndex(row), column);
+    }
+
+    public void setValueAt(Object aValue, int row, int column) {
+        tableModel.setValueAt(aValue, modelIndex(row), column);
+    }
+
+    // Helper classes
+    
+    private class Row implements Comparable {
+        private int modelIndex;
+
+        public Row(int index) {
+            this.modelIndex = index;
+        }
+
+        public int compareTo(Object o) {
+            int row1 = modelIndex;
+            int row2 = ((Row) o).modelIndex;
+
+            for (Iterator it = sortingColumns.iterator(); it.hasNext();) {
+                Directive directive = (Directive) it.next();
+                int column = directive.column;
+                Object o1 = tableModel.getValueAt(row1, column);
+                Object o2 = tableModel.getValueAt(row2, column);
+
+                int comparison = 0;
+                // Define null less than everything, except null.
+                if (o1 == null && o2 == null) {
+                    comparison = 0;                  
+                } else if (o1 == null) { 
+                    comparison = -1;
+                } else if (o2 == null) {
+                    comparison = 1;
+                } else {
+                    comparison = getComparator(column).compare(o1, o2);
+                }
+                if (comparison != 0) {
+                    return directive.direction == DESCENDING ? -comparison : comparison;
+                }
+            }
+            return 0;
+        }
+    }
+
+    private class TableModelHandler implements TableModelListener {
+        public void tableChanged(TableModelEvent e) {
+            // If we're not sorting by anything, just pass the event along. 
+            if (!isSorting()) {
+                fireTableChanged(e);
+                return;
+            }
+                
+            // If the table structure has changed, cancel the sorting; the 
+            // sorting columns may have been either moved or deleted from 
+            // the model. 
+            if (e.getFirstRow() == TableModelEvent.HEADER_ROW) {
+                cancelSorting();
+                fireTableChanged(e);
+                return;
+            }
+
+            // We can map a cell event through to the view without widening             
+            // when the following conditions apply: 
+            // 
+            // a) all the changes are on one row (e.getFirstRow() == e.getLastRow()) and, 
+            // b) all the changes are in one column (column != TableModelEvent.ALL_COLUMNS) and,
+            // c) we are not sorting on that column (getSortingStatus(column) == NOT_SORTED) and, 
+            // d) a reverse lookup will not trigger a sort (modelToView != null)
+            //
+            // Note: INSERT and DELETE events fail this test as they have column == ALL_COLUMNS.
+            // 
+            // The last check, for (modelToView != null) is to see if modelToView 
+            // is already allocated. If we don't do this check; sorting can become 
+            // a performance bottleneck for applications where cells  
+            // change rapidly in different parts of the table. If cells 
+            // change alternately in the sorting column and then outside of             
+            // it this class can end up re-sorting on alternate cell updates - 
+            // which can be a performance problem for large tables. The last 
+            // clause avoids this problem. 
+            int column = e.getColumn();
+            if (e.getFirstRow() == e.getLastRow() 
+             && column != TableModelEvent.ALL_COLUMNS
+             && getSortingStatus(column) == NOT_SORTED
+             && modelToView != null) {
+                int viewIndex = getModelToView()[e.getFirstRow()];
+                fireTableChanged(new TableModelEvent(TableSorter.this, 
+                                                     viewIndex, viewIndex, 
+                                                     column, e.getType()));
+                return;
+            }
+
+            // Something has happened to the data that may have invalidated the row order. 
+            clearSortingState();
+            fireTableDataChanged();
+            return;
+        }
+    }
+
+    private class MouseHandler extends MouseAdapter {
+        public void mouseClicked(MouseEvent e) {
+            JTableHeader h = (JTableHeader) e.getSource();
+            TableColumnModel columnModel = h.getColumnModel();
+            int viewColumn = columnModel.getColumnIndexAtX(e.getX());
+            int column = columnModel.getColumn(viewColumn).getModelIndex();
+            if (column != -1) {
+                int status = getSortingStatus(column);
+                if (!e.isControlDown()) {
+                    cancelSorting();
+                }
+                // Cycle the sorting states through {NOT_SORTED, ASCENDING, DESCENDING} or 
+                // {NOT_SORTED, DESCENDING, ASCENDING} depending on whether shift is pressed. 
+                status = status + (e.isShiftDown() ? -1 : 1);
+                status = (status + 1) % 3 - 1; // signed mod returning {-1, 0, 1}
+                setSortingStatus(column, status);
+            }
+        }
+    }
+
+    private static class Arrow implements Icon {
+        private boolean descending;
+        private int size; 
+        private int priority; 
+        
+        public Arrow(boolean descending, int size, int priority) {
+            this.descending = descending;
+            this.size = size; 
+            this.priority = priority; 
+        }
+
+        public void paintIcon(Component c, Graphics g, int x, int y) {
+            Color color = c == null ? Color.GRAY : c.getBackground();             
+            // In a compound sort, make each succesive triangle 20% 
+            // smaller than the previous one. 
+            int dx = (int)(size/2*Math.pow(0.8, priority));
+            int dy = descending ? dx: -dx;
+            // Align icon (roughly) with font baseline. 
+            y = y + 5*size/6 + (descending ? -dy : 0);
+            int shift = descending ? 1 : -1;
+            g.translate(x, y);
+
+            // Right diagonal. 
+            g.setColor(color.darker());
+            g.drawLine(dx / 2, dy, 0, 0);
+            g.drawLine(dx / 2, dy + shift, 0, shift);
+            
+            // Left diagonal. 
+            g.setColor(color.brighter());
+            g.drawLine(dx / 2, dy, dx, 0);
+            g.drawLine(dx / 2, dy + shift, dx, shift);
+            
+            // Horizontal line. 
+            if (descending) {
+                g.setColor(color.darker().darker());
+            } else {
+                g.setColor(color.brighter().brighter());
+            }
+            g.drawLine(dx, 0, 0, 0);
+
+            g.setColor(color);
+            g.translate(-x, -y);
+        }
+
+        public int getIconWidth() {
+            return size;
+        }
+
+        public int getIconHeight() {
+            return size;
+        }
+    }
+
+    private class SortableHeaderRenderer implements TableCellRenderer {
+        private TableCellRenderer tableCellRenderer;
+
+        public SortableHeaderRenderer(TableCellRenderer tableCellRenderer) {
+            this.tableCellRenderer = tableCellRenderer;
+        }
+
+        public Component getTableCellRendererComponent(JTable table, 
+                                                       Object value,
+                                                       boolean isSelected, 
+                                                       boolean hasFocus,
+                                                       int row, 
+                                                       int column) {
+            Component c = tableCellRenderer.getTableCellRendererComponent(table, value, 
+                                                                          isSelected, 
+                                                                          hasFocus, 
+                                                                          row, column);
+            if (c instanceof JLabel) {
+                JLabel l = (JLabel) c;
+                l.setHorizontalTextPosition(JLabel.LEFT);
+                int modelColumn = table.convertColumnIndexToModel(column);
+                l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize()));
+            }
+            return c;
+        }
+    }
+    
+    private static class Directive {
+        private int column; 
+        private int direction;
+
+        public Directive(int column, int direction) {
+            this.column = column;
+            this.direction = direction;
+        }
+    }
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/Popups.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/Popups.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/Popups.java	(revision 3952)
@@ -0,0 +1,164 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ */
+
+package nu.fw.jeti.util;
+
+import java.awt.Container;
+import java.awt.HeadlessException;
+
+import javax.swing.Icon;
+import javax.swing.JOptionPane;
+
+/**
+ * This class wraps Joptionpane to provide non blocking popups
+ * @author E.S. de Boer
+ */
+public class Popups
+{
+	static private Container main;
+
+    public Popups(Container window)
+    {
+		main = window;
+	}
+
+	public static void errorPopup(String error,String title)
+	{
+		popup(error,title,JOptionPane.ERROR_MESSAGE);
+		//JOptionPane.showMessageDialog(main,error,title,JOptionPane.ERROR_MESSAGE);
+	}
+
+	public static void criticalErrorPopup(String error,String title)
+	{
+		//popup(error,title,javax.swing.JOptionPane.ERROR_MESSAGE);
+		JOptionPane.showMessageDialog(main,error,title,JOptionPane.ERROR_MESSAGE);
+	}
+
+	public static void messagePopup(String message,String title)
+	{
+		popup(message,title,JOptionPane.INFORMATION_MESSAGE);
+		//JOptionPane.showMessageDialog(main,error,title,JOptionPane.ERROR_MESSAGE);
+	}
+
+	public static void popup(String text,String title,int message)
+	{
+		if(main!=null)main.show();// toFront();
+		showMessageDialog(text,title,message);
+	}
+	
+	public static void showOptionDialog(Object message, String title, int optionType, int messageType,
+				Icon icon,final Object[] options, Object initialValue,OptionChoosed choosed)
+	{
+		if(main!=null)main.show();
+		JOptionPaneNonModal.showOptionDialog( main,message,title,optionType,messageType,icon,options,initialValue ,choosed); 		
+	}
+				
+
+	/*
+	static class TempThread extends Thread
+	{
+		private String title;
+		private String text;
+		private int message;
+
+		public TempThread(String text, String title,int message)
+		{
+		    this.text =text;
+			this.title =title;
+			this.message =message;
+		}
+
+		public void run()
+		{
+			popup(text,title,message);
+		}
+	}
+	*/
+	
+	/**
+	 * Brings up a dialog that displays a message using a default
+	 * icon determined by the <code>messageType</code> parameter.
+	 *
+	 * @param parentComponent determines the <code>Frame</code>
+	 *		in which the dialog is displayed; if <code>null</code>,
+	 *		or if the <code>parentComponent</code> has no
+	 *		<code>Frame</code>, a default <code>Frame</code> is used
+	 * @param message   the <code>Object</code> to display
+	 * @param title     the title string for the dialog
+	 * @param messageType the type of message to be displayed:
+	 *                  <code>ERROR_MESSAGE</code>,
+	 *			<code>INFORMATION_MESSAGE</code>,
+	 *			<code>WARNING_MESSAGE</code>,
+	 *                  <code>QUESTION_MESSAGE</code>,
+	 *			or <code>PLAIN_MESSAGE</code>
+	 * @exception HeadlessException if
+	 *   <code>GraphicsEnvironment.isHeadless</code> returns
+	 *   <code>true</code>
+	 * @see java.awt.GraphicsEnvironment#isHeadless
+	 */
+	public static void showMessageDialog(Object message, String title, int messageType)
+	{
+		showMessageDialog(message, title, messageType, null);
+	}
+
+	/**
+	 * Brings up a dialog displaying a message, specifying all parameters.
+	 *
+	 * @param parentComponent determines the <code>Frame</code> in which the
+	 *			dialog is displayed; if <code>null</code>,
+	 *			or if the <code>parentComponent</code> has no
+	 *			<code>Frame</code>, a 
+	 *                  default <code>Frame</code> is used
+	 * @param message   the <code>Object</code> to display
+	 * @param title     the title string for the dialog
+	 * @param messageType the type of message to be displayed:
+	 *                  <code>ERROR_MESSAGE</code>,
+	 *			<code>INFORMATION_MESSAGE</code>,
+	 *			<code>WARNING_MESSAGE</code>,
+	 *                  <code>QUESTION_MESSAGE</code>,
+	 *			or <code>PLAIN_MESSAGE</code>
+	 * @param icon      an icon to display in the dialog that helps the user
+	 *                  identify the kind of message that is being displayed
+	 * @exception HeadlessException if
+	 *   <code>GraphicsEnvironment.isHeadless</code> returns
+	 *   <code>true</code>
+	 * @see java.awt.GraphicsEnvironment#isHeadless
+	 */
+	public static void showMessageDialog(	Object message, String title, int messageType, Icon icon)
+	{
+		showOptionDialog(message, title, JOptionPane.DEFAULT_OPTION, 
+						 messageType, icon, null, null,null);
+	}
+	
+	public interface OptionChoosed
+	{
+		void optionChoosed(int option);
+	}
+
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/JavaScriptServerExpresso.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/JavaScriptServerExpresso.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/JavaScriptServerExpresso.java	(revision 3952)
@@ -0,0 +1,88 @@
+/**
+ * @Authors		: Alexandre Correia - alexandrecorreia@celepar.pr.gov.br 
+ * 			  	  Rodrigo Souza - rodsouza@celepar.pr.gov.br
+ * @Date		: 19/01/2009;
+ * @Description	: Executa JavaScript na pagina do Expresso.  
+ *
+ */
+
+package nu.fw.jeti.util;
+
+import netscape.javascript.JSObject;
+import nu.fw.jeti.jabber.elements.Presence;
+
+public class JavaScriptServerExpresso
+{
+	public void cleanStatus()
+	{
+		final String[] param = new String[2];
+		param[0] = "cleanStatus";	
+		param[1] = " ";
+
+		Thread cleanSt = new Thread()
+		{
+			public void run()
+			{
+				try
+				{
+					if( nu.fw.jeti.applet.Jeti.WINDOWAPPLET != null )
+					{
+						JSObject winApplet = nu.fw.jeti.applet.Jeti.WINDOWAPPLET;
+						winApplet.call("getArgumentsApplet", param);			
+					}
+				}
+				catch(Exception e){}
+			}
+		};
+		
+		cleanSt.start();
+	}
+	
+	private void ExecuteJs(int Status)
+	{
+		final String[] param = new String[2];
+		param[0] = "getArgumentsApplet";
+		param[1] = (String)this.TypeStatus(Status);
+
+		Thread ExeJs = new Thread()
+		{
+			public void run()
+			{
+				try
+				{
+					if( nu.fw.jeti.applet.Jeti.WINDOWAPPLET != null )
+					{
+						JSObject winApplet = nu.fw.jeti.applet.Jeti.WINDOWAPPLET;
+						winApplet.call("getArgumentsApplet", param);			
+					}
+				}
+				catch(Exception e){}
+			}
+		};
+		
+		ExeJs.start();
+	}
+
+	public void getStatus(int Status)
+	{
+		this.ExecuteJs(Status);
+	}
+	
+	private String TypeStatus(int Status)
+	{
+		String nameStatus = new String("");
+		
+        switch (Status)
+        {
+		    case Presence.AVAILABLE:     nameStatus = "available"; break;
+		    case Presence.FREE_FOR_CHAT: nameStatus = "chat"; break;
+		    case Presence.AWAY:          nameStatus = "away"; break;
+		    case Presence.XA:            nameStatus = "xa"; break;
+		    case Presence.DND:           nameStatus = "dnd"; break;
+		    case Presence.UNAVAILABLE:   nameStatus = "unavailable"; break;
+		    case Presence.INVISIBLE:     nameStatus = "invisible"; break;
+        }			
+		
+		return nameStatus;
+	}
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/TreeExpander.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/TreeExpander.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/TreeExpander.java	(revision 3952)
@@ -0,0 +1,85 @@
+package nu.fw.jeti.util;
+
+import java.util.Hashtable;
+
+import javax.swing.JTree;
+import javax.swing.SwingUtilities;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeExpansionListener;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+/**
+ * Used to filter some nodes from a tree. That is nodes which the
+ * selector decides not to show does not seem to exist in the tree.
+ * The selector is assumed to not change behavior over time.
+ *
+ * @author Martin Forssen
+ */
+
+public class TreeExpander implements TreeExpansionListener, TreeModelListener {
+    private JTree tree;
+    private Hashtable known = new Hashtable();
+
+    public TreeExpander(JTree tree, TreeModel model) {
+        this.tree = tree;
+
+        tree.addTreeExpansionListener(this);
+        model.addTreeModelListener(this);
+    }
+
+    public void expand(TreePath path) {
+        //System.out.println("Expand: " + path + " " + tree.isCollapsed(path) + " " + tree.isExpanded(path));
+        known.put(path, new Boolean(true));
+        tree.expandPath(path);
+    }
+
+    /*
+     * TreeModelListener interface
+     */
+    public void treeNodesChanged(TreeModelEvent e) {
+        //System.out.println("changed: " + e);
+    }
+
+    public void treeNodesInserted(TreeModelEvent e) {
+        //System.out.println("inserted: " + e);
+    }
+
+    public void treeNodesRemoved(TreeModelEvent e) {
+        //System.out.println("removed: " + e);
+    }
+
+    public void treeStructureChanged(TreeModelEvent e) {
+        //System.out.println("structChanged: " + e.getTreePath());
+        Boolean b = (Boolean)known.get(e.getTreePath());
+        if (b != null && b.booleanValue()) {
+            // System.out.println("do expand");
+            //if (!tree.isExpanded(e.getTreePath())) {
+                tree.expandPath(e.getTreePath());
+                //}
+                //} else {
+                //System.out.println("Do nothing: " + b);
+        }
+    }
+
+    /*
+     * TreeExpansionListener
+     */
+    public void treeExpanded(TreeExpansionEvent event) {
+        //System.out.println("expanded: " + event.getPath());
+        Boolean b = (Boolean)known.get(event.getPath());
+        if (b == null && !b.booleanValue()) {
+            known.put(event.getPath(), Boolean.FALSE);
+        }
+    }
+
+    public void treeCollapsed(TreeExpansionEvent event) {
+        //System.out.println("collapsed: " + event.getPath());
+        Boolean b = (Boolean)known.get(event.getPath());
+        if (b != null && b.booleanValue()) {
+            known.put(event.getPath(), Boolean.FALSE);
+        }
+    }
+}
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/TreeModelFilter.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/TreeModelFilter.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/TreeModelFilter.java	(revision 3952)
@@ -0,0 +1,238 @@
+package nu.fw.jeti.util;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.LinkedList;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+/**
+ * Used to filter some nodes from a tree. That is nodes which the
+ * selector decides not to show does not seem to exist in the tree.
+ * The selector is assumed to not change behavior over time.
+ *
+ * @author Martin Forssen
+ */
+
+public class TreeModelFilter implements TreeModel, TreeModelListener {
+    private TreeModelSelector selector;
+    private TreeModel model;
+    private LinkedList listeners = new LinkedList();
+    private Hashtable nodes = new Hashtable(30);
+
+    public TreeModelFilter(TreeModel model, TreeModelSelector selector) {
+        this.model = model;
+        this.selector = selector;
+
+        model.addTreeModelListener(this);
+    }
+
+    /*
+     * TreeModel interface
+     */
+    public Object getRoot() {
+        return model.getRoot();
+    }
+
+    public Object getChild(Object parent, int index) {
+        int max = model.getChildCount(parent);
+        for (int i=0, v=0; i<max; i++) {
+            Object child = model.getChild(parent, i);
+            boolean isVisible = selector.isVisible(child);
+            nodes.put(child, Boolean.valueOf(isVisible));
+            if (isVisible && index == v++) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    public int getChildCount(Object parent) {
+        int max = model.getChildCount(parent);
+        int count = 0;
+
+        for (int i=0; i<max; i++) {
+            Object child = model.getChild(parent, i);
+            boolean isVisible = selector.isVisible(child);
+            nodes.put(child,Boolean.valueOf(isVisible));
+            if (isVisible) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    public boolean isLeaf(Object node) {
+        return model.isLeaf(node);
+    }
+
+    public void valueForPathChanged(TreePath path, Object newValue) {
+        // TODO
+    }
+
+    public int getIndexOfChild(Object parent, Object child) {
+        int max = model.getChildCount(parent);
+        for (int i=0, v=0; i<max; i++) {
+            Object mChild = model.getChild(parent, i);
+            if (mChild.equals(child)) {
+                return v;
+            }
+            if (selector.isVisible(child)) {
+                v++;
+            }
+        }
+        return -1;
+    }
+
+    public void addTreeModelListener(TreeModelListener l) {
+        listeners.add(l);
+    }
+
+    public void removeTreeModelListener(TreeModelListener l) {
+        listeners.remove(l);
+    }
+
+    /*
+     * TreeModelListener interface
+     */
+    public void treeNodesChanged(TreeModelEvent e) {
+        int indices[] = e.getChildIndices();
+        Object children[] = e.getChildren();
+
+        if (indices == null || indices.length == 0) {
+            TreeModelEvent e2 = new TreeModelEvent(this, e.getTreePath());
+            for (int i = 0; i < listeners.size(); i++) {
+                ((TreeModelListener)listeners.get(i)).treeNodesChanged(e2);
+            }
+        } else {
+            for (int i=0, v=0; i < indices.length; i++) {
+                nodeChanged(e.getTreePath(), indices[i], children[i]);
+            }
+        }
+    }
+
+    public void treeNodesInserted(TreeModelEvent e) {
+        ArrayList newIndices = new ArrayList();
+        ArrayList newChildren = new ArrayList();
+        Object parent = e.getTreePath().getLastPathComponent();
+        int indices[] = e.getChildIndices();
+        Object children[] = e.getChildren();
+        
+        for (int i=0, v=0; i < indices.length; i++) {
+            boolean vis = selector.isVisible(children[i]);
+            nodes.put(children[i], Boolean.valueOf(vis));
+            if (vis) {
+                newIndices.add(new Integer(upperIndex(parent, indices[i])));
+                newChildren.add(children[i]);
+            }
+        }
+        if (newIndices.size() > 0) {
+            TreeModelEvent e2 = createEvent(e.getTreePath(), newIndices,
+                                            newChildren);
+            for (int i = 0; i < listeners.size(); i++) {
+                ((TreeModelListener)listeners.get(i)).treeNodesInserted(e2);
+            }
+        }
+    }
+
+    public void treeNodesRemoved(TreeModelEvent e) {
+        ArrayList newIndices = new ArrayList();
+        ArrayList newChildren = new ArrayList();
+        Object parent = e.getTreePath().getLastPathComponent();
+        int indices[] = e.getChildIndices();
+        Object children[] = e.getChildren();
+        
+        for (int i=0, v=0; i < indices.length; i++) {
+            Boolean visible = (Boolean)nodes.get(children[i]);
+            if (visible != null && visible.booleanValue()) {
+                newIndices.add(new Integer(upperIndex(parent, indices[i])));
+                newChildren.add(children[i]);
+            }
+            nodes.remove(children[i]);
+        }
+
+        if (newIndices.size() > 0) {
+            TreeModelEvent e2 = createEvent(e.getTreePath(), newIndices,
+                                            newChildren);
+            for (int i = 0; i < listeners.size(); i++) {
+                ((TreeModelListener)listeners.get(i)).treeNodesRemoved(e2);
+            }
+        }
+    }
+
+    public void treeStructureChanged(TreeModelEvent e) {
+        TreePath path = e.getTreePath();
+        Object node = path.getLastPathComponent();
+        nodes.put(node, Boolean.valueOf(selector.isVisible(node)));
+        if (path.getPathCount() > 1) {
+            path = path.getParentPath();
+        }
+        TreeModelEvent e2 = new TreeModelEvent(this, path);
+        for (int i = 0; i < listeners.size(); i++) {
+            ((TreeModelListener)listeners.get(i)).treeStructureChanged(e2);
+        }
+    }
+
+    private TreeModelEvent createEvent(TreePath path,
+                                       ArrayList indices,
+                                       ArrayList children) {
+        int intIndices[] = new int[indices.size()];
+        for (int i=0; i<indices.size(); i++) {
+            intIndices[i] = ((Integer)indices.get(i)).intValue();
+        }
+        return new TreeModelEvent(
+            this, path, intIndices,
+            children.toArray(new Object[children.size()]));
+    }
+
+    private int upperIndex(Object parent, int index) {
+        int vIndex = 0;
+        for (int i=0; i < index; i++) {
+            if (selector.isVisible(model.getChild(parent, i))) {
+                vIndex++;
+            }
+        }
+        return vIndex;
+    }
+
+    private void nodeChanged(TreePath path, int index, Object node) {
+        Object parent = path.getLastPathComponent();
+
+        boolean newVis = selector.isVisible(node);
+        Boolean oldVis = (Boolean)nodes.get(node);
+        if (newVis && (oldVis == null || !oldVis.booleanValue())) {
+            int indices[] = {upperIndex(parent, index)};
+            Object children[] = {node};
+            TreeModelEvent e = new TreeModelEvent(this, path,indices,children);
+            for (int i = 0; i < listeners.size(); i++) {
+                ((TreeModelListener)listeners.get(i)).treeNodesInserted(e);
+            }
+        } else if (!newVis && oldVis != null && oldVis.booleanValue()) {
+            int indices[] = {upperIndex(parent, index)};
+            Object children[] = {node};
+            TreeModelEvent e = new TreeModelEvent(this, path,indices,children);
+            for (int i = 0; i < listeners.size(); i++) {
+                ((TreeModelListener)listeners.get(i)).treeNodesRemoved(e);
+            }
+        } else if (newVis && oldVis != null && oldVis.booleanValue()) {
+            int vIndex = upperIndex(path.getLastPathComponent(), index);
+            TreeModelEvent e = new TreeModelEvent(this, path,
+                                                  new int[] {vIndex},
+                                                  new Object[] {node});
+            for (int i = 0; i < listeners.size(); i++) {
+                ((TreeModelListener)listeners.get(i)).treeNodesChanged(e);
+            }
+        }
+        nodes.put(node, Boolean.valueOf(newVis));
+    }
+}
+
+/*
+ * Overrides for emacs
+ * Local variables:
+ * tab-width: 4
+ * End:
+ */
Index: 3thparty/jmessenger/src/nu/fw/jeti/util/ImageFileFilter.java
===================================================================
--- 3thparty/jmessenger/src/nu/fw/jeti/util/ImageFileFilter.java	(revision 3952)
+++ 3thparty/jmessenger/src/nu/fw/jeti/util/ImageFileFilter.java	(revision 3952)
@@ -0,0 +1,50 @@
+/* 
+ *	Jeti, a Java Jabber client, Copyright (C) 2001 E.S. de Boer  
+ *
+ *  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
+ *
+ *	For questions, comments etc, 
+ *	use the website at http://jeti.jabberstudio.org
+ *  or mail/IM me at jeti@jabber.org
+ *  
+ *  Created on 8-mrt-2006
+ */
+
+package nu.fw.jeti.util;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+public class ImageFileFilter extends FileFilter
+{
+	public boolean accept(File file)
+	{
+		if(file.isDirectory())return true;
+		String filename = file.getName().toLowerCase();
+		if(filename.endsWith("gif") 
+				|| filename.endsWith("png")
+				|| filename.endsWith("jpg")
+				|| filename.endsWith("jpeg")){
+			return true;
+		}
+		return false;
+	}
+
+	public String getDescription()
+	{
+		return "Images, *.jpg,*.png,*.gif";
+	}
+}
Index: 3thparty/jmessenger/src/docs/license.txt
===================================================================
--- 3thparty/jmessenger/src/docs/license.txt	(revision 3952)
+++ 3thparty/jmessenger/src/docs/license.txt	(revision 3952)
@@ -0,0 +1,278 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
Index: 3thparty/jmessenger/src/docs/README.I18N
===================================================================
--- 3thparty/jmessenger/src/docs/README.I18N	(revision 3952)
+++ 3thparty/jmessenger/src/docs/README.I18N	(revision 3952)
@@ -0,0 +1,40 @@
+Jeti supports I18N using the native Java classes for this. The translations are stored in standard "properties" resources bundles. You can either convert this .properties files to .po (POT format) files and work with a GNU Gettext toolchain or work directly with the .properties files.
+
+If you decide to work directly with the .properties file, just open the base translation file (jeti.properties) with your favorite text editor (Ultraedit or Bluefish are my personal favorites), or a specialized tool, and translate the strings wich appear after the = sign. You must be careful: Java's resources bundles requires all the Unicode stuff to be coded, so you'll need to use some tool to work with the resuorce bundle format (there are some tools for this, see below) or code the strings by hand! Shocked
+
+On the other hand, if you prefer to work with POT files (.po), then convert the .properties to .mo and then to .po; then you can use a tool to edit the file (see below for tools), then make the .mo file and convert the .mo back to .properties. Maybe this sounds too complicated, but GNU Gettext suite provides useful tools for this task (man msgfmt and man msgunfmt).
+
+Please note that the keys of the resources have a format. This is, the window.string with spaces replaced by underscores. All shourtcut keys are indicated by the "standard" ampersand & symbol before the key. The I18N classes take care of managing this internally.
+
+The translations files should be named as:
+
+jeti_XX.properties for the main Jeti translation.
+plugins_XX.properties for the plugins translations.
+
+where XX is the two character code for your language, following the ISO standard (please see http://lcweb.loc.gov/standards/iso639-2/langcodes.html).
+
+Then, please submit your translation to Eric or to Equinoxe (see profiles) so it can be commited to CVS (you should always try to translate the CVS version of the properties files Smile ). Of course, if you plan to use the jar distribution from Jeti's site, then you should translate the .properties which come with that distribution.
+
+Tools:
+
+- Resource Bundle Editor https://resourcebundleeditor.dev.java.net/, a free editor for Java's resource bundles. Highly recommended for the Java approach.
+- Eclipse with some plugin for resource bundles edition.
+
+- poEdit http://poedit.sourceforge.net/ is a very good POT editor with the nice feature of remembering previous translations. Highly recommended for the GNU approach.
+- GNU Gettext http://www.gnu.org/software/gettext/ is the GNU's software for I18N.
+- Bluefish http://bluefish.openoffice.nl/ is a nice free programing editor.
+
+Here are some more programs to translate Jeti
+
+http://ostermiller.org/attesoro/similar.html
+
+I use the second one on that site:
+http://www.solyp.com/binaries/2975/babelfish.zip
+
+Please don't use the zaval resource editor, it can't handle the : that are in some keys leading to untranslated entries. further it doesn't warn you when there are untranslated entries, it just fills them in with the default text on save.
+
+
+Just open the jeti.properties or the plugins.properties,(you find them in the language directory of jeti) add a language (in the file menu) and start translating. It is not very difficult this way, just typing the right translation Smile
+
+Finally, this is a good start for I18N in Java: http://www.joconner.com/javai18n/
+
Index: 3thparty/jmessenger/src/docs/README.txt
===================================================================
--- 3thparty/jmessenger/src/docs/README.txt	(revision 3952)
+++ 3thparty/jmessenger/src/docs/README.txt	(revision 3952)
@@ -0,0 +1,42 @@
+Jeti A Java Jabber Messenger
+
+The goal of Jeti is to be a complete Jabber messenger which can be used as applet
+as well as a standalone Java application. It is build with a small core extended
+with plugins, so only the features that are necessary are loaded.
+
+Building Jeti
+
+Jeti consists of a main program and several plugins, you can build Jeti with
+the inculded ant build file in the buildfiles directory. You can also build the
+plugins seperatly with the provided buildfiles.
+
+Standalone, Webstart or Applet version
+
+You can deploy Jeti on three different ways, they all use the same sourcecode.
+The applet version doesn't need the nu.fw.jeti.ui.login package and the other 
+versions don't need the nu.fw.jeti.applet package. The webstart and Applet need
+to be signed to connect to a different server then the one they were started from
+you can do this with the java jarsigner tool included with the JDK
+
+Global overview
+
+Jeti consits of several packages:
+backend This package consists of the low level network interface and the main class (Start)
+backend.roster contains the roster handling code
+events All kinds of events that can be used to be notified of jabber events
+images The standard Jeti images and some roster icon handling code
+jabber The core jabber packages, including the Backend class 
+       to communicate with the rest of Jeti
+jabber.elements The different jabber stanzas as Java classes
+jabber.handlers The Sax xml handlers for the Jabber stanzas
+plugins The different plugins and plugin management
+ui The core user interface
+ui.login The login interface
+ui.models Some Swing models
+util Some utilities, including a non model popup,the I18N class and the Preferences
+
+License
+
+Jeti is licensed under the GPL, which means that you can modify it
+if you make it open source too. If you make some plugins other people could
+use it would be nice if you send it to me so I can add it to the jeti website.
Index: 3thparty/jmessenger/src/com/swabunga/spell/event/SpellCheckEvent.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/event/SpellCheckEvent.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/event/SpellCheckEvent.java	(revision 3952)
@@ -0,0 +1,69 @@
+package com.swabunga.spell.event;
+
+import java.util.List;
+
+/**
+ * This event is fired off by the SpellChecker and is passed to the
+ * registered SpellCheckListeners
+ * <p/>
+ * AFAIK we will only require one implementation of the SpellCheckEvent
+ * (BasicSpellCheckEvent) but I have defnied this interface just in case. The
+ * BasicSpellCheckEvent implementation is currently package private.
+ * <p/>
+ *
+ * @author Jason Height (jheight@chariot.net.au)
+ */
+public interface SpellCheckEvent {
+  /** Field indicating that the incorrect word should be ignored*/
+  public static final short IGNORE = 0;
+  /** Field indicating that the incorrect word should be ignored forever*/
+  public static final short IGNOREALL = 1;
+  /** Field indicating that the incorrect word should be replaced*/
+  public static final short REPLACE = 2;
+  /** Field indicating that the incorrect word should be replaced always*/
+  public static final short REPLACEALL = 3;
+  /** Field indicating that the incorrect word should be added to the dictionary*/
+  public static final short ADDTODICT = 4;
+  /** Field indicating that the spell checking should be terminated*/
+  public static final short CANCEL = 5;
+  /** Initial case for the action */
+  public static final short INITIAL = -1;
+
+  /** Returns the list of suggested Word objects*/
+  public List getSuggestions();
+
+  /** Returns the currently misspelt word*/
+  public String getInvalidWord();
+
+  /** Returns the context in which the misspelt word is used*/
+  public String getWordContext();
+
+  /** Returns the start position of the misspelt word in the context*/
+  public int getWordContextPosition();
+
+  public short getAction();
+
+  public String getReplaceWord();
+
+  /** Set the action to replace the currently misspelt word with the new word
+   *  @param newWord The word to replace the currently misspelt word
+   *  @param replaceAll If set to true, the SpellChecker will replace all
+   *  further occurances of the misspelt word without firing a SpellCheckEvent.
+   */
+  public void replaceWord(String newWord, boolean replaceAll);
+
+  /** Set the action it ignore the currently misspelt word.
+   *  @param ignoreAll If set to true, the SpellChecker will replace all
+   *  further occurances of the misspelt word without firing a SpellCheckEvent.
+   */
+  public void ignoreWord(boolean ignoreAll);
+
+  /** Set the action to add a new word into the dictionary. This will also replace the
+   *  currently misspelt word.
+   */
+  public void addToDictionary(String newWord);
+
+  /** Set the action to terminate processing of the spellchecker.
+   */
+  public void cancel();
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/event/BasicSpellCheckEvent.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/event/BasicSpellCheckEvent.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/event/BasicSpellCheckEvent.java	(revision 3952)
@@ -0,0 +1,111 @@
+package com.swabunga.spell.event;
+
+import java.util.List;
+
+/** This event is fired off by the SpellChecker and is passed to the
+ *  registered SpellCheckListeners
+ *
+ * @author Jason Height (jheight@chariot.net.au)
+ * 
+ */
+public class BasicSpellCheckEvent implements SpellCheckEvent {
+
+  /**The list holding the suggested Word objects for the misspelt word*/
+  private List suggestions;
+  /**The misspelt word*/
+  private String invalidWord;
+  /**The action to be done when the event returns*/
+  private short action = INITIAL;
+  /**Contains the word to be replaced if the action is REPLACE or REPLACEALL*/
+  private String replaceWord = null;
+
+  //private String context;
+  private int startPosition;
+
+
+  /**Consructs the SpellCheckEvent
+   * @param invalidWord The word that is misspelt
+   * @param suggestions A list of Word objects that are suggested to replace the currently mispelt word
+   * @param tokenizer The reference to the tokenizer that caused this
+   * event to fire.
+   */
+  public BasicSpellCheckEvent(String invalidWord, List suggestions, int startPos) {
+    this.invalidWord = invalidWord;
+    this.suggestions = suggestions;
+    //this.context = tokenizer.getContext();
+    //this.startPosition = tokenizer.getCurrentWordPosition();
+    this.startPosition = startPos;
+  }
+
+  /** Returns the list of suggested Word objects*/
+  public List getSuggestions() {
+    return suggestions;
+  }
+
+  /** Returns the currently misspelt word*/
+  public String getInvalidWord() {
+    return invalidWord;
+  }
+
+  public String getWordContext() {
+    //JMH TBD
+    return null;
+  }
+
+  /** Returns the start position of the misspelt word in the context*/
+  public int getWordContextPosition() {
+    return startPosition;
+  }
+
+  public short getAction() {
+    return action;
+  }
+
+  public String getReplaceWord() {
+    return replaceWord;
+  }
+
+  /** Set the action to replace the currently misspelt word with the new word
+   *  @param String newWord The word to replace the currently misspelt word
+   *  @param boolean replaceAll If set to true, the SpellChecker will replace all
+   *  further occurances of the misspelt word without firing a SpellCheckEvent.
+   */
+  public void replaceWord(String newWord, boolean replaceAll) {
+    if (action != INITIAL)
+      throw new IllegalStateException("The action can can only be set once");
+    if (replaceAll)
+      action = REPLACEALL;
+    else
+      action = REPLACE;
+    replaceWord = newWord;
+  }
+
+  /** Set the action it ignore the currently misspelt word.
+   *  @param boolean ignoreAll If set to true, the SpellChecker will replace all
+   *  further occurances of the misspelt word without firing a SpellCheckEvent.
+   */
+  public void ignoreWord(boolean ignoreAll) {
+    if (action != INITIAL)
+      throw new IllegalStateException("The action can can only be set once");
+    if (ignoreAll)
+      action = IGNOREALL;
+    else
+      action = IGNORE;
+  }
+
+  /** Set the action to add a new word into the dictionary. This will also replace the
+   *  currently misspelt word.
+   */
+  public void addToDictionary(String newWord) {
+    if (action != INITIAL)
+      throw new IllegalStateException("The action can can only be set once");
+    action = ADDTODICT;
+    replaceWord = newWord;
+  }
+
+  public void cancel() {
+    if (action != INITIAL)
+      throw new IllegalStateException("The action can can only be set once");
+    action = CANCEL;
+  }
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/event/SpellCheckListener.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/event/SpellCheckListener.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/event/SpellCheckListener.java	(revision 3952)
@@ -0,0 +1,12 @@
+package com.swabunga.spell.event;
+
+import java.util.EventListener;
+
+/**
+ * This is the event based listener interface.
+ *
+ * @author Jason Height (jheight@chariot.net.au)
+ */
+public interface SpellCheckListener extends EventListener {
+  public void spellingError(SpellCheckEvent event);
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/event/SpellChecker.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/event/SpellChecker.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/event/SpellChecker.java	(revision 3952)
@@ -0,0 +1,376 @@
+package com.swabunga.spell.event;
+
+import com.swabunga.spell.engine.Configuration;
+import com.swabunga.spell.engine.SpellDictionary;
+import com.swabunga.spell.engine.SpellDictionaryHashMap;
+import com.swabunga.spell.engine.Word;
+import com.swabunga.util.VectorUtility;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+
+
+/**
+ * This is the main class for spell checking (using the new event based spell
+ *  checking).
+ *
+ * @author     Jason Height (jheight@chariot.net.au)
+ * 19 June 2002
+ */
+public class SpellChecker {
+  /** Flag indicating that the Spell Check completed without any errors present*/
+  public static final int SPELLCHECK_OK = -1;
+  /** Flag indicating that the Spell Check completed due to user cancellation*/
+  public static final int SPELLCHECK_CANCEL = -2;
+
+  private Vector eventListeners = new Vector();
+  private Vector dictionaries = new Vector();
+//  private SpellDictionary userdictionary;
+
+  private Configuration config = Configuration.getConfiguration();
+
+  /**This variable holds all of the words that are to be always ignored */
+//  private Vector ignoredWords = new Vector();
+//  private Hashtable autoReplaceWords = new Hashtable();
+
+  /**
+   * Constructs the SpellChecker.
+   */
+  public SpellChecker() {
+   // try {
+ //     userdictionary = new SpellDictionaryHashMap();
+   // } catch (IOException e) {
+  //    throw new RuntimeException("this exception should never happen because we are using null phonetic file");
+  //  }
+  }
+
+  /**
+   * Constructs the SpellChecker. The default threshold is used
+   *
+   * @param  dictionary  Description of the Parameter
+   */
+  public SpellChecker(SpellDictionary dictionary) {
+    this();
+    addDictionary(dictionary);
+  }
+
+
+  /**
+   * Constructs the SpellChecker with a threshold
+   *
+   * @param  dictionary  Description of the Parameter
+   * @param  threshold   Description of the Parameter
+   */
+  public SpellChecker(SpellDictionary dictionary, int threshold) {
+    this(dictionary);
+    config.setInteger(Configuration.SPELL_THRESHOLD, threshold);
+  }
+
+  public void addDictionary(SpellDictionary dictionary) {
+    if (dictionary == null) {
+      throw new IllegalArgumentException("dictionary must be non-null");
+    }
+    this.dictionaries.addElement(dictionary);
+  }
+  
+  /**
+   *Adds a SpellCheckListener
+   *
+   * @param  listener  The feature to be added to the SpellCheckListener attribute
+   */
+  public void addSpellCheckListener(SpellCheckListener listener) {
+    eventListeners.addElement(listener);
+  }
+
+
+  /**
+   *Removes a SpellCheckListener
+   *
+   * @param  listener  Description of the Parameter
+   */
+  public void removeSpellCheckListener(SpellCheckListener listener) {
+    eventListeners.removeElement(listener);
+  }
+
+  /*
+   * Set user dictionary (used when a word is added)
+   *
+  public void setUserDictionary(SpellDictionary dictionary) {
+    userdictionary = dictionary;
+  }
+
+  /*
+   *
+   * @return Current Configuration
+   *
+  public Configuration getConfiguration() {
+    return config;
+  }
+
+  /**
+   * Fires off a spell check event to the listeners.
+   *
+   * @param  event  Description of the Parameter
+   */
+  protected void fireSpellCheckEvent(SpellCheckEvent event) {
+    for (int i = eventListeners.size() - 1; i >= 0; i--) {
+      ((SpellCheckListener) eventListeners.elementAt(i)).spellingError(event);
+    }
+  }
+
+
+  /*
+   * This method clears the words that are currently being remembered as
+   *  Ignore All words and Replace All words.
+   *
+  public void reset() {
+    ignoredWords = new Vector();
+    autoReplaceWords = new Hashtable();
+  }
+*/
+
+  /**
+   * Checks the text string.
+   *  <p>
+   *  Returns the corrected string.
+   *
+   * @param  text   Description of the Parameter
+   * @return        Description of the Return Value
+   * @deprecated    use checkSpelling(WordTokenizer)
+   */
+  /*
+  public String checkString(String text) {
+    StringWordTokenizer tokens = new StringWordTokenizer(text);
+    checkSpelling(tokens);
+    return tokens.getContext();
+  }
+*/
+
+  /*
+   * Returns true iff this word contains a digit.
+   *
+   * @param  word  Description of the Parameter
+   * @return       The digitWord value
+   */
+  private final static boolean isDigitWord(String word) {
+    for (int i = word.length() - 1; i >= 0; i--) {
+      if (Character.isDigit(word.charAt(i))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+  /**
+   * Returns true iff this word looks like an internet address.
+   *
+   * One limitation is that this method cannot currently recognise email
+   * addresses. Since the 'word' that is passed in may in fact contain
+   * the rest of the document to be checked, it is not (yet!) a good
+   * idea to scan for the @ character.
+   *
+   * @param  word  Description of the Parameter
+   * @return       The iNETWord value
+   */
+    public final static boolean isINETWord(String word) {
+        String lowerCaseWord = word.toLowerCase();
+        return lowerCaseWord.startsWith("http://") ||
+              lowerCaseWord.startsWith("www.") ||
+              lowerCaseWord.startsWith("ftp://") ||
+              lowerCaseWord.startsWith("https://") ||
+              lowerCaseWord.startsWith("ftps://");
+  }
+
+
+  /**
+   * Returns true iif this word contains all upper case characters
+   *
+   * @param  word  Description of the Parameter
+   * @return       The upperCaseWord value
+   */
+  private final static boolean isUpperCaseWord(String word) {
+    for (int i = word.length() - 1; i >= 0; i--) {
+      if (Character.isLowerCase(word.charAt(i))) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+
+  /**
+   * Returns true iif this word contains mixed case characters
+   *
+   * @param  word  Description of the Parameter
+   * @param startsSentence True if this word is at the start of a sentence
+   * @return       The mixedCaseWord value
+   */
+  private final static boolean isMixedCaseWord(String word, boolean startsSentence) {
+    int strLen = word.length();
+    boolean isUpper = Character.isUpperCase(word.charAt(0));
+    //Ignore the first character if this word starts the sentence and the first
+    //character was upper cased, since this is normal behaviour
+    if ((startsSentence) && isUpper && (strLen > 1))
+      isUpper = Character.isUpperCase(word.charAt(1));
+    if (isUpper) {
+      for (int i = word.length() - 1; i > 0; i--) {
+        if (Character.isLowerCase(word.charAt(i))) {
+          return true;
+        }
+      }
+    } else {
+      for (int i = word.length() - 1; i > 0; i--) {
+        if (Character.isUpperCase(word.charAt(i))) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+
+  /*
+   * This method will fire the spell check event and then handle the event
+   *  action that has been selected by the user.
+   *
+   * @param  tokenizer        Description of the Parameter
+   * @param  event            Description of the Parameter
+   * @return                  Returns true if the event action is to cancel the current spell checking, false if the spell checking should continue
+   */
+  protected boolean fireAndHandleEvent(WordTokenizer tokenizer, SpellCheckEvent event) {
+    fireSpellCheckEvent(event);
+    /*
+    String word = event.getInvalidWord();
+    //Work out what to do in response to the event.
+    switch (event.getAction()) {
+      case SpellCheckEvent.INITIAL:
+        break;
+      case SpellCheckEvent.IGNORE:
+        break;
+      case SpellCheckEvent.IGNOREALL:
+        if (!ignoredWords.contains(word)) {
+          ignoredWords.addElement(word);
+        }
+        break;
+      case SpellCheckEvent.REPLACE:
+        tokenizer.replaceWord(event.getReplaceWord());
+        break;
+      case SpellCheckEvent.REPLACEALL:
+        String replaceAllWord = event.getReplaceWord();
+        if (!autoReplaceWords.containsKey(word)) {
+          autoReplaceWords.put(word, replaceAllWord);
+        }
+        tokenizer.replaceWord(replaceAllWord);
+        break;
+      case SpellCheckEvent.ADDTODICT:
+        String addWord = event.getReplaceWord();
+        if (!addWord.equals(word))
+          tokenizer.replaceWord(addWord);
+        userdictionary.addWord(addWord);
+        break;
+      case SpellCheckEvent.CANCEL:
+        return true;
+      default:
+        throw new IllegalArgumentException("Unhandled case.");
+    }
+    */
+    return false;
+  }
+  
+
+  
+  private boolean isCorrect(String word) {
+   // if (userdictionary.isCorrect(word)) return true;
+    for (Enumeration e = dictionaries.elements(); e.hasMoreElements();) {
+      SpellDictionary dictionary = (SpellDictionary) e.nextElement();
+      if (dictionary.isCorrect(word)) return true;
+    }
+    return false;
+  }
+  
+
+  public List getSuggestions(String word, int threshold) {
+    List suggestions = new Vector();//userdictionary.getSuggestions(word, threshold);
+    for (Enumeration e = dictionaries.elements(); e.hasMoreElements();) {
+      SpellDictionary dictionary = (SpellDictionary) e.nextElement();
+      VectorUtility.addAll(suggestions, dictionary.getSuggestions(word, threshold), false);
+    }
+    return suggestions;
+  }
+
+  
+  /**
+   * This method is called to check the spelling of the words that are returned
+   * by the WordTokenizer.
+   * <p>For each invalid word the action listeners will be informed with a new SpellCheckEvent</p>
+   *
+   * @param  tokenizer  Description of the Parameter
+   * @return Either SPELLCHECK_OK, SPELLCHECK_CANCEL or the number of errors found. The number of errors are those that
+   * are found BEFORE any corrections are made.
+   */
+  public final int checkSpelling(WordTokenizer tokenizer) {
+    int errors = 0;
+    boolean terminated = false;
+    //Keep track of the previous word
+//    String previousWord = null;
+    while (tokenizer.hasMoreWords() && !terminated) {
+      String word = tokenizer.nextWord();
+      //Check the spelling of the word
+      if (!isCorrect(word)) {
+ 		if ((config.getBoolean(Configuration.SPELL_IGNOREMIXEDCASE) && isMixedCaseWord(word, tokenizer.isNewSentence())) ||
+            (config.getBoolean(Configuration.SPELL_IGNOREUPPERCASE) && isUpperCaseWord(word)) ||
+            (config.getBoolean(Configuration.SPELL_IGNOREDIGITWORDS) && isDigitWord(word)) ||
+            (config.getBoolean(Configuration.SPELL_IGNOREINTERNETADDRESSES) && isINETWord(word))) {
+          //Null event. Since we are ignoring this word due
+          //to one of the above cases.
+        } else {
+          //We cant ignore this misspelt word
+          //For this invalid word are we ignoring the misspelling?
+      //    if (!ignoredWords.contains(word)) {
+            errors++;
+            //Is this word being automagically replaced
+      //      if (autoReplaceWords.containsKey(word)) {
+       //       tokenizer.replaceWord((String) autoReplaceWords.get(word));
+       //     } else {
+              //JMH Need to somehow capitalise the suggestions if
+              //ignoreSentenceCapitalisation is not set to true
+              //Fire the event.
+              SpellCheckEvent event = new BasicSpellCheckEvent(word, null, tokenizer.getCurrentWordPosition());
+              terminated = fireAndHandleEvent(tokenizer, event);
+            }
+        //  }
+       // }
+      } else {
+        //This is a correctly spelt word. However perform some extra checks
+        /*
+         *  JMH TBD          //Check for multiple words
+         *  if (!ignoreMultipleWords &&) {
+         *  }
+         */
+        //Check for capitalisation (not interesting Eric) 
+//        if ((!config.getBoolean(Configuration.SPELL_IGNORESENTENCECAPITALIZATION)) && (tokenizer.isNewSentence()) && (Character.isLowerCase(word.charAt(0)))) {
+//          errors++;
+//          StringBuffer buf = new StringBuffer(word);
+//          buf.setCharAt(0, Character.toUpperCase(word.charAt(0)));
+//          Vector suggestion = new Vector();
+//          suggestion.addElement(new Word(buf.toString(), 0));
+//          SpellCheckEvent event = new BasicSpellCheckEvent(word, suggestion, tokenizer);
+//          terminated = fireAndHandleEvent(tokenizer, event);
+//        }
+      }
+    }
+    if (terminated)
+      return SPELLCHECK_CANCEL;
+    else if (errors == 0)
+      return SPELLCHECK_OK;
+    else
+      return errors;
+  }
+  
+}
+
+
Index: 3thparty/jmessenger/src/com/swabunga/spell/event/DocumentWordTokenizer.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/event/DocumentWordTokenizer.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/event/DocumentWordTokenizer.java	(revision 3952)
@@ -0,0 +1,183 @@
+package com.swabunga.spell.event;
+
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Segment;
+import java.text.BreakIterator;
+
+
+/** This class tokenizes a swing document model. It also allows for the
+ *  document model to be changed when corrections occur.
+ *
+ * @author Jason Height (jheight@chariot.net.au)
+ */
+public class DocumentWordTokenizer implements WordTokenizer {
+  /** Holds the start character position of the current word*/
+  private int currentWordPos = 0;
+  /** Holds the end character position of the current word*/
+  private int currentWordEnd = 0;
+  /** Holds the start character position of the next word*/
+  private int nextWordPos = -1;
+  /** The actual text that is being tokenized*/
+  private Document document;
+  /** The character iterator over the document*/
+  private Segment text;
+  /** The cumulative word count that have been processed*/
+  private int wordCount = 0;
+  /** Flag indicating if there are any more tokens (words) left*/
+  private boolean moreTokens = true;
+  /** Is this a special case where the currentWordStart, currntWordEnd and
+   *  nextWordPos have already been calculated. (see nextWord)
+   */
+  private boolean first = true;
+  private BreakIterator sentenceIterator;
+  private boolean startsSentence = true;
+
+  public DocumentWordTokenizer(Document document) {
+    this.document = document;
+    //Create a text segment over the etire document
+    text = new Segment();
+    sentenceIterator = BreakIterator.getSentenceInstance();
+    try {
+      document.getText(0, document.getLength(), text);
+      sentenceIterator.setText(text);
+      currentWordPos = getNextWordStart(text, 0);
+      //If the current word pos is -1 then the string was all white space
+      if (currentWordPos != -1) {
+        currentWordEnd = getNextWordEnd(text, currentWordPos);
+        nextWordPos = getNextWordStart(text, currentWordEnd);
+      } else {
+        moreTokens = false;
+      }
+    } catch (BadLocationException ex) {
+      moreTokens = false;
+    }
+  }
+
+  /** This helper method will return the start character of the next
+   * word in the buffer from the start position
+   */
+  private static int getNextWordStart(Segment text, int startPos) {
+    if (startPos <= text.getEndIndex())
+      for (char ch = text.setIndex(startPos); ch != Segment.DONE; ch = text.next()) {
+        if (Character.isLetterOrDigit(ch)) {
+          return text.getIndex();
+        }
+      }
+    return -1;
+  }
+
+  /** This helper method will return the end of the next word in the buffer.
+   *
+   */
+  private static int getNextWordEnd(Segment text, int startPos) {
+    for (char ch = text.setIndex(startPos); ch != Segment.DONE; ch = text.next()) {
+      if (!Character.isLetterOrDigit(ch)) {
+        if (ch == '-' || ch == '\'') { // handle ' and - inside words
+          char ch2 = text.next();
+          text.previous();
+          if (ch2 != Segment.DONE && Character.isLetterOrDigit(ch2))
+            continue;
+        }
+        return text.getIndex();
+      }
+    }
+    return text.getEndIndex();
+  }
+
+  /** Returns true if there are more words that can be processed in the string
+   *
+   */
+  public boolean hasMoreWords() {
+    return moreTokens;
+  }
+
+  /** Returns the current character position in the text
+   *
+   */
+  public int getCurrentWordPosition() {
+    return currentWordPos;
+  }
+
+  /** Returns the current end word position in the text
+   *
+   */
+  public int getCurrentWordEnd() {
+    return currentWordEnd;
+  }
+
+  /** Returns the next word in the text
+   *
+   */
+  public String nextWord() {
+    if (!first) {
+      currentWordPos = nextWordPos;
+      currentWordEnd = getNextWordEnd(text, currentWordPos);
+      nextWordPos = getNextWordStart(text, currentWordEnd + 1);
+    }
+    int current = sentenceIterator.current();
+    if (current == currentWordPos)
+      startsSentence = true;
+    else {
+      startsSentence = false;
+      if (currentWordEnd > current)
+        sentenceIterator.next();
+    }
+    //The nextWordPos has already been populated
+    String word = null;
+    try {
+      word = document.getText(currentWordPos, currentWordEnd - currentWordPos);
+    } catch (BadLocationException ex) {
+      moreTokens = false;
+    }
+    wordCount++;
+    first = false;
+    if (nextWordPos == -1)
+      moreTokens = false;
+    return word;
+  }
+
+  /** Returns the current number of words that have been processed
+   *
+   */
+  public int getCurrentWordCount() {
+    return wordCount;
+  }
+
+  /** Replaces the current word token*/
+  public void replaceWord(String newWord) {
+    if (currentWordPos != -1) {
+      try {
+        document.remove(currentWordPos, currentWordEnd - currentWordPos);
+        document.insertString(currentWordPos, newWord, null);
+        //Need to reset the segment
+        document.getText(0, document.getLength(), text);
+      } catch (BadLocationException ex) {
+        throw new RuntimeException(ex.getMessage());
+      }
+      //Position after the newly replaced word(s)
+      first = true;
+      currentWordPos = getNextWordStart(text, currentWordPos + newWord.length());
+      if (currentWordPos != -1) {
+        currentWordEnd = getNextWordEnd(text, currentWordPos);
+        nextWordPos = getNextWordStart(text, currentWordEnd);
+        sentenceIterator.setText(text);
+        sentenceIterator.following(currentWordPos);
+      } else
+        moreTokens = false;
+    }
+  }
+
+  /** Returns the current text that is being tokenized (includes any changes
+   *  that have been made)
+   */
+  public String getContext() {
+    return text.toString();
+  }
+
+  /** Returns true if the current word is at the start of a sentence*/
+  public boolean isNewSentence() {
+    return startsSentence;
+  }
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/event/WordTokenizer.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/event/WordTokenizer.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/event/WordTokenizer.java	(revision 3952)
@@ -0,0 +1,86 @@
+package com.swabunga.spell.event;
+
+/**
+ * <p>An interface for objects which take a text-based media as input, and iterate through
+ * the words in the text stored in that media. Examples of such media could be Strings,
+ * Documents, Files, TextComponents etc.
+ * </P>
+ *
+ * <P>
+ * When the object is instantiated, and before the first call to <CODE>next()</CODE> is made,
+ * the following methods should throw a <CODE>WordNotFoundException</CODE>:<br>
+ * <CODE>getCurrentWordEnd()</CODE>, <CODE>getCurrentWordPosition()</CODE>,
+ *  <CODE>isNewSentence()</CODE> and <CODE>replaceWord()</CODE>.
+ * </P>
+ *
+ * <P>A call to <CODE>next()</CODE> when <CODE>hasMoreWords()</CODE> returns false
+ * should throw a <CODE>WordNotFoundException</CODE>.</P>
+ * @author Jason Height (jheight@chariot.net.au)
+ */
+public interface WordTokenizer {
+
+  //~ Methods .................................................................
+
+  /**
+   * Returns the context text that is being tokenized (should include any
+   * changes that have been made).
+   * @return the text being searched.
+   */
+  public String getContext();
+
+  /**
+   * Returns the number of word tokens that have been processed thus far
+   * @return the number of words found so far.
+   */
+  public int getCurrentWordCount();
+
+  /**
+   * Returns an index representing the end location of the current word in the text.
+   * @return index of the end of the current word in the text.
+   * @throws WordNotFoundException current word has not yet been set.
+   */
+  public int getCurrentWordEnd();
+
+  /**
+   * Returns an index representing the start location of the current word in the text.
+   * @return index of the start of the current word in the text.
+   * @throws WordNotFoundException current word has not yet been set.
+   */
+  public int getCurrentWordPosition();
+
+  /**
+   * Returns true if the current word is at the start of a sentence
+   * @return true if the current word starts a sentence.
+   * @throws WordNotFoundException current word has not yet been set.
+   */
+  public boolean isNewSentence();
+
+  /**
+   * Returns true if there are more words left
+   * @return true if more words can be found in the text.
+   */
+  public boolean hasMoreWords();
+
+  /**
+   * This returns the next word in the iteration. Note that any implementation should return
+   * the current word, and then replace the current word with the next word found in the
+   * input text (if one exists).
+   * @return the next word in the iteration.
+   * @throws WordNotFoundException search string contains no more words.
+   */
+  public String nextWord();
+
+  /**
+   * Replaces the current word token
+   *
+   * <p/>
+   * When a word is replaced care should be taken that the WordTokenizer
+   * repositions itself such that the words that were added aren't rechecked.
+   * Of course this is not mandatory, maybe there is a case when an
+   * application doesnt need to do this.
+   * <p/>
+   * @param newWord the string which should replace the current word.
+   * @throws WordNotFoundException current word has not yet been set.
+   */
+  public void replaceWord(String newWord);
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/EditDistance.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/EditDistance.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/EditDistance.java	(revision 3952)
@@ -0,0 +1,181 @@
+package com.swabunga.spell.engine;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ * This class is based on Levenshtein Distance algorithms, and it calculates how similar two words are.
+ * If the words are identitical, then the distance is 0. The more that the words have in common, the lower the distance value.
+ * The distance value is based on how many operations it takes to get from one word to the other. Possible operations are
+ * swapping characters, adding a character, deleting a character, and substituting a character.
+ * The resulting distance is the sum of these operations weighted by their cost, which can be set in the Configuration object.
+ * When there are multiple ways to convert one word into the other, the lowest cost distance is returned.
+ * <br/>
+ * Another way to think about this: what are the cheapest operations that would have to be done on the "original" word to end up
+ * with the "similar" word? Each operation has a cost, and these are added up to get the distance.
+ * <br/>
+ *
+ * @see com.swabunga.spell.engine.Configuration#COST_REMOVE_CHAR
+ * @see com.swabunga.spell.engine.Configuration#COST_INSERT_CHAR
+ * @see com.swabunga.spell.engine.Configuration#COST_SUBST_CHARS
+ * @see com.swabunga.spell.engine.Configuration#COST_SWAP_CHARS
+ *
+ */
+
+public class EditDistance {
+
+  /**
+   * JMH Again, there is no need to have a global class matrix variable
+   *  in this class. I have removed it and made the getDistance static final
+   *
+   * DMV: I refactored this method to make it more efficient, more readable, and simpler.
+   * I also fixed a bug with how the distance was being calculated. You could get wrong
+   * distances if you compared ("abc" to "ab") depending on what you had setup your
+   * COST_REMOVE_CHAR and EDIT_INSERTION_COST values to - that is now fixed.
+   *
+   * WRS: I added a distance for case comparison, so a misspelling of "i" would be closer to "I" than
+   * to "a".
+   */
+
+  public static Configuration config = Configuration.getConfiguration();
+
+  public static final int getDistance(String word, String similar) {
+
+    //get the weights for each possible operation
+    final int costOfDeletingSourceCharacter = config.getInteger(Configuration.COST_REMOVE_CHAR);
+    final int costOfInsertingSourceCharacter = config.getInteger(Configuration.COST_INSERT_CHAR);
+    final int costOfSubstitutingLetters = config.getInteger(Configuration.COST_SUBST_CHARS);
+    final int costOfSwappingLetters = config.getInteger(Configuration.COST_SWAP_CHARS);
+    final int costOfChangingCase = config.getInteger(Configuration.COST_CHANGE_CASE);
+
+    int a_size = word.length() + 1;
+    int b_size = similar.length() + 1;
+    int[][] matrix = new int[a_size][b_size];
+    matrix[0][0] = 0;
+
+    for (int i = 1; i != a_size; ++i)
+      matrix[i][0] = matrix[i - 1][0] + costOfInsertingSourceCharacter; //initialize the first column
+
+    for (int j = 1; j != b_size; ++j)
+      matrix[0][j] = matrix[0][j - 1] + costOfDeletingSourceCharacter; //initalize the first row
+
+    word = " " + word;
+    similar = " " + similar;
+
+    for (int i = 1; i != a_size; ++i) {
+      char sourceChar = word.charAt(i);
+      for (int j = 1; j != b_size; ++j) {
+
+        char otherChar = similar.charAt(j);
+        if (sourceChar == otherChar) {
+          matrix[i][j] = matrix[i - 1][j - 1]; //no change required, so just carry the current cost up
+          continue;
+        }
+
+        int costOfSubst = costOfSubstitutingLetters + matrix[i - 1][j - 1];
+        //if needed, add up the cost of doing a swap
+        int costOfSwap = Integer.MAX_VALUE;
+        boolean isSwap = (i != 1) && (j != 1) && sourceChar == similar.charAt(j - 1) && word.charAt(i - 1) == otherChar;
+        if (isSwap)
+          costOfSwap = costOfSwappingLetters + matrix[i - 2][j - 2];
+
+        int costOfDelete = costOfDeletingSourceCharacter + matrix[i][j - 1];
+        int costOfInsertion = costOfInsertingSourceCharacter + matrix[i - 1][j];
+
+        int costOfCaseChange = Integer.MAX_VALUE;
+        String strSrcChar = "" + sourceChar;
+        String strOtherChar = "" + otherChar;
+
+        if (strSrcChar.compareToIgnoreCase(strOtherChar) == 0)
+          costOfCaseChange = costOfChangingCase + matrix[i - 1][j - 1];
+
+        matrix[i][j] = minimum(costOfSubst, costOfSwap, costOfDelete, costOfInsertion, costOfCaseChange);
+      }
+    }
+    int cost = matrix[a_size - 1][b_size - 1];
+
+    if (false)
+      System.out.println(dumpMatrix(word, similar, matrix));
+
+    return cost;
+  }
+
+  /**
+   * For debugging, this creates a string that represents the matrix. To read the matrix, look at any square. That is the cost to get from
+   * the partial letters along the top to the partial letters along the side.
+   * @param src - the source string that the matrix columns are based on
+   * @param dest - the dest string that the matrix rows are based on
+   * @param matrix - a two dimensional array of costs (distances)
+   * @return String
+   */
+  static private String dumpMatrix(String src, String dest, int matrix[][]) {
+    StringBuffer s = new StringBuffer("");
+
+    int cols = matrix.length;
+    int rows = matrix[0].length;
+
+    for (int i = 0; i < cols + 1; i++) {
+      for (int j = 0; j < rows + 1; j++) {
+        if (i == 0 && j == 0) {
+          s.append("\n ");
+          continue;
+
+        }
+        if (i == 0) {
+          s.append("|   ");
+          s.append(dest.charAt(j - 1));
+          continue;
+        }
+        if (j == 0) {
+          s.append(src.charAt(i - 1));
+          continue;
+        }
+        String num = Integer.toString(matrix[i - 1][j - 1]);
+        int padding = 4 - num.length();
+        s.append("|");
+        for (int k = 0; k < padding; k++)
+          s.append(' ');
+        s.append(num);
+      }
+      s.append('\n');
+    }
+    return s.toString();
+
+  }
+
+
+  static private int minimum(int a, int b, int c, int d, int e) {
+    int mi = a;
+    if (b < mi)
+      mi = b;
+    if (c < mi)
+      mi = c;
+    if (d < mi)
+      mi = d;
+    if (e < mi)
+      mi = e;
+
+    return mi;
+  }
+
+
+  public static void main(String[] args) throws Exception {
+    BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
+
+    while (true) {
+
+      String input1 = stdin.readLine();
+      if (input1 == null || input1.length() == 0)
+        break;
+
+      String input2 = stdin.readLine();
+      if (input2 == null || input2.length() == 0)
+        break;
+
+      System.out.println(EditDistance.getDistance(input1, input2));
+    }
+    System.out.println("done");
+  }
+}
+
+
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/DoubleMeta.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/DoubleMeta.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/DoubleMeta.java	(revision 3952)
@@ -0,0 +1,692 @@
+package com.swabunga.spell.engine;
+
+
+/**
+ * A phonetic encoding algorithm that takes an English word and computes a phonetic version of it. This
+ * allows for phonetic matches in a spell checker. This class is a port of the C++ DoubleMetaphone() class,
+ * which was intended to return two possible phonetic translations for certain words, although the Java version
+ * only seems to be concerned with one, making the "double" part erroneous.
+ * <br>
+ * source code for the original C++ can be found
+ * here: <a href="http://aspell.sourceforge.net/metaphone/"/>http://aspell.sourceforge.net/metaphone/</a>
+ * DoubleMetaphone does some processing, such as uppercasing, on the input string first to normalize it. Then, to
+ * create the key, the function traverses the input string in a while loop, sending sucessive characters into a giant
+ * switch statement. Before determining the appropriate pronunciation, the algorithm considers the context
+ * surrounding each character within the input string.
+ * <p>
+ * Things that were changed:
+ *   <br/>The alternate flag could be set to true but was never checked so why bother with it. REMOVED
+ *   <br/>Why was this class serializable?
+ *   <br/>The primary, in, length and last variables could be initialized and local to the
+ *   process method and references passed arround the appropriate methods. As such there are
+ *   no class variables and this class becomes firstly threadsafe and secondly could be static final.
+ *   <br/>The function call SlavoGermaic was called repeatedly in the process function, it is now only called once.
+ *
+ */
+public class DoubleMeta implements Transformator {
+
+  /**
+   * The replace list is used in the getSuggestions method.
+   * All of the letters in the misspelled word are replaced with the characters from
+   * this list to try and generate more suggestions, which implies l*n tries,
+   * if l is the size of the string, and n is the size of this list.
+   *
+   * In addition to that, each of these letters is added to the mispelled word.
+   */
+  private static char[] replaceList = {'A', 'B', 'X', 'S', 'K', 'J', 'T', 'F', 'H', 'L', 'M', 'N', 'P', 'R', '0'};
+
+
+  private static final String[] myList = {"GN", "KN", "PN", "WR", "PS", ""};
+  private static final String[] list1 = {"ACH", ""};
+  private static final String[] list2 = {"BACHER", "MACHER", ""};
+  private static final String[] list3 = {"CAESAR", ""};
+  private static final String[] list4 = {"CHIA", ""};
+  private static final String[] list5 = {"CH", ""};
+  private static final String[] list6 = {"CHAE", ""};
+  private static final String[] list7 = {"HARAC", "HARIS", ""};
+  private static final String[] list8 = {"HOR", "HYM", "HIA", "HEM", ""};
+  private static final String[] list9 = {"CHORE", ""};
+  private static final String[] list10 = {"VAN ", "VON ", ""};
+  private static final String[] list11 = {"SCH", ""};
+  private static final String[] list12 = {"ORCHES", "ARCHIT", "ORCHID", ""};
+  private static final String[] list13 = {"T", "S", ""};
+  private static final String[] list14 = {"A", "O", "U", "E", ""};
+  private static final String[] list15 = {"L", "R", "N", "M", "B", "H", "F", "V", "W", " ", ""};
+  private static final String[] list16 = {"MC", ""};
+  private static final String[] list17 = {"CZ", ""};
+  private static final String[] list18 = {"WICZ", ""};
+  private static final String[] list19 = {"CIA", ""};
+  private static final String[] list20 = {"CC", ""};
+  private static final String[] list21 = {"I", "E", "H", ""};
+  private static final String[] list22 = {"HU", ""};
+  private static final String[] list23 = {"UCCEE", "UCCES", ""};
+  private static final String[] list24 = {"CK", "CG", "CQ", ""};
+  private static final String[] list25 = {"CI", "CE", "CY", ""};
+// DMV: used by the orininal code which returned two phonetic code, but not the current code
+//    private static final String[] list26 = {
+//        "CIO", "CIE", "CIA", ""
+//    };
+  private static final String[] list27 = {" C", " Q", " G", ""};
+  private static final String[] list28 = {"C", "K", "Q", ""};
+  private static final String[] list29 = {"CE", "CI", ""};
+  private static final String[] list30 = {"DG", ""};
+  private static final String[] list31 = {"I", "E", "Y", ""};
+  private static final String[] list32 = {"DT", "DD", ""};
+  private static final String[] list33 = {"B", "H", "D", ""};
+  private static final String[] list34 = {"B", "H", "D", ""};
+  private static final String[] list35 = {"B", "H", ""};
+  private static final String[] list36 = {"C", "G", "L", "R", "T", ""};
+  private static final String[] list37 = {"EY", ""};
+  private static final String[] list38 = {"LI", ""};
+  private static final String[] list39 = {"ES", "EP", "EB", "EL", "EY", "IB", "IL", "IN", "IE", "EI", "ER", ""};
+  private static final String[] list40 = {"ER", ""};
+  private static final String[] list41 = {"DANGER", "RANGER", "MANGER", ""};
+  private static final String[] list42 = {"E", "I", ""};
+  private static final String[] list43 = {"RGY", "OGY", ""};
+  private static final String[] list44 = {"E", "I", "Y", ""};
+  private static final String[] list45 = {"AGGI", "OGGI", ""};
+  private static final String[] list46 = {"VAN ", "VON ", ""};
+  private static final String[] list47 = {"SCH", ""};
+  private static final String[] list48 = {"ET", ""};
+
+//  DMV: used by the orininal code which returned two phonetic code, but not the current code
+//    private static final String[] list49 = {
+//        "IER ", ""
+//    };
+  private static final String[] list50 = {"JOSE", ""};
+  private static final String[] list51 = {"SAN ", ""};
+  private static final String[] list52 = {"SAN ", ""};
+  private static final String[] list53 = {"JOSE", ""};
+  private static final String[] list54 = {"L", "T", "K", "S", "N", "M", "B", "Z", ""};
+  private static final String[] list55 = {"S", "K", "L", ""};
+  private static final String[] list56 = {"ILLO", "ILLA", "ALLE", ""};
+  private static final String[] list57 = {"AS", "OS", ""};
+  private static final String[] list58 = {"A", "O", ""};
+  private static final String[] list59 = {"ALLE", ""};
+  private static final String[] list60 = {"UMB", ""};
+  private static final String[] list61 = {"ER", ""};
+  private static final String[] list62 = {"P", "B", ""};
+  private static final String[] list63 = {"IE", ""};
+  private static final String[] list64 = {"ME", "MA", ""};
+  private static final String[] list65 = {"ISL", "YSL", ""};
+  private static final String[] list66 = {"SUGAR", ""};
+  private static final String[] list67 = {"SH", ""};
+  private static final String[] list68 = {"HEIM", "HOEK", "HOLM", "HOLZ", ""};
+  private static final String[] list69 = {"SIO", "SIA", ""};
+  private static final String[] list70 = {"SIAN", ""};
+  private static final String[] list71 = {"M", "N", "L", "W", ""};
+  private static final String[] list72 = {"Z", ""};
+  private static final String[] list73 = {"Z", ""};
+  private static final String[] list74 = {"SC", ""};
+  private static final String[] list75 = {"OO", "ER", "EN", "UY", "ED", "EM", ""};
+  private static final String[] list76 = {"ER", "EN", ""};
+  private static final String[] list77 = {"I", "E", "Y", ""};
+  private static final String[] list78 = {"AI", "OI", ""};
+  private static final String[] list79 = {"S", "Z", ""};
+  private static final String[] list80 = {"TION", ""};
+  private static final String[] list81 = {"TIA", "TCH", ""};
+  private static final String[] list82 = {"TH", ""};
+  private static final String[] list83 = {"TTH", ""};
+  private static final String[] list84 = {"OM", "AM", ""};
+  private static final String[] list85 = {"VAN ", "VON ", ""};
+  private static final String[] list86 = {"SCH", ""};
+  private static final String[] list87 = {"T", "D", ""};
+  private static final String[] list88 = {"WR", ""};
+  private static final String[] list89 = {"WH", ""};
+  private static final String[] list90 = {"EWSKI", "EWSKY", "OWSKI", "OWSKY", ""};
+  private static final String[] list91 = {"SCH", ""};
+  private static final String[] list92 = {"WICZ", "WITZ", ""};
+  private static final String[] list93 = {"IAU", "EAU", ""};
+  private static final String[] list94 = {"AU", "OU", ""};
+  private static final String[] list95 = {"C", "X", ""};
+
+//  DMV: used by the orininal code which returned two phonetic code, but not the current code
+//    private static final String[] list96 = {
+//        "ZO", "ZI", "ZA", ""
+//    };
+
+  /**
+   * put your documentation comment here
+   * @return
+   */
+  private final static boolean SlavoGermanic(String in) {
+    if ((in.indexOf("W") > -1) || (in.indexOf("K") > -1) || (in.indexOf("CZ") > -1) || (in.indexOf("WITZ") > -1))
+      return true;
+    return false;
+  }
+
+  /**
+   * put your documentation comment here
+   * @param main
+   */
+  private final static void MetaphAdd(StringBuffer primary, String main) {
+    if (main != null) {
+      primary.append(main);
+    }
+  }
+
+  private final static void MetaphAdd(StringBuffer primary, char main) {
+    primary.append(main);
+  }
+
+  /**
+   * put your documentation comment here
+   * @param at
+   * @return
+   */
+  private final static boolean isVowel(String in, int at, int length) {
+    if ((at < 0) || (at >= length))
+      return false;
+    char it = in.charAt(at);
+    if ((it == 'A') || (it == 'E') || (it == 'I') || (it == 'O') || (it == 'U') || (it == 'Y'))
+      return true;
+    return false;
+  }
+
+  /**
+   * put your documentation comment here
+   * @param string
+   * @param start
+   * @param length
+   * @param list
+   * @return
+   */
+  private final static boolean stringAt(String string, int start, int length, String[] list) {
+    if ((start < 0) || (start >= string.length()) || list.length == 0)
+      return false;
+    String substr = string.substring(start, start + length);
+    for (int i = 0; i < list.length; i++) {
+      if (list[i].equals(substr))
+        return true;
+    }
+    return false;
+  }
+
+  /**
+   * Take the given word, and return the best phonetic hash for it.
+   * Vowels are minimized as much as possible, and consenants
+   * that have similiar sounds are converted to the same consenant
+   * for example, 'v' and 'f' are both converted to 'f'
+   */
+  public final String transform(String word) {
+    StringBuffer primary = new StringBuffer(word.length() + 5);
+    String in = word.toUpperCase() + "     ";
+    int current = 0;
+    int length = in.length();
+    if (length < 1)
+      return "";
+    int last = length - 1;
+    boolean isSlavoGermaic = SlavoGermanic(in);
+    if (stringAt(in, 0, 2, myList))
+      current += 1;
+    if (in.charAt(0) == 'X') {
+      MetaphAdd(primary, 'S');
+      current += 1;
+    }
+    while (current < length) {
+      switch (in.charAt(current)) {
+        case 'A':
+        case 'E':
+        case 'I':
+        case 'O':
+        case 'U':
+        case 'Y':
+          if (current == 0)
+            MetaphAdd(primary, 'A');
+          current += 1;
+          break;
+        case 'B':
+          MetaphAdd(primary, 'P');
+          if (in.charAt(current + 1) == 'B')
+            current += 2;
+          else
+            current += 1;
+          break;
+        case '\u00C7':
+          MetaphAdd(primary, 'S');
+          current += 1;
+          break;
+        case 'C':
+          if ((current > 1) && !isVowel(in, current - 2, length) && stringAt(in, (current - 1), 3, list1) && (in.charAt(current + 2) != 'I') && (in.charAt(current + 2) != 'E') || stringAt(in, (current - 2), 6, list2)) {
+            MetaphAdd(primary, 'K');
+            current += 2;
+            break;
+          }
+          if ((current == 0) && stringAt(in, current, 6, list3)) {
+            MetaphAdd(primary, 'S');
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current, 4, list4)) {
+            MetaphAdd(primary, 'K');
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current, 2, list5)) {
+            if ((current > 0) && stringAt(in, current, 4, list6)) {
+              MetaphAdd(primary, 'K');
+              current += 2;
+              break;
+            }
+            if ((current == 0) && stringAt(in, (current + 1), 5, list7) || stringAt(in, current + 1, 3, list8) && !stringAt(in, 0, 5, list9)) {
+              MetaphAdd(primary, 'K');
+              current += 2;
+              break;
+            }
+            if (stringAt(in, 0, 4, list10) || stringAt(in, 0, 3, list11) || stringAt(in, current - 2, 6, list12) || stringAt(in, current + 2, 1, list13) || (stringAt(in, current - 1, 1, list14) || (current == 0)) && stringAt(in, current + 2, 1, list15)) {
+              MetaphAdd(primary, 'K');
+            } else {
+              if (current > 0) {
+                if (stringAt(in, 0, 2, list16))
+                  MetaphAdd(primary, 'K');
+                else
+                  MetaphAdd(primary, 'X');
+              } else {
+                MetaphAdd(primary, 'X');
+              }
+            }
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current, 2, list17) && !stringAt(in, current, 4, list18)) {
+            MetaphAdd(primary, 'S');
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current, 2, list19)) {
+            MetaphAdd(primary, 'X');
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current, 2, list20) && !((current == 1) && in.charAt(0) == 'M')) {
+            if (stringAt(in, current + 2, 1, list21) && !stringAt(in, current + 2, 2, list22)) {
+              if (((current == 1) && (in.charAt(current - 1) == 'A')) || stringAt(in, (current - 1), 5, list23))
+                MetaphAdd(primary, "KS");
+              else
+                MetaphAdd(primary, 'X');
+              current += 3;
+              break;
+            } else {
+              MetaphAdd(primary, 'K');
+              current += 2;
+              break;
+            }
+          }
+          if (stringAt(in, current, 2, list24)) {
+            MetaphAdd(primary, 'K');
+            current += 2;
+            break;
+          } else if (stringAt(in, current, 2, list25)) {
+            MetaphAdd(primary, 'S');
+            current += 2;
+            break;
+          }
+
+          MetaphAdd(primary, 'K');
+          if (stringAt(in, current + 1, 2, list27))
+            current += 3;
+          else if (stringAt(in, current + 1, 1, list28) && !stringAt(in, current + 1, 2, list29))
+            current += 2;
+          else
+            current += 1;
+          break;
+        case 'D':
+          if (stringAt(in, current, 2, list30)) {
+            if (stringAt(in, current + 2, 1, list31)) {
+              MetaphAdd(primary, 'J');
+              current += 3;
+              break;
+            } else {
+              MetaphAdd(primary, "TK");
+              current += 2;
+              break;
+            }
+          }
+          MetaphAdd(primary, 'T');
+          if (stringAt(in, current, 2, list32)) {
+            current += 2;
+          } else {
+            current += 1;
+          }
+          break;
+        case 'F':
+          if (in.charAt(current + 1) == 'F')
+            current += 2;
+          else
+            current += 1;
+          MetaphAdd(primary, 'F');
+          break;
+        case 'G':
+          if (in.charAt(current + 1) == 'H') {
+            if ((current > 0) && !isVowel(in, current - 1, length)) {
+              MetaphAdd(primary, 'K');
+              current += 2;
+              break;
+            }
+            if (current < 3) {
+              if (current == 0) {
+                if (in.charAt(current + 2) == 'I')
+                  MetaphAdd(primary, 'J');
+                else
+                  MetaphAdd(primary, 'K');
+                current += 2;
+                break;
+              }
+            }
+            if ((current > 1) && stringAt(in, current - 2, 1, list33) || ((current > 2) && stringAt(in, current - 3, 1, list34)) || ((current > 3) && stringAt(in, current - 4, 1, list35))) {
+              current += 2;
+              break;
+            } else {
+              if ((current > 2) && (in.charAt(current - 1) == 'U') && stringAt(in, current - 3, 1, list36)) {
+                MetaphAdd(primary, 'F');
+              } else {
+                if ((current > 0) && (in.charAt(current - 1) != 'I'))
+                  MetaphAdd(primary, 'K');
+              }
+              current += 2;
+              break;
+            }
+          }
+          if (in.charAt(current + 1) == 'N') {
+            if ((current == 1) && isVowel(in, 0, length) && !isSlavoGermaic) {
+              MetaphAdd(primary, "KN");
+            } else {
+              if (!stringAt(in, current + 2, 2, list37) && (in.charAt(current + 1) != 'Y') && !isSlavoGermaic) {
+                MetaphAdd(primary, "N");
+              } else {
+                MetaphAdd(primary, "KN");
+              }
+            }
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current + 1, 2, list38) && !isSlavoGermaic) {
+            MetaphAdd(primary, "KL");
+            current += 2;
+            break;
+          }
+          if ((current == 0) && ((in.charAt(current + 1) == 'Y') || stringAt(in, current + 1, 2, list39))) {
+            MetaphAdd(primary, 'K');
+            current += 2;
+            break;
+          }
+          if ((stringAt(in, current + 1, 2, list40) || (in.charAt(current + 1) == 'Y')) && !stringAt(in, 0, 6, list41) && !stringAt(in, current - 1, 1, list42) && !stringAt(in, current - 1, 3, list43)) {
+            MetaphAdd(primary, 'K');
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current + 1, 1, list44) || stringAt(in, current - 1, 4, list45)) {
+            if (stringAt(in, 0, 4, list46) || stringAt(in, 0, 3, list47) || stringAt(in, current + 1, 2, list48)) {
+              MetaphAdd(primary, 'K');
+            } else {
+              MetaphAdd(primary, 'J');
+            }
+            current += 2;
+            break;
+          }
+          if (in.charAt(current + 1) == 'G')
+            current += 2;
+          else
+            current += 1;
+          MetaphAdd(primary, 'K');
+          break;
+        case 'H':
+          if (((current == 0) || isVowel(in, current - 1, length)) && isVowel(in, current + 1, length)) {
+            MetaphAdd(primary, 'H');
+            current += 2;
+          } else {
+            current += 1;
+          }
+          break;
+        case 'J':
+          if (stringAt(in, current, 4, list50) || stringAt(in, 0, 4, list51)) {
+            if ((current == 0) && (in.charAt(current + 4) == ' ') || stringAt(in, 0, 4, list52)) {
+              MetaphAdd(primary, 'H');
+            } else {
+              MetaphAdd(primary, 'J');
+            }
+            current += 1;
+            break;
+          }
+          if ((current == 0) && !stringAt(in, current, 4, list53)) {
+            MetaphAdd(primary, 'J');
+          } else {
+            if (isVowel(in, current - 1, length) && !isSlavoGermaic && ((in.charAt(current + 1) == 'A') || in.charAt(current + 1) == 'O')) {
+              MetaphAdd(primary, 'J');
+            } else {
+              if (current == last) {
+                MetaphAdd(primary, 'J');
+              } else {
+                if (!stringAt(in, current + 1, 1, list54) && !stringAt(in, current - 1, 1, list55)) {
+                  MetaphAdd(primary, 'J');
+                }
+              }
+            }
+          }
+          if (in.charAt(current + 1) == 'J')
+            current += 2;
+          else
+            current += 1;
+          break;
+        case 'K':
+          if (in.charAt(current + 1) == 'K')
+            current += 2;
+          else
+            current += 1;
+          MetaphAdd(primary, 'K');
+          break;
+        case 'L':
+          if (in.charAt(current + 1) == 'L') {
+            if (((current == (length - 3)) && stringAt(in, current - 1, 4, list56)) || ((stringAt(in, last - 1, 2, list57) || stringAt(in, last, 1, list58)) && stringAt(in, current - 1, 4, list59))) {
+              MetaphAdd(primary, 'L');
+              current += 2;
+              break;
+            }
+            current += 2;
+          } else
+            current += 1;
+          MetaphAdd(primary, 'L');
+          break;
+        case 'M':
+          if ((stringAt(in, current - 1, 3, list60) && (((current + 1) == last) || stringAt(in, current + 2, 2, list61))) || (in.charAt(current + 1) == 'M'))
+            current += 2;
+          else
+            current += 1;
+          MetaphAdd(primary, 'M');
+          break;
+        case 'N':
+          if (in.charAt(current + 1) == 'N')
+            current += 2;
+          else
+            current += 1;
+          MetaphAdd(primary, 'N');
+          break;
+        case '\u00D1':
+          current += 1;
+          MetaphAdd(primary, 'N');
+          break;
+        case 'P':
+          if (in.charAt(current + 1) == 'N') {
+            MetaphAdd(primary, 'F');
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current + 1, 1, list62))
+            current += 2;
+          else
+            current += 1;
+          MetaphAdd(primary, 'P');
+          break;
+        case 'Q':
+          if (in.charAt(current + 1) == 'Q')
+            current += 2;
+          else
+            current += 1;
+          MetaphAdd(primary, 'K');
+          break;
+        case 'R':
+          if ((current == last) && !isSlavoGermaic && stringAt(in, current - 2, 2, list63) && !stringAt(in, current - 4, 2, list64)) {
+//            MetaphAdd(primary, "");
+          } else
+            MetaphAdd(primary, 'R');
+          if (in.charAt(current + 1) == 'R')
+            current += 2;
+          else
+            current += 1;
+          break;
+        case 'S':
+          if (stringAt(in, current - 1, 3, list65)) {
+            current += 1;
+            break;
+          }
+          if ((current == 0) && stringAt(in, current, 5, list66)) {
+            MetaphAdd(primary, 'X');
+            current += 1;
+            break;
+          }
+          if (stringAt(in, current, 2, list67)) {
+            if (stringAt(in, current + 1, 4, list68))
+              MetaphAdd(primary, 'S');
+            else
+              MetaphAdd(primary, 'X');
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current, 3, list69) || stringAt(in, current, 4, list70)) {
+            MetaphAdd(primary, 'S');
+            current += 3;
+            break;
+          }
+          if (((current == 0) && stringAt(in, current + 1, 1, list71)) || stringAt(in, current + 1, 1, list72)) {
+            MetaphAdd(primary, 'S');
+            if (stringAt(in, current + 1, 1, list73))
+              current += 2;
+            else
+              current += 1;
+            break;
+          }
+          if (stringAt(in, current, 2, list74)) {
+            if (in.charAt(current + 2) == 'H')
+              if (stringAt(in, current + 3, 2, list75)) {
+                if (stringAt(in, current + 3, 2, list76)) {
+                  MetaphAdd(primary, "X");
+                } else {
+                  MetaphAdd(primary, "SK");
+                }
+                current += 3;
+                break;
+              } else {
+                MetaphAdd(primary, 'X');
+                current += 3;
+                break;
+              }
+            if (stringAt(in, current + 2, 1, list77)) {
+              MetaphAdd(primary, 'S');
+              current += 3;
+              break;
+            }
+            MetaphAdd(primary, "SK");
+            current += 3;
+            break;
+          }
+          if ((current == last) && stringAt(in, current - 2, 2, list78)) {
+            //MetaphAdd(primary, "");
+          } else
+            MetaphAdd(primary, 'S');
+          if (stringAt(in, current + 1, 1, list79))
+            current += 2;
+          else
+            current += 1;
+          break;
+        case 'T':
+          if (stringAt(in, current, 4, list80)) {
+            MetaphAdd(primary, 'X');
+            current += 3;
+            break;
+          }
+          if (stringAt(in, current, 3, list81)) {
+            MetaphAdd(primary, 'X');
+            current += 3;
+            break;
+          }
+          if (stringAt(in, current, 2, list82) || stringAt(in, current, 3, list83)) {
+            if (stringAt(in, (current + 2), 2, list84) || stringAt(in, 0, 4, list85) || stringAt(in, 0, 3, list86)) {
+              MetaphAdd(primary, 'T');
+            } else {
+              MetaphAdd(primary, '0');
+            }
+            current += 2;
+            break;
+          }
+          if (stringAt(in, current + 1, 1, list87)) {
+            current += 2;
+          } else
+            current += 1;
+          MetaphAdd(primary, 'T');
+          break;
+        case 'V':
+          if (in.charAt(current + 1) == 'V')
+            current += 2;
+          else
+            current += 1;
+          MetaphAdd(primary, 'F');
+          break;
+        case 'W':
+          if (stringAt(in, current, 2, list88)) {
+            MetaphAdd(primary, 'R');
+            current += 2;
+            break;
+          }
+          if ((current == 0) && (isVowel(in, current + 1, length) || stringAt(in, current, 2, list89))) {
+            MetaphAdd(primary, 'A');
+          }
+          if (((current == last) && isVowel(in, current - 1, length)) || stringAt(in, current - 1, 5, list90) || stringAt(in, 0, 3, list91)) {
+            MetaphAdd(primary, 'F');
+            current += 1;
+            break;
+          }
+          if (stringAt(in, current, 4, list92)) {
+            MetaphAdd(primary, "TS");
+            current += 4;
+            break;
+          }
+          current += 1;
+          break;
+        case 'X':
+          if (!((current == last) && (stringAt(in, current - 3, 3, list93) || stringAt(in, current - 2, 2, list94))))
+            MetaphAdd(primary, "KS");
+          if (stringAt(in, current + 1, 1, list95))
+            current += 2;
+          else
+            current += 1;
+          break;
+        case 'Z':
+          if (in.charAt(current + 1) == 'H') {
+            MetaphAdd(primary, 'J');
+            current += 2;
+            break;
+          } else {
+            MetaphAdd(primary, 'S');
+          }
+          if (in.charAt(current + 1) == 'Z')
+            current += 2;
+          else
+            current += 1;
+          break;
+        default:
+          current += 1;
+      }
+    }
+    return primary.toString();
+  }
+
+  /**
+   * @see com.swabunga.spell.engine.Transformator#getReplaceList()
+   */
+  public char[] getReplaceList() {
+    return replaceList;
+  }
+}
+
+
+
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/Transformator.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/Transformator.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/Transformator.java	(revision 3952)
@@ -0,0 +1,33 @@
+package com.swabunga.spell.engine;
+
+/**
+ * An interface for all Transformators - which take a dictionary word and converts into its
+ * phonetic hash. These phonetic hashs are useful for determining what other words are
+ * similiar to it, and then list those words as suggestsions.
+ *
+ * @author Robert Gustavsson (robert@lindesign.se)
+ */
+public interface Transformator {
+
+  /**
+   * Take the given word, and return the best phonetic hash for it.
+   */
+  public String transform(String word);
+
+  /**
+   * gets the list of characters that should be swapped in to the misspelled word
+   * in order to try to find more suggestions.
+   * In general, this list represents all of the unique phonetic characters
+   * for this Tranformator.
+   * <p/>
+   * The replace list is used in the getSuggestions method.
+   * All of the letters in the misspelled word are replaced with the characters from
+   * this list to try and generate more suggestions, which implies l*n tries,
+   * if l is the size of the string, and n is the size of this list.
+   * <p/>
+   * In addition to that, each of these letters is added to the mispelled word.
+   * <p/>
+   * @return char[] misspelled words should try replacing with these characters to get more suggestions
+   */
+  public char[] getReplaceList();
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/configuration.properties
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/configuration.properties	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/configuration.properties	(revision 3952)
@@ -0,0 +1,18 @@
+EDIT_DEL1=95
+EDIT_DEL2=95
+EDIT_SWAP=90
+EDIT_SUB=100
+EDIT_CASE=10
+
+#DMV: the following commented out settings do not seem to be used at all
+#EDIT_SIMILAR=10
+#EDIT_MIN=90
+#EDIT_MAX=100
+
+SPELL_THRESHOLD=140
+SPELL_IGNOREUPPERCASE=true
+SPELL_IGNOREMIXEDCASE=false
+SPELL_IGNOREINTERNETADDRESS=true
+SPELL_IGNOREDIGITWORDS=true
+SPELL_IGNOREMULTIPLEWORDS=false
+SPELL_IGNORESENTENCECAPTILIZATION=true
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionary.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionary.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionary.java	(revision 3952)
@@ -0,0 +1,41 @@
+package com.swabunga.spell.engine;
+
+import java.util.List;
+
+/**
+ * An interface for all dictionary implementations. It defines the most basic
+ * operations on a dictionary: adding words, checking if a word is correct, and getting a list
+ * of suggestions for misspelled words.
+ */
+public interface SpellDictionary {
+
+  /**
+   * Add a word permanently to the dictionary.
+   */
+  public void addWord(String word);
+
+  /**
+   * Returns true if the word is correctly spelled against the dictionary.
+   */
+  public boolean isCorrect(String word);
+
+  /**
+   * Returns a list of Word objects that are the suggestions to any word.
+   * If the word is correctly spelled, then this method
+   * could return just that one word, or it could still return a list
+   * of words with similar spellings.
+   * <br/>
+   * Each suggested word has a score, which is an integer
+   * that represents how different the suggested word is from the sourceWord.
+   * If the words are the exactly the same, then the score is 0.
+   * You can get the dictionary to only return the most similiar words by setting
+   * an appropriately low threshold value.
+   * If you set the threshold value too low, you may get no suggestions for a given word.
+   * <p>
+   * @param sourceWord the string that we want to get a list of spelling suggestions for
+   * @param scoreThreshold Any words that have score less than this number are returned.
+   * @return List a List of suggested words
+   * @see com.swabunga.spell.engine.Word
+   */
+  public List getSuggestions(String sourceWord, int scoreThreshold);
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionaryASpell.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionaryASpell.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionaryASpell.java	(revision 3952)
@@ -0,0 +1,229 @@
+/* Created by bgalbs on Jan 30, 2003 at 11:45:25 PM */
+package com.swabunga.spell.engine;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.security.InvalidParameterException;
+import java.util.*;
+
+/**
+ * Container for various methods that any <code>SpellDictionary</code> will use.
+ * Based on the original Jazzy <a href="http://aspell.net/">aspell</a> port.
+ * <p/>
+ *
+ *
+ */
+public abstract class SpellDictionaryASpell implements SpellDictionary {
+
+
+  /** The reference to a Transformator, used to transform a word into it's phonetic code. */
+  protected Transformator tf;
+
+  public SpellDictionaryASpell(File phonetic) throws IOException {
+    if (phonetic == null)
+      tf = new DoubleMeta();
+    else
+      tf = new GenericTransformator(phonetic);
+  }
+
+  public SpellDictionaryASpell(File phonetic, String encoding) throws IOException {
+    if (phonetic == null)
+      tf = new DoubleMeta();
+    else
+      tf = new GenericTransformator(phonetic, encoding);
+  }
+
+  public SpellDictionaryASpell(Reader phonetic) throws IOException {
+    if (phonetic == null)
+      tf = new DoubleMeta();
+    else
+      tf = new GenericTransformator(phonetic);
+  }
+
+
+  /**
+   * Returns a list of Word objects that are the suggestions to an
+   * incorrect word.
+   * <p>
+   * @param word Suggestions for given mispelt word
+   * @param threshold The lower boundary of similarity to mispelt word
+   * @return Vector a List of suggestions
+   */
+  public List getSuggestions(String word, int threshold) {
+
+    Hashtable nearmisscodes = new Hashtable();
+    String code = getCode(word);
+
+    // add all words that have the same phonetics
+    nearmisscodes.put(code, code);
+    Vector phoneticList = getWordsFromCode(word, nearmisscodes);
+
+    // do some tranformations to pick up more results
+    //interchange
+    nearmisscodes = new Hashtable();
+    char[] charArray = word.toCharArray();
+    for (int i = 0; i < word.length() - 1; i++) {
+      char a = charArray[i];
+      char b = charArray[i + 1];
+      charArray[i] = b;
+      charArray[i + 1] = a;
+      String s = getCode(new String(charArray));
+      nearmisscodes.put(s, s);
+      charArray[i] = a;
+      charArray[i + 1] = b;
+    }
+
+    char[] replacelist = tf.getReplaceList();
+
+    //change
+    charArray = word.toCharArray();
+    for (int i = 0; i < word.length(); i++) {
+      char original = charArray[i];
+      for (int j = 0; j < replacelist.length; j++) {
+        charArray[i] = replacelist[j];
+        String s = getCode(new String(charArray));
+        nearmisscodes.put(s, s);
+      }
+      charArray[i] = original;
+    }
+
+    //add
+    charArray = (word += " ").toCharArray();
+    int iy = charArray.length - 1;
+    while (true) {
+      for (int j = 0; j < replacelist.length; j++) {
+        charArray[iy] = replacelist[j];
+        String s = getCode(new String(charArray));
+        nearmisscodes.put(s, s);
+      }
+      if (iy == 0)
+        break;
+      charArray[iy] = charArray[iy - 1];
+      --iy;
+    }
+
+    //delete
+    word = word.trim();
+    charArray = word.toCharArray();
+    char[] charArray2 = new char[charArray.length - 1];
+    for (int ix = 0; ix < charArray2.length; ix++) {
+      charArray2[ix] = charArray[ix];
+    }
+    char a, b;
+    a = charArray[charArray.length - 1];
+    int ii = charArray2.length;
+    while (true) {
+      String s = getCode(new String(charArray));
+      nearmisscodes.put(s, s);
+      if (ii == 0)
+        break;
+      b = a;
+      a = charArray2[ii - 1];
+      charArray2[ii - 1] = b;
+      --ii;
+    }
+
+    nearmisscodes.remove(code); //already accounted for in phoneticList
+
+    Vector wordlist = getWordsFromCode(word, nearmisscodes);
+
+    if (wordlist.size() == 0 && phoneticList.size() == 0)
+      addBestGuess(word, phoneticList);
+
+
+    // We sort a Vector at the end instead of maintaining a
+    // continously sorted TreeSet because everytime you add a collection
+    // to a treeset it has to be resorted. It's better to do this operation
+    // once at the end.
+
+    Collections.sort(phoneticList, new Word()); //always sort phonetic matches along the top
+    Collections.sort(wordlist, new Word()); //the non-phonetic matches can be listed below
+
+    phoneticList.addAll(wordlist);
+    return phoneticList;
+  }
+
+  /**
+   * When we don't come up with any suggestions (probably because the threshold was too strict),
+   * then pick the best guesses from the those words that have the same phonetic code.
+   * @param word - the word we are trying spell correct
+   * @param wordList - the linked list that will get the best guess
+   */
+  private void addBestGuess(String word, Vector wordList) {
+    if (wordList.size() != 0)
+      throw new InvalidParameterException("the wordList vector must be empty");
+
+    int bestScore = Integer.MAX_VALUE;
+
+    String code = getCode(word);
+    List simwordlist = getWords(code);
+
+    LinkedList candidates = new LinkedList();
+
+    for (Iterator j = simwordlist.iterator(); j.hasNext();) {
+      String similar = (String) j.next();
+      int distance = EditDistance.getDistance(word, similar);
+      if (distance <= bestScore) {
+        bestScore = distance;
+        Word goodGuess = new Word(similar, distance);
+        candidates.add(goodGuess);
+      }
+    }
+
+    //now, only pull out the guesses that had the best score
+    for (Iterator iter = candidates.iterator(); iter.hasNext();) {
+      Word candidate = (Word) iter.next();
+      if (candidate.getCost() == bestScore)
+        wordList.add(candidate);
+    }
+
+  }
+
+  private Vector getWordsFromCode(String word, Hashtable codes) {
+    Configuration config = Configuration.getConfiguration();
+    Vector result = new Vector();
+    final int configDistance = config.getInteger(Configuration.SPELL_THRESHOLD);
+
+    for (Enumeration i = codes.keys(); i.hasMoreElements();) {
+      String code = (String) i.nextElement();
+
+      List simwordlist = getWords(code);
+      for (Iterator iter = simwordlist.iterator(); iter.hasNext();) {
+        String similar = (String) iter.next();
+        int distance = EditDistance.getDistance(word, similar);
+        if (distance < configDistance) {
+          Word w = new Word(similar, distance);
+          result.addElement(w);
+        }
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Returns the phonetic code representing the word.
+   */
+  public String getCode(String word) {
+    return tf.transform(word);
+  }
+
+  /**
+   * Returns a list of words that have the same phonetic code.
+   */
+  protected abstract List getWords(String phoneticCode);
+
+  /**
+   * Returns true if the word is correctly spelled against the current word list.
+   */
+  public boolean isCorrect(String word) {
+    List possible = getWords(getCode(word));
+    if (possible.contains(word))
+      return true;
+    //JMH should we always try the lowercase version. If I dont then capitalised
+    //words are always returned as incorrect.
+    else if (possible.contains(word.toLowerCase()))
+      return true;
+    return false;
+  }
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/Configuration.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/Configuration.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/Configuration.java	(revision 3952)
@@ -0,0 +1,125 @@
+package com.swabunga.spell.engine;
+
+
+/**
+ * The various settings used to control how a spell checker works are read from here.
+ * Includes the COST_* constants that decide how to figure the cost of converting one word to
+ * another in the EditDistance class.
+ * <p/>
+ * Also includes SPELL_* constants that control how misspellings are detected, for example, how to handle
+ * mixed-case words, etc.
+ *
+ * @author aim4min
+ * @see EditDistance
+ */
+public abstract class Configuration {
+
+  /** used by EditDistance: the cost of having to remove a character <br/>(integer greater than 0) */
+  public static final String COST_REMOVE_CHAR = "EDIT_DEL1";
+
+  /** used by EditDistance: the cost of having to insert a character <br/>(integer greater than 0)*/
+  public static final String COST_INSERT_CHAR = "EDIT_DEL2";
+
+  /**
+   * used by EditDistance: the cost of having to swap two adjoinging characters
+   * for the swap value to ever be used, it should be smaller than the COST_REMOVE_CHAR or COST_INSERT_CHAR values
+   * <br/>(integer greater than 0)
+   */
+  public static final String COST_SWAP_CHARS = "EDIT_SWAP";
+
+  /**
+   * used by EditDistance: the cost of having to change case, for example, from i to I.
+   * <br/>(integer greater than 0)
+   */
+  public static final String COST_CHANGE_CASE = "EDIT_CASE";
+
+  /**
+   * used by EditDistance: the cost of having to substitute one character for another
+   * for the sub value to ever be used, it should be smaller than the COST_REMOVE_CHAR or COST_INSERT_CHAR values
+   * <br/>(integer greater than 0)
+   */
+  public static final String COST_SUBST_CHARS = "EDIT_SUB";
+
+//    public static final String EDIT_SIMILAR = "EDIT_SIMILAR"; //DMV: these does not seem to be used at all
+//    public static final String EDIT_MIN = "EDIT_MIN";
+//    public static final String EDIT_MAX = "EDIT_MAX";
+
+  /** the maximum cost of suggested spelling. Any suggestions that cost more are thrown away
+   * <br/> integer greater than 1)
+   */
+  public static final String SPELL_THRESHOLD = "SPELL_THRESHOLD";
+
+  /** words that are all upper case are not spell checked, example: "CIA" <br/>(boolean) */
+  public static final String SPELL_IGNOREUPPERCASE = "SPELL_IGNOREUPPERCASE";
+  /**  words that have mixed case are not spell checked, example: "SpellChecker"<br/>(boolean) */
+  public static final String SPELL_IGNOREMIXEDCASE = "SPELL_IGNOREMIXEDCASE";
+  /** words that look like an internet address are not spell checked, example: "http://www.google.com" <br/>(boolean)*/
+  public static final String SPELL_IGNOREINTERNETADDRESSES = "SPELL_IGNOREINTERNETADDRESS";
+  /** words that have digits in them are not spell checked, example: "mach5" <br/>(boolean) */
+  public static final String SPELL_IGNOREDIGITWORDS = "SPELL_IGNOREDIGITWORDS";
+  /** I don't know what this does. It doesn't seem to be used <br/>(boolean) */
+  public static final String SPELL_IGNOREMULTIPLEWORDS = "SPELL_IGNOREMULTIPLEWORDS";
+  /** the first word of a sentance is expected to start with an upper case letter <br/>(boolean) */
+  public static final String SPELL_IGNORESENTENCECAPITALIZATION = "SPELL_IGNORESENTENCECAPTILIZATION";
+
+  /**
+   * Gets one of the integer constants
+   * @param key one of the integer constants defined in this class
+   * @return int value of the setting
+   */
+  public abstract int getInteger(String key);
+
+  /**
+   * Gets one of the boolean constants
+   * @param key one of the boolean constants defined in this class
+   * @return boolean value of the setting
+   */
+  public abstract boolean getBoolean(String key);
+
+  /**
+   * Sets one of the integer constants
+   * @param key one of the integer constants defined in this class
+   * @param value new integer value of the constant
+   */
+  public abstract void setInteger(String key, int value);
+
+  /**
+   * Sets one of the boolean constants
+   * @param key one of the boolean constants defined in this class
+   * @param value new boolean value of this setting
+   */
+  public abstract void setBoolean(String key, boolean value);
+
+  /**
+   * gets a new default Configuration
+   * @return Configuration
+   */
+  public static final Configuration getConfiguration() {
+    return getConfiguration(null);
+  }
+
+  /**
+   * Returns a new instance of a Configuration class
+   * @param className - the class to return, must be based on Configuration
+   * @return Configuration
+   */
+  public static final Configuration getConfiguration(String className) {
+
+    Configuration result;
+
+    if (className != null && className.length() > 0) {
+      try {
+        result = (Configuration) Class.forName(className).newInstance();
+      } catch (InstantiationException e) {
+        result = new PropertyConfiguration();
+      } catch (IllegalAccessException e) {
+        result = new PropertyConfiguration();
+      } catch (ClassNotFoundException e) {
+        result = new PropertyConfiguration();
+      }
+    } else {
+      result = new PropertyConfiguration();
+    }
+    return result;
+  }
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionaryDichoDisk.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionaryDichoDisk.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionaryDichoDisk.java	(revision 3952)
@@ -0,0 +1,173 @@
+package com.swabunga.spell.engine;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Another implementation of <code>SpellDictionary</code> that doesn't cache any words in memory. Avoids the huge
+ * footprint of <code>SpellDictionaryHashMap</code> at the cost of relatively minor latency. A future version
+ * of this class that implements some caching strategies might be a good idea in the future, if there's any
+ * demand for it.
+ *
+ * This implementation requires a special dictionary file, with "code*word" lines sorted by code.
+ * It's using a dichotomy algorithm to search for words in the dictionary
+ *
+ * @author Damien Guillaume
+ * @version 0.1
+ */
+public class SpellDictionaryDichoDisk extends SpellDictionaryASpell {
+
+  /** Holds the dictionary file for reading*/
+  private RandomAccessFile dictFile = null;
+  
+  /** dictionary and phonetic file encoding */
+  private String encoding = null;
+
+  /**
+   * Dictionary Convienence Constructor.
+   */
+  public SpellDictionaryDichoDisk(File wordList)
+    throws FileNotFoundException, IOException {
+    super((File) null);
+    dictFile = new RandomAccessFile(wordList, "r");
+  }
+
+  /**
+   * Dictionary Convienence Constructor.
+   */
+  public SpellDictionaryDichoDisk(File wordList, String encoding)
+    throws FileNotFoundException, IOException {
+    super((File) null);
+    this.encoding = encoding;
+    dictFile = new RandomAccessFile(wordList, "r");
+  }
+
+  /**
+  * Dictionary constructor that uses an aspell phonetic file to
+  * build the transformation table.
+  */
+  public SpellDictionaryDichoDisk(File wordList, File phonetic)
+    throws FileNotFoundException, IOException {
+    super(phonetic);
+    dictFile = new RandomAccessFile(wordList, "r");
+  }
+  
+  /**
+  * Dictionary constructor that uses an aspell phonetic file to
+  * build the transformation table.
+  */
+  public SpellDictionaryDichoDisk(File wordList, File phonetic, String encoding)
+    throws FileNotFoundException, IOException {
+    super(phonetic, encoding);
+    this.encoding = encoding;
+    dictFile = new RandomAccessFile(wordList, "r");
+  }
+  
+  /**
+   * Add a word permanantly to the dictionary (and the dictionary file).
+   * <i>not implemented !</i>
+   */
+  public void addWord(String word) {
+    System.err.println("error: addWord is not implemented for SpellDictionaryDichoDisk");
+  }
+
+  /**
+    * Search the dictionary file for the words corresponding to the code
+    * within positions p1 - p2
+    */
+   private LinkedList dichoFind(String code, long p1, long p2) throws IOException {
+     //System.out.println("dichoFind("+code+","+p1+","+p2+")");
+     long pm = (p1 + p2) / 2;
+    dictFile.seek(pm);
+    String l;
+    if (encoding == null)
+      l = dictFile.readLine();
+    else
+      l = dictReadLine();
+    pm = dictFile.getFilePointer();
+    if (encoding == null)
+      l = dictFile.readLine();
+    else
+      l = dictReadLine();
+    long pm2 = dictFile.getFilePointer();
+    if (pm2 >= p2)
+      return(seqFind(code, p1, p2));
+    int istar = l.indexOf('*');
+    if (istar == -1)
+      throw new IOException("bad format: no * !");
+    String testcode = l.substring(0, istar);
+    int comp = code.compareTo(testcode);
+    if (comp < 0)
+      return(dichoFind(code, p1, pm-1));
+    else if (comp > 0)
+      return(dichoFind(code, pm2, p2));
+    else {
+      LinkedList l1 = dichoFind(code, p1, pm-1);
+      LinkedList l2 = dichoFind(code, pm2, p2);
+      String word = l.substring(istar+1);
+      l1.add(word);
+      l1.addAll(l2);
+      return(l1);
+    }
+   }
+   
+   private LinkedList seqFind(String code, long p1, long p2) throws IOException {
+     //System.out.println("seqFind("+code+","+p1+","+p2+")");
+     LinkedList list = new LinkedList();
+    dictFile.seek(p1);
+    while (dictFile.getFilePointer() < p2) {
+      String l;
+      if (encoding == null)
+        l = dictFile.readLine();
+      else
+        l = dictReadLine();
+      int istar = l.indexOf('*');
+      if (istar == -1)
+        throw new IOException("bad format: no * !");
+      String testcode = l.substring(0, istar);
+      if (testcode.equals(code)) {
+        String word = l.substring(istar+1);
+        list.add(word);
+      }
+    }
+    return(list);
+   }
+   
+   /**
+     * Read a line of dictFile with a specific encoding
+     */
+   private String dictReadLine() throws IOException {
+     int max = 255;
+     byte b=0;
+    byte[] buf = new byte[max];
+    int i=0;
+     try {
+       for (; b != '\n' && b != '\r' && i<max-1; i++) {
+        b = dictFile.readByte();
+         buf[i] = b;
+      }
+    } catch (EOFException ex) {
+    }
+    if (i == 0)
+      return("");
+    String s = new String(buf, 0, i-1, encoding);
+    return(s);
+   }
+   
+  /**
+   * Returns a list of strings (words) for the code.
+   */
+  public List getWords(String code) {
+     //System.out.println("getWords("+code+")");
+    LinkedList list;
+    try {
+      list = dichoFind(code, 0, dictFile.length()-1);
+      //System.out.println(list);
+    } catch (IOException ex) {
+      System.err.println("IOException: " + ex.getMessage());
+      list = new LinkedList();
+    }
+    return list;
+  }
+
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionaryHashMap.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionaryHashMap.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/SpellDictionaryHashMap.java	(revision 3952)
@@ -0,0 +1,246 @@
+/*
+ * put your module comment here
+ * formatted with JxBeauty (c) johann.langhofer@nextra.at
+ */
+
+package com.swabunga.spell.engine;
+
+import java.io.*;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * The SpellDictionaryHashMap holds the dictionary
+ * <p/>
+ * This class is thread safe. Derived classes should ensure that this preserved.
+ * <p/>
+ * There are many open source dictionary files. For just a few see:
+ * http://wordlist.sourceforge.net/
+ * <p/>
+ * This dictionary class reads words one per line. Make sure that your word list
+ * is formatted in this way (most are).
+ */
+public class SpellDictionaryHashMap extends SpellDictionaryASpell {
+  /** A field indicating the initial hash map capacity (16KB) for the main
+   *  dictionary hash map. Interested to see what the performance of a
+   *  smaller initial capacity is like.
+   */
+  private final static int INITIAL_CAPACITY = 16 * 1024;
+
+  /**
+   * The hashmap that contains the word dictionary. The map is hashed on the doublemeta
+   * code. The map entry contains a LinkedList of words that have the same double meta code.
+   */
+  protected Hashtable mainDictionary = new Hashtable(INITIAL_CAPACITY);
+
+  /** Holds the dictionary file for appending*/
+  private File dictFile = null;
+
+  /**
+   * Dictionary Constructor.
+   */
+  public SpellDictionaryHashMap() throws IOException {
+    super((File) null);
+  }
+
+  /**
+   * Dictionary Constructor.
+   */
+  public SpellDictionaryHashMap(Reader wordList) throws IOException {
+    super((File) null);
+    createDictionary(new BufferedReader(wordList));
+  }
+
+  /**
+   * Dictionary Convienence Constructor.
+   */
+  public SpellDictionaryHashMap(File wordList) throws FileNotFoundException, IOException {
+    this(new FileReader(wordList));
+    dictFile = wordList;
+  }
+
+  /**
+   * Dictionary constructor that uses an aspell phonetic file to
+   * build the transformation table.
+   */
+  public SpellDictionaryHashMap(File wordList, File phonetic) throws FileNotFoundException, IOException {
+    super(phonetic);
+    dictFile = wordList;
+    createDictionary(new BufferedReader(new FileReader(wordList)));
+  }
+
+  /**
+   * Dictionary constructor that uses an aspell phonetic file to
+   * build the transformation table.
+   * encoding is used for phonetic file only; default encoding is used for wordList
+   */
+  public SpellDictionaryHashMap(File wordList, File phonetic, String phoneticEncoding) throws FileNotFoundException, IOException {
+    super(phonetic, phoneticEncoding);
+    dictFile = wordList;
+    createDictionary(new BufferedReader(new FileReader(wordList)));
+  }
+
+  /**
+   * Dictionary constructor that uses an aspell phonetic file to
+   * build the transformation table.
+   */
+  public SpellDictionaryHashMap(Reader wordList, Reader phonetic) throws IOException {
+    super(phonetic);
+    dictFile = null;
+    createDictionary(new BufferedReader(wordList));
+  }
+
+  /**
+   * Add words from a file to existing dictionary hashmap.
+   * This function can be called as many times as needed to
+   * build the internal word list. Duplicates are not added.
+   * <p>
+   * Note that adding a dictionary does not affect the target
+   * dictionary file for the addWord method. That is, addWord() continues
+   * to make additions to the dictionary file specified in createDictionary()
+   * <P>
+   * @param wordList a File object that contains the words, on word per line.
+   * @throws FileNotFoundException
+   * @throws IOException
+   */
+  public void addDictionary(File wordList) throws FileNotFoundException, IOException {
+    addDictionaryHelper(new BufferedReader(new FileReader(wordList)));
+  }
+
+  public void addDictionary(Reader wordList) throws IOException {
+    addDictionaryHelper(new BufferedReader(wordList));
+  }
+
+  /**
+   * Add a word permanantly to the dictionary (and the dictionary file).
+   * <p>This needs to be made thread safe (synchronized)</p>
+   */
+  public void addWord(String word) {
+    putWord(word);
+    if (dictFile == null)
+      return;
+    try {
+      FileWriter w = new FileWriter(dictFile.toString(), true);
+      // Open with append.
+      w.write(word);
+      w.write("\n");
+      w.close();
+    } catch (IOException ex) {
+      System.out.println("Error writing to dictionary file");
+    }
+  }
+
+  /**
+   * Constructs the dictionary from a word list file.
+   * <p>
+   * Each word in the reader should be on a seperate line.
+   * <p>
+   * This is a very slow function. On my machine it takes quite a while to
+   * load the data in. I suspect that we could speed this up quite alot.
+   */
+  protected void createDictionary(BufferedReader in) throws IOException {
+    String line = "";
+    while (line != null) {
+      line = in.readLine();
+      if (line != null && line.length() > 0) {
+        line = new String(line.toCharArray());
+        putWord(line);
+      }
+    }
+  }
+
+  /**
+   * Adds to the existing dictionary from a word list file. If the word
+   * already exists in the dictionary, a new entry is not added.
+   * <p>
+   * Each word in the reader should be on a seperate line.
+   * <p>
+   * Note: for whatever reason that I haven't yet looked into, the phonetic codes
+   * for a particular word map to a vector of words rather than a hash table.
+   * This is a drag since in order to check for duplicates you have to iterate
+   * through all the words that use the phonetic code.
+   * If the vector-based implementation is important, it may be better
+   * to subclass for the cases where duplicates are bad.
+   */
+  protected void addDictionaryHelper(BufferedReader in) throws IOException {
+
+    String line = "";
+    while (line != null) {
+      line = in.readLine();
+      if (line != null && line.length() > 0) {
+        line = new String(line.toCharArray());
+        putWordUnique(line);
+      }
+    }
+  }
+
+  /**
+   * Allocates a word in the dictionary
+   */
+  protected void putWord(String word) {
+    String code = getCode(word);
+    Vector list = (Vector) mainDictionary.get(code);
+    if (list != null) {
+      list.addElement(word);
+    } else {
+      list = new Vector();
+      list.addElement(word);
+      mainDictionary.put(code, list);
+    }
+  }
+
+  protected void putWordUnique(String word) {
+
+    String code = getCode(word);
+    Vector list = (Vector) mainDictionary.get(code);
+
+    if (list != null) {
+
+      boolean isAlready = false;
+
+      for (int i = 0; i < list.size(); i++) {
+
+        if (word.equalsIgnoreCase((String) list.elementAt(i))) {
+          isAlready = true;
+          break;
+        }
+      }
+
+      if (!isAlready)
+        list.addElement(word);
+
+    } else {
+
+      list = new Vector();
+      list.addElement(word);
+      mainDictionary.put(code, list);
+
+    }
+  }
+
+  /**
+   * Returns a list of strings (words) for the code.
+   */
+  public List getWords(String code) {
+    //Check the main dictionary.
+    Vector mainDictResult = (Vector) mainDictionary.get(code);
+    if (mainDictResult == null)
+      return new Vector();
+    return mainDictResult;
+  }
+
+  /**
+   * Returns true if the word is correctly spelled against the current word list.
+   */
+  public boolean isCorrect(String word) {
+    List possible = getWords(getCode(word));
+    if (possible.contains(word))
+      return true;
+    //JMH should we always try the lowercase version. If I dont then capitalised
+    //words are always returned as incorrect.
+    else if (possible.contains(word.toLowerCase()))
+      return true;
+    return false;
+  }
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/Word.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/Word.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/Word.java	(revision 3952)
@@ -0,0 +1,69 @@
+package com.swabunga.spell.engine;
+
+import java.util.Comparator;
+
+/**
+ * The Word object holds information for one suggested spelling.
+ * It contains both the suggested word string and the distance cost, which represents how different the suggested
+ * word is from the misspelling.
+ *  <p>This class is now immutable.
+ *  </p>
+ */
+public class Word implements Comparator {
+  private String word;
+  private int score;
+
+  public Word(String word, int score) {
+    this.word = word;
+    this.score = score;
+  }
+
+  public Word() {
+    this.word = "";
+    this.score = 0;
+  }
+
+  /**
+   * Compares two words, mostly for the purpose of sorting words.
+   * @param o1 the first word
+   * @param o2 the second word
+   * @return -1 if the first word is more similar to the mispelled word
+   * <br>1 if the second word is more similar to the misspelled word
+   * <br>0 if both words are equally similar
+   *
+   */
+  public int compare(Object o1, Object o2) {
+    if (((Word) o1).getCost() < ((Word) o2).getCost()) return -1;
+    if (((Word) o1).getCost() == ((Word) o2).getCost()) return 0;
+    return 1;
+  }
+
+  public boolean equals(Object o) {
+    return(((Word)o).getWord().equals(getWord()));
+  }
+  
+  /**
+   * gets suggested spelling
+   * @return the actual text of the suggest spelling
+   */
+  public String getWord() {
+    return word;
+  }
+
+  /**
+   * A cost measures how close a match this word was to the original word
+   * @return 0 if an exact match. Higher numbers are worse matches.
+   * @see EditDistance
+   */
+  public int getCost() {
+    return score;
+  }
+
+  /**
+   * returns the suggested spelling
+   */
+  public String toString() {
+    return word;
+  }
+}
+
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/PropertyConfiguration.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/PropertyConfiguration.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/PropertyConfiguration.java	(revision 3952)
@@ -0,0 +1,78 @@
+package com.swabunga.spell.engine;
+
+import java.io.*;
+import java.net.URL;
+import java.util.Properties;
+
+
+/**
+ * @author aim4min
+ *
+ * To change this generated comment edit the template variable "typecomment":
+ * Window>Preferences>Java>Templates.
+ * To enable and disable the creation of type comments go to
+ * Window>Preferences>Java>Code Generation.
+ */
+public class PropertyConfiguration extends Configuration {
+
+  public Properties prop;
+  public URL filename;
+
+  public PropertyConfiguration() {
+    prop = new Properties();
+    try {
+      filename = getClass().getClassLoader().getResource("com/swabunga/spell/engine/configuration.properties");
+      InputStream in = filename.openStream();
+      prop.load(in);
+    } catch (Exception e) {
+      System.out.println("Could not load Properties file :\n" + e);
+    }
+  }
+
+  /**
+   * @see com.swabunga.spell.engine.Configuration#getBoolean(String)
+   */
+  public boolean getBoolean(String key) {
+    return new Boolean(prop.getProperty(key)).booleanValue();
+  }
+
+  /**
+   * @see com.swabunga.spell.engine.Configuration#getInteger(String)
+   */
+  public int getInteger(String key) {
+    return new Integer(prop.getProperty(key)).intValue();
+  }
+
+  /**
+   * @see com.swabunga.spell.engine.Configuration#setBoolean(String, boolean)
+   */
+  public void setBoolean(String key, boolean value) {
+    String string = null;
+    if (value)
+      string = "true";
+    else
+      string = "false";
+
+    prop.setProperty(key, string);
+    save();
+  }
+
+  /**
+   * @see com.swabunga.spell.engine.Configuration#setInteger(String, int)
+   */
+  public void setInteger(String key, int value) {
+    prop.setProperty(key, Integer.toString(value));
+    save();
+  }
+
+  public void save() {
+    try {
+      File file = new File(filename.getFile());
+      FileOutputStream fout = new FileOutputStream(file);
+      prop.store(fout, "HEADER");
+    } catch (FileNotFoundException e) {
+    } catch (IOException e) {
+    }
+  }
+
+}
Index: 3thparty/jmessenger/src/com/swabunga/spell/engine/GenericTransformator.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/spell/engine/GenericTransformator.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/spell/engine/GenericTransformator.java	(revision 3952)
@@ -0,0 +1,344 @@
+package com.swabunga.spell.engine;
+
+import com.swabunga.util.StringUtility;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Vector;
+
+/**
+ * A Generic implementation of a transformator takes an aspell phonetics file and constructs
+ * some sort of transformation table using the inner class Rule.
+ *
+ * @author Robert Gustavsson (robert@lindesign.se)
+ */
+public class GenericTransformator implements Transformator {
+
+
+  /**
+   * This replace list is used if no phonetic file is supplied or it doesn't
+   * contain the alphabet.
+   */
+  private static final char[] defaultEnglishAlphabet = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
+
+
+  public static final char ALPHABET_START = '[';
+  public static final char ALPHABET_END = ']';
+  public static final String KEYWORD_ALPHBET = "alphabet";
+  public static final String[] IGNORED_KEYWORDS = {"version", "followup", "collapse_result"};
+
+  public static final char STARTMULTI = '(';
+  public static final char ENDMULTI = ')';
+  public static final String DIGITCODE = "0";
+  public static final String REPLACEVOID = "_";
+
+  private Object[] ruleArray = null;
+  private char[] alphabetString = defaultEnglishAlphabet;
+
+  public GenericTransformator(File phonetic) throws IOException {
+    buildRules(new BufferedReader(new FileReader(phonetic)));
+    alphabetString = washAlphabetIntoReplaceList(getReplaceList());
+
+  }
+
+  public GenericTransformator(File phonetic, String encoding) throws IOException {
+    buildRules(new BufferedReader(new InputStreamReader(new FileInputStream(phonetic), encoding)));
+    alphabetString = washAlphabetIntoReplaceList(getReplaceList());
+  }
+
+  public GenericTransformator(Reader phonetic) throws IOException {
+    buildRules(new BufferedReader(phonetic));
+    alphabetString = washAlphabetIntoReplaceList(getReplaceList());
+  }
+
+  /**
+   * Goes through an alphabet and makes sure that only one of those letters
+   * that are coded equally will be in the replace list.
+   * In other words, it removes any letters in the alphabet
+   * that are redundant phonetically.
+   *
+   * This is done to improve speed in the getSuggestion method.
+   *
+   * @param alphabet The complete alphabet to wash.
+   * @return The washed alphabet to be used as replace list.
+   */
+  private char[] washAlphabetIntoReplaceList(char[] alphabet) {
+
+    HashMap letters = new HashMap(alphabet.length);
+
+    for (int i = 0; i < alphabet.length; i++) {
+      String tmp = String.valueOf(alphabet[i]);
+      String code = transform(tmp);
+      if (!letters.containsKey(code)) {
+        letters.put(code, new Character(alphabet[i]));
+      }
+    }
+
+    Object[] tmpCharacters = letters.values().toArray();
+    char[] washedArray = new char[tmpCharacters.length];
+
+    for (int i = 0; i < tmpCharacters.length; i++) {
+      washedArray[i] = ((Character) tmpCharacters[i]).charValue();
+    }
+
+    return washedArray;
+  }
+
+
+  /**
+   * Takes out all single character replacements and put them in a char array.
+   * This array can later be used for adding or changing letters in getSuggestion().
+   * @return char[] An array of chars with replacements characters
+   */
+  public char[] getCodeReplaceList() {
+    char[] replacements;
+    TransformationRule rule;
+    Vector tmp = new Vector();
+
+    if (ruleArray == null)
+      return null;
+    for (int i = 0; i < ruleArray.length; i++) {
+      rule = (TransformationRule) ruleArray[i];
+      if (rule.getReplaceExp().length() == 1)
+        tmp.addElement(rule.getReplaceExp());
+    }
+    replacements = new char[tmp.size()];
+    for (int i = 0; i < tmp.size(); i++) {
+      replacements[i] = ((String) tmp.elementAt(i)).charAt(0);
+    }
+    return replacements;
+  }
+
+  /**
+   * Builds up an char array with the chars in the alphabet of the language as it was read from the
+   * alphabet tag in the phonetic file.
+   * @return char[] An array of chars representing the alphabet or null if no alphabet was available.
+   */
+  public char[] getReplaceList() {
+    return alphabetString;
+  }
+
+  /**
+   * Returns the phonetic code of the word.
+   */
+  public String transform(String word) {
+
+    if (ruleArray == null)
+      return null;
+
+    TransformationRule rule;
+    StringBuffer str = new StringBuffer(word.toUpperCase());
+    int strLength = str.length();
+    int startPos = 0, add = 1;
+
+    while (startPos < strLength) {
+
+      add = 1;
+      if (Character.isDigit(str.charAt(startPos))) {
+        StringUtility.replace(str, startPos, startPos + DIGITCODE.length(), DIGITCODE);
+        startPos += add;
+        continue;
+      }
+
+      for (int i = 0; i < ruleArray.length; i++) {
+        //System.out.println("Testing rule#:"+i);
+        rule = (TransformationRule) ruleArray[i];
+        if (rule.startsWithExp() && startPos > 0)
+          continue;
+        if (startPos + rule.lengthOfMatch() > strLength) {
+          continue;
+        }
+        if (rule.isMatching(str, startPos)) {
+          String replaceExp = rule.getReplaceExp();
+
+          add = replaceExp.length();
+          StringUtility.replace(str, startPos, startPos + rule.getTakeOut(), replaceExp);
+          strLength -= rule.getTakeOut();
+          strLength += add;
+          //System.out.println("Replacing with rule#:"+i+" add="+add);
+          break;
+        }
+      }
+      startPos += add;
+    }
+    //System.out.println(word);
+    //System.out.println(str.toString());
+    return str.toString();
+  }
+
+  // Used to build up the transformastion table.
+  private void buildRules(BufferedReader in) throws IOException {
+    String read = null;
+    Vector ruleList = new Vector();
+    while ((read = in.readLine()) != null) {
+      buildRule(realTrimmer(read), ruleList);
+    }
+    ruleArray = new TransformationRule[ruleList.size()];
+    ruleList.copyInto(ruleArray);
+  }
+
+  // Here is where the real work of reading the phonetics file is done.
+  private void buildRule(String str, Vector ruleList) {
+    if (str.length() < 1)
+      return;
+    for (int i = 0; i < IGNORED_KEYWORDS.length; i++) {
+      if (str.startsWith(IGNORED_KEYWORDS[i]))
+        return;
+    }
+
+    // A different alphabet is used for this language, will be read into
+    // the alphabetString variable.
+    if (str.startsWith(KEYWORD_ALPHBET)) {
+      int start = str.indexOf(ALPHABET_START);
+      int end = str.lastIndexOf(ALPHABET_END);
+      if (end != -1 && start != -1) {
+        alphabetString = str.substring(++start, end).toCharArray();
+      }
+      return;
+    }
+
+    TransformationRule rule = null;
+    StringBuffer matchExp = new StringBuffer();
+    StringBuffer replaceExp = new StringBuffer();
+    boolean start = false,
+        end = false;
+    int takeOutPart = 0,
+        matchLength = 0;
+    boolean match = true,
+        inMulti = false;
+    for (int i = 0; i < str.length(); i++) {
+      if (Character.isWhitespace(str.charAt(i))) {
+        match = false;
+      } else {
+        if (match) {
+          if (!isReservedChar(str.charAt(i))) {
+            matchExp.append(str.charAt(i));
+            if (!inMulti) {
+              takeOutPart++;
+              matchLength++;
+            }
+            if (str.charAt(i) == STARTMULTI || str.charAt(i) == ENDMULTI)
+              inMulti = !inMulti;
+          }
+          if (str.charAt(i) == '-')
+            takeOutPart--;
+          if (str.charAt(i) == '^')
+            start = true;
+          if (str.charAt(i) == '$')
+            end = true;
+        } else {
+          replaceExp.append(str.charAt(i));
+        }
+      }
+    }
+    if (replaceExp.toString().equals(REPLACEVOID)) {
+      replaceExp = new StringBuffer("");
+      //System.out.println("Changing _ to \"\" for "+matchExp.toString());
+    }
+    rule = new TransformationRule(matchExp.toString(), replaceExp.toString(), takeOutPart, matchLength, start, end);
+    //System.out.println(rule.toString());
+    ruleList.addElement(rule);
+  }
+
+  // Chars with special meaning to aspell. Not everyone is implemented here.
+  private boolean isReservedChar(char ch) {
+    if (ch == '<' || ch == '>' || ch == '^' || ch == '$' || ch == '-' || Character.isDigit(ch))
+      return true;
+    return false;
+  }
+
+  // Trims off everything we don't care about.
+  private String realTrimmer(String row) {
+    int pos = row.indexOf('#');
+    if (pos != -1) {
+      row = row.substring(0, pos);
+    }
+    return row.trim();
+  }
+
+  // Inner Classes
+  /*
+  * Holds the match string and the replace string and all the rule attributes.
+  * Is responsible for indicating matches.
+  */
+  private class TransformationRule {
+
+    private String replace;
+    private char[] match;
+    // takeOut=number of chars to replace;
+    // matchLength=length of matching string counting multies as one.
+    private int takeOut, matchLength;
+    private boolean start, end;
+
+    // Construktor
+    public TransformationRule(String match, String replace, int takeout, int matchLength, boolean start, boolean end) {
+      this.match = match.toCharArray();
+      this.replace = replace;
+      this.takeOut = takeout;
+      this.matchLength = matchLength;
+      this.start = start;
+      this.end = end;
+    }
+
+    /*
+    * Returns true if word from pos and forward matches the match string.
+    * Precondition: wordPos+matchLength<word.length()
+    */
+    public boolean isMatching(StringBuffer word, int wordPos) {
+      boolean matching = true, inMulti = false, multiMatch = false;
+      char matchCh;
+
+      for (int matchPos = 0; matchPos < match.length; matchPos++) {
+        matchCh = match[matchPos];
+        if (matchCh == STARTMULTI || matchCh == ENDMULTI) {
+          inMulti = !inMulti;
+          if (!inMulti)
+            matching = matching & multiMatch;
+          else
+            multiMatch = false;
+        } else {
+          if (matchCh != word.charAt(wordPos)) {
+            if (inMulti)
+              multiMatch = multiMatch | false;
+            else
+              matching = false;
+          } else {
+            if (inMulti)
+              multiMatch = multiMatch | true;
+            else
+              matching = true;
+          }
+          if (!inMulti)
+            wordPos++;
+          if (!matching)
+            break;
+        }
+      }
+      if (end && wordPos != word.length())
+        matching = false;
+      return matching;
+    }
+
+    public String getReplaceExp() {
+      return replace;
+    }
+
+    public int getTakeOut() {
+      return takeOut;
+    }
+
+    public boolean startsWithExp() {
+      return start;
+    }
+
+    public int lengthOfMatch() {
+      return matchLength;
+    }
+
+    // Just for debugging purposes.
+    public String toString() {
+      return "Match:" + String.valueOf(match) + " Replace:" + replace + " TakeOut:" + takeOut + " MatchLength:" + matchLength + " Start:" + start + " End:" + end;
+    }
+
+  }
+}
Index: 3thparty/jmessenger/src/com/swabunga/util/VectorUtility.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/util/VectorUtility.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/util/VectorUtility.java	(revision 3952)
@@ -0,0 +1,19 @@
+package com.swabunga.util;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class VectorUtility {
+  public static List addAll(List dest, List src) {
+    return addAll(dest, src, true);
+  }
+
+  public static List addAll(List dest, List src, boolean allow_duplicates) {
+    for (Iterator e = src.iterator(); e.hasNext();) {
+      Object o = e.next();
+      if (allow_duplicates || !dest.contains(o))
+        dest.add(o);
+    }
+    return dest;
+  }
+}
Index: 3thparty/jmessenger/src/com/swabunga/util/StringUtility.java
===================================================================
--- 3thparty/jmessenger/src/com/swabunga/util/StringUtility.java	(revision 3952)
+++ 3thparty/jmessenger/src/com/swabunga/util/StringUtility.java	(revision 3952)
@@ -0,0 +1,18 @@
+package com.swabunga.util;
+
+public class StringUtility {
+  public static StringBuffer replace(StringBuffer buf, int start, int end, String text) {
+    int len = text.length();
+    char[] ch = new char[buf.length() + len - (end - start)];
+    buf.getChars(0, start, ch, 0);
+    text.getChars(0, len, ch, start);
+    buf.getChars(end, buf.length(), ch, start + len);
+    buf.setLength(0);
+    buf.append(ch);
+    return buf;
+  }
+
+  public static void main(String[] args) {
+    System.out.println(StringUtility.replace(new StringBuffer(args[0]), Integer.parseInt(args[2]), Integer.parseInt(args[3]), args[1]));
+  }
+}
