Initial revision
authorArt Cancro <ajc@citadel.org>
Tue, 27 Jul 1999 21:34:40 +0000 (21:34 +0000)
committerArt Cancro <ajc@citadel.org>
Tue, 27 Jul 1999 21:34:40 +0000 (21:34 +0000)
19 files changed:
shaggy/NamedPanel.java [new file with mode: 0644]
shaggy/PairPanel.java [new file with mode: 0644]
shaggy/Panel3D.java [new file with mode: 0644]
shaggy/citFrame.java [new file with mode: 0644]
shaggy/citPanel.java [new file with mode: 0644]
shaggy/citReply.java [new file with mode: 0644]
shaggy/citadel.java [new file with mode: 0644]
shaggy/displayInfo.java [new file with mode: 0644]
shaggy/enterPanel.java [new file with mode: 0644]
shaggy/expressWindow.java [new file with mode: 0644]
shaggy/loginPanel.java [new file with mode: 0644]
shaggy/logoffPanel.java [new file with mode: 0644]
shaggy/mainPanel.java [new file with mode: 0644]
shaggy/messagePanel.java [new file with mode: 0644]
shaggy/net.java [new file with mode: 0644]
shaggy/pageWindow.java [new file with mode: 0644]
shaggy/passwordWindow.java [new file with mode: 0644]
shaggy/server.java [new file with mode: 0644]
shaggy/user.java [new file with mode: 0644]

