001    /*
002        Copyright (C) 2003 Adam Olsen
003    
004            This program is free software; you can redistribute it and/or modify
005        it under the terms of the GNU General Public License as published by
006        the Free Software Foundation; either version 1, or (at your option)
007        any later version.
008    
009        This program is distributed in the hope that it will be useful,
010        but WITHOUT ANY WARRANTY; without even the implied warranty of
011        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012        GNU General Public License for more details.
013    
014        You should have received a copy of the GNU General Public License
015        along with this program; if not, write to the Free Software
016        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
017    */
018    
019    package com.valhalla.jbother;
020    
021    import javax.swing.SwingUtilities;
022    import java.awt.event.*;
023    
024    import org.jivesoftware.smack.*;
025    import org.jivesoftware.smack.filter.*;
026    import org.jivesoftware.smack.packet.*;
027    import org.jivesoftware.smackx.packet.*;
028    
029    import java.util.*;
030    import java.lang.reflect.*;
031    
032    import com.valhalla.gui.*;
033    import com.valhalla.jbother.*;
034    import com.valhalla.jbother.jabber.smack.*;
035    import org.jivesoftware.smack.provider.ProviderManager;
036    
037    import com.valhalla.jbother.jabber.smack.provider.*;
038    import com.valhalla.jbother.jabber.*;
039    import com.valhalla.settings.Settings;
040    
041    /**
042     * Attempts to connect to the server.
043     * If the connection is made successfully, it sets up the various packet listeners
044     * and displays the BuddyList
045     *
046     * @author Adam Olsen
047     * @version 1.0
048    */
049    public class ConnectorThread implements Runnable
050    {
051            private ResourceBundle resources = ResourceBundle.getBundle( "JBotherBundle", Locale.getDefault() );
052            private String server, username, password, resource;
053            private boolean ssl;
054            private int port = 0;
055            private String errorMessage = null;
056            private XMPPConnection connection = null;
057            private static boolean hasHadError = false;
058            private static com.valhalla.jbother.jabber.smack.ConnectionListener conListener =
059                    new com.valhalla.jbother.jabber.smack.ConnectionListener();
060            private static com.valhalla.jbother.jabber.smack.RosterListener rosterListener =
061                    new com.valhalla.jbother.jabber.smack.RosterListener();
062            private Presence.Mode connectMode = Presence.Mode.AVAILABLE;
063            private String statusString = null;
064            private boolean persistent = false;
065            private static boolean cancelled = false;
066            private boolean away = false;
067    
068            /**
069             * Sets up the connector thread
070             * @param box the Object that contains all the required login information
071            */
072            public ConnectorThread( Presence.Mode connectMode, String statusString, boolean away )
073            {
074                    this.server = Settings.getInstance().getProperty( "defaultServer" );
075                    this.username = Settings.getInstance().getProperty( "username" );
076                    this.password = Settings.getInstance().getProperty( "password" );
077                    this.resource = Settings.getInstance().getProperty( "resource" );
078                    this.ssl = Settings.getInstance().getBoolean( "useSSL" );
079    
080                    String p = Settings.getInstance().getProperty( "port" );
081                    if( p != null )
082                    {
083                            try {
084                                    port = Integer.parseInt( p );
085                            }
086                            catch( NumberFormatException ex ) { }
087                    }
088    
089                    hasHadError = false;
090                    this.connectMode = connectMode;
091                    this.statusString = statusString;
092                    this.away = away;
093            }
094    
095            public static void setCancelled( boolean c )
096            {
097                    cancelled = c;
098            }
099    
100            /**
101             * Sets whether or not this thread should try to reconnect if there is a connection error
102             *
103             * @param perisisten set to <tt>true</tt> if you want the thread to continue to try and connect even if there's an error
104            */
105            public void setPersistent( boolean persistent ) { this.persistent = persistent; }
106    
107            /**
108             * Returns the ConnectionListener
109             * @return the connection listener
110            */
111            public static com.valhalla.jbother.jabber.smack.ConnectionListener getConnectionListener() { return conListener; }
112    
113            /**
114             * Sets whether or not a connection error has already been thrown for this connection
115             * @param has true if an error has already been thrown
116            **/
117            public static void setHasHadError( boolean has ) { hasHadError = has; }
118    
119            /**
120             * Called when the Threads .start() method is called
121            */
122            public void run()
123            {
124                    if( !BuddyList.getInstance().isVisible() ) BuddyList.getInstance().setVisible( true );
125                    if( !BuddyList.getInstance().getTopMenu().getStatusMenu().blinkTimerIsRunning() )
126                    {
127                            BuddyList.getInstance().getTopMenu().getStatusMenu().startBlinkTimer();
128                    }
129    
130                    if( cancelled )
131                    {
132                            BuddyList.getInstance().getTopMenu().getStatusMenu().stopBlinkTimer();
133                            cancelled = false;
134                            return;
135                    }
136    
137                    try {
138                            if( com.valhalla.settings.Arguments.getInstance().getProperty( "smackdebug" ) != null ) XMPPConnection.DEBUG_ENABLED = true;
139    
140                            int port = this.port;
141                            if( port == 0 && ssl ) port = 5223;
142                            else if( port == 0 && !ssl ) port = 5222;
143    
144                            // set up the connection
145                            if( !ssl ) connection = connection = new XMPPConnection( server, port );
146                            else connection = new SSLXMPPConnection( server, port );
147                            BuddyList.getInstance().clearTree();
148                            BuddyList.getInstance().init( connection );
149    
150                    }
151                    catch( Exception ex )
152                    {
153                            errorMessage = ex.getMessage();
154                    }
155    
156                    if( cancelled )
157                    {
158                            BuddyList.getInstance().getTopMenu().getStatusMenu().stopBlinkTimer();
159                            cancelled = false;
160    
161                            return;
162                    }
163    
164                    // get the resource from the login box
165                    String tmp = resource;
166                    if( tmp == null || tmp.equals( "" ) ) tmp = "JBother";
167                    final String resource = tmp;
168    
169                    if( errorMessage == null && connection != null )
170                    {
171                            // sets up the various packet listeners
172                            PacketFilter filter = new PacketTypeFilter( Presence.class );
173                            connection.addPacketListener( new PresencePacketListener(), filter );
174                            filter = new PacketTypeFilter( Message.class );
175                            connection.addPacketListener( new MessagePacketListener(), filter );
176                            connection.addConnectionListener( conListener );
177                            filter = new PacketTypeFilter( Version.class );
178                            connection.addPacketListener( new VersionListener(), filter );
179                            filter = new PacketTypeFilter( Time.class );
180                            connection.addPacketListener( new TimeListener(), filter );
181    
182                            ProviderManager.addIQProvider( "query", "jabber:iq:version", new VersionProvider() );
183                            ProviderManager.addIQProvider( "query", "jabber:iq:time", new TimeProvider() );
184    
185                            // attempts to connect
186                            try {
187                                    connection.login( username, password, resource );
188                                    connection.getRoster().setSubscriptionMode( Roster.SUBSCRIPTION_MANUAL );
189                                    connection.getRoster().addRosterListener( rosterListener );
190                            }
191                            catch( XMPPException e )
192                            {
193                                    errorMessage = e.getMessage();
194                            }
195    
196                    }
197    
198                    // if there was an error, display it, and then redisplay a LoginDialog
199                    if( errorMessage != null || connection == null )
200                    {
201                            BuddyList.getInstance().getTopMenu().getStatusMenu().setModeChecked( null );
202    
203                            if( persistent )
204                            {
205                                    try {
206                                            Thread.sleep( 3000 );
207                                    }
208                                    catch( InterruptedException ex ) { }
209                                    errorMessage = null;
210    
211                                    if( cancelled )
212                                    {
213                                            cancelled = false;
214    
215                                            BuddyList.getInstance().getTopMenu().getStatusMenu().stopBlinkTimer();
216                                            return;
217                                    }
218    
219                                    run();
220                                    return;
221                            }
222    
223                            BuddyList.getInstance().getTopMenu().getStatusMenu().stopBlinkTimer();
224    
225    
226                            if( errorMessage == null ) errorMessage = resources.getString( "connectionError" );
227                            if( errorMessage.equals( "Unauthorized" ) )
228                                            errorMessage = new String( resources.getString( "invalidPassword" ) );
229    
230                            Standard.warningMessage( null, resources.getString( "couldNotConnect" ), errorMessage );
231    
232                            return;
233                    }
234    
235                    BuddyList.getInstance().getTopMenu().getStatusMenu().stopBlinkTimer();
236    
237                    // otherwise, set up and display the buddy list
238                    com.valhalla.Logger.debug( "Connected" );
239    
240                    BuddyList.getInstance().startTimer();
241                    BuddyList.getInstance().getTopMenu().getStatusMenu().setModeChecked( connectMode );
242                    BuddyList.getInstance().initBuddies(); // display the buddies
243                    BuddyList.getInstance().setStatus( connectMode, statusString, false );
244    
245                    if( away )
246                    {
247                            BuddyList.getInstance().getAwayHandler().actionPerformed( new ActionEvent( BuddyList.getInstance(), 1, "away" ) );
248                    }
249    
250                    return;
251            }
252    }