jwiki : Wiki-Wiki Web Server in Java

Download jwiki.zip

Synopsis:

WikiPostable.java
WikiFile.java
WikiDir.java
Users.java
uploadcode.java
upload.java
test.java
sourcecode.java
search.java
recentchanges.java
Passwords.java
PagesDir.java
MessageHandlers.java
logindone.java
login.java
KeyValueTable.java
JWikiCvt.java
JWiki.java
JHandler.java
IniFile.java
IncomingLog.java
IncomingHTTPRequest.java
HTMLspew.java
homepage.java
HandleEditingCmd.java
HandleEditCmd.java
HandleCreatePageCmd.java
Globals.java
editsave.java
EditLocks.java
edit.java
Constants.java
Config.java
Comparator.java
allpages.java


WikiPostable.java

Synopsis
package jwiki;

// The interface of any object that can handle a POST event in a JHandler of JWiki.
// The name of the class must be like jwiki.edit for the edit POST.
// The class name is the action=edit part of the INPUT clause in HTML.
public interface WikiPostable
  {
  // Called in JHandler to handle a POST request from a browser.
  // @param h The handler that called you.
  // @param buf The byte buffer with the data part of the POST sitting in it.
  // @param fin The already open file to read the
  // @throws Any exception you want.
  void handlePOST(JHandler h, byte buf[], int bufSize) throws Exception;
  }

WikiFile.java

Synopsis
package jwiki;

import java.io.IOException;
import java.io.FileInputStream;

//---------------------------------------------------------------------
public class WikiFile
  {
  private FileInputStream m_fin;
  private String m_pagename;
  private String m_content;

  private KeyValueTable m_pairs;
  private byte m_ioBuf[];
  private JHandler m_h;

  //---------------------------------------------------------------------
  WikiFile(JHandler h, String pagename)
    {
    m_pagename = pagename;
    m_fin = null;
    m_content = null;
    m_ioBuf = new byte[Constants.MaxPostSize];
    m_h = h;
    }

  //---------------------------------------------------------------------
  public void read() throws IOException
    {
    m_fin = new FileInputStream(Globals.pageDir.locate(m_pagename));

    readheader();

    Globals.doDebug(2, "WikiFile::read() enter");
    StringBuffer b = new StringBuffer();
    try
      {
      int n;
      for (;;)
        {
        n = m_fin.read(m_ioBuf);
        if (n <= 0)
          break;
        b.append(new String(m_ioBuf, 0, n));
        }
      m_content = b.toString();
      }
    catch (Exception e)
      {
      m_content = null;
      m_h.writeInLog("WikiFile::read: exception:" + e.getMessage());
      e.printStackTrace();
      }
    }

  //---------------------------------------------------------------------
  public void close()
    {
    try
      {
      m_fin.close();
      }
    catch (IOException e)
      {
      m_h.writeInLog("WikiFile::close: exception:" + e.getMessage());
      e.printStackTrace();
      }

    m_fin = null;
    }

  //---------------------------------------------------------------------
  public String pageName()
    {
    return m_pagename;
    }

  //---------------------------------------------------------------------
  public boolean exists()
    {
    return Globals.pageDir.Exists(m_h, m_pagename);
    }

  //---------------------------------------------------------------------
  public void setHeaderDefaults()
    {
    m_pairs = new KeyValueTable();
    m_pairs.put("read", "all");
    m_pairs.put("write", "all");
    m_pairs.put("preamble", "true");
    m_pairs.put("postamble", "true");
    m_pairs.put("editbutton", "true");
    m_pairs.put("html", "true");
    }

  //---------------------------------------------------------------------
  public boolean isWriteLoginRequired()
    {
    return m_pairs.get("write").equals("passwd") && m_h.userLoggedIn() == false;
    }

  //---------------------------------------------------------------------
  public boolean isNoWritePermission()
    {
    String writePerm = (String) m_pairs.get("write");
    return (writePerm == null || writePerm.equals("none"));
    }

  //---------------------------------------------------------------------
  public boolean isWriteAllChecked()
    {
    String writePerm = (String) m_pairs.get("write");
    return (writePerm == null || writePerm.equals("all"));
    }

  //---------------------------------------------------------------------
  public boolean isWriteNoWriteChecked()
    {
    return m_pairs.get("write").equals("none");
    }

  //---------------------------------------------------------------------
  public boolean isWritePasswordChecked()
    {
    return m_pairs.get("write").equals("passwd");
    }

  //---------------------------------------------------------------------
  public boolean isNoReadPermission()
    {
    String readPerm = (String) m_pairs.get("read");
    return (readPerm == null || readPerm.equals("none"));
    }

  //---------------------------------------------------------------------
  public boolean isReadLoginRequired()
    {
    String readPerm = (String) m_pairs.get("read");
    return (readPerm.equals("passwd") && m_h.userLoggedIn() == false);
    }

  //---------------------------------------------------------------------
  public boolean isReadAllowed()
    {
    String readPerm = (String) m_pairs.get("read");
    return (readPerm.equals("all") ||
            (readPerm.equals("passwd") && m_h.userLoggedIn() == true));
    }

  //---------------------------------------------------------------------
  public boolean isReadAllChecked()
    {
    String readPerm = (String) m_pairs.get("read");
    return (readPerm == null || readPerm.equals("all"));
    }

  //---------------------------------------------------------------------
  public boolean isReadPasswordChecked()
    {
    return m_pairs.get("read").equals("passwd");
    }

  //---------------------------------------------------------------------
  public String readPermAsString()
    {
    return (String) m_pairs.get("read");
    }

  //---------------------------------------------------------------------
  public boolean isShowLoginButton()
    {
    return isReadLoginRequired() || isWriteLoginRequired();
    }

  //---------------------------------------------------------------------
  public boolean isShowEditButton()
    {
    return m_pairs.get("editbutton").equals("true") && !m_pairs.get("write").equals("none");
    }

  //---------------------------------------------------------------------
  public boolean isHtmlOK()
    {
    return m_pairs.get("html") != null && m_pairs.get("html").equals("true");
    }

  //---------------------------------------------------------------------
  public boolean isWikiOK()
    {
    return m_pairs.get("wiki") != null && m_pairs.get("wiki").equals("true");
    }

  //---------------------------------------------------------------------
  public boolean isShowPreamble()
    {
    return m_pairs.get("preamble") != null && m_pairs.get("preamble").equals("true");
    }

  //---------------------------------------------------------------------
  public boolean isShowPostamble()
    {
    return m_pairs.get("postamble") != null && m_pairs.get("postamble").equals("true");
    }

  //---------------------------------------------------------------------
  public String content()
    {
    return m_content;
    }

  //---------------------------------------------------------------------
  //-- private files from here on
  //---------------------------------------------------------------------

  //---------------------------------------------------------------------
  private void readheader()
    {
    m_pairs = new KeyValueTable();

    String s = readLine();
    try
      {
      while (s.length() != 0)
        {
        m_pairs.Parse(s, ":");
        s = readLine();
        }
      }
    catch (Exception e)
      {
      return ;
      }
    }

  //---------------------------------------------------------------------
  private String readLine()
    {
    int i = 0;
    try
      {
      int n;
      for (;;)
        {
        n = m_fin.read(m_ioBuf, i, 1);
        if (!(n > 0 && m_ioBuf[i] != Constants.LF && i < (m_ioBuf.length - 1)))
          break;
        if (m_ioBuf[i] != Constants.CR)
          i++;
        }
      }
    catch (Exception e)
      {
      m_h.writeInLog("WikiFile::readLine: exception:" + e.getMessage());
      e.printStackTrace();
      }

    String s = new String(m_ioBuf, 0, i);
    Globals.doDebug(4, "readLine [" + s + "]");
    return s;
    }
  }

WikiDir.java

Synopsis
package jwiki;

import java.io.File;

//--------------------------------------------------------------------------
public class WikiDir
  {
  protected String m_path;
  private String m_dirname;

  //--------------------------------------------------------------------------
  public WikiDir(String rootdir, String dirname)
    {
    m_dirname = dirname;
    m_path = rootdir + File.separator + m_dirname;
    }

  //--------------------------------------------------------------------------
  boolean prefixOf(String path)
    {
    return path.startsWith(m_dirname + "/");
    }

  //--------------------------------------------------------------------------
  File locate(String fileName)
    {
    if (fileName.length() == 0)
      return null;

    // Don't allow hackers to access the file system out of the docs directory.
    if (isBogusFileName(fileName))
      {
      Globals.doDebug(3, "WikiDir::locate: bad fileName (hack attack?): '" + fileName + "'");
      return null;
      }

    File f = new File(m_dirname, fileName);
    if (!f.exists())
      {
      Globals.doDebug(3, "WikiDir::locate: '" + f + "' does not exist in " + m_dirname);
      return null;
      }

    Globals.doDebug(3, "WikiDir::locate: returning " + f);
    return f;
    }

  //--------------------------------------------------------------------------
  public boolean isBogusFileName(String fileName)
    {
    return fileName.indexOf("..") != -1 ||
           fileName.indexOf('/') != -1 ||
           fileName.indexOf('\\') != -1 ||
           fileName.charAt(0) == '.';
    }

  //--------------------------------------------------------------------------
  //-- privates from here on
  //--------------------------------------------------------------------------
  }

Users.java

Synopsis
package jwiki;

//------------------------------------------------------------------------------
public class Users extends IniFile
  {
  private KeyValueTable m_users;

  //------------------------------------------------------------------------------
  public Users()
    {
    m_users = new KeyValueTable();
    }

  //------------------------------------------------------------------------------
  String get(String hostname, String hostaddress)
    {
    String user = m_users.value(hostname);
    if (user == null)
      user = m_users.value(hostaddress);

    if (user == null)
      return "unknown user";

    return user;
    }

  //------------------------------------------------------------------------------
  public void Save(String name, String value)
    {
    m_users.put(name, value);
    }

  //------------------------------------------------------------------------------
  public void read()
    {
    Globals.doDebug(2, "Users::read()");
    m_users.clear();
    read("users");
    }
  }

uploadcode.java

Synopsis
package jwiki;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.File;

