/*
* Copyright 2006-2010 Daniel Henninger. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package net.sf.kraken.protocols.yahoo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.sf.kraken.pseudoroster.PseudoRoster;
import net.sf.kraken.pseudoroster.PseudoRosterItem;
import net.sf.kraken.pseudoroster.PseudoRosterManager;
import net.sf.kraken.registration.Registration;
import net.sf.kraken.session.TransportSession;
import net.sf.kraken.type.*;
import org.apache.log4j.Logger;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.NotFoundException;
import org.openymsg.network.AccountLockedException;
import org.openymsg.network.AuthenticationState;
import org.openymsg.network.DirectConnectionHandler;
import org.openymsg.network.FailedLoginException;
import org.openymsg.network.LoginRefusedException;
import org.openymsg.network.Session;
import org.openymsg.network.Status;
import org.openymsg.network.YahooProtocol;
import org.openymsg.network.YahooUser;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
/**
* Represents a Yahoo session.
*
* This is the interface with which the base transport functionality will
* communicate with Yahoo.
*
* @author Daniel Henninger
* Heavily inspired by Noah Campbell's work.
*/
public class YahooSession extends TransportSession<YahooBuddy> {
/**
* Yahoo requires every contact to be in at least one group. If no groups
* are supplied by XMPP, we'll add the user to a group with this name.
*/
public static final String DEFAULT_GROUPNAME = "Friends";
static Logger Log = Logger.getLogger(YahooSession.class);
/**
* Create a Yahoo Session instance.
*
* @param registration Registration informationed used for logging in.
* @param jid JID associated with this session.
* @param transport Transport instance associated with this session
* @param priority Priority of this session
*/
public YahooSession(Registration registration, JID jid, YahooTransport transport, Integer priority) {
super(registration, jid, transport, priority);
setSupportedFeature(SupportedFeature.attention);
setSupportedFeature(SupportedFeature.chatstates);
pseudoRoster = PseudoRosterManager.getInstance().getPseudoRoster(registration);
}
/**
* Our pseudo roster.
*
* We only really use it for nickname tracking.
*/
private PseudoRoster pseudoRoster;
public PseudoRoster getPseudoRoster() {
return pseudoRoster;
}
/**
* Run thread.
*/
private Thread runThread;
/**
* Yahoo session
*/
private Session yahooSession;
/**
* Yahoo session listener.
*/
private YahooListener yahooListener;
/**
* @see net.sf.kraken.session.TransportSession#logIn(net.sf.kraken.type.PresenceType, String)
*/
@Override
public void logIn(PresenceType presenceType, String verboseStatus) {
setPendingPresenceAndStatus(presenceType, verboseStatus);
if (!isLoggedIn()) {
yahooSession = new Session(new DirectConnectionHandler(
JiveGlobals.getProperty("plugin.gateway.yahoo.connecthost", "scs.msg.yahoo.com"),
JiveGlobals.getIntProperty("plugin.gateway.yahoo.connectport", 5050)
));
yahooListener = new YahooListener(this);
yahooSession.addSessionListener(yahooListener);
runThread = new Thread() {
@Override
public void run() {
try {
yahooSession.setStatus(Status.AVAILABLE);
yahooSession.login(registration.getUsername(), registration.getPassword());
setLoginStatus(TransportLoginStatus.LOGGED_IN);
// yahooSession.setStatus(((YahooTransport)getTransport()).convertXMPPStatusToYahoo(getPresence()));
//
// syncUsers();
}
catch (FailedLoginException e) {
yahooSession.reset();
String reason = LocaleUtils.getLocalizedString("gateway.yahoo.loginrefused", "kraken");
Log.debug("Yahoo login failure for "+getJID()+": "+reason);
getTransport().sendMessage(
getJID(),
getTransport().getJID(),
reason,
Message.Type.error
);
setLoginStatus(TransportLoginStatus.LOGGED_OUT);
setFailureStatus(ConnectionFailureReason.CAN_NOT_CONNECT);
sessionDisconnected(reason);
}
catch (LoginRefusedException e) {
yahooSession.reset();
String reason = LocaleUtils.getLocalizedString("gateway.yahoo.loginrefused", "kraken");
AuthenticationState state = e.getStatus();
if (state == AuthenticationState.BADUSERNAME) {
reason = LocaleUtils.getLocalizedString("gateway.yahoo.unknownuser", "kraken");
setFailureStatus(ConnectionFailureReason.USERNAME_OR_PASSWORD_INCORRECT);
sessionDisconnectedNoReconnect(reason);
}
else if (state == AuthenticationState.BAD) {
reason = LocaleUtils.getLocalizedString("gateway.yahoo.badpassword", "kraken");
setFailureStatus(ConnectionFailureReason.USERNAME_OR_PASSWORD_INCORRECT);
sessionDisconnectedNoReconnect(reason);
}
else if (state == AuthenticationState.LOCKED) {
AccountLockedException e2 = (AccountLockedException)e;
if(e2.getWebPage() != null) {
reason = LocaleUtils.getLocalizedString("gateway.yahoo.accountlockedwithurl", "kraken", Arrays.asList(e2.getWebPage().toString()));
}
else {
reason = LocaleUtils.getLocalizedString("gateway.yahoo.accountlocked", "kraken");
}
setFailureStatus(ConnectionFailureReason.LOCKED_OUT);
sessionDisconnectedNoReconnect(reason);
}
Log.debug("Yahoo login refused for "+getJID()+": "+reason);
getTransport().sendMessage(
getJID(),
getTransport().getJID(),
reason,
Message.Type.error
);
setLoginStatus(TransportLoginStatus.LOGGED_OUT);
}
catch (IOException e) {
Log.debug("Yahoo login caused IO exception:", e);
getTransport().sendMessage(
getJID(),
getTransport().getJID(),
LocaleUtils.getLocalizedString("gateway.yahoo.unknownerror", "kraken"),
Message.Type.error
);
setLoginStatus(TransportLoginStatus.LOGGED_OUT);
setFailureStatus(ConnectionFailureReason.CAN_NOT_CONNECT);
sessionDisconnected(LocaleUtils.getLocalizedString("gateway.yahoo.unknownerror", "kraken"));
}
}
};
runThread.start();
}
}
/**
* @see net.sf.kraken.session.TransportSession#logOut()
*/
@Override
public void logOut() {
cleanUp();
sessionDisconnectedNoReconnect(null);
}
/**
* @see net.sf.kraken.session.TransportSession#cleanUp()
*/
@Override
public void cleanUp() {
if (yahooSession != null) {
if (yahooListener != null) {
yahooSession.removeSessionListener(yahooListener);
yahooListener = null;
}
try {
yahooSession.logout();
}
catch (IOException e) {
Log.debug("Failed to log out from Yahoo.");
}
catch (IllegalStateException e) {
// Not logged in, well then no problem.
}
try {
yahooSession.reset();
}
catch (Exception e) {
// If this fails it's ok, move on
}
yahooSession = null;
}
if (runThread != null) {
try {
runThread.interrupt();
}
catch (Exception e) {
// Ignore
}
runThread = null;
}
}
/**
* Syncs up the yahoo roster with the jabber roster.
*/
public void syncUsers() {
// Run through the entire list of users and set up our sync group.
for (Object userObj : yahooSession.getRoster().toArray()) {
YahooUser user = (YahooUser)userObj;
PseudoRosterItem rosterItem = pseudoRoster.getItem(user.getId());
String nickname = null;
if (rosterItem != null) {
nickname = rosterItem.getNickname();
}
if (nickname == null) {
nickname = user.getId();
}
getBuddyManager().storeBuddy(new YahooBuddy(this.getBuddyManager(), user, nickname, user.getGroupIds(), rosterItem));
}
// Lets try the actual sync.
try {
getTransport().syncLegacyRoster(getJID(), getBuddyManager().getBuddies());
}
catch (UserNotFoundException e) {
Log.debug("Unable to sync yahoo contact list for " + getJID());
}
getBuddyManager().activate();
}
/**
* @see net.sf.kraken.session.TransportSession#addContact(org.xmpp.packet.JID, String, java.util.ArrayList)
*/
@Override
public void addContact(JID jid, String nickname, ArrayList<String> groups) {
// OpenYMSG requires a user to be in at least one group.
if (groups == null ) {
groups = new ArrayList<String>();
}
if (groups.isEmpty()) {
// add the default Yahoo group
groups.add(DEFAULT_GROUPNAME);
}
// Syncing will take care of add.
String contact = getTransport().convertJIDToID(jid);
PseudoRosterItem rosterItem;
if (pseudoRoster.hasItem(contact)) {
rosterItem = pseudoRoster.getItem(contact);
rosterItem.setNickname(nickname);
}
else {
rosterItem = pseudoRoster.createItem(contact, nickname, groups);
}
YahooUser yUser = new YahooUser(contact);
for (String grp : groups) {
yUser.addGroupId(grp);
}
yahooSession.getRoster().add(yUser);
YahooBuddy yBuddy = new YahooBuddy(getBuddyManager(), yUser, nickname, groups, rosterItem);
getBuddyManager().storeBuddy(yBuddy);
}
/**
* @see net.sf.kraken.session.TransportSession#removeContact(net.sf.kraken.roster.TransportBuddy)
*/
@Override
public void removeContact(YahooBuddy contact) {
String yahooContact = getTransport().convertJIDToID(contact.getJID());
yahooSession.getRoster().remove((contact).yahooUser);
pseudoRoster.removeItem(yahooContact);
}
/**
* @see net.sf.kraken.session.TransportSession#updateContact(net.sf.kraken.roster.TransportBuddy)
*/
@Override
public void updateContact(YahooBuddy contact) {
// Yahoo requires each user to be in at least one group.
if (contact.getGroups() == null || contact.getGroups().isEmpty()) {
List<String> defaultGroup = new ArrayList<String>();
defaultGroup.add(DEFAULT_GROUPNAME);
contact.setGroups(defaultGroup);
}
String yahooContact = getTransport().convertJIDToID(contact.getJID());
PseudoRosterItem rosterItem;
if (pseudoRoster.hasItem(yahooContact)) {
rosterItem = pseudoRoster.getItem(yahooContact);
rosterItem.setNickname(contact.getNickname());
}
else {
rosterItem = pseudoRoster.createItem(yahooContact, contact.getNickname(), null);
}
try {
YahooBuddy yBuddy = getBuddyManager().getBuddy(contact.getJID());
yBuddy.pseudoRosterItem = rosterItem;
for (String newGroup : yBuddy.getGroups()) {
if (!yBuddy.yahooUser.getGroupIds().contains(newGroup)) {
// Add new group to user
yBuddy.yahooUser.addGroupId(newGroup);
}
}
for (String oldGroup : yBuddy.yahooUser.getGroupIds()) {
if (!yBuddy.getGroups().contains(oldGroup)) {
// Remove group from user
// TODO: This needs to be implemented...
//yBuddy.yahooUser.removeGroupId(oldGroup);
}
}
}
catch (NotFoundException e) {
Log.debug("Yahoo: Updated buddy not found in buddy manager: "+yahooContact);
}
}
/**
* @see net.sf.kraken.session.TransportSession#acceptAddContact(JID)
*/
@Override
public void acceptAddContact(JID jid) {
final String userID = getTransport().convertJIDToID(jid);
Log.debug("Yahoo: accept add contact " + userID);
try {
yahooSession.acceptFriendAuthorization(userID, YahooProtocol.YAHOO);
} catch (IOException e) {
Log.debug("Yahoo: Failed to accept add contact request.");
}
}
/**
* @see net.sf.kraken.session.TransportSession#sendMessage(org.xmpp.packet.JID, String)
*/
@Override
public void sendMessage(JID jid, String message) {
try {
yahooSession.sendMessage(getTransport().convertJIDToID(jid), message);
}
catch (IOException e) {
Log.debug("Failed to send message to yahoo user.");
}
}
/**
* @see net.sf.kraken.session.TransportSession#sendChatState(org.xmpp.packet.JID,net.sf.kraken.type.ChatStateType)
*/
@Override
public void sendChatState(JID jid, ChatStateType chatState) {
try {
if (chatState.equals(ChatStateType.composing)) {
yahooSession.sendTypingNotification(getTransport().convertJIDToID(jid), true);
}
else {
yahooSession.sendTypingNotification(getTransport().convertJIDToID(jid), false);
}
}
catch (IOException e) {
Log.debug("Failed to send typing notification to yahoo user.");
}
}
/**
* @see net.sf.kraken.session.TransportSession#sendBuzzNotification(org.xmpp.packet.JID, String)
*/
@Override
public void sendBuzzNotification(JID jid, String message) {
try {
yahooSession.sendBuzz(getTransport().convertJIDToID(jid));
}
catch (IOException e) {
Log.debug("Failed to send buzz notification to yahoo user.");
}
}
/**
* @see net.sf.kraken.session.TransportSession#updateLegacyAvatar(String, byte[])
*/
@Override
public void updateLegacyAvatar(String type, byte[] data) {
}
/**
* @see net.sf.kraken.session.TransportSession#updateStatus(net.sf.kraken.type.PresenceType, String)
*/
@Override
public void updateStatus(PresenceType presenceType, String verboseStatus) {
try {
if (isLoggedIn()) {
yahooSession.setStatus(((YahooTransport)getTransport()).convertXMPPStatusToYahoo(presenceType));
setPresenceAndStatus(presenceType, verboseStatus);
}
else {
// TODO: Should we consider auto-logging back in?
}
}
catch (Exception e) {
Log.debug("Unable to set Yahoo Status:", e);
}
}
/**
* Retrieve the actual Yahoo Session interface.
*/
public Session getYahooSession() {
return yahooSession;
}
}