]> code.citadel.org Git - citadel.git/commitdiff
Added the chat system
authorArt Cancro <ajc@citadel.org>
Fri, 18 Dec 1998 01:48:32 +0000 (01:48 +0000)
committerArt Cancro <ajc@citadel.org>
Fri, 18 Dec 1998 01:48:32 +0000 (01:48 +0000)
16 files changed:
webcit/ChangeLog
webcit/child.h
webcit/paging.c
webcit/static/Makefile [new file with mode: 0644]
webcit/static/MultiUserChat102.class [new file with mode: 0644]
webcit/static/MultiUserChat102.java [new file with mode: 0644]
webcit/static/README.txt [new file with mode: 0644]
webcit/static/ReceiveChat.class [new file with mode: 0644]
webcit/static/wcCitServer.class [new file with mode: 0644]
webcit/static/wcCitServer.java [new file with mode: 0644]
webcit/static/wcCitUtil.class [new file with mode: 0644]
webcit/static/wcCitUtil.java [new file with mode: 0644]
webcit/static/wcKeepAlive.class [new file with mode: 0644]
webcit/static/wcchat.class [new file with mode: 0644]
webcit/static/wcchat.java [new file with mode: 0644]
webcit/webcit.c

index f0af12b20e328efeb318ea76f1483c1c36059cc0..cd8d7390f60b69c2050bbee829e9f5df6ac62337 100644 (file)
@@ -1,6 +1,7 @@
 Thu Dec 17 20:38:00 EST 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
        * Added the screens to send pages
        * Changed message headers to display in bigger font, non-boldface
+       * Added the chat system
 
 Wed Dec 16 16:23:58 EST 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
        * Replace "Citadel/UX" in menu bar with the Citadel/UX logo
index c7cffc1c804bf74e96f782adac0f1ff0d85c8733..ab29eec91088dba3696e36b0d3ea2fecab9c77f4 100644 (file)
@@ -52,3 +52,4 @@ void userlist(void);
 void showuser(void);
 void display_page(void);
 void page_user(void);
+void do_chat(void);
index 7bd2ebc9b7dc149a8e6758d5af8d2937055609c8..e40f44f042e74f87a9cf806913421d858b120078 100644 (file)
@@ -96,3 +96,34 @@ void page_user(void) {
         wprintf("</BODY></HTML>\n");
         wDumpContent();
        }