public class uploadcode implements WikiPostable
  {
  private JHandler m_h;
  private HTMLspew m_html;
  private FileOutputStream m_fos;
  private KeyValueTable m_vars;
  private String m_fileName;

  //---------------------------------------------------------------------
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    m_h = _h;
    m_html = m_h.getHTML();
    m_vars = m_h.parsePOST(buf, bufSize);

    Globals.doDebug(2, "uploadcode: enter");
    if (isBadFileName())
      return ;

    if (isFailedOpen(fileOpen()))
      return ;

    writeContents();

    if (isFailedClose(fileClose()))
      return ;

    isSuccess();
    }

  //-----------------------------------------------------------------------
  //-- private from here on
  //-----------------------------------------------------------------------

  //-----------------------------------------------------------------------
  private boolean isBadFileName() throws IOException
    {
    m_fileName = m_vars.value("file");
    Globals.doDebug(1, "uploadcode: filename=" + m_fileName);
    if (m_fileName == null)
      {
      m_html.transmitStringAsPage("No file name specified in the POST.<BR> The INPUT HTML must be wrong.");
      return true;
      }

    return false;
    }


  //-----------------------------------------------------------------------
  private boolean isFailedOpen(String err) throws IOException
    {
    if (err == null)
      return false;

    m_h.writeInLog("edit: file open, read exception: " + err);
    m_html.transmitStringAsPage("Can not open file <B>" + m_fileName + "</B> to save: " + err);
    return true;
    }

  //-----------------------------------------------------------------------
  private String fileOpen()
    {
    try
      {
      if (Globals.sourceDir.Exists(m_h, m_fileName))
        return "File already exists. Choose another name.";

      m_fos = new FileOutputStream(Globals.GetSourceDir() + File.separator + m_fileName);

      }
    catch (Exception oe)
      {
      oe.printStackTrace();
      return oe.getMessage();
      }

    return null;
    }

  //-----------------------------------------------------------------------
  private String fileClose()
    {
    try
      {
      m_fos.close();
      }
    catch (Exception ec)
      {
      ec.printStackTrace();
      return ec.getMessage();
      }
    return null;
    }

  //-----------------------------------------------------------------------
  private boolean isFailedClose(String err) throws IOException
    {
    if (err == null)
      return false;

    m_h.writeInLog("uploadcode: file close exception: " + err);
    m_html.transmitStringAsPage("File <B>" + m_fileName + "</B> saved.\n\n" +
                                "However there was an exception on the file close:\n" +
                                err + "\n\n" +
                                "Check the file content for any errors.\n" +
                                "Use the <B>Back</B> button on your browser to\n" +
                                "get back to the page you just edited.\n" +
                                "Then press the <B>Reload</B> button to see the new page.");
    return true;
    }

  //  //-----------------------------------------------------------------------
  //  private void writeHeader() throws Exception
  //    {
  //    if (m_vars.isFound("html"))
  //      m_fos.write(("html:true\n").getBytes());
  //
  //    if (m_vars.isFound("wiki"))
  //      m_fos.write(("wiki:true\n").getBytes());
  //
  //    if (m_vars.isFound("preamble"))
  //      m_fos.write(("preamble:true\n").getBytes());
  //
  //    if (m_vars.isFound("postamble"))
  //      m_fos.write(("postamble:true\n").getBytes());
  //
  //    if (m_vars.isFound("editbutton"))
  //      m_fos.write(("editbutton:true\n").getBytes());
  //
  //    m_fos.write(("read:" + m_vars.value("readPerm") + "\n").getBytes());
  //    m_fos.write(("write:" + m_vars.value("writePerm") + "\n").getBytes());
  //    Globals.crlf(m_fos);
  //    }

  //-----------------------------------------------------------------------
  private void writeContents() throws Exception
    {
    m_fos.write((m_vars.value("txt")).getBytes());
    }

  //-----------------------------------------------------------------------
  private void isSuccess() throws IOException
    {
    m_h.writeInLog("uploadcode: edit of " + m_fileName + " saved");
    m_html.transmitStringAsPage("File <B>" + m_fileName + "</B> saved.\n\n" +
                                "Use the <B>Back</B> button on your browser to\n" +
                                "get back to the page you just edited.\n" +
                                "Then press the <B>Reload</B> button to see the new page.");
    }
  }

upload.java

Synopsis
package jwiki;

//----------------------------------------------------------------------------
// Does the login function of JWiki.  The logindone WikiPostable
// processes the response of username and password.
public class upload implements WikiPostable
  {
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    HTMLspew _html = _h.getHTML();
    _html.transmitUploadPage();
    }
  }

test.java

Synopsis
import java.io.*;
//import java.util.*;
//import java.net.*;

public class test // implements Runnable
  {

  static void fn(int x, int y)
    {
    System.out.println("in fn: x=" + x + " y=" + y);
    x++;
    y++;
    System.out.println("in fn: x=" + x + " y=" + y);
    }

  public static class MyClass
    {
    int x;
    int y;
    }
  static void fn2(MyClass c)
    {
    System.out.println("in fn: x=" + c.x + " y=" + c.y);
    c.x++;
    c.y++;
    System.out.println("in fn: x=" + c.x + " y=" + c.y);
    }

  public static void main(String[] args)
    {
    int x = 10;
    int y = 20;
    System.out.println("before: x=" + x + " y=" + y);
    fn(x, y);
    System.out.println("after : x=" + x + " y=" + y);
    MyClass c;
    c = new MyClass();
    c.x = 30;
    c.y = 40;
    System.out.println("before: x=" + c.x + " y=" + c.y);
    fn2(c);
    System.out.println("after : x=" + c.x + " y=" + c.y);
    }

  }

sourcecode.java

Synopsis
package jwiki;

import java.io.File;
import java.util.TreeSet;

// shows a list of all files changed within x days
public class sourcecode implements WikiPostable
  {
  //-------------------------------------
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    TreeSet fnames = Globals.sourceDir.getFiles(_h, new SourceCodeComparator());

    HTMLspew _html = _h.getHTML();
    _html.transmitSourceCodeListPage(fnames, "<br><strong>There are " + fnames.size() + " source code snippets.</strong>");
    }


  //-------------------------------------
  private class SourceCodeComparator implements Comparator
    {
    public String compare(File fname)
      {
      return fname.getName();
      }
    }
  }

search.java

Synopsis
package jwiki;

import java.io.File;
import java.util.TreeSet;

public class search implements WikiPostable
  {
  // The most hits that are ever returned.
  private final static int maxHits = 200;

  private String m_lowpattern;
  private String m_pattern;
  private JHandler m_h;

  //------------------------------------------------------------------------
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    m_h = _h;
    HTMLspew _html = m_h.getHTML();

    getSearchString(buf, bufSize);
    if (m_pattern == null)
      {
      _html.transmitStringAsPage("You must specify what to search for.");
      return ;
      }

    m_lowpattern = m_pattern.toLowerCase();
    TreeSet fnames = Globals.pageDir.getFiles(m_h, new SearchComparator());
    _html.transmitFileListPage(fnames, maxHits, "<br>There were " + fnames.size() + " matching files found that matched '" + m_pattern + "'. ");
    }

  //------------------------------------------------------------------------
  public void getSearchString(byte buf[], int bufSize)
    {
    KeyValueTable vars = m_h.parsePOST(buf, bufSize);

    m_pattern = vars.value("whatfor");
    if (m_pattern != null && m_pattern.length() == 0)
      m_pattern = null;

    if (m_pattern != null)
      m_h.writeInLog("search: searching for '" + m_pattern + "'");
    }

  //-------------------------------------
  private class SearchComparator implements Comparator
    {
    public String compare(File f)
      {
      String fname = f.getName();
      if (matchContents(fname))
        return fname;

      if (matchName(fname))
        return fname + "  (matches file name only)";

      return null;
      }
    }

  //------------------------------------------------------------------------
  private boolean match(String s)
    {
    return s.toLowerCase().indexOf(m_lowpattern) != -1;
    }

  //------------------------------------------------------------------------
  private boolean matchName(String pagename)
    {
    return match(pagename);
    }

  //------------------------------------------------------------------------
  private boolean matchContents(String pagename)
    {
    try
      {
      if (match(getFileContents(pagename)))
        return true;
      }
    catch (Exception e)
      {
      m_h.writeInLog("search::matchContents: page '" + pagename + "' exception:" + e.getMessage());
      e.printStackTrace();
      }
    return false;
    }

  //------------------------------------------------------------------------
  String getFileContents(String pagename) throws Exception
    {
    WikiFile wf = new WikiFile(m_h, pagename);
    wf.read();
    wf.close();
    return wf.content();
    }
  }

recentchanges.java

Synopsis
package jwiki;

import java.io.File;
import java.util.TreeSet;
import java.util.Date;
import java.util.Calendar;

// shows a list of all files changed within x days
public class recentchanges implements WikiPostable
  {

  // The most hits that are ever returned.
  private final static int maxFiles = 500;
  private final static int numDaysOld = 5;
  private Date m_daysago;

  //-------------------------------------
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    calculatePastDate(numDaysOld);
    TreeSet fnames = Globals.pageDir.getFiles(_h, new RecentChangesComparator());

    HTMLspew _html = _h.getHTML();
    _html.transmitFileListPage(fnames, maxFiles, "<br>There have been " + fnames.size() + " files modified or created in the last " + numDaysOld + " days. ");
    }

  //-------------------------------------
  private void calculatePastDate(long days)
    {
    Calendar now = Calendar.getInstance();
    now.add(Calendar.DATE, (int) ( -1 * days));
    Globals.doDebug(2, "recentchanges::calculatePastDate: days=" + days + " now=" + now.toString());
    m_daysago = now.getTime();
    }

  //-------------------------------------
  private class RecentChangesComparator implements Comparator
    {
    public String compare(File fname)
      {
      Date fd = new Date(fname.lastModified());
      if (fd.after(m_daysago))
        return fname.getName();

      return null;
      }
    }
  }

Passwords.java

Synopsis
package jwiki;

//-----------------------------------------------------------------------------------
public class Passwords extends IniFile
  {
  private KeyValueTable m_passwords;
  private boolean m_secure;

  //-----------------------------------------------------------------------------------
  public Passwords()
    {
    m_secure = false;
    m_passwords = new KeyValueTable();
    }

  //-----------------------------------------------------------------------------------
  public void setSecurity(boolean sec)
    {
    Globals.doDebug(3, "Passwords::setSecurity : " + sec);
    m_secure = sec;
    }

  //-----------------------------------------------------------------------------------
  public boolean authorized(String user, String pass)
    {
    //no security? then authorize everything...
    if (m_secure == false)
      return true;

    String mpass = lookup(user);
    // Globals.doDebug(3, "Passwords::authorized : '" + mpass + "' pass='" + pass + "'");
    return mpass != null && mpass.equals(pass);
    }

  //------------------------------------------------------------------------------
  public void Save(String name, String value)
    {
    m_passwords.put(name, value);
    }

  //-----------------------------------------------------------------------------------
  public void read()
    {
    Globals.doDebug(2, "Passwords::read()");
    m_passwords.clear();

    // Back door if desired.  Else comment it out.
    //m_passwords.put("login", "password");
    read("passwords");
    }

  //-----------------------------------------------------------------------------------
  //-- private from here on
  //-----------------------------------------------------------------------------------

  //-----------------------------------------------------------------------------------
  private String lookup(String userName)
    {
    Globals.doDebug(2, "Passwords::lookup(" + userName + ")");
    String pwd = m_passwords.value(userName);
    if (pwd == null)
      Globals.doDebug(2, "Passwords::lookup no such user");
    else
      Globals.doDebug(2, "Passwords::lookup found it.");
    return pwd;
    }
  }

PagesDir.java

Synopsis
package jwiki;

import java.io.File;
import java.util.TreeSet;

public class PagesDir extends WikiDir
  {
  //--------------------------------------------------------------------------
  public PagesDir(String rootdir, String dirname)
    {
    super(rootdir, dirname);
    }

  //--------------------------------------------------------------------------
  public File locate(String pageName)
    {
    File f;

    if (pageName.length() == 0)
      {
      pageName = "Index";
      Globals.doDebug(3, "locatePage: 0 length pagename, trying " + pageName);
      }

    // Don't allow hackers to access the file system out of the docs directory.
    if (isBogusFileName(pageName))
      return defaultFile("locatePage: bad pageName (hack attack?): '" + pageName + "', defaulting ...");

    //if no page supplied, try Index page
    f = new File(m_path, pageName);

    if (!f.exists())
      f = defaultFile("locatePage: '" + f + "' does not exist, defaulting...");

    Globals.doDebug(3, "locatePage returning " + f);
    return f;
    }

  //--------------------------------------------------------------------------
  public boolean Exists(JHandler h, String pageName)
    {
    if (pageName.length() == 0)
      {
      Globals.doDebug(3, "PagesDir::Exists: 0 length pagename");
      return false;
      }

    boolean exists;
    try
      {
      File f = new File(m_path, pageName);
      exists = f.exists();
      }
    catch (Exception e)
      {
      h.writeInLog("PagesDir::Exists: exception:" + e.getMessage());
      e.printStackTrace();
      exists = false;
      }
    return exists;
    }

  //-------------------------------------
  public TreeSet getFiles(JHandler _h, Comparator cmp)
    {
    TreeSet ts = new TreeSet();
    try
      {
      File docs = new File(m_path);
      File fnames[] = docs.listFiles();
      for (int i = 0; i < fnames.length; i++)
        {
        String fn = cmp.compare(fnames[i]);
        if (fn != null)
          ts.add(fn);
        }
      }
    catch (Exception e)
      {
      _h.writeInLog("PagesDir::getFiles: exception:" + e.getMessage());
      e.printStackTrace();
      ts.add("Threw an exception: " + e.getMessage());
      }
    return ts;
    }

  //--------------------------------------------------------------------------
  //-- privates from here on
  //--------------------------------------------------------------------------

  //--------------------------------------------------------------------------
  private File defaultFile(String debugstring)
    {
    Globals.doDebug(3, debugstring);
    return new File(m_path, "noPage");
    }
  }


