dw_fluffos_v2/
dw_fluffos_v2/fluffos-2.9-ds2.05/
dw_fluffos_v2/fluffos-2.9-ds2.05/ChangeLog.old/
dw_fluffos_v2/fluffos-2.9-ds2.05/Win32/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/
dw_fluffos_v2/fluffos-2.9-ds2.05/compat/simuls/
dw_fluffos_v2/fluffos-2.9-ds2.05/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/clone/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/command/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/data/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/etc/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/include/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/inherit/master/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/log/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/compiler/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/efuns/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/single/tests/operators/
dw_fluffos_v2/fluffos-2.9-ds2.05/testsuite/u/
dw_fluffos_v2/fluffos-2.9-ds2.05/tmp/
dw_fluffos_v2/fluffos-2.9-ds2.05/windows/
dw_fluffos_v2/lib/
dw_fluffos_v2/lib/binaries/cmds/
dw_fluffos_v2/lib/binaries/cmds/creator/
dw_fluffos_v2/lib/binaries/cmds/living/
dw_fluffos_v2/lib/binaries/cmds/player/
dw_fluffos_v2/lib/binaries/d/admin/obj/
dw_fluffos_v2/lib/binaries/d/liaison/
dw_fluffos_v2/lib/binaries/global/virtual/
dw_fluffos_v2/lib/binaries/global/virtual/setup_compiler/
dw_fluffos_v2/lib/binaries/obj/handlers/autodoc/
dw_fluffos_v2/lib/binaries/obj/handlers/terrain_things/
dw_fluffos_v2/lib/binaries/obj/misc/
dw_fluffos_v2/lib/binaries/obj/misc/buckets/
dw_fluffos_v2/lib/binaries/obj/monster/
dw_fluffos_v2/lib/binaries/obj/reactions/
dw_fluffos_v2/lib/binaries/obj/reagents/
dw_fluffos_v2/lib/binaries/secure/cmds/creator/
dw_fluffos_v2/lib/binaries/secure/master/
dw_fluffos_v2/lib/binaries/std/
dw_fluffos_v2/lib/binaries/std/dom/
dw_fluffos_v2/lib/binaries/std/effects/object/
dw_fluffos_v2/lib/binaries/std/guilds/
dw_fluffos_v2/lib/binaries/std/languages/
dw_fluffos_v2/lib/binaries/std/races/
dw_fluffos_v2/lib/binaries/std/room/
dw_fluffos_v2/lib/binaries/std/room/basic/
dw_fluffos_v2/lib/binaries/std/shops/
dw_fluffos_v2/lib/binaries/std/shops/inherit/
dw_fluffos_v2/lib/binaries/www/
dw_fluffos_v2/lib/cmds/guild-race/
dw_fluffos_v2/lib/cmds/guild-race/crafts/
dw_fluffos_v2/lib/cmds/guild-race/other/
dw_fluffos_v2/lib/cmds/playtester/
dw_fluffos_v2/lib/cmds/playtester/senior/
dw_fluffos_v2/lib/d/admin/
dw_fluffos_v2/lib/d/admin/log/
dw_fluffos_v2/lib/d/admin/mapper/31-10-01/mapmaker/event/
dw_fluffos_v2/lib/d/admin/meetings/
dw_fluffos_v2/lib/d/admin/obj/
dw_fluffos_v2/lib/d/admin/room/we_care/
dw_fluffos_v2/lib/d/admin/save/
dw_fluffos_v2/lib/d/dist/
dw_fluffos_v2/lib/d/dist/mtf/
dw_fluffos_v2/lib/d/dist/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/chars/
dw_fluffos_v2/lib/d/dist/pumpkin/desert/
dw_fluffos_v2/lib/d/dist/pumpkin/gumboot/
dw_fluffos_v2/lib/d/dist/pumpkin/hospital/
dw_fluffos_v2/lib/d/dist/pumpkin/inherit/
dw_fluffos_v2/lib/d/dist/pumpkin/map/
dw_fluffos_v2/lib/d/dist/pumpkin/plain/
dw_fluffos_v2/lib/d/dist/pumpkin/pumpkin/
dw_fluffos_v2/lib/d/dist/pumpkin/save/
dw_fluffos_v2/lib/d/dist/pumpkin/squash/
dw_fluffos_v2/lib/d/dist/pumpkin/terrain/
dw_fluffos_v2/lib/d/dist/pumpkin/woods/
dw_fluffos_v2/lib/d/dist/start/
dw_fluffos_v2/lib/d/learning/TinyTown/buildings/
dw_fluffos_v2/lib/d/learning/TinyTown/map/
dw_fluffos_v2/lib/d/learning/TinyTown/roads/
dw_fluffos_v2/lib/d/learning/add_command/
dw_fluffos_v2/lib/d/learning/arms_and_weps/
dw_fluffos_v2/lib/d/learning/chars/
dw_fluffos_v2/lib/d/learning/cutnpaste/
dw_fluffos_v2/lib/d/learning/examples/npcs/
dw_fluffos_v2/lib/d/learning/examples/player_houses/npcs/
dw_fluffos_v2/lib/d/learning/examples/terrain_map/basic/
dw_fluffos_v2/lib/d/learning/functions/
dw_fluffos_v2/lib/d/learning/handlers/
dw_fluffos_v2/lib/d/learning/help_topics/npcs/
dw_fluffos_v2/lib/d/learning/help_topics/objects/
dw_fluffos_v2/lib/d/learning/help_topics/rcs_demo/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/crowd/
dw_fluffos_v2/lib/d/learning/help_topics/rooms/situations/
dw_fluffos_v2/lib/d/learning/items/
dw_fluffos_v2/lib/d/learning/save/
dw_fluffos_v2/lib/d/liaison/
dw_fluffos_v2/lib/d/liaison/NEWBIE/doc/
dw_fluffos_v2/lib/d/liaison/NEWBIE/save/oldlog/
dw_fluffos_v2/lib/db/
dw_fluffos_v2/lib/doc/
dw_fluffos_v2/lib/doc/creator/
dw_fluffos_v2/lib/doc/creator/autodoc/include/reaction/
dw_fluffos_v2/lib/doc/creator/autodoc/include/ritual_system/
dw_fluffos_v2/lib/doc/creator/autodoc/include/talker/
dw_fluffos_v2/lib/doc/creator/autodoc/include/terrain_map/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/baggage/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clock/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/clothing/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/cont_save/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/corpse/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/money/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/monster/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/scabbard/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/service_provider/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/state_changer/
dw_fluffos_v2/lib/doc/creator/autodoc/obj/wand/
dw_fluffos_v2/lib/doc/creator/autodoc/std/book_dir/
dw_fluffos_v2/lib/doc/creator/autodoc/std/key/
dw_fluffos_v2/lib/doc/creator/autodoc/std/learning/
dw_fluffos_v2/lib/doc/creator/autodoc/std/map/
dw_fluffos_v2/lib/doc/creator/autodoc/std/race/
dw_fluffos_v2/lib/doc/creator/autodoc/std/weapon_logic/
dw_fluffos_v2/lib/doc/creator/files/
dw_fluffos_v2/lib/doc/creator/policy/
dw_fluffos_v2/lib/doc/creator/room/
dw_fluffos_v2/lib/doc/effects/
dw_fluffos_v2/lib/doc/ideas/
dw_fluffos_v2/lib/doc/known_command/
dw_fluffos_v2/lib/doc/lpc/basic_manual/
dw_fluffos_v2/lib/doc/lpc/intermediate/
dw_fluffos_v2/lib/doc/new/add_command/
dw_fluffos_v2/lib/doc/new/handlers/
dw_fluffos_v2/lib/doc/new/living/
dw_fluffos_v2/lib/doc/new/living/race/
dw_fluffos_v2/lib/doc/new/living/spells/
dw_fluffos_v2/lib/doc/new/player/
dw_fluffos_v2/lib/doc/new/room/guild/
dw_fluffos_v2/lib/doc/new/room/outside/
dw_fluffos_v2/lib/doc/new/room/storeroom/
dw_fluffos_v2/lib/doc/object/
dw_fluffos_v2/lib/doc/playtesters/
dw_fluffos_v2/lib/doc/policy/
dw_fluffos_v2/lib/doc/weapons/
dw_fluffos_v2/lib/global/handlers/
dw_fluffos_v2/lib/global/virtual/setup_compiler/
dw_fluffos_v2/lib/include/
dw_fluffos_v2/lib/include/cmds/
dw_fluffos_v2/lib/include/effects/
dw_fluffos_v2/lib/include/npc/
dw_fluffos_v2/lib/include/shops/
dw_fluffos_v2/lib/net/daemon/chars/
dw_fluffos_v2/lib/net/inherit/
dw_fluffos_v2/lib/net/intermud3/
dw_fluffos_v2/lib/net/intermud3/services/
dw_fluffos_v2/lib/net/obj/
dw_fluffos_v2/lib/net/save/
dw_fluffos_v2/lib/net/smnmp/
dw_fluffos_v2/lib/net/snmp/
dw_fluffos_v2/lib/obj/amulets/
dw_fluffos_v2/lib/obj/b_day/
dw_fluffos_v2/lib/obj/examples/
dw_fluffos_v2/lib/obj/food/alcohol/
dw_fluffos_v2/lib/obj/food/chocolates/
dw_fluffos_v2/lib/obj/food/fruits/
dw_fluffos_v2/lib/obj/food/meat/
dw_fluffos_v2/lib/obj/food/nuts/
dw_fluffos_v2/lib/obj/food/seafood/
dw_fluffos_v2/lib/obj/food/vegetables/
dw_fluffos_v2/lib/obj/fungi/
dw_fluffos_v2/lib/obj/furnitures/artwork/
dw_fluffos_v2/lib/obj/furnitures/bathroom/
dw_fluffos_v2/lib/obj/furnitures/beds/
dw_fluffos_v2/lib/obj/furnitures/cabinets/
dw_fluffos_v2/lib/obj/furnitures/chairs/
dw_fluffos_v2/lib/obj/furnitures/chests/
dw_fluffos_v2/lib/obj/furnitures/clocks/
dw_fluffos_v2/lib/obj/furnitures/crockery/
dw_fluffos_v2/lib/obj/furnitures/cupboards/
dw_fluffos_v2/lib/obj/furnitures/cushions/
dw_fluffos_v2/lib/obj/furnitures/fake_plants/
dw_fluffos_v2/lib/obj/furnitures/lamps/
dw_fluffos_v2/lib/obj/furnitures/mirrors/
dw_fluffos_v2/lib/obj/furnitures/outdoor/
dw_fluffos_v2/lib/obj/furnitures/safes/
dw_fluffos_v2/lib/obj/furnitures/shelves/
dw_fluffos_v2/lib/obj/furnitures/sideboards/
dw_fluffos_v2/lib/obj/furnitures/sofas/
dw_fluffos_v2/lib/obj/furnitures/stoves/
dw_fluffos_v2/lib/obj/furnitures/tables/
dw_fluffos_v2/lib/obj/furnitures/wardrobes/
dw_fluffos_v2/lib/obj/handlers/
dw_fluffos_v2/lib/obj/handlers/autodoc/
dw_fluffos_v2/lib/obj/jewellery/anklets/
dw_fluffos_v2/lib/obj/jewellery/bracelets/
dw_fluffos_v2/lib/obj/jewellery/earrings/
dw_fluffos_v2/lib/obj/jewellery/misc/
dw_fluffos_v2/lib/obj/jewellery/necklaces/
dw_fluffos_v2/lib/obj/jewellery/rings/
dw_fluffos_v2/lib/obj/media/
dw_fluffos_v2/lib/obj/misc/buckets/
dw_fluffos_v2/lib/obj/misc/jars/
dw_fluffos_v2/lib/obj/misc/papers/
dw_fluffos_v2/lib/obj/misc/player_shop/
dw_fluffos_v2/lib/obj/misc/shops/
dw_fluffos_v2/lib/obj/misc/traps/
dw_fluffos_v2/lib/obj/monster/
dw_fluffos_v2/lib/obj/monster/godmother/
dw_fluffos_v2/lib/obj/monster/transport/
dw_fluffos_v2/lib/obj/plants/inherit/
dw_fluffos_v2/lib/obj/potions/
dw_fluffos_v2/lib/open/boards/
dw_fluffos_v2/lib/save/autodoc/
dw_fluffos_v2/lib/save/bank_accounts/
dw_fluffos_v2/lib/save/boards/frog/
dw_fluffos_v2/lib/save/books/bed_catalog/
dw_fluffos_v2/lib/save/creators/
dw_fluffos_v2/lib/save/mail/
dw_fluffos_v2/lib/save/mail/p/
dw_fluffos_v2/lib/save/soul/data/
dw_fluffos_v2/lib/save/tasks/
dw_fluffos_v2/lib/save/vaults/
dw_fluffos_v2/lib/secure/cmds/lord/
dw_fluffos_v2/lib/secure/config/
dw_fluffos_v2/lib/secure/items/
dw_fluffos_v2/lib/secure/player/
dw_fluffos_v2/lib/soul/
dw_fluffos_v2/lib/soul/i/
dw_fluffos_v2/lib/soul/j/
dw_fluffos_v2/lib/soul/k/
dw_fluffos_v2/lib/soul/o/
dw_fluffos_v2/lib/soul/q/
dw_fluffos_v2/lib/soul/to_approve/
dw_fluffos_v2/lib/soul/u/
dw_fluffos_v2/lib/soul/v/
dw_fluffos_v2/lib/soul/wish_list/
dw_fluffos_v2/lib/soul/y/
dw_fluffos_v2/lib/soul/z/
dw_fluffos_v2/lib/std/creator/
dw_fluffos_v2/lib/std/effects/
dw_fluffos_v2/lib/std/effects/attached/
dw_fluffos_v2/lib/std/effects/external/
dw_fluffos_v2/lib/std/effects/fighting/
dw_fluffos_v2/lib/std/effects/other/
dw_fluffos_v2/lib/std/environ/
dw_fluffos_v2/lib/std/guilds/
dw_fluffos_v2/lib/std/hospital/
dw_fluffos_v2/lib/std/house/
dw_fluffos_v2/lib/std/house/onebedhouse/
dw_fluffos_v2/lib/std/house/onebedhut/
dw_fluffos_v2/lib/std/house/tworoomflat/
dw_fluffos_v2/lib/std/languages/
dw_fluffos_v2/lib/std/liquids/
dw_fluffos_v2/lib/std/nationality/
dw_fluffos_v2/lib/std/nationality/accents/
dw_fluffos_v2/lib/std/nationality/accents/national/
dw_fluffos_v2/lib/std/nationality/accents/regional/
dw_fluffos_v2/lib/std/npc/goals/
dw_fluffos_v2/lib/std/npc/goals/basic/
dw_fluffos_v2/lib/std/npc/goals/misc/
dw_fluffos_v2/lib/std/npc/inherit/
dw_fluffos_v2/lib/std/npc/plans/
dw_fluffos_v2/lib/std/npc/plans/basic/
dw_fluffos_v2/lib/std/outsides/
dw_fluffos_v2/lib/std/races/shadows/
dw_fluffos_v2/lib/std/room/basic/topography/
dw_fluffos_v2/lib/std/room/controller/
dw_fluffos_v2/lib/std/room/controller/topography/
dw_fluffos_v2/lib/std/room/furniture/games/
dw_fluffos_v2/lib/std/room/furniture/inherit/
dw_fluffos_v2/lib/std/room/inherit/carriage/
dw_fluffos_v2/lib/std/room/inherit/topography/
dw_fluffos_v2/lib/std/room/punishments/
dw_fluffos_v2/lib/std/room/topography/area/
dw_fluffos_v2/lib/std/room/topography/iroom/
dw_fluffos_v2/lib/std/room/topography/milestone/
dw_fluffos_v2/lib/std/shadows/
dw_fluffos_v2/lib/std/shadows/attached/
dw_fluffos_v2/lib/std/shadows/curses/
dw_fluffos_v2/lib/std/shadows/disease/
dw_fluffos_v2/lib/std/shadows/fighting/
dw_fluffos_v2/lib/std/shadows/room/
dw_fluffos_v2/lib/std/shops/controllers/
dw_fluffos_v2/lib/std/shops/objs/
dw_fluffos_v2/lib/std/shops/player_shop/
dw_fluffos_v2/lib/std/shops/player_shop/office_code/
dw_fluffos_v2/lib/std/socket/
dw_fluffos_v2/lib/www/
dw_fluffos_v2/lib/www/external/autodoc/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/
dw_fluffos_v2/lib/www/external/java/telnet/Documentation/images/
dw_fluffos_v2/lib/www/external/java/telnet/examples/
dw_fluffos_v2/lib/www/external/java/telnet/tools/
dw_fluffos_v2/lib/www/pics/
dw_fluffos_v2/lib/www/secure/creator/
dw_fluffos_v2/lib/www/secure/editors/
dw_fluffos_v2/lib/www/secure/survey_results/
dw_fluffos_v2/win32/
package socket;

