/* Wirc is an IRC client written in Java, designed to be easily */ /* implemented as an application or an applet. */ /* */ /* Copyright (C) 1999 Michael Stern. stern@wheels.org. */ /* 222 West 23rd Street, #507, New York City 10011 */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of version 2 of the GNU General Public License as */ /* published by the Free Software Foundation. */ /* */ /* This program 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 this program; if not, write to the Free Software */ /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; // fun with JDK 1.1 import java.applet.*; public class wirc extends Applet implements ActionListener,KeyListener { static final int DEFAULT_PORT = 6667; static final String DEFAULT_HOST = "irc.sprynet.com"; static final String DEFAULT_NICK = "Stern_a"; static final String DEFAULT_USERNAME = "stern"; // username of the user on the client static final String DEFAULT_REALNAME = "Michael J. Stern"; static final String DEFAULT_CHANNEL = "#macintosh"; static Socket socket = null; static outgoingMesg sout = null; static MsgReader msgReader = null; static BufferedReader sin = null; static int port = DEFAULT_PORT; static String host = DEFAULT_HOST; // used to be null static String password = null; static String nickname = DEFAULT_NICK; static String username = DEFAULT_USERNAME; static String realname = DEFAULT_REALNAME; static String channel = DEFAULT_CHANNEL; static void Usage () { System.out.println (" Usage: "); System.out.println (" java wirc "); System.exit(0); } static void closeall() { try { if (wirc.sout != null) wirc.sout.close(); if (wirc.socket != null) wirc.socket.close(); } catch (IOException e1) { } } public static void main (String[] args) { if (args.length != 3) { // System.out.println("you gave me " + args.length + " args.\n"); Usage (); } host = args[0]; try { port = Integer.parseInt(args[1]); } catch (NumberFormatException e) { Usage(); } // I need to include some way for the defaults to be used channel = "#" + args[2]; Frame frame= new Frame(); frame.setTitle("Stern's IRC client"); wirc gui = new wirc(); gui.init(); frame.add("Center", gui); frame.pack(); frame.show(); } // end main public void textValueChanged(TextEvent e) {} public void actionPerformed(ActionEvent evt) { if (evt.getSource() == commButton) commButton.connectORsend(); if (evt.getSource() == quitButton) quitButton.actionQuit(evt); if (evt.getSource() == usernamefield) commButton.connectORsend(); } public void keyPressed(KeyEvent e) { char x; if((x = e.getKeyChar()) == e.VK_ENTER) { e.consume(); commButton.connectORsend(); } } public void keyTyped(KeyEvent e) { } public void keyReleased(KeyEvent e) { } // // Para la interfaz grafica // static TextArea outgoingText = null; Label nicknameLabel = new Label("your nickname: ", Label.RIGHT); static TextArea incomingText = new TextArea(10, 20); TextField usernamefield = new TextField(10); static CommButton commButton = null; QuitButton quitButton = null; static Label statusLabel = new Label ("not connected.", Label.LEFT); public void init () { setLayout(new BorderLayout()); outgoingText = new TextArea( 4, 20); outgoingText.addKeyListener(this); commButton = new CommButton("Connect", outgoingText, usernamefield,statusLabel); commButton.addActionListener(this); quitButton = new QuitButton("Quit"); quitButton.addActionListener(this); usernamefield.addActionListener(this); // Crear paneles Panel topPanel = new Panel(); Panel centerPanel = new Panel(); Panel bottomPanel = new Panel(); topPanel.setLayout (new BorderLayout()); centerPanel.setLayout (new BorderLayout()); bottomPanel.setLayout (new BorderLayout()); Label discussionLabel = new Label ("Discussion Area:", Label.LEFT); Label postLabel = new Label ("Post Area:", Label.LEFT); Label padLabel = new Label (" ", Label.LEFT); // Agregar componentes al panel superior // topPanel.add ("North", titleLabel); topPanel.add ("Center", discussionLabel); // Agregar componentes al panel central centerPanel.add ("North", incomingText ); centerPanel.add ("Center", postLabel); centerPanel.add ("South", outgoingText ); // incoming no es editable pero outgoing si (en start()) incomingText.setEditable(false); // Font f = new Font("Courier",Font.PLAIN,12); // incomingText.setFont(f); disableOutgoingText(); Panel usernameAndButton = new Panel(); usernameAndButton.add (usernamefield); usernameAndButton.add (commButton); usernameAndButton.add (quitButton); // Agregar componentes al panel inferior bottomPanel.add("West", nicknameLabel); bottomPanel.add("Center", usernameAndButton); bottomPanel.add("East", padLabel); bottomPanel.add("South", statusLabel); // Agregar artefactos y paneles add("North", topPanel); add("Center", centerPanel); add("South", bottomPanel); // Partimos el lector de mensajes msgReader = new MsgReader (incomingText, statusLabel, commButton); msgReader.start(); resize (getMinimumSize()); } public void start() { // Donde ? host = getCodeBase().getHost(); // what the fuck is this? } static void disableOutgoingText () { outgoingText.setEditable(false); } static void enableOutgoingText () { outgoingText.setEditable(true); } public void destroy() { msgReader.stop(); closeall(); } public Dimension getMinimumSize() { return new Dimension(370,430); } public Dimension getPreferredSize() { return getMinimumSize(); } } // end of wirc applet class parser // maybe make this a thread { String toParse = null; // that which we will be parsing String toSend = null; String toShow = null; parser(String s) { toParse = s; } parser() { } String whatShouldISend() { return toSend; } String whatShouldIShow() { return toShow; } // the following has to be run on parsed text String breakLines(TextArea fitHere, Font usedFont,String breakThis) { int fragwidth = 0; int thisSpace = 0; int previousSpace = 0; String brokenText = ""; String goodFragment = ""; String previousFragment = ""; String theRest = ""; String padding = " "; Dimension d = fitHere.getSize(); int useWidth = d.width - 25; // 25 for scrollerspace FontMetrics fm = fitHere.getFontMetrics(usedFont); // if the line is longer than the space available and there's a place we could break it, String firstWord = breakThis.substring(0,breakThis.indexOf(' ')); if (fm.stringWidth(breakThis) > useWidth && fm.stringWidth(firstWord) 0) { answer.reset(); answer = minigrep(":",2); int meatstart = answer.start; String meat = toParse.substring(meatstart+1,toParse.length()); answer.reset(); answer = minigrep("!",1); String talker = toParse.substring(1,answer.start); answer.reset(); answer = minigrep("ACTION",1); if (answer.start > 0) toShow = "* " + talker + meat.substring(7,meat.length()); else toShow = "<" + talker + "> " + meat; done = true; } // start JOIN test if (!done) { answer = minigrep("JOIN",1); if (answer.stop > 0) { answer.reset(); answer = minigrep("!",1); String talker = toParse.substring(1,answer.start); toShow = "* " + talker + " has joined channel " + wirc.channel + "."; done = true; } } // start NICK test if (!done) { answer = minigrep("NICK",1); if (answer.stop > 0) { answer.reset(); answer = minigrep("!",1); String talker = toParse.substring(1,answer.start); int meatstart = toParse.lastIndexOf(':'); String meat = toParse.substring(meatstart+1,toParse.length()); toShow = "* " + talker + " is now known as " + meat + "."; done = true; } } // start PART test if (!done) { answer = minigrep("PART",1); if (answer.stop > 0) { answer.reset(); answer = minigrep("!",1); String talker = toParse.substring(1,answer.start); toShow = "* " + talker + " has left the channel."; done = true; } } // start QUIT test if (!done) { answer = minigrep("QUIT",1); if (answer.stop > 0) { answer.reset(); answer = minigrep("!",1); String talker = toParse.substring(1,answer.start); int meatstart = toParse.lastIndexOf(':'); String meat = toParse.substring(meatstart+1,toParse.length()); toShow = "* " + talker + " has quit. " + meat; done = true; } } // start MODE test if (!done) { answer = minigrep("MODE",1); if (answer.stop > 0) { String meat = new String(""); int meatstart = 0; String modechanger = new String(""); int modewordend = answer.stop; answer.reset(); answer = minigrep("!",1); if (answer.stop > 0) { // it's somebody else changing the mode modechanger = toParse.substring(1,answer.start); meatstart = modewordend+ wirc.channel.length()+2; meat = toParse.substring(meatstart,toParse.length()); } else { // I changed the mode modechanger = wirc.nickname; meatstart = 2*wirc.nickname.length()+8; meat = toParse.substring(meatstart,toParse.length()); } toShow = "* " + modechanger + " has changed the mode of the channel " + meat; done = true; } } // start KICK test if (!done) { answer = minigrep("KICK",1); if (answer.stop > 0) { String kickee = new String(""); int kickwordend = answer.stop; answer.reset(); answer = minigrep("!",1); String kicker = toParse.substring(1,answer.start); int meatstart = toParse.lastIndexOf(':'); String meat = toParse.substring(meatstart+1,toParse.length()); kickee = toParse.substring(kickwordend+wirc.channel.length()+2,meatstart); toShow = "* " + kicker + " has kicked " + kickee + " from the channel. " + meat; done = true; } } // start initial topic test if (!done) { startstop smTest = new startstop(0,0); answer = minigrep("332",1); smTest = minigrep(":",1); if (smTest.start == 0 && answer.stop > 0) { answer.reset(); answer = minigrep(":",2); String meat = toParse.substring(answer.stop,toParse.length()); toShow = "* Our topic is \"" + meat + "\""; done = true; } } // parse found nothing to deal with if (!done) toShow = toParse; } // end of the crazy long constructor } class outgoingParse extends parser { // a very long and elaborate constructor boolean isServerCommand = false; outgoingParse(String s) { toParse = s; String whatItyped = toParse; String test = whatItyped.substring(0,1); if (test.equals("/")) { // I have a control message to worry about // msg test = whatItyped.substring(1,4); if (test.equals("MSG") || test.equals("msg")) { String submeat = whatItyped.substring(5,whatItyped.length()); int namemark = submeat.indexOf(' '); String target = submeat.substring(0,namemark); String subsubmeat = submeat.substring(namemark,submeat.length()); toSend = "PRIVMSG " + target + " :" + subsubmeat + "\r\n"; toShow = "-> *" + target + "* " + subsubmeat + "\n"; } // kick test = whatItyped.substring(1,5); if (test.equals("kick") || test.equals("KICK")) { String target = null; String reason = ""; String submeat = whatItyped.substring(6,whatItyped.length()); int namemark = submeat.indexOf(' '); if (namemark == -1) { target = submeat; } else { target = submeat.substring(0,namemark); reason = submeat.substring(namemark+1,submeat.length()); } toSend = "KICK " + wirc.channel + " " + target + " :" + reason + "\r\n"; toShow = ""; } // join (PART and JOIN, since this is a single-channel client) if (test.equals("join") || test.equals("JOIN")) { String newChannel = null; String meatyBit = whatItyped.substring(6,whatItyped.length()); int endChannel = meatyBit.indexOf(' '); if (endChannel == -1) { newChannel = meatyBit; } else { newChannel = meatyBit.substring(0,endChannel); // comment = meatyBit.substring(endChannel+1,meatyBit.length()); } String oldChannel = wirc.channel; wirc.channel = newChannel; toSend = "PART " + oldChannel + "\r\n" + "JOIN " + newChannel + "\r\n"; toShow = ""; } //me (ACTION) test = whatItyped.substring(1,3); if (test.equals("ME") || test.equals("me")) { String submeat = whatItyped.substring(4,whatItyped.length()); String marker = "\u0001"; toSend = "PRIVMSG " + wirc.channel + " :" + marker + "ACTION " + submeat + marker + "\r\n"; toShow = "* " + wirc.nickname + " " + submeat + "\n"; } isServerCommand = true; } else { isServerCommand = false; toSend = "PRIVMSG " + wirc.channel + " :" + whatItyped + "\r\n"; toShow = "> " + whatItyped + "\n"; } } // end of constructor boolean isCommand() { return isServerCommand; } } // ends outgoingParse class MsgReader extends Thread { TextArea Output = null; Label statusLabel = null; CommButton commButton = null; MsgReader (TextArea output, Label _statusLabel, CommButton _commButton) { Output = output; statusLabel = _statusLabel; commButton = _commButton; } public void run() { for (;;) { suspend(); // suspends the thread try { // Leemos el mensaje for (;;) { String line = wirc.sin.readLine(); if (line == null) break; incomingParse readParser = new incomingParse(line); String printLine = readParser.breakLines(wirc.incomingText,wirc.incomingText.getFont(),readParser.whatShouldIShow()); // if a VERSION or something came in that I need to deal with, I probably want to deal with // it out here. PONGs too. if (printLine == null) break; Output.append (printLine + "\n"); yield(); // causes the currently executing thread to yield } } catch (IOException e) { System.out.println ("Error reading message."); System.out.println (e); } finally { statusLabel.setText (" Server died, please restart it."); commButton.disconnect(); } } } // ends run static void exit () { System.out.println (" Server terminated. Connection closed."); System.exit(0); } } class outgoingMesg extends PrintWriter { // String message = null; outgoingMesg(OutputStream ostream) { super(ostream); // message = s; // out = ostream; } void send (String s) { println(s); flush(); } } class QuitButton extends Button { QuitButton(String label) { super(label); } void actionQuit(ActionEvent evt,String adiosMesg) { if (adiosMesg == "") { adiosMesg = "adios"; } String msg_toSend = "QUIT :" + adiosMesg + "\r\n"; wirc.sout.send(msg_toSend); wirc.msgReader.stop(); wirc.closeall(); // wirc.quit(); } // this is the only one that's getting used right now void actionQuit(ActionEvent evt) { if (wirc.commButton.IsConnected() == true) { String adiosMesg = "adios"; String msg_toSend = "QUIT :" + adiosMesg + "\r\n"; wirc.sout.send(msg_toSend); wirc.msgReader.stop(); wirc.closeall(); wirc.commButton.connected = false; // wirc.quit(); } } } class CommButton extends Button //implements ActionListener { TextArea outgoingText = null; TextField usernameField = null; Label statusLabel = null; boolean connected = false; CommButton (String label, TextArea textArea, TextField _usernameField, Label _statusLabel) { super (label); outgoingText = textArea; usernameField = _usernameField; statusLabel = _statusLabel; } synchronized boolean IsConnected() { return (connected); } synchronized void disconnect() { connected = false; statusLabel.setText ("Connection Lost."); setLabel ("Connect"); wirc.disableOutgoingText(); } void connectORsend() { if (!IsConnected()) { actionConnect(); } else { actionSend(); } } // void actionSend (ActionEvent evt) void actionSend() { wirc.nickname = usernameField.getText(); String message = outgoingText.getText(); outgoingText.setText(""); // Enviamos el mensaje final. outgoingParse outParser = new outgoingParse(message); String msg_shown = new String(); if (outParser.isCommand() == true) msg_shown = outParser.whatShouldIShow(); else msg_shown = outParser.breakLines(wirc.incomingText,wirc.incomingText.getFont(),outParser.whatShouldIShow()); wirc.incomingText.append(msg_shown+"\n"); String msg_toSend = outParser.whatShouldISend(); wirc.sout.send(msg_toSend); if (IsConnected()) statusLabel.setText ("Message sent."); } // void actionConnect (ActionEvent evt) void actionConnect () { // System.out.println("actionConnect\n"); wirc.nickname = usernameField.getText(); if ( !wirc.nickname.equals("") ) { // Conectamos try { // Creamos el socket wirc.socket = new Socket (wirc.host, wirc.port); // Creamos stream de escritura sobre el socket wirc.sout = new outgoingMesg(wirc.socket.getOutputStream()); // Creamos stream de lectura sobre el socket wirc.sin = new BufferedReader ( new InputStreamReader(wirc.socket.getInputStream()) ); // do we have some kind of error message if this doesn't work? statusLabel.setText(" Connected to " + wirc.socket.getInetAddress() + ":" + wirc.socket.getPort() ); // As dictated by RFC1459... // first, we send the PASS message wirc.sout.send("PASS " + wirc.password + "\r\n"); // then we send the NICK message wirc.sout.send("NICK " + wirc.nickname + "\r\n"); // finally, we send the USER message wirc.sout.send ("USER " + wirc.username + " localhost " + wirc.host + ":" + wirc.realname + "\r\n"); //as a temporary kludge wirc.sout.send("JOIN " + wirc.channel + "\r\n"); // partimos (puede ser de nuevo) el thread de lectura de mensajes wirc.msgReader.resume(); connected = true; setLabel ("Send"); // Habilita para escribir... wirc.enableOutgoingText(); } catch (IOException e) { System.err.println (e); statusLabel.setText(" Server not running. Start it first."); } } // if name != "" else { statusLabel.setText ("Please set your nickname first"); } } // actionConnect } // Class CommButton