MessageHandlers.java

Synopsis
package jwiki;

import java.util.Vector;

//-------------------------------------------------------------------
public class MessageHandlers
  {
  private Vector m_handlers;

  //-------------------------------------------------------------------
  public MessageHandlers()
    {
    m_handlers = new Vector(10, 10);
    }

  //-------------------------------------------------------------------
  public void add(JHandler h)
    {
    m_handlers.addElement(h);
    }

  //-------------------------------------------------------------------
  public void remove(JHandler h)
    {
    m_handlers.removeElement(h);
    }
  }

logindone.java

Synopsis
package jwiki;

// Processes the response of username and password.
public class logindone implements WikiPostable
  {
  //----------------------------------------------------------------------------
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    HTMLspew _html = _h.getHTML();
    KeyValueTable vars = _h.parsePOST(buf, bufSize);
    String user = vars.value("user");
    String pass = vars.value("pass");

    // WARNING -- This is a dangerous debug statement because it reveals passwords.
    // Comment it out when not immediately in use.
    //Globals.doDebug(2, "logindone: user=" + user + "  pass=" + pass);

    if (!Globals.passwords.authorized(user, pass))
      {
      _h.writeInLog("logindone: user=" + user + " failed to log in");
      _html.transmitStringAsPage("<H4><FONT COLOR=ff0000>Invalid Login</FONT></H4>");
      return ;
      }
    _h.writeInLog("logindone: user=" + user + " has logged in successfully");

    _html.transmitHTTPHeaderSetPassword();
    _html.transmitPreamble();
    _html.transmitStyles();
    _html.sendString("<br><br><H4><FONT COLOR=0000ff>Login Successful</FONT></H4><br>\n");
    _html.transmitPostamble();
    }
  }

login.java

Synopsis
package jwiki;

//----------------------------------------------------------------------------
// Does the login function of JWiki.  The logindone WikiPostable
// processes the response of username and password.
public class login implements WikiPostable
  {
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    HTMLspew _html = _h.getHTML();
    _html.transmitLoginPage();
    }
  }

KeyValueTable.java

Synopsis
package jwiki;

import java.util.Hashtable;

//-----------------------------------------------------------------------
class KeyValueTable extends Hashtable
  {
  //-----------------------------------------------------------------------
  public void Parse(String line, String delimiter) throws Exception
    {
    Globals.doDebug(2, "KeyValueTable::Parse: Got delim='" + delimiter + "' line=" + line);
    int idx = line.indexOf(delimiter);
    if (idx < 0)
      throw new Exception();

    String key = line.substring(0, idx);
    String val = line.substring(idx + 1);
    Globals.doDebug(2, "KeyValueTable::Parse: Key:" + key + "  value:" + val);

    put(key, val);
    }

  //-----------------------------------------------------------------------
  public String value(String key)
    {
    return (String)get(key);
    }

  //-----------------------------------------------------------------------
  public boolean isFound(String key)
    {
    return get(key) != null;
    }

  //-----------------------------------------------------------------------
  public boolean isValueEqual(String key, String expval)
    {
    String actval = value(key);
    if (actval == null)
      return expval == null;

    return actval.equals(expval);
    }
  }

JWikiCvt.java

Synopsis
package jwiki;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.FileInputStream;

//---------------------------------------------------------------
// A utility class to convert from Wiki format to HTML.
// A ---- is converted to the HR tag.  A word with leading
// uppercase, then lower case, then uppercase is treated as
// a link to the named doccument.
// A hyperref like http://www.xmission.com/~rkeene is treated as a hyper ref.
// All other text is unchanged.
public class JWikiCvt
  {
  private StringReader _in;
  private StringBuffer _sBuf;
  private boolean _ungetIsValid;
  private int _ungetChar;
  private JHandler m_h;

  //---------------------------------------------------------------
  //--Converts string to HTML assuming the contents are Wiki style text.
  public static String wikiToHTML(JHandler h, String s) throws IOException
    {
    JWikiCvt c = new JWikiCvt(h);
    return c.wikiToHTMLEntry(s);
    }

  //---------------------------------------------------------------
  private JWikiCvt(JHandler h)
    {
    m_h = h;
    _ungetIsValid = false;
    }

  //---------------------------------------------------------------
  private String toHREF(String frag)
    {
    return toHREF(frag, frag);
    }

  //---------------------------------------------------------------
  private String toHREF(String frag1, String frag2)
    {
    return "<A style=\"padding=0:margin=0\" HREF=\"" + frag1 + "\">" + frag2 + "</a>";
    }

  //---------------------------------------------------------------
  private boolean isHardLink(String frag)
    {
    return frag.indexOf("http:") == 0 ||
           frag.indexOf("mailto:") == 0 ||
           frag.indexOf("news:") == 0 ||
           frag.indexOf("ftp:") == 0 ||
           frag.indexOf("mailto:") == 0 ||
           frag.indexOf("gopher:") == 0 ||
           frag.indexOf("telnet:") == 0;
    }

  //---------------------------------------------------------------
  private void BuildWikiLink(String frag, StringBuffer _sBuf)
    {
    int start = 0;
    int end = frag.length() - 1;

    //skip leading puncuation
    if (!Character.isLetterOrDigit(frag.charAt(start)))
      _sBuf.append(frag.charAt(start++));

    //adjust length for trailing punctuation
    int enddelta = 1;
    if (!Character.isLetterOrDigit(frag.charAt(end)))
      enddelta--;

    String pagename = frag.substring(start, end + enddelta);
    String linkname = pagename;
    if (!Globals.pageDir.Exists(m_h, pagename))
      {
      //page doesn't exist, treat the pagename as text
      //and a link to the noPage page and tack on a trailing ?
      _sBuf.append(pagename);
      pagename = "noPage";
      linkname = "?";
      }
    _sBuf.append(toHREF(pagename, linkname));

    //tack on trailing punctuation
    if (!Character.isLetterOrDigit(frag.charAt(end)))
      _sBuf.append(frag.charAt(end));
    }

  //---------------------------------------------------------------
  // The real guts of the converter.
  private String wikiToHTMLEntry(String s) throws IOException
    {
    _in = new StringReader(s);
    _sBuf = new StringBuffer();
    boolean isPrevCharCR = true;

    while (true)
      {
      //readFrag returns: null, a lone CR, whitespace, or non-whitespace
      String frag = readFrag();
      if (frag == null)
        break;

      boolean isCurCharCR = isEOL(frag.charAt(0));
      //Globals.doDebug(1, "wthe: cur=" + isCurCharCR + " prev=" +  isPrevCharCR + " frag='" + frag + "'");

      //chunk of white? most common let's test it first
      if (isWhiteSpace(frag.charAt(0)))
        _sBuf.append(frag);

      else if (isCurCharCR)
        {
        if (isPrevCharCR)  //two Cr's in a row
          _sBuf.append("<BR>");

        _sBuf.append((char) Constants.CR);
        }

      //nonwhite: a wiki horiz. rule?
      else if (isPrevCharCR && frag.indexOf("---") == 0)
        _sBuf.append("\n<HR>\n");

      //nonwhite: a link?
      else if (isHardLink(frag))
        _sBuf.append(toHREF(frag));

      //nonwhite: a wikilink?
      else if (isWikiLink(frag))
        BuildWikiLink(frag, _sBuf);

      //nonwhite: just text
      else
        _sBuf.append(frag);

      isPrevCharCR = isCurCharCR;
      }

    return _sBuf.toString();
    }

  //---------------------------------------------------------------
  // True if s has the first character upper case
  // then lower case, then later is upper case again.
  // This is a HREF in Wiki style docs.
  private boolean isWikiLink(String s)
    {
    int len = s.length();
    if (len < 4)
      return false;  //got to be at least 4 chars...

    int i = 0;
    char ch = s.charAt(i);
    if (ch == '(' || ch == '[' || ch == '{')
      ch = s.charAt(++i);  //skip leading punctuation

    boolean curup = Character.isUpperCase(ch);
    if (!curup)
      return false;  //first char must be uppercase

    int upcount = 1;
    boolean prevup;
    for (i++; i < len - 1; i++)
      {
      prevup = curup;

      ch = s.charAt(i);
      if (!Character.isLetterOrDigit(ch))
        return false; //not alphanumeric

      curup = Character.isUpperCase(ch);
      if (curup && prevup)
        return false;  //two upper case in a row?

      //current is upper
      if (curup)
        upcount++;
      }

    if (upcount < 2)
      return false; //got to have more than one upper case.

    return isLowerCaseOrPunctuation(s.charAt(i));
    }

  //--------------------------------------------------------------------
  private boolean isLowerCaseOrPunctuation(char ch)
    {
    return (Character.isLetterOrDigit(ch) && !Character.isUpperCase(ch))
           || ch == '!'
           || ch == ')'
           || ch == ']'
           || ch == '}'
           || ch == ';'
           || ch == ':'
           || ch == ','
           || ch == '.'
           || ch == '?';
    }

  //--------------------------------------------------------------------
  private int skipLineFeeds(int c) throws IOException
    {
    while (isLineFeed(c))
      c = getC();
    return c;
    }

  //--------------------------------------------------------------------
  private boolean isLineFeed(int c)
    {
    return c == Constants.LF;
    }

  //--------------------------------------------------------------------
  private boolean isEOS(int c)
    {
    return c == -1;
    }

  //--------------------------------------------------------------------
  private boolean isEOL(int c)
    {
    return c == Constants.CR;
    }

  //--------------------------------------------------------------------
  private boolean isWhiteSpace(int c)
    {
    return c == ' ' || c == '\t';
    }

  //--------------------------------------------------------------------
  private void getAllWhiteSpace(StringBuffer b, int c) throws IOException
    {
    while (isWhiteSpace(c))
      {
      b.append((char)c);
      c = getC();
      }
    ungetC(c);
    }

  //--------------------------------------------------------------------
  private void getAllNonWhiteSpace(StringBuffer b, int c) throws IOException
    {
    while (!isWhiteSpace(c) && !isEOL(c) && !isLineFeed(c) && !isEOS(c))
      {
      b.append((char)c);
      c = getC();
      }
    ungetC(c);
    }

  //--------------------------------------------------------------------
  /** Reads the next fragment of the input.
   ** A frag is either a series of white space, a lone CR,
   ** or a series of printables.
   ** @returns null when no more input.
   **/
  private String readFrag() throws IOException
    {
    StringBuffer b = new StringBuffer();

    int c = getC();
    c = skipLineFeeds(c);

    if (isEOS(c))
      return null; //the frag is all LF's or is an empty string

    //A frag is either a lone CR ...
    if (isEOL(c))
      {
      b.append((char)c);
      return b.toString();
      }

    //or a series of white space ...
    if (isWhiteSpace(c))
      {
      getAllWhiteSpace(b, c);
      return b.toString();
      }

    //or a series of printables.
    getAllNonWhiteSpace(b, c);
    return b.toString();
    }

  //--------------------------------------------------------------------
  private int getC() throws IOException
    {
    if (_ungetIsValid)
      {
      _ungetIsValid = false;
      return _ungetChar;
      }
    return _in.read();
    }

  //--------------------------------------------------------------------
  private void ungetC(int c)
    {
    if (_ungetIsValid)
      System.out.println("Warning: JWikiCvt unget overflow.");

    _ungetIsValid = true;
    _ungetChar = c;
    }

  //---------------------------------------------------------------
  /** Reads up to 64k characters from the file specified by args[0]
   ** then translates it and prints the results.  For debug use.
   **/
  public static void main(String args[])
    {
    //for (int i = 20; i < 100; i++)
    //for (int i = 100; i < 180; i++)
    /*
    for (int i = 0; i < 0x80; i++)
      {
      char cap = 'l';
      if (Character.isUpperCase((char) i))
        cap = 'C';
      char alpha = '-';
      if (Character.isLetterOrDigit((char)i))
        alpha = 'a';
      char title = '-';
      if (Character.isTitleCase((char)i))
        title = 'T';
      System.out.println(Integer.toString(i, 16)+ ":'" + (char) i+ "':" + cap + ":" + alpha + ":" + title);
      }
     */
    try
      {
      File f = new File(args[0]);
      FileInputStream in = new FileInputStream(f);

      byte[] buf = new byte[65535];
      int r = in.read(buf);
      in.close();

      String s = new String(buf, 0, r);

      System.out.println(s);
      }
    catch (Exception e)
      {
      System.out.println("exception: " + e.getMessage());
      e.printStackTrace();
      }
    }
  }