import java.io.IOException;
import java.util.Date;

/** Wrapper for a Java Telnet call. 
 * To use, make a new TelnetWrapper() with the name or IP address of a host.
 * Then, for most uses, the easiest way is to call setPrompt() with the
 * expected prompt, then call login(), and a sequence of sendLine()'s
 * until you get what you want done.
 * <P>
 * If you don't know the prompt ahead of time, you have to do a sequence of
 * send() and wait() or receiveUntil() calls.  send() sends a string across
 * the telnet connection. Add a '\r' to the end if you want to
 * complete a command. wait() waits for an exact string from the other side
 * of the telnet connection, and returns nothing,
 * receiveUntil() also waits for a string, but returns all the data
 * that it received while waiting, including the string itself. 
 * Use this if you want the output from a command. Please note that
 * the telnet connection will usually echo the sent command. 
 * <P>
 * sendLine() is generally better, since it adds the '\r'
 * automatically, waits for the prompt before returning, and returns all
 * data received before the prompt, with the prompt itself cut off the
 * end, and the sent command cut off the beginning. login() and
 * sendLine() are implemented using send(), wait() and receiveUntil().
 * They can be freely mixed and matched.
 * <P>
 * Here is a simple example of the use of TelnetWrapper:
 * <PRE>
 * // creates a new file in /tmp, lists the directory to prove it done
 * {
 *   TelnetWrapper telnet = new TelnetWrapper("123.45.78.90");
 *
 *   // setting the correct prompt ahead of time is very important 
 *   // if you want to use login and sendLine
 *   telnet.setPrompt("$ ");
 *   telnet.login("loginname", "password");
 *
 *   // this is how you have to do it otherwise
 *   telnet.send("touch /tmp/TELNET_WRAPPER" + "\r");
 *   telnet.wait("$ ");
 *
 *   // sendLine 1: adds the \r automatically, 2: waits for the prompt
 *   // before returning 3: returns what was printed from the command
 *   String ls = telnet.sendLine("ls /tmp");
 *   System.out.println(ls);
 *
 *   // clean up
 *   telnet.disconnect();
 * } 
 * </PRE>
 * @author George Ruban 3/4/97
 * @version 0.2 5/15/97 - added comments, replaced String += with
 *    StringBuffer.append() in receiveUntil(), added port constructor
 * @version 0.3 7/30/97 - added optional timeout to receiveUntil() and wait()
 * @see TelnetIO
 */
