/* * 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; } }