JWiki.java

Synopsis
package jwiki;

import java.net.ServerSocket;
import java.net.Socket;

//-----------------------------------------------------------------------------------
//  This server runs with no awt window. It listens on port 8088 (default) The
//  command line argument is the root of the JWiki file system. <BR>
//-----------------------------------------------------------------------------------
public class JWiki implements Runnable
  {
  private boolean m_keepRunning;
  private ServerSocket m_serversock;

  //-----------------------------------------------------------------------------------
  // The only command line arg is not optional and is the root directory of the
  // JWiki file system.
  public static void main(String[] args)
    {
    if (args.length != 1)
      {
      System.out.println("Must have only the root directory name on the command line.");
      return ;
      }
    JWiki j = new JWiki(args[0]);
    (new Thread(j)).start();
    }

  //------------------------------------------------------
  public JWiki(String root)
    {
    m_keepRunning = true;
    m_serversock = null;

    Globals.Init(root);
    if (!Globals.RootIsValid())
      return ;

    Globals.config.read();
    Globals.passwords.read();
    Globals.users.read();
    }

  //-----------------------------------------------------------------------------------
  public void run()
    {
    Globals.doDebug(1, "JWiki thread started.");
    try
      {
      InitializeServerSocket();
      while (m_keepRunning)
        HandleConnection();

      TerminateServerSocket();
      }
    catch (Exception e)
      {}


    }


  //-----------------------------------------------------------------------------------
  //-- private from here on
  //-----------------------------------------------------------------------------------

  //-----------------------------------------------------------------------------------
  private void InitializeServerSocket() throws Exception
    {
    try
      {
      m_serversock = new ServerSocket(Globals.config.getPort());
      }
    catch (Exception e)
      {
      Globals.doDebug(0, "JWiki::run: new serversocket exception:" + e.getMessage());
      e.printStackTrace();
      throw e;
      }
    }

  //-----------------------------------------------------------------------------------
  private void HandleConnection() throws Exception
    {
    try
      {
      Socket newSock = m_serversock.accept();

      Globals.editLocks.releaseOld();

      JHandler hand = new JHandler(newSock);
      (new Thread(hand)).start();
      }
    catch (Exception e)
      {
      Globals.doDebug(0, "JWiki::run: new JHandler exception:" + e.getMessage());
      e.printStackTrace();
      throw e;
      }
    }

  //-----------------------------------------------------------------------------------
  private void TerminateServerSocket() throws Exception
    {
    try
      {
      m_serversock.close();
      }
    catch (Exception e)
      {
      Globals.doDebug(0, "JWiki::run: sock.close Exception: " + e.getMessage());
      throw e;
      }
    }
  }

JHandler.java

Synopsis
package jwiki;

import java.io.OutputStream;
import java.io.InputStream;
import java.net.Socket;
import java.net.InetAddress;

public class JHandler implements Runnable
  {
  //private variables
  private Socket m_sock;
  private IncomingLog m_inlog;
  private HTMLspew m_html;

  // Set just after reading the head information.  If the WikiLogin cookie
  // is present then they are logged in.  The login and logindone WikiPostables
  // set the cookie.  Login is good for 6 hours.
  private boolean m_userLogin;

  //---------------------------------------------------------------------
  public JHandler(Socket sock)
    {
    m_userLogin = false;
    m_sock = sock;
    m_inlog = new IncomingLog(m_sock.getPort());

    String currentUser = PrintLogHeader();
    Globals.handlers.add(this);
    m_html = new HTMLspew(this, currentUser);
    }

  //---------------------------------------------------------------------
  //-- Main processing method for the JHandler object
  public void run()
    {
    OutputStream out = null;
    InputStream in = null;
    try
      {
      out = m_sock.getOutputStream();
      in = m_sock.getInputStream();
      m_html.setStream(out);
      handleMessage(in);
      }
    catch (Exception e)
      {
      //fall thru, maybe we can close something...
      writeInLog("JHandler::run: (1) exception: " + e.getMessage());
      e.printStackTrace();
      }

    try
      {
      m_sock.close();
      if (out != null)
        out.close();
      if (in != null)
        in.close();
      }
    catch (Exception e1)
      {
      writeInLog("JHandler::run: (2) exception: " + e1.getMessage());
      e1.printStackTrace();
      }

    Globals.handlers.remove(this);
    }

  //----------------------------------------------------------------------
  public void writeInLog(String s)
    {
    m_inlog.write(s);
    }

  //----------------------------------------------------------------------
  public HTMLspew getHTML()
    {
    return m_html;
    }

  //---------------------------------------------------------------------
  //  Returns false on not authorized. Login is detected by the cookie WikiLogin
  //  being set to the user name.
  public boolean userLoggedIn()
    {
    return m_userLogin;
    }

  //---------------------------------------------------------------------
  public void doGet(String pagename)throws Exception
    {
    if (isImage(pagename))
      doGetImage(pagename);
    else if (isSource(pagename))
      doGetSource(pagename);
    else
      doGetPage(pagename);
    }

  //---------------------------------------------------------------------
  private byte HexCharToNybble(byte b)
    {
    if (b >= '0' && b <= '9')
      return (byte) (b - '0');

    if (b >= 'a' && b <= 'f')
      return (byte) (10 + b - 'a');

    return (byte) (10 + b - 'A');
    }

  //---------------------------------------------------------------------
  private byte ConvertTwoHexCharsToByte(byte buf[], int i)
    {
    //Globals.doDebug(1, "cthctb: i="+i+" buf[i]=" + buf[i]+"='"+(char)buf[i]+"' buf[i+1]=" + buf[i+1]+"='"+(char)buf[i+1]+"'");
    byte b = HexCharToNybble(buf[i]);
    b = (byte) (b * 16);
    b += HexCharToNybble(buf[i + 1]);
    //Globals.doDebug(1, "cthctb: b="+b);
    return b;
    }

  //---------------------------------------------------------------------
  private void StoreKeyValuePair(StringBuffer buf, KeyValueTable pairs) throws Exception
    {
    String line = buf.toString();
    pairs.Parse(line, "=");
    buf.setLength(0);
    }

  //---------------------------------------------------------------------
  // Parses a buffer of POST data into entries in a KeyValueTable.
  //@param  buf  The buffer of data.
  //@param  n    How much data is in buf.
  //@return      Description of the Returned Value
  //@returns     The new table, or null on error.
  public KeyValueTable parsePOST(byte buf[], int n)
    {
    KeyValueTable pairs = new KeyValueTable();
    StringBuffer tmp = new StringBuffer(1024);

    try
      {
      for (int i = 0; i < n; i++)
        {
        if (buf[i] == '%')
          {
          i++; //skip over %, now pointing to first hex char
          tmp.append((char) ConvertTwoHexCharsToByte(buf, i));
          i++; //skip over second hex char
          }
        else if (buf[i] == '&')
          StoreKeyValuePair(tmp, pairs);
        else if (buf[i] == '+')
          tmp.append(' ');
        else
          tmp.append((char) (buf[i]));
        }

      StoreKeyValuePair(tmp, pairs);
      return pairs;
      }
    catch (Exception e)
      {
      return null;
      }
    }

  //---------------------------------------------------------------------
  public void kill()
    {
    try
      {
      m_sock.close();
      }
    catch (Exception e)
      {
      writeInLog("JHandler::kill: sock close exception: " + e.getMessage());
      e.printStackTrace();
      }
    }

  //---------------------------------------------------------------------
  //-- private from here on
  //---------------------------------------------------------------------

  private String PrintLogHeader()
    {
    writeInLog("---Incoming on Socket: " + m_sock.toString());
    InetAddress ina = m_sock.getInetAddress();
    writeInLog("---HostName    : " + ina.getHostName());
    writeInLog("---HostAddress : " + ina.getHostAddress());
    String currentUser = Globals.users.get(ina.getHostName(), ina.getHostAddress());
    writeInLog("---User        : " + currentUser);
    return currentUser;
    }

  //---------------------------------------------------------------------
  // This is the main routine that handles message content.
  private void handleMessage(InputStream instream) throws Exception
    {
    // Read header lines:
    // we only look for Type, pageurl, Content-Length and the Cookie: WikiLogin
    IncomingHTTPRequest req = new IncomingHTTPRequest(this, instream);
    try
      {
      req.Read();
      }
    catch (Exception e)
      {
      writeInLog("JHandler::handleMessage: req.Read exception: " + e.getMessage());
      e.printStackTrace();
      return ;
      }

    m_userLogin = req.userLogin();

    req.logit(this);
    if (req.isGet())
      doGet(req.pageUrl());
    else if (req.isPost())
      doPOST(req.pageUrl(), req.content(), req.contentLength());
    else
      req.logBadType(this);
    }

  //---------------------------------------------------------------------
  private boolean isImage(String pagename)
    {
    return pagename.indexOf(".jpg") != -1 || pagename.indexOf(".gif") != -1;
    }

  //---------------------------------------------------------------------
  private boolean isSource(String pagename)
    {
    return Globals.sourceDir.prefixOf(pagename);
    }

  //---------------------------------------------------------------------
  private void doGetImage(String fname)throws Exception
    {
    m_html.transmitFile(Globals.imageDir.locate(fname));
    }

  //---------------------------------------------------------------------
  private void doGetSource(String fname)throws Exception
    {
    String fn = fname.substring(7);
    m_html.transmitSourceFile(Globals.sourceDir.locate(fn));
    }

  //---------------------------------------------------------------------
  private void doGetPage(String pagename)throws Exception
    {
    WikiFile f = new WikiFile(this, pagename);

    f.read();
    f.close();

    if (f.isNoReadPermission())
      m_html.transmitStringAsPage("No read permission for page:" + pagename);
    else if (f.isReadLoginRequired())
      m_html.transmitStringAsPage("You must login to see that file.<br>" +
                                  "<form id=formx method=\"post\" action=\"login\">\n" +
                                  "<input type=\"Submit\" name=Submit value=\"Login\">\n" +
                                  "</form>" +
                                  "\n&nbsp;Login here then Back to this page and press Reload");
    else if (f.isReadAllowed())
      m_html.transmitHtmlPage(f); //pagename, fin, header);
    else
      m_html.transmitStringAsPage("Unknown read permission: '" + f.readPermAsString() + "'");
    }


  //---------------------------------------------------------------------
  /**
   *  A POST is handled by assuming the urls is the class name that handles the
   *  POST. For example, if urls is "edit" then jwiki.edit is the class name. A
   *  class to do this must implement the WikiPostable interface.
   *
   *@param  cname    Description of Parameter
   *@param  buf      Description of Parameter
   *@param  bufSize  Description of Parameter
   */
  private void doPOST(String cname, byte buf[], int bufSize)
    {
    Globals.doDebug(1, "POST class: " + cname);
    try
      {
      Class c = Class.forName("jwiki." + cname);

      WikiPostable p = (WikiPostable) c.newInstance();
      p.handlePOST(this, buf, bufSize);
      }
    catch (Exception ec)
      {
      writeInLog("JHandler::doPost: cname=" + cname + "exception: " + ec.getMessage());
      ec.printStackTrace();
      }
    }
  }

