added support for copying moves
[fleet.git] / src / de / mud / telnet / socket / TelnetIO.java
1 package de.mud.telnet.socket;
2 /* "In case you would like to use the packages as libraries please
3  *  apply the GNU Library General Public License as documented in the
4  *  file COPYING.LIB." (from Telnet/Documentation/index.html)
5  */
6
7 /**
8  * socket.TelnetIO - a telnet implementation
9  * --
10  * $Id: TelnetIO.java,v 1.10 1998/02/09 10:22:18 leo Exp $
11  * $timestamp: Tue May 27 13:27:05 1997 by Matthias L. Jugel :$
12  *
13  * This file is part of "The Java Telnet Applet".
14  *
15  * This is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 2, or (at your option)
18  * any later version.
19  *
20  * "The Java Telnet Applet" is distributed in the hope that it will be 
21  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * 
25  * You should have received a copy of the GNU General Public License
26  * along with this software; see the file COPYING.  If not, write to the
27  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28  * Boston, MA 02111-1307, USA.
29  */
30
31 import java.net.Socket;
32 import java.io.BufferedInputStream;
33 import java.io.BufferedOutputStream;
34 import java.io.IOException;
35 import java.awt.Dimension;
36 import java.util.Vector;
37
38 /**
39  * Implements simple telnet io
40  *
41  * @version $Id: TelnetIO.java,v 1.10 1998/02/09 10:22:18 leo Exp $
42  * @author  Matthias L. Jugel, Marcus Meißner
43  * @version 1.2 3/7/97 George Ruban added available() because it was needed.
44  */
45 public class TelnetIO implements StatusPeer
46 {
47   /**
48    * Return the version of TelnetIO.
49    */
50   public String toString() { return "$Id: TelnetIO.java,v 1.10 1998/02/09 10:22:18 leo Exp $"; }
51   
52         /**
53          * Debug level. This results in additional diagnostic messages on the
54          * java console.
55          */
56         private static int debug = 0;
57
58         /**
59          * State variable for telnetnegotiation reader
60          */
61         private byte neg_state = 0;
62
63         /**
64          * constants for the negotiation state
65          */
66         private final static byte STATE_DATA    = 0;
67         private final static byte STATE_IAC     = 1;
68         private final static byte STATE_IACSB   = 2;
69         private final static byte STATE_IACWILL = 3;
70         private final static byte STATE_IACDO   = 4;
71         private final static byte STATE_IACWONT = 5;
72         private final static byte STATE_IACDONT = 6;
73         private final static byte STATE_IACSBIAC        = 7;
74         private final static byte STATE_IACSBDATA       = 8;
75         private final static byte STATE_IACSBDATAIAC    = 9;
76
77         /**
78          * What IAC SB <xx> we are handling right now
79          */
80         private byte current_sb;
81
82         /**
83          * IAC - init sequence for telnet negotiation.
84          */
85         private final static byte IAC  = (byte)255;
86         /**
87          * [IAC] End Of Record
88          */
89         private final static byte EOR  = (byte)239;
90         /**
91          * [IAC] WILL
92          */
93         private final static byte WILL  = (byte)251;
94         /**
95          * [IAC] WONT
96          */
97         private final static byte WONT  = (byte)252;
98         /**
99          * [IAC] DO
100          */
101         private final static byte DO    = (byte)253;
102         /**
103          * [IAC] DONT
104          */
105         private final static byte DONT  = (byte)254;
106         /**
107          * [IAC] Sub Begin 
108          */
109         private final static byte SB  = (byte)250;
110         /**
111          * [IAC] Sub End
112          */
113         private final static byte SE  = (byte)240;
114         /**
115          * Telnet option: echo text
116          */
117         private final static byte TELOPT_ECHO  = (byte)1;  /* echo on/off */
118         /**
119          * Telnet option: End Of Record
120          */
121         private final static byte TELOPT_EOR   = (byte)25;  /* end of record */
122         /**
123          * Telnet option: Negotiate About Window Size
124          */
125         private final static byte TELOPT_NAWS  = (byte)31;  /* NA-WindowSize*/
126         /**
127          * Telnet option: Terminal Type
128          */
129         private final static byte TELOPT_TTYPE  = (byte)24;  /* terminal type */
130
131         private final static byte[] IACWILL  = { IAC, WILL };
132         private final static byte[] IACWONT  = { IAC, WONT };
133         private final static byte[] IACDO    = { IAC, DO        };
134         private final static byte[] IACDONT  = { IAC, DONT };
135         private final static byte[] IACSB  = { IAC, SB };
136         private final static byte[] IACSE  = { IAC, SE };
137
138         /** 
139          * Telnet option qualifier 'IS' 
140          */
141         private final static byte TELQUAL_IS = (byte)0;
142
143         /** 
144          * Telnet option qualifier 'SEND' 
145          */
146         private final static byte TELQUAL_SEND = (byte)1;
147
148         /**
149          * What IAC DO(NT) request do we have received already ?
150          */
151         private byte[] receivedDX;
152   
153         /**
154          * What IAC WILL/WONT request do we have received already ?
155          */
156         private byte[] receivedWX;
157         /**
158          * What IAC DO/DONT request do we have sent already ?
159          */
160         private byte[] sentDX;
161         /**
162          * What IAC WILL/WONT request do we have sent already ?
163          */
164         private byte[] sentWX;
165
166         private Socket socket;
167         private BufferedInputStream is;
168         private BufferedOutputStream os;
169
170         private StatusPeer peer = this;         /* peer, notified on status */
171
172         /**
173          * Connect to the remote host at the specified port.
174          * @param address the symbolic host address
175          * @param port the numeric port
176          * @see #disconnect
177          */
178         public void connect(String address, int port) throws IOException {
179                 if(debug > 0) System.out.println("Telnet.connect("+address+","+port+")");
180                 socket = new Socket(address, port);
181                 is = new BufferedInputStream(socket.getInputStream());
182                 os = new BufferedOutputStream(socket.getOutputStream());
183                 neg_state = 0;
184                 receivedDX = new byte[256]; 
185                 sentDX = new byte[256];
186                 receivedWX = new byte[256]; 
187                 sentWX = new byte[256];
188         }
189
190         /**
191          * Disconnect from remote host.
192          * @see #connect
193          */
194         public void disconnect() throws IOException {
195           if(debug > 0) System.out.println("TelnetIO.disconnect()");
196           if(socket !=null) socket.close();
197         }
198   
199         /**
200          * Connect to the remote host at the default telnet port (23).
201          * @param address the symbolic host address
202          */
203         public void connect(String address) throws IOException {
204                 connect(address, 23);
205         }
206
207         /**
208          * Set the object to be notified about current status.
209          * @param obj object to be notified.
210          */
211         public void setPeer(StatusPeer obj) { peer = obj; }
212
213         /** Returns bytes available to be read.  Since they haven't been
214          * negotiated over, this could be misleading.
215          * Most useful as a boolean value - "are any bytes available" -
216          * rather than as an exact count of "how many ara available."
217          *
218          * @exception IOException on problems with the socket connection
219          */
220         public int available() throws IOException
221         {
222           return is.available();
223         }
224         
225
226         /**
227          * Read data from the remote host. Blocks until data is available. 
228          * Returns an array of bytes.
229          * @see #send
230          */
231         public byte[] receive() throws IOException {
232                 int count = is.available();
233                 byte buf[] = new byte[count];
234                 count = is.read(buf);
235                 if(count < 0) throw new IOException("Connection closed.");
236                 if(debug > 1) System.out.println("TelnetIO.receive(): read bytes: "+count);
237                 buf = negotiate(buf, count);
238                 return buf;
239         }
240
241         /**
242          * Send data to the remote host.
243          * @param buf array of bytes to send
244          * @see #receive
245          */
246         public void send(byte[] buf) throws IOException {
247                 if(debug > 1) System.out.println("TelnetIO.send("+buf+")");
248                 os.write(buf);
249                 os.flush();
250         }
251
252         public void send(byte b) throws IOException {
253                 if(debug > 1) System.out.println("TelnetIO.send("+b+")");
254                 os.write(b);
255                 os.flush();
256         }
257
258         /**
259          * Handle an incoming IAC SB <type> <bytes> IAC SE
260          * @param type type of SB
261          * @param sbata byte array as <bytes>
262          * @param sbcount nr of bytes. may be 0 too.
263          */
264         private void handle_sb(byte type, byte[] sbdata, int sbcount) 
265                 throws IOException 
266         {
267                 if(debug > 1) 
268                         System.out.println("TelnetIO.handle_sb("+type+")");
269                 switch (type) {
270                 case TELOPT_TTYPE:
271                         if (sbcount>0 && sbdata[0]==TELQUAL_SEND) {
272                                 String ttype;
273                                 send(IACSB);send(TELOPT_TTYPE);send(TELQUAL_IS);
274                                 /* FIXME: need more logic here if we use 
275                                  * more than one terminal type
276                                  */
277                                 Vector vec = new Vector(2);
278                                 vec.addElement("TTYPE");
279                                 ttype = (String)peer.notifyStatus(vec);
280                                 if(ttype == null) ttype = "dumb";
281                                 byte[] bttype = new byte[ttype.length()];
282
283                                 ttype.getBytes(0,ttype.length(), bttype, 0);
284                                 send(bttype);
285                                 send(IACSE);
286                         }
287
288                 }
289         }
290
291         /**
292          * Notify about current telnet status. This method is called top-down.
293          * @param status contains status information
294          */
295         public Object notifyStatus(Vector status) {
296                 if(debug > 0) 
297                   System.out.println("TelnetIO.notifyStatus("+status+")");
298                 return null;
299         }
300
301         /* wo faengt buf an bei buf[0] oder bei buf[1] */
302         private byte[] negotiate(byte buf[], int count) throws IOException {
303                 if(debug > 1) 
304                         System.out.println("TelnetIO.negotiate("+buf+","+count+")");
305                 byte nbuf[] = new byte[count];
306                 byte sbbuf[] = new byte[count];
307                 byte sendbuf[] = new byte[3];
308                 byte b,reply;
309                 int  sbcount = 0;
310                 int boffset = 0, noffset = 0;
311                 Vector  vec = new Vector(2);
312
313                 while(boffset < count) {
314                         b=buf[boffset++];
315                         /* of course, byte is a signed entity (-128 -> 127)
316                          * but apparently the SGI Netscape 3.0 doesn't seem
317                          * to care and provides happily values up to 255
318                          */
319                         if (b>=128)
320                                 b=(byte)((int)b-256);
321                         switch (neg_state) {
322                         case STATE_DATA:
323                                 if (b==IAC) {
324                                         neg_state = STATE_IAC;
325                                 } else {
326                                         nbuf[noffset++]=b;
327                                 }
328                                 break;
329                         case STATE_IAC:
330                                 switch (b) {
331                                 case IAC:
332                                         if(debug > 2) 
333                                                 System.out.print("IAC ");
334                                         neg_state = STATE_DATA;
335                                         nbuf[noffset++]=IAC;
336                                         break;
337                                 case WILL:
338                                         if(debug > 2)
339                                                 System.out.print("WILL ");
340                                         neg_state = STATE_IACWILL;
341                                         break;
342                                 case WONT:
343                                         if(debug > 2)
344                                                 System.out.print("WONT ");
345                                         neg_state = STATE_IACWONT;
346                                         break;
347                                 case DONT:
348                                         if(debug > 2)
349                                                 System.out.print("DONT ");
350                                         neg_state = STATE_IACDONT;
351                                         break;
352                                 case DO:
353                                         if(debug > 2)
354                                                 System.out.print("DO ");
355                                         neg_state = STATE_IACDO;
356                                         break;
357                                 case EOR:
358                                         if(debug > 2)
359                                                 System.out.print("EOR ");
360                                         neg_state = STATE_DATA;
361                                         break;
362                                 case SB:
363                                         if(debug > 2)
364                                                 System.out.print("SB ");
365                                         neg_state = STATE_IACSB;
366                                         sbcount = 0;
367                                         break;
368                                 default:
369                                         if(debug > 2)
370                                                 System.out.print(
371                                                         "<UNKNOWN "+b+" > "
372                                                 );
373                                         neg_state = STATE_DATA;
374                                         break;
375                                 }
376                                 break;
377                         case STATE_IACWILL:
378                                 switch(b) {
379                                 case TELOPT_ECHO:
380                                         if(debug > 2) 
381                                                 System.out.println("ECHO");
382                                         reply = DO;
383                                         vec = new Vector(2);
384                                         vec.addElement("NOLOCALECHO");
385                                         peer.notifyStatus(vec);
386                                         break;
387                                 case TELOPT_EOR:
388                                         if(debug > 2) 
389                                                 System.out.println("EOR");
390                                         reply = DO;
391                                         break;
392                                 default:
393                                         if(debug > 2)
394                                                 System.out.println(
395                                                         "<UNKNOWN,"+b+">"
396                                                 );
397                                         reply = DONT;
398                                         break;
399                                 }
400                                 if(debug > 1)
401                                   System.out.println("<"+b+", WILL ="+WILL+">");
402                                 if (    reply != sentDX[b+128] ||
403                                         WILL != receivedWX[b+128]
404                                 ) {
405                                         sendbuf[0]=IAC;
406                                         sendbuf[1]=reply;
407                                         sendbuf[2]=b;
408                                         send(sendbuf);
409                                         sentDX[b+128] = reply;
410                                         receivedWX[b+128] = WILL;
411                                 }
412                                 neg_state = STATE_DATA;
413                                 break;
414                         case STATE_IACWONT:
415                                 switch(b) {
416                                 case TELOPT_ECHO:
417                                         if(debug > 2) 
418                                                 System.out.println("ECHO");
419
420                                         vec = new Vector(2);
421                                         vec.addElement("LOCALECHO");
422                                         peer.notifyStatus(vec);
423                                         reply = DONT;
424                                         break;
425                                 case TELOPT_EOR:
426                                         if(debug > 2) 
427                                                 System.out.println("EOR");
428                                         reply = DONT;
429                                         break;
430                                 default:
431                                         if(debug > 2) 
432                                                 System.out.println(
433                                                         "<UNKNOWN,"+b+">"
434                                                 );
435                                         reply = DONT;
436                                         break;
437                                 }
438                                 if (    reply != sentDX[b+128] ||
439                                         WONT != receivedWX[b+128]
440                                 ) {
441                                         sendbuf[0]=IAC;
442                                         sendbuf[1]=reply;
443                                         sendbuf[2]=b;
444                                         send(sendbuf);
445                                         sentDX[b+128] = reply;
446                                         receivedWX[b+128] = WILL;
447                                 }
448                                 neg_state = STATE_DATA;
449                                 break;
450                         case STATE_IACDO:
451                                 switch (b) {
452                                 case TELOPT_ECHO:
453                                         if(debug > 2) 
454                                                 System.out.println("ECHO");
455                                         reply = WILL;
456                                         vec = new Vector(2);
457                                         vec.addElement("LOCALECHO");
458                                         peer.notifyStatus(vec);
459                                         break;
460                                 case TELOPT_TTYPE:
461                                         if(debug > 2) 
462                                                 System.out.println("TTYPE");
463                                         reply = WILL;
464                                         break;
465                                 case TELOPT_NAWS:
466                                         if(debug > 2) 
467                                                 System.out.println("NAWS");
468                                         vec = new Vector(2);
469                                         vec.addElement("NAWS");
470                                         Dimension size = (Dimension)
471                                                 peer.notifyStatus(vec);
472                                         receivedDX[b] = DO;
473                                         if(size == null)
474                                         {
475                                                 /* this shouldn't happen */
476                                                 send(IAC);
477                                                 send(WONT);
478                                                 send(TELOPT_NAWS);
479                                                 reply = WONT;
480                                                 sentWX[b] = WONT;
481                                                 break;
482                                         }
483                                         reply = WILL;
484                                         sentWX[b] = WILL;
485                                         sendbuf[0]=IAC;
486                                         sendbuf[1]=WILL;
487                                         sendbuf[2]=TELOPT_NAWS;
488                                         send(sendbuf);
489                                         send(IAC);send(SB);send(TELOPT_NAWS);
490                                         send((byte) (size.width >> 8));
491                                         send((byte) (size.width & 0xff));
492                                         send((byte) (size.height >> 8));
493                                         send((byte) (size.height & 0xff));
494                                         send(IAC);send(SE);
495                                         break;
496                                 default:
497                                         if(debug > 2) 
498                                                 System.out.println(
499                                                         "<UNKNOWN,"+b+">"
500                                                 );
501                                         reply = WONT;
502                                         break;
503                                 }
504                                 if (    reply != sentWX[128+b] ||
505                                         DO != receivedDX[128+b]
506                                 ) {
507                                         sendbuf[0]=IAC;
508                                         sendbuf[1]=reply;
509                                         sendbuf[2]=b;
510                                         send(sendbuf);
511                                         sentWX[b+128] = reply;
512                                         receivedDX[b+128] = DO;
513                                 }
514                                 neg_state = STATE_DATA;
515                                 break;
516                         case STATE_IACDONT:
517                                 switch (b) {
518                                 case TELOPT_ECHO:
519                                         if(debug > 2) 
520                                                 System.out.println("ECHO");
521                                         reply   = WONT;
522                                         vec = new Vector(2);
523                                         vec.addElement("NOLOCALECHO");
524                                         peer.notifyStatus(vec);
525                                         break;
526                                 case TELOPT_NAWS:
527                                         if(debug > 2) 
528                                                 System.out.println("NAWS");
529                                         reply   = WONT;
530                                         break;
531                                 default:
532                                         if(debug > 2) 
533                                                 System.out.println(
534                                                         "<UNKNOWN,"+b+">"
535                                                 );
536                                         reply   = WONT;
537                                         break;
538                                 }
539                                 if (    reply   != sentWX[b+128] ||
540                                         DONT    != receivedDX[b+128]
541                                 ) {
542                                         send(IAC);send(reply);send(b);
543                                         sentWX[b+128]           = reply;
544                                         receivedDX[b+128]       = DONT;
545                                 }
546                                 neg_state = STATE_DATA;
547                                 break;
548                         case STATE_IACSBIAC:
549                                 if(debug > 2) System.out.println(""+b+" ");
550                                 if (b == IAC) {
551                                         sbcount         = 0;
552                                         current_sb      = b;
553                                         neg_state       = STATE_IACSBDATA;
554                                 } else {
555                                         System.out.println("(bad) "+b+" ");
556                                         neg_state       = STATE_DATA;
557                                 }
558                                 break;
559                         case STATE_IACSB:
560                                 if(debug > 2) System.out.println(""+b+" ");
561                                 switch (b) {
562                                 case IAC:
563                                         neg_state = STATE_IACSBIAC;
564                                         break;
565                                 default:
566                                         current_sb      = b;
567                                         sbcount         = 0;
568                                         neg_state       = STATE_IACSBDATA;
569                                         break;
570                                 }
571                                 break;
572                         case STATE_IACSBDATA:
573                                 if (debug > 2) System.out.println(""+b+" ");
574                                 switch (b) {
575                                 case IAC:
576                                         neg_state = STATE_IACSBDATAIAC;
577                                         break;
578                                 default:
579                                         sbbuf[sbcount++] = b;
580                                         break;
581                                 }
582                                 break;
583                         case STATE_IACSBDATAIAC:
584                                 if (debug > 2) System.out.println(""+b+" ");
585                                 switch (b) {
586                                 case IAC:
587                                         neg_state = STATE_IACSBDATA;
588                                         sbbuf[sbcount++] = IAC;
589                                         break;
590                                 case SE:
591                                         handle_sb(current_sb,sbbuf,sbcount);
592                                         current_sb      = 0;
593                                         neg_state       = STATE_DATA;
594                                         break;
595                                 case SB:
596                                         handle_sb(current_sb,sbbuf,sbcount);
597                                         neg_state       = STATE_IACSB;
598                                         break;
599                                 default:
600                                         neg_state       = STATE_DATA;
601                                         break;
602                                 }
603                                 break;
604                         default:
605                                 if (debug > 2) 
606                                         System.out.println(
607                                                 "This should not happen: "+
608                                                 neg_state+" "
609                                         );
610                                 neg_state = STATE_DATA;
611                                 break;
612                         }
613                 }
614                 buf     = new byte[noffset];
615                 System.arraycopy(nbuf, 0, buf, 0, noffset);
616                 return buf;
617         }
618 }