public class TelnetWrapper
{
  /** The telnet connection. That which is wrapped. */
  TelnetIO tio;
  /** Set to true for System.out.println debugging. */
  public boolean debug = false;
  /** The current prompt on the remote system. */
  private String prompt;

  /** The default prompt used by all TelnetWrappers unless specifically
   * overridden.
   * @see #setPrompt
   */
  private static String defaultPrompt = "$ ";

  /** The default login name used by TelnetWrappers.
   * If defaultLogin and defaultPassword are both non-null
   * when a TelnetWrapper is created, the TelnetWrapper will attempt
   * to login.
   */
  private static String defaultLogin = null;

  /** The default password used by TelnetWrappers.
   * If defaultLogin and defaultPassword are both non-null
   * when a TelnetWrapper is created, the TelnetWrapper will attempt
   * to login.
   */
  private static String defaultPassword = null;
  
  /** Skip any received data until the token appears. 
   * More efficient than receiveUntil, but liable to fail on large
   * tokens that can be spread over several "send"s. In that case,
   * consider using receiveUntil and ignoring the return value.
   * @param token String to wait for
   * @exception IOException on problems with the socket connection
   * @see #receiveUntil
   */
  public void wait(String token) throws IOException
  {
    wait(token, -1);
  }

  /** Wait for a String or a timeout. 
   * If time runs out, throws a TimedOutException.
   * Sleeps in intervals of 100 milliseconds until either receiving the
   * token or timeout.
   * <P>
   * More efficient than receiveUntil, but liable to fail on large
   * tokens that can be spread over several "send"s. In that case,
   * consider using receiveUntil and ignoring the return value.
   * @param token String to wait for
   * @param timeout time in milliseconds to wait (negative means wait forever)
   * @exception IOException on problems with the socket connection
   * @exception TimedOutException if time runs out before token received
   * @see #receiveUntil(String, long)
   */
  public void wait(String token, long timeout) 
    throws IOException, TimedOutException
  {
    if(debug) System.out.println("wait(" + token + ", " + timeout + ")...");
    String tmp = "";
    long deadline = 0;
    if(timeout >= 0) 
      deadline = new Date().getTime() + timeout;
    
    do {
      if(timeout >= 0)
      {
	while(available() <= 0)
	{
	  if(new Date().getTime() > deadline) 
	    throw new TimedOutException();
	  try{
	    Thread.currentThread().sleep(100);
	  }
	  catch(InterruptedException ignored)
	  {}
	}
      }
      tmp = receive();
    } while(tmp.indexOf(token) == -1);
    if(debug) System.out.println("wait(" + token  + ", " + timeout + 
				 ") successful.");
  }