IniFile.java

Synopsis
package jwiki;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.util.StringTokenizer;

//-----------------------------------------------------------------------------------
public abstract class IniFile
  {
  private String m_fname;

  public abstract void Save(String key, String value);

  //-----------------------------------------------------------------------------------
  protected void read(String fname)
    {
    m_fname = fname;
    try
      {
      BufferedReader _in = new BufferedReader(new FileReader(m_fname));
      ReadFile(_in);
      _in.close();
      }
    catch (Exception e)
      {
      System.err.println("Failed to read file '" + fname + "'.  Exiting...");
      System.exit(1);
      }
    }

  //--------------------------------------------------------------------------
  private void ReadFile(BufferedReader _in) throws Exception
    {
    StringTokenizer tokenizer;
    String currentLine;

    while (_in.ready())
      {
      currentLine = _in.readLine();
      if (currentLine == null)
        return ;

      // skip if a comment or a blank line.
      if (currentLine.length() == 0 || currentLine.charAt(0) == '#')
        continue;

      tokenizer = new StringTokenizer(currentLine, ":");
      Save(tokenizer.nextToken(), tokenizer.nextToken());
      }
    }
  }

IncomingLog.java

Synopsis
package jwiki;

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;

public class IncomingLog
  {
  private DataOutputStream m_inlog;
  private int m_port;

  //---------------------------------------------------------------------
  public IncomingLog(int port)
    {
    m_port = port;
    try
      {
      m_inlog = new DataOutputStream(new FileOutputStream("incoming.log", true));
      }
    catch (FileNotFoundException e)
      {
      Globals.doDebug(1, "IncomingLog::ctor: Exception: " + e.getMessage());
      }
    }

  //---------------------------------------------------------------------
  synchronized public void write(String s)
    {
    try
      {
      m_inlog.writeBytes("[" + m_port + "] ");
      m_inlog.writeBytes(s);
      Globals.crlf(m_inlog);
      m_inlog.flush();
      Globals.doDebug(1, "log: [" + m_port + "] " + s);
      }
    catch (Exception e)
      {
      Globals.doDebug(1, "IncomingLog::write: Exception: " + e.getMessage());
      }
    }
  }

IncomingHTTPRequest.java

Synopsis
package jwiki;

import java.io.InputStream;

//---------------------------------------------------------------------
public class IncomingHTTPRequest
  {
  private InputStream m_instream;
  private byte m_ioBuf[];

  private String m_type;
  private String m_pageurl;
  private String m_version;
  private int m_contentLength;
  private boolean m_userLogin;
  private JHandler m_h;

  //---------------------------------------------------------------------
  public IncomingHTTPRequest(JHandler h, InputStream in)
    {
    m_h = h;
    m_instream = in;
    m_ioBuf = new byte[Constants.MaxPostSize];
    m_userLogin = false;
    }

  //---------------------------------------------------------------------
  public void Read() throws Exception
    {
    ReadHeader();
    ReadContent();
    }

  //---------------------------------------------------------------------
  public String pageUrl()
    {
    return m_pageurl;
    }

  //---------------------------------------------------------------------
  public byte[] content()
    {
    return m_ioBuf;
    }

  //---------------------------------------------------------------------
  public int contentLength()
    {
    return m_contentLength;
    }

  //---------------------------------------------------------------------
  public boolean userLogin()
    {
    return m_userLogin;
    }

  //---------------------------------------------------------------------
  public boolean isGet()
    {
    return m_type.equalsIgnoreCase("GET");
    }

  //---------------------------------------------------------------------
  public boolean isPost()
    {
    return m_type.equalsIgnoreCase("POST");
    }

  //---------------------------------------------------------------------
  public void logit(JHandler _h)
    {
    _h.writeInLog("Type=" + m_type + " Page:" + m_pageurl);
    }

  //---------------------------------------------------------------------
  public void logBadType(JHandler _h)
    {
    _h.writeInLog("Unknown HTTP request type: " + m_type);
    }

  //---------------------------------------------------------------------
  //-- private from here on
  //---------------------------------------------------------------------

  //---------------------------------------------------------------------
  private void ReadHeader() throws Exception
    {
    m_contentLength = -1;
    ReadHeaderFirst();

    String orig;
    while (true)
      {
      orig = readLine();
      if (orig.length() == 0)
        break;

      orig = orig.trim();
      Globals.doDebug(2, "Parsing HTTP header: " + orig);
      String origUpper = orig.toUpperCase();

      CheckForContentLength(orig, origUpper);
      CheckForCookie(orig, origUpper);
      }
    }

  //---------------------------------------------------------------------
  private void ReadContent() throws Exception
    {
    int clen = m_contentLength;
    Globals.doDebug(1, "  contentlen: " + clen);

    if (clen <= 0)
      return ;

    Globals.doDebug(1, "The request had content length   =" + clen);

    int n = 0;
    while (n != clen)
      {
      Globals.doDebug(1, "The bytes remaining in buffer are=" + m_instream.available());
      if (m_instream.available() == 0)
        break;
      int n1 = m_instream.read(m_ioBuf, n, clen - n);
      Globals.doDebug(1, "The number of bytes actually read=" + n1);
      if (n1 == -1)
        break;
      n += n1;
      }

    if (n != clen)
      Globals.doDebug(1, "Amount of content read did not match content length, (" + n + ")");

    String s = new String(m_ioBuf, 0, n);
    Globals.doDebug(4, "Read content from browser of: " + s);
    }

  //---------------------------------------------------------------------
  private void ReadHeaderFirst() throws Exception
    {
    String orig = readLine();
    if (orig.length() == 0)
      {
      Globals.doDebug(1, "No HTTP status line (no first line)");
      return ;
      }
    Globals.doDebug(1, "  Request is: " + orig);
    orig = orig.trim();

    int i = orig.indexOf(" ");
    if (i == -1)
      {
      Globals.doDebug(1, "No HTTP type");
      return ;
      }
    m_type = orig.substring(0, i).trim();
    Globals.doDebug(2, "     type is: " + m_type);

    int j = orig.indexOf(" ", i + 1);
    if (j == -1)
      {
      Globals.doDebug(1, "No HTTP URL");
      return ;
      }
    // Skip the leading / too
    m_pageurl = orig.substring(i + 2, j).trim();
    Globals.doDebug(2, "     url  is: " + m_pageurl);

    i = j;
    m_version = orig.substring(i + 1).trim();
    Globals.doDebug(2, "     vers is: " + m_version);
    }

  //---------------------------------------------------------------------
  private void CheckForCookie(String orig, String origUpper) throws Exception
    {
    int i = origUpper.indexOf("COOKIE:");
    if (i != 0)
      return ;

    i = orig.indexOf(":");
    if (i == -1)
      {
      Globals.doDebug(1, "Invalid HTTP header line, no ':'");
      throw new Exception("Invalid HTTP line");
      }

    String val = orig.substring(i + 1).trim();
    if (val.indexOf("WikiLogin=true") != -1)
      m_userLogin = true;
    }

  //---------------------------------------------------------------------
  private void CheckForContentLength(String orig, String origUpper) throws Exception
    {
    int i = origUpper.indexOf("CONTENT-LENGTH:");
    if (i != 0)
      return ;

    i = orig.indexOf(":");
    if (i == -1)
      {
      Globals.doDebug(1, "Invalid HTTP header line, no ':'");
      throw new Exception("Invalid HTTP line");
      }

    String val = orig.substring(i + 1).trim();
    try
      {
      m_contentLength = Integer.parseInt(val);
      }
    catch (Exception e)
      {
      m_h.writeInLog("IncomingHTTPRequest::CheckforContentLength: val=" + val + " exception: " + e.getMessage());
      e.printStackTrace();
      throw e;
      }
    }

  //---------------------------------------------------------------------
  // Reads a line up to the terminating crlf and puts it in the m_ioBuf If
  // m_ioBuf[0] is '\0' then end of header lines. Returns a String or "" if no more data.
  private String readLine()
    {
    int i = 0;
    int n;

    try
      {
      n = m_instream.read(m_ioBuf, i, 1);
      while (n > 0 && m_ioBuf[i] != Constants.LF && i < (m_ioBuf.length - 1))
        {
        if (m_ioBuf[i] != Constants.CR)
          {
          i++;
          }
        n = m_instream.read(m_ioBuf, i, 1);
        }
      }
    catch (Exception e)
      {
      m_h.writeInLog("IncomingHTTPRequest::readline: exception: " + e.getMessage());
      e.printStackTrace();
      }

    String s = new String(m_ioBuf, 0, i);
    return s;
    }
  }

HTMLspew.java

Synopsis
package jwiki;

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.util.TreeSet;
import java.util.Date;
import java.util.Iterator;

