/*
* Gamers Own Instant Messenger
* Copyright (C) 2005-2006 Herbert Poul (kahless@sphene.net)
* http://goim.sphene.net
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package net.sphene.goim.rcp.xmpp;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sphene.goim.rcp.GOIMPlugin;
import net.sphene.goim.rcp.beans.GOIMAccount;
import net.sphene.goim.rcp.beans.GOIMGameItem;
import net.sphene.goim.rcp.beans.GOIMGameList;
import net.sphene.goim.rcp.beans.GOIMRosterEvent;
import net.sphene.goim.rcp.beans.GOIMRosterListener;
import net.sphene.goim.rcp.beans.GameStatusChangeEvent;
import net.sphene.goim.rcp.preferences.PreferenceConstants;
import net.sphene.goim.rcp.ui.wizard.authrequested.AuthRequestedWizardDialog;
import net.sphene.goim.rcp.xmpp.util.FileTransferUtil;
import net.sphene.goim.rcp.xmpp.util.MUCUtils;
import net.sphene.goim.rcp.xmpp.util.MUCUtils.MUCRoom;
import net.sphene.libs.SpheneListener;
import net.sphene.libs.SpheneListenerList;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.jivesoftware.smack.AccountManager;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.SSLXMPPConnection;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Registration;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.ServiceDiscoveryManager;
import org.jivesoftware.smackx.filetransfer.FileTransferListener;
import org.jivesoftware.smackx.filetransfer.FileTransferManager;
import org.jivesoftware.smackx.filetransfer.FileTransferRequest;
import org.jivesoftware.smackx.filetransfer.IncomingFileTransfer;
import org.jivesoftware.smackx.muc.InvitationListener;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jivesoftware.smackx.packet.Version;
public class XMPPManager {
protected GOIMAccount account;
protected XMPPConnection conn;
protected Roster roster;
protected Presence lastPresenceSent;
public SpheneListenerList<StatusChangedEvent,SpheneListener<StatusChangedEvent>> ownPresenceChanged = new SpheneListenerList<StatusChangedEvent,SpheneListener<StatusChangedEvent>>();
public SpheneListenerList<GOIMRosterEvent,GOIMRosterListener> rosterListener = new SpheneListenerList<GOIMRosterEvent,GOIMRosterListener>();
protected ServiceDiscoveryManager disco;
protected GOIMGameList gameList;
public XMPPManager(GOIMAccount account) {
this.account = account;
this.gameList = GOIMPlugin.getPreferenceObject(GOIMGameList.class);
}
public XMPPConnection getConnection() { return conn; }
public GOIMAccount getAccount() { return account; }
public void connect(final Presence.Type type, final Presence.Mode mode) {
new Thread() {
public void run() {
try {
XMPPConnection.DEBUG_ENABLED = GOIMPlugin.getDefault().getMyPreferenceStore().getBoolean(PreferenceConstants.P_ENABLE_SMACK_DEBUGGING);
ServiceDiscoveryManager.setIdentityName("GOIM " + Platform.getProduct().getDefiningBundle().getHeaders().get("Bundle-Version"));
Roster.setDefaultSubscriptionMode(Roster.SUBSCRIPTION_MANUAL);
//conn = new XMPPConnection(account.getServer());
if(conn != null) conn.close();
createConnection();
if(!conn.isConnected()) { System.out.println("Not connected ?!"); }
account.gotConnection();
MultiUserChat.addInvitationListener(conn,new InvitationListener() {
public void invitationReceived(final XMPPConnection conn, final String room, final String inviter, final String reason, final String password, final Message message) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if(window == null) window = PlatformUI.getWorkbench().getWorkbenchWindows()[0];
boolean join = MessageDialog.openQuestion(window.getShell(),inviter + " invites you to the MUC room " + room,inviter + " invites you to the MUC room " + room + ": '" + reason + "' - Accept invitation ?");
if(join) {
MUCUtils.joinMUCRoom(account,room,password);
} else {
MultiUserChat.decline(conn,room,inviter,"User rejected Invitation (Not asked for a reason)");
}
}
});
}
});
FileTransferManager fileTransferManager = new FileTransferManager(conn);
fileTransferManager.addFileTransferListener(new FileTransferListener() {
public void fileTransferRequest(final FileTransferRequest request) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
Shell parent = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
boolean accept = MessageDialog.openConfirm(parent,
"Incoming File Transfer",
"You are receiving an incoming file transfer request from " +
request.getRequestor() + ", who wants to send you the file: " +
request.getFileName() + " (" + request.getFileSize() + " Bytes):\n" +
request.getDescription() + "\n" + "Would you like to accept it?");
if(!accept) return;
FileDialog fileDialog = new FileDialog(parent,SWT.SINGLE|SWT.SAVE);
fileDialog.setFileName(request.getFileName());
fileDialog.setText("Select Destination to save received file.");
String file = fileDialog.open();
if(file == null) return;
IncomingFileTransfer fileTransfer = request.accept();
try {
fileTransfer.recieveFile(new File(file));
FileTransferUtil.showProgressMonitor(parent,fileTransfer,"Receiving file " + request.getFileName());
} catch (Exception e) {
throw new RuntimeException("Error while receiving file: " + e.getLocalizedMessage(),e);
}
}
});
} });
conn.addPacketListener(new PacketListener(){
public void processPacket(Packet packet) {
//System.out.println("Got Packet from " + packet.getFrom() + " : " + packet.getClass().toString() + " : " + packet.toString());
if(packet instanceof Message) {
account.chatWindowExtensionManager.handleMessage((Message)packet);
} else if(packet instanceof Presence) {
Presence presence = (Presence)packet;
if(presence.getType() == Presence.Type.SUBSCRIBE) {
askForSubscribePermission(presence);
}
} else if(packet instanceof Version) {
Version version = (Version)packet;
if(version.getType() == IQ.Type.GET) {
Version reply = new Version();
reply.setType(IQ.Type.RESULT);
reply.setTo(version.getFrom());
reply.setName("GOIM (Gamers Own Instant Messenger)");
reply.setVersion((String)Platform.getProduct().getDefiningBundle().getHeaders().get("Bundle-Version"));
reply.setOs(System.getProperty("os.name") + " " + System.getProperty("os.version") + " (Java: " + System.getProperty("java.vendor") + " " + System.getProperty("java.version") + ")");
conn.sendPacket(reply);
}
}
}},new AndFilter());
conn.addConnectionListener(new ConnectionListener() {
public void connectionClosed() {
}
public void connectionClosedOnError(final Exception e) {
conn = null;
Presence previousPresence = lastPresenceSent;
lastPresenceSent = null;
ownPresenceChanged.fireEvent(new StatusChangedEvent(this,previousPresence));
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
ErrorDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),"Error while logging in","Error during connection with jabber server: " + account.getServer() + e.getLocalizedMessage(),new Status(IStatus.ERROR,GOIMPlugin.ID,IStatus.OK,"Error while connected with account " + account.name,e));
}
});
}});
doLogin(type, mode);
} catch (final XMPPException e) {
e.printStackTrace();
final XMPPError error = e.getXMPPError();
if(false && error != null && error.getCode() == 401) {
// MessageBox box = new MessageBox(GOIMPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(),SWT.ICON_QUESTION | SWT.OK | SWT.CANCEL);
// box.setMessage("Could not log in .. (401 Unauthorized) Trying to register ?");
// int test = box.open();
// if(test != SWT.OK) return;
try {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
}});
tryRegister();
doLogin(type,mode);
} catch (XMPPException e1) {
e1.printStackTrace();
return;
}
} else {
if(conn != null)
conn.close();
conn = null;
lastPresenceSent = null;
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
ErrorDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),"Error while logging in","Error while trying to log into jabber server: " + account.getServer() + (error == null ? "" : ": " + error.getMessage()),new Status(IStatus.ERROR,GOIMPlugin.ID,IStatus.OK,"Error while logging into account " + account.name,e));
}
});
}
}
}
}.start();
}
public void tryRegister() throws XMPPException {
if(conn != null) conn.close();
createConnection();
AccountManager manager = new AccountManager(conn);
manager.createAccount(account.getUsername(),account.password);
conn.close();
conn = null;
}
public void createConnection() throws XMPPException {
if(account.useSSL)
conn = new SSLXMPPConnection(account.getServer(),account.getPort(),StringUtils.parseServer(account.jid));
else
conn = new XMPPConnection(account.getServer(),account.getPort(),StringUtils.parseServer(account.jid));
}
public IQ sendIQPacketAndWaitForReply(IQ reg) throws XMPPException {
PacketFilter filter = new AndFilter(new PacketIDFilter(reg
.getPacketID()), new PacketTypeFilter(IQ.class));
PacketCollector collector = conn
.createPacketCollector(filter);
conn.sendPacket(reg);
IQ result = (IQ) collector.nextResult(SmackConfiguration
.getPacketReplyTimeout());
// Stop queuing results
collector.cancel();
if (result == null) {
throw new XMPPException("No response from server.");
}
else if (result.getType() == IQ.Type.ERROR) {
throw new XMPPException(result.getError());
}
return result;
}
public Shell workbenchShell;
public void askForSubscribePermission(final Presence presence) {
if(StringUtils.parseBareAddress(presence.getFrom()).equals(GOIMAccount.STATSCONTACTJID)) {
Presence reply = new Presence(Presence.Type.SUBSCRIBED);
reply.setTo(presence.getFrom());
conn.sendPacket(reply);
Presence requestSubscribe = new Presence(Presence.Type.SUBSCRIBE);
requestSubscribe.setTo(presence.getFrom());
conn.sendPacket(requestSubscribe);
return;
}
if(workbenchShell == null) workbenchShell = account.getDefaultShell();
workbenchShell.getDisplay().asyncExec(new Runnable() {
public void run() {
new AuthRequestedWizardDialog(null,account,presence.getFrom()).open();
// MessageDialog dialog = new MessageDialog(
// workbenchShell,
// "Subscribe Request",
// null,
// presence.getFrom() + " is requesting subscribe authorization for " + account.jid + ".",
// MessageDialog.QUESTION,
// new String[] { "Decline", "Auth + Add" },
// 1);
// dialog.setBlockOnOpen(true);
// int r = dialog.open();
// Presence reply = null;
// if(r == 1)
// reply = new Presence(Presence.Type.SUBSCRIBED);
// else
// reply = new Presence(Presence.Type.UNSUBSCRIBED);
// if(r == 1) {
// ItemType itemType = getRoster().getEntry(StringUtils.parseBareAddress(presence.getFrom())).getType();
// if(itemType != ItemType.TO && itemType != ItemType.BOTH) {
// //getRoster().createEntry(presence.getFrom(),null,null);
// Presence requestSubscribe = new Presence(Presence.Type.SUBSCRIBE);
// requestSubscribe.setTo(presence.getFrom());
// conn.sendPacket(requestSubscribe);
// }
// }
// reply.setTo(presence.getFrom());
// conn.sendPacket(reply);
}
});
}
Presence presenceByUser = null;
public void changeStatus(Presence.Type type, Presence.Mode mode, boolean byuser) {
if(type == Presence.Type.UNAVAILABLE) {
Presence previousPresence = lastPresenceSent;
conn.close();conn = null;lastPresenceSent = null;roster = null;
ownPresenceChanged.fireEvent(new StatusChangedEvent(this,previousPresence));
rosterListener.fireEvent(new GOIMRosterEvent(XMPPManager.this,GOIMRosterEvent.TYPE_MODIFIED, null));
return;
}
Presence presence = new Presence(type);
presence.setMode(mode);
StringBuffer status = new StringBuffer();
boolean changestate = false;
String changeStateTo = GOIMPlugin.getDefault().getMyPreferenceStore().getString(PreferenceConstants.P_CHANGE_STATUS_TO_ON_GAME_LAUNCH);
for(GOIMGameItem game : gameList) {
String str = game.retrieveExtensionProxy().getStatusString(game);
PacketExtension ex = game.retrieveExtensionProxy().getStatusPacket(game);
if(str != null) status.append(str).append(" ");
if(ex != null) presence.addExtension(ex);
if(game.retrieveExtensionProxy().needsOnlineStatusChange(game))
changestate = true;
}
if(!byuser && (presenceByUser == null || presenceByUser.getMode() == Presence.Mode.AVAILABLE)) {
if(changestate) {
if(!changeStateTo.equals(""))
presence.setMode(Presence.Mode.fromString(changeStateTo));
} else {
presence.setMode(presenceByUser.getMode());
}
}
String statusMsg = status.toString();
if(!statusMsg.equals("")) statusMsg = "Currently Playing: " + statusMsg + " (visit http://goim.sphene.net)";
if(status.length() > 0) presence.setStatus(statusMsg);
presence.setPriority(account.resourcePriority);
conn.sendPacket(presence);
Presence previousPresence = lastPresenceSent;
lastPresenceSent = presence;
if(byuser) presenceByUser = presence;
ownPresenceChanged.fireEvent(new StatusChangedEvent(this,previousPresence));
}
public Presence getOwnPresence() { return lastPresenceSent; }
public Roster getRoster() { if(roster == null) return (conn == null ? null : conn.getRoster()); else return roster; }
public boolean isConnected() {
return conn != null && conn.isConnected();
}
public ServiceDiscoveryManager getDiscoManager() { return disco; }
private void doLogin(final Presence.Type type, final Presence.Mode mode) throws XMPPException {
conn.login(account.getUsername(),account.password,account.resource);
disco = ServiceDiscoveryManager.getInstanceFor(conn);
disco.addFeature("jabber:iq:version");
changeStatus(type,mode,true);
roster = conn.getRoster();
roster.addRosterListener(new RosterListener(){
public void rosterModified() {
// System.out.println("rosterModified()");
rosterListener.fireEvent(new GOIMRosterEvent(XMPPManager.this,GOIMRosterEvent.TYPE_MODIFIED,null));
}
public void presenceChanged(String jid) {
// System.out.println("Presence Changed: " + jid);
rosterListener.fireEvent(new GOIMRosterEvent(XMPPManager.this,GOIMRosterEvent.TYPE_PRESENCE_CHANGED,jid));
}
// TODO Find a better solution for this .. maybe forward all of the three events !
// (Since smack 2.1 the rosterModified() method is split up in the following three methods)
public void entriesAdded(Collection arg0) {
rosterModified();
}
public void entriesUpdated(Collection arg0) {
rosterModified();
}
public void entriesDeleted(Collection arg0) {
rosterModified();
}});
rosterListener.fireEvent(new GOIMRosterEvent(XMPPManager.this,GOIMRosterEvent.TYPE_MODIFIED,null));
GOIMPlugin.gameStatusChanged.addListener(new SpheneListener<GameStatusChangeEvent>() {
public void handleEvent(GameStatusChangeEvent event) {
changeStatus(lastPresenceSent.getType(),lastPresenceSent.getMode(),false);
}
});
List<MUCRoom> list = MUCUtils.getAutoJoinList(account);
if(list != null)
for(MUCRoom room : list) {
MUCUtils.joinMUCRoom(account,room);
}
}
public void removeContact(RosterEntry selectedEntry, Shell shell) {
if(!GOIMPlugin.getDefault().getMyPreferenceStore().getString(PreferenceConstants.CONTACTLIST_CONFIRM_REMOVE_ENTRY_TOGGLE).equals(MessageDialogWithToggle.ALWAYS)) {
MessageDialogWithToggle dialog = MessageDialogWithToggle.openOkCancelConfirm(
shell,
"Remove Entry",
"Are you sure you want to remove the entry " + (selectedEntry.getName() == null ? selectedEntry.getUser() : selectedEntry.getName()),
null,
false,
GOIMPlugin.getDefault().getMyPreferenceStore(),
PreferenceConstants.CONTACTLIST_CONFIRM_REMOVE_ENTRY_TOGGLE
);
if(dialog.getReturnCode() != IDialogConstants.OK_ID) return;
}
if(selectedEntry.getUser().indexOf('@') == -1) {
Registration reg = new Registration();
Map<String,String> map = new HashMap<String,String>();
map.put("remove","");
reg.setAttributes(map);
reg.setType(IQ.Type.SET);
reg.setTo(selectedEntry.getUser());
conn.sendPacket(reg);
}
try {
getRoster().removeEntry(selectedEntry);
} catch (XMPPException e) {
ErrorDialog.openError(shell,"Error while removing Entry","Error while removing entry: " + e.toString(),new Status(Status.ERROR,GOIMPlugin.ID,Status.OK,"Error while Removing Entry",e));
e.printStackTrace();
}
}
}