  /** Returns bytes available to be read.  Since they haven't been
   * negotiated over, this could be misleading...
   */
  public int available() throws IOException
  {
    return tio.available();
  }
	
  /** Returns a String from the telnet connection. Blocks
   * until one is available. No guarantees that the string is in
   * any way complete.
   * NOTE: uses Java 1.0.2 style String-bytes conversion.*/
  public String receive() throws IOException
  {
    String s = new String(receiveBytes(), 0);
    if(debug) System.out.println(s);
    return s;
  }

  /** Returns a byte array. Blocks until data is available. */
  public byte[] receiveBytes() throws IOException
  {
    return tio.receive();
  }

  /** Returns all data received up until a certain token. 
   * @param token String to wait for
   * @exception IOException on problems with the socket connection
   * @see #wait
   */
  public String receiveUntil(String token) throws IOException
  {
    return receiveUntil(token, -1);
  }
  

  /** Returns all data received up until a certain token. 
   * @param token String to wait for
   * @param timeout time in milliseconds to wait (negative means wait forever)
   * @exception IOException on problems with the socket connection
   * @exception TimedOutException if time runs out before token received
   * @see #wait(String, long)
   */
  public String receiveUntil(String token, long timeout) 
    throws IOException, TimedOutException
  {
    StringBuffer buf = new StringBuffer();
    long deadline = 0;
    if(timeout >= 0) 
      deadline = new Date().getTime() + timeout;
    do
    {
      if(timeout >= 0)
      {
	while(available() <= 0)
	{
	  if(new Date().getTime() > deadline) 
	    throw new TimedOutException();
	  try{
	    Thread.currentThread().sleep(100);
	  }
	  catch(InterruptedException ignored)
	  {}
	}
      }
      buf.append(receive());
    } while(buf.toString().indexOf(token) == -1);
    return buf.toString();
  }
  
