Another small fix, for logging in s2smanager
[prosody.git] / net / server.lua
1 --[[\r
2 \r
3                 server.lua by blastbeat of the luadch project\r
4                 \r
5                 re-used here under the MIT/X Consortium License\r
6 \r
7                 - this script contains the server loop of the program\r
8                 - other scripts can reg a server here\r
9 \r
10 ]]--\r
11 \r
12 ----------------------------------// DECLARATION //--\r
13 \r
14 --// constants //--\r
15 \r
16 local STAT_UNIT = 1 / ( 1024 * 1024 )    -- mb\r
17 \r
18 --// lua functions //--\r
19 \r
20 local function use( what ) return _G[ what ] end\r
21 \r
22 local type = use "type"\r
23 local pairs = use "pairs"\r
24 local ipairs = use "ipairs"\r
25 local tostring = use "tostring"\r
26 local collectgarbage = use "collectgarbage"\r
27 \r
28 --// lua libs //--\r
29 \r
30 local table = use "table"\r
31 local coroutine = use "coroutine"\r
32 \r
33 --// lua lib methods //--\r
34 \r
35 local table_concat = table.concat\r
36 local table_remove = table.remove\r
37 local string_sub = use'string'.sub\r
38 local coroutine_wrap = coroutine.wrap\r
39 local coroutine_yield = coroutine.yield\r
40 local print = print;\r
41 local out_put = function () end --print;\r
42 local out_error = print;\r
43 \r
44 --// extern libs //--\r
45 \r
46 local luasec = select(2, pcall(require, "ssl"))\r
47 local luasocket = require "socket"\r
48 \r
49 --// extern lib methods //--\r
50 \r
51 local ssl_wrap = ( luasec and luasec.wrap )\r
52 local socket_bind = luasocket.bind\r
53 local socket_select = luasocket.select\r
54 local ssl_newcontext = ( luasec and luasec.newcontext )\r
55 \r
56 --// functions //--\r
57 \r
58 local loop\r
59 local stats\r
60 local addtimer\r
61 local closeall\r
62 local addserver\r
63 local firetimer\r
64 local closesocket\r
65 local removesocket\r
66 local wrapserver\r
67 local wraptcpclient\r
68 local wrapsslclient\r
69 \r
70 --// tables //--\r
71 \r
72 local listener\r
73 local readlist\r
74 local writelist\r
75 local socketlist\r
76 local timelistener\r
77 \r
78 --// simple data types //--\r
79 \r
80 local _\r
81 local readlen = 0    -- length of readlist\r
82 local writelen = 0    -- lenght of writelist\r
83 \r
84 local sendstat= 0\r
85 local receivestat = 0\r
86 \r
87 ----------------------------------// DEFINITION //--\r
88 \r
89 listener = { }    -- key = port, value = table\r
90 readlist = { }    -- array with sockets to read from\r
91 writelist = { }    -- arrary with sockets to write to\r
92 socketlist = { }    -- key = socket, value = wrapped socket\r
93 timelistener = { }\r
94 \r
95 stats = function( )\r
96         return receivestat, sendstat\r
97 end\r
98 \r
99 wrapserver = function( listener, socket, ip, serverport, mode, sslctx )    -- this function wraps a server\r
100 \r
101         local dispatch, disconnect = listener.listener, listener.disconnect    -- dangerous\r
102 \r
103         local wrapclient, err\r
104 \r
105         if sslctx then\r
106                 if not ssl_newcontext then\r
107                         return nil, "luasec not found"\r
108                 end\r
109                 if type( sslctx ) ~= "table" then\r
110                         out_error "server.lua: wrong server sslctx"\r
111                         return nil, "wrong server sslctx"\r
112                 end\r
113                 sslctx, err = ssl_newcontext( sslctx )\r
114                 if not sslctx then\r
115                         err = err or "wrong sslctx parameters"\r
116                         out_error( "server.lua: ", err )\r
117                         return nil, err\r
118                 end\r
119                 wrapclient = wrapsslclient\r
120                 wrapclient = wraptlsclient\r
121         else\r
122                 wrapclient = wraptcpclient\r
123         end\r
124 \r
125         local accept = socket.accept\r
126         local close = socket.close\r
127 \r
128         --// public methods of the object //--    \r
129 \r
130         local handler = { }\r
131 \r
132         handler.shutdown = function( ) end\r
133 \r
134         --[[handler.listener = function( data, err )\r
135                 return ondata( handler, data, err )\r
136         end]]\r
137         handler.ssl = function( )\r
138                 return sslctx and true or false\r
139         end\r
140         handler.close = function( closed )\r
141                 _ = not closed and close( socket )\r
142                 writelen = removesocket( writelist, socket, writelen )\r
143                 readlen = removesocket( readlist, socket, readlen )\r
144                 socketlist[ socket ] = nil\r
145                 handler = nil\r
146         end\r
147         handler.ip = function( )\r
148                 return ip\r
149         end\r
150         handler.serverport = function( )\r
151                 return serverport\r
152         end\r
153         handler.socket = function( )\r
154                 return socket\r
155         end\r
156         handler.receivedata = function( )\r
157                 local client, err = accept( socket )    -- try to accept\r
158                 if client then\r
159                         local ip, clientport = client:getpeername( )\r
160                         client:settimeout( 0 )\r
161                         local handler, client, err = wrapclient( listener, client, ip, serverport, clientport, mode, sslctx )    -- wrap new client socket\r
162                         if err then    -- error while wrapping ssl socket\r
163                                 return false\r
164                         end\r
165                         out_put( "server.lua: accepted new client connection from ", ip, ":", clientport )\r
166                         return dispatch( handler )\r
167                 elseif err then    -- maybe timeout or something else\r
168                         out_put( "server.lua: error with new client connection: ", err )\r
169                         return false\r
170                 end\r
171         end\r
172         return handler\r
173 end\r
174 \r
175 wrapsslclient = function( listener, socket, ip, serverport, clientport, mode, sslctx )    -- this function wraps a ssl cleint\r
176 \r
177         local dispatch, disconnect = listener.listener, listener.disconnect\r
178 \r
179         --// transform socket to ssl object //--\r
180 \r
181         local err\r
182         socket, err = ssl_wrap( socket, sslctx )    -- wrap socket\r
183         if err then\r
184                 out_put( "server.lua: ssl error: ", err )\r
185                 return nil, nil, err    -- fatal error\r
186         end\r
187         socket:settimeout( 0 )\r
188 \r
189         --// private closures of the object //--\r
190 \r
191         local writequeue = { }    -- buffer for messages to send\r
192 \r
193         local eol   -- end of buffer\r
194 \r
195         local sstat, rstat = 0, 0\r
196 \r
197         --// local import of socket methods //--\r
198 \r
199         local send = socket.send\r
200         local receive = socket.receive\r
201         local close = socket.close\r
202         --local shutdown = socket.shutdown\r
203 \r
204         --// public methods of the object //--\r
205 \r
206         local handler = { }\r
207 \r
208         handler.getstats = function( )\r
209                 return rstat, sstat\r
210         end\r
211 \r
212         handler.listener = function( data, err )\r
213                 return listener( handler, data, err )\r
214         end\r
215         handler.ssl = function( )\r
216                 return true\r
217         end\r
218         handler.send = function( _, data, i, j )\r
219                         return send( socket, data, i, j )\r
220         end\r
221         handler.receive = function( pattern, prefix )\r
222                         return receive( socket, pattern, prefix )\r
223         end\r
224         handler.shutdown = function( pattern )\r
225                 --return shutdown( socket, pattern )\r
226         end\r
227         handler.close = function( closed )\r
228                 close( socket )\r
229                 writelen = ( eol and removesocket( writelist, socket, writelen ) ) or writelen\r
230                 readlen = removesocket( readlist, socket, readlen )\r
231                 socketlist[ socket ] = nil\r
232                 out_put "server.lua: closed handler and removed socket from list"\r
233         end\r
234         handler.ip = function( )\r
235                 return ip\r
236         end\r
237         handler.serverport = function( )\r
238                 return serverport\r
239         end\r
240         handler.clientport = function( ) \r
241                 return clientport\r
242         end\r
243 \r
244         handler.write = function( data )\r
245                 if not eol then\r
246                         writelen = writelen + 1\r
247                         writelist[ writelen ] = socket\r
248                         eol = 0\r
249                 end\r
250                 eol = eol + 1\r
251                 writequeue[ eol ] = data\r
252         end\r
253         handler.writequeue = function( )\r
254                 return writequeue\r
255         end\r
256         handler.socket = function( )\r
257                 return socket\r
258         end\r
259         handler.mode = function( )\r
260                 return mode\r
261         end\r
262         handler._receivedata = function( )\r
263                 local data, err, part = receive( socket, mode )    -- receive data in "mode"\r
264                 if not err or ( err == "timeout" or err == "wantread" ) then    -- received something\r
265                         local data = data or part or ""\r
266                         local count = #data * STAT_UNIT\r
267                         rstat = rstat + count\r
268                         receivestat = receivestat + count\r
269                         out_put( "server.lua: read data '", data, "', error: ", err )\r
270                         return dispatch( handler, data, err )\r
271                 else    -- connections was closed or fatal error\r
272                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
273                         handler.close( )\r
274                         disconnect( handler, err )\r
275                         writequeue = nil\r
276                         handler = nil\r
277                         return false\r
278                 end\r
279         end\r
280         handler._dispatchdata = function( )    -- this function writes data to handlers\r
281                 local buffer = table_concat( writequeue, "", 1, eol )\r
282                 local succ, err, byte = send( socket, buffer )\r
283                 local count = ( succ or 0 ) * STAT_UNIT\r
284                 sstat = sstat + count\r
285                 sendstat = sendstat + count\r
286                 out_put( "server.lua: sended '", buffer, "', bytes: ", succ, ", error: ", err, ", part: ", byte, ", to: ", ip, ":", clientport )\r
287                 if succ then    -- sending succesful\r
288                         --writequeue = { }\r
289                         eol = nil\r
290                         writelen = removesocket( writelist, socket, writelen )    -- delete socket from writelist\r
291                         return true\r
292                 elseif byte and ( err == "timeout" or err == "wantwrite" ) then    -- want write\r
293                         buffer = string_sub( buffer, byte + 1, -1 )    -- new buffer\r
294                         writequeue[ 1 ] = buffer    -- insert new buffer in queue\r
295                         eol = 1\r
296                         return true\r
297                 else    -- connection was closed during sending or fatal error\r
298                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
299                         handler.close( )\r
300                         disconnect( handler, err )\r
301                         writequeue = nil\r
302                         handler = nil\r
303                         return false\r
304                 end\r
305         end\r
306 \r
307         -- // COMPAT // --\r
308 \r
309         handler.getIp = handler.ip\r
310         handler.getPort = handler.clientport\r
311 \r
312         --// handshake //--\r
313 \r
314         local wrote\r
315 \r
316         handler.handshake = coroutine_wrap( function( client )\r
317                         local err\r
318                         for i = 1, 10 do    -- 10 handshake attemps\r
319                                 _, err = client:dohandshake( )\r
320                                 if not err then\r
321                                         out_put( "server.lua: ssl handshake done" )\r
322                                         writelen = ( wrote and removesocket( writelist, socket, writelen ) ) or writelen\r
323                                         handler.receivedata = handler._receivedata    -- when handshake is done, replace the handshake function with regular functions\r
324                                         handler.dispatchdata = handler._dispatchdata\r
325                                         return dispatch( handler )\r
326                                 else\r
327                                         out_put( "server.lua: error during ssl handshake: ", err )\r
328                                         if err == "wantwrite" then\r
329                                                 if wrote == nil then\r
330                                                         writelen = writelen + 1\r
331                                                         writelist[ writelen ] = client\r
332                                                         wrote = true\r
333                                                 end\r
334                                         end\r
335                                         coroutine_yield( handler, nil, err )    -- handshake not finished\r
336                                 end\r
337                         end\r
338                         _ = err ~= "closed" and close( socket )\r
339                         handler.close( )\r
340                         disconnect( handler, err )\r
341                         writequeue = nil\r
342                         handler = nil\r
343                         return false    -- handshake failed\r
344                 end\r
345         )\r
346         handler.receivedata = handler.handshake\r
347         handler.dispatchdata = handler.handshake\r
348 \r
349         handler.handshake( socket )    -- do handshake\r
350 \r
351         socketlist[ socket ] = handler\r
352         readlen = readlen + 1\r
353         readlist[ readlen ] = socket\r
354 \r
355         return handler, socket\r
356 end\r
357 \r
358 wraptlsclient = function( listener, socket, ip, serverport, clientport, mode, sslctx )    -- this function wraps a tls cleint\r
359 \r
360         local dispatch, disconnect = listener.listener, listener.disconnect\r
361 \r
362         --// transform socket to ssl object //--\r
363 \r
364         local err\r
365 \r
366         socket:settimeout( 0 )\r
367 \r
368         --// private closures of the object //--\r
369 \r
370         local writequeue = { }    -- buffer for messages to send\r
371 \r
372         local eol   -- end of buffer\r
373 \r
374         local sstat, rstat = 0, 0\r
375 \r
376         --// local import of socket methods //--\r
377 \r
378         local send = socket.send\r
379         local receive = socket.receive\r
380         local close = socket.close\r
381         --local shutdown = socket.shutdown\r
382 \r
383         --// public methods of the object //--\r
384 \r
385         local handler = { }\r
386 \r
387         handler.getstats = function( )\r
388                 return rstat, sstat\r
389         end\r
390 \r
391         handler.listener = function( data, err )\r
392                 return listener( handler, data, err )\r
393         end\r
394         handler.ssl = function( )\r
395                 return false\r
396         end\r
397         handler.send = function( _, data, i, j )\r
398                         return send( socket, data, i, j )\r
399         end\r
400         handler.receive = function( pattern, prefix )\r
401                         return receive( socket, pattern, prefix )\r
402         end\r
403         handler.shutdown = function( pattern )\r
404                 --return shutdown( socket, pattern )\r
405         end\r
406         handler.close = function( closed )\r
407                 close( socket )\r
408                 writelen = ( eol and removesocket( writelist, socket, writelen ) ) or writelen\r
409                 readlen = removesocket( readlist, socket, readlen )\r
410                 socketlist[ socket ] = nil\r
411                 out_put "server.lua: closed handler and removed socket from list"\r
412         end\r
413         handler.ip = function( )\r
414                 return ip\r
415         end\r
416         handler.serverport = function( )\r
417                 return serverport\r
418         end\r
419         handler.clientport = function( ) \r
420                 return clientport\r
421         end\r
422 \r
423         handler.write = function( data )\r
424                 if not eol then\r
425                         writelen = writelen + 1\r
426                         writelist[ writelen ] = socket\r
427                         eol = 0\r
428                 end\r
429                 eol = eol + 1\r
430                 writequeue[ eol ] = data\r
431         end\r
432         handler.writequeue = function( )\r
433                 return writequeue\r
434         end\r
435         handler.socket = function( )\r
436                 return socket\r
437         end\r
438         handler.mode = function( )\r
439                 return mode\r
440         end\r
441         handler._receivedata = function( )\r
442                 local data, err, part = receive( socket, mode )    -- receive data in "mode"\r
443                 if not err or ( err == "timeout" or err == "wantread" ) then    -- received something\r
444                         local data = data or part or ""\r
445                         local count = #data * STAT_UNIT\r
446                         rstat = rstat + count\r
447                         receivestat = receivestat + count\r
448                         --out_put( "server.lua: read data '", data, "', error: ", err )\r
449                         return dispatch( handler, data, err )\r
450                 else    -- connections was closed or fatal error\r
451                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
452                         handler.close( )\r
453                         disconnect( handler, err )\r
454                         writequeue = nil\r
455                         handler = nil\r
456                         return false\r
457                 end\r
458         end\r
459         handler._dispatchdata = function( )    -- this function writes data to handlers\r
460                 local buffer = table_concat( writequeue, "", 1, eol )\r
461                 local succ, err, byte = send( socket, buffer )\r
462                 local count = ( succ or 0 ) * STAT_UNIT\r
463                 sstat = sstat + count\r
464                 sendstat = sendstat + count\r
465                 out_put( "server.lua: sended '", buffer, "', bytes: ", succ, ", error: ", err, ", part: ", byte, ", to: ", ip, ":", clientport )\r
466                 if succ then    -- sending succesful\r
467                         --writequeue = { }\r
468                         eol = nil\r
469                         writelen = removesocket( writelist, socket, writelen )    -- delete socket from writelist\r
470                         if handler.need_tls then\r
471                                 out_put("server.lua: connection is ready for tls handshake");\r
472                                 handler.starttls(true);\r
473                                 if handler.need_tls then\r
474                                         out_put("server.lua: uh-oh... we still want tls, something must be wrong");\r
475                                 end\r
476                         end\r
477                         return true\r
478                 elseif byte and ( err == "timeout" or err == "wantwrite" ) then    -- want write\r
479                         buffer = string_sub( buffer, byte + 1, -1 )    -- new buffer\r
480                         writequeue[ 1 ] = buffer    -- insert new buffer in queue\r
481                         eol = 1\r
482                         return true\r
483                 else    -- connection was closed during sending or fatal error\r
484                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
485                         handler.close( )\r
486                         disconnect( handler, err )\r
487                         writequeue = nil\r
488                         handler = nil\r
489                         return false\r
490                 end\r
491         end\r
492 \r
493         handler.receivedata, handler.dispatchdata = handler._receivedata, handler._dispatchdata;\r
494         -- // COMPAT // --\r
495 \r
496         handler.getIp = handler.ip\r
497         handler.getPort = handler.clientport\r
498 \r
499         --// handshake //--\r
500 \r
501         local wrote, read\r
502         \r
503         handler.starttls = function (now)\r
504                 if not now then out_put("server.lua: we need to do tls, but delaying until later"); handler.need_tls = true; return; end\r
505                 out_put( "server.lua: attempting to start tls on "..tostring(socket) )\r
506                 socket, err = ssl_wrap( socket, sslctx )    -- wrap socket\r
507                 out_put("sslwrapped socket is "..tostring(socket));\r
508                 if err then\r
509                         out_put( "server.lua: ssl error: ", err )\r
510                         return nil, nil, err    -- fatal error\r
511                 end\r
512                 socket:settimeout( 1 )\r
513                 send = socket.send\r
514                 receive = socket.receive\r
515                 close = socket.close\r
516                 handler.ssl = function( )\r
517                         return true\r
518                 end\r
519                 handler.send = function( _, data, i, j )\r
520                         return send( socket, data, i, j )\r
521                 end\r
522                 handler.receive = function( pattern, prefix )\r
523                         return receive( socket, pattern, prefix )\r
524                 end\r
525                 \r
526                         handler.handshake = coroutine_wrap( function( client )\r
527                                         local err\r
528                                         for i = 1, 10 do    -- 10 handshake attemps\r
529                                                 _, err = client:dohandshake( )\r
530                                                 if not err then\r
531                                                         out_put( "server.lua: ssl handshake done" )\r
532                                                         writelen = ( wrote and removesocket( writelist, socket, writelen ) ) or writelen\r
533                                                         handler.receivedata = handler._receivedata    -- when handshake is done, replace the handshake function with regular functions\r
534                                                         handler.dispatchdata = handler._dispatchdata\r
535                                                         handler.need_tls = nil\r
536                                                         socketlist[ client ] = handler\r
537                                                         readlen = readlen + 1\r
538                                                         readlist[ readlen ] = client                                                                                            \r
539                                                         return true;\r
540                                                 else\r
541                                                         out_put( "server.lua: error during ssl handshake: ", err )\r
542                                                         if err == "wantwrite" then\r
543                                                                 if wrote == nil then\r
544                                                                         writelen = writelen + 1\r
545                                                                         writelist[ writelen ] = client\r
546                                                                         wrote = true\r
547                                                                 end\r
548                                                         end\r
549                                                         coroutine_yield( handler, nil, err )    -- handshake not finished\r
550                                                 end\r
551                                         end\r
552                                         _ = err ~= "closed" and close( socket )\r
553                                         handler.close( )\r
554                                         disconnect( handler, err )\r
555                                         writequeue = nil\r
556                                         handler = nil\r
557                                         return false    -- handshake failed\r
558                                 end\r
559                         )\r
560                         handler.receivedata = handler.handshake\r
561                         handler.dispatchdata = handler.handshake\r
562 \r
563                         handler.handshake( socket )    -- do handshake\r
564                 end\r
565         socketlist[ socket ] = handler\r
566         readlen = readlen + 1\r
567         readlist[ readlen ] = socket\r
568 \r
569         return handler, socket\r
570 end\r
571 \r
572 wraptcpclient = function( listener, socket, ip, serverport, clientport, mode )    -- this function wraps a socket\r
573 \r
574         local dispatch, disconnect = listener.listener, listener.disconnect\r
575 \r
576         --// private closures of the object //--\r
577 \r
578         local writequeue = { }    -- list for messages to send\r
579 \r
580         local eol\r
581 \r
582         local rstat, sstat = 0, 0\r
583 \r
584         --// local import of socket methods //--\r
585 \r
586         local send = socket.send\r
587         local receive = socket.receive\r
588         local close = socket.close\r
589         local shutdown = socket.shutdown\r
590 \r
591         --// public methods of the object //--\r
592 \r
593         local handler = { }\r
594 \r
595         handler.getstats = function( )\r
596                 return rstat, sstat\r
597         end\r
598 \r
599         handler.listener = function( data, err )\r
600                 return listener( handler, data, err )\r
601         end\r
602         handler.ssl = function( )\r
603                 return false\r
604         end\r
605         handler.send = function( _, data, i, j )\r
606                         return send( socket, data, i, j )\r
607         end\r
608         handler.receive = function( pattern, prefix )\r
609                         return receive( socket, pattern, prefix )\r
610         end\r
611         handler.shutdown = function( pattern )\r
612                 return shutdown( socket, pattern )\r
613         end\r
614         handler.close = function( closed )\r
615                 _ = not closed and shutdown( socket )\r
616                 _ = not closed and close( socket )\r
617                 writelen = ( eol and removesocket( writelist, socket, writelen ) ) or writelen\r
618                 readlen = removesocket( readlist, socket, readlen )\r
619                 socketlist[ socket ] = nil\r
620                 out_put "server.lua: closed handler and removed socket from list"\r
621         end\r
622         handler.ip = function( )\r
623                 return ip\r
624         end\r
625         handler.serverport = function( )\r
626                 return serverport\r
627         end\r
628         handler.clientport = function( ) \r
629                 return clientport\r
630         end\r
631         handler.write = function( data )\r
632                 if not eol then\r
633                         writelen = writelen + 1\r
634                         writelist[ writelen ] = socket\r
635                         eol = 0\r
636                 end\r
637                 eol = eol + 1\r
638                 writequeue[ eol ] = data\r
639         end\r
640         handler.writequeue = function( )\r
641                 return writequeue\r
642         end\r
643         handler.socket = function( )\r
644                 return socket\r
645         end\r
646         handler.mode = function( )\r
647                 return mode\r
648         end\r
649         \r
650         handler.receivedata = function( )\r
651                 local data, err, part = receive( socket, mode )    -- receive data in "mode"\r
652                 if not err or ( err == "timeout" or err == "wantread" ) then    -- received something\r
653                         local data = data or part or ""\r
654                         local count = #data * STAT_UNIT\r
655                         rstat = rstat + count\r
656                         receivestat = receivestat + count\r
657                         out_put( "server.lua: read data '", data, "', error: ", err )\r
658                         return dispatch( handler, data, err )\r
659                 else    -- connections was closed or fatal error\r
660                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
661                         handler.close( )\r
662                         disconnect( handler, err )\r
663                         writequeue = nil\r
664                         handler = nil\r
665                         return false\r
666                 end\r
667         end\r
668         \r
669         handler.dispatchdata = function( )    -- this function writes data to handlers\r
670                 local buffer = table_concat( writequeue, "", 1, eol )\r
671                 local succ, err, byte = send( socket, buffer )\r
672                 local count = ( succ or 0 ) * STAT_UNIT\r
673                 sstat = sstat + count\r
674                 sendstat = sendstat + count\r
675                 out_put( "server.lua: sended '", buffer, "', bytes: ", succ, ", error: ", err, ", part: ", byte, ", to: ", ip, ":", clientport )\r
676                 if succ then    -- sending succesful\r
677                         --writequeue = { }\r
678                         eol = nil\r
679                         writelen = removesocket( writelist, socket, writelen )    -- delete socket from writelist\r
680                         return true\r
681                 elseif byte and ( err == "timeout" or err == "wantwrite" ) then    -- want write\r
682                         buffer = string_sub( buffer, byte + 1, -1 )    -- new buffer\r
683                         writequeue[ 1 ] = buffer    -- insert new buffer in queue\r
684                         eol = 1\r
685                         return true\r
686                 else    -- connection was closed during sending or fatal error\r
687                         out_put( "server.lua: client ", ip, ":", clientport, " error: ", err )\r
688                         handler.close( )\r
689                         disconnect( handler, err )\r
690                         writequeue = nil\r
691                         handler = nil\r
692                         return false\r
693                 end\r
694         end\r
695 \r
696         -- // COMPAT // --\r
697 \r
698         handler.getIp = handler.ip\r
699         handler.getPort = handler.clientport\r
700 \r
701         socketlist[ socket ] = handler\r
702         readlen = readlen + 1\r
703         readlist[ readlen ] = socket\r
704 \r
705         return handler, socket\r
706 end\r
707 \r
708 addtimer = function( listener )\r
709         timelistener[ #timelistener + 1 ] = listener\r
710 end\r
711 \r
712 firetimer = function( listener )\r
713         for i, listener in ipairs( timelistener ) do\r
714                 listener( )\r
715         end\r
716 end\r
717 \r
718 addserver = function( listeners, port, addr, mode, sslctx )    -- this function provides a way for other scripts to reg a server\r
719         local err\r
720         if type( listeners ) ~= "table" then\r
721                 err = "invalid listener table"\r
722         else\r
723                 for name, func in pairs( listeners ) do\r
724                         if type( func ) ~= "function" then\r
725                                 --err = "invalid listener function"\r
726                                 break\r
727                         end\r
728                 end\r
729         end\r
730         if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then\r
731                 err = "invalid port"\r
732         elseif listener[ port ] then\r
733                 err=  "listeners on port '" .. port .. "' already exist"\r
734         elseif sslctx and not luasec then\r
735                 err = "luasec not found"\r
736         end\r
737         if err then\r
738                 out_error( "server.lua: ", err )\r
739                 return nil, err\r
740         end\r
741         addr = addr or "*"\r
742         local server, err = socket_bind( addr, port )\r
743         if err then\r
744                 out_error( "server.lua: ", err )\r
745                 return nil, err\r
746         end\r
747         local handler, err = wrapserver( listeners, server, addr, port, mode, sslctx )    -- wrap new server socket\r
748         if not handler then\r
749                 server:close( )\r
750                 return nil, err\r
751         end\r
752         server:settimeout( 0 )\r
753         readlen = readlen + 1\r
754         readlist[ readlen ] = server\r
755         listener[ port ] = listeners\r
756         socketlist[ server ] = handler\r
757         out_put( "server.lua: new server listener on ", addr, ":", port )\r
758         return true\r
759 end\r
760 \r
761 removesocket = function( tbl, socket, len )    -- this function removes sockets from a list\r
762         for i, target in ipairs( tbl ) do\r
763                 if target == socket then\r
764                         len = len - 1\r
765                         table_remove( tbl, i )\r
766                         return len\r
767                 end\r
768         end\r
769         return len\r
770 end\r
771 \r
772 closeall = function( )\r
773         for sock, handler in pairs( socketlist ) do\r
774                 handler.shutdown( )\r
775                 handler.close( )\r
776                 socketlist[ sock ] = nil\r
777         end\r
778         writelist, readlist, socketlist = { }, { }, { }\r
779 end\r
780 \r
781 closesocket = function( socket )\r
782         writelen = removesocket( writelist, socket, writelen )\r
783         readlen = removesocket( readlist, socket, readlen )\r
784         socketlist[ socket ] = nil\r
785         socket:close( )\r
786 end\r
787 \r
788 loop = function( )    -- this is the main loop of the program\r
789         --signal_set( "hub", "run" )\r
790         repeat\r
791                 --[[print(readlen, writelen)\r
792                 for _, s in ipairs(readlist) do print("R:", tostring(s)) end\r
793                 for _, s in ipairs(writelist) do print("W:", tostring(s)) end\r
794                 out_put("select()"..os.time())]]\r
795                 local read, write, err = socket_select( readlist, writelist, 1 )    -- 1 sec timeout, nice for timers\r
796                 for i, socket in ipairs( write ) do    -- send data waiting in writequeues\r
797                         local handler = socketlist[ socket ]\r
798                         if handler then\r
799                                 handler.dispatchdata( )\r
800                         else\r
801                                 closesocket( socket )\r
802                                 out_put "server.lua: found no handler and closed socket (writelist)"    -- this should not happen\r
803                         end\r
804                 end\r
805                 for i, socket in ipairs( read ) do    -- receive data\r
806                         local handler = socketlist[ socket ]\r
807                         if handler then\r
808                                 handler.receivedata( )\r
809                         else\r
810                                 closesocket( socket )\r
811                                 out_put "server.lua: found no handler and closed socket (readlist)"    -- this can happen\r
812                         end\r
813                 end\r
814                 firetimer( )\r
815         until false\r
816         return\r
817 end\r
818 \r
819 ----------------------------------// BEGIN //--\r
820 \r
821 ----------------------------------// PUBLIC INTERFACE //--\r
822 \r
823 return {\r
824 \r
825         add = addserver,\r
826         loop = loop,\r
827         stats = stats,\r
828         closeall = closeall,\r
829         addtimer = addtimer,\r
830         wraptlsclient = wraptlsclient,\r
831 }\r