Merge 0.9->0.10
authorKim Alvefur <zash@zash.se>
Tue, 19 Apr 2016 15:30:30 +0000 (17:30 +0200)
committerKim Alvefur <zash@zash.se>
Tue, 19 Apr 2016 15:30:30 +0000 (17:30 +0200)
1  2 
net/server_event.lua
plugins/muc/muc.lib.lua

index 2edc9a0c30c5c6dbecb980f436f43ad2919172bf,1c6f154789d88b08011543cbb9c99bbdf96b2cc9..a67d5afb83dd22371fd6fb95939a426c956bb30a
  
  -- End of client interface methods
  
 -local handleclient;
 -do
 -      local string_sub = string.sub  -- caching table lookups
 -      local addevent = base.addevent
 -      local socket_gettime = socket.gettime
 -      function handleclient( client, ip, port, server, pattern, listener, sslctx )  -- creates an client interface
 -              --vdebug("creating client interfacce...")
 -              local interface = {
 -                      type = "client";
 -                      conn = client;
 -                      currenttime = socket_gettime( );  -- safe the origin
 -                      writebuffer = {};  -- writebuffer
 -                      writebufferlen = 0;  -- length of writebuffer
 -                      send = client.send;  -- caching table lookups
 -                      receive = client.receive;
 -                      onconnect = listener.onconnect;  -- will be called when client disconnects
 -                      ondisconnect = listener.ondisconnect;  -- will be called when client disconnects
 -                      onincoming = listener.onincoming;  -- will be called when client sends data
 -                      ontimeout = listener.ontimeout; -- called when fatal socket timeout occurs
 -                      ondrain = listener.ondrain; -- called when writebuffer is empty
 -                      ondetach = listener.ondetach; -- called when disassociating this listener from this connection
 -                      onstatus = listener.onstatus; -- called for status changes (e.g. of SSL/TLS)
 -                      eventread = false, eventwrite = false, eventclose = false,
 -                      eventhandshake = false, eventstarthandshake = false;  -- event handler
 -                      eventconnect = false, eventsession = false;  -- more event handler...
 -                      eventwritetimeout = false;  -- even more event handler...
 -                      eventreadtimeout = false;
 -                      fatalerror = false;  -- error message
 -                      writecallback = false;  -- will be called on write events
 -                      readcallback = false;  -- will be called on read events
 -                      nointerface = true;  -- lock/unlock parameter of this interface
 -                      noreading = false, nowriting = false;  -- locks of the read/writecallback
 -                      startsslcallback = false;  -- starting handshake callback
 -                      position = false;  -- position of client in interfacelist
 -                      
 -                      -- Properties
 -                      _ip = ip, _port = port, _server = server, _pattern = pattern,
 -                      _serverport = (server and server:port() or nil),
 -                      _sslctx = sslctx; -- parameters
 -                      _usingssl = false;  -- client is using ssl;
 -              }
 -              if not ssl then interface.starttls = false; end
 -              interface.id = tostring(interface):match("%x+$");
 -              interface.writecallback = function( event )  -- called on write events
 -                      --vdebug( "new client write event, id/ip/port:", interface, ip, port )
 -                      if interface.nowriting or ( interface.fatalerror and ( "client to close" ~= interface.fatalerror ) ) then  -- leave this event
 -                              --vdebug( "leaving this event because:", interface.nowriting or interface.fatalerror )
 -                              interface.eventwrite = false
 -                              return -1
 -                      end
 -                      if EV_TIMEOUT == event then  -- took too long to write some data to socket -> disconnect
 -                              interface.fatalerror = "timeout during writing"
 -                              debug( "writing failed:", interface.fatalerror )
 -                              interface:_close()
 -                              interface.eventwrite = false
 -                              return -1
 -                      else  -- can write :)
 -                              if interface._usingssl then  -- handle luasec
 -                                      if interface.eventreadtimeout then  -- we have to read first
 -                                              local ret = interface.readcallback( )  -- call readcallback
 -                                              --vdebug( "tried to read in writecallback, result:", ret )
 -                                      end
 -                                      if interface.eventwritetimeout then  -- luasec only
 -                                              interface.eventwritetimeout:close( )  -- first we have to close timeout event which where regged after a wantread error
 -                                              interface.eventwritetimeout = false
 -                                      end
 +local function handleclient( client, ip, port, server, pattern, listener, sslctx )  -- creates an client interface
 +      --vdebug("creating client interfacce...")
 +      local interface = {
 +              type = "client";
 +              conn = client;
 +              currenttime = socket_gettime( );  -- safe the origin
 +              writebuffer = {};  -- writebuffer
 +              writebufferlen = 0;  -- length of writebuffer
 +              send = client.send;  -- caching table lookups
 +              receive = client.receive;
 +              onconnect = listener.onconnect;  -- will be called when client disconnects
 +              ondisconnect = listener.ondisconnect;  -- will be called when client disconnects
 +              onincoming = listener.onincoming;  -- will be called when client sends data
 +              ontimeout = listener.ontimeout; -- called when fatal socket timeout occurs
 +              onreadtimeout = listener.onreadtimeout; -- called when socket inactivity timeout occurs
 +              ondrain = listener.ondrain; -- called when writebuffer is empty
 +              ondetach = listener.ondetach; -- called when disassociating this listener from this connection
 +              onstatus = listener.onstatus; -- called for status changes (e.g. of SSL/TLS)
 +              eventread = false, eventwrite = false, eventclose = false,
 +              eventhandshake = false, eventstarthandshake = false;  -- event handler
 +              eventconnect = false, eventsession = false;  -- more event handler...
 +              eventwritetimeout = false;  -- even more event handler...
 +              eventreadtimeout = false;
 +              fatalerror = false;  -- error message
 +              writecallback = false;  -- will be called on write events
 +              readcallback = false;  -- will be called on read events
 +              nointerface = true;  -- lock/unlock parameter of this interface
 +              noreading = false, nowriting = false;  -- locks of the read/writecallback
 +              startsslcallback = false;  -- starting handshake callback
 +              position = false;  -- position of client in interfacelist
 +
 +              -- Properties
 +              _ip = ip, _port = port, _server = server, _pattern = pattern,
 +              _serverport = (server and server:port() or nil),
 +              _sslctx = sslctx; -- parameters
 +              _usingssl = false;  -- client is using ssl;
 +      }
 +      if not has_luasec then interface.starttls = false; end
 +      interface.id = tostring(interface):match("%x+$");
 +      interface.writecallback = function( event )  -- called on write events
 +              --vdebug( "new client write event, id/ip/port:", interface, ip, port )
 +              if interface.nowriting or ( interface.fatalerror and ( "client to close" ~= interface.fatalerror ) ) then  -- leave this event
 +                      --vdebug( "leaving this event because:", interface.nowriting or interface.fatalerror )
 +                      interface.eventwrite = false
 +                      return -1
 +              end
 +              if EV_TIMEOUT == event then  -- took too long to write some data to socket -> disconnect
 +                      interface.fatalerror = "timeout during writing"
 +                      debug( "writing failed:", interface.fatalerror )
 +                      interface:_close()
 +                      interface.eventwrite = false
 +                      return -1
 +              else  -- can write :)
 +                      if interface._usingssl then  -- handle luasec
 +                              if interface.eventreadtimeout then  -- we have to read first
 +                                      local ret = interface.readcallback( )  -- call readcallback
 +                                      --vdebug( "tried to read in writecallback, result:", ret )
                                end
 -                              interface.writebuffer = { t_concat(interface.writebuffer) }
 -                              local succ, err, byte = interface.conn:send( interface.writebuffer[1], 1, interface.writebufferlen )
 -                              --vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte )
 -                              if succ then  -- writing succesful
 -                                      interface.writebuffer[1] = nil
 -                                      interface.writebufferlen = 0
 -                                      interface:ondrain();
 -                                      if interface.fatalerror then
 -                                              debug "closing client after writing"
 -                                              interface:_close()  -- close interface if needed
 -                                      elseif interface.startsslcallback then  -- start ssl connection if needed
 -                                              debug "starting ssl handshake after writing"
 -                                              interface.eventstarthandshake = addevent( base, nil, EV_TIMEOUT, interface.startsslcallback, 0 )
 -                                      elseif interface.eventreadtimeout then
 -                                              return EV_WRITE, EV_TIMEOUT
 -                                      end
 -                                      if interface.writebuffer ~= 0 then
 -                                              -- data possibly written from ondrain
 -                                              return EV_WRITE, cfg.WRITE_TIMEOUT
 -                                      end
 -                                      interface.eventwrite = nil
 -                                      return -1
 -                              elseif byte and (err == "timeout" or err == "wantwrite") then  -- want write again
 -                                      --vdebug( "writebuffer is not empty:", err )
 -                                      interface.writebuffer[1] = string_sub( interface.writebuffer[1], byte + 1, interface.writebufferlen )  -- new buffer
 -                                      interface.writebufferlen = interface.writebufferlen - byte
 -                                      if "wantread" == err then  -- happens only with luasec
 -                                              local callback = function( )
 -                                                      interface:_close()
 -                                                      interface.eventwritetimeout = nil
 -                                                      return -1;
 -                                              end
 -                                              interface.eventwritetimeout = addevent( base, nil, EV_TIMEOUT, callback, cfg.WRITE_TIMEOUT )  -- reg a new timeout event
 -                                              debug( "wantread during write attempt, reg it in readcallback but dont know what really happens next..." )
 -                                              -- hopefully this works with luasec; its simply not possible to use 2 different write events on a socket in luaevent
 -                                              return -1
 -                                      end
 -                                      return EV_WRITE, cfg.WRITE_TIMEOUT
 -                              else  -- connection was closed during writing or fatal error
 -                                      interface.fatalerror = err or "fatal error"
 -                                      debug( "connection failed in write event:", interface.fatalerror )
 -                                      interface:_close()
 -                                      interface.eventwrite = nil
 -                                      return -1
 +                              if interface.eventwritetimeout then  -- luasec only
 +                                      interface.eventwritetimeout:close( )  -- first we have to close timeout event which where regged after a wantread error
 +                                      interface.eventwritetimeout = false
                                end
                        end
 -              end
 -              
 -              interface.readcallback = function( event )  -- called on read events
 -                      --vdebug( "new client read event, id/ip/port:", tostring(interface.id), tostring(ip), tostring(port) )
 -                      if interface.noreading or interface.fatalerror then  -- leave this event
 -                              --vdebug( "leaving this event because:", tostring(interface.noreading or interface.fatalerror) )
 -                              interface.eventread = nil
 -                              return -1
 -                      end
 -                      if EV_TIMEOUT == event then  -- took too long to get some data from client -> disconnect
 -                              interface.fatalerror = "timeout during receiving"
 -                              debug( "connection failed:", interface.fatalerror )
 -                              interface:_close()
 -                              interface.eventread = nil
 -                              return -1
 -                      else -- can read
 -                              if interface._usingssl then  -- handle luasec
 -                                      if interface.eventwritetimeout then  -- ok, in the past writecallback was regged
 -                                              local ret = interface.writecallback( )  -- call it
 -                                              --vdebug( "tried to write in readcallback, result:", tostring(ret) )
 -                                      end
 -                                      if interface.eventreadtimeout then
 -                                              interface.eventreadtimeout:close( )
 -                                              interface.eventreadtimeout = nil
 -                                      end
 +                      interface.writebuffer = { t_concat(interface.writebuffer) }
 +                      local succ, err, byte = interface.conn:send( interface.writebuffer[1], 1, interface.writebufferlen )
 +                      --vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte )
 +                      if succ then  -- writing succesful
 +                              interface.writebuffer[1] = nil
 +                              interface.writebufferlen = 0
 +                              interface:ondrain();
 +                              if interface.fatalerror then
 +                                      debug "closing client after writing"
 +                                      interface:_close()  -- close interface if needed
 +                              elseif interface.startsslcallback then  -- start ssl connection if needed
 +                                      debug "starting ssl handshake after writing"
 +                                      interface.eventstarthandshake = addevent( base, nil, EV_TIMEOUT, interface.startsslcallback, 0 )
 +                              elseif interface.eventreadtimeout then
 +                                      return EV_WRITE, EV_TIMEOUT
                                end
 -                              local buffer, err, part = interface.conn:receive( interface._pattern )  -- receive buffer with "pattern"
 -                              --vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) )
 -                              buffer = buffer or part
 -                              if buffer and #buffer > cfg.MAX_READ_LENGTH then  -- check buffer length
 -                                      interface.fatalerror = "receive buffer exceeded"
 -                                      debug( "fatal error:", interface.fatalerror )
 -                                      interface:_close()
 -                                      interface.eventread = nil
 -                                      return -1