  /** Sends a String to the remote host.
   * NOTE: uses Java 1.0.2 style String-bytes conversion.
   * @exception IOException on problems with the socket connection
   */
  public void send(String s) throws IOException
  {
    if(debug) System.out.println(s);
    byte[] buf = new byte[s.length()];
    s.getBytes(0, buf.length, buf, 0);
    tio.send(buf);
  }

  /** Sends a line to the remote host, returns all data before the prompt.
   * Since telnet seems to rely on carriage returns ('\r'), 
   * one will be appended to the sent string, if necessary.
   * @param command command line to send
   * @return whatever data the command produced before the prompt.
   * @see #setPrompt
   */
  public String sendLine(String command) throws IOException
  {
    if(command.charAt(command.length() -1) != '\r') 
      command += "\r";
    send(command);
    String s = receiveUntil(prompt);

    // telnet typically echoes the command with a \r\n ending...
    return s.substring(command.length() + 1, s.indexOf(prompt));
  }
  
  /** Sends bytes over the telnet connection. */
  public void send(byte[] buf) throws IOException
  {
    tio.send(buf);
  }
  
  /** Logs in as a particular user and password. 
    * Returns after receiving prompt. */
  public void login(String loginName, String password) throws IOException
  {
    wait("login:");
    send(loginName + "\r");
    wait("Password:");
    sendLine(password + "\r");
  }
    
