001 /*
002 Copyright (C) 2003 Adam Olsen
003 This program is free software; you can redistribute it and/or modify
004 it under the terms of the
005 GNU General Public License as published by
006 the Free Software Foundation; either version 1, or (at your option)
007 any later version.
008 This program is distributed in the hope that it will be useful,
009 but WITHOUT ANY WARRANTY; without even the implied warranty of
010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
011 GNU General Public License for more details.
012 You should have received a copy of the GNU General Public License
013 along with this program; if not, write to the Free Software
014 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
015 */
016 package com.valhalla.jbother;
017
018 import java.awt.event.ActionEvent;
019 import java.util.*;
020
021 import javax.swing.*;
022
023 import org.jivesoftware.smack.*;
024 import org.jivesoftware.smack.filter.*;
025 import org.jivesoftware.smack.packet.*;
026 import org.jivesoftware.smack.provider.ProviderManager;
027 import org.jivesoftware.smackx.*;
028 import org.jivesoftware.smackx.packet.*;
029 import org.jivesoftware.smackx.filetransfer.*;
030 import org.jivesoftware.smackx.muc.MultiUserChat;
031 import org.jivesoftware.smackx.packet.Time;
032 import org.jivesoftware.smackx.provider.*;
033
034 import com.valhalla.gui.Standard;
035 import com.valhalla.jbother.*;
036 import com.valhalla.jbother.jabber.smack.*;
037 import com.valhalla.jbother.jabber.*;
038 import com.valhalla.jbother.preferences.*;
039 import com.valhalla.jbother.jabber.smack.provider.*;
040 import com.valhalla.misc.GnuPG;
041 import com.valhalla.misc.SimpleXOR;
042 import com.valhalla.settings.Settings;
043
044 /**
045 * Attempts to connect to the server. If the connection is made successfully, it
046 * sets up the various packet listeners and displays the BuddyList
047 *
048 * @author Adam Olsen
049 * @author Andrey Zakirov
050 * @created April 10, 2005
051 * @version 1.0
052 */
053 public class ConnectorThread implements Runnable {
054 private static ConnectorThread instance = null;
055 private Thread thread = null;
056
057 private ResourceBundle resources = ResourceBundle.getBundle(
058 "JBotherBundle", Locale.getDefault());
059
060 private String server, username, resource;
061
062 private String password = null;
063
064 private boolean ssl;
065 private boolean gmail;
066 private boolean proxy;
067
068 private int port = 0;
069 private String proxyhost = "";
070 private int proxyport = 0;
071
072 private String errorMessage;
073
074 private XMPPConnection connection = null;
075
076 private boolean hasHadError = false;
077
078 private com.valhalla.jbother.jabber.smack.ConnectionListener conListener = new com.valhalla.jbother.jabber.smack.ConnectionListener();
079
080 private com.valhalla.jbother.jabber.smack.RosterListener
081 rosterListener =
082 new com.valhalla.jbother.jabber.smack.RosterListener();
083 private MessagePacketListener messageListener = new MessagePacketListener();
084
085 private Presence.Mode connectMode = Presence.Mode.AVAILABLE;
086
087 private String statusString = null;
088
089 private boolean persistent = false;
090
091 private boolean cancelled = false;
092
093 private boolean away = false;
094
095 private MessageEventManager eventManager;
096
097 private String gnupgSecretKey = Settings.getInstance().getProperty(
098 "gnupgSecretKeyID");
099
100 private String gnupgTempPass = null;
101
102 private int connectCount = 0;
103 private Roster roster = null;
104 private RosterExchangeManager exchangeManager;
105 private FileTransferManager ftmanager = null;
106
107 /**
108 * Sets up the connector thread
109 *
110 * @param connectMode
111 * Description of the Parameter
112 * @param statusString
113 * Description of the Parameter
114 * @param away
115 * Description of the Parameter
116 */
117 private ConnectorThread() {
118
119 ProviderManager.addIQProvider("query", "jabber:iq:last",
120 new LastActivityProvider());
121
122 ProviderManager.addExtensionProvider("x", "jabber:x:encrypted",
123 new EncryptedProvider());
124 ProviderManager.addExtensionProvider("x", "jabber:x:signed",
125 new SignedProvider());
126
127 ProviderManager.addIQProvider("query", "jabber:iq:search",
128 new com.valhalla.jbother.jabber.smack.provider.SearchProvider());
129
130 ProviderManager.addIQProvider("vCard", "vcard-temp",
131 new VCardProvider());
132
133 PrivateDataManager.addPrivateDataProvider("storage", "storage:bookmarks", new BookmarkProvider());
134 }
135
136 public XMPPConnection getConnection() {
137 return connection;
138 }
139
140 public FileTransferManager getFileTransferManager() {
141 return ftmanager;
142 }
143
144 public boolean isAlive()
145 {
146 if( thread == null ) return false;
147 else return thread.isAlive();
148 }
149
150 public void start()
151 {
152 thread = new Thread(this);
153 thread.start();
154 }
155
156 public RosterExchangeManager getExchangeManager() { return exchangeManager; }
157
158 public static ConnectorThread getInstance()
159 {
160 if( instance == null ) instance = new ConnectorThread();
161 return instance;
162 }
163
164 public Roster getRoster() { return roster; }
165
166 public ConnectorThread init( Presence.Mode connectMode, String statusString,
167 boolean away )
168 {
169 this.server = Settings.getInstance().getProperty("defaultServer");
170 if( server != null ) server = server.toLowerCase();
171 this.username = Settings.getInstance().getProperty("username");
172 this.resource = Settings.getInstance().getProperty("resource");
173 this.ssl = Settings.getInstance().getBoolean("useSSL");
174 this.gmail = Settings.getInstance().getBoolean("gmailBox");
175 this.proxy = Settings.getInstance().getBoolean("useProxy");
176
177 String p = Settings.getInstance().getProperty("port");
178 if (p != null) {
179 try{ port = Integer.parseInt(p); }
180 catch (NumberFormatException ignore){ }
181 }
182
183 if(this.proxy){
184 this.proxyhost = Settings.getInstance().getProperty("proxyHost");
185 String t = Settings.getInstance().getProperty("proxyPort");
186 try{ proxyport = Integer.parseInt(t); }
187 catch(NumberFormatException ignore){ }
188 }
189
190 hasHadError = false;
191 this.connectMode = connectMode;
192 this.statusString = statusString;
193 this.away = away;
194 connectCount = 0;
195
196 return instance;
197 }
198
199 public void resetCredentials() {
200 gnupgSecretKey = Settings.getInstance().getProperty("gnupgSecretKeyID");
201 password = Settings.getInstance().getProperty("password");
202 if (password != null)
203 password = SimpleXOR.decrypt(password, "JBother rules!");
204 gnupgTempPass = null;
205 }
206
207 /**
208 * Gets the messageEventManager attribute of the ConnectorThread class
209 *
210 * @return The messageEventManager value
211 */
212 public MessageEventManager getMessageEventManager() {
213 return eventManager;
214 }
215
216 /**
217 * Sets the cancelled attribute of the ConnectorThread class
218 *
219 * @param c
220 * The new cancelled value
221 */
222 public void setCancelled(boolean c) {
223 cancelled = c;
224 connectCount = 0;
225 }
226
227 /**
228 * Sets whether or not this thread should try to reconnect if there is a
229 * connection error
230 *
231 * @param persistent
232 * set to <tt>true</tt> if you want the thread to continue to
233 * try and connect even if there's an error
234 */
235 public void setPersistent(boolean persistent) {
236 this.persistent = persistent;
237 connectCount = 0;
238 }
239
240 /**
241 * Returns the ConnectionListener
242 *
243 * @return the connection listener
244 */
245 public com.valhalla.jbother.jabber.smack.ConnectionListener getConnectionListener() {
246 return conListener;
247 }
248
249 /**
250 * Sets whether or not a connection error has already been thrown for this
251 * connection
252 *
253 * @param has
254 * true if an error has already been thrown
255 */
256 public void setHasHadError(boolean has) {
257 hasHadError = has;
258 }
259
260 /**
261 * Called when the Threads .start() method is called
262 */
263 public void run() {
264 errorMessage = null;
265 resetCredentials();
266 com.valhalla.Logger.debug("Connector thread starting...");
267 if (!BuddyList.getInstance().getStatusMenu()
268 .blinkTimerIsRunning()) {
269 BuddyList.getInstance().getStatusMenu()
270 .startBlinkTimer();
271 }
272
273 if( connectCount >= 15 )
274 {
275 BuddyList.getInstance().getStatusMenu().stopBlinkTimer();
276
277 SwingUtilities.invokeLater( new Runnable()
278 {
279 public void run()
280 {
281 BuddyList.getInstance().getStatusMenu()
282 .setModeChecked(null);
283 }
284 } );
285 int result = JOptionPane.showConfirmDialog(
286 null,
287 resources.getString( "connectCountTooHigh" ),
288 "JBother", JOptionPane.YES_NO_OPTION);
289
290 connectCount = 0;
291
292 if( result == JOptionPane.YES_OPTION )
293 {
294 run();
295 return;
296 }
297
298 cancelled = true;
299 return;
300 }
301
302 if (password == null) {
303 PasswordDialog dialog = new PasswordDialog(BuddyList.getInstance().getContainerFrame(),resources
304 .getString("jabberPassword"));
305 password = dialog.getText();
306 }
307
308 if (gnupgSecretKey != null && !JBotherLoader.isGPGEnabled()) {
309 int result = JOptionPane
310 .showConfirmDialog(
311 null,
312 "Warning: There is a GnuPG secrety key ID in your profile,\nbut it appears as though GnuPG is not installed on this system.\nWould you still like to connect to the server?",
313 "GnuPG", JOptionPane.YES_NO_OPTION);
314
315 if (result != JOptionPane.YES_OPTION) {
316 BuddyList.getInstance().getStatusMenu()
317 .stopBlinkTimer();
318 BuddyList.getInstance().init(null);
319 return;
320 }
321 }
322
323 else if ((gnupgSecretKey != null && JBotherLoader.isGPGEnabled())
324 && (BuddyList.getInstance().getGnuPGPassword() == null)) {
325 GnuPG gnupg = new GnuPG();
326 while (true) {
327 PasswordDialog dialog = new PasswordDialog(BuddyList.getInstance().getContainerFrame(),resources
328 .getString("gnupgKeyPassword"));
329 gnupgTempPass = dialog.getText();
330 if ((gnupgTempPass != null)
331 && (gnupg.sign("1", gnupgSecretKey, gnupgTempPass))) {
332 BuddyList.getInstance().setGnuPGPassword(gnupgTempPass);
333 break;
334 } else {
335 BuddyList.getInstance().getStatusMenu()
336 .stopBlinkTimer();
337 Standard
338 .warningMessage(null, "GnuPG Error",
339 "Wrong GnuPG passphrase! Please, try connecting again.");
340 BuddyList.getInstance().init(null);
341 BuddyList.getInstance().setGnuPGPassword(null);
342 return;
343 }
344 }
345 }
346
347 if (cancelled) {
348 BuddyList.getInstance().getStatusMenu()
349 .stopBlinkTimer();
350 cancelled = false;
351 connectCount = 0;
352 return;
353 }
354
355 try {
356 XMPPConnection.DEBUG_ENABLED = true;
357 if (com.valhalla.settings.Arguments.getInstance().getProperty("smackdebug") != null) {
358 }else {
359 System.setProperty("smack.debuggerClass", "com.valhalla.jbother.jabber.Debugger");
360 }
361
362 int port = this.port;
363 if (port == 0 && ssl) {
364 port = 5223;
365 } else if (port == 0 && !ssl) {
366 port = 5222;
367 }
368
369 connection = null;
370
371 //smart user@service with different servername
372 //talk.google.com users use username@gmail.com and talk.google.com for servername
373 int b = 0;
374 String service = null;
375 if( ( b = username.indexOf("@") ) != -1){
376 service = username.substring(b+1);
377 username = username.substring(0, b);
378 }
379
380 if(proxy){
381 if ( service == null){
382 connection = new XMPPConnection(server, port, server, new ProxySocketFactory(proxyhost, proxyport));
383 } else connection = new XMPPConnection(server, port, service, new ProxySocketFactory(proxyhost, proxyport));
384 }else if (ssl){
385 if ( service == null){
386 connection = new SSLXMPPConnection(server, port);
387 } else connection = new SSLXMPPConnection(server, port, service);
388 }else{
389 if ( service == null){
390 connection = new XMPPConnection(server, port);
391 } else connection = new XMPPConnection(server, port, service);
392 }
393
394 BuddyList.getInstance().init(connection);
395 BuddyList.getInstance().clearTree();
396 }catch(Exception ex){
397 errorMessage = ex.getMessage();
398 }
399
400 if(cancelled){
401 BuddyList.getInstance().getStatusMenu().stopBlinkTimer();
402 cancelled = false;
403
404 return;
405 }
406
407 // get the resource from the login box
408 String tmp = resource;
409 if (tmp == null || tmp.equals("")) {
410 tmp = "JBother";
411 }
412 final String resource = tmp;
413
414 if (errorMessage == null && connection != null) {
415 PacketFilter anyFilter = new PacketFilter() {
416 public boolean accept(Packet packet) {
417 return true;
418 }
419 };
420
421 // sets up the various packet listeners
422 PacketFilter filter = new PacketTypeFilter(Presence.class);
423
424 connection.addPacketListener(new PresencePacketListener(), filter);
425 filter = new PacketTypeFilter(Message.class);
426 connection.addPacketListener(messageListener, filter);
427 connection.addConnectionListener(conListener);
428 filter = new PacketTypeFilter(Version.class);
429 connection.addPacketListener(new VersionListener(), filter);
430 filter = new PacketTypeFilter(com.valhalla.jbother.jabber.smack.LastActivity.class);
431 connection.addPacketListener(new LastActivityListener(), filter);
432 filter = new PacketTypeFilter(Time.class);
433 connection.addPacketListener(new TimeListener(), filter);
434 connection.addPacketListener(new IQPacketListener(), filter);
435 ftmanager = new FileTransferManager(connection);
436 ftmanager.addFileTransferListener(new FTReceiveListener());
437
438 exchangeManager = new RosterExchangeManager(connection);
439 exchangeManager.addRosterListener(new ExchangeListener());
440
441 // this filter will listen to three types of messages:
442 // <si>, <streamhost> and <streamhost-used>
443 /*filter = new OrFilter(new PacketTypeFilter(Streamhost.class),
444 new PacketTypeFilter(StreamhostUsed.class));
445 OrFilter filter2 = new OrFilter(new PacketTypeFilter(
446 StreamInitiation.class), filter);
447 connection.addPacketListener( new StreamInitiationListener(),
448 filter2 );*/
449
450 // attempts to connect
451 try {
452 connection.login(username, password, resource);
453
454 //SmackConfiguration.setPacketReplyTimeout(5000);
455
456 roster = connection.getRoster();
457
458 roster.setSubscriptionMode(
459 Roster.SUBSCRIPTION_MANUAL);
460 roster.addRosterListener( rosterListener );
461
462 eventManager = new MessageEventManager(connection);
463
464 eventManager
465 .addMessageEventNotificationListener(new EventNotificationListener());
466 eventManager
467 .addMessageEventRequestListener(new EventRequestListener());
468 MultiUserChat.addInvitationListener(connection,
469 new InvitationPacketListener());
470 } catch (XMPPException e) {
471 errorMessage = e.getMessage();
472 if (e.getXMPPError() != null) {
473 errorMessage = resources.getString("xmppError"
474 + e.getXMPPError().getCode());
475 }
476 }
477 }
478
479 // if there was an error, display it, and then redisplay a LoginDialog
480 if (errorMessage != null || connection == null) {
481 resetCredentials();
482
483 SwingUtilities.invokeLater( new Runnable()
484 {
485 public void run()
486 {
487 BuddyList.getInstance().getStatusMenu()
488 .setModeChecked(null);
489 }
490 } );
491
492 if (connection != null) {
493 connection.removeConnectionListener(conListener);
494 }
495
496 if (persistent) {
497 try {
498 Thread.sleep(connectCount*5000);
499 } catch (InterruptedException ex) {
500 com.valhalla.Logger.logException(ex);
501 }
502 com.valhalla.Logger.debug("Connection error was: "
503 + errorMessage);
504 errorMessage = null;
505
506 if (cancelled) {
507 cancelled = false;
508
509 BuddyList.getInstance().getStatusMenu()
510 .stopBlinkTimer();
511 connectCount = 0;
512 return;
513 }
514
515 messageListener.resetQueue();
516 com.valhalla.Logger.debug( "Retrying, attempt #" + connectCount );
517 connectCount++;
518
519 run();
520 return;
521 }
522
523 password = Settings.getInstance().getProperty("password");
524
525 BuddyList.getInstance().getStatusMenu()
526 .stopBlinkTimer();
527
528 if (errorMessage == null) {
529 errorMessage = resources.getString("connectionError");
530 }
531 if (errorMessage.equals("Unauthorized")) {
532 errorMessage = new String(resources
533 .getString("invalidPassword"));
534 }
535
536 Standard.warningMessage(null, resources
537 .getString("couldNotConnect"), errorMessage);
538 BuddyList.getInstance().init(null);
539
540 return;
541 }
542
543 SwingUtilities.invokeLater( new Runnable() {
544 public void run()
545 {
546 BuddyList.getInstance().getStatusMenu().stopBlinkTimer();
547 BuddyList.getInstance().getBuddiesMenu().logOn();
548
549 // otherwise, set up and display the buddy list
550 com.valhalla.Logger.debug("Connected");
551
552 BuddyList.getInstance().resetAwayTimer();
553
554 BuddyList.getInstance().getStatusMenu().setModeChecked(
555 connectMode);
556 BuddyList.getInstance().initBuddies();
557
558 // display the buddies
559 BuddyList.getInstance().setStatus(connectMode, statusString, false);
560 messageListener.startTimer();
561
562 if (away) {
563 BuddyList.getInstance().getAwayHandler().actionPerformed(
564 new ActionEvent(BuddyList.getInstance(), 1, "away"));
565
566 }
567 }
568 } );
569
570 return;
571 }
572 }
573