//---------------------------------------------------------------
public class HTMLspew
  {
  OutputStream m_out;
  String m_currentUser;
  JHandler m_h;

  //---------------------------------------------------------------
  public HTMLspew(JHandler h, String currentUser)
    {
    m_h = h;
    m_currentUser = currentUser;
    }

  //---------------------------------------------------------------
  public void setStream(OutputStream out)
    {
    m_out = out;
    }

  //------------------------------------------------------------------------
  // Transmits a file list
  public void transmitFileListPage(TreeSet fnames, int maxFiles, String title) throws IOException
    {
    transmitHTTPHeader();
    transmitPreamble();
    transmitStyles();
    transmitRecentChangesForm();
    transmitAllPagesForm();
    transmitHomePageForm();
    transmitSearchForm();
    transmitRule();
    sendString(title);
    sendString("Maximum of " + maxFiles + " shown.<br><br>");
    transmitFileList(fnames, maxFiles);
    transmitPostamble();
    }

  //------------------------------------------------------------------------
  // Transmits a file list
  public void transmitSourceCodeListPage(TreeSet fnames, String title) throws IOException
    {
    transmitHTTPHeader();
    transmitPreamble();
    transmitStyles();
    transmitRecentChangesForm();
    transmitAllPagesForm();
    transmitHomePageForm();
    transmitSearchForm();
    transmitRule();
    transmitFormBegin("upload");
    transmitFormButton(true, "Submit", "UploadNewSnippet");
    transmitFormEnd();
    sendString("<br>");
    sendString(title);
    transmitFileTable(fnames);
    transmitPostamble();
    }

  //---------------------------------------------------------------
  public void transmitUploadPage()throws IOException
    {
    transmitHTTPHeader();
    transmitPreamble();
    transmitStyles();
    transmitRecentChangesForm();
    transmitAllPagesForm();
    transmitHomePageForm();
    transmitSearchForm();
    transmitRule();

    transmitFormBegin("uploadcode");
    transmitTextArea(null);
    sendString("<br>\n");
    transmitFormButton(true, "Submit", "Upload");
    sendString("File Name  ");
    transmitFormText(true, "file", "", 20);
    transmitFormEnd();
    transmitPostamble();
    }

  //------------------------------------------------------------------------
  // Transmits the JWiki page called pageName  Puts the preamble and postamble
  // and such in.
  public void transmitHtmlPage(WikiFile f) throws IOException
    {
    transmitHTTPHeader();
    transmitPreamble(f.isShowPreamble());
    transmitStyles();
    transmitLoginForm(f.isShowLoginButton());
    transmitRecentChangesForm();
    transmitAllPagesForm();
    transmitHomePageForm();
    transmitSourceCodeForm();
    transmitEditForm(f.isShowEditButton(), f.pageName());
    transmitSearchForm();
    transmitRule();

    String contents = f.content();
    if (contents == null)
      Globals.doDebug(1, "File transfer failed (1)");

    if (!f.isHtmlOK())
      contents = safeHTML(contents);

    if (f.isWikiOK())
      contents = JWikiCvt.wikiToHTML(m_h, contents);

    sendString(contents);
    transmitPostamble(f.isShowPostamble());
    }

  //---------------------------------------------------------------
  public void transmitHTTPHeader() throws IOException
    {
    transmitHTTPHeaderBegin();
    transmitHTTPHeaderEnd();
    }

  //---------------------------------------------------------------
  public void transmitHTTPHeaderSetPassword() throws IOException
    {
    transmitHTTPHeaderBegin();
    Date now = new Date();
    Date later = new Date(now.getTime() + 1000 * 60 * 60 * 6);   // Now + 6 hours
    Globals.doDebug(2, "Set-cookie: WikiLogin=true; expires=" + later.toString());
    send("Set-cookie: WikiLogin=true; expires=" + later.toString());
    transmitHTTPHeaderEnd();
    }

  //------------------------------------------------------------------------
  public void transmitTextArea(String content)throws IOException
    {
    sendString("<TEXTAREA name=txt cols=80 rows=25>\n");
    if (content != null)
      sendSafeString(content);
    sendString("</TEXTAREA>\n");
    }

  //---------------------------------------------------------------------
  // Transmits a message or arbitrary String as a page instead of sending a
  // file . Sticks a &lt;PRE&gt; block around the string.
  public void transmitStringAsPage(String contents) throws IOException
    {
    transmitHTTPHeader();
    transmitPreamble();

    contents = "<PRE>\n" + contents + "\n</PRE>";
    Globals.doDebug(1, "Writing contents:" + contents);
    sendString(contents);
    transmitPostamble();
    }

  //------------------------------------------------------------------------
  public void transmitRecentChangesForm() throws IOException
    {
    transmitFormBegin("recentchanges");
    transmitFormButton(true, "Submit", "RecentChanges");
    transmitFormEnd();
    }

  //------------------------------------------------------------------------
  public void transmitSourceCodeForm() throws IOException
    {
    transmitFormBegin("sourcecode");
    transmitFormButton(true, "Submit", "SourceCode");
    transmitFormEnd();
    }

  //------------------------------------------------------------------------
  public void transmitHomePageForm() throws IOException
    {
    transmitFormBegin("homepage");
    transmitFormButton(true, "Submit", "Home");
    transmitFormEnd();
    }

  //------------------------------------------------------------------------
  public void transmitSearchForm() throws IOException
    {
    transmitFormBegin("search");
    transmitFormButton(true, "edit", "Search");
    transmitFormText(true, "whatfor", "", 30);
    transmitFormEnd();
    }

  //------------------------------------------------------------------------
  public void transmitRule() throws IOException
    {
    sendString("<hr>\n");
    }

  //---------------------------------------------------------------
  public void transmitLoginPage()throws IOException
    {
    transmitHTTPHeaderClearPassword();
    transmitPreamble();

    transmitStyles();
    sendString("<H4>Login to Wiki System</H4>\n");
    sendString("Please enter login name and password.<br>\n");

    // Now the body of the form.
    transmitFormBegin("logindone");
    sendStrongString("User Name:");
    transmitFormText(true, "user", "", 20);
    sendString("<BR>\n");
    sendStrongString("Password : &nbsp;");
    sendString("<INPUT TYPE=password name=pass cols=20 value=\"\">");
    sendString("<BR>\n");
    transmitFormButton(true, "Submit", "OK");
    transmitFormEnd();
    transmitPostamble();
    }

  //---------------------------------------------------------------
  public void transmitSourceFile(File f)throws IOException
    {
    sendString("<PRE>\n");
    transmitFile(f);
    sendString("</PRE>\n");
    }

  //---------------------------------------------------------------------
  public boolean transmitFile(File f)
    {
    Globals.doDebug(2, "Transferring file " + f);

    try
      {
      if (f == null)
        {
        transmitHTTPNotFound();
        return true;
        }

      boolean ret = true;
      FileInputStream fin = new FileInputStream(f);
      ret = transmitFile(fin);
      fin.close();
      return ret;
      }
    catch (Exception e)
      {
      m_h.writeInLog("HTMLSpew: file exception: " + e.getMessage());
      e.printStackTrace();
      }
    return false;
    }

  //------------------------------------------------------------------------
  public void transmitAllPagesForm() throws IOException
    {
    transmitFormBegin("allpages");
    transmitFormButton(true, "Submit", "AllPages");
    transmitFormEnd();
    }

  //------------------------------------------------------------------------
  public void transmitStyles() throws IOException
    {
    sendString(
      " <Style>\n" +
      " #formbtn {font-size: 1;display: inline; padding: 0em; margin: 0em; border: groove thin; background-color: lightblue}\n" +
      " #textx   {display: inline; margin: 0 em; padding: 0 em; border-style: groove; border-width: thin; background-color: white}\n" +
      " #formx   {display: inline; margin: 0 em}\n" +
      " </Style>\n"
    );
    }

  //------------------------------------------------------------------------
  public void transmitFormBegin(String name) throws IOException
    {
    sendString("<FORM id=formx METHOD=POST ACTION=" + name + ">\n");
    }

  //------------------------------------------------------------------------
  public void transmitFormEnd() throws IOException
    {
    sendString("</FORM>\n");
    }

  //------------------------------------------------------------------------
  public void transmitFormButton(boolean enabled, String name, String value) throws IOException
    {
    String en = "";
    if (!enabled)
      en = " disabled";
    sendString(
      "<Input " +
      " type=submit" +
      en +
      " ID=formbtn " +
      " name=" + name +
      " value=" + value +
      " />\n");
    }


  //---------------------------------------------------------------------------------
  public void transmitPreamble(boolean sendfile) throws IOException
    {
    sendString("<HTML>\n" +
               "<HEADER>\n" +
               "</HEADER>\n" +
               "<BODY>\n");

    // Do the preambleFile if needed.
    if (sendfile)
      {
      if (transmitFile(Constants.preambleFile) == false)
        Globals.doDebug(1, "File transfer failed (preamble)");
      }
    sendString("Current User: <a href=\"" + m_currentUser + "\">" + m_currentUser + "</a> ");
    }

  //---------------------------------------------------------------------------------
  public void transmitPreamble() throws IOException
    {
    transmitPreamble(true);
    }

  //---------------------------------------------------------------------------------
  public void transmitPostamble(boolean sendfile) throws IOException
    {
    if (sendfile)
      {
      if (transmitFile(Constants.postambleFile) == false)
        Globals.doDebug(1, "File transfer failed (postamble)");
      flush();
      }

    sendString("</BODY>\n" +
               "</HTML>\n");
    }

  //---------------------------------------------------------------------------------
  public void transmitPostamble() throws IOException
    {
    transmitPostamble(true);
    }

  //---------------------------------------------------------------------
  public void sendSafeString(String s) throws IOException
    {
    String contents = safeHTML(s);
    sendString(contents);
    }

  //---------------------------------------------------------------------
  public void sendCheckBox(boolean f, String name, String value, String text) throws IOException
    {
    sendString("<INPUT TYPE=checkbox " + setChecked(f) + " NAME=\"" + name + "\" VALUE=\"" + value + "\">" + text + "\n");
    }

  //---------------------------------------------------------------------
  public void sendRadio(boolean f, String name, String value, String text) throws IOException
    {
    sendString("<INPUT TYPE=checkbox " + setChecked(f) + " NAME=\"" + name + "\" VALUE=\"" + value + "\">" + text + "\n");
    }

  //---------------------------------------------------------------------
  public void sendStrongString(String s) throws IOException
    {
    sendString("<Strong>" + s + "</Strong>");
    }

  //---------------------------------------------------------------------
  //tmp public. should be private
  public void sendString(String s) throws IOException
    {
    m_out.write(s.getBytes());
    }

  //---------------------------------------------------------------
  //-- private from here on
  //---------------------------------------------------------------

  //---------------------------------------------------------------------
  private String setChecked(boolean f)
    {
    if (f)
      return "CHECKED";

    return "";
    }

  //------------------------------------------------------------------------
  private void transmitFileList(TreeSet fnames, int maxFiles) throws IOException
    {
    int i = 0;
    for (Iterator it = fnames.iterator(); it.hasNext() && i < maxFiles; i++)
      {
      String nm = (String)it.next();
      sendString("<A HREF=" + nm + ">" + nm + "</A><BR>");
      }
    }

  //------------------------------------------------------------------------
  private void transmitFileTable(TreeSet fnames) throws IOException
    {
    sendString("<table border=1 cellpadding=3 cellspacing=0 frame=box margin=2>");
    int i = 0;
    for (Iterator it = fnames.iterator(); it.hasNext(); i++)
      {
      String nm = (String)it.next();
      //sendString("<A HREF=" + nm + ">" + nm + "</A><BR>");
      sendString("<tr><td><a href=\"source\\" + nm + "\">" + nm + "</a><td>description here");
      }
    sendString("</table>");
    }

  //---------------------------------------------------------------
  private void flush() throws IOException
    {
    m_out.flush();
    }

  //---------------------------------------------------------------
  private void transmitHTTPHeaderBegin() throws IOException
    {
    send("HTTP/1.0 200 Ok");
    send("Connection: close");
    }

  //---------------------------------------------------------------
  private void transmitHTTPHeaderEnd() throws IOException
    {
    crlf();
    flush();
    }

  //---------------------------------------------------------------
  private void transmitHTTPNotFound() throws IOException
    {
    send("HTTP/1.0 404 That file or resource is not found.");
    }

  //---------------------------------------------------------------
  private void send(String s) throws IOException
    {
    m_out.write(s.getBytes());
    crlf();
    }

  //---------------------------------------------------------------
  private void crlf() throws IOException
    {
    Globals.crlf(m_out);
    }

  //---------------------------------------------------------------------
  // Converts s such that any HTML special characters are encoded as
  //  ampersand-whatever-semicolon
  private String safeHTML(String s)
    {
    StringBuffer b = new StringBuffer();
    int i;

    Globals.doDebug(2, "safeHTML(String)");
    for (i = 0; i < s.length(); i++)
      {
      int c = s.charAt(i);
      switch (c)
        {
      case '"':
        b.append("&quot;");
        break;
      case '&':
        b.append("&amp;");
        break;
      case '<':
        b.append("&lt;");
        break;
      case '>':
        b.append("&gt;");
        break;
      default:
        b.append((char) c);
        break;
        }
      }
    return b.toString();
    }
  //---------------------------------------------------------------------------------
  private boolean transmitFile(FileInputStream fin)
    {
    Globals.doDebug(2, "Transferring file (given FileInputStream)");
    try
      {
      byte _ioBuf[] = new byte[Constants.MaxPostSize];
      for (int n = fin.read(_ioBuf); n > 0; n = fin.read(_ioBuf))
        {
        m_out.write(_ioBuf, 0, n);
        flush();
        }

      fin.close();
      }
    catch (Exception e)
      {
      m_h.writeInLog("HTMLspew::transmitFile: Exception: " + e.getMessage());
      e.printStackTrace();
      return false;
      }
    return true;
    }

  //---------------------------------------------------------------
  private void transmitHTTPHeaderClearPassword() throws IOException
    {
    transmitHTTPHeaderBegin();
    send("Set-cookie: WikiLogin=false");
    transmitHTTPHeaderEnd();
    }

  //------------------------------------------------------------------------
  private void transmitFormText(boolean enabled, String name, String value, int charswide) throws IOException
    {
    String en = "";
    if (!enabled)
      en = " disabled";
    sendString(
      "<Input " +
      " type=text" +
      en +
      " ID=textx " +
      " Style=\"width: " + charswide + " em\" " +
      " name=" + name +
      " value=\"" + value + "\"" +
      " />\n");
    }


  //------------------------------------------------------------------------
  private void transmitEditForm(boolean enabled, String pageName) throws IOException
    {
    sendString("<br>");
    transmitFormBegin("edit");
    transmitFormButton(enabled, "Submit", "Edit");
    transmitFormButton(enabled, "Submit", "Create");
    transmitFormText(enabled, "file", pageName, 15);
    transmitFormEnd();
    }

  //------------------------------------------------------------------------
  private void transmitLoginForm(boolean enabled)throws IOException
    {
    transmitFormBegin("login");
    transmitFormButton(enabled, "login", "Login");
    transmitFormEnd();
    }
  }