++                              if interface.writebuffer ~= 0 then
++                                      -- data possibly written from ondrain
++                                      return EV_WRITE, cfg.WRITE_TIMEOUT
+                               end
 -                              if err and ( err ~= "timeout" and err ~= "wantread" ) then
 -                                      if "wantwrite" == err then -- need to read on write event
 -                                              if not interface.eventwrite then  -- register new write event if needed
 -                                                      interface.eventwrite = addevent( base, interface.conn, EV_WRITE, interface.writecallback, cfg.WRITE_TIMEOUT )
 -                                              end
 -                                              interface.eventreadtimeout = addevent( base, nil, EV_TIMEOUT,
 -                                                      function( )
 -                                                              interface:_close()
 -                                                      end, cfg.READ_TIMEOUT
 -                                              )
 -                                              debug( "wantwrite during read attempt, reg it in writecallback but dont know what really happens next..." )
 -                                              -- to be honest i dont know what happens next, if it is allowed to first read, the write etc...
 -                                      else  -- connection was closed or fatal error
 -                                              interface.fatalerror = err
 -                                              debug( "connection failed in read event:", interface.fatalerror )
 +                              interface.eventwrite = nil
 +                              return -1
 +                      elseif byte and (err == "timeout" or err == "wantwrite") then  -- want write again
 +                              --vdebug( "writebuffer is not empty:", err )
 +                              interface.writebuffer[1] = s_sub( interface.writebuffer[1], byte + 1, interface.writebufferlen )  -- new buffer
 +                              interface.writebufferlen = interface.writebufferlen - byte
 +                              if "wantread" == err then  -- happens only with luasec
 +                                      local callback = function( )
                                                interface:_close()
 -                                              interface.eventread = nil
 -                                              return -1
 +                                              interface.eventwritetimeout = nil
 +                                              return -1;
                                        end
 -                              else
 -                                      interface.onincoming( interface, buffer, err )  -- send new data to listener
 -                              end
 -                              if interface.noreading then
 -                                      interface.eventread = nil;
 -                                      return -1;
 +                                      interface.eventwritetimeout = addevent( base, nil, EV_TIMEOUT, callback, cfg.WRITE_TIMEOUT )  -- reg a new timeout event
 +                                      debug( "wantread during write attempt, reg it in readcallback but dont know what really happens next..." )
 +                                      -- hopefully this works with luasec; its simply not possible to use 2 different write events on a socket in luaevent
 +                                      return -1
                                end
 -                              return EV_READ, cfg.READ_TIMEOUT
 +                              return EV_WRITE, cfg.WRITE_TIMEOUT
 +                      else  -- connection was closed during writing or fatal error
 +                              interface.fatalerror = err or "fatal error"
 +                              debug( "connection failed in write event:", interface.fatalerror )
 +                              interface:_close()
 +                              interface.eventwrite = nil
 +                              return -1
                        end
                end
 +      end
  
 -              client:settimeout( 0 )  -- set non blocking
 -              setmetatable(interface, interface_mt)
 -              interfacelist( "add", interface )  -- add to interfacelist
 -              return interface
 -      end
 -end
 -
 -local handleserver
 -do
 -      function handleserver( server, addr, port, pattern, listener, sslctx )  -- creates an server interface
 -              debug "creating server interface..."
 -              local interface = {
 -                      _connections = 0;
 -                      
 -                      conn = server;
 -                      onconnect = listener.onconnect;  -- will be called when new client connected
 -                      eventread = false;  -- read event handler
 -                      eventclose = false; -- close event handler
 -                      readcallback = false; -- read event callback
 -                      fatalerror = false; -- error message
 -                      nointerface = true;  -- lock/unlock parameter
 -                      
 -                      _ip = addr, _port = port, _pattern = pattern,
 -                      _sslctx = sslctx;
 -              }
 -              interface.id = tostring(interface):match("%x+$");
 -              interface.readcallback = function( event )  -- server handler, called on incoming connections
 -                      --vdebug( "server can accept, id/addr/port:", interface, addr, port )
 -                      if interface.fatalerror then
 -                              --vdebug( "leaving this event because:", self.fatalerror )
 -                              interface.eventread = nil
 -                              return -1
 -                      end
 -                      local delay = cfg.ACCEPT_DELAY
 -                      if EV_TIMEOUT == event then
 -                              if interface._connections >= cfg.MAX_CONNECTIONS then  -- check connection count
 -                                      debug( "to many connections, seconds to wait for next accept:", delay )
 -                                      return EV_TIMEOUT, delay  -- timeout...
 -                              else
 -                                      return EV_READ  -- accept again
 -                              end
 +      interface.readcallback = function( event )  -- called on read events
 +              --vdebug( "new client read event, id/ip/port:", tostring(interface.id), tostring(ip), tostring(port) )
 +              if interface.noreading or interface.fatalerror then  -- leave this event
 +                      --vdebug( "leaving this event because:", tostring(interface.noreading or interface.fatalerror) )
 +                      interface.eventread = nil
 +                      return -1
 +              end
 +              if EV_TIMEOUT == event and interface:onreadtimeout() ~= true then
 +                      return -1 -- took too long to get some data from client -> disconnect
 +              end
 +              if interface._usingssl then  -- handle luasec
 +                      if interface.eventwritetimeout then  -- ok, in the past writecallback was regged
 +                              local ret = interface.writecallback( )  -- call it
 +                              --vdebug( "tried to write in readcallback, result:", tostring(ret) )
                        end
 -                      --vdebug("max connection check ok, accepting...")
 -                      local client, err = server:accept()    -- try to accept; TODO: check err
 -                      while client do
 -                              if interface._connections >= cfg.MAX_CONNECTIONS then
 -                                      client:close( )  -- refuse connection
 -                                      debug( "maximal connections reached, refuse client connection; accept delay:", delay )
 -                                      return EV_TIMEOUT, delay  -- delay for next accept attempt
 -                              end
 -                              local client_ip, client_port = client:getpeername( )
 -                              interface._connections = interface._connections + 1  -- increase connection count
 -                              local clientinterface = handleclient( client, client_ip, client_port, interface, pattern, listener, sslctx )
 -                              --vdebug( "client id:", clientinterface, "startssl:", startssl )
 -                              if ssl and sslctx then
 -                                      clientinterface:starttls(sslctx, true)
 -                              else
 -                                      clientinterface:_start_session( true )
 -                              end
 -                              debug( "accepted incoming client connection from:", client_ip or "<unknown IP>", client_port or "<unknown port>", "to", port or "<unknown port>");
 -                              
 -                              client, err = server:accept()    -- try to accept again
 +                      if interface.eventreadtimeout then
 +                              interface.eventreadtimeout:close( )
 +                              interface.eventreadtimeout = nil
                        end
 -                      return EV_READ
                end
 -              
 -              server:settimeout( 0 )
 -              setmetatable(interface, interface_mt)
 -              interfacelist( "add", interface )
 -              interface:_start_session()
 -              return interface
 -      end
 -end
 -
 -local addserver = ( function( )
 -      return function( addr, port, listener, pattern, sslcfg, startssl )  -- TODO: check arguments
 -              --vdebug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslcfg or "nil", startssl or "nil")
 -              local server, err = socket.bind( addr, port, cfg.ACCEPT_QUEUE )  -- create server socket
 -              if not server then
 -                      debug( "creating server socket on "..addr.." port "..port.." failed:", err )
 -                      return nil, err
 +              local buffer, err, part = interface.conn:receive( interface._pattern )  -- receive buffer with "pattern"
 +              --vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) )
 +              buffer = buffer or part
 +              if buffer and #buffer > cfg.MAX_READ_LENGTH then  -- check buffer length
 +                      interface.fatalerror = "receive buffer exceeded"
 +                      debug( "fatal error:", interface.fatalerror )
 +                      interface:_close()
 +                      interface.eventread = nil
 +                      return -1
                end
 -              local sslctx
 -              if sslcfg then
 -                      if not ssl then
 -                              debug "fatal error: luasec not found"
 -                              return nil, "luasec not found"
 -                      end
 -                      sslctx, err = sslcfg
 -                      if err then
 -                              debug( "error while creating new ssl context for server socket:", err )
 -                              return nil, err
 +              if err and ( err ~= "timeout" and err ~= "wantread" ) then
 +                      if "wantwrite" == err then -- need to read on write event
 +                              if not interface.eventwrite then  -- register new write event if needed
 +                                      interface.eventwrite = addevent( base, interface.conn, EV_WRITE, interface.writecallback, cfg.WRITE_TIMEOUT )
 +                              end
 +                              interface.eventreadtimeout = addevent( base, nil, EV_TIMEOUT,
 +                              function( )
 +                                      interface:_close()
 +                              end, cfg.READ_TIMEOUT
 +                              )
 +                              debug( "wantwrite during read attempt, reg it in writecallback but dont know what really happens next..." )
 +                              -- to be honest i dont know what happens next, if it is allowed to first read, the write etc...
 +                      else  -- connection was closed or fatal error
 +                              interface.fatalerror = err
 +                              debug( "connection failed in read event:", interface.fatalerror )
 +                              interface:_close()
 +                              interface.eventread = nil
 +                              return -1
                        end
 +              else
 +                      interface.onincoming( interface, buffer, err )  -- send new data to listener
 +              end
 +              if interface.noreading then
 +                      interface.eventread = nil;
 +                      return -1;
                end
 -              local interface = handleserver( server, addr, port, pattern, listener, sslctx, startssl )  -- new server handler
 -              debug( "new server created with id:", tostring(interface))
 -              return interface
 +              return EV_READ, cfg.READ_TIMEOUT
        end
 -end )( )
  
 -local addclient, wrapclient
 -do
 -      function wrapclient( client, ip, port, listeners, pattern, sslctx )
 -              local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx )
 -              interface:_start_connection(sslctx)
 -              return interface, client
 -              --function handleclient( client, ip, port, server, pattern, listener, _, sslctx )  -- creates an client interface
 -      end
 -      
 -      function addclient( addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl )
 -              local client, err = socket.tcp()  -- creating new socket
 -              if not client then
 -                      debug( "cannot create socket:", err )
 -                      return nil, err
 +      client:settimeout( 0 )  -- set non blocking
 +      setmetatable(interface, interface_mt)
 +      interfacelist[ interface ] = true  -- add to interfacelist
 +      return interface
 +end
 +
 +local function handleserver( server, addr, port, pattern, listener, sslctx )  -- creates an server interface
 +      debug "creating server interface..."
 +      local interface = {
 +              _connections = 0;
 +
 +              type = "server";
 +              conn = server;
 +              onconnect = listener.onconnect;  -- will be called when new client connected
 +              eventread = false;  -- read event handler
 +              eventclose = false; -- close event handler
 +              readcallback = false; -- read event callback
 +              fatalerror = false; -- error message
 +              nointerface = true;  -- lock/unlock parameter
 +
 +              _ip = addr, _port = port, _pattern = pattern,
 +              _sslctx = sslctx;
 +      }
 +      interface.id = tostring(interface):match("%x+$");
 +      interface.readcallback = function( event )  -- server handler, called on incoming connections
 +              --vdebug( "server can accept, id/addr/port:", interface, addr, port )
 +              if interface.fatalerror then
 +                      --vdebug( "leaving this event because:", self.fatalerror )
 +                      interface.eventread = nil
 +                      return -1
                end
 -              client:settimeout( 0 )  -- set nonblocking
 -              if localaddr then
 -                      local res, err = client:bind( localaddr, localport, -1 )
 -                      if not res then
 -                              debug( "cannot bind client:", err )
 -                              return nil, err
 +              local delay = cfg.ACCEPT_DELAY
 +              if EV_TIMEOUT == event then
 +                      if interface._connections >= cfg.MAX_CONNECTIONS then  -- check connection count
 +                              debug( "to many connections, seconds to wait for next accept:", delay )
 +                              return EV_TIMEOUT, delay  -- timeout...
 +                      else
 +                              return EV_READ  -- accept again
                        end
                end
 -              local sslctx
 -              if sslcfg then  -- handle ssl/new context
 -                      if not ssl then
 -                              debug "need luasec, but not available"
 -                              return nil, "luasec not found"
 +              --vdebug("max connection check ok, accepting...")
 +              local client, err = server:accept()    -- try to accept; TODO: check err
 +              while client do
 +                      if interface._connections >= cfg.MAX_CONNECTIONS then
 +                              client:close( )  -- refuse connection
 +                              debug( "maximal connections reached, refuse client connection; accept delay:", delay )
 +                              return EV_TIMEOUT, delay  -- delay for next accept attempt
                        end
 -                      sslctx, err = sslcfg
 -                      if err then
 -                              debug( "cannot create new ssl context:", err )
 -                              return nil, err
 +                      local client_ip, client_port = client:getpeername( )
 +                      interface._connections = interface._connections + 1  -- increase connection count
 +                      local clientinterface = handleclient( client, client_ip, client_port, interface, pattern, listener, sslctx )
 +                      --vdebug( "client id:", clientinterface, "startssl:", startssl )
 +                      if has_luasec and sslctx then
 +                              clientinterface:starttls(sslctx, true)
 +                      else
 +                              clientinterface:_start_session( true )
                        end
 +                      debug( "accepted incoming client connection from:", client_ip or "<unknown IP>", client_port or "<unknown port>", "to", port or "<unknown port>");
 +
 +                      client, err = server:accept()    -- try to accept again
                end
 -              local res, err = client:connect( addr, serverport )  -- connect
 -              if res or ( err == "timeout" ) then
 -                      local ip, port = client:getsockname( )
 -                      local interface = wrapclient( client, ip, serverport, listener, pattern, sslctx, startssl )
 -                      interface:_start_connection( startssl )
 -                      debug( "new connection id:", interface.id )
 -                      return interface, err
 +              return EV_READ
 +      end
 +
 +      server:settimeout( 0 )
 +      setmetatable(interface, interface_mt)
 +      interfacelist[ interface ] = true
 +      interface:_start_session()
 +      return interface
 +end
 +
 +local function addserver( addr, port, listener, pattern, sslctx, startssl )  -- TODO: check arguments
 +      --vdebug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslctx or "nil", startssl or "nil")
 +      if sslctx and not has_luasec then
 +              debug "fatal error: luasec not found"
 +              return nil, "luasec not found"
 +      end
 +      local server, err = socket.bind( addr, port, cfg.ACCEPT_QUEUE )  -- create server socket
 +      if not server then
 +              debug( "creating server socket on "..addr.." port "..port.." failed:", err )
 +              return nil, err
 +      end
 +      local interface = handleserver( server, addr, port, pattern, listener, sslctx, startssl )  -- new server handler
 +      debug( "new server created with id:", tostring(interface))
 +      return interface
 +end
 +
 +local function wrapclient( client, ip, port, listeners, pattern, sslctx )
 +      local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx )
 +      interface:_start_connection(sslctx)
 +      return interface, client
 +      --function handleclient( client, ip, port, server, pattern, listener, _, sslctx )  -- creates an client interface
 +end
 +
 +local function addclient( addr, serverport, listener, pattern, sslctx, typ )
 +      if sslctx and not has_luasec then
 +              debug "need luasec, but not available"
 +              return nil, "luasec not found"
 +      end
 +      if not typ then
 +              local addrinfo, err = getaddrinfo(addr)
 +              if not addrinfo then return nil, err end
 +              if addrinfo[1] and addrinfo[1].family == "inet6" then
 +                      typ = "tcp6"
                else
 -                      debug( "new connection failed:", err )
 -                      return nil, err
 +                      typ = "tcp"
                end
        end
 +      local create = socket[typ]
 +      if type( create ) ~= "function"  then
 +              return nil, "invalid socket type"
 +      end
 +      local client, err = create()  -- creating new socket
 +      if not client then
 +              debug( "cannot create socket:", err )
 +              return nil, err
 +      end
 +      client:settimeout( 0 )  -- set nonblocking
 +      local res, err = client:connect( addr, serverport )  -- connect
 +      if res or ( err == "timeout" ) then
 +              local ip, port = client:getsockname( )
 +              local interface = wrapclient( client, ip, serverport, listener, pattern, sslctx )
 +              debug( "new connection id:", interface.id )
 +              return interface, err
 +      else
 +              debug( "new connection failed:", err )
 +              return nil, err
 +      end
  end
  
 -
 -local loop = function( )  -- starts the event loop
 +local function loop( )  -- starts the event loop
        base:loop( )
        return "quitting";
  end
