/*
* Copyright (c) 2002-2003, The Joust Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* - Neither the name of the Joust Project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* File created by keith @ Mar 26, 2003
*
*/
package edu.tufts.vue.collab.im;
import net.kano.joscar.BinaryTools;
import net.kano.joscar.ByteBlock;
import net.kano.joscar.OscarTools;
import net.kano.joscar.flap.FlapCommand;
import net.kano.joscar.flap.FlapPacketEvent;
import net.kano.joscar.flapcmd.LoginFlapCmd;
import net.kano.joscar.flapcmd.SnacCommand;
import net.kano.joscar.flapcmd.SnacPacket;
import net.kano.joscar.ratelim.RateLimitingQueueMgr;
import net.kano.joscar.rv.NewRvSessionEvent;
import net.kano.joscar.rv.RecvRvEvent;
import net.kano.joscar.rv.RvProcessor;
import net.kano.joscar.rv.RvProcessorListener;
import net.kano.joscar.rv.RvSession;
import net.kano.joscar.rv.RvSessionListener;
import net.kano.joscar.rv.RvSnacResponseEvent;
import net.kano.joscar.rvcmd.AbstractRequestRvCmd;
import net.kano.joscar.rvcmd.DefaultRvCommandFactory;
import net.kano.joscar.rvcmd.RvConnectionInfo;
import net.kano.joscar.rvcmd.addins.AddinsReqRvCmd;
import net.kano.joscar.rvcmd.chatinvite.ChatInvitationRvCmd;
import net.kano.joscar.rvcmd.directim.DirectIMReqRvCmd;
import net.kano.joscar.rvcmd.getfile.GetFileReqRvCmd;
import net.kano.joscar.rvcmd.icon.SendBuddyIconRvCmd;
import net.kano.joscar.rvcmd.sendbl.SendBuddyListRvCmd;
import net.kano.joscar.rvcmd.sendfile.FileSendReqRvCmd;
import net.kano.joscar.rvcmd.trillcrypt.AbstractTrillianCryptRvCmd;
import net.kano.joscar.snac.SnacPacketEvent;
import net.kano.joscar.snac.SnacRequest;
import net.kano.joscar.snac.SnacRequestListener;
import net.kano.joscar.snac.SnacResponseEvent;
import net.kano.joscar.snaccmd.CapabilityBlock;
import net.kano.joscar.snaccmd.ExtraInfoBlock;
import net.kano.joscar.snaccmd.ExtraInfoData;
import net.kano.joscar.snaccmd.FullUserInfo;
import net.kano.joscar.snaccmd.MiniUserInfo;
import net.kano.joscar.snaccmd.SnacFamilyInfoFactory;
import net.kano.joscar.snaccmd.buddy.BuddyOfflineCmd;
import net.kano.joscar.snaccmd.buddy.BuddyStatusCmd;
import net.kano.joscar.snaccmd.conn.ClientReadyCmd;
import net.kano.joscar.snaccmd.conn.ClientVersionsCmd;
import net.kano.joscar.snaccmd.conn.RateAck;
import net.kano.joscar.snaccmd.conn.RateChange;
import net.kano.joscar.snaccmd.conn.RateClassInfo;
import net.kano.joscar.snaccmd.conn.RateInfoCmd;
import net.kano.joscar.snaccmd.conn.RateInfoRequest;
import net.kano.joscar.snaccmd.conn.ServerReadyCmd;
import net.kano.joscar.snaccmd.conn.SnacFamilyInfo;
import net.kano.joscar.snaccmd.conn.WarningNotification;
import net.kano.joscar.snaccmd.icbm.InstantMessage;
import net.kano.joscar.snaccmd.icbm.RecvImIcbm;
import net.kano.joscar.snaccmd.icbm.RecvRvIcbm;
import net.kano.joscar.snaccmd.icbm.RvCommand;
import net.kano.joscar.snaccmd.icbm.RvResponse;
import net.kano.joscar.snaccmd.icbm.SendImIcbm;
import net.kano.joscar.snaccmd.rooms.RoomInfoReq;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.StringTokenizer;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import tufts.Util;
import tufts.vue.Actions;
import tufts.vue.DEBUG;
import tufts.vue.LWComponent;
import tufts.vue.LWMap;
import tufts.vue.LWNode;
import tufts.vue.LWSelection;
import tufts.vue.LayoutAction;
import tufts.vue.MapDropTarget;
import tufts.vue.VUE;
import tufts.vue.VueResources;
import tufts.vue.VueUtil;
import tufts.vue.NodeTool.NodeModeTool;
import edu.tufts.vue.collab.im.security.SecureSession;
import edu.tufts.vue.collab.im.security.SecureSessionException;
import edu.tufts.vue.layout.TabularLayout;
import edu.tufts.vue.metadata.MetadataList;
public abstract class BasicConn extends AbstractFlapConn {
private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(BasicConn.class);
protected final ByteBlock cookie;
protected boolean sentClientReady = false;
protected int[] snacFamilies = null;
protected SnacFamilyInfo[] snacFamilyInfos;
protected RateLimitingQueueMgr rateMgr = new RateLimitingQueueMgr();
public RvProcessor rvProcessor = new RvProcessor(snacProcessor);
protected RvProcessorListener rvListener = new RvProcessorListener() {
public void handleNewSession(NewRvSessionEvent event) {
System.out.println("new RV session: " + event.getSession());
event.getSession().addListener(rvSessionListener);
}
};
public Map trillianEncSessions = new HashMap();
public RvSessionListener rvSessionListener = new RvSessionListener() {
public void handleRv(RecvRvEvent event) {
RvCommand cmd = event.getRvCommand();
RvSession session = event.getRvSession();
SnacCommand snaccmd = event.getSnacCommand();
if (!(snaccmd instanceof RecvRvIcbm)) return;
RecvRvIcbm icbm = (RecvRvIcbm) snaccmd;
System.out.println("got rendezvous on session <" + session + ">");
System.out.println("- command: " + cmd);
if (cmd instanceof FileSendReqRvCmd) {
FileSendReqRvCmd rv = (FileSendReqRvCmd) cmd;
RvConnectionInfo connInfo = rv.getConnInfo();
InetAddress ip = connInfo.getExternalIP();
int port = connInfo.getPort();
if (ip != null && port != -1) {
System.out.println("starting ft thread..");
long cookie = icbm.getIcbmMessageId();
new RecvFileThread(tester, ip, port, session, cookie,
connInfo.isEncrypted()).start();
}
} else if (cmd instanceof AbstractTrillianCryptRvCmd) {
String key = OscarTools.normalize(session.getScreenname());
TrillianEncSession encSession = (TrillianEncSession)
trillianEncSessions.get(key);
if (encSession == null) {
encSession = new TrillianEncSession(session);
trillianEncSessions.put(key, encSession);
}
encSession.handleRv(event);
} else if (cmd instanceof DirectIMReqRvCmd) {
if (((DirectIMReqRvCmd) cmd).getRequestType()
== AbstractRequestRvCmd.REQTYPE_INITIALREQUEST) {
new DirectIMSession(tester.getScreenname(), session, event);
}
} else if (cmd instanceof SendBuddyIconRvCmd) {
/*
SendBuddyIconRvCmd iconCmd = (SendBuddyIconRvCmd) cmd;
ByteBlock iconData = iconCmd.getIconData();
if (iconData != null) {
try {
File dir = new File("buddy-icons");
if (!dir.isDirectory()) dir.mkdir();
File file = new File(dir, session.getScreenname()
+ ".icon");
System.out.println("writing icon to " + file + "!!");
FileOutputStream out = new FileOutputStream(file);
iconData.write(out);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
*/
} else if (cmd instanceof SendBuddyListRvCmd) {
session.sendResponse(RvResponse.CODE_NOT_ACCEPTING);
} else if (cmd instanceof GetFileReqRvCmd) {
if (((GetFileReqRvCmd) cmd).getCode() != -1) {
new HostGetFileThread(session, event).start();
}
} else if (cmd instanceof AddinsReqRvCmd) {
session.sendRv(cmd);
} else if (cmd instanceof ChatInvitationRvCmd) {
ChatInvitationRvCmd circ = (ChatInvitationRvCmd) cmd;
ByteBlock securityInfo = circ.getSecurityInfo();
if (securityInfo != null) {
String sn = icbm.getSender().getScreenname();
String cookie = circ.getRoomInfo().getCookie();
String roomName = OscarTools.getRoomNameFromCookie(cookie);
try {
SecureSession secureSession = tester.getSecureSession();
secureSession.setChatKey(roomName,
secureSession.extractChatKey(sn, securityInfo));
} catch (SecureSessionException e) {
e.printStackTrace();
}
}
tester.request(new RoomInfoReq(circ.getRoomInfo()));
}
}
public void handleSnacResponse(RvSnacResponseEvent event) {
System.out.println("got SNAC response for <"
+ event.getRvSession() + ">: "
+ event.getSnacCommand());
}
};
{ // init
snacProcessor.setSnacQueueManager(rateMgr);
rvProcessor.registerRvCmdFactory(new DefaultRvCommandFactory());
rvProcessor.addListener(rvListener);
}
public BasicConn(VUEAim tester, ByteBlock cookie) {
super(tester);
this.cookie = cookie;
}
public BasicConn(String host, int port, VUEAim tester,
ByteBlock cookie) {
super(host, port, tester);
this.cookie = cookie;
}
public BasicConn(InetAddress ip, int port, VUEAim tester,
ByteBlock cookie) {
super(ip, port, tester);
this.cookie = cookie;
}
protected DateFormat dateFormat
= DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.SHORT);
protected PrintWriter imLogger = null;
private static Properties senders = new Properties();
private static HashMap<String,Boolean> approvedSenders = new HashMap<String,Boolean>();
protected void handleFlapPacket(FlapPacketEvent e) {
FlapCommand cmd = e.getFlapCommand();
if (cmd instanceof LoginFlapCmd) {
getFlapProcessor().sendFlap(new LoginFlapCmd(cookie));
} else {
System.out.println("got FLAP command on channel 0x"
+ Integer.toHexString(e.getFlapPacket().getChannel())
+ ": " + cmd);
}
}
protected void resetApprovedContributors()
{
if (this.approvedSenders !=null)
{
approvedSenders.clear();
}
}
protected synchronized void handleSnacPacket(SnacPacketEvent e) {
SnacPacket packet = e.getSnacPacket();
System.out.println("got snac packet type "
+ Integer.toHexString(packet.getFamily()) + "/"
+ Integer.toHexString(packet.getCommand()) + ": "
+ e.getSnacCommand());
SnacCommand cmd = e.getSnacCommand();
if (cmd instanceof ServerReadyCmd) {
ServerReadyCmd src = (ServerReadyCmd) cmd;
setSnacFamilies(src.getSnacFamilies());
SnacFamilyInfo[] familyInfos = SnacFamilyInfoFactory
.getDefaultFamilyInfos(src.getSnacFamilies());
setSnacFamilyInfos(familyInfos);
tester.registerSnacFamilies(this);
request(new ClientVersionsCmd(familyInfos));
request(new RateInfoRequest());
} else if (cmd instanceof RecvImIcbm) {
RecvImIcbm icbm = (RecvImIcbm) cmd;
String sn = icbm.getSenderInfo().getScreenname();
InstantMessage message = icbm.getMessage();
String msg = null;
if (message.isEncrypted()) {
ByteBlock encData = message.getEncryptedData();
System.out.println("got [" + encData.getLength() + "]");
SecureSession secureSession = tester.getSecureSession();
if (secureSession.hasCert(sn)) {
try {
msg = secureSession.decodeEncryptedIM(sn, encData);
} catch (SecureSessionException e1) {
e1.printStackTrace();
}
} else {
System.out.println(sn + " tried sending an encrypted "
+ "message, but I don't have his/her certificate "
+ " - try typing 'getcertinfo " + sn + "'");
}
} else {
msg = OscarTools.stripHtml(message.getMessage());
// OldIconHashInfo iconInfo = icbm.getIconInfo();
//
// if (iconInfo != null) {
// System.out.println("(" + sn
// + " has a buddy icon: " + iconInfo + ")");
// }
// sendRequest(new SnacRequest(new SendImIcbm(sn, msg), null));
}
String str = dateFormat.format(new Date()) + " IM from "
+ sn + ": " + msg;
if (imLogger != null) {
imLogger.println(str);
}
if (msg != null) {
String encFlag = (message.isEncrypted() ? "**ENCRYPTED** " : "");
System.out.println(encFlag + "*" + sn + "* " + msg);
}
if (tester.requireApproval)
{
Boolean b = this.approvedSenders.get(sn);
if (b == null)
{
//get approval
int value = tufts.vue.VueUtil.confirm(VUE.getApplicationFrame(),
VueResources.getMessageString("im.approve.message",new Object[]{sn}),
VueResources.getString("im.approve.title"));
if (value == JOptionPane.YES_OPTION) {
approvedSenders.put(sn, new Boolean(true));
} else if (value == JOptionPane.NO_OPTION) {
approvedSenders.put(sn, new Boolean(false));
return;
}
}
else if (b.booleanValue() == false)
return;
//else do nothing.
}
if (!tester.ignoreIMs)
{
LWNode newNode = NodeModeTool.createNewNode(VueUtil.formatLines(OscarTools.stripHtml(msg), 30));
MetadataList mlist = new MetadataList();
mlist.add("submitted by", sn);
mlist.add("timestamp",now().toString());
if (msg.indexOf("?") > -1)
mlist.add("Type of Sentence", "question");
else if (msg.indexOf("!") > -1)
mlist.add("Type of Sentence", "exclamation");
else
mlist.add("Type of Sentence", "statement");
newNode.setMetadataList(mlist);
String c = senders.getProperty(sn);
Color color = null;
if (c == null)
{
color = randomColor();
senders.setProperty(sn, color.getRed() +","+color.getGreen()+","+color.getBlue());
}
else
{
StringTokenizer tokens = new StringTokenizer(c,",");
color = new Color(Integer.parseInt((String)tokens.nextElement()),Integer.parseInt((String)tokens.nextElement()),Integer.parseInt((String)tokens.nextElement()));
}
if (tester.assignColors)
newNode.setFillColor(color);
if (tester.forceNodeWidth)
{
newNode.setAutoSized(false);
newNode.setFrame(newNode.getX(),newNode.getY(),200,newNode.getHeight());
}
// VUE.getActiveMap().add(newNode);//
//getFocal().dropChild(newNode);
List<LWComponent> compList = new ArrayList<LWComponent>();
// VUE.getActiveViewer().getSelection().clear();
// VUE.getActiveViewer().getSelection().add(newNode);
Collection<LWComponent> collection = VUE.getActiveMap().getActiveLayer().getAllDescendents();
compList.addAll(collection);
compList.add(newNode);
if (collection.size() <= 1)
{
//if its a fresh map try not to let it go all the way off the creen
if (!(VUE.getActiveMap().getAllDescendents().size() < 1 && VUE.getActiveViewer().getLastMousePressMapPoint().getX()+200 > VUE.getActiveViewer().getWidth()))
newNode.setLocation(VUE.getActiveViewer().getLastMousePressMapPoint());
VUE.getActiveMap().addChild(newNode);
}
else
VUE.getActiveMap().dropChild(newNode);
//VUE.getActiveViewer().getSelection().setTo(compList);
if (!tester.makeTables)
{
if (compList.size() > 1)
LayoutAction.search.act(new LWSelection(compList),!tester.keepAt100);
MapDropTarget.makeRoomFor(newNode);
MapDropTarget.makeRoomFor(newNode);
}
else
{
if (compList.size() > 1)
{
TabularLayout.setIMLayout(true);
LayoutAction.table.act(new LWSelection(compList),!tester.keepAt100);
TabularLayout.setIMLayout(false);
}
}
}
} else if (cmd instanceof WarningNotification) {
WarningNotification wn = (WarningNotification) cmd;
MiniUserInfo warner = wn.getWarner();
if (warner == null) {
System.out.println("*** You were warned anonymously to "
+ wn.getNewLevel() + "%");
} else {
System.out.println("*** " + warner.getScreenname()
+ " warned you up to " + wn.getNewLevel() + "%");
}
} else if (cmd instanceof BuddyStatusCmd) {
BuddyStatusCmd bsc = (BuddyStatusCmd) cmd;
FullUserInfo info = bsc.getUserInfo();
String sn = info.getScreenname();
ExtraInfoBlock[] extraInfos = info.getExtraInfoBlocks();
if (extraInfos != null) {
for (int i = 0; i < extraInfos.length; i++) {
ExtraInfoBlock extraInfo = extraInfos[i];
ExtraInfoData data = extraInfo.getExtraData();
// if ((hashInfo.getFlags() & ExtraInfoData.FLAG_ICON_PRESENT)
// != 0) {
// System.out.println(sn +
// " has an icon! requesting it.. (excode="
// + iconInfos[i].getExtraCode() + ")");
// request(new IconRequest(sn, iconInfos[i]));
//
// break;
// }
if (extraInfo.getType() == ExtraInfoBlock.TYPE_AVAILMSG) {
ByteBlock msgBlock = data.getData();
int len = BinaryTools.getUShort(msgBlock, 0);
byte[] msgBytes = msgBlock.subBlock(2, len).toByteArray();
String msg;
try {
msg = new String(msgBytes, "UTF-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
return;
}
if (msg.length() > 0) {
System.out.println(info.getScreenname()
+ " availability: " + msg);
}
}
}
}
if (info.getCapabilityBlocks() != null) {
List known = Arrays.asList(new CapabilityBlock[] {
CapabilityBlock.BLOCK_CHAT,
CapabilityBlock.BLOCK_DIRECTIM,
CapabilityBlock.BLOCK_FILE_GET,
CapabilityBlock.BLOCK_FILE_SEND,
CapabilityBlock.BLOCK_GAMES,
CapabilityBlock.BLOCK_GAMES2,
CapabilityBlock.BLOCK_ICON,
CapabilityBlock.BLOCK_SENDBUDDYLIST,
CapabilityBlock.BLOCK_TRILLIANCRYPT,
CapabilityBlock.BLOCK_VOICE,
CapabilityBlock.BLOCK_ADDINS,
CapabilityBlock.BLOCK_ICQCOMPATIBLE,
CapabilityBlock.BLOCK_SOMETHING,
});
List caps = new ArrayList(Arrays.asList(
info.getCapabilityBlocks()));
caps.removeAll(known);
if (!caps.isEmpty()) {
System.out.println(sn + " has " + caps.size()
+ " unknown caps:");
for (Iterator it = caps.iterator(); it.hasNext();) {
System.out.println("- " + it.next());
}
}
/*
caps = new ArrayList(known);
caps.removeAll(Arrays.asList(info.getCapabilityBlocks()));
if (!caps.isEmpty()) {
System.out.println(sn + " is missing " + caps.size()
+ " caps:");
for (Iterator it = caps.iterator(); it.hasNext();) {
System.out.println("- " + it.next());
}
}
*/
}
} else if (cmd instanceof BuddyOfflineCmd) {
BuddyOfflineCmd boc = (BuddyOfflineCmd) cmd;
} else if (cmd instanceof RateChange) {
RateChange rc = (RateChange) cmd;
System.out.println("rate change: current avg is "
+ rc.getRateInfo().getCurrentAvg());
}
}
public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";
public static String now() {
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW);
return sdf.format(cal.getTime());
}
private static Random rand = new Random();
public Color randomColor()
{
return(new Color(rand.nextInt(256),
rand.nextInt(256),
rand.nextInt(256)));
}
protected void handleSnacResponse(SnacResponseEvent e) {
SnacPacket packet = e.getSnacPacket();
System.out.println("got snac response type "
+ Integer.toHexString(packet.getFamily()) + "/"
+ Integer.toHexString(packet.getCommand()) + ": "
+ e.getSnacCommand());
SnacCommand cmd = e.getSnacCommand();
if (cmd instanceof RateInfoCmd) {
RateInfoCmd ric = (RateInfoCmd) cmd;
RateClassInfo[] rateClasses = ric.getRateClassInfos();
int[] classes = new int[rateClasses.length];
for (int i = 0; i < rateClasses.length; i++) {
classes[i] = rateClasses[i].getRateClass();
// System.out.println("- " + rateClasses[i] + ": " + Arrays.asList(rateClasses[i].getCommands()));
}
request(new RateAck(classes));
}
}
public int[] getSnacFamilies() { return snacFamilies; }
protected void setSnacFamilies(int[] families) {
this.snacFamilies = (int[]) families.clone();
Arrays.sort(snacFamilies);
}
protected void setSnacFamilyInfos(SnacFamilyInfo[] infos) {
snacFamilyInfos = infos;
}
protected boolean supportsFamily(int family) {
return Arrays.binarySearch(snacFamilies, family) >= 0;
}
protected void clientReady() {
if (!sentClientReady) {
sentClientReady = true;
request(new ClientReadyCmd(snacFamilyInfos));
}
}
protected SnacRequest dispatchRequest(SnacCommand cmd) {
return dispatchRequest(cmd, null);
}
protected SnacRequest dispatchRequest(SnacCommand cmd,
SnacRequestListener listener) {
SnacRequest req = new SnacRequest(cmd, listener);
dispatchRequest(req);
return req;
}
protected void dispatchRequest(SnacRequest req) {
tester.handleRequest(req);
}
protected SnacRequest request(SnacCommand cmd,
SnacRequestListener listener) {
SnacRequest req = new SnacRequest(cmd, listener);
handleReq(req);
return req;
}
private void handleReq(SnacRequest request) {
int family = request.getCommand().getFamily();
if (snacFamilies == null || supportsFamily(family)) {
// this connection supports this snac, so we'll send it here
sendRequest(request);
} else {
tester.handleRequest(request);
}
}
}