colloquy-1.35.30/
colloquy-1.35.30/data/lang/
colloquy-1.35.30/data/misc/
colloquy-1.35.30/docs/
main = 1;
punctuation = [[!;':@?,`.]]

_oldError = _ERRORMESSAGE;
lastInput = "<none>";
lastUser = "<none>";

ErrorTime = getSecs();
Errors = 0;

function HandleError(s)

   if (secs > (ErrorTime + 5)) then
     ErrorTime = secs;
     Errors = 0;
   end;

   Errors = Errors + 1;

   if (Errors > 15) then
     print("ERK!  Too many errors, giving up and quitting.");
     exit(1);
   end;

   local r, i;
   r = s .. "\ncaused by <";
   r = r .. lastInput;
   r = r .. "> by ";
   r = r .. lastUser;
   r = r .. ". stack backtrace:\n";

   local f;

   if not colloquy.noFork then
     f = writeto('|mail -s "[colloquy] stack backtrace" ' .. colloquy.email);
   else
     f = _STDERR;
   end

     print(strsub(r, 1, -2));
   if (f) then
     write(f, r);
   end;

   for i = 2, 1000 do
      local a = getinfo(i)
      if a==nil then break end
      r = i - 1 .. ":"
      if (a.name) then r =r .. " function '" .. a.name .. "'"; end;
      if (a.currentline > -1) then
        r = r .. " at line " .. a.currentline;
      end;
      r = r .. " [" .. a.short_src .. "]\n";
      print(strsub(r, 1, -2));
      if (f) then
        write(f, r);
      end;
   end
   write("\n");
   if (f) then
     closefile(f);
   end;
   print("");
   sendToAll("WARNING: Caught an error that shouldn't have happened.  Blame " .. lastUser .. ".", S_ALERT);
end

function db(v)
  print(v);
end;

function handleWrites(sockets)
   local i, v, err;

   for i, v in sockets do
      if (v == colloquy.resolver.socket) then
        colloquy.resolver:readySend();
      elseif (colloquy.connections[v].socket:readySend() ~= nil and not colloquy.connections[v].socket.toClose) then
        disconnectUser(v, "- Connection closed.");
      end;

      local conn = colloquy.connections[v];

      if (v ~= colloquy.resolver.socket and conn.socket.toClose == 2) then
        conn.socket.socket:close();
        colloquy.connections[v] = nil;
      end;

   end;
end;

function printString(a)
  local i, s;

  for i=1,strlen(a) do
    write(strbyte(a, i) .. " ");
  end
  write("\n");

  for i=1,strlen(a) do
    if (strbyte(a, i) > 31 and strbyte(a, i) < 100) then
      write(strsub(a, i, i) .. "  ");
    elseif (strbyte(a, i) > 100 and strbyte(a, i) < 127) then
      write(strsub(a, i, i) .. "   ");
    else
      write("   ");
    end;
  end;
  write("\n\n");

end;

function handleReads(sockets)
   local i, v, err, string;

   for i, v in sockets do
      if (v == colloquy.server) then
         connectUser();
      elseif (v == colloquy.botServer) then
         connectUser(1);
      elseif (v == colloquy.metaServer) then
         connectUser(2);
      elseif (v == colloquy.resolver.socket) then
         string = colloquy.resolver:readyRead("\n");
         if (string == nil) then
            log("Connection to resolver lost.");
            return nil;
         elseif (string ~= "") then
            resolverResult(string);
         end;
      else
        if (colloquy.connections[v] == nil or colloquy.connections[v].socket == nil) then
          return nil;
        end;
       
       local conn = colloquy.connections[v];

       local opts = conn.socket:readyPeek("\255");
       if (opts ~= nil and opts ~= "") then
         opts = conn.socket:readyRead("\255");
         string = parseTelnetOpts(opts, conn);
       end;

       if (string == nil) then
         string = conn.socket:readyRead("[\n\r]");
       else
         string = string .. conn.socket:readyRead("[\n\r]");
       end;

       if (string == nil) then
         disconnectUser(v, "- Connection closed.");
         conn.socket:close();
         colloquy.connections[v] = nil;
         return nil;
       elseif (string ~= "") then
         -- wahey!  We've a line of text.  Let us bounce.
         -- remove delete characters first...
   
         while (strfind(string, "\127")) do
           string = gsub(string, "^\127", "");
           string = gsub(string, "[^\127]\127", "");
         end;

         while (strfind(string, "\8")) do
           string = gsub(string, "^\8", "");
           string = gsub(string, "[^\8]\8", "");
         end;

         lastUser = conn.username;
         lastConnection = conn;
      
         -- work though thr string, getting each chunk ended by \r\n, and pass it to parseInput...
         local tmp = gsub(string, "\r\n", "\n");
         tmp = gsub(string, "\n\r", "\n");
         if (not strfind(tmp, "\n", 1, 1)) then
           tmp = tmp .. "\n";
         end;
          
         local l;
         repeat
           l = strsub(tmp, 1, strfind(tmp, "\n", 1, 1) - 1);
           call(parseInput, {v, l}, "x", HandleError);
           tmp = strsub(tmp, strfind(tmp, "\n", 1, 1) + 1, -1);
         until not strfind(tmp, "\n", 1, 1) 
       end;
    end;
 end;
end;

function log(...)
   tinsert(arg, 1, date() .. ": ");
   tinsert(arg, 1, colloquy.logfile);
   tinsert(arg, arg.n + 1, "\n");
   call(write, arg); 
   flush(colloquy.logfile);
end;

lastLogRotation = date("%Y%m%d");

function expireLists()
  local toDie = {}
  for i, v in lists do
    if ( not v.used ) then
      -- this has never been used!  give it the benefit of the doubt, and set it to now.
      v.used = secs;
    end;

    if ( (secs - v.used) > (60*60*24*(colloquy.listExpirey)) and not strfind(v.flags, "P", 1, 1) ) then
      -- Awwww!  I bet it feels unwanted.  Let's kill it, and put it out of it's misery.
      tinsert(toDie, i);
    end;
  end;
  for i, v in toDie do
    if (type(v) == "string") then
      lists[v] = nil;
    end;
  end;
end;

function doHousekeeping()
   
   local i, v;
   local toDelete = {}
   
   for i, v in colloquy.connections do
     if (v.status == 0) then
       -- they've not yet logged on...
       if ((secs - v.idle) > 30) then
         send("Your logon has timed out.", v, S_DISCONNECT);
         disconnectUser(i, "");
       end;
     end;

     if (colloquy.guestTimeout and v.status == 1) then
       -- timeout guests...
       if ((secs - v.conTime) > colloquy.guestTimeout) then
         sendGM(v, S_DISCONNECT, "gGuestTimeout");
         disconnectUser(i, "- Guest for too long.");
       elseif (not v.warned and (secs - v.conTime) > (colloquy.guestTimeout - 60)) then
         sendGM(v, S_WARN, "gGuestTimeout1")
         v.warned = 1;
       end
     end
 
     if ((secs - v.idle) > colloquy.maxIdle * 60) then
       if (colloquy.kickIdle) then
         if ((v.privs and strfind(v.privs, "Z", 1, 1)) or (v.restrict and strfind(v.restrict, "B", 1, 1))) then
           v.idle = secs;
         else
           sendGM(v, S_DISCONNECT, "gIdledOut");
           disconnectUser(i, "- Idled out");
         end;
       elseif (not v.veryIdle and not (v.restrict and strfind(v.restrict, "B", 1, 1))) then
         sendGMAll(S_IDLE, "gAutoIdle", v.username);
         v.idleReason = "Became automatically idle: " .. date("%a %b %e %H:%M:%S %Y");
         v.veryIdle = colloquy.maxIdle * 60 ;
       end;
     end;

     if (v.timeWarn ~= 0 and v.timeWarn > 0 and (secs > v.timeTick)) then
       v.timeTick = secs + (v.timeWarn * 60);
       commandMark(v.socket.socket, ".-");
    end;
 
    if (v.group == "" and v.c) then
      -- an empty connection - must fix this, this shouldn't happen.
      tinsert(toDelete, {v.socket.socket, i});
    end;
   end;

   if (getn(toDelete) > 0) then
     for i, v in toDelete do
       if (i ~= "n") then
         close(v[1]);
         colloquy.connections[v[2]] = nil;
       end
     end
     buildSocketReaders();
   end 

   if (colloquy.logRotate and date("%H%M") == "0000" and lastLogRotation ~= date("%Y%m%d")) then
     log("Rotating log file");
     lastLogRotation = date("%Y%m%d");
     closefile(colloquy.logfile);
     execute(date(colloquy.logRotate));
     remove(colloquy.logName)
     colloquy.logfile = openfile(colloquy.logName, "a")
     log("Rotated log file");
   
     if (not colloquy.kickIdle) then
       -- save all the users to disc.
       local i, v;
       for i, v in colloquy.connections do
         if (type(v) == "table") then
           saveUser(i);
         end;
       end;
       saveUsers(colloquy.users);
       log("Saved connected users to disc");
       
       local oldUsage = gcinfo();
       collectgarbage();
       log("Collected " .. tostring(oldUsage - gcinfo()) .. "kB of garbage");
     end;
     -- now expire lists that havn't been used in 28 days...
     expireLists();
   end;
   
   if date("%H%M") == "0000" and lastNewDate ~= date("%Y%m%d") then
     -- now tell everybody about the date change
     sendToAll(format("Date changed to %s.", date("%A %b %d %Y")), S_DONE);
     lastNewDate = date("%Y%m%d");
   end;

end;

function selectery()
   local sockets, readyRead, readySend, error;
   getSecs(1);
   if not colloquy.readingSockets then buildSocketReaders() end;
   readyRead, readyWrite, error = select(colloquy.readingSockets, clientSocket.writers, 2);

   secs = getSecs();

   if (error ~= "timeout") then
      handleWrites(readyWrite);
      handleReads(readyRead);
   end;

   if (secs - lastHousekeep >= 2) then
      doHousekeeping();
      lastHousekeep = secs;
   end;
end;

function empty(t)
  local i, v, k;
  k = 0;
  for i, v in t do
    k = k + 1;
  end;
  return k == 0;
end;