text: HOST = null; // localhost
  PORT = 8001;
  
  // when the daemon started
  var starttime = (new Date()).getTime();
  
  var mem = process.memoryUsage();
  // every 10 seconds poll for the memory.
  setInterval(function () {
    mem = process.memoryUsage();
  }, 10*1000);
  
  
  var mcrnmz = require("./mcrnmz"),
      sys = require("sys"),
      url = require("url"),
      qs = require("querystring");
  
  var MESSAGE_BACKLOG = 200,
      SESSION_TIMEOUT = 60 * 1000;
  
  var channel = new function () {
    var messages = [],
        callbacks = [];
  
    this.appendMessage = function (nick, type, text) {
      var m = { nick: nick
              , type: type // "msg", "join", "part"
              , text: text
              , timestamp: (new Date()).getTime()
              };
      var timestamp = new Date();
      switch (type) {
        case "msg":
          sys.puts(timestamp + "<" + nick + "> " + text);
          break;
        case "join":
          sys.puts(timestamp + "<" + nick + ">" + " JOIN");
          break;
        case "part":
          sys.puts(timestamp + "<" + nick + ">" + " PART");
          break;
      }
  
      messages.push( m );
  
      while (callbacks.length > 0) {
        callbacks.shift().callback([m]);
      }
  
      while (messages.length > MESSAGE_BACKLOG)
        messages.shift();
    };
  
    this.query = function (since, callback) {
      var matching = [];
      for (var i = 0; i < messages.length; i++) {
        var message = messages[i];
        if (message.timestamp > since)
          matching.push(message)
      }
  
      if (matching.length != 0) {
        callback(matching);
      } else {
        callbacks.push({ timestamp: new Date(), callback: callback });
      }
    };
  
    // clear old callbacks
    // they can hang around for at most 30 seconds.
    setInterval(function () {
      var now = new Date();
      while (callbacks.length > 0 && now - callbacks[0].timestamp > 30*1000) {
        callbacks.shift().callback([]);
      }
    }, 3000);
  };
  
  var sessions = {};
  
  function createSession (nick) {
    if (nick.length > 50) return null;
    if (/[^\w_\-^!]/.exec(nick)) return null;
  
    for (var i in sessions) {
      var session = sessions[i];
      if (session && session.nick === nick) return null;
    }
  
    var session = { 
      nick: nick, 
      id: Math.floor(Math.random()*99999999999).toString(),
      timestamp: new Date(),
  
      poke: function () {
        session.timestamp = new Date();
      },
  
      destroy: function () {
        channel.appendMessage(session.nick, "part");
        delete sessions[session.id];
      }
    };
  
    sessions[session.id] = session;
    return session;
  }
  
  // interval to kill off old sessions
  setInterval(function () {
    var now = new Date();
    for (var id in sessions) {
      if (!sessions.hasOwnProperty(id)) continue;
      var session = sessions[id];
  
      if (now - session.timestamp > SESSION_TIMEOUT) {
        session.destroy();
      }
    }
  }, 1000);
  
  mcrnmz.listen(Number(process.env.PORT || PORT), HOST);
  
  mcrnmz.get("/", mcrnmz.staticHandler("index.html"));
  mcrnmz.get("/style.css", mcrnmz.staticHandler("style.css"));
  mcrnmz.get("/client.js", mcrnmz.staticHandler("client.js"));
  mcrnmz.get("/jquery-1.2.6.min.js", mcrnmz.staticHandler("jquery-1.2.6.min.js"));
  
  mcrnmz.get("/who", function (req, res) {
    var nicks = [];
    for (var id in sessions) {
      if (!sessions.hasOwnProperty(id)) continue;
      var session = sessions[id];
      nicks.push(session.nick);
    }
    res.simpleJSON(200, { nicks: nicks
                        , rss: mem.rss
                        });
  });
  
  mcrnmz.get("/join", function (req, res) {
    var nick = qs.parse(url.parse(req.url).query).nick;
    if (nick == null || nick.length == 0) {
      res.simpleJSON(400, {error: "bad nick, bad!"});
      return;
    }
    var session = createSession(nick);
    if (session == null) {
      res.simpleJSON(400, {error: "somebody already has that nickname!"});
      return;
    }
  
    //sys.puts("connection: " + nick + "@" + res.connection.remoteAddress);
  
    channel.appendMessage(session.nick, "join");
    res.simpleJSON(200, { id: session.id
                        , nick: session.nick
                        , rss: mem.rss
                        , starttime: starttime
                        });
  });
  
  mcrnmz.get("/part", function (req, res) {
    var id = qs.parse(url.parse(req.url).query).id;
    var session;
    if (id && sessions[id]) {
      session = sessions[id];
      session.destroy();
    }
    res.simpleJSON(200, { rss: mem.rss });
  });
  
  mcrnmz.get("/recv", function (req, res) {
    if (!qs.parse(url.parse(req.url).query).since) {
      res.simpleJSON(400, { error: "need a param!" });
      return;
    }
    var id = qs.parse(url.parse(req.url).query).id;
    var session;
    if (id && sessions[id]) {
      session = sessions[id];
      session.poke();
    }
  
    var since = parseInt(qs.parse(url.parse(req.url).query).since, 10);
  
    channel.query(since, function (messages) {
      if (session) session.poke();
      res.simpleJSON(200, { messages: messages, rss: mem.rss });
    });
  });
  
  mcrnmz.get("/send", function (req, res) {
    var id = qs.parse(url.parse(req.url).query).id;
    var text = qs.parse(url.parse(req.url).query).text;
  
    var session = sessions[id];
    if (!session || !text) {
      res.simpleJSON(400, { error: "dunno!" });
      return;
    }
  
    session.poke();
  
    channel.appendMessage(session.nick, "msg", text);
    res.simpleJSON(200, { rss: mem.rss });
  });
  