/*
* Hamsam - Instant Messaging API
* Copyright (C) 2003 Raghu K
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package hamsam.protocol.msn;
import hamsam.api.Buddy;
import hamsam.api.Conference;
import hamsam.api.Message;
import hamsam.api.MessageComponent;
import hamsam.api.SmileyComponent;
import hamsam.api.TextComponent;
import hamsam.api.URLComponent;
import hamsam.exception.IllegalStateException;
import hamsam.net.ProxyInfo;
import java.awt.Color;
import java.awt.Font;
import java.io.IOException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A switchboard server is used for instant messaging. Both one to one messaging
* and conferences are carried out here.
*/
class SwitchboardServer extends MsnServer
{
/**
* Event processor for this server.
*/
private EventProcessor processor;
/**
* MsnProtocol instance that owns this server.
*/
private MsnProtocol protocol;
/**
* A conference object representing this switchboard session.
*/
private Conference conf;
/**
* Start a switchboard session for a specific conference. This constructor
* is used when the user logged in using Hamsam starts a new SB session.
*
* @param protocol the MsnProtocol instance that owns this server.
* @param processor the event processor for this server.
* @param hash the hash to be used to connect to this session.
* @param host the host name to connect to.
* @param port the TCP/IP port to connect to.
* @param conf the conference object that represents this SB session.
* @param info proxy information to be used for connections.
* @throws UnknownHostException if host is not known.
* @throws IOException if an I/O error occurs while connecting to the host.
* @throws IllegalStateException if info is not initialized properly.
*/
SwitchboardServer(MsnProtocol protocol, EventProcessor processor, String hash, String host, int port, Conference conf, ProxyInfo info) throws IOException, IllegalStateException
{
this(protocol, processor, host, port, info, conf);
String passport = conf.getHost().getUsername();
Command usr = new Command("USR");
usr.addParam(passport);
usr.addParam(hash);
sendToServer(usr, "processUSR");
}
/**
* Constructs a switchboard server with a specified event processor and proxy
* information. This constuctor is used when the user logged in using Hamsam joins
* a SB session initiated by another buddy.
*
* @param protocol the MsnProtocol instance that owns this server.
* @param processor the event processor for this server.
* @param host the host name to connect to.
* @param port the TCP/IP port to connect to.
* @param info proxy information to be used for connections.
* @param conf the conference object that represents this SB session.
* @throws UnknownHostException if host is not known.
* @throws IOException if an I/O error occurs while connecting to the host.
* @throws IllegalStateException if info is not initialized properly.
*/
SwitchboardServer(MsnProtocol protocol, EventProcessor processor, String host, int port, ProxyInfo info, Conference conf) throws java.io.IOException, hamsam.exception.IllegalStateException
{
super(host, port, info);
this.protocol = protocol;
this.processor = processor;
this.conf = conf;
}
/**
* Process the reply of the USR command.
*
* @param cmd the reply from the server.
*/
void processUSR(AbstractCommand cmd)
{
if(cmd instanceof Command)
{
if("USR".equals(cmd.getType()) && "OK".equals(cmd.getParam(0)))
{
Buddy[] buddies = conf.getParticipants();
for(int i = 0; i < buddies.length; i++)
{
String username = buddies[i].getUsername();
if(!username.equals(this.protocol.getUsername()))
{
Command cal = new Command("CAL");
cal.addParam(username);
sendToServer(cal, "doNothing");
}
}
}
}
}
/**
* This method does nothing. It used for callback where we don't have
* anything to do.
*/
void doNothing(AbstractCommand cmd)
{
}
/**
* Join the switchboard session by sending a ANS command.
*
* @param authString authentication string to be used for joining.
* @param sessionID session id to be used for joining
*/
void join(String authString, String sessionID)
{
Command ans = new Command("ANS");
ans.addParam(protocol.getUsername());
ans.addParam(authString);
ans.addParam(sessionID);
sendToServer(ans, "processANS");
}
/**
* Process the reply of the ANS command.
*
* @param cmd the reply from the server.
*/
void processANS(AbstractCommand cmd)
{
if(cmd instanceof Command)
{
String type = cmd.getType();
if("IRO".equals(type))
{
Buddy buddy = new Buddy(conf.getProtocol(), cmd.getParam(2));
buddy.setAlias(Util.urlDecode(cmd.getParam(3)));
conf.addParticipant(buddy);
}
else if("ANS".equals(type) && "OK".equals(cmd.getParam(0)))
{
Buddy[] buddies = conf.getParticipants();
for(int i = 0; i < buddies.length; i++)
processor.conferenceParticipantJoined(conf, buddies[i]);
// Add yourself to the conference
Buddy myself = new Buddy(protocol, protocol.getUsername());
conf.addParticipant(myself);
}
}
}
private static Pattern xMmsImFormatPattern = Pattern.compile("FN\\s*=\\s*([^;]*);\\s*EF\\s*=\\s*([^;]*);\\s*CO\\s*=\\s*([^;]*);");
/**
* Processes messages received from MSN server.
*
* @param msg the message received from server.
*/
protected synchronized void processMessage(MsnMessage msg)
{
String type = msg.getHeaderField("Content-Type");
if("text/plain".equals(type) || "text/plain; charset=UTF-8".equals(type))
{
// Get the font and color info from X-MMS-IM-Format header
Font font = new Font(null, Font.PLAIN, 10);
Color color = Color.black;
String fontAndColor = msg.getHeaderField("X-MMS-IM-Format");
if(fontAndColor != null)
{
Matcher m = xMmsImFormatPattern.matcher(fontAndColor);
if(m.lookingAt())
{
String fontName = m.group(1);
String effects = m.group(2);
String bgr = m.group(3);
if(fontName != null && !fontName.trim().equals(""))
font = new Font(Util.urlDecode(fontName), Font.PLAIN, 10);
if(effects != null && !effects.trim().equals(""))
{
int style = effects.indexOf('B') != -1 ? Font.BOLD : Font.PLAIN;
style |= effects.indexOf('I') != -1 ? Font.ITALIC : 0;
font = font.deriveFont(style);
}
if(bgr != null && !bgr.trim().equals(""))
{
int bgrVal = Integer.parseInt(bgr);
color = new Color(
bgrVal & 0xff, // red component
(bgrVal >>> 8) & 0xff, // green component
(bgrVal >>> 16) & 0xff // blue component
);
}
}
}
String body = msg.getBody();
if(body != null)
{
Message apiMessage = Util.parseMSNMessage(body, font, color);
String passport = msg.getParam(0);
String alias = msg.getParam(1);
Buddy buddy = new Buddy(protocol, passport);
buddy.setAlias(alias);
processor.conferenceMessageReceived(this.conf, buddy, apiMessage);
}
}
}
/**
* Processes asynchronous commands received from MSN server.
*
* @param cmd the command received from server.
*/
protected void processAsyncCommand(AbstractCommand cmd)
{
if(cmd instanceof Command)
{
String type = cmd.getType();
if("BYE".equals(type))
{
String passport = cmd.getParam(0);
if(passport != null)
{
Buddy buddy = new Buddy(protocol, passport);
conf.removeParticipant(buddy);
processor.conferenceParticipantLeft(conf, buddy);
if(conf.getParticipants().length == 1)
{
processor.conferenceClosed(conf);
try
{
protocol.quitConference(conf);
}
catch(IllegalStateException e)
{
// This will not be thrown, ever.
}
}
}
}
else if("JOI".equals(type))
{
String passport = cmd.getParam(0);
String alias = Util.urlDecode(cmd.getParam(1));
Buddy buddy = new Buddy(this.protocol, passport);
buddy.setAlias(alias);
this.conf.addParticipant(buddy);
processor.conferenceParticipantJoined(this.conf, buddy);
}
}
}
/**
* Send a message to this conference.
*
* @param message the message to be sent.
*/
void sendMessage(Message message)
{
MsnMessage mmsg = messageToMSNMessage(message);
sendToServer(mmsg, "doNothing");
}
/**
* Converts a Hamsam message to MSN Message.
*
* @param message the Hamsam message to be converted
* @return the equivalent MSN message
*/
private MsnMessage messageToMSNMessage(Message message)
{
Font font = null;
Color color = null;
StringBuffer buff = new StringBuffer();
Enumeration e = message.getComponents();
while(e.hasMoreElements())
{
MessageComponent comp = (MessageComponent) e.nextElement();
if(comp instanceof TextComponent)
{
TextComponent text = (TextComponent) comp;
if(font == null)
font = text.getFont();
if(color == null)
color = text.getColor();
buff.append(text.getSequence());
}
else if(comp instanceof SmileyComponent)
{
SmileyComponent smiley = (SmileyComponent) comp;
buff.append(smiley.getText());
}
else if(comp instanceof URLComponent)
{
URLComponent url = (URLComponent) comp;
buff.append(url.getLinkURL());
}
}
return new MsnMessage(font, color, buff.toString());
}
/**
* Check whether this is a one to one IM session.
*
* @param buddy the other buddy of the requested session.
* @return <code>true</code>, if this SB session is with the single buddy specified.
*/
boolean isIMSession(Buddy buddy)
{
Buddy[] buddies = conf.getParticipants();
if(buddies.length != 2)
return false;
else if(buddies[0].equals(buddy) || buddies[1].equals(buddy))
return true;
else
return false;
}
/**
* Invoked when the associated reader thread exits abnormally. This
* method shuts down the IM session.
*/
protected void readerExited()
{
shutdown();
protocol.switchBoardTerminated(this);
}
/**
* Invoked when the associated writer thread exits abnormally. This
* method shuts down the IM session.
*/
protected void writerExited()
{
shutdown();
protocol.switchBoardTerminated(this);
}
}