  /** Connects to the default telnet port on the given host. 
   * If the defaultLogin and defaultPassword are non-null, attempts login. */
  public TelnetWrapper(String host) throws IOException
  {
    tio = new TelnetIO();
    setPrompt(defaultPrompt);
    tio.connect(host);
    if(defaultLogin != null && defaultPassword != null)
    {
      login(defaultLogin, defaultPassword);
    }
  }

  /** Connects to a specific telnet port on the given host. 
   * If the defaultLogin and defaultPassword are non-null, attempts login. */
  public TelnetWrapper(String host, int port) throws IOException
  {
    tio = new TelnetIO();
    setPrompt(defaultPrompt);
    tio.connect(host, port);
    if(defaultLogin != null && defaultPassword != null)
    {
      login(defaultLogin, defaultPassword);
    }
  }
  
  /** Sets the expected prompt. 
   * If this function is not explicitly called, the default prompt is used.
   * @see #setDefaultPrompt
   */
  public void setPrompt(String prompt)
  {
    if(prompt == null) throw new IllegalArgumentException("null prompt.");
    this.prompt = prompt;
  }

  /** Sets the default prompt used by all TelnetWrappers.
   * This can be specifically overridden for a specific instance.
   * The default prompt starts out as "$ " until this function is called.
   * @see #setPrompt
   */
  public static void setDefaultPrompt(String prompt)
  {
    if(prompt == null) throw new IllegalArgumentException("null prompt.");
    defaultPrompt = prompt;
  }