homepage.java

Synopsis
package jwiki;

//---------------------------------------------------------------------
public class homepage implements WikiPostable
  {

  //---------------------------------------------------------------------
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    _h.doGet("Index");
    }
  }

HandleEditingCmd.java

Synopsis
package jwiki;

import java.io.IOException;

//---------------------------------------------------------------------
public abstract class HandleEditingCmd
  {
  protected String m_contents;
  protected WikiFile m_file;
  protected HTMLspew _html;

  //---------------------------------------------------------------------
  HandleEditingCmd(HTMLspew _html)
    {
    this._html = _html;
    }

  //---------------------------------------------------------------------
  public boolean cannotLock(String f) throws IOException
    {
    return false;
    }

  //---------------------------------------------------------------------
  boolean InitializeFile(JHandler _h, String fileName) throws IOException
    {
    m_file = new WikiFile(_h, fileName);
    return true;
    }

  //---------------------------------------------------------------------
  public void sendPage() throws IOException
    {
    Globals.doDebug(1, "edit: generating page...");

    _html.transmitHTTPHeader();
    _html.transmitPreamble(true);
    _html.transmitStyles();

    if (m_file.isNoWritePermission())
      _html.sendString("You do not have permission to edit that file.");
    else if (m_file.isWriteLoginRequired())
      _html.sendString("You must login to edit that file.");
    else
      SendEditForm();

    _html.transmitPostamble(true);
    }

  //---------------------------------------------------------------------
  //-- private from here on
  //---------------------------------------------------------------------

  //---------------------------------------------------------------------
  private void SendEditForm() throws IOException
    {
    _html.sendString("<H4>Editing file <FONT COLOR=0000FF>" + m_file.pageName() + "</FONT></H4>");
    _html.transmitFormBegin("editsave");
    _html.sendString("<INPUT id=formbtn TYPE=HIDDEN NAME=\"file\" VALUE=\"" + m_file.pageName() + "\">\n");

    SendFormButtons();
    SendFormBody();
    SendFormButtons();

    _html.transmitFormEnd();
    }

  //---------------------------------------------------------------------
  private void SendFormButtons() throws IOException
    {
    _html.transmitFormButton(true, "Submit", "Save");
    _html.transmitFormButton(true, "Submit", "Cancel");
    }

  //---------------------------------------------------------------------
  private void SendFormBody() throws IOException
    {
    _html.sendString("Make sure you exit using <B>Save</B> or <B>Cancel</B>.");

    SendPageOptions();
    SendEditOption();
    SendReadWriteOptions();
    SendTextArea();
    }

  //---------------------------------------------------------------------
  private void SendPageOptions() throws IOException
    {
    _html.sendString("<br>\n");
    _html.sendCheckBox(m_file.isHtmlOK(), "html", "html", "Allow HTML");
    _html.sendCheckBox(m_file.isWikiOK(), "wiki", "wiki", "Wiki");
    _html.sendCheckBox(m_file.isShowPreamble(), "preamble", "preamble", "Preamble");
    _html.sendCheckBox(m_file.isShowPostamble(), "postamble", "postamble", "Postamble");
    }

  //---------------------------------------------------------------------
  private void SendEditOption() throws IOException
    {
    _html.sendString("<br>\n");
    _html.sendCheckBox(m_file.isShowEditButton(), "editbutton", "editbutton", "EditButton");
    _html.sendStrongString("<small>Warning: Once off, it can be turned on only by editing the file on the server.</small>\n");
    }

  //---------------------------------------------------------------------
  private void SendReadWriteOptions() throws IOException
    {
    _html.sendString("<br>\n");
    SendReadOptions();
    _html.sendString("&nbsp;&nbsp;&nbsp;");
    SendWriteOptions();
    }

  //---------------------------------------------------------------------
  private void SendTextArea() throws IOException
    {
    _html.sendString("<br>");
    _html.transmitTextArea(m_contents);
    _html.sendString("<br>");
    }

  //---------------------------------------------------------------------
  private void SendReadOptions() throws IOException
    {
    _html.sendStrongString("Read:");
    _html.sendRadio(m_file.isReadAllChecked(), "readPerm", "all", "all");
    _html.sendRadio(m_file.isReadPasswordChecked(), "readPerm", "passwd", "login");
    }

  //---------------------------------------------------------------------
  private void SendWriteOptions() throws IOException
    {
    _html.sendStrongString("Write:");
    _html.sendRadio(m_file.isWriteAllChecked(), "writePerm", "all", "all");
    _html.sendRadio(m_file.isWritePasswordChecked(), "writePerm", "passwd", "login");
    _html.sendRadio(m_file.isWriteNoWriteChecked(), "writePerm", "none", "not writeable");
    }
  }


HandleEditCmd.java

Synopsis
package jwiki;

import java.io.IOException;

//---------------------------------------------------------------------
public class HandleEditCmd extends HandleEditingCmd
  {
  //---------------------------------------------------------------------
  HandleEditCmd(HTMLspew _html)
    {
    super(_html);
    }

  //---------------------------------------------------------------------
  public boolean cannotLock(String f) throws IOException
    {
    boolean cannotgetit = Globals.editLocks.get(f) == false;
    if (cannotgetit)
      _html.transmitStringAsPage("Someone is already editing the file '" + f + "'");
    return cannotgetit;
    }

  //---------------------------------------------------------------------
  public boolean InitializeFile(JHandler _h, String fileName) throws IOException
    {
    if (!super.InitializeFile(_h, fileName))
      return false;

    try
      {
      m_file.read();
      m_file.close();
      m_contents = m_file.content();
      }
    catch (Exception oe)
      {
      _html.transmitStringAsPage("Can not open file <B>" + m_file.pageName() + "</B>: " + oe.getMessage());
      _h.writeInLog("edit: file open, read exception: " + oe.getMessage());
      oe.printStackTrace();
      return false;
      }

    return true;
    }
  }


HandleCreatePageCmd.java

Synopsis
package jwiki;

import java.io.IOException;

//---------------------------------------------------------------------
public class HandleCreatePageCmd extends HandleEditingCmd
  {
  //---------------------------------------------------------------------
  HandleCreatePageCmd(HTMLspew _html)
    {
    super(_html);
    }

  //---------------------------------------------------------------------
  boolean InitializeFile(JHandler _h, String fileName) throws IOException
    {
    if (!super.InitializeFile(_h, fileName))
      return false;

    if (m_file.exists())
      {
      _html.transmitStringAsPage("The file <B>" + m_file.pageName() + "</B> already exists.");
      return false;
      }

    m_file.setHeaderDefaults();
    m_contents = "Type the new file here.";
    return true;
    }
  }

Globals.java

Synopsis
package jwiki;

import java.io.DataOutputStream;
import java.io.OutputStream;
import java.io.File;
import java.io.IOException;

//---------------------------------------------------------------------
public final class Globals
  {
  public static PagesDir pageDir;
  public static PagesDir sourceDir;
  public static WikiDir imageDir;

  public static MessageHandlers handlers;
  public static EditLocks editLocks;
  public static Passwords passwords;
  public static Users users;
  public static Config config;

  private static int m_debugLevel;
  private static byte m_crlf[];
  private static String m_root;

  private final static String cSourceDirName = "source";
  private final static String cImageDirName = "images";
  private final static String cPageDirName = "pages";

  //---------------------------------------------------------------------
  //TODO: kill this function..
  public static String GetPageDir()
    {
    return m_root + File.separator + cPageDirName;
    }
  public static String GetSourceDir()
    {
    return m_root + File.separator + cSourceDirName;
    }

  //-----------------------------------------------------------------------
  public static void Init(String rootpath)
    {
    m_root = rootpath;

    //    String pagedir = GetPageDir();
    pageDir = new PagesDir(m_root, cPageDirName);
    sourceDir = new PagesDir(m_root, cSourceDirName);
    imageDir = new WikiDir(m_root, cImageDirName);

    Constants.preambleFile = new File(GetPageDir(), "preamble");
    Constants.postambleFile = new File(GetPageDir(), "postamble");

    m_crlf = new byte[2];
    m_crlf[0] = Constants.CR;
    m_crlf[1] = Constants.LF;

    handlers = new MessageHandlers();
    editLocks = new EditLocks();
    passwords = new Passwords();
    users = new Users();
    config = new Config();
    }

  //---------------------------------------------------------------------
  public static void setDebugLevel(int lev)
    {
    m_debugLevel = lev;
    }

  //---------------------------------------------------------------------
  // Puts mes out to wherever debug goes. Usualy the log.txt file.
  //@param  lev  The leve of the message. Higher levels are more detailed
  //      messages. A level of 0 always outputs.
  //@param  mes  The message to put out.
  public static void doDebug(int lev, String mes)
    {
    if (m_debugLevel < lev)
      return ;

    // TODO: write to a _logFile...!
    System.out.println(mes);
    return ;
    }

  //---------------------------------------------------------------------
  public static File GetConfigFile()
    {
    File f = new File(m_root, "config.ini");
    if (f.exists() == false)
      {
      doDebug(0, "No config.ini file in " + m_root);
      return null;
      }

    return f;
    }


  //---------------------------------------------------------------------
  public static boolean RootIsValid()
    {
    File r = new File(m_root);
    if (r.isDirectory() == false)
      {
      doDebug(0, "The root directory does not exist. [" + m_root + "]");
      return false;
      }
    return true;
    }

  //---------------------------------------------------------------------
  public static void crlf(OutputStream f) throws IOException
    {
    f.write(m_crlf, 0, 2);
    }

  }

editsave.java

Synopsis
package jwiki;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.File;