index 552b9e49970a5e6e5f950d6b598316d6d65008dc,f8e8f74dcfe7026dc1df765d846a9be1dbe76634..88ee43c1f4ac0ed0cafef209519169705a437b43
@@@ -681,52 -668,93 +681,59 @@@ function room_mt:process_form(origin, s
        if form.attr.type == "cancel" then origin.send(st.reply(stanza)); return; end
        if form.attr.type ~= "submit" then origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form")); return; end
  
 -
 -      local fields = self:get_form_layout():data(form);
 -      if fields.FORM_TYPE ~= "http://jabber.org/protocol/muc#roomconfig" then origin.send(st.error_reply(stanza, "cancel", "bad-request", "Form is not of type room configuration")); return; end
 -
 -      local dirty = false
 -
 -      local event = { room = self, fields = fields, changed = dirty };
 -      module:fire_event("muc-config-submitted", event);
 -      dirty = event.changed or dirty;
 -
 -      local name = fields['muc#roomconfig_roomname'];
 -      if name ~= self:get_name() then
 -              self:set_name(name);
 -      end
 -
 -      local description = fields['muc#roomconfig_roomdesc'];
 -      if description ~= self:get_description() then
 -              self:set_description(description);
+       if form.tags[1] == nil then
+               -- instant room
+               if self.save then self:save(true); end
+               origin.send(st.reply(stanza));
+               return true;
+       end
 +      local fields, errors, present = self:get_form_layout(stanza.attr.from):data(form);
 +      if fields.FORM_TYPE ~= "http://jabber.org/protocol/muc#roomconfig" then
 +              origin.send(st.error_reply(stanza, "cancel", "bad-request", "Form is not of type room configuration"));
 +              return;
        end
  
 -      local persistent = fields['muc#roomconfig_persistentroom'];
 -      dirty = dirty or (self:is_persistent() ~= persistent)
 -      module:log("debug", "persistent=%s", tostring(persistent));
 +      local changed = {};
  
 -      local moderated = fields['muc#roomconfig_moderatedroom'];
 -      dirty = dirty or (self:is_moderated() ~= moderated)
 -      module:log("debug", "moderated=%s", tostring(moderated));
 -
 -      local membersonly = fields['muc#roomconfig_membersonly'];
 -      dirty = dirty or (self:is_members_only() ~= membersonly)
 -      module:log("debug", "membersonly=%s", tostring(membersonly));
 -
 -      local public = fields['muc#roomconfig_publicroom'];
 -      dirty = dirty or (self:is_hidden() ~= (not public and true or nil))
 -
 -      local changesubject = fields['muc#roomconfig_changesubject'];
 -      dirty = dirty or (self:get_changesubject() ~= (not changesubject and true or nil))
 -      module:log('debug', 'changesubject=%s', changesubject and "true" or "false")
 -
 -      local historylength = tonumber(fields['muc#roomconfig_historylength']);
 -      dirty = dirty or (historylength and (self:get_historylength() ~= historylength));
 -      module:log('debug', 'historylength=%s', historylength)
 -
 -
 -      local whois = fields['muc#roomconfig_whois'];
 -      if not valid_whois[whois] then
 -          origin.send(st.error_reply(stanza, 'cancel', 'bad-request', "Invalid value for 'whois'"));
 -          return;
 +      local function handle_option(name, field, allowed)
 +              if not present[field] then return; end
 +              local new = fields[field];
 +              if allowed and not allowed[new] then return; end
 +              if new == self["get_"..name](self) then return; end
 +              changed[name] = true;
 +              self["set_"..name](self, new);
        end
 -      local whois_changed = self._data.whois ~= whois
 -      self._data.whois = whois
 -      module:log('debug', 'whois=%s', whois)
  
 -      local password = fields['muc#roomconfig_roomsecret'];
 -      if self:get_password() ~= password then
 -              self:set_password(password);
 -      end
 -      self:set_moderated(moderated);
 -      self:set_members_only(membersonly);
 -      self:set_persistent(persistent);
 -      self:set_hidden(not public);
 -      self:set_changesubject(changesubject);
 -      self:set_historylength(historylength);
 +      local event = { room = self, fields = fields, changed = changed, stanza = stanza, origin = origin, update_option = handle_option };
 +      module:fire_event("muc-config-submitted", event);
 +
 +      handle_option("name", "muc#roomconfig_roomname");
 +      handle_option("description", "muc#roomconfig_roomdesc");
 +      handle_option("persistent", "muc#roomconfig_persistentroom");
 +      handle_option("moderated", "muc#roomconfig_moderatedroom");
 +      handle_option("members_only", "muc#roomconfig_membersonly");
 +      handle_option("public", "muc#roomconfig_publicroom");
 +      handle_option("changesubject", "muc#roomconfig_changesubject");
 +      handle_option("historylength", "muc#roomconfig_historylength");
 +      handle_option("whois", "muc#roomconfig_whois", valid_whois);
 +      handle_option("password", "muc#roomconfig_roomsecret");
  
        if self.save then self:save(true); end
 +      if self.locked then
 +              module:fire_event("muc-room-unlocked", { room = self });
 +              self.locked = nil;
 +      end
        origin.send(st.reply(stanza));
  
 -      if dirty or whois_changed then
 +      if next(changed) then
                local msg = st.message({type='groupchat', from=self.jid})
 -                      :tag('x', {xmlns='http://jabber.org/protocol/muc#user'});
 -
 -              if dirty then
 -                      msg.tags[1]:tag('status', {code = '104'}):up();
 -              end
 -              if whois_changed then
 -                      local code = (whois == 'moderators') and "173" or "172";
 +                      :tag('x', {xmlns='http://jabber.org/protocol/muc#user'})
 +                              :tag('status', {code = '104'}):up();
 +              if changed.whois then
 +                      local code = (self:get_whois() == 'moderators') and "173" or "172";
                        msg.tags[1]:tag('status', {code = code}):up();
                end
 -              msg:up();
 -
                self:broadcast_message(msg, false)
        end
  end