  /** Sets the default login used by TelnetWrappers.
   * If this method is called with non-null login and password,
   * all TelnetWrappers will attempt to login when first created.
   * @param login login name to use
   * @param password password to use
   * @see #login
   * @see #unsetLogin
   */
  public static void setLogin(String login, String password)
  {
    if(login == null || password == null)
      throw new IllegalArgumentException("null login or password.");
    defaultLogin = login;
    defaultPassword = password;
  }


  /** Turns off the default login of TelnetWrappers.
   * After this method is called, TelnetWrappers will not
   * login until that method is explicitly called.
   * @see #setLogin
   * @see #login
   */
  public static void unsetLogin()
  {
    defaultLogin = defaultPassword = null;
  }
  
  /** Ends the telnet connection. */
  public void disconnect() throws IOException
  {
    if(tio != null) tio.disconnect();
    tio = null;
  }
  
  /** Ends the telnet connection. */
  public void finalize()
  {
    try
    {
      disconnect();
    }
    catch(IOException e)
    {} // after all, what can be done at this point?
  }  

  /** Telnet test driver.
   * Modeled after the IOtest.java example in the Telnet Applet.
   * Logs in to "host", creates a timestamped file in /tmp, lists the
   * /tmp directory to System.out, disconnects.  Shows off several
   * TelnetWrapper methods.
   * @param args host login password prompt
   */
  public static void main(String args[]) throws IOException
  {
    if(args.length != 4) throw new 
      IllegalArgumentException("Usage: TelnetWrapper host login password prompt");
    
    String host = args[0];
    String login = args[1];
    String password = args[2];
    String prompt = args[3];

    Date now = new Date();
    String timestamp = now.getYear() + "-" +
		(now.getMonth()+1) + "-" + now.getDate() + "-" +
		  now.getHours() + ":" + now.getMinutes() + ":" +
		    now.getSeconds();
    TelnetWrapper telnet = new TelnetWrapper(host);
    telnet.debug = true;

    // setting the correct prompt ahead of time is very important 
    // if you want to use login and sendLine
    telnet.setPrompt(prompt);
    telnet.login(login, password);

    // this is how you have to do it otherwise
    telnet.send("touch /tmp/TELNET_WRAPPER-" + timestamp + "\r");
    telnet.wait(prompt);

    // sendLine 1: adds the \r automatically, 2: waits for the prompt
    // before returning 3: returns what was printed from the command
    String ls = telnet.sendLine("ls /tmp");
    System.out.println(ls);

    // clean up
    telnet.disconnect();
  }
}