//-----------------------------------------------------------------------
//-- Does the edit save and cancel function of JWiki
public class editsave implements WikiPostable
  {
  private JHandler m_h;
  private HTMLspew m_html;
  private FileOutputStream m_fos;
  private KeyValueTable m_vars;
  private String m_fileName;

  //-----------------------------------------------------------------------
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    m_h = _h;
    m_html = m_h.getHTML();
    m_vars = m_h.parsePOST(buf, bufSize);

    if (isBadFileName())
      return ;

    Globals.editLocks.release(m_fileName);

    if (isACancel())
      return ;

    if (isFailedOpen(fileOpen()))
      return ;

    writeHeader();
    writeContents();

    if (isFailedClose(fileClose()))
      return ;

    isSuccess();
    }

  //-----------------------------------------------------------------------
  //-- private from here on
  //-----------------------------------------------------------------------

  //-----------------------------------------------------------------------
  private boolean isBadFileName() throws IOException
    {
    m_fileName = m_vars.value("file");
    if (m_fileName == null)
      {
      m_html.transmitStringAsPage("No file name specified in the POST.<BR> The INPUT HTML must be wrong.");
      return true;
      }

    return false;
    }

  //-----------------------------------------------------------------------
  private boolean isACancel() throws IOException
    {
    if (!m_vars.isValueEqual("Submit", "Cancel"))
      return false;

    m_h.writeInLog("editsave: edit of " + m_fileName + " cancelled");
    m_html.transmitStringAsPage("Edit of <B>" + m_fileName + "</B> canceled.\n\n" +
                                "Use the <B>Back</B> button on your browser to\n" +
                                "get back to the page you just edited.\n" +
                                "Then press the <B>Reload</B> button to see the new page.");
    return true;
    }

  //-----------------------------------------------------------------------
  private boolean isFailedOpen(String err) throws IOException
    {
    if (err == null)
      return false;

    m_h.writeInLog("edit: file open, read exception: " + err);
    m_html.transmitStringAsPage("Can not open file <B>" + m_fileName + "</B> to save: " + err);
    return true;
    }

  //-----------------------------------------------------------------------
  private String fileOpen()
    {
    try
      {
      m_fos = new FileOutputStream(Globals.GetPageDir() + File.separator + m_fileName);
      }
    catch (Exception oe)
      {
      oe.printStackTrace();
      return oe.getMessage();
      }

    return null;
    }

  //-----------------------------------------------------------------------
  private String fileClose()
    {
    try
      {
      m_fos.close();
      }
    catch (Exception ec)
      {
      ec.printStackTrace();
      return ec.getMessage();
      }
    return null;
    }

  //-----------------------------------------------------------------------
  private boolean isFailedClose(String err) throws IOException
    {
    if (err == null)
      return false;

    m_h.writeInLog("editsave: file close exception: " + err);
    m_html.transmitStringAsPage("File <B>" + m_fileName + "</B> saved.\n\n" +
                                "However there was an exception on the file close:\n" +
                                err + "\n\n" +
                                "Check the file content for any errors.\n" +
                                "Use the <B>Back</B> button on your browser to\n" +
                                "get back to the page you just edited.\n" +
                                "Then press the <B>Reload</B> button to see the new page.");
    return true;
    }

  //-----------------------------------------------------------------------
  private void writeHeader() throws Exception
    {
    if (m_vars.isFound("html"))
      m_fos.write(("html:true\n").getBytes());

    if (m_vars.isFound("wiki"))
      m_fos.write(("wiki:true\n").getBytes());

    if (m_vars.isFound("preamble"))
      m_fos.write(("preamble:true\n").getBytes());

    if (m_vars.isFound("postamble"))
      m_fos.write(("postamble:true\n").getBytes());

    if (m_vars.isFound("editbutton"))
      m_fos.write(("editbutton:true\n").getBytes());

    m_fos.write(("read:" + m_vars.value("readPerm") + "\n").getBytes());
    m_fos.write(("write:" + m_vars.value("writePerm") + "\n").getBytes());
    Globals.crlf(m_fos);
    }

  //-----------------------------------------------------------------------
  private void writeContents() throws Exception
    {
    m_fos.write((m_vars.value("txt")).getBytes());
    }

  //-----------------------------------------------------------------------
  private void isSuccess() throws IOException
    {
    m_h.writeInLog("editsave: edit of " + m_fileName + " saved");
    m_html.transmitStringAsPage("File <B>" + m_fileName + "</B> saved.\n\n" +
                                "Use the <B>Back</B> button on your browser to\n" +
                                "get back to the page you just edited.\n" +
                                "Then press the <B>Reload</B> button to see the new page.");
    }
  }

EditLocks.java

Synopsis
package jwiki;

import java.util.Date;
import java.util.Enumeration;

//-------------------------------------------------------------------------
public class EditLocks
  {
  private KeyValueTable m_editLocks;
  private long m_editTimeout;

  //-------------------------------------------------------------------------
  public EditLocks()
    {
    m_editLocks = new KeyValueTable();
    m_editTimeout = 5;
    }

  //-------------------------------------------------------------------------
  public void setTimeout(long timeout)
    {
    Globals.doDebug(3, "EditLocks::setTimeout: " + timeout);
    m_editTimeout = timeout;
    }

  //-------------------------------------------------------------------------
  public synchronized boolean get(String fileName)
    {
    boolean notthere = find(fileName, new equalsLockFunctor()) == null;

    if (notthere)
      m_editLocks.put(fileName, new Date());

    Globals.doDebug(2, "Get edit lock on " + fileName + ": " + (notthere ? "succeeded" : "failed, already locked"));
    return notthere;
    }

  //-------------------------------------------------------------------------
  public synchronized void release(String fileName)
    {
    boolean isthere = find(fileName, new equalsLockFunctor()) != null;
    if (isthere)
      m_editLocks.remove(fileName);

    Globals.doDebug(2, "Release edit lock on " + fileName + "." + (isthere ? "" : " (Was not locked)"));
    }

  //-------------------------------------------------------------------------
  public synchronized void releaseOld()
    {
    apply(new releaseOldDatesFunctor());
    }

  //-------------------------------------------------------------------------
  //-- private from here on
  //-------------------------------------------------------------------------

  //-------------------------------------------------------------------------
  private interface FindFunctor
    {
    boolean compare(String fn1, String fn2);
    }

  //-------------------------------------------------------------------------
  private class equalsLockFunctor implements FindFunctor
    {
    public boolean compare(String fn1, String fn2)
      {
      return fn1.equals(fn2);
      }
    }

  //-------------------------------------------------------------------------
  private String find(String fileName, FindFunctor cmp)
    {
    for (Enumeration e = m_editLocks.keys(); e.hasMoreElements(); )
      {
      String fn = (String) e.nextElement();
      if (cmp.compare(fn, fileName))
        return fn;
      }
    return null;
    }

  //-------------------------------------------------------------------------
  private interface ApplyFunctor
    {
    void doit(String fn);
    }

  //-------------------------------------------------------------------------
  private void apply(ApplyFunctor apf)
    {
    for (Enumeration e = m_editLocks.keys(); e.hasMoreElements(); )
      {
      String fn = (String) e.nextElement();
      apf.doit(fn);
      }
    }

  //-------------------------------------------------------------------------
  private class releaseOldDatesFunctor implements ApplyFunctor
    {
    long timeInThePast = (new Date()).getTime() - 1000 * 60 * m_editTimeout;
    public void doit(String fn)
      {
      Date dt = (Date) m_editLocks.get(fn);

      if (dt.getTime() > timeInThePast)
        return ;

      m_editLocks.remove(fn);
      Globals.doDebug(2, "Removed edit lock on " + fn + ".  Timedout.");
      }
    }
  }

edit.java

Synopsis
package jwiki;

import java.io.IOException;

public class edit implements WikiPostable
  {
  private HTMLspew _html;
  private KeyValueTable m_vars;

  //---------------------------------------------------------------------
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    _html = _h.getHTML();
    m_vars = _h.parsePOST(buf, bufSize);

    String fileName = getFileName(_h);
    if (fileName == null)
      return ;

    HandleEditingCmd hec;
    if (isEdit())
      hec = new HandleEditCmd(_html);
    else
      hec = new HandleCreatePageCmd(_html);

    if (hec.cannotLock(fileName))
      return ;

    if (!hec.InitializeFile(_h, fileName))
      return ;

    hec.sendPage();
    }

  //---------------------------------------------------------------------
  //-- private from here on
  //---------------------------------------------------------------------

  //---------------------------------------------------------------------
  private String getFileName(JHandler _h) throws IOException
    {
    String fileName = m_vars.value("file");
    if (fileName == null)
      _html.transmitStringAsPage("No file name specified in the POST.<BR> The 'INPUT' HTML must be wrong.");
    else
      _h.writeInLog("edit: " + fileName);
    return fileName;
    }

  //---------------------------------------------------------------------
  private boolean isEdit()
    {
    return m_vars.isValueEqual("Submit", "Edit");
    }
  }

Constants.java

Synopsis
package jwiki;

import java.io.File;

public class Constants
  {
  public final static int CR = 0x0D;
  public final static int LF = 0x0A;
  public final static int MaxPostSize = (1024 * 128) + 100;

  //psuedo constants. They are not static but
  //they do not change for the all communications
  public static File preambleFile;
  public static File postambleFile;
  }

Config.java

Synopsis
package jwiki;

import java.io.File;

//-----------------------------------------------------------------------------------
public class Config extends IniFile
  {
  private int m_port;

  //------------------------------------------------------------------------------
  public int getPort()
    {
    return m_port;
    }

  //------------------------------------------------------------------------------
  public void Save(String name, String val)
    {
    name = name.toUpperCase();
    name = name.trim();
    val = val.trim();

    if (name.equals("PORT"))
      m_port = Integer.parseInt(val);
    else if (name.equals("DEBUG"))
      Globals.setDebugLevel(Integer.parseInt(val));
    else if (name.equals("EDITTIMEOUT"))
      Globals.editLocks.setTimeout((long) Integer.parseInt(val));
    else if (name.equals("SECURE"))
      Globals.passwords.setSecurity(val.equalsIgnoreCase("false"));
    else
      Globals.doDebug(0, "Unknown name " + name + " in config.ini");
    }

  //-----------------------------------------------------------------------------------
  public void read()
    {
    Globals.doDebug(2, "Config::read()");

    File f = Globals.GetConfigFile();
    if (f == null)
      return ;

    read(f.getName());
    }


  //-----------------------------------------------------------------------------------
  //-- private from here on
  //-----------------------------------------------------------------------------------

  }

Comparator.java

Synopsis
package jwiki;

import java.io.File;

//functor used by Globals.getFiles()
public interface Comparator
  {
  String compare(File fname);
  }

allpages.java

Synopsis
package jwiki;

import java.io.File;
import java.util.TreeSet;

// shows a list of all files changed within x days
public class allpages implements WikiPostable
  {
  // The most hits that are ever returned.
  private final static int maxFiles = 200;

  //-----------------------------------------------------------------------------------
  public void handlePOST(JHandler _h, byte buf[], int bufSize) throws Exception
    {
    TreeSet fnames = Globals.pageDir.getFiles(_h, new AllPagesComparator());

    HTMLspew _html = _h.getHTML();
    _html.transmitFileListPage(fnames, maxFiles, "<br>There are " + fnames.size() + " files available. ");
    }

  //-------------------------------------
  private class AllPagesComparator implements Comparator
    {
    public String compare(File fname)
      {
      return fname.getName();
      }
    }
  }






Contact me about content on this page using john_web-at-arrizza-dot-com
For Web Master or site problems contact: webadmin-at-arrizza-dot-com
Copyright John Arrizza (c) 2001-2010