diff --git a/shaggy/NamedPanel.java b/shaggy/NamedPanel.java
new file mode 100644 (file)
index 0000000..567b9f8
--- /dev/null
@@ -0,0 +1,165 @@
+import java.awt.*;
+
+public class NamedPanel extends Panel {
+       public static final int         LEFT=0, CENTER=1, RIGHT=2;
+       public static final int         TOP=0, MIDDLE=1, BOTTOM=2;
+
+       boolean         clean;
+
+       int             x_tl, y_tl;
+       int             x_tlm, y_tlm;
+       int             x_trm, y_trm;
+       int             x_tr, y_tr;
+       int             x_br, y_br;
+       int             x_bl, y_bl;
+
+       String          name;
+       int             h_alignment, v_alignment;
+
+       int             name_width, name_height, name_a_height;
+       Color           back, nw, se;
+       Insets          border, pane;
+       Insets          theInsets;
+       Dimension       old_size;
+
+       NamedPanel( String name ) {
+               this( name, LEFT, MIDDLE );
+               }
+
+       NamedPanel( String name, int h_alignment ) {
+               this( name, h_alignment, MIDDLE );
+               }
+
+       NamedPanel( String name, int h_alignment, int v_alignment ) {
+               border = new Insets( 4, 4, 4, 4 );
+               pane = new Insets( 4, 4, 4, 4 );
+
+               theInsets = addInsets( border, pane );
+
+               this.name = name;
+               this.h_alignment = h_alignment;
+               this.v_alignment = v_alignment;
+               clean = false;
+               }
+
+       public void addNotify() {
+               super.addNotify();
+               fontBox();
+
+               theInsets = addInsets( border, pane );
+               theInsets.top += name_height;
+               validate();
+               }
+
+       final public Insets addInsets( Insets i1, Insets i2 )
+               {
+               return new Insets( i1.top + i2.top, i1.left + i2.left, i1.bottom + i2.bottom, i1.right + i2.right );
+               }
+
+       public Insets insets() {
+               return theInsets;
+               }
+
+       public Insets getInsets() {
+               return theInsets;
+               }
+
+       public void setLabel( String name ) {
+               this.name = name;
+               clean = false;
+               repaint();
+               }
+
+       public void align( int alignment ) {
+               h_alignment = alignment;
+               clean = false;
+               }
+
+       public void paint( Graphics g ) {
+               super.paint( g );
+
+               Dimension       tmp = size();
+               
+               if( (!clean) || ((tmp.width != old_size.width) || (tmp.height != old_size.height) )  ) {
+                       old_size = tmp;
+
+                       calculateCrap();
+                       clean = true;
+//                     g.clearRect( 0, 0, tmp.width, tmp.height);
+                       }
+
+               g.setColor( nw );
+               g.drawLine( x_tl, y_tl, x_tlm, y_tlm );
+               g.drawLine( x_trm, y_trm, x_tr, y_tr );
+               g.drawLine( x_bl, y_bl, x_tl, y_tl );
+
+               g.drawLine( x_tr-1, y_tr+1, x_br-1, y_br-1 );
+               g.drawLine( x_bl+1, y_bl-1, x_br-1, y_br-1 );
+
+               g.drawLine( x_trm, y_trm, x_trm, y_trm+1 );
+
+               g.setColor( se );
+               g.drawLine( x_tl+1, y_tl+1, x_tlm-1, y_tlm+1 );
+               g.drawLine( x_trm+1, y_trm+1, x_tr-1, y_tr+1 );
+               g.drawLine( x_bl+1, y_bl-1, x_tl+1, y_tl+1 );
+
+               g.drawLine( x_tr, y_tr+1, x_br, y_br );
+               g.drawLine( x_bl+1, y_bl, x_br, y_br );
+
+               g.drawLine( x_tlm, y_tlm, x_tlm, y_tlm+1 );
+
+               g.setColor( getForeground() );
+               g.drawString( name, x_tlm + 5, border.top+name_a_height );
+               }
+
+       void fontBox() {
+               FontMetrics     fm = getFontMetrics( getFont() );
+
+               name_height = fm.getMaxAscent() + fm.getMaxDescent();
+               name_a_height = fm.getMaxAscent();
+               name_width = fm.stringWidth( name );
+               }
+
+       void calculateCrap() {
+               fontBox();
+
+               back = getBackground();
+               se = back.brighter();
+               nw = back.darker();
+
+               x_tl = border.left;
+               y_tl = border.top;
+
+               switch( v_alignment ) {
+                       default: break;
+                       case MIDDLE: y_tl += name_height/2; break;
+                       case BOTTOM: y_tl += name_height; break;
+                       }
+
+               x_tr = old_size.width - border.left;
+               y_tr = y_tl;
+
+               x_br = x_tr;
+               y_br = old_size.height - border.bottom;
+
+               x_bl = x_tl;
+               y_bl = y_br;
+
+               y_tlm = y_trm = y_tl;
+
+               switch( h_alignment ) {
+                       default:
+                               x_tlm = x_tl + 20;
+                               x_trm = x_tlm + name_width + 8;
+                               break;
+                       case CENTER:
+                               x_tlm = (x_tl + (x_tr - x_tl)/2) - name_width/2 - 5;
+                               x_trm = x_tlm + name_width + 8;
+                               break;
+                       case RIGHT:
+                               x_trm = x_tr - 5;
+                               x_tlm = x_trm - (name_width + 8);
+                               break;
+                       }
+               }
+       }
diff --git a/shaggy/PairPanel.java b/shaggy/PairPanel.java
new file mode 100644 (file)
index 0000000..e300554
--- /dev/null
@@ -0,0 +1,41 @@
+/* PairPanel.java
+ * Utility class so I don't have to think about GridBagLayout
+ */
+
+import java.awt.*;
+
+public class PairPanel extends Panel
+       {
+       GridBagLayout           gbLayout;
+       GridBagConstraints      gbLeft, gbRight;
+
+       PairPanel() {
+               this( 0, 0 );
+               }
+
+       PairPanel( int x, int y ) {
+               setLayout( gbLayout = new GridBagLayout() );
+
+               gbLeft = new GridBagConstraints();
+               gbLeft.gridwidth = 1;
+               gbLeft.ipadx = x;
+               gbLeft.ipady = y;
+               gbLeft.anchor = GridBagConstraints.EAST;
+
+               gbRight = new GridBagConstraints();
+               gbRight.ipadx = x;
+               gbRight.ipady = y;
+               gbRight.gridwidth = GridBagConstraints.REMAINDER;
+               gbRight.anchor = GridBagConstraints.WEST;
+               }
+
+       public void addLeft( Component c ) {
+               gbLayout.setConstraints( c, gbLeft );
+               add( c );
+               }
+
+       public void addRight( Component c ) {
+               gbLayout.setConstraints( c, gbRight );
+               add( c );
+               }
+       }
diff --git a/shaggy/Panel3D.java b/shaggy/Panel3D.java
new file mode 100644 (file)
index 0000000..25479c6
--- /dev/null
@@ -0,0 +1,74 @@
+import java.awt.*;
+
+public class Panel3D extends Panel
+       {
+       static final int        LOWER = 1;
+       static final int        HIGHER = 0;
+       static final int        RIDGE = 2;
+
+       public Insets   outside, ridge, plane;
+       int             state = 0;
+       
+       public Panel3D( )
+               {
+               setUp( HIGHER, null, null, null );
+               }
+
+       public Panel3D( int state )
+               {
+               setUp( state, null, null, null );
+               this.state = state;
+               }
+       
+       public Panel3D( int state, Insets outside )
+               {
+               setUp( state, outside, null, null );
+               }
+       
+       public Panel3D( int state, Insets outside, Insets ridge )
+               {
+               setUp( state, outside, ridge, null );
+               }
+       
+       public Panel3D( int state, Insets outside, Insets ridge, Insets plane )
+               {
+               setUp( state, outside, ridge, plane );
+               }
+
+       public void setUp( int state, Insets outside, Insets ridge, Insets plane )
+               {
+               this.state              = state;
+               
+               this.outside    = (outside == null)     ? new Insets( 3, 3, 3, 3 ) : outside;
+               this.ridge              = addInsets( this.outside, (ridge == null) ? new Insets( 3, 3, 3, 3 ) : ridge );
+               this.plane              = addInsets( ( (state & RIDGE) == RIDGE) ? this.ridge: this.outside, (plane == null ) ? new Insets( 4, 4, 4, 4 ) : plane );
+               }
+
+       final public Insets addInsets( Insets i1, Insets i2 )
+               {
+               return new Insets( i1.top + i2.top, i1.left + i2.left, i1.bottom + i2.bottom, i1.right + i2.right );
+               }
+
+       public void paint( Graphics g )
+               {
+               super.paint( g );
+               Dimension d = size();
+               Color bg = getBackground();
+
+               g.setColor(bg);
+               g.draw3DRect( outside.left, outside.top, d.width - outside.right - outside.left, d.height - outside.top - outside.bottom, (state & LOWER) != LOWER );
+               if( (state & RIDGE) == RIDGE )
+                       g.draw3DRect( ridge.left, ridge.top, d.width - ridge.right - ridge.left, d.height - ridge.top - ridge.bottom, (state & LOWER) == LOWER ); 
+
+               }
+
+       public Insets insets() {
+               return plane;
+               }
+
+       public Insets getInsets() {
+               return plane;
+               }
+       }
+       
+
diff --git a/shaggy/citFrame.java b/shaggy/citFrame.java
new file mode 100644 (file)
index 0000000..2c60f99
--- /dev/null
@@ -0,0 +1,31 @@
+/* citFrame.java
+ * shell for citPanel, so we can be an applet and application
+ */
+
+import java.awt.*;
+
+public class citFrame extends Frame {
+  citPanel     cp;
+
+  public citFrame() {
+    super( "Citadel!" );
+
+    setLayout( new BorderLayout() );
+
+    add( "Center", cp = new citPanel() );
+
+    resize( 400, 400 );
+    show();
+  }
+
+  public boolean handleEvent( Event e ) {
+    if( e.id == Event.WINDOW_DESTROY ) {
+      if( citadel.me.theNet != null )
+       citadel.me.theNet.println( "QUIT" );
+      System.out.println( "Bye bye!" );
+      System.exit( 0 );
+    }
+    return super.handleEvent( e );
+  }
+}
+
diff --git a/shaggy/citPanel.java b/shaggy/citPanel.java
new file mode 100644 (file)
index 0000000..57c4f4c
--- /dev/null
@@ -0,0 +1,57 @@
+/* citPanel.java
+ * The daddy of the mack daddy.
+ */
+
+import java.awt.*;
+
+public class citPanel extends Panel {
+  loginPanel   lp;
+  mainPanel    mp;
+  messagePanel msgp;
+  enterPanel   ep;
+  logoffPanel  offP;
+
+  CardLayout   deck;
+
+  public  citPanel() {
+    setLayout( deck = new CardLayout() );
+
+    add( "Login", lp = new loginPanel() );
+    add( "Main", mp = new mainPanel() );
+    add( "Message", msgp = new messagePanel() );
+    add( "Enter", ep = new enterPanel() );
+    add( "Logoff", offP = new logoffPanel() );
+
+    citadel.me.cp = this;
+    login();
+  }
+
+  public void login() {
+    deck.show( this, "Login" );
+    lp.refresh();
+  }
+
+  public void mainMenu() {
+    deck.show( this, "Main" );
+    mp.refresh();
+  }
+
+  public void enterRoom( citReply r ) {
+    deck.show( this, "Message" );
+    msgp.refresh( r );
+  }
+
+  public void enterMsg( String room ) {
+    enterMsg( room, null );
+  }
+
+  public void enterMsg( String room, String recip ) {
+    deck.show( this, "Enter" );
+    ep.refresh( room, recip );
+  }
+
+  public void logoff( String err ) {
+    offP.refresh( err );
+    deck.show( this, "Logoff" );
+  }
+}
diff --git a/shaggy/citReply.java b/shaggy/citReply.java
new file mode 100644 (file)
index 0000000..a63f959
--- /dev/null
@@ -0,0 +1,130 @@
+/* citReply.java
+ *
+ * Object to parse the reply from the server, so I don't have to think
+ * about it when I write code.
+ */
+
+import java.util.*;
+
+public class citReply {
+  public static final int      LISTING_FOLLOWS=100, OK=200, MORE_DATA=300,
+    SEND_LISTING=400, ERROR=500, BINARY_FOLLOWS=600, SEND_BINARY=700,
+    START_CHAT_MODE=800;
+
+  int          res_code;
+  String       line,data;
+  Vector       args, listing;
+  boolean      expressmsg;
+
+  public citReply( String line ) {
+    this.line = line;
+
+    args = new Vector();
+    listing = null;
+    data = null;
+    res_code = ERROR;
+    expressmsg = false;
+
+    parseLine();
+  }
+
+  public void parseLine() {
+    try {
+      res_code = Integer.parseInt( line.substring( 0, 3 ) );
+    } catch( Exception e ) {};
+
+    StringBuffer       s = new StringBuffer();
+
+    if( (line.length() > 3) && (line.charAt( 3 ) == '*') )
+      expressmsg = true;
+
+    for( int i = 4; i < line.length(); i++ ) {
+      char     c = line.charAt( i );
+      if( c == '|' ) {
+       args.addElement( s.toString() );
+       s = new StringBuffer();
+      }
+      else
+       s.append( c );
+    }
+    if( s.length() != 0 ) args.addElement( s.toString() );
+
+  }
+
+  public boolean expressMessage() {
+    return expressmsg;
+  }
+
+  public boolean listingFollows() {
+    return res_code/100 == LISTING_FOLLOWS/100;
+  }
+
+  public boolean ok() {
+    return res_code/100 == OK/100;
+  }
+
+  public boolean moreData() {
+    return res_code/100 == MORE_DATA/100;
+  }
+
+  public boolean sendListing() {
+    return res_code/100 == SEND_LISTING/100;
+  }
+
+  public boolean error() {
+    return res_code/100 == ERROR/100;
+  }
+
+  public  boolean binaryFollows() {
+    return res_code/100 == BINARY_FOLLOWS/100;
+  }
+
+  public boolean sendBinary() {
+    return res_code/100 == SEND_BINARY/100;
+  }
+
+  public boolean startChatMode() {
+    return res_code/100 == START_CHAT_MODE/100;
+  }
+
+  public boolean addData( String s ) {
+    if( s.equals( "000" ) )
+      return false;
+
+    if( listing == null ) listing = new Vector();
+    listing.addElement( s );
+    return true;
+  }
+
+  public String getLine( int i ) {
+    if( listing == null ) return null;
+
+    if( (i<0) || (i>=listing.size()) ) return null;
+
+    return (String)listing.elementAt( i );
+  }
+
+  public String getData() {
+    if( data != null ) return data;
+
+    StringBuffer       s = new StringBuffer();
+
+    for( Enumeration e=listing.elements(); e.hasMoreElements(); ) {
+      s.append( (String)e.nextElement() );
+      s.append( "\n" );
+    }
+
+    data = s.toString();
+
+    return data;
+  }
+
+  public String getArg( int i ) {
+    if( args == null ) return "";
+
+    if( (i<0) || (i>=args.size()) ) return "";
+
+    return (String)args.elementAt( i );
+  } 
+}
+
diff --git a/shaggy/citadel.java b/shaggy/citadel.java
new file mode 100644 (file)
index 0000000..33ba5b7
--- /dev/null
@@ -0,0 +1,145 @@
+/* citadel.java
+ *
+ * the "main" object
+ */
+
+public class citadel {
+  String                       host;
+  boolean                      applet;
+  net                          theNet;
+  server                       serverInfo;
+  user                         theUser;
+  citPanel                     cp;
+
+  boolean                      floors;
+
+  public static citadel        me;
+
+  public static void main( String args[] ) {
+    citadel cb = new citadel( false );
+    if( args.length > 0 )
+      cb.openConnection( args[0] );
+    else
+      cb.openConnection();
+    citFrame cf = new citFrame();
+  }
+
+  public citadel( boolean applet ) {
+    me = this;
+    this.applet = false;
+    theUser = null;
+  }
+
+  public void lostNetwork( String reason ) {
+    theNet = null;
+    cp.logoff( "lost network connection: " + reason );
+  }
+
+  public boolean openConnection( ) {
+    return openConnection( "127.0.0.1" );
+  }
+
+  public boolean openConnection( String host ) {
+    this.host = host;
+    if( theNet == null ) theNet = new net( );
+
+    if( theNet.connect(host) ) {
+      System.out.println( "Connected to server." );
+
+      citReply rep = theNet.getReply( "INFO" );
+      if( rep.listingFollows() )
+       serverInfo = new server( rep );
+
+      return true;
+    }
+    else {
+      System.out.println( "Couldn't connect to server." );
+    }
+    return false;
+  }
+
+  public String getBlurb() {
+    if( serverInfo != null ) return serverInfo.blurb;
+    return "";
+  }
+
+  public String getSystemMessage( String name ) {
+    citReply rep = theNet.getReply( "MESG " + name );
+    if( rep.listingFollows() )
+      return rep.getData();
+    else
+      return "Couldn't find " + name;
+  }
+
+  public void loggedIn( citReply r ) {
+    theUser = new user( r );
+    floors = serverInfo.floor_flag != 0;
+    floors &= theUser.floors();
+    cp.mainMenu();
+  }
+
+  public citReply getReply( String s ) {
+    if( theNet == null ) return null;
+
+    return theNet.getReply( s );
+  }
+
+  public void enterRoom( String room ) {
+    enterRoom( room, null );
+  }
+
+  public void enterRoom( String room, String pass ) {
+    String cmd = "GOTO " + room;
+    if( pass != null )
+      cmd = cmd + " " + pass;
+    citReply   r=getReply( cmd );
+    if( r.ok() ) {
+      cp.mp.visited( room );
+      cp.enterRoom( r );
+    } else if( r.res_code == 540 ) /* ERROR+PASSWORD_REQUIRED */
+      new passwordWindow( room );
+  }
+
+  public void enterMsg( String room ) {
+    cp.enterMsg( room );
+  }
+
+  public void nextNewRoom() {
+    enterRoom( cp.mp.nextNewRoom() );
+  }
+
+  public void expressMsg() {
+    citReply   r=getReply( "GEXP" );
+    System.out.println( "EXPRESS MSG" );
+    if( !r.error() ) new expressWindow( r );
+  }
+
+  public void sendMessage( String body, String rec, boolean mail ) {
+    String cmd = "ENT0 1|";
+    if( mail ) cmd = cmd + rec;
+    cmd = cmd + "|0|0|0";
+
+    citReply   r = getReply( cmd );
+    if( r.sendListing() ) {
+      theNet.println( body );
+      theNet.println( "000" );
+    }
+  }
+
+  public void logoff() {
+    cp.logoff(null);
+    theNet.println( "quit" );
+  }
+
+  public void who_online() {
+    System.out.println( "Who, pray tell, is online?" );
+  }
+
+  public void page_user() {
+    page_user( null );
+  }
+
+  public void page_user( String who ) {
+    new pageWindow( who );
+  }
+}
diff --git a/shaggy/displayInfo.java b/shaggy/displayInfo.java
new file mode 100644 (file)
index 0000000..b6cde78
--- /dev/null
@@ -0,0 +1,42 @@
+/* displayInfo.java
+ * Display room's info
+ */
+
+import java.awt.*;
+
+public class displayInfo extends Frame {
+  String       name;
+  TextArea     text;
+
+  public displayInfo( String name ) {
+    super( "Info for : " + name );
+
+    this.name = name;
+    setLayout( new BorderLayout() );
+
+    add( "Center", text = new TextArea() );
+    Panel p;
+    add( "South", p = new Panel() );
+    p.add( new Button( "Close" ) );
+
+    citReply   r = citadel.me.getReply( "RINF" );
+    if( r.listingFollows() )
+      text.append( r.getData() );
+    else
+      text.append( "<no room info>" );
+    resize( 300, 300 );
+    show();
+  }
+
+  public boolean handleEvent( Event e ) {
+    if( e.id == Event.WINDOW_DESTROY )
+      dispose();
+    return super.handleEvent( e );
+  }
+
+  public boolean action( Event e, Object o ) {
+    if( e.target instanceof Button )
+      dispose();
+    return super.action( e, o );
+  }
+}
diff --git a/shaggy/enterPanel.java b/shaggy/enterPanel.java
new file mode 100644 (file)
index 0000000..36241e3
--- /dev/null
@@ -0,0 +1,76 @@
+/* enterPanel.java
+ * For entering messages
+ */
+
+import java.awt.*;
+
+public class enterPanel extends Panel {
+  boolean      mail;
+  String       room, recip;
+  NamedPanel   np;
+
+  TextField    who;
+  TextArea     msg;
+
+  Button       ok, cancel;
+
+  public enterPanel() {
+    PairPanel  pp = new PairPanel();
+    pp.addLeft( new Label( "Recipient : " ) );
+    pp.addRight( who = new TextField( 10 ) );
+
+    setLayout( new BorderLayout() );
+    add( "North", pp );
+
+    np = new NamedPanel( "room name" );
+    np.setLayout( new BorderLayout() );
+    np.add( "Center", msg = new TextArea() );
+
+    add( "Center", np );
+
+    Panel p = new Panel();
+
+    p.add( ok = new Button ( "Send" ) );
+    p.add( cancel = new Button ( "Cancel" ) );
+    add( "South", p );
+    mail = false;
+  }
+
+  public void refresh( String room, String r ) {
+    if( room.equalsIgnoreCase( "Mail" ) ) {
+      who.enable();
+      who.setText( r );
+      mail = true;
+    } else {
+      who.disable();
+      who.setText( "DISABLED" );
+      mail = false;
+    }
+
+    msg.setText( "" );
+
+    np.setLabel( room );
+    this.room = room;
+    this.recip = r;
+
+    msg.enable();
+    if( !mail ) {
+      citReply rep = citadel.me.getReply( "ENT0 0|0|0|0|0" );
+      if( !rep.ok() ) {
+       msg.setText( "You aren't allowed to post here." );
+       msg.disable();
+      }
+    }
+  }
+
+  public boolean action( Event e, Object o ) {
+    if( e.target == ok ) {
+      citadel.me.sendMessage( msg.getText(), who.getText(), mail );
+      citadel.me.cp.deck.show( citadel.me.cp, "Message" ); // Umm... this is bad
+    } else if( e.target == cancel ) {
+      citadel.me.cp.deck.show( citadel.me.cp, "Message" ); // Umm... this is bad    } else if( e.taget == who ) {
+      msg.requestFocus();
+    }
+    return super.action( e, o );
+  }
+}
diff --git a/shaggy/expressWindow.java b/shaggy/expressWindow.java
new file mode 100644 (file)
index 0000000..aea6949
--- /dev/null
@@ -0,0 +1,49 @@
+/* expressWindow.java
+ * for showing express messages...
+ */
+
+import java.awt.*;
+
+public class expressWindow extends Frame {
+  String       user;
+  TextArea     msg;
+  Button       reply, ok;
+
+  public expressWindow( citReply r ) {
+    user = r.getArg( 3 );
+    
+    setTitle( user + " : express message" );
+
+    setLayout( new BorderLayout() );
+    NamedPanel np = new NamedPanel( "Message" );
+
+    np.setLayout( new BorderLayout() );
+    np.add( "Center", msg = new TextArea() );
+    msg.append( r.getData() );
+
+    add( "Center", np );
+
+    Panel p = new Panel();
+    p.add( reply = new Button( "Reply" ) );
+    p.add( ok = new Button( "OK" ) );
+    add( "South", p );
+
+    resize( 300, 300 );
+    show();
+  }
+
+  public boolean handleEvent( Event e ) {
+    if( e.id == Event.WINDOW_DESTROY )
+      dispose();
+    return super.handleEvent( e );
+  }
+
+  public boolean action( Event e, Object o ) {
+    if( e.target == reply ) {
+      dispose();
+      citadel.me.page_user( user );
+    } else if( e.target == ok )
+      dispose();
+    return super.action( e, o );
+  }
+}
diff --git a/shaggy/loginPanel.java b/shaggy/loginPanel.java
new file mode 100644 (file)
index 0000000..7fdd564
--- /dev/null
@@ -0,0 +1,89 @@
+/* loginPanel.java
+ * login screen
+ */
+
+import java.awt.*;
+
+public class loginPanel extends Panel {
+  TextArea     text;
+  TextField    user, pass;
+  Button       login;
+
+  public loginPanel() {
+    setLayout( new BorderLayout() );
+
+    String     blurb = citadel.me.getBlurb();
+    if( blurb.length() == 0 ) blurb = "Citadel";
+
+    add( "North", new Label( blurb ) );
+    add( "Center", text = new TextArea() );
+
+    text.setFont( new Font( "Courier", Font.PLAIN, 12 ) );
+
+    Panel      p = new Panel();
+    p.setLayout( new BorderLayout() );
+
+    PairPanel  pp = new PairPanel();
+
+    pp.addLeft( new Label( "Username:" ) );
+    pp.addRight( user = new TextField( 10 ) );
+    pp.addLeft( new Label( "Password:" ) );
+    pp.addRight( pass = new TextField( 10 ) );
+    pass.setEchoCharacter( '.' );
+
+    p.add( "Center", pp );
+
+    Panel      ppp = new Panel();
+    ppp.add( login = new Button( "Login" ) );
+
+    p.add( "East", ppp );
+
+    add( "South", p );
+  }
+
+  public boolean handleEvent( Event e ) {
+    if( e.id == Event.GOT_FOCUS ) {
+      if( (e.target != user) && (e.target != pass) )
+       user.requestFocus();
+    }
+    return super.handleEvent( e );
+  }
+
+  public boolean action( Event e, Object o ) {
+    if( e.target == user ) {
+      pass.requestFocus();
+    }
+    if( (e.target == login) || (e.target == pass) ) {
+      String   u = user.getText(), p=pass.getText();
+
+      if( u.length() == 0 ) {
+       text.append( "\nNeed to enter in username.\n" );
+       user.requestFocus();
+      } else if (p.length() == 0 ) {
+       text.append( "\nNeed to enter in password.\n" );
+       pass.requestFocus();
+      } else {
+       citReply r = citadel.me.getReply( "USER " + u );
+       if( r.moreData() ) {
+         r = citadel.me.getReply( "PASS " + p );
+         if( r.error() ) {
+           text.append( "<bad password>" + r.getArg( 0 ) );
+         } else {
+           citadel.me.loggedIn( r );
+         }
+       } else {
+         text.append( "<bad user>" + r.getArg( 0 ) );
+       }
+      }
+    }
+    return super.action( e, o );
+  }
+
+  public void refresh() {
+    text.setText( citadel.me.getSystemMessage( "hello" ) );
+    user.setText( "" );
+    pass.setText( "" );
+  }
+}
+
+
diff --git a/shaggy/logoffPanel.java b/shaggy/logoffPanel.java
new file mode 100644 (file)
index 0000000..40501eb
--- /dev/null
@@ -0,0 +1,60 @@
+/* logoffPanel.java
+ * Display the "goodbye" message
+ */
+
+import java.awt.*;
+
+public class logoffPanel extends Panel {
+  TextArea     area;
+  TextField    host;
+  Button       connect,close;
+  boolean      applet;
+
+  public logoffPanel() {
+    applet = citadel.me.applet;
+    setLayout( new BorderLayout() );
+
+    NamedPanel np = new NamedPanel("Disconnected");
+    np.setLayout( new BorderLayout() );
+
+    np.add( "Center", area = new TextArea() );
+    area.setFont( new Font( "Courier", Font.PLAIN, 12 ) );
+
+    add( "Center", np );
+
+    Panel      p = new Panel();
+
+    if( !applet ) {
+      p.add( host = new TextField() );
+      host.setText( citadel.me.host );
+    }
+
+    p.add( connect = new Button ( "Connect" ) );
+
+    if( !applet )
+      p.add( close = new Button( "Close" ) );
+
+    add( "South", p );
+  }
+
+  public boolean action ( Event e, Object o ) {
+    if( e.target == connect ) {
+      if( !applet )
+       citadel.me.host = host.getText();
+
+      citadel.me.openConnection( citadel.me.host );
+      citadel.me.cp.login();
+    } else if ( e.target == close ) {
+      System.out.println( "Thanks!" );
+      System.exit( 0 );
+    }
+    return super.action( e, o );
+  }
+
+  public void refresh( String s ) {
+    if( s == null )
+      area.setText( citadel.me.getSystemMessage( "goodbye" ) );
+    else
+      area.setText( s );
+  }
+}
diff --git a/shaggy/mainPanel.java b/shaggy/mainPanel.java
new file mode 100644 (file)
index 0000000..4fcaf13
--- /dev/null
@@ -0,0 +1,120 @@
+/* mainPanel.java
+ */
+
+import java.awt.*;
+
+public class mainPanel extends Panel {
+  List         new_msgs, seen;
+  Button       next_room, goto_room;
+  Button       who_is_online, page_user;
+  Button       logout;
+
+  public mainPanel() {
+    setLayout( new BorderLayout() );
+
+    Panel      p = new Panel();
+    p.setLayout( new GridLayout( 1, 2 ) );
+
+    NamedPanel np = new NamedPanel( "New Messages" );
+    np.setLayout( new BorderLayout() );
+    np.add( "Center", new_msgs = new List() );
+
+    p.add( np );
+
+    np = new NamedPanel( "Seen Messages" );
+    np.setLayout( new BorderLayout() );
+    np.add( "Center", seen = new List() );
+    p.add( np );
+
+    add( "Center", p );
+
+    p = new Panel();
+    p.add( next_room = new Button ( "Next Room" ) );
+    p.add( goto_room = new Button ( "Goto Room" ) );
+    p.add( who_is_online = new Button( "Who is Online" ) );
+    p.add( page_user = new Button( "Page User" ) );
+    add( "North", p );
+
+    p = new Panel();
+    p.add( logout = new Button( "Logout" ) );
+    add( "South", p );
+  }
+
+  public boolean action( Event e, Object o ) {
+    if( (e.target == new_msgs) || (e.target == seen) || (e.target == goto_room)) {
+      String room = getRoom();
+      if( room != null )
+       citadel.me.enterRoom( room );
+    } else if( e.target == who_is_online ) {
+      citadel.me.who_online();
+    } else if( e.target == page_user ) {
+      citadel.me.page_user();
+    } else if (e.target == next_room ) {
+      citadel.me.nextNewRoom();
+    } else if( e.target == logout ) {
+      citadel.me.logoff();
+    } else {
+      return super.action( e, o );
+    }
+    return true;
+  }
+
+
+  public boolean handleEvent( Event e ) {
+    if( e.id == Event.LIST_SELECT ) {
+      if( e.target == new_msgs ) {
+       int     i = seen.getSelectedIndex();
+       if( i != -1 )
+         seen.deselect( i );
+      } else {
+       int     i = new_msgs.getSelectedIndex();
+       if( i != -1 )
+         new_msgs.deselect( i );
+      }
+    }
+    return super.handleEvent( e );
+  }
+
+  public void refresh() { 
+    new_msgs.clear(); 
+    parseRooms( new_msgs, citadel.me.getReply( "LKRN" ) );
+
+    seen.clear();
+    parseRooms( seen, citadel.me.getReply( "LKRO" ) );
+  }
+       
+  public void parseRooms( List l, citReply r ) {
+    int                i=0;
+    String     s;
+
+    while( (s = r.getLine( i++) ) != null ) {
+      int j = s.indexOf( '|' );
+      if( j != -1 )
+       l.addItem( s.substring( 0, j ) );
+      else
+       l.addItem( s );
+    }
+  }
+
+  public String getRoom() {
+    String s = new_msgs.getSelectedItem();
+    if( s == null ) s = seen.getSelectedItem();
+
+    return s;
+  }
+
+  public String nextNewRoom() {
+    if( new_msgs.countItems() == 0 ) return "Lobby";
+
+    return new_msgs.getItem( 0 );
+  }
+
+  public void visited( String room ) {
+    for( int i = 0; i < new_msgs.countItems(); i++ ) {
+      if( room.equals( new_msgs.getItem( i ) ) ) {
+       new_msgs.delItem( i );
+       seen.addItem( room );
+      }
+    }
+  }
+}
diff --git a/shaggy/messagePanel.java b/shaggy/messagePanel.java
new file mode 100644 (file)
index 0000000..2159fd7
--- /dev/null
@@ -0,0 +1,196 @@
+/* messagePanel.java
+ */
+
+import java.awt.*;
+import java.util.*;
+
+public class messagePanel extends Panel {
+  Choice       reading;
+  Button       who_is_online, room_info;
+  Button       next_room, page_user;
+  Button       next_msg, prev_msg, enter_msg, back;
+  TextField    msgInfo;
+  TextArea     theMsg;
+  NamedPanel   np;
+
+  Vector       msgs;
+  int          cur_pos = -1;
+
+  String       name;
+  int          total, unread, info, flags, highest, highest_read;
+  boolean      mail, aide;
+  int          mail_num, floor;
+
+  public messagePanel() {
+    setLayout( new BorderLayout() );
+
+    Panel      p = new Panel();
+    p.add( reading = new Choice() );
+    reading.addItem( "Read New" );
+    reading.addItem( "Read All" );
+    reading.addItem( "Last 5" );
+    reading.select( 0 );
+
+    //    p.add( who_is_online = new Button( "Who is Online" ) );
+    p.add( room_info = new Button( "Room Info" ) );
+    p.add( next_room = new Button( "Next Room" ) );
+    p.add( page_user = new Button( "Page User" ) );
+    add( "North", p );
+
+    np = new NamedPanel( "Message" );
+    np.setLayout( new BorderLayout() );
+    np.add( "North", msgInfo = new TextField() );
+    np.add( "Center", theMsg = new TextArea() );
+    add( "Center", np );
+
+    p = new Panel();
+    p.add( next_msg = new Button( "Next Message" ) );
+    p.add( prev_msg = new Button( "Prev Message" ) );
+    p.add( enter_msg = new Button( "Enter Message" ) );
+    p.add( back = new Button( "Back" ) );
+    add( "South", p );
+  }
+
+  public boolean action( Event e, Object o ) {
+    if( e.target == reading ) {
+      getMsgsPtrs();
+    } else if( e.target == page_user ) {
+      citadel.me.page_user();
+    } else if( e.target == who_is_online ) {
+      citadel.me.who_online();
+    } else if( e.target == room_info ) {
+      new displayInfo( name );
+    } else if( e.target == next_msg ) {
+      cur_pos++;
+      displayMessage();
+    } else if( e.target == prev_msg ) {
+      cur_pos--;
+      displayMessage();
+    } else if( e.target == enter_msg ) {
+      citadel.me.enterMsg( name );
+    } else if( e.target == next_room ) {
+      citadel.me.nextNewRoom();
+    } else if( e.target == back ) {
+      citadel.me.cp.mainMenu();
+    }
+    return super.action( e, o );
+  }
+
+  public void refresh( citReply r ) {
+    name = r.getArg( 0 );
+    np.setLabel( name );
+    total = atoi( r.getArg( 1 ) );
+    unread = atoi( r.getArg( 2 ) );
+    info = atoi( r.getArg( 3 ) );
+    flags = atoi( r.getArg( 4 ) );
+    highest = atoi( r.getArg( 5 ) );
+    highest_read = atoi( r.getArg( 6 ) );
+    mail = atoi( r.getArg( 7 ) ) != 0;
+    aide = atoi( r.getArg( 8 ) ) != 0;
+    mail_num = atoi( r.getArg( 9 ) ); 
+    floor = atoi( r.getArg( 10 ) );
+
+    if( info != 0 ) new displayInfo( name );
+    getMsgsPtrs();
+  }
+
+  public int atoi( String s ) {
+    try {
+      return Integer.parseInt( s );
+    } catch( Exception e ) {
+      return 0;
+    }
+  }
+
+  public void getMsgsPtrs() {
+    msgs = null;
+    String     which = "new";
+    if( reading.getSelectedIndex() == 1 ) which = "all";
+    else if( reading.getSelectedIndex() == 2 ) which = "last|5";
+    citReply   r = citadel.me.getReply( "MSGS " + which );
+    if( !r.error() ) {
+      msgs = r.listing;
+      cur_pos = 0;
+    }
+    displayMessage();
+  }
+
+  public void displayMessage() {
+    if( msgs == null) { 
+      msgInfo.setText( "<no messages>" );
+      theMsg.setText( "" );
+      return;
+    }
+
+    if( cur_pos <= 0 ) {
+      cur_pos = 0;
+      prev_msg.disable();
+    } else
+      prev_msg.enable();
+
+    if( cur_pos >= msgs.size()-1 ) {
+      cur_pos = msgs.size()-1;
+      next_msg.disable();
+    } else
+      next_msg.enable();
+
+    String     num = (String)msgs.elementAt( cur_pos );
+
+    citReply   r = citadel.me.getReply( "MSG0 " + num + "|0" );
+    if( r.error() ) {
+      msgInfo.setText( "<error>" );
+      theMsg.setText( r.getArg(0) );
+      return;
+    }
+
+    int        n = atoi( num );
+    if( n > highest_read ) {
+      highest_read = n;
+      citadel.me.getReply( "SLRP " + num );
+    }
+
+    Vector     msg = r.listing;
+    String     s, from="", time="", room="", node="", rcpt="";
+    do {
+      s = (String)msg.firstElement();
+      System.out.println( s );
+      msg.removeElementAt( 0 );
+      from = begin( from, s, "from=" );
+      time = begin( time, s, "time=" );
+      room = begin( room, s, "room=" );
+      node = begin( node, s, "hnod=" );
+      rcpt = begin( rcpt, s, "rcpt=" );
+    } while( (msg.size() > 0) && (!s.equals( "text" )) );
+
+    time = makeDate( time );
+
+    String     sum = (cur_pos+1) + "/" + msgs.size() + " " + time + " from " + from;
+    if( !node.equals( citadel.me.serverInfo.human_name ) )
+      sum = sum + " (@"+node+")";
+    if( !rcpt.equals( "" ) )
+      sum = sum + " to " + rcpt;
+    if( !room.equals(name) )
+      sum = sum + " in " + room;
+   
+    msgInfo.setText( sum );
+    theMsg.setText( r.getData() ); /* this relies on the fact that we've removed the header lines above.  probably a messy way to deal with references. */
+  }
+
+  public String makeDate( String time ) {
+    long       t=0;
+    try {
+      t = Long.parseLong( time );
+    } catch( NumberFormatException nfe ) {
+    };
+
+    Date       d = new Date( t*1000 );
+    return d.toLocaleString();
+  }
+
+  public String begin( String def, String data, String key ) {
+    if( data.indexOf( key ) == 0 )
+      return data.substring( key.length() );
+    return def;
+  }
+                                                 
+}
diff --git a/shaggy/net.java b/shaggy/net.java
new file mode 100644 (file)
index 0000000..0ee98fb
--- /dev/null
@@ -0,0 +1,99 @@
+/* net.java
+ * chilly@alumni.psu.edu
+ *
+ * Object to talk the network talk with cit/ux server.
+ * Dispatches events.
+ */
+
+import java.io.*;
+import java.net.*;
+
+public class net {
+  Socket               theServer;
+
+  DataInputStream      in;
+  DataOutputStream     out;
+
+  public net() {
+    theServer = null;
+  }
+
+  public void println( String s ) {
+    System.out.println( ">" + s );
+    try {
+      if( theServer != null )
+       out.writeBytes( s + "\n" );
+    } catch( IOException ioe ) {
+      citadel.me.lostNetwork( "Connection dropped (write)" );
+      if( theServer != null ) {
+       try { theServer.close(); theServer = null; }
+       catch( Exception e ) {};
+      }
+    }
+  }
+
+  public String readLine( ) {
+    try {
+      if( theServer != null ) {
+       String s = in.readLine();
+       System.out.println( "<" + s );
+       return s;
+      }
+    } catch( IOException ioe ) {
+      citadel.me.lostNetwork( "Network error: reead" );
+      if( theServer != null ) {
+       try { theServer.close(); theServer = null; }
+       catch( Exception e ) {};
+      }
+    }
+    return null;
+  }
+
+  public boolean connect( ) {
+    return connect( "127.0.0.1" );
+  }
+
+  public boolean connect( String serverName ) {
+    return connect( serverName, 504 );
+  }
+
+  public boolean connect( String serverName, int port ) {
+    try {
+      theServer = new Socket( serverName, port );
+
+      in = new DataInputStream( theServer.getInputStream() );
+      out = new DataOutputStream( theServer.getOutputStream() );
+
+    } catch( IOException ioe ) {
+      citadel.me.lostNetwork( "Couldn't connect to server" );
+      return false;
+    }
+
+    citReply rep = getReply();
+
+    if( rep.ok() ) return true;
+    return false;
+  }
+
+  public citReply getReply() {
+    return getReply( (String)null );
+  }
+
+  public citReply getReply( String cmd ) {
+    if( cmd != null ) println( cmd );
+
+    citReply r = new citReply( readLine() );
+    if( r.listingFollows() ) {
+      while( r.addData( readLine() ) ) ;
+    }
+
+    if( r.expressMessage() )
+      citadel.me.expressMsg();
+
+    return r;
+  }
+}
+
+
+
+
diff --git a/shaggy/pageWindow.java b/shaggy/pageWindow.java
new file mode 100644 (file)
index 0000000..c86632f
--- /dev/null
@@ -0,0 +1,73 @@
+/* pageWindow.java
+ */
+
+import java.awt.*;
+
+public class pageWindow extends Frame {
+  String       user;
+  Choice       who;
+  TextField    msg;
+  Button       send, cancel;
+
+  public pageWindow( String user ) {
+    this.user = user;
+
+    setTitle( "Page a user" );
+    setLayout( new BorderLayout() );
+
+    PairPanel  pp = new PairPanel();
+    pp.addLeft( new Label( "Send message to:" ) );
+    pp.addRight( who = new Choice() );
+
+    add( "North", pp );
+
+    NamedPanel np = new NamedPanel( "Message" );
+    np.setLayout( new BorderLayout() );
+    np.add( "Center", msg = new TextField() );
+
+    add( "Center", np );
+
+    Panel p = new Panel();
+    p.add( send = new Button( "Send" ) );
+    p.add( cancel = new Button( "Cancel" ) );
+    add( "South", p );
+
+    citReply   r = citadel.me.getReply( "RWHO" );
+    int        i=0, which=0;
+    String     s;
+    while( (s=r.getLine( i++ )) != null ) {
+      String u = getUser(s);
+      if( u.equalsIgnoreCase( user ) ) which = i-1;
+      who.addItem( u );
+    }
+
+    who.select( which );
+
+    resize( 300, 150 );
+    show();
+  }
+
+  public String getUser( String s ) {
+    int        i = s.indexOf( '|' )+1;
+    int j = s.indexOf( '|', i );
+    return s.substring( i, j );
+  }
+
+  public boolean handleEvent( Event e ) {
+    if( e.id == Event.WINDOW_DESTROY )
+      dispose();
+    return super.handleEvent( e );
+  }
+
+  public boolean action( Event e, Object o ) {
+    if( (e.target == msg) || (e.target == send) ) {
+      String user = who.getSelectedItem();
+      String m = msg.getText();
+      if( m.length() > 0 )
+        citadel.me.getReply( "SEXP " + user + "|" + m );
+      dispose();
+    } else if( e.target == cancel )
+      dispose();
+    return super.action( e, o );
+  }
+}
diff --git a/shaggy/passwordWindow.java b/shaggy/passwordWindow.java
new file mode 100644 (file)
index 0000000..c24def3
--- /dev/null
@@ -0,0 +1,51 @@
+/* passwordWindow.java
+ * Prompt user for room password
+ */
+
+import java.awt.*;
+
+public class passwordWindow extends Frame {
+  String       room;
+  TextField    text;
+
+  public passwordWindow( String room ) {
+    super( "Password for: " + room );
+
+    this.room = room;
+
+    setLayout( new BorderLayout() );
+    add( "North", new Label( "Please enter password for " + room ) );
+
+    PairPanel  pp = new PairPanel();
+    pp.addLeft( new Label ( "Password: " ) );
+    pp.addRight( text = new TextField() );
+    text.setEchoCharacter( '.' );
+
+    add( "South", new Button( "Goto!" ) );
+
+    resize( 250, 150 );
+    show();
+  }
+
+  public boolean handleEvent( Event e ) {
+    if( e.id == Event.WINDOW_DESTROY ) {
+      dispose();
+    }
+    return super.handleEvent( e );
+  }
+
+  public boolean action( Event e, Object o ) {
+    if( (e.target == text) || (e.target instanceof Button) ) {
+      String   s = text.getText();
+      citReply r = citadel.me.getReply( "GOTO " + room + " " + s );
+      if( r.ok() ) {
+       citadel.me.enterRoom( r );
+       dispose();
+      } else {
+       dispose();
+       new passwordWindow( room );
+      }
+    }
+    return super.action( e, o );
+  }
+}
diff --git a/shaggy/server.java b/shaggy/server.java
new file mode 100644 (file)
index 0000000..68e4033
--- /dev/null
@@ -0,0 +1,42 @@
+/* server.java
+ * server info data structure
+ */
+
+public class server {
+  int          session_id;
+  String       node_name, human_name, fqdn, server_name;
+  int          rev_level;
+  String       geo_local, sysadmin;
+  int          server_type;
+  String       page_prompt;
+  int          floor_flag, page_level;
+
+  String       blurb;
+
+  public server( citReply r ) {
+    session_id = atoi( r.getLine( 0 ) );
+    node_name = r.getLine( 1 );
+    human_name = r.getLine( 2 );
+    fqdn = r.getLine( 3 );
+    server_name = r.getLine( 4 );
+    rev_level = atoi( r.getLine( 5 ) );
+    geo_local = r.getLine( 6 );
+    sysadmin = r.getLine( 7 );
+    server_type = atoi( r.getLine( 8 ) );
+    page_prompt = r.getLine( 9 );
+    floor_flag = atoi( r.getLine( 10 ) );
+    page_level = atoi( r.getLine( 11 ) );
+
+    blurb = server_name + " " + human_name + " " + geo_local;
+    System.out.println( blurb );
+  }
+
+  public int atoi( String s ) {
+    if( s == null ) return 0;
+
+    try {
+      return Integer.parseInt( s );
+    } catch( NumberFormatException nfe ) {};
+    return 0;
+  }
+}
diff --git a/shaggy/user.java b/shaggy/user.java
new file mode 100644 (file)
index 0000000..752c303
--- /dev/null
@@ -0,0 +1,35 @@
+/* user.java
+ * user structure
+ */
+
+public class user {
+  public static final int NEEDVALID=1, PERM=4, LASTOLD=16, EXPERT=32,
+    UNLISTED=64, NOPROMPT=128, DISAPPEAR=512, REGIS=1024, PAGINATOR=2048,
+    INTERNET=4096, FLOORS=8192;
+
+  String       username;
+  int          access, call, msg, num;
+  long         flags;
+
+  public user( citReply r ) {
+    username = r.getArg( 0 );
+    access = atoi( r.getArg( 1 ) );
+    call = atoi( r.getArg( 2 ) );
+    msg = atoi( r.getArg( 3 ) );
+    flags = atoi( r.getArg( 4 ) );
+    num = atoi( r.getArg( 5 ) );
+  }
+
+  public boolean floors() {
+    return (flags & FLOORS)==FLOORS;
+  }
+
+  public int atoi( String s ) {
+    if( s == null ) return 0;
+
+    try {
+      return Integer.parseInt( s );
+    } catch( NumberFormatException nfe ) {};
+    return 0;
+  }
+}