+
+
+
+/*
+ * multiuser chat
+ */
+void do_chat(void) {
+
+        printf("HTTP/1.0 200 OK\n");
+        output_headers(1);
+
+        wprintf("<TABLE WIDTH=100% BORDER=0 BGCOLOR=000077><TR><TD>");
+        wprintf("<FONT SIZE=+1 COLOR=\"FFFFFF\"");
+        wprintf("<B>Real-time chat</B>\n");
+        wprintf("</FONT></TD></TR></TABLE>\n");
+
+       wprintf("A chat window should be appearing on your screen ");
+       wprintf("momentarily.  When you're ");
+       wprintf("done, type <TT>/quit</TT> to exit.  You can also ");
+       wprintf("type <TT>/help</TT> for more commands.\n");
+
+       wprintf("<applet codebase=\"/static\" ");
+       wprintf("code=\"wcchat\" width=2 height=2>\n");
+       wprintf("<PARAM NAME=username VALUE=\"%s\">\n", wc_username);
+       wprintf("<PARAM NAME=password VALUE=\"%s\">\n", wc_password);
+       wprintf("<H2>Oops!</H2>Looks like your browser doesn't support Java, ");
+       wprintf("so you won't be able to access Chat.  Sorry.\n");
+       wprintf("</applet>\n");
+        wprintf("</BODY></HTML>\n");
+        wDumpContent();
+       }
diff --git a/webcit/static/Makefile b/webcit/static/Makefile
new file mode 100644 (file)
index 0000000..254e8d4
--- /dev/null
@@ -0,0 +1,13 @@
+all: MultiUserChat102.class wcCitUtil.class wcCitServer.class wcchat.class
+
+MultiUserChat102.class: MultiUserChat102.java
+       javac MultiUserChat102.java
+
+wcCitServer.class: wcCitServer.java
+       javac wcCitServer.java
+
+wcCitUtil.class: wcCitUtil.java
+       javac wcCitUtil.java
+
+wcchat.class: wcchat.java
+       javac wcchat.java
diff --git a/webcit/static/MultiUserChat102.class b/webcit/static/MultiUserChat102.class
new file mode 100644 (file)
index 0000000..0969ce5
Binary files /dev/null and b/webcit/static/MultiUserChat102.class differ
diff --git a/webcit/static/MultiUserChat102.java b/webcit/static/MultiUserChat102.java
new file mode 100644 (file)
index 0000000..fe620ba
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * MultiUserChat102.java
+ *
+ * Chat mode...
+ *
+ */
+
+import java.awt.*;
+import java.util.*;
+
+/*
+ * ReceiveChat implements a thread which listens on the server socket and
+ * display anything which comes across the wire in the Panel specified
+ * to the constructor.
+ */
+class ReceiveChat extends Thread {
+       Panel TheArea;
+       wcCitServer serv;
+       String boof;
+       String cUser;
+       String cText;
+       String ThisLine;
+       String LastLineUser;
+       String MyName;
+       StringTokenizer ST;
+       MultiUserChat102 ParentMUC;
+       Label[] Linez = new Label[25];
+       int a;
+       
+       ReceiveChat(MultiUserChat102 muc, Panel t, wcCitServer s, String n) {
+               TheArea = t;
+               serv = s;
+               MyName = n;
+               ParentMUC = muc;
+               serv.AddClientThread(this);
+
+               TheArea.setLayout(new GridLayout(25,1));
+
+               for (a=0; a<25; ++a) {
+                       Linez[a] = new Label(" ");
+                       Linez[a].setBackground(Color.black);
+                       Linez[a].setForeground(Color.black);
+                       TheArea.add(Linez[a]);
+                       }
+
+               TheArea.validate();
+
+               }
+
+       private void ScrollIt(String TheNewLine, Color NewLineColor) {
+               for (a=0; a<24; ++a) {
+                       Linez[a].setText(Linez[a+1].getText());
+                       Linez[a].setForeground(Linez[a+1].getForeground());
+                       }
+               Linez[24].setText(TheNewLine);
+               Linez[24].setForeground(NewLineColor);
+               }
+
+
+       public void run() {
+               Color UserColor;
+               int a;
+
+               LastLineUser = "  ";
+               while(true) {
+                       boof = serv.ServGets();
+
+                       if (boof.equals("000")) {
+                               serv.ServPuts("QUIT");
+                               ParentMUC.dispose();
+                               stop();
+                               destroy();
+                               }
+
+
+                       ST = new StringTokenizer(boof, "|");
+                       if (ST.hasMoreTokens()) {
+                               cUser = ST.nextToken();
+                               }
+                       else {
+                               cUser = ":";
+                               }
+                       if (ST.hasMoreTokens()) {
+                               cText = ST.nextToken();
+                               }
+                       else {
+                               cText = " ";
+                               }
+                       if (!cText.startsWith("NOOP")) {
+                               if (!LastLineUser.equals(cUser)) {
+                                       ScrollIt("", Color.black);
+                                       ThisLine = cUser + ": ";
+                                       }
+                               else {
+ThisLine = "                                  ".substring(0, cUser.length()+2);
+                                       }
+                               ThisLine = ThisLine + cText;
+                               UserColor = Color.green;
+                               if (cUser.equals(":")) {
+                                       UserColor = Color.red;
+                                       }
+                               if (cUser.equalsIgnoreCase(MyName)) {
+                                       UserColor = Color.yellow;
+                                       }
+                               ScrollIt(ThisLine, UserColor);
+                               LastLineUser = cUser;
+                               }
+                       }
+               }
+
+       }
+
+
+
+
+
+public class MultiUserChat102
+       extends Frame {
+
+wcCitServer serv;
+ReceiveChat MyReceive;
+Panel AllUsers;
+TextField SendBox;
+wcchat ParentApplet;
+
+
+MultiUserChat102(wcCitServer PrimaryServ, wcchat P) {
+       super ("Multiuser Chat");
+
+       String boof;
+
+       /* Set up a new server connection as a piggyback to the first. */
+       serv = PrimaryServ;
+       ParentApplet = P;
+
+       resize(600,400);
+       setLayout(new BorderLayout());
+
+       boof = "This is the buffer before the chat command.";
+       serv.ServPuts("CHAT");
+       boof = serv.ServGets();
+
+       if (boof.charAt(0) != '8') {
+               add("Center", new Label("ERROR: " + boof) );
+               show();
+               }
+
+       else {
+               DoChat(PrimaryServ.GetUserName());
+               }
+
+       }
+
+
+/*
+ * Do the actual chat stuff
+ */
+private void DoChat(String MyName) {
+       String boof;
+
+       SendBox = new TextField(80);
+
+       AllUsers = new Panel();
+
+       add("Center", AllUsers);
+       add("South", SendBox);
+       show();
+
+       MyReceive = new ReceiveChat(this, AllUsers, serv, MyName);
+       MyReceive.start();
+
+       SendBox.requestFocus();
+       }
+
+
+public boolean handleEvent(Event evt) {
+       int LastSpace;
+
+       if ( (evt.target == SendBox) && (evt.id == Event.ACTION_EVENT) ) {
+               serv.ServPuts(SendBox.getText());
+               SendBox.setText("");
+               }
+
+       else if (evt.target == SendBox) {
+               if ( SendBox.getText().length() + serv.GetUserName().length() > 78 ) {
+                       LastSpace = SendBox.getText().lastIndexOf(' ');
+                       if (LastSpace < 0) {
+                               serv.ServPuts(SendBox.getText());
+                               SendBox.setText("");
+                               }
+                       else {
+                               serv.ServPuts(SendBox.getText().substring(0,LastSpace));
+                               SendBox.setText(SendBox.getText().substring(LastSpace));
+                               if (SendBox.getText().charAt(0) == ' ') {
+                                       SendBox.setText(SendBox.getText().substring(1));
+                                       }
+                               }
+                       }
+               }
+
+               return super.handleEvent(evt);
+       }
+
+
+}
diff --git a/webcit/static/README.txt b/webcit/static/README.txt
new file mode 100644 (file)
index 0000000..a939960
--- /dev/null
@@ -0,0 +1,9 @@
+The program in this directory comprises a Java-based chat client to be used
+with the WebCit client for Citadel/UX.  Most of the code was lifted from an
+all-Java client I'm currently writing, but since we expect this one to work
+from the most popular browsers, everything has been tweaked in such a way as
+to make it run happily in a JDK 1.0.2 environment.
+ Since not everyone has a Java compiler on hand, I've supplied the binaries
+as a courtesy.  Since Java bytecode is universal, there should be no need to
+recompile.
diff --git a/webcit/static/ReceiveChat.class b/webcit/static/ReceiveChat.class
new file mode 100644 (file)
index 0000000..120d5c0
Binary files /dev/null and b/webcit/static/ReceiveChat.class differ
diff --git a/webcit/static/wcCitServer.class b/webcit/static/wcCitServer.class
new file mode 100644 (file)
index 0000000..0e8904c
Binary files /dev/null and b/webcit/static/wcCitServer.class differ
diff --git a/webcit/static/wcCitServer.java b/webcit/static/wcCitServer.java
new file mode 100644 (file)
index 0000000..dff9325
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * wcCitServer.java
+ *
+ * This module handles the tasks involving establishing a connection to a
+ * Citadel/UX server and moving data across the connection.  It also handles
+ * server "keepalives", the dispatching of "express messages", the 
+ * registration & termination of multiple threads, and a semaphore mechanism
+ * to prevent multiple threads from executing server commands at the same
+ * time.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+
+/*
+ * We send 'keepalive' commands (actually just a NOOP) to the server every
+ * fifteen seconds, as a separate thread.  This prevents the server session
+ * from timing out, and also allows the client to be notified of any incoming
+ * express messages if the user isn't doing anything.
+ */
+class wcKeepAlive extends Thread {
+
+       wcCitServer serv;               /* Pointer to server connection class */
+       boolean FullKeepAlives; /* TRUE for full keepalives, FALSE for half keepalives */
+
+       wcKeepAlive() {
+               }
+
+       public void PointToServer(wcCitServer which_serv, boolean WhatKindOfKeepAlives) {
+               serv = which_serv;
+               serv.AddClientThread(this);
+               FullKeepAlives = WhatKindOfKeepAlives;
+               }
+
+       public void run() {
+               String buf;
+               while(true) {
+
+                       /* Sleep for sixty seconds between keepalives. */
+                       try {
+                               sleep(60000);
+                               }
+                       catch (InterruptedException e) {
+                               }
+                       /* Full keepalives - send a NOOP, wait for a reply, then retrieve
+                        * express messages if the server said there are any.
+                        */
+                       if (FullKeepAlives) {
+                               buf = serv.ServTrans("NOOP") + "    ";
+                               }
+
+                       /* Half keepalives - blindly send a NOOP and we're done. */
+                       else {
+                               serv.ServPuts("NOOP");
+                               }
+
+                       }
+               }
+               
+       }
+
+
+
+/*
+ * the wcCitServer class handles communication with the server.
+ */
+public class wcCitServer {
+       int connected = 0;
+       DataOutputStream ofp;
+       DataInputStream ifp;
+       Socket sock;
+       String TransBuf;
+       wcKeepAlive ka;
+       boolean in_trans = false;
+       Vector ClientThreads = new Vector();
+       Vector ServerInfo = new Vector();
+       String PrimaryServerHost;
+       int PrimaryServerPort;
+       String PrimaryServerUser;
+       String PrimaryServerPassword;
+
+       public void SetTransBuf(String bufstring) {
+               TransBuf = bufstring;
+               }
+
+       public String GetTransBuf() {
+               return TransBuf;
+               }
+
+/* attach to the server */
+       private void BuildConnection(String ServerHost, int ServerPort, boolean WhichKA) {
+
+               String buf;
+
+               try {
+                       sock = new Socket(ServerHost, ServerPort);
+                       ofp = new
+                               DataOutputStream(sock.getOutputStream());
+                       ifp = new
+                               DataInputStream(sock.getInputStream());
+                       }
+               catch(UnknownHostException e) {
+                       System.out.println(e);
+                       }
+               catch(IOException e) {
+                       System.out.println(e);
+                       }
+
+               /* Connection established.  At this point, this function
+                * has the server connection all to itself, so we can do
+                * whatever we want with it. */
+       
+               /* Get the 'server ready' message */    
+               buf = ServGets();
+
+               /* Identify the client software to the server */
+               ServTrans("IDEN 0|5|001|Cit/UX Java Client|");
+               
+               /* Download various information about the server */
+               SetTransBuf("");
+               buf = ServTrans("INFO");
+               StringTokenizer InfoST = new StringTokenizer(TransBuf,"\n");
+               while (InfoST.hasMoreTokens()) {
+                       ServerInfo.addElement(InfoST.nextToken());
+                       }
+
+
+               /* At this point, all server accesses must cooperate with
+                * server accesses from other threads by using the BeginTrans
+                * and EndTrans semaphore mechanism.
+                */
+               ka = new wcKeepAlive();
+               ka.PointToServer(this, WhichKA);
+               ka.start();
+
+               }
+
+
+/*
+ * Attach to server command for the primary socket
+ */
+       public void AttachToServer(String ServerHost, int ServerPort) {
+               
+               /* Connect to the server */
+/* NOTE ... we've changed the primary connection keepalives to HALF because we're using the
+ * primary connection to jump right into a chat window.
+ */
+               BuildConnection(ServerHost, ServerPort, false);
+
+               /* And remember where we connected to, in case we have to
+                * build any piggyback connections later on.
+                */
+               PrimaryServerHost = ServerHost;
+               PrimaryServerPort = ServerPort;
+               }
+
+
+/* 
+ * Learn info about the primary connection for setting up piggybacks
+ */
+       public String GetServerHost() {
+               return PrimaryServerHost;
+               }
+
+       public int GetServerPort() {    
+               return PrimaryServerPort;
+               }
+
+
+/*
+ * Set up a piggyback connection
+ */
+       public void Piggyback(wcCitServer PrimaryServ, boolean KeepAliveType) {
+               PrimaryServerHost = PrimaryServ.GetServerHost();
+               PrimaryServerPort = PrimaryServ.GetServerPort();
+               PrimaryServerUser = PrimaryServ.GetUserName();
+               PrimaryServerPassword = PrimaryServ.GetPassword();
+               BuildConnection(PrimaryServerHost, PrimaryServerPort,
+                               KeepAliveType);
+               
+               }
+
+
+
+/* return info about the site we're currently connected to */
+       public String ServInfo(int index) {
+               String retbuf;
+               if (index >= ServerInfo.size()) {
+                       return("");
+                       }
+               else {
+                       retbuf = ServerInfo.elementAt(index).toString();
+                       return(retbuf);
+                       }
+               }
+
+
+/* read a line from the server */
+       public String ServGets() {
+               
+               String buf = "";
+
+               try {
+                       buf = ifp.readLine();
+                       }
+               catch(IOException e) {
+                       System.out.println(e);
+                       }
+               return buf;
+               }
+
+
+/* write a line to the server */
+       public void ServPuts(String buf) {
+
+               try {
+                       ofp.writeBytes(buf + "\n");
+                       }
+               catch(IOException e) {
+                       System.out.println(e);
+                       }
+               }
+
+
+/* lock the server connection so other threads don't screw us up */
+       public synchronized void BeginTrans() {
+               while(in_trans) {
+                       try {
+                               System.out.println("-sleeping-"); /* trace */
+                               Thread.sleep(100);
+                               }
+                       catch (InterruptedException e) {
+                               }
+                       }
+
+               in_trans = true;
+               }
+
+/* release the lock */
+       public void EndTrans() {
+               in_trans = false;
+               }
+
+
+/* perform an autonomous server transaction */
+       public String ServTrans(String ServCmd) {
+               String buf; 
+               BeginTrans();
+               buf = DataTrans(ServCmd);
+               EndTrans();
+               return buf;
+               }
+
+/* perform a non-autonomous server transaction */
+       public String DataTrans(String ServCmd) {
+               String buf = "";
+               String inbuf = "";
+               String expbuf = "";
+
+               /* perform the transaction */
+
+               System.out.println(">"+ServCmd);        /* trace */
+               ServPuts(ServCmd);
+               buf = ServGets();
+               System.out.println("<"+buf);            /* trace */
+
+           try {
+               if (buf.startsWith("4")) {
+                       ofp.writeBytes(TransBuf);
+                       if (!TransBuf.endsWith("\n")) {
+                               ofp.writeBytes("\n");
+                               }
+                       ofp.writeBytes("000");
+                       TransBuf = "";
+                       }
+
+               if (buf.startsWith("1")) {
+                       TransBuf = "";
+                       inbuf = "";
+                       do {
+                               inbuf = ServGets();
+                               if (!TransBuf.equals("")) {
+                                       TransBuf = TransBuf + "\n";
+                                       }
+                               if (!inbuf.equals("000")) {
+                                       TransBuf = TransBuf + inbuf;
+                                       }
+                               } while (!inbuf.equals("000"));
+                       }
+                   }
+           catch(IOException e) {
+               System.out.println(e);
+               }
+
+               return(buf);
+               }
+
+       public void AddClientThread(Thread ct) {
+               ClientThreads.addElement(ct);
+               System.out.println("--new thread registered--");
+               }
+
+       public void RemoveClientThread(Thread ct) {
+               ClientThreads.removeElement(ct);
+               System.out.println("--thread removed--");
+               }
+
+
+
+       /* The following four functions take care of keeping track of the
+        * user name and password being used on the primary server connection
+        * in case we want to use them for a piggyback connection.
+        */
+       public void SetUserName(String U) {
+               PrimaryServerUser = U;
+               }
+       
+       public void SetPassword(String P) {
+               PrimaryServerPassword = P;
+               }
+
+       public String GetUserName() {
+               return PrimaryServerUser;
+               }
+
+       public String GetPassword() {
+               return PrimaryServerPassword;
+               }
+
+
+
+       }
+
+
diff --git a/webcit/static/wcCitUtil.class b/webcit/static/wcCitUtil.class
new file mode 100644 (file)
index 0000000..e3b7d86
Binary files /dev/null and b/webcit/static/wcCitUtil.class differ
diff --git a/webcit/static/wcCitUtil.java b/webcit/static/wcCitUtil.java
new file mode 100644 (file)
index 0000000..4e34deb
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * wcCitUtil.java
+ *
+ * Miscellaneous functionality...
+ */
+
+import java.util.*;
+
+
+public class wcCitUtil extends Object {
+
+public static String Extract(String SourceString, int ParmNum) {
+       StringTokenizer toks;
+       String buf;
+       int pos;
+
+       pos = 0;
+       toks = new StringTokenizer(SourceString, "|");
+       while (toks.hasMoreTokens()) {
+               buf = toks.nextToken();
+               if (pos == ParmNum) return buf;
+               ++pos;
+               }
+       return "";
+       }
+
+}
diff --git a/webcit/static/wcKeepAlive.class b/webcit/static/wcKeepAlive.class
new file mode 100644 (file)
index 0000000..412e4b1
Binary files /dev/null and b/webcit/static/wcKeepAlive.class differ
diff --git a/webcit/static/wcchat.class b/webcit/static/wcchat.class
new file mode 100644 (file)
index 0000000..05b4af9
Binary files /dev/null and b/webcit/static/wcchat.class differ
diff --git a/webcit/static/wcchat.java b/webcit/static/wcchat.java
new file mode 100644 (file)
index 0000000..a6df95a
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Java client for Citadel/UX
+ * Copyright (C) 1997 by Art Cancro - All Rights Reserved
+ *
+ * This module is designed to be able to run either as an application or
+ * as an applet.
+ */
+
+import java.applet.*;
+
+public class wcchat extends Applet {
+
+String ServerHost = "uncnsrd.mt-kisco.ny.us";
+int ServerPort = 504;
+       
+       public void init() {
+
+               /* Unless overridden, the Citadel server is expected to be
+                * the same as the applet host.  In most cases this is all
+                * that's allowed anyway.
+                */
+               if (getDocumentBase() != null) {
+                       ServerHost = getDocumentBase().getHost();
+                       }
+
+               /* The 'host' parameter tells the client to look somewhere other
+                * than the applet host for the Citadel server.
+                */
+               if (getParameter("host") != null) {
+                       ServerHost = getParameter("host");
+                       }
+
+               /* The 'port' parameter tells the client to look on a
+                * nonstandard port for the Citadel server.
+                */
+               if (getParameter("port") != null) {
+                       ServerPort = Integer.parseInt(getParameter("port"));
+                       }
+
+               }
+
+       public void start() {
+               wcCitServer serv = new wcCitServer();
+               String buf = null;
+
+               serv.AttachToServer(ServerHost, ServerPort);
+               buf = serv.ServTrans("USER " + getParameter("username"));
+               if (buf.charAt(0) == '3') {
+                       buf = serv.ServTrans("PASS "+getParameter("password"));
+                       if (buf.charAt(0) == '2') {
+                               serv.SetUserName(wcCitUtil.Extract(buf.substring(4), 0));
+                               new MultiUserChat102(serv, this);
+                               }
+                       }
+               else {
+                       System.out.println("ooops...");
+                       }
+               }
+
+}
index b879c097d7e5af51cabe80cbddb1443218624045..b3e5208f150819c7e49bd7b7b7566d7e963b0239 100644 (file)
@@ -664,6 +664,10 @@ fclose(fp);
                page_user();
                }
 
+       else if (!strcasecmp(action, "chat")) {
+               do_chat();
+               }
+
        /* When all else fails... */
        else {
                printf("HTTP/1.0 200 OK\n");