/*
This file is part of leafdigital leafChat.
leafChat 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 3 of the License, or
(at your option) any later version.
leafChat 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 leafChat. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012 Samuel Marshall.
*/
package com.leafdigital.ircui;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.*;
import org.w3c.dom.*;
import util.*;
import util.xml.*;
import com.leafdigital.irc.api.*;
import com.leafdigital.irc.api.Server.StatusPrefix;
import com.leafdigital.ircui.api.IRCActionListMsg;
import com.leafdigital.logs.api.Logger;
import com.leafdigital.ui.api.*;
import leafchat.core.api.*;
/**
* Chat windows based on an IRC server.
*/
public abstract class ServerChatWindow extends ChatWindow
{
private Server s;
private boolean connected=true;
/** Panel used to display 'away' button. */
public HorizontalPanel awayUI;
protected Server getServer() { return s; }
protected boolean isConnected() { return connected; }
int requestIDServerDisconnected=-1;
private final String
INTERNALACTION_RECONNECT="Reconnect",
INTERNALACTION_JOIN="Join";
/** Special class that changes the window title when the server name changes. */
public class TitleChanger
{
/**
* Server message: updates the title.
* @param msg Message
*/
public void msg(ServerMsg msg)
{
setTitle();
}
}
private Object oTitleChanger=new TitleChanger();
/** Class that handles the display or otherwise of the 'away' button. */
public class AwayHandler
{
/**
* IRC message: numeric (used for away, unaway).
* @param msg Message
*/
public void msg(NumericIRCMsg msg)
{
switch(msg.getNumeric())
{
case NumericIRCMsg.RPL_NOWAWAY:
awayUI.setVisible(true);
break;
case NumericIRCMsg.RPL_UNAWAY:
awayUI.setVisible(false);
break;
}
}
}
private Object awayHandler=new AwayHandler();
/**
* Constructs a server-based chat window.
* @param context Plugin context
* @param s Server
* @param xmlFile Name of xml file within ircui package, excluding ".xml"
* @param showNow If true, shows before exiting constructor
* @param visible If true, appears popped-up, otherwise minimised
* @throws GeneralException
*/
public ServerChatWindow(PluginContext context,Server s,String xmlFile,
boolean showNow,boolean visible) throws GeneralException
{
super(context, xmlFile, showNow, visible);
// Start listening and setup server
context.requestMessages(ServerConnectionFinishedMsg.class,this,null,Msg.PRIORITY_EARLY);
initServer(s,true);
// Redo title when server state changes
context.requestMessages(ServerMsg.class,oTitleChanger,null,Msg.PRIORITY_EARLY);
// Tell spare window it should close
if(!(this instanceof SpareWindow))
{
((IRCUIPlugin)context.getPlugin()).gotNonSpareWindow(s);
}
}
@Override
protected void internalAction(Element e) throws GeneralException
{
String text=XML.getText(e);
if(text.equals(INTERNALACTION_RECONNECT))
{
if(s.isConnected()) return;
if(getPluginContext().getSingle(UI.class).showQuestion(getWindow(),"Reconnect",
"You can reconnect to server "+oldServerHost+", port "+oldServerPort+".",
UI.BUTTON_YES|UI.BUTTON_CANCEL,"Reconnect",null,null,UI.BUTTON_YES)==
UI.BUTTON_YES)
{
((IRCUIPlugin)getPluginContext().getPlugin()).directConnect(
s.getReportedOrConnectedHost(),s.getConnectedPort());
}
}
if(text.equals(INTERNALACTION_JOIN))
{
if(!s.isConnected()) return;
try
{
String chan=XML.getText(XML.getChild(e.getParentNode(),"chan"));
s.sendLine(IRCMsg.constructBytes("JOIN "+chan));
}
catch(XMLException e1)
{
return;
}
}
}
/**
* Initialises the window to handle a particular server. Used on construct
* and if the server changes.
* @param s Server, or null if disconnect
* @param firstTime True if this is initial setup
*/
private void initServer(Server s,boolean firstTime)
{
if(s!=null) this.s=s;
// Unrequest existing messages.
if(requestIDServerDisconnected!=-1)
{
getPluginContext().unrequestMessages(ServerDisconnectedMsg.class,this,requestIDServerDisconnected);
requestIDServerDisconnected=-1;
}
getPluginContext().unrequestMessages(null,awayHandler,PluginContext.ALLREQUESTS);
// Request new ones
if(s!=null)
{
requestIDServerDisconnected=getPluginContext().requestMessages(
ServerDisconnectedMsg.class,this,new ServerFilter(s),Msg.PRIORITY_EARLY);
if(awayUI!=null) getPluginContext().requestMessages(
NumericIRCMsg.class,awayHandler,new ServerFilter(s),Msg.PRIORITY_EARLY);
}
// Set up other variables
connected=s!=null;
if(commandUI!=null) commandUI.setEnabled(s!=null);
if(!firstTime) initServerInner(s,false);
// Tell spare window it should close
if(s!=null && !(this instanceof SpareWindow))
{
((IRCUIPlugin)getPluginContext().getPlugin()).gotNonSpareWindow(s);
}
}
/**
* Overridable equivalent of initServer. Not called automatically on first
* run, so must be called from constructor of subclasses.
* @param s Server, or null if disconnect
* @param firstTime True if this is initial setup
*/
protected void initServerInner(Server s,boolean firstTime)
{
}
/** Should set window title. Called whenever server connections change. Default does nothing. */
protected void setTitle()
{
}
@Override
public void windowClosed() throws GeneralException
{
getPluginContext().unrequestMessages(null,oTitleChanger,PluginContext.ALLREQUESTS);
getPluginContext().unrequestMessages(null,awayHandler,PluginContext.ALLREQUESTS);
super.windowClosed();
}
private String oldServerHost;
private int oldServerPort;
/**
* Server message: disconnected. Used to display text.
* @param msg Message
* @throws GeneralException
*/
public void msg(ServerDisconnectedMsg msg) throws GeneralException
{
if(s == null)
{
return;
}
oldServerHost = s.getReportedOrConnectedHost();
oldServerPort = s.getConnectedPort();
boolean auto = ((IRCUIPlugin)getPluginContext().getPlugin()).considerAutoReconnect(msg);
if(auto)
{
addLine("[Disconnected] (<key>reconnecting automatically</key>)");
}
else
{
addLine("[Disconnected] (<internalaction>"+INTERNALACTION_RECONNECT+"</internalaction>)");
}
msg.markHandled();
initServer(null,false);
}
/**
* Server message: fully connected. Used to re-init with the new server.
* @param msg Message
* @throws GeneralException
*/
public void msg(ServerConnectionFinishedMsg msg) throws GeneralException
{
if(connected || s==null)
{
return;
}
// OK, if we're disconnected and reconnect to same server or network, set
// things up again
if(msg.getServer().getReportedOrConnectedHost().equals(
s.getReportedOrConnectedHost()))
{
addLine("[Reconnected]");
initServer(msg.getServer(),false);
}
else
{
String previousNetwork=s.getPreferences().getAnonParent().get(IRCPrefs.PREF_NETWORK,null);
if(previousNetwork!=null && previousNetwork.equals(
msg.getServer().getPreferences().getAnonParent().get(IRCPrefs.PREF_NETWORK,null)))
{
addLine("[Reconnected]");
initServer(msg.getServer(),false);
}
}
}
/** Handler for the special 'boxed' multi-line message display. */
public class CurrentBox implements Runnable
{
BoxedMessage bm;
NumericIRCMsg start,end;
List<NumericIRCMsg> middle = new LinkedList<NumericIRCMsg>();
long lastValid;
private int request=-1;
CurrentBox(NumericIRCMsg start,BoxedMessage bm)
{
if(bm.hasHeading())
this.start=start;
else
middle.add(start);
this.bm=bm;
if(!bm.isEndKnown())
{
// If the end is not known, we request all messages and stop after
// we get anything that doesn't match.
request=getPluginContext().requestMessages(
IRCMsg.class,this,new ServerFilter(getServer()),Msg.PRIORITY_FIRST);
// Also set a timer and end it 1 second after receiving the last
lastValid=System.currentTimeMillis();
TimeUtils.addTimedEvent(this,1000,true);
}
}
@Override
public void run()
{
if(System.currentTimeMillis() - lastValid > 1000)
endIt();
else
TimeUtils.addTimedEvent(this,1000,true);
}
/**
* Handles any IRC message to possibly cancel the box.
* @param msg Message
*/
public void msg(IRCMsg msg)
{
if(!(msg instanceof NumericIRCMsg) || !bm.matchesMiddle((NumericIRCMsg)msg))
endIt();
}
private void endIt()
{
if(currentBox==this)
{
bm.display(this,ServerChatWindow.this);
currentBox=null;
}
if(request!=-1)
getPluginContext().unrequestMessages(IRCMsg.class,this,request);
}
boolean check(NumericIRCMsg nim)
{
if(bm.matchesEnd(nim))
{
end=nim;
endIt();
return true;
}
else if(bm.matchesMiddle(nim))
{
lastValid=System.currentTimeMillis();
middle.add(nim);
return true;
}
else
{
endIt();
return false;
}
}
}
private CurrentBox currentBox=null;
private static class BoxedMessage
{
String tag;
int start=0,end=0;
int[] middle;
boolean isEndKnown()
{
return end!=0;
}
BoxedMessage(String tag,int start,int end)
{
this.tag=tag;
this.start=start;
this.end=end;
}
BoxedMessage(String tag,int[] middle)
{
this.tag=tag;
this.middle=middle;
}
boolean hasHeading()
{
return true;
}
boolean matchesStart(NumericIRCMsg nim)
{
if(middle!=null)
return matchesMiddle(nim);
else
return nim.getNumeric()==start;
}
boolean matchesEnd(NumericIRCMsg nim)
{
if(middle!=null)
return false;
else
return nim.getNumeric()==end;
}
boolean matchesMiddle(NumericIRCMsg nim)
{
if(middle!=null)
{
for(int i=0;i<middle.length;i++)
{
if(middle[i]==nim.getNumeric()) return true;
}
return false;
}
else
return true;
}
String getText(ServerChatWindow cw,NumericIRCMsg nim)
{
return "<line>"+esc(getNumericText(nim))+"</line>";
}
void display(CurrentBox cb,ServerChatWindow cw)
{
StringBuffer xml=new StringBuffer();
xml.append("<"+tag+"><box>");
if(hasHeading()) xml.append("<boxstart>"+getText(cw,cb.start).replaceAll("</?line>","")+"</boxstart>");
for(NumericIRCMsg numeric : cb.middle)
{
xml.append(getText(cw, numeric));
}
xml.append("</box></"+tag+">");
cw.addLine(xml.toString(),true,null,true);
}
}
private static class BoxedMessageWhois extends BoxedMessage
{
BoxedMessageWhois()
{
super("whois",NumericIRCMsg.RPL_WHOISUSER,NumericIRCMsg.RPL_ENDOFWHOIS);
}
@Override
String getText(ServerChatWindow cw,NumericIRCMsg m)
{
switch(m.getNumeric())
{
case NumericIRCMsg.RPL_WHOISUSER:
if(m.getParams().length==6)
{
IRCUserAddress ua=new IRCUserAddress(m.getParamISO(1),m.getParamISO(2),m.getParamISO(3));
return "<line><nick>"+esc(ua.getNick())+"</nick> ("+esc(ua.getUser())+"@"+
esc(ua.getHost())+") - "+esc(m.convertEncoding(m.getParams()[5]))+"</line>";
}
break;
case NumericIRCMsg.RPL_WHOISSERVER:
if(m.getParams().length==4)
{
return "<line>Connected to <key>"+esc(m.getParamISO(2))+"</key> - "+esc(m.getParamISO(3))+"</line>";
}
break;
case NumericIRCMsg.RPL_WHOISIDLE:
String value=null;
if(m.getParams().length>=4 && m.isParamInteger(2)) // RFC1459 standard version
{
long ms=1000L*Long.parseLong(m.getParamISO(2));
if(ms>60000)
value="<line>Idle since <key>"+displayTime(System.currentTimeMillis()-ms)+"</key> (<key>"+
StringUtils.displayMilliseconds(ms)+"</key>)</line>";
else
value="<line>Idle for <key>"+StringUtils.displayMilliseconds(ms)+"</key></line>";
}
if(m.getParams().length==5 && m.isParamInteger(3)) // Extended version with signon time
{
value+="<line>Signed on at <key>"+displayTime(Long.parseLong(m.getParamISO(3))*1000L)+"</key></line>";
}
if(value!=null) return value;
break;
case NumericIRCMsg.RPL_WHOISREGNICK: // Nonstandard, used on sorcery.net esper.net dal.net
if(m.getParams().length==3)
{
return "<line>"+esc(StringUtils.capitalise(m.getParamISO(2))).replaceAll(
"(identified|registered)","<key>$1</key>")+"</line>";
}
break;
case NumericIRCMsg.RPL_WHOISOPERATOR:
if(m.getParams().length==3)
{
return "<line>"+esc(StringUtils.capitalise(m.getParamISO(2))).replaceAll(
"(operator)","<key>$1</key>")+"</line>";
}
break;
case NumericIRCMsg.RPL_WHOISMASKED:
if(m.getParams().length==3)
{
return "<line>"+esc(StringUtils.capitalise(m.getParamISO(2))).replaceAll(
"([a-z0-9]+(\\.[a-z0-9]+)+)","<key>$1</key>")+"</line>";
}
break;
case NumericIRCMsg.RPL_WHOISHOST:
if(m.getParams().length==3)
{
return "<line>"+esc(StringUtils.capitalise(m.getParamISO(2))).replaceAll(
" ([^ ]+@[^ ]+) "," <key>$1</key> ")+"</line>";
}
break;
case NumericIRCMsg.RPL_WHOISCHANNELS:
if(m.getParams().length>=3)
{
StringBuffer sb=new StringBuffer("<line>In channels:");
StatusPrefix[] prefixes=cw.s.getPrefix();
for(int param=2;param<m.getParams().length;param++)
{
String chan=m.getParamISO(param);
if(chan.length()==0) continue;
char c=chan.charAt(0);
boolean found=false;
for(int prefix=0;prefix<prefixes.length;prefix++)
{
if(prefixes[prefix].getPrefix()==c)
found=true;
}
if(found)
{
sb.append(" "+esc(c+"")+"<chan>"+esc(chan.substring(1))+"</chan>");
}
else
{
sb.append(" <chan>"+esc(chan)+"</chan>");
}
}
sb.append("</line>");
return sb.toString();
}
break;
default:
// Nothing
break;
}
return super.getText(cw,m);
}
}
private static class BoxedMessageWho extends BoxedMessage
{
BoxedMessageWho()
{
super("who",NumericIRCMsg.RPL_WHOREPLY,NumericIRCMsg.RPL_ENDOFWHO);
}
@Override
boolean hasHeading()
{
return false;
}
@Override
String getText(ServerChatWindow cw,NumericIRCMsg m)
{
switch(m.getNumeric())
{
case NumericIRCMsg.RPL_WHOREPLY:
// <channel> <user> <host> <server> <nick> <H|G>[*][@|+] :<hopcount> <real name>
if(m.getParams().length>=7)
{
IRCUserAddress ua=new IRCUserAddress(m.getParamISO(5),m.getParamISO(2),m.getParamISO(3));
String last=m.convertEncoding(m.getParams()[7]);
String hopCount="?";
if(last.matches("[0-9]+ .*"))
{
hopCount=last.substring(0,last.indexOf(' '));
last=last.substring(last.indexOf(' ')+1);
}
String flags=m.getParamISO(6);
String away=flags.startsWith("H") ? "Here" : "Away";
flags=flags.substring(1);
String chanPart=esc(m.getParamISO(1));
if(chanPart.equals("*"))
chanPart="";
else
chanPart="<chan>"+chanPart+"</chan> ";
return "<line>"+chanPart+"<nick>"+esc(ua.getNick())+"</nick> ("+esc(ua.getUser())+"@"+
esc(ua.getHost())+") <key>"+away+"</key>, mode <key>"+XML.esc(flags)+"</key>, <key>"+hopCount+ "</key> hops - "+
esc(last)+"</line>";
}
break;
default:
// Nothing
break;
}
return super.getText(cw,m);
}
}
private static class BoxedMessageLusers extends BoxedMessage
{
BoxedMessageLusers()
{
super("lusers",new int[]
{
NumericIRCMsg.RPL_LUSERCHANNELS,
NumericIRCMsg.RPL_LUSERCLIENT,
NumericIRCMsg.RPL_LUSERME,
NumericIRCMsg.RPL_LUSEROP,
NumericIRCMsg.RPL_LUSERUNKNOWN ,
NumericIRCMsg.RPL_GLOBALUSERS,
NumericIRCMsg.RPL_LOCALUSERS,
NumericIRCMsg.RPL_STATSCONN
});
}
@Override
boolean hasHeading()
{
return false;
}
@Override
String getText(ServerChatWindow cw,NumericIRCMsg nim)
{
StringBuffer params=new StringBuffer();
int startParam=1;
// These two messages duplicate the numbers in the text, so
// for those, skip the numeric parameters
if(nim.getNumeric()==NumericIRCMsg.RPL_LOCALUSERS ||
nim.getNumeric()==NumericIRCMsg.RPL_GLOBALUSERS)
startParam=nim.getParams().length-1;
for(int param=startParam;param<nim.getParams().length;param++)
{
if(param!=startParam) params.append(' ');
params.append(nim.getParamISO(param));
}
return "<line>"+esc(params.toString()).replaceAll("(\\b[0-9]+\\b)","<key>$1</key>")+"</line>";
}
}
private static class BoxedMessageLinks extends BoxedMessage
{
private static Pattern REGEX_HOPCOUNT = Pattern.compile("([0-9]{1,9})(?: (.*))?");
BoxedMessageLinks()
{
super("links", NumericIRCMsg.RPL_LINKS, NumericIRCMsg.RPL_ENDOFLINKS);
}
@Override
boolean hasHeading()
{
return false;
}
@Override
String getText(ServerChatWindow cw, NumericIRCMsg msg)
{
int count = msg.getParams().length;
if( count >= 4)
{
String server = msg.getParamISO(1);
String countAndInfo = msg.convertEncoding(msg.getParams()[count-1]);
Matcher m = REGEX_HOPCOUNT.matcher(countAndInfo);
if(m.matches())
{
int hops = Integer.parseInt(m.group(1));
String info = m.group(2);
if(info == null)
{
info = "";
}
// Indent servers past 0 hops
StringBuffer out = new StringBuffer("<line>");
for(int i=0; i<hops; i++)
{
out.append(" ");
if(i == hops-1)
{
out.append("↳");
}
}
out.append("<server>" + esc(server) + "</server>");
if(info.length() > 0)
{
out.append(" " + esc(info));
}
out.append("</line>");
return out.toString();
}
}
return "";
}
}
private static final BoxedMessage[] BOXEDMESSAGES=
{
new BoxedMessage("motd",NumericIRCMsg.RPL_MOTDSTART,NumericIRCMsg.RPL_ENDOFMOTD),
new BoxedMessageWhois(),
new BoxedMessageLusers(),
new BoxedMessageWho(),
new BoxedMessageLinks()
};
/**
* Watch message. Displays info.
* @param msg Message
*/
public void handleWatchMsg(WatchMsg msg)
{
msg.markHandled();
boolean on=(msg instanceof OnWatchMsg);
String description = on
? (description=msg.isRealChange() ? "logged <key>on</key>" : "is <key>on</key>line")
: (description=msg.isRealChange() ? "logged <key>off</key>" : null);
if(description==null)
return; // Don't display those or it would be tedious on login
IRCUserAddress ua=msg.getUserAddress();
String host = ua.getHost().length()==0 ? ""
: "("+esc(ua.getUser())+"@"+esc(ua.getHost())+") ";
addLine(EVENTSYMBOL+"<nick>"+esc(msg.getNick())+"</nick> "+host+description);
}
/**
* Matches part of the reply to USERHOST messages, except that you have to
* add a space to the end before applying this (makes it simpler to handle
* different numbers of replies). Groups: 1=nick, 2=ircop *, 3=+/- away,
* 4=username, 5=host
*/
private final static Pattern RPL_USERHOST_REGEX = Pattern.compile(
"([^ *]+)(\\*)?=([+-])(?:([^@ ]+)@)?(\\S*)\\s+");
/**
* Unhandled message. Provides standard display for many message types.
* @param msg Message
* @throws GeneralException
*/
public void handleUnhandledMsg(IRCMsg msg) throws GeneralException
{
if(msg instanceof NumericIRCMsg)
{
NumericIRCMsg numeric=(NumericIRCMsg)msg;
if(currentBox==null)
{
for(int i=0;i<BOXEDMESSAGES.length;i++)
{
if(BOXEDMESSAGES[i].matchesStart(numeric))
{
currentBox=new CurrentBox(numeric,BOXEDMESSAGES[i]);
msg.markHandled();
return;
}
}
}
else
{
if(currentBox.check(numeric))
{
msg.markHandled();
return;
}
else // Try again
{
handleUnhandledMsg(msg);
return;
}
}
boolean done=false;
switch(numeric.getNumeric())
{
case NumericIRCMsg.RPL_WELCOME:
if(numeric.getParams().length>1)
{
addLine("<line>"+esc(numeric.getParamISO(1)).replaceAll(
"([^ !]+)!([^ @]+)@([^ ]+)","<key>$1</key>!<key>$2</key>@<key>$3</key>")+"</line>");
done=true;
}
break;
case NumericIRCMsg.RPL_YOURHOST:
if(numeric.getParams().length>1)
{
addLine("<line>"+esc(numeric.getParamISO(1)).replaceAll(
"host is ([^ ,]+)","host is <key>$1</key>").replaceAll(
"version ([^ ]+)$","version <key>$1</key>")+"</line>");
done=true;
}
break;
case NumericIRCMsg.RPL_CREATED:
if(numeric.getParams().length>1)
{
addLine("<line>"+esc(numeric.getParamISO(1)).replaceAll(
"was created (.+) at (.+)$","was created <key>$1</key> at <key>$2</key>")+"</line>");
done=true;
}
break;
case NumericIRCMsg.RPL_MYINFO: // Don't display, it's not very interesting
done=true;
break;
// Just display these
case NumericIRCMsg.RPL_NOWAWAY :
case NumericIRCMsg.RPL_UNAWAY :
addLine("<line>"+EVENTSYMBOL+esc(numeric.getParamISO(1))+"</line>");
done=true;
break;
case NumericIRCMsg.RPL_VERSION :
if(numeric.getParams().length > 2)
{
String version = numeric.getParamISO(1);
if(version.endsWith("."))
{
version = version.substring(0, version.length() - 1);
}
String extraVersion = "";
for(int i=3; i<numeric.getParams().length; i++)
{
if(extraVersion.length() != 0)
{
extraVersion += " ";
}
extraVersion += numeric.getParamISO(i);
}
addLine("<line><server>" + esc(numeric.getParamISO(2))
+ "</server> running server version <key>"
+ version + "</key> " + extraVersion + "</line>");
done = true;
}
break;
case NumericIRCMsg.RPL_TIME :
if(numeric.getParams().length > 1)
{
String server = esc(numeric.getParamISO(1));
String time = esc(numeric.getParamISO(2));
addLine("<line><server>" + server
+ "</server> " + time + "</line>");
done = true;
}
break;
case NumericIRCMsg.RPL_USERHOST:
if(numeric.getParams().length >= 2)
{
Matcher m = RPL_USERHOST_REGEX.matcher(numeric.getParamISO(1) + " ");
StringBuffer out = new StringBuffer("<line>" + EVENTSYMBOL + " ");
boolean first = true;
while(m.find())
{
if(first)
{
first = false;
}
else
{
out.append("; ");
}
out.append("<nick>" + esc(m.group(1)) + "</nick>");
if("*".equals(m.group(2)))
{
out.append(" (<key>IRC operator</key>)");
}
out.append(" <key>" + esc(m.group(4)) + "</key>@<key>"
+ esc(m.group(5)) + "</key>");
if(m.group(3).equals("-"))
{
out.append(" (away)");
}
else
{
out.append(" (present)");
}
}
if(first)
{
out.append("No result from /userhost");
}
out.append("</line>");
addLine(out.toString());
done = true;
}
break;
}
if(!done)
{
addLine("<unhandled>"+esc(getNumericText(numeric))+"</unhandled>");
}
}
else if(msg instanceof UserNoticeIRCMsg)
{
UserNoticeIRCMsg unim=(UserNoticeIRCMsg)msg;
IRCUserAddress ua=unim.getSourceUser();
String sXML="-<nick>"+esc(ua.getNick())+"</nick>- "+esc(unim.convertEncoding(unim.getText()));
addLine(sXML);
getPluginContext().getSingle(Logger.class).log(
getServer().getReportedOrConnectedHost(),Logger.CATEGORY_USER,ua.getNick(),"notice",sXML);
}
else if(msg instanceof ServerNoticeIRCMsg)
{
ServerNoticeIRCMsg snim=(ServerNoticeIRCMsg)msg;
addLine("-<server>"+esc(snim.getSourceServer())+"</server>- "+esc(IRCMsg.convertISO(snim.getText())));
}
else if(msg instanceof UserModeIRCMsg)
{
UserModeIRCMsg umim=(UserModeIRCMsg)msg;
addLine(EVENTSYMBOL+"<nick>"+esc(umim.getTargetNick())+"</nick> user mode set to: <key>"+esc(umim.getModes())+"</key>");
}
else if(msg instanceof NickIRCMsg)
{
NickIRCMsg nim=(NickIRCMsg)msg;
addLine(EVENTSYMBOL+"<nick>"+esc(nim.getSourceUser().getNick())+
"</nick> <nickchange>changes nick to</nickchange> <nick>"+esc(nim.getNewNick())+
"</nick>");
}
else if(msg instanceof InviteIRCMsg)
{
InviteIRCMsg iim=(InviteIRCMsg)msg;
addLine(EVENTSYMBOL+"<nick>"+esc(iim.getSourceUser().getNick())+
"</nick> ("+esc(iim.getSourceUser().getUser())+"@"+esc(iim.getSourceUser().getHost())+") " +
"invites you to join <chan>"+esc(iim.getChannel())+"</chan> (<internalaction>"+INTERNALACTION_JOIN+"</internalaction>)");
}
else if(msg instanceof UserCTCPRequestIRCMsg)
{
UserCTCPRequestIRCMsg request=(UserCTCPRequestIRCMsg)msg;
String extra="";
if(request.getText().length>0)
{
String text=request.convertEncoding(request.getText());
if(request.getRequest().equals("PING") && text.matches("[0-9]{1,10}"))
{
SimpleDateFormat sdf=new SimpleDateFormat("EEE, d MMMMM yyyy HH:mm:ss");
text=sdf.format(new Date(Long.parseLong(text)*1000));
}
extra=": "+esc(text);
}
addLine(EVENTSYMBOL+"<nick>"+esc(request.getSourceUser().getNick())+
"</nick> sent CTCP <key>"+request.getRequest()+"</key>"+extra);
}
else if(msg instanceof UserCTCPResponseIRCMsg)
{
UserCTCPResponseIRCMsg response=(UserCTCPResponseIRCMsg)msg;
String extra="";
if(response.getText().length>0)
{
String text=response.convertEncoding(response.getText());
if(response.getRequest().equals("PING") && text.matches("[0-9]{1,10}"))
{
long seconds=System.currentTimeMillis()/1000L - Long.parseLong(text);
text=seconds+(seconds==1 ? " second" : " seconds");
}
extra=": "+esc(text);
}
addLine(EVENTSYMBOL+"<nick>"+esc(response.getSourceUser().getNick())+
"</nick> responded to CTCP <key>"+response.getRequest()+"</key>"+extra);
}
else if(msg instanceof ErrorIRCMsg)
{
byte[][] params = msg.getParams();
addLine(EVENTSYMBOL + "<server>" + esc(msg.getServer().getReportedOrConnectedHost())
+ "</server> " + esc(msg.convertEncoding(params[params.length - 1])));
}
else
{
addLine("<unhandled>"+esc(msg.convertEncoding(msg.getLine()))+"</unhandled>");
}
msg.markHandled();
}
protected String ifMessage(IRCMsg m,byte[] abText)
{
if(abText==null) return "";
return ": "+m.convertEncoding(abText);
}
/**
* IRC message: quit. Displays quit.
* @param msg Message
* @throws GeneralException
*/
public void msg(QuitIRCMsg msg) throws GeneralException
{
// Don't check if it's been handled because we display in multiple places.
IRCUserAddress ua=msg.getSourceUser();
addLine(EVENTSYMBOL+"<nick>"+esc(ua.getNick())+"</nick> <quit>has quit IRC</quit>"+
esc(ifMessage(msg,msg.getMessage())),"quit");
msg.markHandled();
}
/**
* IRC message: nick. Displays nick.
* @param msg Message
* @throws GeneralException
*/
public void msg(NickIRCMsg msg) throws GeneralException
{
// DOn't check if it's been handled because we display in multiple places.
addLine(EVENTSYMBOL+"<nick>"+esc(msg.getSourceUser().getNick())+
"</nick> <nickchange>changes nick to</nickchange> <nick>"+esc(msg.getNewNick())+
"</nick>","nick");
msg.markHandled();
}
@Override
public void addItems(PopupMenu pm,Node n)
{
if(!connected) return;
String nick=null,chan=null;
if(n!=null)
{
while(n!=null && !(n instanceof Element))
{
n=n.getParentNode();
}
Element e=(Element)n;
if(e.getTagName().equals("nick"))
{
nick=XML.getText(e);
}
else if(e.getTagName().equals("chan"))
{
chan=XML.getText(e);
}
}
IRCActionListMsg menuContext=new IRCActionListMsg(s,getContextChannel(),getContextNick(),
chan,nick!=null ? new String[] {nick} : null);
((IRCUIPlugin)getPluginContext().getPlugin()).getActionListOwner().fillMenu(menuContext,pm);
}
@Override
protected String getOwnNick()
{
String ourNick = s==null ? null : s.getOurNick();
return ourNick == null ? "[Not connected]" : ourNick;
}
private static String getNumericText(NumericIRCMsg nim)
{
StringBuffer display=new StringBuffer();
for(int i=1;i<nim.getParams().length;i++)
{
if(i>1) display.append(" ");
display.append(nim.getParamISO(i));
}
return display.toString();
}
@Override
protected String getLogSource()
{
return getServer().getReportedOrConnectedHost();
}
/**
* Callback: User clicks Away button.
* @throws GeneralException
*/
@UIAction
public void actionAway() throws GeneralException
{
Commands c=getPluginContext().getSingle(Commands.class);
doCommand(c,"/away");
}
}