Browse Source

* re-add some somehow missed units, update to latest

git-svn-id: trunk@5857 -
Almindor 18 years ago
parent
commit
2ddc5fed76

+ 9 - 1
.gitattributes

@@ -8136,18 +8136,26 @@ utils/fppkg/fppkg.pp svneol=native#text/plain
 utils/fppkg/fprepos.pp svneol=native#text/plain
 utils/fppkg/fpxmlrep.pp svneol=native#text/plain
 utils/fppkg/lnet/LICENSE -text
+utils/fppkg/lnet/LICENSE.ADDON -text
 utils/fppkg/lnet/fastcgi.pp svneol=native#text/plain
 utils/fppkg/lnet/lcommon.pp svneol=native#text/plain
 utils/fppkg/lnet/lcontainers.inc svneol=native#text/plain
 utils/fppkg/lnet/lcontainersh.inc svneol=native#text/plain
 utils/fppkg/lnet/lcontrolstack.pp svneol=native#text/plain
 utils/fppkg/lnet/levents.pp svneol=native#text/plain
+utils/fppkg/lnet/lfastcgi.pp svneol=native#text/plain
 utils/fppkg/lnet/lftp.pp svneol=native#text/plain
+utils/fppkg/lnet/lhttp.pp svneol=native#text/plain
+utils/fppkg/lnet/lhttputil.pp svneol=native#text/plain
+utils/fppkg/lnet/lmimetypes.pp svneol=native#text/plain
 utils/fppkg/lnet/lnet.pp svneol=native#text/plain
+utils/fppkg/lnet/lprocess.pp svneol=native#text/plain
+utils/fppkg/lnet/lsmtp.pp svneol=native#text/plain
+utils/fppkg/lnet/lspawnfcgi.pp svneol=native#text/plain
 utils/fppkg/lnet/lstrbuffer.pp svneol=native#text/plain
 utils/fppkg/lnet/ltelnet.pp svneol=native#text/plain
+utils/fppkg/lnet/ltimer.pp svneol=native#text/plain
 utils/fppkg/lnet/lwebserver.pp svneol=native#text/plain
-utils/fppkg/lnet/openssl.pp -text svneol=unset#text/plain
 utils/fppkg/lnet/sys/lepolleventer.inc svneol=native#text/plain
 utils/fppkg/lnet/sys/lepolleventerh.inc svneol=native#text/plain
 utils/fppkg/lnet/sys/lkqueueeventer.inc svneol=native#text/plain

+ 19 - 0
utils/fppkg/lnet/LICENSE.ADDON

@@ -0,0 +1,19 @@
+This is the file LICENSE.Addon, it applies to the Lighweight Network Library (lnet).
+
+The source code of the Lightweight Network library are
+distributed under the Library GNU General Public License 
+(see the file LICENSE) with the following modification:
+
+- object files and libraries linked into an application may be
+  distributed without source code.
+  
+The unit tomwinsock.pas is EXLUDED from both the GPL and this addon license.
+It is distributed under the terms of BSD license as mentioned in the file.
+I am NOT the author of tomwinsock.pas
+
+If you didn't receive a copy of the file LICENSE, contact:
+      Free Software Foundation, Inc.,
+      59 Temple Place - Suite 330
+      Boston, MA 02111
+      USA
+

+ 1 - 1
utils/fppkg/lnet/levents.pp

@@ -317,7 +317,7 @@ function TLEventer.Bail(const msg: string; const Ernum: Integer): Boolean;
 begin
   Result := False; // always false, substitute for caller's result
   if Assigned(FOnError) then
-    FOnError(msg + ': ' + LStrError(Ernum), Self);
+    FOnError(msg + '[' + IntToStr(Ernum) + ']: ' + LStrError(Ernum), Self);
 end;
 
 procedure TLEventer.AddForFree(aHandle: TLHandle);

+ 910 - 0
utils/fppkg/lnet/lfastcgi.pp

@@ -0,0 +1,910 @@
+{ FastCGI requester support for lNet
+
+  Copyright (C) 2006 Micha Nelissen
+
+  This library is Free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  This program is diStributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
+  for more details.
+
+  You should have received a Copy of the GNU Library General Public License
+  along with This library; if not, Write to the Free Software Foundation,
+  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+  
+  This license has been modified. See file LICENSE.ADDON for more information.
+  Should you find these sources without a LICENSE File, please contact
+  me at [email protected]
+}
+
+unit lfastcgi;
+
+{$mode objfpc}{$h+}
+
+interface
+
+uses
+  classes, sysutils, fastcgi, lnet, levents, lstrbuffer, ltimer;
+
+type
+  TLFastCGIClient = class;
+  TLFastCGIRequest = class;
+  TLFastCGIPool = class;
+
+  TLFastCGIRequestEvent = procedure(ARequest: TLFastCGIRequest) of object;
+
+  PLFastCGIRequest = ^TLFastCGIRequest;
+  TLFastCGIRequest = class(TObject)
+  protected
+    FID: integer;
+    FClient: TLFastCGIClient;
+    FBuffer: TStringBuffer;
+    FBufferSendPos: integer;
+    FHeader: FCGI_Header;
+    FHeaderPos: integer;
+    FContentLength: integer;
+    FInputBuffer: pchar;
+    FInputSize: integer;
+    FOutputDone: boolean;
+    FStderrDone: boolean;
+    FOutputPending: boolean;
+    FNextFree: TLFastCGIRequest;
+    FNextSend: TLFastCGIRequest;
+    FOnEndRequest: TLFastCGIRequestEvent;
+    FOnInput: TLFastCGIRequestEvent;
+    FOnOutput: TLFastCGIRequestEvent;
+    FOnStderr: TLFastCGIRequestEvent;
+
+    procedure HandleReceive;
+    procedure HandleReceiveEnd;
+    function  HandleSend: boolean;
+    procedure DoEndRequest;
+    procedure DoOutput;
+    procedure DoStderr;
+    procedure EndRequest;
+    procedure RewindBuffer;
+    procedure SetContentLength(NewLength: integer);
+    procedure SendEmptyRec(AType: integer);
+    procedure SendGetValues;
+    procedure SetID(const NewID: integer);
+  public
+    constructor Create;
+    destructor Destroy; override;
+    
+    procedure AbortRequest;
+    function  Get(ABuffer: pchar; ASize: integer): integer;
+    procedure ParseClientBuffer;
+    function  SendBuffer: integer;
+    function  SendPrivateBuffer: boolean;
+    procedure SendBeginRequest(AType: integer);
+    procedure SendParam(const AName, AValue: string; AReqType: integer = FCGI_PARAMS);
+    function  SendInput(const ABuffer: pchar; ASize: integer): integer;
+    procedure DoneParams;
+    procedure DoneInput;
+
+    property ID: integer read FID write SetID;
+    property StderrDone: boolean read FStderrDone;
+    property OutputDone: boolean read FOutputDone;
+    property OutputPending: boolean read FOutputPending;
+    property OnEndRequest: TLFastCGIRequestEvent read FOnEndRequest write FOnEndRequest;
+    property OnInput: TLFastCGIRequestEvent read FOnInput write FOnInput;
+    property OnOutput: TLFastCGIRequestEvent read FOnOutput write FOnOutput;
+    property OnStderr: TLFastCGIRequestEvent read FOnStderr write FOnStderr;
+  end;
+
+  TFastCGIClientState = (fsIdle, fsConnecting, fsConnectingAgain, 
+    fsStartingServer, fsHeader, fsData, fsFlush);
+  
+  PLFastCGIClient = ^TLFastCGIClient;
+  TLFastCGIClient = class(TLTcp)
+  protected
+    FRequests: PLFastCGIRequest;
+    FRequestsCount: integer;
+    FNextRequestID: integer;
+    FRequestsSent: integer;
+    FFreeRequest: TLFastCGIRequest;
+    FSendRequest: TLFastCGIRequest;
+    FRequest: TLFastCGIRequest;
+    FState: TFastCGIClientState;
+    FNextFree: TLFastCGIClient;
+    FPool: TLFastCGIPool;
+    FBuffer: pchar;
+    FBufferEnd: pchar;
+    FBufferPos: pchar;
+    FBufferSize: dword;
+    FReqType: byte;
+    FContentLength: integer;
+    FPaddingLength: integer;
+
+    function Connect: Boolean; override;
+    procedure ConnectEvent(ASocket: TLHandle); override;
+    procedure DisconnectEvent(ASocket: TLHandle); override;
+    procedure ErrorEvent(const Msg: string; ASocket: TLHandle); override;
+    function  CreateRequester: TLFastCGIRequest;
+    procedure HandleGetValuesResult;
+    procedure HandleReceive(ASocket: TLSocket);
+    procedure HandleSend(ASocket: TLSocket);
+    procedure ParseBuffer;
+  public
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+
+    procedure AddToSendQueue(ARequest: TLFastCGIRequest);
+    function  BeginRequest(AType: integer): TLFastCGIRequest;
+    procedure EndRequest(ARequest: TLFastCGIRequest);
+    procedure Flush;
+    function  GetBuffer(ABuffer: pchar; ASize: integer): integer;
+
+    property ReqType: byte read FReqType;
+    property RequestsSent: integer read FRequestsSent;
+  end;
+
+  TSpawnState = (ssNone, ssSpawning, ssSpawned);
+
+  TLFastCGIPool = class(TObject)
+  protected
+    FClients: PLFastCGIClient;
+    FClientsCount: integer;
+    FClientsAvail: integer;
+    FClientsMax: integer;
+    FMaxRequestsConn: integer;
+    FFreeClient: TLFastCGIClient;
+    FTimer: TLTimer;
+    FEventer: TLEventer;
+    FAppName: string;
+    FAppEnv: string;
+    FHost: string;
+    FPort: integer;
+    FSpawnState: TSpawnState;
+    
+    procedure AddToFreeClients(AClient: TLFastCGIClient);
+    function  CreateClient: TLFastCGIClient;
+    procedure ConnectClients(Sender: TObject);
+    procedure StartServer;
+  public
+    constructor Create;
+    destructor Destroy; override;
+
+    function  BeginRequest(AType: integer): TLFastCGIRequest;
+    procedure EndRequest(AClient: TLFastCGIClient);
+
+    property AppEnv: string read FAppEnv write FAppEnv;
+    property AppName: string read FAppName write FAppName;
+    property ClientsMax: integer read FClientsMax write FClientsMax;
+    property Eventer: TLEventer read FEventer write FEventer;
+    property MaxRequestsConn: integer read FMaxRequestsConn write FMaxRequestsConn;
+    property Host: string read FHost write FHost;
+    property Port: integer read FPort write FPort;
+    property Timer: TLTimer read FTimer;
+  end;
+
+implementation
+
+uses
+  lSpawnFCGI;
+
+{ TLFastCGIRequest }
+
+constructor TLFastCGIRequest.Create;
+begin
+  inherited;
+
+  FBuffer := InitStringBuffer(504);
+  FHeader.Version := FCGI_VERSION_1;
+  FHeaderPos := -1;
+end;
+
+destructor TLFastCGIRequest.Destroy;
+begin
+  inherited;
+  FreeMem(FBuffer.Memory);
+end;
+
+procedure TLFastCGIRequest.HandleReceive;
+begin
+  case FClient.ReqType of
+    FCGI_STDOUT: DoOutput;
+    FCGI_STDERR: DoStderr;
+    FCGI_END_REQUEST: EndRequest;
+    FCGI_GET_VALUES_RESULT: FClient.HandleGetValuesResult;
+  else
+    FClient.Flush;
+  end;
+end;
+
+procedure TLFastCGIRequest.HandleReceiveEnd;
+begin
+  case FClient.ReqType of
+    FCGI_STDOUT: FOutputDone := true;
+    FCGI_STDERR: FStderrDone := true;
+  end;
+end;
+
+function TLFastCGIRequest.HandleSend: boolean;
+begin
+  if FOnInput <> nil then
+    FOnInput(Self);
+  Result := FInputBuffer = nil;
+end;
+
+procedure TLFastCGIRequest.DoOutput;
+begin
+  if FOnOutput <> nil then
+    FOnOutput(Self);
+end;
+
+procedure TLFastCGIRequest.DoStderr;
+begin
+  if FOnStderr <> nil then
+    FOnStderr(Self);
+end;
+
+procedure TLFastCGIRequest.DoEndRequest;
+begin
+  if FOnEndRequest <> nil then
+    FOnEndRequest(Self);
+end;
+
+procedure TLFastCGIRequest.EndRequest;
+begin
+  FOutputDone := false;
+  FStderrDone := false;
+  FClient.EndRequest(Self);
+  FClient.Flush;
+  RewindBuffer;
+  DoEndRequest;
+end;
+
+function TLFastCGIRequest.Get(ABuffer: pchar; ASize: integer): integer;
+begin
+  Result := FClient.GetBuffer(ABuffer, ASize);
+end;
+
+procedure TLFastCGIRequest.ParseClientBuffer;
+begin
+  FOutputPending := false;
+  if (FClient.Iterator <> nil) and FClient.Iterator.IgnoreRead then
+    FClient.HandleReceive(nil)
+  else
+    FClient.ParseBuffer;
+end;
+
+procedure TLFastCGIRequest.SetID(const NewID: integer);
+begin
+  FID := NewID;
+  FHeader.RequestIDB0 := byte(NewID and $FF);
+  FHeader.RequestIDB1 := byte((NewID shr 8) and $FF);
+end;
+
+procedure TLFastCGIRequest.SetContentLength(NewLength: integer);
+begin
+  FContentLength := NewLength;
+  FHeader.ContentLengthB0 := byte(NewLength and $FF);
+  FHeader.ContentLengthB1 := byte((NewLength shr 8) and $FF);
+  FHeader.PaddingLength := byte(7-((NewLength+7) and 7));
+end;
+
+const
+  PaddingBuffer: array[0..7] of char = (#0, #0, #0, #0, #0, #0, #0, #0);
+type
+  TLFastCGIStringSize = record
+    Size: integer;
+    SizeBuf: array[0..3] of char;
+  end;
+
+function GetFastCGIStringSize(ABufferPos: pbyte; var ASize: integer): integer;
+begin
+  ASize := ABufferPos[0];
+  if ASize >= 128 then
+  begin
+    ASize := ((ABufferPos[0] shl 24) and $7f) or (ABufferPos[1] shl 16)
+      or (ABufferPos[2] shl 8) or ABufferPos[3];
+    Result := 4;
+  end else
+    Result := 1;
+end;
+  
+procedure FillFastCGIStringSize(const AStr: string; var AFastCGIStr: TLFastCGIStringSize);
+var
+  lLen: dword;
+begin
+  lLen := dword(Length(AStr));
+  if lLen > 127 then
+  begin
+    AFastCGIStr.Size := 4;
+    AFastCGIStr.SizeBuf[0] := char($80 + ((lLen shr 24) and $ff));
+    AFastCGIStr.SizeBuf[1] := char((lLen shr 16) and $ff);
+    AFastCGIStr.SizeBuf[2] := char((lLen shr 8) and $ff);
+    AFastCGIStr.SizeBuf[3] := char(lLen and $ff);
+  end else begin
+    AFastCGIStr.Size := 1;
+    AFastCGIStr.SizeBuf[0] := char(lLen);
+  end;
+end;
+
+procedure TLFastCGIRequest.SendBeginRequest(AType: integer);
+var
+  lBody: FCGI_BeginRequestBody;
+begin
+  lBody.roleB1 := byte((AType shr 8) and $ff);
+  lBody.roleB0 := byte(AType and $ff);
+  lBody.flags := FCGI_KEEP_CONN;
+  FHeader.ReqType := FCGI_BEGIN_REQUEST;
+  SetContentLength(sizeof(lBody));
+  AppendString(FBuffer, @FHeader, sizeof(FHeader));
+  AppendString(FBuffer, @lBody, sizeof(lBody));
+end;
+
+procedure TLFastCGIRequest.SendParam(const AName, AValue: string; AReqType: integer = FCGI_PARAMS);
+var
+  lNameLen: TLFastCGIStringSize;
+  lValueLen: TLFastCGIStringSize;
+  lTotalLen: integer;
+begin
+  FillFastCGIStringSize(AName, lNameLen);
+  FillFastCGIStringSize(AValue, lValueLen);
+  lTotalLen := lNameLen.Size+lValueLen.Size+Length(AName)+Length(AValue);
+  if (FHeader.ReqType = AReqType) and (FBufferSendPos = 0) 
+    and (0 <= FHeaderPos) and (FHeaderPos < FBuffer.Pos - FBuffer.Memory) then
+  begin
+    { undo padding }
+    Dec(FBuffer.Pos, FHeader.PaddingLength);
+    SetContentLength(FContentLength+lTotalLen);
+    Move(FHeader, FBuffer.Memory[FHeaderPos], sizeof(FHeader));
+  end else begin
+    FHeader.ReqType := AReqType;
+    SetContentLength(lTotalLen);
+    FHeaderPos := FBuffer.Pos - FBuffer.Memory;
+    AppendString(FBuffer, @FHeader, sizeof(FHeader));
+  end;
+  AppendString(FBuffer, @lNameLen.SizeBuf[0], lNameLen.Size);
+  AppendString(FBuffer, @lValueLen.SizeBuf[0], lValueLen.Size);
+  AppendString(FBuffer, AName);
+  AppendString(FBuffer, AValue);
+  AppendString(FBuffer, @PaddingBuffer[0], FHeader.PaddingLength);
+end;
+
+procedure TLFastCGIRequest.SendGetValues;
+var
+  lRequestID: integer;
+begin
+  { management record type has request id 0 }
+  lRequestID := ID;
+  ID := 0;
+  SendParam('FCGI_MAX_REQS', '', FCGI_GET_VALUES);
+  { if we're the first connection, ask max. # connections }
+  if FClient.FPool.FClientsAvail = 1 then
+    SendParam('FCGI_MAX_CONNS', '', FCGI_GET_VALUES);
+  ID := lRequestID;
+end;
+
+function  TLFastCGIRequest.SendInput(const ABuffer: pchar; ASize: integer): integer;
+begin
+  { first send current buffer if any }
+  if FInputBuffer <> nil then
+  begin
+    Result := SendBuffer;
+    if FInputBuffer <> nil then exit;
+  end else Result := 0;
+  if Result >= ASize then exit;
+  if FInputBuffer = nil then
+  begin
+    FInputBuffer := ABuffer+Result;
+    FInputSize := ASize-Result;
+    FHeader.ReqType := FCGI_STDIN;
+    SetContentLength(FInputSize);
+    AppendString(FBuffer, @FHeader, sizeof(FHeader));
+  end;
+  Inc(Result, SendBuffer);
+end;
+
+procedure TLFastCGIRequest.RewindBuffer;
+begin
+  FBufferSendPos := 0;
+  FHeaderPos := -1;
+  { rewind stringbuffer }
+  FBuffer.Pos := FBuffer.Memory;
+end;
+
+function TLFastCGIRequest.SendPrivateBuffer: boolean;
+var
+  lWritten: integer;
+begin
+  { nothing to send ? }
+  if FBuffer.Pos-FBuffer.Memory = FBufferSendPos then
+    exit(true);
+  { already a queue and we are not first in line ? no use in trying to send then }
+  if (FClient.FSendRequest = nil) or (FClient.FSendRequest = Self) then
+  begin
+    lWritten := FClient.Send(FBuffer.Memory[FBufferSendPos], 
+      FBuffer.Pos-FBuffer.Memory-FBufferSendPos);
+    Inc(FBufferSendPos, lWritten);
+    Result := FBufferSendPos = FBuffer.Pos-FBuffer.Memory;
+    { do not rewind buffer, unless remote side has had chance to disconnect }
+    if Result then
+      RewindBuffer;
+  end else
+    Result := false;
+  if not Result then
+    FClient.AddToSendQueue(Self);
+end;
+
+function TLFastCGIRequest.SendBuffer: integer;
+var
+  lWritten: integer;
+begin
+  { already a queue and we are not first in line ? no use in trying to send then }
+  if (FClient.FSendRequest <> nil) and (FClient.FSendRequest <> Self) then 
+    exit(0);
+
+  { header to be sent? }
+  if not SendPrivateBuffer then exit(0);
+  { first write request header, then wait for possible disconnect }
+  if FBufferSendPos > 0 then exit(0);
+  if FInputBuffer = nil then exit(0);
+
+  lWritten := FClient.Send(FInputBuffer^, FInputSize);
+  Inc(FInputBuffer, lWritten);
+  Dec(FInputSize, lWritten);
+  if FInputSize = 0 then
+  begin
+    FInputBuffer := nil;
+    AppendString(FBuffer, @PaddingBuffer[0], FHeader.PaddingLength);
+  end else
+    FClient.AddToSendQueue(Self);
+  Result := lWritten;
+end;
+
+procedure TLFastCGIRequest.SendEmptyRec(AType: integer);
+begin
+  FHeader.ReqType := AType;
+  SetContentLength(0);
+  AppendString(FBuffer, @FHeader, sizeof(FHeader));
+  { no padding needed for empty string }
+end;
+
+procedure TLFastCGIRequest.DoneParams;
+begin
+  SendEmptyRec(FCGI_PARAMS);
+end;
+
+procedure TLFastCGIRequest.DoneInput;
+begin
+  SendEmptyRec(FCGI_STDIN);
+  SendPrivateBuffer;
+end;
+
+procedure TLFastCGIRequest.AbortRequest;
+begin
+  FHeader.ReqType := FCGI_ABORT_REQUEST;
+  SetContentLength(0);
+  AppendString(FBuffer, @FHeader, sizeof(FHeader));
+  SendPrivateBuffer;
+end;
+
+{ TLFastCGIClient }
+
+const
+  DataBufferSize = 64*1024-1;
+
+constructor TLFastCGIClient.Create(AOwner: TComponent);
+begin
+  inherited;
+
+  FBuffer := GetMem(DataBufferSize+1);
+  FBufferPos := FBuffer;
+  FBufferEnd := FBuffer;
+  FRequests := AllocMem(sizeof(TLFastCGIRequest));
+  FRequestsCount := 1;
+  FFreeRequest := nil;
+  OnReceive := @HandleReceive;
+  OnCanSend := @HandleSend;
+end;
+
+destructor TLFastCGIClient.Destroy;
+var
+  I: integer;
+begin
+  for I := 0 to FNextRequestID-1 do
+    FRequests[I].Free;
+  FreeMem(FRequests);
+  FreeMem(FBuffer);
+  inherited;
+end;
+
+function TLFastCGIClient.GetBuffer(ABuffer: pchar; ASize: integer): integer;
+begin
+  Result := FBufferEnd - FBufferPos;
+  if Result > FContentLength then 
+    Result := FContentLength;
+  if Result > ASize then
+    Result := ASize;
+  Move(FBufferPos^, ABuffer^, Result);
+  Inc(FBufferPos, Result);
+  Dec(FContentLength, Result);
+  { buffer empty? reset }
+  if FBufferPos = FBufferEnd then
+  begin
+    FBufferPos := FBuffer;
+    FBufferEnd := FBuffer;
+  end;
+end;
+
+procedure TLFastCGIClient.ConnectEvent(ASocket: TLHandle);
+begin
+  if FState = fsStartingServer then
+    FPool.FSpawnState := ssSpawned;
+  FState := fsHeader;
+  if FPool <> nil then
+    FPool.AddToFreeClients(Self);
+
+  inherited;
+end;
+
+procedure TLFastCGIClient.DisconnectEvent(ASocket: TLHandle);
+var
+  I: integer;
+  needReconnect: boolean;
+begin
+  inherited;
+  FRequestsSent := 0;
+  needReconnect := false;
+  for I := 0 to FNextRequestID-1 do
+    if FRequests[I].FNextFree = nil then
+    begin
+      { see if buffer contains request, then assume we can resend that }
+      if FRequests[I].FBufferSendPos > 0 then
+      begin
+        needReconnect := true;
+        FRequests[I].FBufferSendPos := 0;
+        FRequests[I].SendPrivateBuffer;
+      end else
+      if FRequests[I].FBuffer.Memory = FRequests[I].FBuffer.Pos then
+        needReconnect := true
+      else
+        FRequests[I].EndRequest;
+    end;
+  if needReconnect then 
+    Connect;
+end;
+
+procedure TLFastCGIClient.ErrorEvent(const Msg: string; ASocket: TLHandle);
+begin
+  if (FState = fsConnectingAgain) 
+    or ((FState = fsConnecting) and (FPool.FSpawnState = ssSpawned)) then
+  begin
+    FRequest.DoEndRequest;
+    EndRequest(FRequest);
+    FState := fsIdle;
+  end else
+  if FState = fsConnecting then
+  begin
+    FPool.StartServer;
+    FState := fsStartingServer;
+  end;
+end;
+
+procedure TLFastCGIClient.HandleGetValuesResult;
+var
+  lNameLen, lValueLen, lIntVal, lCode: integer;
+  lBufferPtr: pchar;
+  lPrevChar: char;
+
+  procedure GetIntVal;
+  begin
+    lPrevChar := lBufferPtr[lNameLen+lValueLen];
+    lBufferPtr[lNameLen+lValueLen] := #0;
+    Val(lBufferPtr+lNameLen, lIntVal, lCode);
+    lBufferPtr[lNameLen+lValueLen] := lPrevChar;
+  end;
+
+begin
+  repeat
+    lBufferPtr := FBufferPos;
+    Inc(lBufferPtr, GetFastCGIStringSize(PByte(lBufferPtr), lNameLen));
+    Inc(lBufferPtr, GetFastCGIStringSize(PByte(lBufferPtr), lValueLen));
+    if lBufferPtr + lNameLen + lValueLen > FBufferEnd then exit;
+    if StrLComp(lBufferPtr, 'FCGI_MAX_REQS', lNameLen) = 0 then
+    begin
+      GetIntVal;
+      if (lCode = 0) and (FRequestsCount <> lIntVal) then
+      begin
+        FRequestsCount := lIntVal;
+        ReallocMem(FRequests, sizeof(TLFastCGIRequest)*lIntVal);
+      end;
+    end else
+    if StrLComp(lBufferPtr, 'FCGI_MAX_CONNS', lNameLen) = 0 then
+    begin
+      GetIntVal;
+      if lCode = 0 then
+        FPool.ClientsMax := lIntVal;
+    end;
+    Inc(lBufferPtr, lNameLen+lValueLen);
+    Dec(FContentLength, lBufferPtr-FBufferPos);
+    FBufferPos := lBufferPtr;
+  until FContentLength = 0;
+end;
+
+procedure TLFastCGIClient.HandleReceive(ASocket: TLSocket);
+var
+  lRead: integer;
+begin
+  lRead := Get(FBufferEnd^, DataBufferSize-PtrUInt(FBufferEnd-FBuffer));
+  if lRead = 0 then exit;
+  { remote side has had chance to disconnect, clear buffer }
+  Inc(FBufferEnd, lRead);
+  ParseBuffer;
+end;
+
+procedure TLFastCGIClient.HandleSend(ASocket: TLSocket);
+var
+  lRequest: TLFastCGIRequest;
+begin
+  if FSendRequest = nil then exit;
+  lRequest := FSendRequest.FNextSend;
+  repeat
+    if not lRequest.SendPrivateBuffer or not lRequest.HandleSend then
+      exit;
+
+    lRequest.FNextSend := nil;
+    { only this one left in list ? }
+    if FSendRequest = lRequest then
+    begin
+      FSendRequest := nil;
+      exit;
+    end else begin
+      lRequest := lRequest.FNextSend;
+      FSendRequest.FNextSend := lRequest;
+    end;
+  until false;
+end;
+
+procedure TLFastCGIClient.AddToSendQueue(ARequest: TLFastCGIRequest);
+begin
+  if ARequest.FNextSend <> nil then exit;
+
+  if FSendRequest = nil then
+    FSendRequest := ARequest
+  else
+    ARequest.FNextSend := FSendRequest.FNextSend;
+  FSendRequest.FNextSend := ARequest;
+end;
+
+procedure TLFastCGIClient.ParseBuffer;
+var
+  lHeader: PFCGI_Header;
+  lReqIndex: integer;
+begin
+  repeat
+    case FState of
+      fsHeader:
+      begin
+        if FBufferEnd-FBufferPos < sizeof(FCGI_Header) then
+          exit;
+        lHeader := PFCGI_Header(FBufferPos);
+        FReqType := lHeader^.ReqType;
+        lReqIndex := (lHeader^.RequestIDB1 shl 8) or lHeader^.RequestIDB0;
+        FContentLength := (lHeader^.ContentLengthB1 shl 8) or lHeader^.ContentLengthB0;
+        FPaddingLength := lHeader^.PaddingLength;
+        Inc(FBufferPos, sizeof(lHeader^));
+        if lReqIndex > 0 then
+          Dec(lReqIndex);
+        if (lReqIndex < FRequestsCount) and (FRequests[lReqIndex] <> nil) then
+        begin
+          FRequest := FRequests[lReqIndex];
+          if FContentLength > 0 then
+            FState := fsData
+          else begin
+            FRequest.HandleReceiveEnd;
+            Flush;
+          end;
+        end else
+          Flush;
+      end;
+      fsData: 
+      begin
+        FRequest.HandleReceive;
+        if FContentLength = 0 then 
+          Flush
+        else begin
+          FRequest.FOutputPending := true;
+          exit;
+        end;
+      end;
+      fsFlush: Flush;
+    end;
+  until FBufferPos = FBufferEnd;
+end;
+
+procedure TLFastCGIClient.Flush;
+
+  function FlushSize(var ANumBytes: integer): boolean;
+  var
+    lFlushBytes: integer;
+  begin
+    lFlushBytes := ANumBytes;
+    if lFlushBytes > FBufferEnd - FBufferPos then
+      lFlushBytes := FBufferEnd - FBufferPos;
+    Dec(ANumBytes, lFlushBytes);
+    Inc(FBufferPos, lFlushBytes);
+    Result := ANumBytes = 0;
+  end;
+
+begin
+  FState := fsFlush;
+  if FlushSize(FContentLength) and FlushSize(FPaddingLength) then
+  begin
+    { buffer empty? reset }
+    if FBufferPos = FBufferEnd then
+    begin
+      FBufferPos := FBuffer;
+      FBufferEnd := FBuffer;
+    end;
+    FState := fsHeader;
+    FRequest := nil;
+  end;
+end;
+
+function TLFastCGIClient.CreateRequester: TLFastCGIRequest;
+begin
+  if FRequests[FNextRequestID] = nil then
+    FRequests[FNextRequestID] := TLFastCGIRequest.Create;
+  Result := FRequests[FNextRequestID];
+  Inc(FNextRequestID);
+  Result.FClient := Self;
+  Result.ID := FNextRequestID;  { request ids start at 1 }
+end;
+
+function TLFastCGIClient.Connect: Boolean;
+begin
+  Result := inherited Connect(FPool.Host, FPool.Port);
+  FRequest := FRequests[0];
+  if FRequest.FBuffer.Pos = FRequest.FBuffer.Memory then
+    FRequest.SendGetValues;
+  if FState <> fsStartingServer then
+    FState := fsConnecting
+  else
+    FState := fsConnectingAgain;
+end;
+
+function TLFastCGIClient.BeginRequest(AType: integer): TLFastCGIRequest;
+begin
+  if FFreeRequest <> nil then
+  begin
+    Result := FFreeRequest.FNextFree;
+    if FFreeRequest = FFreeRequest.FNextFree then
+      FFreeRequest := nil
+    else
+      FFreeRequest.FNextFree := FFreeRequest.FNextFree.FNextFree;
+    Result.FNextFree := nil;
+  end else
+  if FNextRequestID = FRequestsCount then
+    exit(nil)
+  else begin
+    Result := CreateRequester;
+  end;
+
+  if not Connected then
+    Connect;
+
+  Result.SendBeginRequest(AType);
+  Inc(FRequestsSent);
+end;
+
+procedure TLFastCGIClient.EndRequest(ARequest: TLFastCGIRequest);
+begin
+  if FFreeRequest <> nil then
+    ARequest.FNextFree := FFreeRequest.FNextFree
+  else
+    FFreeRequest := ARequest;
+  FFreeRequest.FNextFree := ARequest;
+  if FPool <> nil then
+    FPool.EndRequest(Self);
+end;
+   
+{ TLFastCGIPool }
+
+constructor TLFastCGIPool.Create;
+begin
+  FClientsMax := 1;
+  FMaxRequestsConn := 1;
+  inherited;
+end;
+
+destructor TLFastCGIPool.Destroy;
+var
+  I: integer;
+begin
+  for I := 0 to FClientsAvail-1 do
+    FClients[I].Free;
+  FreeMem(FClients);
+  if FTimer <> nil then
+    FTimer.Free;
+  inherited;
+end;
+
+function  TLFastCGIPool.CreateClient: TLFastCGIClient;
+begin
+  if FClientsAvail = FClientsCount then
+  begin
+    Inc(FClientsCount, 64);
+    ReallocMem(FClients, FClientsCount*sizeof(TLFastCGIRequest));
+  end;
+  Result := TLFastCGIClient.Create(nil);
+  Result.FPool := Self;
+  Result.Eventer := FEventer;
+  FClients[FClientsAvail] := Result;
+  Inc(FClientsAvail);
+end;
+
+function  TLFastCGIPool.BeginRequest(AType: integer): TLFastCGIRequest;
+var
+  lTempClient: TLFastCGIClient;
+begin
+  Result := nil;
+  while FFreeClient <> nil do
+  begin
+    lTempClient := FFreeClient.FNextFree;
+    Result := lTempClient.BeginRequest(AType);
+    if Result <> nil then break;
+    { Result = nil -> no free requesters on next free client }
+    if lTempClient = FFreeClient then
+      FFreeClient := nil
+    else
+      FFreeClient.FNextFree := lTempClient.FNextFree;
+    lTempClient.FNextFree := nil;
+  end;
+
+  { all clients busy }
+  if Result = nil then
+    if FClientsAvail < FClientsMax then
+      Result := CreateClient.BeginRequest(AType);
+end;
+
+procedure TLFastCGIPool.EndRequest(AClient: TLFastCGIClient);
+begin
+  { TODO: wait for other requests to be completed }
+  if AClient.RequestsSent = FMaxRequestsConn then
+    AClient.Disconnect;
+  AddToFreeClients(AClient);
+end;
+
+procedure TLFastCGIPool.AddToFreeClients(AClient: TLFastCGIClient);
+begin
+  if AClient.FNextFree <> nil then exit;
+  
+  if FFreeClient = nil then
+    FFreeClient := AClient
+  else
+    AClient.FNextFree := FFreeClient.FNextFree;
+  FFreeClient.FNextFree := AClient;
+end;
+
+procedure TLFastCGIPool.ConnectClients(Sender: TObject);
+var
+  I: integer;
+begin
+  for I := 0 to FClientsAvail-1 do
+    if FClients[I].FState = fsStartingServer then
+      FClients[I].Connect;
+end;
+
+procedure TLFastCGIPool.StartServer;
+begin
+  if FSpawnState = ssNone then
+  begin
+    FSpawnState := ssSpawning;
+    SpawnFCGIProcess(FAppName, FAppEnv, FPort);
+    if FTimer = nil then
+      FTimer := TLTimer.Create;
+    FTimer.OneShot := true;
+    FTimer.OnTimer := @ConnectClients;
+  end;
+  FTimer.Interval := 2000;
+end;
+
+end.

+ 50 - 10
utils/fppkg/lnet/lftp.pp

@@ -35,9 +35,9 @@ type
   TLFTP = class;
   TLFTPClient = class;
 
-  TLFTPStatus = (fsNone, fsCon, fsAuth, fsPasv, fsPort, fsList, fsRetr, fsStor,
-                 fsType, fsCWD, fsMKD, fsRMD, fsDEL, fsRNFR, fsRNTO, fsSYS,
-                 fsFeat, fsPWD, fsHelp, fsLast);
+  TLFTPStatus = (fsNone, fsCon, fsAuth, fsPass, fsPasv, fsPort, fsList, fsRetr,
+                 fsStor, fsType, fsCWD, fsMKD, fsRMD, fsDEL, fsRNFR, fsRNTO,
+                 fsSYS, fsFeat, fsPWD, fsHelp, fsLast);
                  
   TLFTPStatusSet = set of TLFTPStatus;
                  
@@ -131,6 +131,7 @@ type
     procedure OnControlEr(const msg: string; aSocket: TLSocket);
     procedure OnControlRe(aSocket: TLSocket);
     procedure OnControlCo(aSocket: TLSocket);
+    procedure OnControlDs(aSocket: TLSocket);
     
     function GetTransfer: Boolean;
 
@@ -170,6 +171,8 @@ type
     
     function Authenticate(const aUsername, aPassword: string): Boolean;
     
+    function SendPassword(const aPassword: string): Boolean;
+
     function GetData(var aData; const aSize: Integer): Integer;
     function GetDataMessage: string;
     
@@ -211,6 +214,8 @@ type
     property OnFailure: TLFTPClientStatusEvent read FOnFailure write FOnFailure;
   end;
   
+  function FTPStatusToStr(const aStatus: TLFTPStatus): string;
+  
 implementation
 
 uses
@@ -222,7 +227,7 @@ const
 
   EMPTY_REC: TLFTPStatusRec = (Status: fsNone; Args: ('', ''));
 
-  FTPStatusStr: array[TLFTPStatus] of string = ('None', 'Connect', 'Authenticate',
+  FTPStatusStr: array[TLFTPStatus] of string = ('None', 'Connect', 'Authenticate', 'Password',
                                                 'Passive', 'Active', 'List', 'Retrieve',
                                                 'Store', 'Type', 'CWD', 'MKDIR',
                                                 'RMDIR', 'Delete', 'RenameFrom',
@@ -258,13 +263,18 @@ begin
   Result.Args[2] := Arg2;
 end;
 
+function FTPStatusToStr(const aStatus: TLFTPStatus): string;
+begin
+  Result := FTPStatusStr[aStatus];
+end;
+
 {$i lcontainers.inc}
 
 { TLFTP }
 
 function TLFTP.GetConnected: Boolean;
 begin
-  Result  :=  FControl.Connected;
+  Result := FControl.Connected;
 end;
 
 function TLFTP.GetTimeout: DWord;
@@ -331,6 +341,7 @@ begin
   FControl.OnReceive := @OnControlRe;
   FControl.OnConnect := @OnControlCo;
   FControl.OnError := @OnControlEr;
+  FControl.OnDisconnect := @OnControlDs;
 
   FData.OnReceive := @OnRe;
   FData.OnDisconnect := @OnDs;
@@ -372,7 +383,6 @@ end;
 
 procedure TLFTPClient.OnDs(aSocket: TLSocket);
 begin
-  // TODO: figure it out brainiac
   FSending := False;
   Writedbg(['Disconnected']);
 end;
@@ -409,6 +419,12 @@ begin
     FOnConnect(aSocket);
 end;
 
+procedure TLFTPClient.OnControlDs(aSocket: TLSocket);
+begin
+  if Assigned(FOnError) then
+    FOnError('Connection lost', aSocket);
+end;
+
 function TLFTPClient.GetTransfer: Boolean;
 begin
   Result := FData.Connected;
@@ -586,10 +602,22 @@ begin
                        FStatus.Remove;
                      end;
                    331,
-                   332: begin
-                          FStatusFlags[FStatus.First.Status] := False;
-                          FControl.SendMessage('PASS ' + FPassword + FLE);
-                        end;
+                   332: SendPassword(FPassword);
+                   else
+                     begin
+                       FStatusFlags[FStatus.First.Status] := False;
+                       Eventize(FStatus.First.Status, False);
+                       FStatus.Remove;
+                     end;
+                 end;
+                 
+        fsPass : case x of
+                   230:
+                     begin
+                       FStatusFlags[FStatus.First.Status] := True;
+                       Eventize(FStatus.First.Status, True);
+                       FStatus.Remove;
+                     end;
                    else
                      begin
                        FStatusFlags[FStatus.First.Status] := False;
@@ -819,6 +847,7 @@ begin
     case Status of
       fsNone : Exit;
       fsAuth : Authenticate(Args[1], Args[2]);
+      fsPass : SendPassword(Args[1]);
       fsList : List(Args[1]);
       fsRetr : Retrieve(Args[1]);
       fsStor : Put(Args[1]);
@@ -909,6 +938,17 @@ begin
   end;
 end;
 
+function TLFTPClient.SendPassword(const aPassword: string): Boolean;
+begin
+  Result := not FPipeLine;
+  if CanContinue(fsPass, aPassword, '') then begin
+    FControl.SendMessage('PASS ' + aPassword + FLE);
+    FStatus.Insert(MakeStatusRec(fsPass, '', ''));
+    Result := True;
+  end;
+end;
+
+
 function TLFTPClient.Retrieve(const FileName: string): Boolean;
 begin
   Result := not FPipeLine;

+ 2264 - 0
utils/fppkg/lnet/lhttp.pp

@@ -0,0 +1,2264 @@
+{ HTTP server and client components
+
+  Copyright (C) 2006 Micha Nelissen
+
+  This library is Free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
+  for more details.
+
+  You should have received a Copy of the GNU Library General Public License
+  along with This library; if not, Write to the Free Software Foundation,
+  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+  
+  This license has been modified. See file LICENSE.ADDON for more information.
+  Should you find these sources without a LICENSE File, please contact
+  me at [email protected]
+}
+
+unit lhttp;
+
+{$mode objfpc}{$h+}
+{$inline on}
+
+interface
+
+uses
+  classes, sysutils, lnet, levents, lhttputil, lstrbuffer;
+
+type
+  TLHTTPMethod = (hmHead, hmGet, hmPost, hmUnknown);
+  TLHTTPParameter = (hpConnection, hpContentLength, hpContentType,
+    hpAccept, hpAcceptCharset, hpAcceptEncoding, hpAcceptLanguage, hpHost,
+    hpFrom, hpReferer, hpUserAgent, hpRange, hpTransferEncoding,
+    hpIfModifiedSince, hpIfUnmodifiedSince, hpCookie);
+  TLHTTPStatus = (hsUnknown, hsOK, hsNoContent, hsMovedPermanently, hsFound, hsNotModified, 
+    hsBadRequest, hsForbidden, hsNotFound, hsPreconditionFailed, hsRequestTooLong,
+    hsInternalError, hsNotImplemented, hsNotAllowed);
+  TLHTTPTransferEncoding = (teIdentity, teChunked);
+  TLHTTPClientError = (ceNone, ceMalformedStatusLine, ceVersionNotSupported,
+    ceUnsupportedEncoding);
+
+const
+  HTTPDisconnectStatuses = [hsBadRequest, hsRequestTooLong, hsForbidden, 
+    hsInternalError, hsNotAllowed];
+  HTTPMethodStrings: array[TLHTTPMethod] of string =
+    ('HEAD', 'GET', 'POST', '');
+  HTTPParameterStrings: array[TLHTTPParameter] of string =
+    ('CONNECTION', 'CONTENT-LENGTH', 'CONTENT-TYPE', 'ACCEPT', 
+     'ACCEPT-CHARSET', 'ACCEPT-ENCODING', 'ACCEPT-LANGUAGE', 'HOST',
+     'FROM', 'REFERER', 'USER-AGENT', 'RANGE', 'TRANSFER-ENCODING',
+     'IF-MODIFIED-SINCE', 'IF-UNMODIFIED-SINCE', 'COOKIE');
+  HTTPStatusCodes: array[TLHTTPStatus] of dword =
+    (0, 200, 204, 301, 302, 304, 400, 403, 404, 412, 414, 500, 501, 504);
+  HTTPTexts: array[TLHTTPStatus] of string = 
+    ('', 'OK', 'No Content', 'Moved Permanently', 'Found', 'Not Modified', 'Bad Request', 'Forbidden', 
+     'Not Found', 'Precondition Failed', 'Request Too Long', 'Internal Error',
+     'Method Not Implemented', 'Method Not Allowed');
+  HTTPDescriptions: array[TLHTTPStatus] of string = (
+      { hsUnknown }
+    '',
+      { hsOK }
+    '',
+      { hsNoContent }
+    '',
+      { hsMovedPermanently }
+    '',
+      { hsFound }
+    '',
+      { hsNotModified }
+    '',
+      { hsBadRequest }
+    '<html><head><title>400 Bad Request</title></head><body>'+#10+
+    '<h1>Bad Request</h1>'+#10+
+    '<p>Your browser did a request this server did not understand.</p>'+#10+
+    '</body></html>'+#10,
+      { hsForbidden }
+    '<html><head><title>403 Forbidden</title></head><body>'+#10+
+    '<h1>Forbidden</h1>'+#10+
+    '<p>You do not have permission to access this resource.</p>'+#10+
+    '</body></html>'+#10,
+      { hsNotFound }
+    '<html><head><title>404 Not Found</title></head><body>'+#10+
+    '<h1>Not Found</h1>'+#10+
+    '<p>The requested URL was not found on this server.</p>'+#10+
+    '</body></html>'+#10,
+      { hsPreconditionFailed }
+    '<html><head><title>412 Precondition Failed</title></head><body>'+#10+
+    '<h1>Precondition Failed</h1>'+#10+
+    '<p>The precondition on the request evaluated to false.</p>'+#10+
+    '</body></html>'+#10,
+      { hsRequestTooLong }
+    '<html><head><title>414 Request Too Long</title></head><body>'+#10+
+    '<h1>Bad Request</h1>'+#10+
+    '<p>Your browser did a request that was too long for this server to parse.</p>'+#10+
+    '</body></html>'+#10,
+      { hsInternalError }
+    '<html><head><title>500 Internal Error</title></head><body>'+#10+
+    '<h1>Internal Error</h1>'+#10+
+    '<p>An error occurred while generating the content for this request.</p>'+#10+
+    '</body></html>'+#10,
+      { hsNotImplemented }
+    '<html><head><title>501 Method Not Implemented</title></head><body>'+#10+
+    '<h1>Method Not Implemented</h1>'+#10+
+    '<p>The method used in the request is invalid.</p>'+#10+
+    '</body></html>'+#10,
+      { hsNotAllowed }
+    '<html><head><title>504 Method Not Allowed</title></head><body>'+#10+
+    '<h1>Method Not Allowed</h1>'+#10+
+    '<p>The method used in the request is not allowed on the resource specified in the URL.</p>'+#10+
+    '</body></html>'+#10);
+
+
+type
+  TLHTTPSocket = class;
+  TLHTTPConnection = class;
+  TLHTTPClientSocket = class;
+  
+  PRequestInfo = ^TRequestInfo;
+  TRequestInfo = record
+    RequestType: TLHTTPMethod;
+    DateTime: TDateTime;
+    Method: pchar;
+    Argument: pchar;
+    QueryParams: pchar;
+    VersionStr: pchar;
+    Version: dword;
+  end;
+
+  PClientRequest = ^TClientRequest;
+  TClientRequest = record
+    Method: TLHTTPMethod;
+    URI: string;
+    QueryParams: string;
+    RangeStart: qword;
+    RangeEnd: qword;
+  end;
+
+  PClientResponse = ^TClientResponse;
+  TClientResponse = record
+    Status: TLHTTPStatus;
+    Version: dword;
+    Reason: string;
+  end;
+
+  PHeaderOutInfo = ^THeaderOutInfo;
+  THeaderOutInfo = record
+    ContentLength: integer;
+    TransferEncoding: TLHTTPTransferEncoding;
+    ExtraHeaders: TStringBuffer;
+    Version: dword;
+  end;
+
+  PResponseInfo = ^TResponseInfo;
+  TResponseInfo = record
+    Status: TLHTTPStatus;
+    ContentType: string;
+    ContentCharset: string;
+    LastModified: TDateTime;
+  end;
+
+  TWriteBlockStatus = (wsPendingData, wsWaitingData, wsDone);
+  TWriteBlockMethod = function: TWriteBlockStatus of object;
+
+  TOutputItem = class(TObject)
+  protected
+    FBuffer: pchar;
+    FBufferPos: integer;
+    FBufferSize: integer;
+    FBufferOffset: integer;
+    FOutputPending: boolean;
+    FEof: boolean;
+    FPrev: TOutputItem;
+    FNext: TOutputItem;
+    FPrevDelayFree: TOutputItem;
+    FNextDelayFree: TOutputItem;
+    FSocket: TLHTTPSocket;
+    FWriteBlock: TWriteBlockMethod;
+
+    procedure DoneInput; virtual;
+    function  HandleInput(ABuffer: pchar; ASize: integer): integer; virtual;
+    function  WriteBlock: TWriteBlockStatus; virtual;
+  public
+    constructor Create(ASocket: TLHTTPSocket);
+    destructor Destroy; override;
+
+    procedure LogError(const AMessage: string);
+
+    property Socket: TLHTTPSocket read FSocket;
+  end;
+
+  TProcMethod = procedure of object;
+
+  TBufferOutput = class(TOutputItem)
+  protected
+    FPrepareBuffer: TProcMethod;
+    FFinishBuffer: TProcMethod;
+    FBufferMemSize: integer;
+
+    procedure PrepareBuffer;
+    procedure PrepareChunk;
+    procedure FinishBuffer;
+    procedure FinishChunk;
+    procedure SelectChunked;
+    procedure SelectBuffered;
+    procedure SelectPlain;
+    procedure PrependBufferOutput(MinBufferSize: integer);
+    procedure PrependStreamOutput(AStream: TStream; AFree: boolean);
+    function FillBuffer: TWriteBlockStatus; virtual; abstract;
+    function WriteChunk: TWriteBlockStatus;
+    function WriteBuffer: TWriteBlockStatus;
+    function WritePlain: TWriteBlockStatus;
+    function WriteBlock: TWriteBlockStatus; override;
+  public
+    constructor Create(ASocket: TLHTTPSocket);
+    destructor Destroy; override;
+
+    procedure Add(ABuf: pointer; ASize: integer);
+    procedure Add(const AStr: string);
+    procedure Add(AStream: TStream; AQueue: boolean = false; AFree: boolean = true);
+  end;
+
+  TMemoryOutput = class(TOutputItem)
+  protected
+    FFreeBuffer: boolean;
+  public
+    constructor Create(ASocket: TLHTTPSocket; ABuffer: pointer; 
+      ABufferOffset, ABufferSize: integer; AFreeBuffer: boolean);
+    destructor Destroy; override;
+  end;
+
+  TStreamOutput = class(TBufferOutput)
+  protected
+    FStream: TStream;
+    FFreeStream: boolean;
+    FStreamSize: integer;
+
+    function FillBuffer: TWriteBlockStatus; override;
+  public
+    constructor Create(ASocket: TLHTTPSocket; AStream: TStream; AFreeStream: boolean);
+    destructor Destroy; override;
+  end;
+  
+  TMemoryStreamOutput = class(TOutputItem)
+  protected
+    FFreeStream: boolean;
+    FStream: TMemoryStream;
+
+    function WriteBlock: TWriteBlockStatus; override;
+  public
+    constructor Create(ASocket: TLHTTPSocket; AStream: TMemoryStream; AFreeStream: boolean);
+    destructor Destroy; override;
+  end;
+
+  TChunkState = (csInitial, csData, csDataEnd, csTrailer, csFinished);
+  TLHTTPParameterArray = array[TLHTTPParameter] of pchar;
+  
+  TParseBufferMethod = function: boolean of object;
+  TLInputEvent = function(ASocket: TLHTTPClientSocket; ABuffer: pchar; ASize: integer): integer of object;
+  TLCanWriteEvent = procedure(ASocket: TLHTTPClientSocket; var OutputEof: TWriteBlockStatus) of object;
+  TLHTTPClientEvent = procedure(ASocket: TLHTTPClientSocket) of object;
+
+  TLHTTPConnection = class(TLTcp)
+  protected
+    procedure CanSendEvent(aSocket: TLHandle); override;
+    procedure LogAccess(const AMessage: string); virtual;
+    procedure ReceiveEvent(aSocket: TLHandle); override;
+  public
+    destructor Destroy; override;
+  end;
+
+  TLHTTPSocket = class(TLSocket)
+  protected
+    FBuffer: pchar;
+    FBufferPos: pchar;
+    FBufferEnd: pchar;
+    FBufferSize: integer;
+    FRequestBuffer: pchar;
+    FRequestPos: pchar;
+    FRequestInputDone: boolean;
+    FRequestHeaderDone: boolean;
+    FOutputDone: boolean;
+    FInputRemaining: integer;
+    FChunkState: TChunkState;
+    FCurrentInput: TOutputItem;
+    FCurrentOutput: TOutputItem;
+    FLastOutput: TOutputItem;
+    FKeepAlive: boolean;
+    FParseBuffer: TParseBufferMethod;
+    FParameters: TLHTTPParameterArray;
+    FDelayFreeItems: TOutputItem;
+
+    procedure AddContentLength(ALength: integer); virtual; abstract;
+    function  CalcAvailableBufferSpace: integer;
+    procedure DelayFree(AOutputItem: TOutputItem);
+    procedure Disconnect; override;
+    procedure DoneBuffer(AOutput: TBufferOutput); virtual;
+    procedure FreeDelayFreeItems;
+    procedure LogAccess(const AMessage: string); virtual;
+    procedure LogMessage; virtual;
+    procedure FlushRequest; virtual;
+    procedure PackRequestBuffer;
+    procedure PackInputBuffer;
+    function  ParseRequest: boolean;
+    function  ParseEntityPlain: boolean;
+    function  ParseEntityChunked: boolean;
+    procedure ParseLine(pLineEnd: pchar); virtual;
+    procedure ParseParameterLine(pLineEnd: pchar);
+    function  ProcessEncoding: boolean;
+    procedure ProcessHeaders; virtual; abstract;
+    procedure RelocateVariable(var AVar: pchar);
+    procedure RelocateVariables; virtual;
+    procedure ResetDefaults; virtual;
+    function  SetupEncoding(AOutputItem: TBufferOutput; AHeaderOut: PHeaderOutInfo): boolean;
+    procedure WriteError(AStatus: TLHTTPStatus); virtual;
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+
+    procedure AddToOutput(AOutputItem: TOutputItem);
+    procedure PrependOutput(ANewItem, AItem: TOutputItem);
+    procedure RemoveOutput(AOutputItem: TOutputItem);
+    procedure HandleReceive;
+    function  ParseBuffer: boolean;
+    procedure WriteBlock;
+    
+    property Parameters: TLHTTPParameterArray read FParameters;
+  end;
+
+  { http server }
+
+  TSetupEncodingState = (seNone, seWaitHeaders, seStartHeaders);
+  
+  TLHTTPServerSocket = class(TLHTTPSocket)
+  protected
+    FLogMessage: TStringBuffer;
+    FRequestInfo: TRequestInfo;
+    FResponseInfo: TResponseInfo;
+    FHeaderOut: THeaderOutInfo;
+    FSetupEncodingState: TSetupEncodingState;
+
+    procedure AddContentLength(ALength: integer); override;
+    procedure DoneBuffer(AOutput: TBufferOutput); override;
+    procedure FlushRequest; override;
+    function  HandleURI: TOutputItem; virtual;
+    procedure LogAccess(const AMessage: string); override;
+    procedure LogMessage; override;
+    procedure RelocateVariables; override;
+    procedure ResetDefaults; override;
+    procedure ParseLine(pLineEnd: pchar); override;
+    procedure ParseRequestLine(pLineEnd: pchar);
+    function  PrepareResponse(AOutputItem: TOutputItem; ACustomErrorMessage: boolean): boolean;
+    procedure ProcessHeaders; override;
+    procedure WriteError(AStatus: TLHTTPStatus); override;
+    procedure WriteHeaders(AHeaderResponse, ADataResponse: TOutputItem);
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+
+    function  SetupEncoding(AOutputItem: TBufferOutput): boolean;
+    procedure StartMemoryResponse(AOutputItem: TMemoryOutput; ACustomErrorMessage: boolean = false);
+    procedure StartResponse(AOutputItem: TBufferOutput; ACustomErrorMessage: boolean = false);
+
+    property HeaderOut: THeaderOutInfo read FHeaderOut;
+    property RequestInfo: TRequestInfo read FRequestInfo;
+    property ResponseInfo: TResponseInfo read FResponseInfo;
+  end;
+  
+  TURIHandler = class(TObject)
+  private
+    FNext: TURIHandler;
+  protected
+    function HandleURI(ASocket: TLHTTPServerSocket): TOutputItem; virtual; abstract;
+    procedure RegisterWithEventer(AEventer: TLEventer); virtual;
+  end;
+
+  TLAccessEvent = procedure(AMessage: string) of object;
+
+  TLHTTPServer = class(TLHTTPConnection)
+  protected
+    FHandlerList: TURIHandler;
+    FLogMessageTZString: string;
+    FServerSoftware: string;
+    FOnAccess: TLAccessEvent;
+
+    function HandleURI(ASocket: TLHTTPServerSocket): TOutputItem;
+  protected
+    procedure LogAccess(const AMessage: string); override;
+    procedure RegisterWithEventer; override;
+  public
+    constructor Create(AOwner: TComponent); override;
+
+    procedure RegisterHandler(AHandler: TURIHandler);
+
+    property ServerSoftware: string read FServerSoftware write FServerSoftware;
+    property OnAccess: TLAccessEvent read FOnAccess write FOnAccess;
+  end;
+
+  { http client }
+
+  TLHTTPClientSocket = class(TLHTTPSocket)
+  protected
+    FRequest: PClientRequest;
+    FResponse: PClientResponse;
+    FHeaderOut: PHeaderOutInfo;
+    FError: TLHTTPClientError;
+    
+    procedure AddContentLength(ALength: integer); override;
+    function  GetResponseReason: string;
+    function  GetResponseStatus: TLHTTPStatus;
+    procedure Cancel(AError: TLHTTPClientError);
+    procedure ParseLine(pLineEnd: pchar); override;
+    procedure ParseStatusLine(pLineEnd: pchar);
+    procedure ProcessHeaders; override;
+    procedure ResetDefaults; override;
+  public
+    constructor Create; override;
+    destructor Destroy; override;
+
+    procedure SendRequest;
+
+    property Error: TLHTTPClientError read FError write FError;
+    property Response: PClientResponse read FResponse;
+    property ResponseReason: string read GetResponseReason;
+    property ResponseStatus: TLHTTPStatus read GetResponseStatus;
+  end;
+
+  TLHTTPClientState = (hcsIdle, hcsWaiting, hcsReceiving);
+
+  TLHTTPClient = class(TLHTTPConnection)
+  protected
+    FRequest: TClientRequest;
+    FResponse: TClientResponse;
+    FHeaderOut: THeaderOutInfo;
+    FState: TLHTTPClientState;
+    FPendingResponses: integer;
+    FOutputEof: boolean;
+    FOnCanWrite: TLCanWriteEvent;
+    FOnDoneInput: TLHTTPClientEvent;
+    FOnInput: TLInputEvent;
+    FOnProcessHeaders: TLHTTPClientEvent;
+    
+    procedure ConnectEvent(aSocket: TLHandle); override;
+    procedure DoDoneInput(ASocket: TLHTTPClientSocket);
+    function  DoHandleInput(ASocket: TLHTTPClientSocket; ABuffer: pchar; ASize: integer): integer;
+    procedure DoProcessHeaders(ASocket: TLHTTPClientSocket);
+    function  DoWriteBlock(ASocket: TLHTTPClientSocket): TWriteBlockStatus;
+    function  InitSocket(aSocket: TLSocket): TLSocket; override;
+    procedure InternalSendRequest;
+  public
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+
+    procedure AddExtraHeader(const AHeader: string);
+    procedure ResetRange;
+    procedure SendRequest;
+
+    property ContentLength: integer read FHeaderOut.ContentLength write FHeaderOut.ContentLength;
+    property Method: TLHTTPMethod read FRequest.Method write FRequest.Method;
+    property PendingResponses: integer read FPendingResponses;
+    property RangeStart: qword read FRequest.RangeStart write FRequest.RangeStart;
+    property RangeEnd: qword read FRequest.RangeEnd write FRequest.RangeEnd;
+    property Request: TClientRequest read FRequest;
+    property State: TLHTTPClientState read FState;
+    property URI: string read FRequest.URI write FRequest.URI;
+    property Response: TClientResponse read FResponse;
+    property OnCanWrite: TLCanWriteEvent read FOnCanWrite write FOnCanWrite;
+    property OnDoneInput: TLHTTPClientEvent read FOnDoneInput write FOnDoneInput;
+    property OnInput: TLInputEvent read FOnInput write FOnInput;
+    property OnProcessHeaders: TLHTTPClientEvent read FOnProcessHeaders write FOnProcessHeaders;
+  end;
+
+implementation
+
+uses
+  lCommon;
+
+const
+  RequestBufferSize = 1024;
+  DataBufferSize = 16*1024;
+
+  BufferEmptyToWriteStatus: array[boolean] of TWriteBlockStatus =
+    (wsPendingData, wsDone);
+  EofToWriteStatus: array[boolean] of TWriteBlockStatus =
+    (wsWaitingData, wsDone);
+
+{ helper functions }
+
+function TrySingleDigit(ADigit: char; out OutDigit: byte): boolean;
+begin
+  Result := (ord(ADigit) >= ord('0')) and (ord(ADigit) <= ord('9'));
+  if not Result then exit;
+  OutDigit := ord(ADigit) - ord('0');
+end;
+
+function HTTPVersionCheck(AStr, AStrEnd: pchar; out AVersion: dword): boolean;
+var
+  lMajorVersion, lMinorVersion: byte;
+begin
+  Result := ((AStrEnd-AStr) = 8) 
+    and CompareMem(AStr, pchar('HTTP/'), 5)
+    and TrySingleDigit(AStr[5], lMajorVersion) 
+    and (AStr[6] = '.')
+    and TrySingleDigit(AStr[7], lMinorVersion);
+  AVersion := lMajorVersion * 10 + lMinorVersion;
+end;
+
+function CodeToHTTPStatus(ACode: dword): TLHTTPStatus;
+begin
+  for Result := Low(TLHTTPStatus) to High(TLHTTPStatus) do
+    if HTTPStatusCodes[Result] = ACode then exit;
+  Result := hsUnknown;
+end;
+
+const
+   HexDigits: array[0..15] of char = '0123456789ABCDEF';
+
+function HexReverse(AValue: dword; ABuffer: pchar): integer;
+begin
+  Result := 0;
+  repeat
+    ABuffer^ := HexDigits[AValue and $F];
+    AValue := AValue shr 4;
+    Dec(ABuffer);
+    Inc(Result);
+  until AValue = 0;
+end;
+
+procedure HexToInt(ABuffer: pchar; out AValue: dword; out ACode: integer);
+var
+  Val, Incr: dword;
+  Start: pchar;
+begin
+  Val := 0;
+  ACode := 0;
+  Start := ABuffer;
+  while ABuffer^ <> #0 do
+  begin
+    if (ABuffer^ >= '0') and (ABuffer^ <= '9') then
+      Incr := ord(ABuffer^) - ord('0')
+    else if (ABuffer^ >= 'A') and (ABuffer^ <= 'F') then
+      Incr := ord(ABuffer^) - ord('A') + 10
+    else if (ABuffer^ >= 'a') and (ABuffer^ <= 'f') then
+      Incr := ord(ABuffer^) - ord('a') + 10
+    else begin
+      ACode := ABuffer - Start + 1;
+      break;
+    end;
+    Val := (Val shl 4) + Incr;
+    Inc(ABuffer);
+  end;
+  AValue := Val;
+end;
+
+{ TURIHandler }
+
+procedure TURIHandler.RegisterWithEventer(AEventer: TLEventer);
+begin
+end;
+
+{ TOutputItem }
+
+constructor TOutputItem.Create(ASocket: TLHTTPSocket);
+begin
+  FSocket := ASocket;
+  inherited Create;
+end;
+
+destructor TOutputItem.Destroy;
+begin
+  if FSocket.FCurrentInput = Self then
+    FSocket.FCurrentInput := nil;
+    
+  if FPrevDelayFree = nil then
+    FSocket.FDelayFreeItems := FNextDelayFree
+  else
+    FPrevDelayFree.FNextDelayFree := FNextDelayFree;
+  if FNextDelayFree <> nil then
+    FNextDelayFree.FPrevDelayFree := FPrevDelayFree;
+
+  inherited;
+end;
+
+procedure TOutputItem.DoneInput;
+begin
+end;
+
+function TOutputItem.HandleInput(ABuffer: pchar; ASize: integer): integer;
+begin
+  { discard input }
+  Result := ASize;
+end;
+
+procedure TOutputItem.LogError(const AMessage: string);
+begin
+  FSocket.LogError(AMessage, 0);
+end;
+
+function TOutputItem.WriteBlock: TWriteBlockStatus;
+var
+  lWritten: integer;
+begin
+  if FOutputPending then
+  begin
+    if FBufferSize > FBufferPos then
+    begin
+      lWritten := FSocket.Send(FBuffer[FBufferPos], FBufferSize-FBufferPos);
+      Inc(FBufferPos, lWritten);
+    end;
+    FOutputPending := FBufferPos < FBufferSize;
+    Result := BufferEmptyToWriteStatus[not FOutputPending];
+  end else
+    Result := EofToWriteStatus[FEof];
+end;
+
+const
+  ReserveChunkBytes = 12;
+
+constructor TBufferOutput.Create(ASocket: TLHTTPSocket);
+begin
+  inherited;
+  GetMem(FBuffer, DataBufferSize);
+  FWriteBlock := @WritePlain;
+  FPrepareBuffer := @PrepareBuffer;
+  FFinishBuffer := @FinishBuffer;
+  FBufferMemSize := DataBufferSize;
+end;
+
+destructor TBufferOutput.Destroy;
+begin
+  inherited;
+  FreeMem(FBuffer);
+end;
+
+procedure TBufferOutput.Add(ABuf: pointer; ASize: integer);
+var
+  copySize: integer;
+begin
+  repeat
+    copySize := FBufferSize-FBufferPos;
+    if copySize > ASize then
+      copySize := ASize;
+    Move(ABuf^, FBuffer[FBufferPos], copySize);
+    Inc(FBufferPos, copySize);
+    Dec(ASize, copySize);
+    if ASize = 0 then
+      break;
+    PrependBufferOutput(ASize);
+  until false;
+end;
+
+procedure TBufferOutput.Add(const AStr: string);
+begin
+  Add(PChar(AStr), Length(AStr));
+end;
+
+procedure TBufferOutput.PrependStreamOutput(AStream: TStream; AFree: boolean);
+begin
+  if AStream is TMemoryStream then
+    FSocket.PrependOutput(TMemoryStreamOutput.Create(FSocket, TMemoryStream(AStream), AFree), Self)
+  else
+    FSocket.PrependOutput(TStreamOutput.Create(FSocket, AStream, AFree), Self);
+end;
+
+procedure TBufferOutput.Add(AStream: TStream; AQueue: boolean = false; 
+  AFree: boolean = true);
+var
+  size, copySize: integer;
+begin
+  size := AStream.Size - AStream.Position;
+  repeat
+    copySize := FBufferSize-FBufferPos;
+    if copySize > size then
+      copySize := size;
+    AStream.Read(FBuffer[FBufferPos], copySize);
+    Inc(FBufferPos, copySize);
+    Dec(size, copySize);
+    if size = 0 then
+      break;
+    if AQueue then
+    begin
+      PrependBufferOutput(0);
+      PrependStreamOutput(AStream, AFree);
+    end else begin
+      PrependBufferOutput(size);
+    end;
+  until false;
+end;
+
+procedure TBufferOutput.PrepareChunk;
+begin
+  { 12 bytes for starting space, 7 bytes to end: <CR><LF>0<CR><LF><CR><LF> }
+  FBufferPos := ReserveChunkBytes;
+  FBufferOffset := FBufferPos;
+  FBufferSize := FBufferMemSize-7;
+end;
+
+procedure TBufferOutput.FinishChunk;
+var
+  lOffset: integer;
+begin
+  lOffset := HexReverse(FBufferPos-FBufferOffset, FBuffer+FBufferOffset-3);
+  FBuffer[FBufferOffset-2] := #13;
+  FBuffer[FBufferOffset-1] := #10;
+  FBuffer[FBufferPos] := #13;
+  FBuffer[FBufferPos+1] := #10;
+  FBufferSize := FBufferPos+2;
+  FBufferPos := FBufferOffset-lOffset-2;
+end;
+
+procedure TBufferOutput.PrepareBuffer;
+  { also for "plain" encoding }
+begin
+  FBufferPos := 0;
+  FBufferOffset := 0;
+  FBufferSize := FBufferMemSize;
+end;
+
+procedure TBufferOutput.FinishBuffer;
+begin
+  { nothing to do }
+end;
+
+procedure TBufferOutput.PrependBufferOutput(MinBufferSize: integer);
+begin
+  FFinishBuffer();
+  FSocket.PrependOutput(TMemoryOutput.Create(FSocket, FBuffer, FBufferOffset,
+    FBufferPos, true), Self);
+  FBufferMemSize := MinBufferSize;
+  if FBufferMemSize < DataBufferSize then
+    FBufferMemSize := DataBufferSize;
+  FBuffer := GetMem(FBufferMemSize);
+  FPrepareBuffer();
+end;
+
+function TBufferOutput.WriteChunk: TWriteBlockStatus;
+begin
+  if not FOutputPending and not FEof then
+  begin
+    Result := FillBuffer;
+    FEof := Result = wsDone;
+    FOutputPending := FBufferPos > FBufferOffset;
+    if FOutputPending then
+      FinishChunk;
+    if FEof then
+    begin
+      if not FOutputPending then
+      begin
+        { FBufferPos/Size still in "fill mode" }
+        FBufferSize := 0;
+        FBufferPos := 0;
+        FOutputPending := true;
+      end;
+      FBuffer[FBufferSize] := '0';
+      FBuffer[FBufferSize+1] := #13;
+      FBuffer[FBufferSize+2] := #10;
+      { no trailer }
+      FBuffer[FBufferSize+3] := #13;
+      FBuffer[FBufferSize+4] := #10;
+      inc(FBufferSize, 5);
+    end;
+  end else   
+    Result := EofToWriteStatus[FEof];
+  if FOutputPending then
+  begin
+    Result := inherited WriteBlock;
+    if (Result = wsDone) and not FEof then
+    begin
+      Result := wsPendingData;
+      PrepareChunk;
+    end;
+  end;
+end;
+  
+function TBufferOutput.WriteBuffer: TWriteBlockStatus;
+begin
+  if not FOutputPending then
+  begin
+    Result := FillBuffer;
+    FEof := Result = wsDone;
+    FOutputPending := FEof;
+    if FOutputPending or (FBufferPos = FBufferSize) then
+    begin
+      if FBufferPos > FBufferOffset then
+      begin
+        FSocket.AddContentLength(FBufferPos-FBufferOffset);
+        if not FEof then
+          PrependBufferOutput(0)
+        else begin
+          FBufferSize := FBufferPos;
+          FBufferPos := FBufferOffset;
+        end;
+      end else begin
+        FBufferPos := 0;
+        FBufferSize := 0;
+      end;
+      if FEof then
+        FSocket.DoneBuffer(Self);
+    end;
+  end else
+    Result := EofToWriteStatus[FEof];
+  if Result = wsDone then
+    Result := inherited WriteBlock;
+end;
+
+function TBufferOutput.WritePlain: TWriteBlockStatus;
+begin
+  if not FOutputPending then
+  begin
+    Result := FillBuffer;
+    FEof := Result = wsDone;
+    if FBufferPos > FBufferOffset then
+    begin
+      FOutputPending := true;
+      FBufferSize := FBufferPos;
+      FBufferPos := FBufferOffset;
+    end else begin
+      FBufferSize := 0;
+      FBufferPos := 0;
+    end;
+  end;
+  Result := inherited WriteBlock;
+  if Result <> wsPendingData then
+  begin
+    PrepareBuffer;
+    if not FEof then
+      Result := wsPendingData;
+  end;
+end;
+
+function TBufferOutput.WriteBlock: TWriteBlockStatus;
+begin
+  Result := FWriteBlock();
+end;
+
+procedure TBufferOutput.SelectChunked;
+begin
+  FPrepareBuffer := @PrepareChunk;
+  FWriteBlock := @WriteChunk;
+  FFinishBuffer := @FinishChunk;
+  PrepareChunk;
+end;
+  
+procedure TBufferOutput.SelectBuffered;
+begin
+  FPrepareBuffer := @PrepareBuffer;
+  FWriteBlock := @WriteBuffer;
+  FFinishBuffer := @FinishBuffer;
+  PrepareBuffer;
+end;
+  
+procedure TBufferOutput.SelectPlain;
+begin
+  FPrepareBuffer := @PrepareBuffer;
+  FWriteBlock := @WritePlain;
+  FFinishBuffer := @FinishBuffer;
+  PrepareBuffer;
+end;
+
+{ TMemoryOutput }
+
+constructor TMemoryOutput.Create(ASocket: TLHTTPSocket; ABuffer: pointer; 
+  ABufferOffset, ABufferSize: integer; AFreeBuffer: boolean);
+begin
+  inherited Create(ASocket);
+
+  FBuffer := ABuffer;
+  FBufferPos := ABufferOffset;
+  FBufferSize := ABufferSize;
+  FFreeBuffer := AFreeBuffer;
+  FOutputPending := true;
+end;
+
+destructor TMemoryOutput.Destroy;
+begin
+  inherited;
+  if FFreeBuffer then
+    FreeMem(FBuffer);
+end;
+
+{ TStreamOutput }
+
+constructor TStreamOutput.Create(ASocket: TLHTTPSocket; AStream: TStream; AFreeStream: boolean);
+begin
+  inherited Create(ASocket);
+  FStream := AStream;
+  FFreeStream := AFreeStream;
+  FStreamSize := AStream.Size;
+end;
+
+destructor TStreamOutput.Destroy;
+begin
+  if FFreeStream then
+    FStream.Free;
+  inherited;
+end;
+
+function TStreamOutput.FillBuffer: TWriteBlockStatus;
+var
+  lRead: integer;
+begin
+  lRead := FStream.Read(FBuffer[FBufferPos], FBufferSize-FBufferPos);
+  Inc(FBufferPos, lRead);
+  Result := BufferEmptyToWriteStatus[FStream.Position >= FStreamSize];
+end;
+
+{ TMemoryStreamOutput }
+
+constructor TMemoryStreamOutput.Create(ASocket: TLHTTPSocket; AStream: TMemoryStream; 
+  AFreeStream: boolean);
+begin
+  inherited Create(ASocket);
+  FStream := AStream;
+  FFreeStream := AFreeStream;
+  FOutputPending := true;
+end;
+
+destructor TMemoryStreamOutput.Destroy;
+begin
+  if FFreeStream then
+    FStream.Free;
+  inherited;
+end;
+
+function TMemoryStreamOutput.WriteBlock: TWriteBlockStatus;
+var
+  lWritten: integer;
+begin
+  if not FOutputPending then
+    exit(wsDone);
+
+  lWritten := FSocket.Send(PByte(FStream.Memory)[FStream.Position], FStream.Size-FStream.Position);
+  FStream.Position := FStream.Position + lWritten;
+  FOutputPending := FStream.Position < FStream.Size;
+  FEof := not FOutputPending;
+  Result := EofToWriteStatus[FEof];
+end;
+
+{ TLHTTPSocket }
+
+constructor TLHTTPSocket.Create;
+begin
+  inherited;
+
+  FBuffer := GetMem(RequestBufferSize);
+  FBufferSize := RequestBufferSize;
+  FBufferPos := FBuffer;
+  FBufferEnd := FBufferPos;
+  FBuffer[0] := #0;
+  FKeepAlive := true;
+end;
+
+destructor TLHTTPSocket.Destroy;
+begin
+  FreeDelayFreeItems;
+  inherited;
+  FreeMem(FBuffer);
+end;
+
+procedure TLHTTPSocket.Disconnect;
+var
+  lOutput: TOutputItem;
+begin
+  inherited Disconnect;
+  while FCurrentOutput <> nil do
+  begin
+    lOutput := FCurrentOutput;
+    FCurrentOutput := FCurrentOutput.FNext;
+    lOutput.Free;
+  end;
+  if FCurrentInput <> nil then
+    FreeAndNil(FCurrentInput);
+end;
+
+procedure TLHTTPSocket.FreeDelayFreeItems;
+var
+  lItem: TOutputItem;
+begin
+  while FDelayFreeItems <> nil do
+  begin
+    lItem := FDelayFreeItems;
+    FDelayFreeItems := FDelayFreeItems.FNextDelayFree;
+    lItem.Free;
+  end;
+end;
+
+procedure TLHTTPSocket.DelayFree(AOutputItem: TOutputItem);
+begin
+  if AOutputItem = nil then exit;
+  if FDelayFreeItems <> nil then
+    FDelayFreeItems.FPrevDelayFree := AOutputItem;
+  AOutputItem.FNextDelayFree := FDelayFreeItems;
+  FDelayFreeItems := AOutputItem;
+end;
+
+procedure TLHTTPSocket.DoneBuffer(AOutput: TBufferOutput);
+begin
+end;
+
+procedure TLHTTPSocket.LogMessage;
+begin
+end;
+
+procedure TLHTTPSocket.LogAccess(const AMessage: string);
+begin
+end;
+
+procedure TLHTTPSocket.WriteError(AStatus: TLHTTPStatus);
+begin
+end;
+
+procedure TLHTTPSocket.AddToOutput(AOutputItem: TOutputItem);
+begin
+  AOutputItem.FPrev := FLastOutput;
+  if FLastOutput <> nil then
+  begin
+    FLastOutput.FNext := AOutputItem;
+  end else begin
+    FCurrentOutput := AOutputItem;
+  end;
+  FLastOutput := AOutputItem;
+end;
+
+procedure TLHTTPSocket.PrependOutput(ANewItem, AItem: TOutputItem);
+begin
+  ANewItem.FPrev := AItem.FPrev;
+  ANewItem.FNext := AItem;
+  AItem.FPrev := ANewItem;
+  if FCurrentOutput = AItem then
+    FCurrentOutput := ANewItem;
+end;
+
+procedure TLHTTPSocket.RemoveOutput(AOutputItem: TOutputItem);
+begin
+  if AOutputItem.FPrev <> nil then
+    AOutputItem.FPrev.FNext := AOutputItem.FNext;
+  if AOutputItem.FNext <> nil then
+    AOutputItem.FNext.FPrev := AOutputItem.FPrev;
+  if FLastOutput = AOutputItem then
+    FLastOutput := AOutputItem.FPrev;
+  if FCurrentOutput = AOutputItem then
+    FCurrentOutput := AOutputItem.FNext;
+  AOutputItem.FPrev := nil;
+  AOutputItem.FNext := nil;
+end;
+
+procedure TLHTTPSocket.ResetDefaults;
+begin
+  FParseBuffer := @ParseRequest;
+end;
+
+procedure TLHTTPSocket.FlushRequest;
+begin
+  FillDWord(FParameters, sizeof(FParameters) div 4, 0);
+  ResetDefaults;
+end;
+
+function TLHTTPSocket.CalcAvailableBufferSpace: integer;
+begin
+  Result := FBufferSize-(FBufferEnd-FBuffer)-1;
+end;
+
+procedure TLHTTPSocket.HandleReceive;
+var
+  lRead: integer;
+begin
+  if FRequestInputDone then 
+  begin
+    IgnoreRead := true;
+    exit;
+  end;
+
+  lRead := CalcAvailableBufferSpace;
+  { if buffer has filled up, keep ignoring and continue parsing requests }
+  if lRead > 0 then
+  begin
+    IgnoreRead := false;
+    lRead := Get(FBufferEnd^, lRead);
+    if lRead = 0 then exit;
+    Inc(FBufferEnd, lRead);
+    FBufferEnd^ := #0;
+  end;
+  ParseBuffer;
+
+  if FIgnoreWrite then
+    WriteBlock;
+end;
+
+procedure TLHTTPSocket.RelocateVariable(var AVar: pchar);
+begin
+  if AVar = nil then exit;
+  AVar := FBuffer + (AVar - FRequestPos);
+end;
+
+procedure TLHTTPSocket.RelocateVariables;
+var
+  I: TLHTTPParameter;
+begin
+  for I := Low(TLHTTPParameter) to High(TLHTTPParameter) do
+    RelocateVariable(FParameters[I]);
+end;
+
+procedure TLHTTPSocket.PackRequestBuffer;
+var
+  lBytesLeft: integer;
+  lFreeBuffer: pchar;
+begin
+  if (FRequestBuffer <> nil) and (FBufferEnd-FBufferPos <= RequestBufferSize) then
+  begin
+    { switch back to normal size buffer }
+    lFreeBuffer := FBuffer;
+    FBuffer := FRequestBuffer;
+    FBufferSize := RequestBufferSize;
+    FRequestBuffer := nil;
+  end else
+    lFreeBuffer := nil;
+  if FRequestPos <> nil then
+  begin
+    lBytesLeft := FBufferEnd-FRequestPos;
+    FBufferEnd := FBuffer+lBytesLeft;
+    RelocateVariable(FBufferPos);
+    RelocateVariables;
+    { include null-terminator, where FBufferEnd is pointing at }
+    Move(FRequestPos^, FBuffer^, lBytesLeft+1);
+    FRequestPos := nil;
+  end;
+  if lFreeBuffer <> nil then
+    FreeMem(lFreeBuffer);
+end;
+
+procedure TLHTTPSocket.PackInputBuffer;
+var
+  lBytesLeft: integer;
+begin
+  { use bigger buffer for more speed }
+  if FRequestBuffer = nil then
+  begin
+    FRequestBuffer := FBuffer;
+    FBuffer := GetMem(DataBufferSize);
+    FBufferSize := DataBufferSize;
+    FRequestPos := nil;
+  end;
+  lBytesLeft := FBufferEnd-FBufferPos;
+  Move(FBufferPos^, FBuffer^, lBytesLeft);
+  FBufferEnd := FBuffer+lBytesLeft;
+  FBufferPos := FBuffer;
+end;
+
+function TLHTTPSocket.ParseEntityPlain: boolean;
+var
+  lNumBytes: integer;
+begin
+  lNumBytes := FBufferEnd - FBufferPos;
+  if lNumBytes > FInputRemaining then
+    lNumBytes := FInputRemaining;
+  { if no output item to feed into, discard }
+  if FCurrentInput <> nil then
+    lNumBytes := FCurrentInput.HandleInput(FBufferPos, lNumBytes);
+  inc(FBufferPos, lNumBytes);
+  dec(FInputRemaining, lNumBytes);
+  Result := FInputRemaining > 0;
+  { prepare for more data, if more data coming }
+  if Result and (FBufferPos+FInputRemaining > FBuffer+FBufferSize) then
+    PackInputBuffer;
+end;
+
+function TLHTTPSocket.ParseEntityChunked: boolean;
+var
+  lLineEnd, lNextLine: pchar;
+  lCode: integer;
+begin
+  repeat
+    if FChunkState = csFinished then
+      exit(false);
+    if FChunkState = csData then
+      if ParseEntityPlain then 
+        exit(true)
+      else
+        FChunkState := csDataEnd;
+    
+    lLineEnd := StrScan(FBufferPos, #10);
+    if lLineEnd = nil then
+      exit(true);
+    
+    lNextLine := lLineEnd+1;
+    if (lLineEnd > FBufferPos) and ((lLineEnd-1)^ = #13) then
+      dec(lLineEnd);
+    case FChunkState of 
+      csInitial:
+      begin
+        lLineEnd^ := #0;
+        HexToInt(FBufferPos, dword(FInputRemaining), lCode);
+        if lCode = 1 then
+        begin
+          FChunkState := csFinished;
+          Disconnect;
+          exit(false);
+        end;
+        if FInputRemaining = 0 then
+          FChunkState := csTrailer
+        else
+          FChunkState := csData;
+      end;
+      csDataEnd:
+      begin
+        { skip empty line }
+        FChunkState := csInitial;
+      end;
+      csTrailer:
+      begin
+        { trailer is optional, empty line indicates end }
+        if lLineEnd = FBufferPos then
+          FChunkState := csFinished
+        else
+          ParseParameterLine(lLineEnd);
+      end;
+    end;
+    FBufferPos := lNextLine;
+  until false;
+end;
+
+function TLHTTPSocket.ParseRequest: boolean;
+var
+  pNextLine, pLineEnd: pchar;
+begin
+  if FRequestHeaderDone then exit(not FRequestInputDone);
+  repeat
+    pLineEnd := StrScan(FBufferPos, #10);
+    if pLineEnd = nil then
+    begin
+      if (FRequestBuffer <> nil) or (FRequestPos <> nil) then
+        PackRequestBuffer
+      else if CalcAvailableBufferSpace = 0 then
+        WriteError(hsRequestTooLong);
+      exit(true);
+    end;
+  
+    pNextLine := pLineEnd+1;
+    if (pLineEnd > FBufferPos) and ((pLineEnd-1)^ = #13) then
+      dec(pLineEnd);
+    pLineEnd^ := #0;
+    ParseLine(pLineEnd);
+    FBufferPos := pNextLine;
+    if FRequestHeaderDone then
+      exit(not FRequestInputDone);
+  until false;
+end;
+
+procedure TLHTTPSocket.ParseParameterLine(pLineEnd: pchar);
+var
+  lPos: pchar;
+  I: TLHTTPParameter;
+  lLen: integer;
+begin
+  lPos := StrScan(FBufferPos, ' ');
+  if (lPos = nil) or (lPos = FBufferPos) or ((lPos-1)^ <> ':') then
+  begin
+    WriteError(hsBadRequest);
+    exit;
+  end;
+
+  { null-terminate at colon }
+  (lPos-1)^ := #0;
+  StrUpper(FBufferPos);
+  lLen := lPos-FBufferPos-1;
+  for I := Low(TLHTTPParameter) to High(TLHTTPParameter) do
+    if (Length(HTTPParameterStrings[I]) = lLen)
+    and CompareMem(FBufferPos, PChar(HTTPParameterStrings[I]), lLen) then
+    begin
+      repeat
+        inc(lPos);
+      until lPos^ <> ' ';
+      FParameters[I] := lPos;
+      break;
+    end;
+end;
+
+procedure TLHTTPSocket.ParseLine(pLineEnd: pchar);
+begin
+  if FBufferPos[0] = #0 then
+  begin
+    FRequestHeaderDone := true;
+    ProcessHeaders;
+  end else
+    ParseParameterLine(pLineEnd);
+end;
+        
+function TLHTTPSocket.ParseBuffer: boolean;
+var
+  lParseFunc: TParseBufferMethod;
+begin
+  repeat
+    lParseFunc := FParseBuffer;
+    Result := FParseBuffer();
+    if not Result and not FRequestInputDone then
+    begin
+      FRequestInputDone := true;
+      if FCurrentInput <> nil then
+        FCurrentInput.DoneInput;
+    end;
+    { if parse func changed mid-run, then we should continue calling the new 
+      one: header + data }
+  until (lParseFunc = FParseBuffer) or not Result;
+end;
+
+function TLHTTPSocket.ProcessEncoding: boolean;
+var
+  lCode: integer;
+begin
+  Result := true;
+  if FParameters[hpContentLength] <> nil then
+  begin
+    FParseBuffer := @ParseEntityPlain;
+    Val(FParameters[hpContentLength], FInputRemaining, lCode);
+    if lCode <> 0 then
+    begin
+      WriteError(hsBadRequest);
+      exit;
+    end;
+  end else 
+  if FParameters[hpTransferEncoding] <> nil then
+  begin
+    if (StrIComp(FParameters[hpTransferEncoding], 'chunked') = 0) then
+    begin
+      FParseBuffer := @ParseEntityChunked;
+      FChunkState := csInitial;
+    end else begin
+      Result := false;
+    end;
+  end else begin
+    FRequestInputDone := true;
+  end;
+end;
+
+function TLHTTPSocket.SetupEncoding(AOutputItem: TBufferOutput; AHeaderOut: PHeaderOutInfo): boolean;
+begin
+  if AHeaderOut^.ContentLength = 0 then
+  begin
+    if AHeaderOut^.Version >= 11 then
+    begin
+      { we can use chunked encoding }
+      AHeaderOut^.TransferEncoding := teChunked;
+      AOutputItem.SelectChunked;
+    end else begin
+      { we need to buffer the response to find its length }
+      AHeaderOut^.TransferEncoding := teIdentity;
+      AOutputItem.SelectBuffered;
+      { need to accumulate data before starting header output }
+      AddToOutput(AOutputItem);
+      exit(false);
+    end;
+  end else begin
+    AHeaderOut^.TransferEncoding := teIdentity;
+    AOutputItem.SelectPlain;
+  end;
+  Result := true;
+end;
+
+procedure TLHTTPSocket.WriteBlock;
+begin
+  while true do
+  begin
+    if FCurrentOutput = nil then
+    begin
+      if not FOutputDone or (not FRequestInputDone and FKeepAlive) then
+        break;
+
+      if not FKeepAlive then
+      begin
+        Disconnect;
+        exit;
+      end;
+
+      { next request }
+      FRequestInputDone := false;
+      FRequestHeaderDone := false;
+      FOutputDone := false;
+      FRequestPos := FBufferPos;
+      FlushRequest;
+      { rewind buffer pointers if at end of buffer anyway }
+      if FBufferPos = FBufferEnd then
+        PackRequestBuffer;
+
+      if ParseBuffer and IgnoreRead then 
+      begin
+        { end of input buffer reached, try reading more }
+        HandleReceive;
+      end;
+
+      if FCurrentOutput = nil then 
+        break;
+    end;
+
+    { if we cannot send, then the send buffer is full }
+    if not FCanSend or not FConnected then
+      break;
+
+    case FCurrentOutput.WriteBlock of
+      wsDone:
+      begin
+        if FCurrentOutput = FLastOutput then
+          FLastOutput := nil;
+        { some output items may trigger this parse/write loop }
+        DelayFree(FCurrentOutput);
+        FCurrentOutput := FCurrentOutput.FNext;
+      end;
+      wsWaitingData:
+      begin
+        { wait for more data from external source }
+        break;
+      end;
+    end;
+    { nothing left to write, request was busy and now completed }
+    if FCurrentOutput = nil then
+    begin
+      LogMessage;
+      FOutputDone := true;
+    end;
+  end;
+end;
+
+{ TLHTTPServerSocket }
+
+constructor TLHTTPServerSocket.Create;
+begin
+  inherited;
+
+  FLogMessage := InitStringBuffer(256);
+  FHeaderOut.ExtraHeaders := InitStringBuffer(256);
+  ResetDefaults;
+end;
+
+destructor TLHTTPServerSocket.Destroy;
+begin
+  FreeMem(FLogMessage.Memory);
+  FreeMem(FHeaderOut.ExtraHeaders.Memory);
+  inherited;
+end;
+
+procedure TLHTTPServerSocket.AddContentLength(ALength: integer);
+begin
+  Inc(FHeaderOut.ContentLength, ALength);
+end;
+
+procedure TLHTTPServerSocket.DoneBuffer(AOutput: TBufferOutput);
+begin
+  if FCurrentOutput <> AOutput then
+  begin
+    RemoveOutput(AOutput);
+    AOutput.FNext := FCurrentOutput;
+    FCurrentOutput := AOutput;
+  end;
+  WriteHeaders(AOutput, nil);
+end;
+
+procedure TLHTTPServerSocket.LogAccess(const AMessage: string);
+begin
+  TLHTTPConnection(FCreator).LogAccess(AMessage);
+end;
+
+procedure TLHTTPServerSocket.LogMessage;
+begin
+  { log a message about this request, 
+    '<StatusCode> <Length> "<Referer>" "<User-Agent>"' }
+  AppendString(FLogMessage, IntToStr(HTTPStatusCodes[FResponseInfo.Status]));
+  AppendChar(FLogMessage, ' ');
+  AppendString(FLogMessage, IntToStr(FHeaderOut.ContentLength));
+  AppendString(FLogMessage, ' "');
+  AppendString(FLogMessage, FParameters[hpReferer]);
+  AppendString(FLogMessage, '" "');
+  AppendString(FLogMessage, FParameters[hpUserAgent]);
+  AppendChar(FLogMessage, '"');
+  AppendChar(FLogMessage, #0);
+  LogAccess(FLogMessage.Memory);
+end;
+
+procedure TLHTTPServerSocket.ResetDefaults;
+begin
+  inherited;
+  FRequestInfo.RequestType := hmUnknown;
+  FSetupEncodingState := seNone;
+  with FResponseInfo do
+  begin
+    Status := hsOK;
+    ContentType := 'application/octet-stream';
+    ContentCharset := '';
+    LastModified := 0.0;
+  end;
+end;
+
+procedure TLHTTPServerSocket.FlushRequest;
+  { reset structure to zero, not called from constructor }
+begin
+  with FRequestInfo do
+  begin
+    { request }
+    Argument := nil;
+    QueryParams := nil;
+    Version := 0;
+  end;
+  with FHeaderOut do
+  begin
+    ContentLength := 0;
+    TransferEncoding := teIdentity;
+    ExtraHeaders.Pos := ExtraHeaders.Memory;
+    Version := 0;
+  end;
+  inherited;
+end;
+  
+procedure TLHTTPServerSocket.RelocateVariables;
+begin
+  RelocateVariable(FRequestInfo.Method);
+  RelocateVariable(FRequestInfo.Argument);
+  RelocateVariable(FRequestInfo.QueryParams);
+  RelocateVariable(FRequestInfo.VersionStr);
+  inherited;
+end;
+
+procedure TLHTTPServerSocket.ParseLine(pLineEnd: pchar);
+begin
+  if FRequestInfo.RequestType = hmUnknown then
+  begin
+    ParseRequestLine(pLineEnd);
+    exit;
+  end;
+
+  inherited;
+end;
+
+procedure TLHTTPServerSocket.ParseRequestLine(pLineEnd: pchar);
+var
+  lPos: pchar;
+  I: TLHTTPMethod;
+  NowLocal: TDateTime;
+begin
+  { make a timestamp for this request }
+  NowLocal := Now;
+  FRequestInfo.DateTime := LocalTimeToGMT(NowLocal);
+  { begin log message }
+  FLogMessage.Pos := FLogMessage.Memory;
+  AppendString(FLogMessage, PeerAddress);
+  AppendString(FLogMessage, ' - [');
+  AppendString(FLogMessage, FormatDateTime('dd/mmm/yyyy:hh:nn:ss', NowLocal));
+  AppendString(FLogMessage, TLHTTPServer(FCreator).FLogMessageTZString);
+  AppendString(FLogMessage, FBufferPos, pLineEnd-FBufferPos);
+  AppendString(FLogMessage, '" ');
+
+  { decode version }
+  lPos := pLineEnd;
+  repeat
+    if lPos^ = ' ' then break;
+    dec(lPos);
+    if lPos < FBufferPos then
+    begin
+      WriteError(hsBadRequest);
+      exit;
+    end;
+  until false;
+
+  lPos^ := #0;
+  inc(lPos);
+  { lPos = version string }
+  if not HTTPVersionCheck(lPos, pLineEnd, FRequestInfo.Version) then
+  begin
+    WriteError(hsBadRequest);
+    exit;
+  end;
+  FRequestInfo.VersionStr := lPos;
+  FHeaderOut.Version := FRequestInfo.Version;
+  
+  { trim spaces at end of URI }
+  dec(lPos);
+  repeat
+    if lPos = FBufferPos then break;
+    dec(lPos);
+    if lPos^ <> ' ' then break;
+    lPos^ := #0;
+  until false;
+
+  { decode method }
+  FRequestInfo.Method := FBufferPos;
+  lPos := StrScan(FBufferPos, ' ');
+  if lPos = nil then
+  begin
+    WriteError(hsBadRequest);
+    exit;
+  end;
+
+  lPos^ := #0;
+  for I := Low(TLHTTPMethod) to High(TLHTTPMethod) do
+  begin
+    if I = hmUnknown then
+    begin
+      WriteError(hsNotImplemented);
+      exit;
+    end;
+    
+    if ((lPos-FBufferPos) = Length(HTTPMethodStrings[I]))
+    and CompareMem(FBufferPos, PChar(HTTPMethodStrings[I]), lPos-FBufferPos) then
+    begin
+      repeat
+        inc(lPos);
+      until lPos^ <> ' ';
+      FRequestInfo.Argument := lPos;
+      FRequestInfo.RequestType := I;
+      break;
+    end;
+  end;
+
+  if ((pLineEnd-FRequestInfo.Argument) > 7) and (StrIComp(FRequestInfo.Argument, 'http://') = 0) then
+  begin
+    { absolute URI }
+    lPos := FRequestInfo.Argument+7;
+    while (lPos^ = '/') do 
+      Inc(lPos);
+    FParameters[hpHost] := lPos;
+    lPos := StrScan(lPos, '/');
+    FRequestInfo.Argument := lPos;
+  end;
+  { FRequestInfo.Argument now points to an "abs_path" }
+  if FRequestInfo.Argument[0] <> '/' then
+  begin
+    WriteError(hsBadRequest);
+    exit;
+  end;
+  repeat
+    Inc(FRequestInfo.Argument);
+  until FRequestInfo.Argument[0] <> '/';
+end;
+
+procedure TLHTTPServerSocket.ProcessHeaders;
+  { process request }
+var
+  lPos: pchar;
+begin
+  { do HTTP/1.1 Host-field present check }
+  if (FRequestInfo.Version > 10) and (FParameters[hpHost] = nil) then
+  begin
+    WriteError(hsBadRequest);
+    exit;
+  end;
+      
+  lPos := StrScan(FRequestInfo.Argument, '?');
+  if lPos <> nil then
+  begin
+    lPos^ := #0;
+    FRequestInfo.QueryParams := lPos+1;
+  end;
+
+  FKeepAlive := FRequestInfo.Version > 10;
+  if FParameters[hpConnection] <> nil then
+  begin
+    if StrIComp(FParameters[hpConnection], 'keep-alive') = 0 then
+      FKeepAlive := true
+    else
+    if StrIComp(FParameters[hpConnection], 'close') = 0 then
+      FKeepAlive := false;
+  end;
+  
+  HTTPDecode(FRequestInfo.Argument);
+  if not CheckPermission(FRequestInfo.Argument) then
+  begin
+    WriteError(hsForbidden);
+  end else begin
+    if not ProcessEncoding then
+    begin
+      WriteError(hsNotImplemented);
+      exit;
+    end;
+      
+    FCurrentInput := HandleURI;
+    { if we have a valid outputitem, wait until it is ready 
+      to produce its response }
+    if FCurrentInput = nil then
+    begin
+      if FResponseInfo.Status = hsOK then
+        WriteError(hsNotFound)
+      else
+        WriteError(FResponseInfo.Status);
+    end else if FRequestInputDone then
+      FCurrentInput.DoneInput;
+  end;
+end;
+
+function TLHTTPServerSocket.PrepareResponse(AOutputItem: TOutputItem; ACustomErrorMessage: boolean): boolean;
+var
+  lDateTime: TDateTime;
+begin
+  { check modification date }
+  if FResponseInfo.Status < hsBadRequest then
+  begin
+    if (FParameters[hpIfModifiedSince] <> nil) 
+      and (FResponseInfo.LastModified <> 0.0) then
+    begin
+      if TryHTTPDateStrToDateTime(FParameters[hpIfModifiedSince], lDateTime) then
+      begin
+        if lDateTime > FRequestInfo.DateTime then
+          FResponseInfo.Status := hsBadRequest
+        else
+        if FResponseInfo.LastModified <= lDateTime then
+          FResponseInfo.Status := hsNotModified;
+      end;
+    end else
+    if (FParameters[hpIfUnmodifiedSince] <> nil) then
+    begin
+      if TryHTTPDateStrToDateTime(FParameters[hpIfUnmodifiedSince], lDateTime) then
+      begin
+        if (FResponseInfo.LastModified = 0.0) 
+          or (lDateTime < FResponseInfo.LastModified) then
+          FResponseInfo.Status := hsPreconditionFailed;
+      end;
+    end;
+  end;
+
+  if (FResponseInfo.Status < hsOK) or (FResponseInfo.Status in [hsNoContent, hsNotModified]) then
+  begin
+    { RFC says we MUST not include a response for these statuses }
+    ACustomErrorMessage := false;
+    FHeaderOut.ContentLength := 0;
+  end;
+  
+  Result := (FResponseInfo.Status = hsOK) or ACustomErrorMessage;
+  if not Result then
+  begin
+    WriteError(FResponseInfo.Status);
+    DelayFree(AOutputItem);
+  end;
+end;
+
+procedure TLHTTPServerSocket.StartMemoryResponse(AOutputItem: TMemoryOutput; ACustomErrorMessage: boolean = false);
+begin
+  if PrepareResponse(AOutputItem, ACustomErrorMessage) then
+  begin
+    if FRequestInfo.RequestType <> hmHead then
+      FHeaderOut.ContentLength := AOutputItem.FBufferSize
+    else
+      FHeaderOut.ContentLength := 0;
+    WriteHeaders(nil, AOutputItem);
+  end;
+end;
+
+function TLHTTPServerSocket.SetupEncoding(AOutputItem: TBufferOutput): boolean;
+const
+  SetupEncodingToState: array[boolean] of TSetupEncodingState = (seWaitHeaders, seStartHeaders);
+begin
+  if FSetupEncodingState > seNone then
+    exit(FSetupEncodingState = seStartHeaders);
+  Result := inherited SetupEncoding(AOutputItem, @FHeaderOut);
+  FSetupEncodingState := SetupEncodingToState[Result];
+end;
+
+procedure TLHTTPServerSocket.StartResponse(AOutputItem: TBufferOutput; ACustomErrorMessage: boolean = false);
+begin
+  if PrepareResponse(AOutputItem, ACustomErrorMessage) then
+    if (FRequestInfo.RequestType = hmHead) or SetupEncoding(AOutputItem) then
+      WriteHeaders(nil, AOutputItem);
+end;
+
+function TLHTTPServerSocket.HandleURI: TOutputItem; {inline;} {<--- triggers IE}
+begin
+  Result := TLHTTPServer(FCreator).HandleURI(Self);
+end;
+
+procedure TLHTTPServerSocket.WriteError(AStatus: TLHTTPStatus);
+var
+  lMessage: string;
+  lMsgOutput: TMemoryOutput;
+begin
+  if AStatus in HTTPDisconnectStatuses then
+    FKeepAlive := false;
+  lMessage := HTTPDescriptions[AStatus];
+  FRequestHeaderDone := true;
+  FResponseInfo.Status := AStatus;
+  FHeaderOut.ContentLength := Length(lMessage);
+  FHeaderOut.TransferEncoding := teIdentity;
+  if Length(lMessage) > 0 then
+  begin
+    FResponseInfo.ContentType := 'text/html';
+    lMsgOutput := TMemoryOutput.Create(Self, PChar(lMessage), 0, Length(lMessage), false)
+  end else begin
+    FResponseInfo.ContentType := '';
+    lMsgOutput := nil;
+  end;
+  WriteHeaders(nil, lMsgOutput);
+end;
+
+procedure TLHTTPServerSocket.WriteHeaders(AHeaderResponse, ADataResponse: TOutputItem);
+var
+  lTemp: string[23];
+  lMessage: TStringBuffer;
+  tempStr: string;
+begin
+  lMessage := InitStringBuffer(504);
+  
+  AppendString(lMessage, 'HTTP/1.1 ');
+  Str(HTTPStatusCodes[FResponseInfo.Status], lTemp);
+  AppendString(lMessage, lTemp);
+  AppendChar(lMessage, ' ');
+  AppendString(lMessage, HTTPTexts[FResponseInfo.Status]);
+  AppendString(lMessage, #13#10+'Date: ');
+  AppendString(lMessage, FormatDateTime(HTTPDateFormat, FRequestInfo.DateTime));
+  AppendString(lMessage, ' GMT');
+  tempStr := TLHTTPServer(FCreator).ServerSoftware;
+  if Length(tempStr) > 0 then
+  begin
+    AppendString(lMessage, #13#10+'Server: ');
+    AppendString(lMessage, tempStr);
+  end;
+  if Length(FResponseInfo.ContentType) > 0 then
+  begin
+    AppendString(lMessage, #13#10+'Content-Type: ');
+    AppendString(lMessage, FResponseInfo.ContentType);
+    if Length(FResponseInfo.ContentCharset) > 0 then
+    begin
+      AppendString(lMessage, '; charset=');
+      AppendString(lMessage, FResponseInfo.ContentCharset);
+    end;
+  end;
+  if FHeaderOut.TransferEncoding = teIdentity then
+  begin
+    AppendString(lMessage, #13#10+'Content-Length: ');
+    Str(FHeaderOut.ContentLength, lTemp);
+    AppendString(lMessage, lTemp);
+  end else begin
+    { only other possibility: teChunked }
+    AppendString(lMessage, #13#10+'Transfer-Encoding: chunked');
+  end;
+  if FResponseInfo.LastModified <> 0.0 then
+  begin
+    AppendString(lMessage, #13#10+'Last-Modified: ');
+    AppendString(lMessage, FormatDateTime(HTTPDateFormat, FResponseInfo.LastModified));
+    AppendString(lMessage, ' GMT');
+  end;
+  AppendString(lMessage, #13#10+'Connection: ');
+  if FKeepAlive then
+    AppendString(lMessage, 'keep-alive')
+  else
+    AppendString(lMessage, 'close');
+  AppendString(lMessage, #13#10);
+  with FHeaderOut.ExtraHeaders do
+    AppendString(lMessage, Memory, Pos-Memory);
+  AppendString(lMessage, #13#10);
+  if AHeaderResponse <> nil then
+  begin
+    AHeaderResponse.FBuffer := lMessage.Memory;
+    AHeaderResponse.FBufferSize := lMessage.Pos-lMessage.Memory;
+  end else
+    AddToOutput(TMemoryOutput.Create(Self, lMessage.Memory, 0,
+      lMessage.Pos-lMessage.Memory, true));
+
+  if ADataResponse <> nil then
+  begin
+    if FRequestInfo.RequestType = hmHead then
+      DelayFree(ADataResponse)
+    else
+      AddToOutput(ADataResponse);
+  end;
+end;
+
+{ TLHTTPConnection }
+
+destructor TLHTTPConnection.Destroy;
+begin
+  inherited;
+end;
+
+procedure TLHTTPConnection.LogAccess(const AMessage: string);
+begin
+end;
+
+procedure TLHTTPConnection.ReceiveEvent(aSocket: TLHandle);
+begin
+  TLHTTPSocket(aSocket).HandleReceive;
+  TLHTTPSocket(aSocket).FreeDelayFreeItems;
+end;
+
+procedure TLHTTPConnection.CanSendEvent(aSocket: TLHandle);
+begin
+  TLHTTPSocket(aSocket).WriteBlock;
+  TLHTTPSocket(aSocket).FreeDelayFreeItems;
+end;
+
+{ TLHTTPServer }
+
+constructor TLHTTPServer.Create(AOwner: TComponent);
+var
+  TZSign: char;
+  TZSecsAbs: integer;
+begin
+  inherited Create(AOwner);
+
+  SocketClass := TLHTTPServerSocket;
+  if TZSeconds >= 0 then
+    TZSign := '+'
+  else
+    TZSign := '-';
+  TZSecsAbs := Abs(TZSeconds);
+  FLogMessageTZString := Format(' %s%.2d%.2d] "', 
+    [TZSign, TZSecsAbs div 3600, (TZSecsAbs div 60) mod 60]);
+end;
+
+function TLHTTPServer.HandleURI(ASocket: TLHTTPServerSocket): TOutputItem;
+var
+  lHandler: TURIHandler;
+begin
+  Result := nil;
+  lHandler := FHandlerList;
+  while lHandler <> nil do
+  begin
+    Result := lHandler.HandleURI(ASocket);
+    if ASocket.ResponseInfo.Status <> hsOK then break;
+    if Result <> nil then break;
+    lHandler := lHandler.FNext;
+  end;
+end;
+
+procedure TLHTTPServer.LogAccess(const AMessage: string);
+begin
+  if Assigned(FOnAccess) then
+    FOnAccess(AMessage);
+end;
+
+procedure TLHTTPServer.RegisterHandler(AHandler: TURIHandler);
+begin
+  if AHandler = nil then exit;
+  AHandler.FNext := FHandlerList;
+  FHandlerList := AHandler;
+  if Eventer <> nil then
+    AHandler.RegisterWithEventer(Eventer);
+end;
+
+procedure TLHTTPServer.RegisterWithEventer;
+var
+  lHandler: TURIHandler;
+begin
+  inherited;
+  lHandler := FHandlerList;
+  while lHandler <> nil do
+  begin
+    lHandler.RegisterWithEventer(Eventer);
+    lHandler := lHandler.FNext;
+  end;
+end;
+
+{ TClientInput }
+
+type
+  TClientOutput = class(TOutputItem)
+  protected
+    FPersistent: boolean;
+    
+    procedure DoneInput; override;
+  public
+    constructor Create(ASocket: TLHTTPClientSocket);
+    destructor Destroy; override;
+    procedure FreeInstance; override;
+
+    function  HandleInput(ABuffer: pchar; ASize: integer): integer; override;
+    function  WriteBlock: TWriteBlockStatus; override;
+  end;
+
+constructor TClientOutput.Create(ASocket: TLHTTPClientSocket);
+begin
+  inherited Create(ASocket);
+  FPersistent := true;
+end;
+
+destructor TClientOutput.Destroy;
+begin
+  if FPersistent then exit; 
+  inherited;
+end;
+
+procedure TClientOutput.FreeInstance;
+begin
+  if FPersistent then exit; 
+  inherited;
+end;
+
+procedure TClientOutput.DoneInput;
+begin
+  TLHTTPClient(TLHTTPClientSocket(FSocket).FCreator).
+    DoDoneInput(TLHTTPClientSocket(FSocket));
+end;
+
+function  TClientOutput.HandleInput(ABuffer: pchar; ASize: integer): integer;
+begin
+  Result := TLHTTPClient(TLHTTPClientSocket(FSocket).FCreator).
+    DoHandleInput(TLHTTPClientSocket(FSocket), ABuffer, ASize);
+end;
+
+function  TClientOutput.WriteBlock: TWriteBlockStatus;
+begin
+  Result := TLHTTPClient(TLHTTPClientSocket(FSocket).FCreator).
+    DoWriteBlock(TLHTTPClientSocket(FSocket));
+end;
+
+{ TLHTTPClientSocket }
+
+constructor TLHTTPClientSocket.Create;
+begin
+  inherited Create;
+
+  FCurrentInput := TClientOutput.Create(Self);
+  ResetDefaults;
+end;
+
+destructor TLHTTPClientSocket.Destroy;
+begin
+  if Assigned(FCurrentInput) then begin
+    TClientOutput(FCurrentInput).FPersistent := false;
+    FreeAndNil(FCurrentInput);
+  end;
+  inherited;
+end;
+
+procedure TLHTTPClientSocket.AddContentLength(ALength: integer);
+begin
+  Inc(TLHTTPClient(FCreator).FHeaderOut.ContentLength, ALength);
+end;
+
+procedure TLHTTPClientSocket.Cancel(AError: TLHTTPClientError);
+begin
+  FError := AError;
+  Disconnect;
+end;
+
+function TLHTTPClientSocket.GetResponseReason: string;
+begin
+  Result := FResponse^.Reason;
+end;
+
+function TLHTTPClientSocket.GetResponseStatus: TLHTTPStatus;
+begin
+  Result := FResponse^.Status;
+end;
+
+procedure TLHTTPClientSocket.SendRequest;
+var
+  lMessage: TStringBuffer;
+  lTemp: string[23];
+  hasRangeStart, hasRangeEnd: boolean;
+begin
+  lMessage := InitStringBuffer(504);
+
+  AppendString(lMessage, HTTPMethodStrings[FRequest^.Method]);
+  AppendChar(lMessage, ' ');
+  AppendString(lMessage, FRequest^.URI);
+  AppendChar(lMessage, ' ');
+  AppendString(lMessage, 'HTTP/1.1'+#13#10+'Host: ');
+  AppendString(lMessage, TLHTTPClient(FCreator).Host);
+  if TLHTTPClient(FCreator).Port <> 80 then
+  begin
+    AppendChar(lMessage, ':');
+    Str(TLHTTPClient(FCreator).Port, lTemp);
+    AppendString(lMessage, lTemp);
+  end;
+  AppendString(lMessage, #13#10);
+  hasRangeStart := TLHTTPClient(FCreator).RangeStart <> high(qword);
+  hasRangeEnd := TLHTTPClient(FCreator).RangeEnd <> high(qword);
+  if hasRangeStart or hasRangeEnd then
+  begin
+    AppendString(lMessage, 'Range: bytes=');
+    if hasRangeStart then
+    begin
+      Str(TLHTTPClient(FCreator).RangeStart, lTemp);
+      AppendString(lMessage, lTemp);
+    end;
+    AppendChar(lMessage, '-');
+    if hasRangeEnd then
+    begin
+      Str(TLHTTPClient(FCreator).RangeEnd, lTemp);
+      AppendString(lMessage, lTemp);
+    end;
+  end;
+  with FHeaderOut^.ExtraHeaders do
+    AppendString(lMessage, Memory, Pos-Memory);
+  AppendString(lMessage, #13#10);
+  AddToOutput(TMemoryOutput.Create(Self, lMessage.Memory, 0,
+    lMessage.Pos-lMessage.Memory, true));
+  AddToOutput(FCurrentInput);
+  
+  WriteBlock;
+end;
+
+procedure TLHTTPClientSocket.ParseLine(pLineEnd: pchar);
+begin
+  if FError <> ceNone then
+    exit;
+
+  if FResponse^.Status = hsUnknown then
+  begin
+    ParseStatusLine(pLineEnd);
+    exit;
+  end;
+
+  inherited;
+end;
+
+procedure TLHTTPClientSocket.ParseStatusLine(pLineEnd: pchar);
+var
+  lPos: pchar;
+begin
+  lPos := FBufferPos;
+  repeat
+    if lPos >= pLineEnd then
+    begin
+      Cancel(ceMalformedStatusLine);
+      exit;
+    end;
+    if lPos^ = ' ' then
+      break;
+    Inc(lPos);
+  until false;
+  if not HTTPVersionCheck(FBufferPos, lPos, FResponse^.Version) then
+  begin
+    Cancel(ceMalformedStatusLine);
+    exit;
+  end;
+
+  if (FResponse^.Version > 11) then
+  begin
+    Cancel(ceVersionNotSupported);
+    exit;
+  end;
+
+  { status code }
+  Inc(lPos);
+  if (lPos+3 >= pLineEnd) or (lPos[3] <> ' ') then
+  begin
+    Cancel(ceMalformedStatusLine);
+    exit;
+  end;
+  FResponse^.Status := CodeToHTTPStatus((ord(lPos[0])-ord('0'))*100
+    + (ord(lPos[1])-ord('0'))*10 + (ord(lPos[2])-ord('0')));
+  if FResponse^.Status = hsUnknown then
+  begin
+    Cancel(ceMalformedStatusLine);
+    exit;
+  end;
+
+  Inc(lPos, 4);
+  if lPos < pLineEnd then
+    FResponse^.Reason := lPos;
+end;
+
+procedure TLHTTPClientSocket.ProcessHeaders;
+begin
+  if not ProcessEncoding then
+    Cancel(ceUnsupportedEncoding);
+
+  TLHTTPClient(FCreator).DoProcessHeaders(Self);
+end;
+
+procedure TLHTTPClientSocket.ResetDefaults;
+begin
+  inherited;
+
+  FError := ceNone;
+end;
+
+{ TLHTTPClient }
+
+constructor TLHTTPClient.Create(AOwner: TComponent);
+begin
+  FPort:=80;
+  inherited;
+
+  SocketClass := TLHTTPClientSocket;
+  FRequest.Method := hmGet;
+  FHeaderOut.ExtraHeaders := InitStringBuffer(256);
+  ResetRange;
+end;
+
+destructor TLHTTPClient.Destroy;
+begin
+  FreeMem(FHeaderOut.ExtraHeaders.Memory);
+  inherited;
+end;
+
+procedure TLHTTPClient.AddExtraHeader(const AHeader: string);
+begin
+  AppendString(FHeaderOut.ExtraHeaders, AHeader);
+  AppendString(FHeaderOut.ExtraHeaders, #13#10);
+end;
+
+procedure TLHTTPClient.ConnectEvent(aSocket: TLHandle);
+begin
+  inherited;
+  InternalSendRequest;
+end;
+
+procedure TLHTTPClient.DoDoneInput(ASocket: TLHTTPClientSocket);
+begin
+  Dec(FPendingResponses);
+  if FPendingResponses = 0 then
+    FState := hcsIdle
+  else
+    FState := hcsWaiting;
+  if Assigned(FOnDoneInput) then
+    FOnDoneInput(ASocket);
+end;
+
+function  TLHTTPClient.DoHandleInput(ASocket: TLHTTPClientSocket; ABuffer: pchar; ASize: integer): integer;
+begin
+  FState := hcsReceiving;
+  if Assigned(FOnInput) then
+    Result := FOnInput(ASocket, ABuffer, ASize)
+  else
+    Result := ASize;
+end;
+
+procedure TLHTTPClient.DoProcessHeaders(ASocket: TLHTTPClientSocket);
+begin
+  if Assigned(FOnProcessHeaders) then
+    FOnProcessHeaders(ASocket);
+end;
+
+function  TLHTTPClient.DoWriteBlock(ASocket: TLHTTPClientSocket): TWriteBlockStatus;
+begin
+  Result := wsDone;
+  if not FOutputEof then
+    if Assigned(FOnCanWrite) then
+      FOnCanWrite(ASocket, Result)
+end;
+
+function  TLHTTPClient.InitSocket(aSocket: TLSocket): TLSocket;
+begin
+  Result := inherited;
+  TLHTTPClientSocket(aSocket).FHeaderOut := @FHeaderOut;
+  TLHTTPClientSocket(aSocket).FRequest := @FRequest;
+  TLHTTPClientSocket(aSocket).FResponse := @FResponse;
+end;
+
+procedure TLHTTPClient.InternalSendRequest;
+begin
+  FOutputEof := false;
+  TLHTTPClientSocket(FIterator).SendRequest;
+  Inc(FPendingResponses);
+  if FState = hcsIdle then
+    FState := hcsWaiting;
+end;
+
+procedure TLHTTPClient.ResetRange;
+begin
+  FRequest.RangeStart := High(FRequest.RangeStart);
+  FRequest.RangeEnd := High(FRequest.RangeEnd);
+end;
+
+procedure TLHTTPClient.SendRequest;
+begin
+  if not Connected then
+    Connect(FHost, FPort)
+  else
+    InternalSendRequest;
+end;
+
+end.
+

+ 232 - 0
utils/fppkg/lnet/lhttputil.pp

@@ -0,0 +1,232 @@
+{ Utility routines for HTTP server component
+
+  Copyright (C) 2006 Micha Nelissen
+
+  This library is Free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  This program is diStributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
+  for more details.
+
+  You should have received a Copy of the GNU Library General Public License
+  along with This library; if not, Write to the Free Software Foundation,
+  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+  
+  This license has been modified. See file LICENSE.ADDON for more information.
+  Should you find these sources without a LICENSE File, please contact
+  me at [email protected]
+}
+
+unit lHTTPUtil;
+
+{$mode objfpc}{$h+}
+{$inline on}
+
+interface
+
+uses
+  sysutils, 
+  strutils;
+
+const
+  HTTPDateFormat: string = 'ddd, dd mmm yyyy hh:nn:ss';
+  HTTPAllowedChars = ['A'..'Z','a'..'z', '*','@','.','_','-', 
+      '0'..'9', '$','!','''','(',')'];
+
+type
+  PSearchRec = ^TSearchRec;
+
+  function GMTToLocalTime(ADateTime: TDateTime): TDateTime;
+  function LocalTimeToGMT(ADateTime: TDateTime): TDateTime;
+  function TryHTTPDateStrToDateTime(ADateStr: pchar; var ADest: TDateTime): boolean;
+
+  function SeparatePath(var InPath: string; out ExtraPath: string; const Mode:Longint;
+    ASearchRec: PSearchRec = nil): boolean;
+  function CheckPermission(const ADocument: pchar): boolean;
+  function HTTPDecode(AStr: pchar): pchar;
+  function HTTPEncode(const AStr: string): string;
+  function HexToNum(AChar: char): byte;
+
+implementation
+
+uses
+  lCommon;
+
+function GMTToLocalTime(ADateTime: TDateTime): TDateTime;
+begin
+  Result := ADateTime + (TZSeconds*1000/MSecsPerDay);
+end;
+
+function LocalTimeToGMT(ADateTime: TDateTime): TDateTime;
+begin
+  Result := ADateTime - (TZSeconds*1000/MSecsPerDay);
+end;
+
+function TryHTTPDateStrToDateTime(ADateStr: pchar; var ADest: TDateTime): boolean;
+var
+  lYear, lMonth, lDay: word;
+  lTime: array[0..2] of word;
+  I, lCode: integer;
+begin
+  if StrLen(ADateStr) < Length(HTTPDateFormat)+4 then exit(false);
+  { skip redundant short day string }
+  Inc(ADateStr, 5);
+  { day }
+  if ADateStr[2] = ' ' then
+    ADateStr[2] := #0
+  else 
+    exit(false);
+  Val(ADateStr, lDay, lCode);
+  if lCode <> 0 then exit(false);
+  Inc(ADateStr, 3);
+  { month }
+  lMonth := 1;
+  repeat
+    if CompareMem(ADateStr, @ShortMonthNames[lMonth][1], 3) then break;
+    inc(lMonth);
+    if lMonth = 13 then exit(false);
+  until false;
+  Inc(ADateStr, 4);
+  { year }
+  if ADateStr[4] = ' ' then
+    ADateStr[4] := #0
+  else
+    exit(false);
+  Val(ADateStr, lYear, lCode);
+  if lCode <> 0 then exit(false);
+  Inc(ADateStr, 5);
+  { hour, minute, second }
+  for I := 0 to 2 do
+  begin
+    ADateStr[2] := #0;
+    Val(ADateStr, lTime[I], lCode);
+    Inc(ADateStr, 3);
+    if lCode <> 0 then exit(false);
+  end;
+  ADest := EncodeDate(lYear, lMonth, lDay) + EncodeTime(lTime[0], lTime[1], lTime[2], 0);
+  Result := true;
+end;
+
+function SeparatePath(var InPath: string; out ExtraPath: string; const Mode:Longint; 
+  ASearchRec: PSearchRec = nil): boolean;
+var
+  lFullPath: string;
+  lPos: integer;
+  lSearchRec: TSearchRec;
+begin
+  if ASearchRec = nil then
+    ASearchRec := @lSearchRec;
+  ExtraPath := '';
+  if Length(InPath) <= 2 then exit(false);
+  lFullPath := InPath;
+  if InPath[Length(InPath)] = PathDelim then
+    SetLength(InPath, Length(InPath)-1);
+  repeat
+    Result := SysUtils.FindFirst(InPath, Mode, ASearchRec^) = 0;
+    SysUtils.FindClose(ASearchRec^);
+    if Result then
+    begin
+      ExtraPath := Copy(lFullPath, Length(InPath)+1, Length(lFullPath)-Length(InPath));
+      break;
+    end;
+    lPos := RPos(PathDelim, InPath);
+    if lPos > 0 then
+      SetLength(InPath, lPos-1)
+    else
+      break;
+  until false;
+end;
+
+function HexToNum(AChar: char): byte;
+begin
+  if ('0' <= AChar) and (AChar <= '9') then
+    Result := ord(AChar) - ord('0')
+  else if ('A' <= AChar) and (AChar <= 'F') then
+    Result := ord(AChar) - (ord('A') - 10)
+  else if ('a' <= AChar) and (AChar <= 'f') then
+    Result := ord(AChar) - (ord('a') - 10)
+  else
+    Result := 0;
+end;
+
+function HTTPDecode(AStr: pchar): pchar;
+var
+  lPos, lNext, lDest: pchar;
+begin
+  lDest := AStr;
+  repeat
+    lPos := AStr;
+    while not (lPos^ in ['%', '+', #0]) do
+      Inc(lPos);
+    if (lPos[0]='%') and (lPos[1] <> #0) and (lPos[2] <> #0) then
+    begin
+      lPos^ := char((HexToNum(lPos[1]) shl 4) + HexToNum(lPos[2]));
+      lNext := lPos+2;
+    end else if lPos[0] = '+' then
+    begin
+      lPos^ := ' ';
+      lNext := lPos+1;
+    end else
+      lNext := nil;
+    Inc(lPos);
+    if lDest <> AStr then
+      Move(AStr^, lDest^, lPos-AStr);
+    Inc(lDest, lPos-AStr);
+    AStr := lNext;
+  until lNext = nil;
+  Result := lDest;
+end;
+
+function HTTPEncode(const AStr: string): string;
+  { code from MvC's web }
+var
+  src, srcend, dest: pchar;
+  hex: string[2];
+  len: integer;
+begin
+  len := Length(AStr);
+  SetLength(Result, len*3); // Worst case scenario
+  if len = 0 then
+    exit;
+  dest := pchar(Result);
+  src := pchar(AStr);
+  srcend := src + len; 
+  while src < srcend do
+  begin 
+    if src^ in HTTPAllowedChars then
+      dest^ := src^
+    else if src^ = ' ' then
+      dest^ := '+'
+    else begin
+      dest^ := '%';
+      inc(dest);
+      hex := HexStr(Ord(src^),2);
+      dest^ := hex[1];
+      inc(dest);
+      dest^ := hex[2];
+    end;
+    inc(dest);
+    inc(src);
+  end;
+  SetLength(Result, dest - pchar(Result));
+end;
+
+function CheckPermission(const ADocument: pchar): boolean;
+var
+  lPos: pchar;
+begin
+  lPos := ADocument;
+  repeat
+    lPos := StrScan(lPos, '/');
+    if lPos = nil then exit(true);
+    if (lPos[1] = '.') and (lPos[2] = '.') and ((lPos[3] = '/') or (lPos[3] = #0)) then
+      exit(false);
+    inc(lPos);
+  until false;
+end;
+
+end.

+ 108 - 0
utils/fppkg/lnet/lmimetypes.pp

@@ -0,0 +1,108 @@
+{ Mime types helper
+
+  Copyright (C) 2006 Micha Nelissen
+
+  This library is Free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  This program is diStributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
+  for more details.
+
+  You should have received a Copy of the GNU Library General Public License
+  along with This library; if not, Write to the Free Software Foundation,
+  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+  
+  This license has been modified. See file LICENSE.ADDON for more information.
+  Should you find these sources without a LICENSE File, please contact
+  me at [email protected]
+}
+
+unit lMimeTypes;
+
+{$mode objfpc}{$h+}
+
+interface
+
+uses
+  classes, sysutils, strutils;
+
+type
+  TStringObject = class(TObject)
+    Str: string;
+  end;
+  
+  procedure InitMimeList(const aFileName: string);
+
+var
+  MimeList: TStringList = nil;
+
+implementation
+
+var
+  MimeFileName: string;
+
+procedure InitMimeList(const aFileName: string);
+var
+  MimeFile: Text;
+  lPos, lNextPos: integer;
+  lLine, lName: string;
+  lStrObj: TStringObject;
+  lBuffer: array[1..32*1024] of byte;
+begin
+  if not Assigned(MimeList) then begin
+    MimeFileName := aFileName;
+    MimeList := TStringList.Create;
+    if FileExists(MimeFileName) then
+    begin
+      Assign(MimeFile, MimeFileName);
+      Reset(MimeFile);
+      SetTextBuf(MimeFile, lBuffer);
+      while not Eof(MimeFile) do
+      begin
+        ReadLn(MimeFile, lLine);
+        if (Length(lLine) = 0) or (lLine[1] = '#') then
+          continue;
+
+        lPos := Pos(#9, lLine);
+        if lPos = 0 then
+          continue;
+        lName := Copy(lLine, 1, lPos-1);
+
+        while (lPos <= Length(lLine)) and (lLine[lPos] in [#9,' ']) do
+          Inc(lPos);
+        if lPos > Length(lLine) then
+          continue;
+        repeat
+          lNextPos := PosEx(' ', lLine, lPos);
+          if lNextPos = 0 then
+            lNextPos := Length(lLine)+1;
+          lStrObj := TStringObject.Create;
+          lStrObj.Str := lName;
+          MimeList.AddObject('.'+Copy(lLine, lPos, lNextPos-lPos), lStrObj);
+          lPos := lNextPos+1;
+        until lPos > Length(lLine);
+      end;
+      close(MimeFile);
+    end;
+    MimeList.Sorted := true;
+  end;
+end;
+
+procedure FreeMimeList;
+var
+  I: integer;
+begin
+  if Assigned(MimeList) then begin
+    for I := 0 to MimeList.Count - 1 do
+      MimeList.Objects[I].Free;
+    FreeAndNil(MimeList);
+  end;
+end;
+
+finalization
+  FreeMimeList;
+end.

+ 1 - 1
utils/fppkg/lnet/lnet.pp

@@ -440,7 +440,7 @@ procedure TLSocket.LogError(const msg: string; const ernum: Integer);
 begin
   if Assigned(FOnError) then
     if ernum > 0 then
-      FOnError(Self, msg + ': ' + LStrError(ernum))
+      FOnError(Self, msg + '[' + IntToStr(ernum) + ']: ' + LStrError(ernum))
     else
       FOnError(Self, msg);
 end;

+ 185 - 0
utils/fppkg/lnet/lprocess.pp

@@ -0,0 +1,185 @@
+{ Asynchronous process support
+
+  Copyright (C) 2006 Micha Nelissen
+
+  This library is Free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  This program is diStributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
+  for more details.
+
+  You should have received a Copy of the GNU Library General Public License
+  along with This library; if not, Write to the Free Software Foundation,
+  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+  
+  This license has been modified. See file LICENSE.ADDON for more information.
+  Should you find these sources without a LICENSE File, please contact
+  me at [email protected]
+}
+
+unit lProcess;
+
+{$mode objfpc}{$h+}
+
+interface
+
+uses
+  sysutils, classes, process, levents, pipes;
+
+type
+  TLInputPipeStream = class(TInputPipeStream)
+  protected
+    FEvent: TLHandle;
+  public
+    function Read(var Buffer; Count: longint): longint; override;
+  end;
+
+  TLOutputPipeStream = class(TOutputPipeStream)
+  protected
+    FEvent: TLHandle;
+  public
+    function Write(const Buffer; Count: longint): longint; override;          
+  end;
+
+  TLProcess = class(TProcess)
+  protected
+    FInputEvent: TLHandle;
+    FOutputEvent: TLHandle;
+    FStderrEvent: TLHandle;
+    FEventer: TLEventer;
+
+    function  GetOnNeedInput: TLHandleEvent;
+    function  GetOnHasOutput: TLHandleEvent;
+    function  GetOnHasStderr: TLHandleEvent;
+    procedure SetOnNeedInput(NewOnInput: TLHandleEvent);
+    procedure SetOnHasOutput(NewOnOutput: TLHandleEvent);
+    procedure SetOnHasStderr(NewOnStderr: TLHandleEvent);
+  public
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+    
+    procedure CloseInput; override;
+    procedure CloseOutput; override;
+    procedure CloseStderr; override;
+    procedure Execute; override;
+
+    property InputEvent: TLHandle read FInputEvent;
+    property OutputEvent: TLHandle read FOutputEvent;
+    property StderrEvent: TLHandle read FStderrEvent;
+    property Eventer: TLEventer read FEventer write FEventer;
+    property OnNeedInput: TLHandleEvent read GetOnNeedInput write SetOnNeedInput;
+    property OnHasOutput: TLHandleEvent read GetOnHasOutput write SetOnHasOutput;
+    property OnHasStderr: TLHandleEvent read GetOnHasStderr write SetOnHasStderr;
+  end;
+
+implementation
+
+function TLInputPipeStream.Read(var Buffer; Count: longint): longint;
+begin
+  Result := inherited;
+  FEvent.IgnoreRead := false;
+end;
+
+function TLOutputPipeStream.Write(const Buffer; Count: longint): longint;
+begin
+  Result := inherited;
+  FEvent.IgnoreWrite := false;
+end;
+
+constructor TLProcess.Create(AOwner: TComponent);
+begin
+  inherited;
+
+  FInputEvent := TLHandle.Create;
+  FOutputEvent := TLHandle.Create;
+  FStderrEvent := TLHandle.Create;
+end;
+
+destructor TLProcess.Destroy;
+begin
+  inherited;
+  FInputEvent.Free;
+  FOutputEvent.Free;
+  FStderrEvent.Free;
+end;
+
+procedure TLProcess.CloseInput;
+begin
+  FEventer.UnplugHandle(FInputEvent);
+  inherited;
+end;
+
+procedure TLProcess.CloseOutput;
+begin
+  FEventer.UnplugHandle(FOutputEvent);
+  inherited;
+end;
+
+procedure TLProcess.CloseStderr;
+begin
+  FEventer.UnplugHandle(FStderrEvent);
+  inherited;
+end;
+
+procedure TLProcess.Execute;
+begin
+  inherited;
+
+  if (poUsePipes in Options) and (FEventer <> nil) then
+  begin
+    if Input <> nil then
+    begin
+      FInputEvent.Handle := Input.Handle;
+      FInputEvent.IgnoreRead := true;
+      FEventer.AddHandle(FInputEvent);
+    end;
+    if Output <> nil then
+    begin
+      FOutputEvent.Handle := Output.Handle;
+      FOutputEvent.IgnoreWrite := true;
+      FEventer.AddHandle(FOutputEvent);
+    end;
+    if Stderr <> nil then
+    begin
+      FStderrEvent.Handle := Stderr.Handle;
+      FStderrEvent.IgnoreWrite := true;
+      FEventer.AddHandle(FStderrEvent);
+    end;
+  end;
+end;
+
+function TLProcess.GetOnNeedInput: TLHandleEvent;
+begin
+  Result := FInputEvent.OnWrite;
+end;
+
+function TLProcess.GetOnHasOutput: TLHandleEvent;
+begin
+  Result := FOutputEvent.OnRead;
+end;
+
+function TLProcess.GetOnHasStderr: TLHandleEvent;
+begin
+  Result := FStderrEvent.OnRead;
+end;
+
+procedure TLProcess.SetOnNeedInput(NewOnInput: TLHandleEvent);
+begin
+  FInputEvent.OnWrite := NewOnInput;
+end;
+
+procedure TLProcess.SetOnHasOutput(NewOnOutput: TLHandleEvent);
+begin
+  FOutputEvent.OnRead := NewOnOutput;
+end;
+
+procedure TLProcess.SetOnHasStderr(NewOnStderr: TLHandleEvent);
+begin
+  FStderrEvent.OnRead := NewOnStderr;
+end;
+
+end.

+ 530 - 0
utils/fppkg/lnet/lsmtp.pp

@@ -0,0 +1,530 @@
+{ lNet SMTP unit
+
+  CopyRight (C) 2005-2006 Ales Katona
+
+  This library is Free software; you can rediStribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  This program is diStributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
+  for more details.
+
+  You should have received a Copy of the GNU Library General Public License
+  along with This library; if not, Write to the Free Software Foundation,
+  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+  
+  This license has been modified. See File LICENSE.ADDON for more inFormation.
+  Should you find these sources without a LICENSE File, please contact
+  me at [email protected]
+}
+
+unit lsmtp;
+
+{$mode objfpc}{$H+}
+{$inline on}
+
+interface
+
+uses
+  Classes, lNet, lEvents, lCommon;
+  
+type
+  TLSMTP = class;
+  TLSMTPClient = class;
+  
+  TLSMTPStatus = (ssNone, ssCon, ssHelo, ssEhlo, ssMail,
+                  ssRcpt, ssData, ssRset, ssQuit);
+
+  TLSMTPStatusSet = set of TLSMTPStatus;
+
+  TLSMTPStatusRec = record
+    Status: TLSMTPStatus;
+    Args: array[1..2] of string;
+  end;
+  
+  { TLSMTPStatusFront }
+  {$DEFINE __front_type__  :=  TLSMTPStatusRec}
+  {$i lcontainersh.inc}
+  TLSMTPStatusFront = TLFront;
+
+  TLSMTPClientStatusEvent = procedure (aSocket: TLSocket;
+                                       const aStatus: TLSMTPStatus) of object;
+
+  TLSMTP = class(TLComponent)
+   protected
+    FConnection: TLTcp;
+   protected
+    function GetTimeout: DWord;
+    procedure SetTimeout(const AValue: DWord);
+    
+    function GetConnected: Boolean;
+
+    function GetSocketClass: TLSocketClass;
+    procedure SetSocketClass(const AValue: TLSocketClass);
+    
+    function GetEventer: TLEventer;
+    procedure SetEventer(Value: TLEventer);
+   public
+    constructor Create(aOwner: TComponent); override;
+    destructor Destroy; override;
+   public
+    property Connected: Boolean read GetConnected;
+    property Connection: TLTcp read FConnection;
+
+    property SocketClass: TLSocketClass read GetSocketClass write SetSocketClass;
+    property Eventer: TLEventer read GetEventer write SetEventer;
+    property Timeout: DWord read GetTimeout write SetTimeout;
+  end;
+
+  { TLSMTPClient }
+
+  TLSMTPClient = class(TLSMTP, ILClient)
+   protected
+    FStatus: TLSMTPStatusFront;
+    FCommandFront: TLSMTPStatusFront;
+    FPipeLine: Boolean;
+
+    FOnConnect: TLSocketEvent;
+    FOnReceive: TLSocketEvent;
+    FOnDisconnect: TLSocketEvent;
+    FOnSuccess: TLSMTPClientStatusEvent;
+    FOnFailure: TLSMTPClientStatusEvent;
+    FOnError: TLSocketErrorEvent;
+
+    FSL: TStringList;
+    FStatusSet: TLSMTPStatusSet;
+    FMessage: string;
+   protected
+    procedure OnEr(const msg: string; aSocket: TLSocket);
+    procedure OnRe(aSocket: TLSocket);
+    procedure OnCo(aSocket: TLSocket);
+    procedure OnDs(aSocket: TLSocket);
+   protected
+    function CanContinue(const aStatus: TLSMTPStatus; const Arg1, Arg2: string): Boolean;
+    
+    function CleanInput(var s: string): Integer;
+    
+    procedure EvaluateAnswer(const Ans: string);
+    
+    procedure ExecuteFrontCommand;
+   public
+    constructor Create(aOwner: TComponent); override;
+    destructor Destroy; override;
+    
+    function Connect(const aHost: string; const aPort: Word = 25): Boolean; virtual;
+    function Connect: Boolean; virtual;
+    
+    function Get(var aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; virtual;
+    function GetMessage(out msg: string; aSocket: TLSocket = nil): Integer; virtual;
+
+    procedure SendMail(const From, Recipients, Subject, Msg: string);
+    procedure Helo(aHost: string = '');
+    procedure Ehlo(aHost: string = '');
+    procedure Mail(const From: string);
+    procedure Rcpt(const RcptTo: string);
+    procedure Data(const Msg: string);
+    procedure Rset;
+    procedure Quit;
+    
+    procedure Disconnect; override;
+    
+    procedure CallAction; override;
+   public
+    property PipeLine: Boolean read FPipeLine write FPipeLine;
+    property StatusSet: TLSMTPStatusSet read FStatusSet write FStatusSet;
+    property OnConnect: TLSocketEvent read FOnConnect write FOnConnect;
+    property OnReceive: TLSocketEvent read FOnReceive write FOnReceive;
+    property OnDisconnect: TLSocketEvent read FOnDisconnect write FOnDisconnect;
+    property OnSuccess: TLSMTPClientStatusEvent read FOnSuccess write FOnSuccess;
+    property OnFailure: TLSMTPClientStatusEvent read FOnFailure write FOnFailure;
+    property OnError: TLSocketErrorEvent read FOnError write FOnError;
+  end;
+
+implementation
+
+uses
+  SysUtils;
+
+const
+  EMPTY_REC: TLSMTPStatusRec = (Status: ssNone; Args: ('', ''));
+  SLE                        = #13#10;
+  
+{$i lcontainers.inc}
+
+function StatusToStr(const aStatus: TLSMTPStatus): string;
+const
+  STATAR: array[ssNone..ssQuit] of string = ('ssNone', 'ssCon', 'ssHelo', 'ssEhlo', 'ssMail',
+                                             'ssRcpt', 'ssData', 'ssRset', 'ssQuit');
+begin
+  Result := STATAR[aStatus];
+end;
+
+function MakeStatusRec(const aStatus: TLSMTPStatus; const Arg1, Arg2: string): TLSMTPStatusRec;
+begin
+  Result.Status := aStatus;
+  Result.Args[1] := Arg1;
+  Result.Args[2] := Arg2;
+end;
+
+{ TLSMTP }
+
+function TLSMTP.GetTimeout: DWord;
+begin
+  Result := FConnection.Timeout;
+end;
+
+procedure TLSMTP.SetTimeout(const AValue: DWord);
+begin
+  FConnection.Timeout := aValue;
+end;
+
+function TLSMTP.GetConnected: Boolean;
+begin
+  Result := FConnection.Connected;
+end;
+
+function TLSMTP.GetSocketClass: TLSocketClass;
+begin
+  Result := FConnection.SocketClass;
+end;
+
+procedure TLSMTP.SetSocketClass(const AValue: TLSocketClass);
+begin
+  FConnection.SocketClass := AValue;
+end;
+
+function TLSMTP.GetEventer: TLEventer;
+begin
+  Result := FConnection.Eventer;
+end;
+
+procedure TLSMTP.SetEventer(Value: TLEventer);
+begin
+  FConnection.Eventer := Value;
+end;
+
+constructor TLSMTP.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  
+  FConnection := TLTcp.Create(nil);
+end;
+
+destructor TLSMTP.Destroy;
+begin
+  FConnection.Free;
+
+  inherited Destroy;
+end;
+
+{ TLSMTPClient }
+
+constructor TLSMTPClient.Create(aOwner: TComponent);
+begin
+  inherited Create(aOwner);
+  FPort := 25;
+  FStatusSet := []; // empty set for "ok/not-ok" Event
+  FSL := TStringList.Create;
+  FHost := '';
+  FMessage := '';
+//  {$warning TODO: fix pipelining support when server does it}
+  FPipeLine := False;
+  
+  FConnection.OnError := @OnEr;
+  FConnection.OnReceive := @OnRe;
+  FConnection.OnConnect := @OnCo;
+
+  FStatus := TLSMTPStatusFront.Create(EMPTY_REC);
+  FCommandFront := TLSMTPStatusFront.Create(EMPTY_REC);
+end;
+
+destructor TLSMTPClient.Destroy;
+begin
+  Quit;
+  FSL.Free;
+  FStatus.Free;
+  FCommandFront.Free;
+
+  inherited Destroy;
+end;
+
+procedure TLSMTPClient.OnEr(const msg: string; aSocket: TLSocket);
+begin
+  if Assigned(FOnError) then
+    FOnError(msg, aSocket);
+end;
+
+procedure TLSMTPClient.OnRe(aSocket: TLSocket);
+begin
+  if Assigned(FOnReceive) then
+    FOnReceive(aSocket);
+end;
+
+procedure TLSMTPClient.OnCo(aSocket: TLSocket);
+begin
+  if Assigned(FOnConnect) then
+    FOnConnect(aSocket);
+end;
+
+procedure TLSMTPClient.OnDs(aSocket: TLSocket);
+begin
+  if Assigned(FOnDisconnect) then
+    FOnDisconnect(aSocket);
+end;
+
+function TLSMTPClient.CanContinue(const aStatus: TLSMTPStatus; const Arg1, Arg2: string): Boolean;
+begin
+  Result := FPipeLine or FStatus.Empty;
+  if not Result then
+    FCommandFront.Insert(MakeStatusRec(aStatus, Arg1, Arg2));
+end;
+
+function TLSMTPClient.CleanInput(var s: string): Integer;
+var
+  i: Integer;
+begin
+  FSL.Text := s;
+  if FSL.Count > 0 then
+    for i := 0 to FSL.Count-1 do
+      if Length(FSL[i]) > 0 then EvaluateAnswer(FSL[i]);
+  s := StringReplace(s, SLE, LineEnding, [rfReplaceAll]);
+  i := Pos('PASS', s);
+  if i > 0 then
+    s := Copy(s, 1, i-1) + 'PASS';
+  Result := Length(s);
+end;
+
+procedure TLSMTPClient.EvaluateAnswer(const Ans: string);
+
+  function GetNum: Integer;
+  begin
+    try
+      Result := StrToInt(Copy(Ans, 1, 3));
+    except
+      Result := -1;
+    end;
+  end;
+  
+  function ValidResponse(const Answer: string): Boolean; inline;
+  begin
+    Result := (Length(Ans) >= 3) and
+            (Ans[1] in ['1'..'5']) and
+            (Ans[2] in ['0'..'9']) and
+            (Ans[3] in ['0'..'9']);
+
+    if Result then
+      Result := (Length(Ans) = 3) or ((Length(Ans) > 3) and (Ans[4] = ' '));
+  end;
+  
+  procedure Eventize(const aStatus: TLSMTPStatus; const Res: Boolean);
+  begin
+    if Res then begin
+      if Assigned(FOnSuccess) and (aStatus in FStatusSet) then
+        FOnSuccess(FConnection.Iterator, aStatus);
+    end else begin
+      if Assigned(FOnFailure) and (aStatus in FStatusSet) then
+        FOnFailure(FConnection.Iterator, aStatus);
+    end;
+  end;
+  
+var
+  x: Integer;
+begin
+  x := GetNum;
+  if ValidResponse(Ans) and not FStatus.Empty then
+    case FStatus.First.Status of
+      ssCon,
+      ssHelo,
+      ssEhlo: case x of
+                200..299: begin
+                            Eventize(FStatus.First.Status, True);
+                            FStatus.Remove;
+                          end;
+              else        begin
+                            Eventize(FStatus.First.Status, False);
+                            Disconnect;
+                          end;
+              end;
+               
+      ssMail,
+      ssRcpt: begin
+                Eventize(FStatus.First.Status, (x >= 200) and (x < 299));
+                FStatus.Remove;
+              end;
+
+      ssData: case x of
+                200..299: begin
+                            Eventize(FStatus.First.Status, True);
+                            FStatus.Remove;
+                          end;
+                300..399: if Length(FMessage) > 0 then begin
+                            FConnection.SendMessage(FMessage);
+                            FMessage := '';
+                          end;
+              else        begin
+                            Eventize(FStatus.First.Status, False);
+                            FStatus.Remove;
+                          end;
+              end;
+              
+      ssRset: begin
+                Eventize(FStatus.First.Status, (x >= 200) and (x < 299));
+                FStatus.Remove;
+              end;
+              
+      ssQuit: begin
+                Eventize(FStatus.First.Status, (x >= 200) and (x < 299));
+                FStatus.Remove;
+                if Assigned(FOnDisconnect) then
+                  FOnDisconnect(FConnection.Iterator);
+                Disconnect;
+              end;
+    end;
+  if FStatus.Empty and not FCommandFront.Empty then
+    ExecuteFrontCommand;
+end;
+
+procedure TLSMTPClient.ExecuteFrontCommand;
+begin
+  with FCommandFront.First do
+    case Status of
+      ssHelo: Helo(Args[1]);
+      ssEhlo: Ehlo(Args[1]);
+      ssMail: Mail(Args[1]);
+      ssRcpt: Rcpt(Args[1]);
+      ssData: Data(Args[1]);
+      ssRset: Rset;
+      ssQuit: Quit;
+    end;
+  FCommandFront.Remove;
+end;
+
+function TLSMTPClient.Connect(const aHost: string; const aPort: Word = 25): Boolean;
+begin
+  Result := False;
+  Disconnect;
+  if FConnection.Connect(aHost, aPort) then begin
+    FHost := aHost;
+    FPort := aPort;
+    FStatus.Insert(MakeStatusRec(ssCon, '', ''));
+    Result := True;
+  end;
+end;
+
+function TLSMTPClient.Connect: Boolean;
+begin
+  Result := Connect(FHost, FPort);
+end;
+
+function TLSMTPClient.Get(var aData; const aSize: Integer; aSocket: TLSocket): Integer;
+var
+  s: string;
+begin
+  Result := FConnection.Get(aData, aSize, aSocket);
+  if Result > 0 then begin
+    SetLength(s, Result);
+    Move(aData, PChar(s)^, Result);
+    CleanInput(s);
+  end;
+end;
+
+function TLSMTPClient.GetMessage(out msg: string; aSocket: TLSocket): Integer;
+begin
+  Result := FConnection.GetMessage(msg, aSocket);
+  if Result > 0 then
+    Result := CleanInput(msg);
+end;
+
+procedure TLSMTPClient.SendMail(const From, Recipients, Subject, Msg: string);
+var
+  i: Integer;
+begin
+  if (Length(Recipients) > 0) and (Length(From) > 0) then begin
+    Mail(From);
+    FSL.CommaText := StringReplace(Recipients, ' ', ',', [rfReplaceAll]);
+    for i := 0 to FSL.Count-1 do
+      Rcpt(FSL[i]);
+    Data('From: ' + From + SLE + 'Subject: ' + Subject + SLE + 'To: ' + FSL.CommaText + SLE + Msg);
+    Rset;
+  end;
+end;
+
+procedure TLSMTPClient.Helo(aHost: string = '');
+begin
+  if Length(Host) = 0 then
+    aHost := FHost;
+  if CanContinue(ssHelo, aHost, '') then begin
+    FConnection.SendMessage('HELO ' + aHost + SLE);
+    FStatus.Insert(MakeStatusRec(ssHelo, '', ''));
+  end;
+end;
+
+procedure TLSMTPClient.Ehlo(aHost: string = '');
+begin
+  if Length(aHost) = 0 then
+    aHost := FHost;
+  if CanContinue(ssEhlo, aHost, '') then begin
+    FConnection.SendMessage('EHLO ' + aHost + SLE);
+    FStatus.Insert(MakeStatusRec(ssEhlo, '', ''));
+  end;
+end;
+
+procedure TLSMTPClient.Mail(const From: string);
+begin
+  if CanContinue(ssMail, From, '') then begin
+    FConnection.SendMessage('MAIL FROM:' + '<' + From + '>' + SLE);
+    FStatus.Insert(MakeStatusRec(ssMail, '', ''));
+  end;
+end;
+
+procedure TLSMTPClient.Rcpt(const RcptTo: string);
+begin
+  if CanContinue(ssRcpt, RcptTo, '') then begin
+    FConnection.SendMessage('RCPT TO:' + '<' + RcptTo + '>' + SLE);
+    FStatus.Insert(MakeStatusRec(ssRcpt, '', ''));
+  end;
+end;
+
+procedure TLSMTPClient.Data(const Msg: string);
+begin
+  if CanContinue(ssData, Msg, '') then begin
+    // TODO: clean SLEs and '.' on line starts
+    FMessage := Msg + SLE + '.' + SLE;
+    FConnection.SendMessage('DATA' + SLE);
+    FStatus.Insert(MakeStatusRec(ssData, '', ''));
+  end;
+end;
+
+procedure TLSMTPClient.Rset;
+begin
+  if CanContinue(ssRset, '', '') then begin
+    FConnection.SendMessage('RSET' + SLE);
+    FStatus.Insert(MakeStatusRec(ssRset, '', ''));
+  end;
+end;
+
+procedure TLSMTPClient.Quit;
+begin
+  if CanContinue(ssQuit, '', '') then begin
+    FConnection.SendMessage('QUIT' + SLE);
+    FStatus.Insert(MakeStatusRec(ssQuit, '', ''));
+  end;
+end;
+
+procedure TLSMTPClient.Disconnect;
+begin
+  FConnection.Disconnect;
+  FStatus.Clear;
+  FCommandFront.Clear;
+end;
+
+procedure TLSMTPClient.CallAction;
+begin
+  FConnection.CallAction;
+end;
+
+end.
+

+ 21 - 0
utils/fppkg/lnet/lspawnfcgi.pp

@@ -0,0 +1,21 @@
+unit lSpawnFCGI;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Sockets, lNet, lCommon;
+
+  function SpawnFCGIProcess(App, Enviro: string; const aPort: Word): Integer;
+
+implementation
+
+{$ifdef UNIX}
+  {$i lspawnfcgiunix.inc}
+{$else}
+  {$i lspawnfcgiwin.inc}
+{$endif}
+
+end.
+

+ 61 - 0
utils/fppkg/lnet/ltimer.pp

@@ -0,0 +1,61 @@
+unit ltimer;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils;
+
+type
+
+  { TLTimer }
+
+  TLTimer = class(TObject)
+  protected
+    FOnTimer: TNotifyEvent;
+    FInterval: TDateTime;
+    FStarted: TDateTime;
+    FOneShot: Boolean;
+    FEnabled: Boolean;
+
+    function  GetInterval: Integer;
+    procedure SetInterval(const aValue: Integer);
+  public
+    procedure CallAction;
+    property Enabled: Boolean read FEnabled write FEnabled;
+    property Interval: Integer read GetInterval write SetInterval;
+    property OneShot: Boolean read FOneShot write FOneShot;
+    property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
+  end;
+
+implementation
+
+{ TLTimer }
+
+function TLTimer.GetInterval: Integer;
+begin
+  Result := Round(FInterval * MSecsPerDay);
+end;
+
+procedure TLTimer.SetInterval(const aValue: Integer);
+begin
+  FInterval := AValue / MSecsPerDay;
+  FStarted := Now;
+  FEnabled := true;
+end;
+
+procedure TLTimer.CallAction;
+begin
+  if FEnabled and Assigned(FOnTimer) and (Now - FStarted >= FInterval) then 
+  begin
+    FOnTimer(Self);
+    if not FOneShot then
+      FStarted := Now
+    else
+      FEnabled := false;
+  end;
+end;
+
+end.
+

+ 0 - 1452
utils/fppkg/lnet/openssl.pp

@@ -1,1452 +0,0 @@
-unit OpenSSL;
-
-{==============================================================================|
-| Project : Ararat Synapse                                       | 003.004.001 |
-|==============================================================================|
-| Content: SSL support by OpenSSL                                              |
-|==============================================================================|
-| Copyright (c)1999-2005, Lukas Gebauer                                        |
-| All rights reserved.                                                         |
-|                                                                              |
-| Redistribution and use in source and binary forms, with or without           |
-| modification, are permitted provided that the following conditions are met:  |
-|                                                                              |
-| Redistributions of source code must retain the above copyright notice, this  |
-| list of conditions and the following disclaimer.                             |
-|                                                                              |
-| Redistributions in binary form must reproduce the above copyright notice,    |
-| this list of conditions and the following disclaimer in the documentation    |
-| and/or other materials provided with the distribution.                       |
-|                                                                              |
-| Neither the name of Lukas Gebauer nor the names of its contributors may      |
-| be used to endorse or promote products derived from this software without    |
-| specific prior written permission.                                           |
-|                                                                              |
-| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  |
-| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE    |
-| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE   |
-| ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR  |
-| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL       |
-| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR   |
-| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER   |
-| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT           |
-| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY    |
-| OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH  |
-| DAMAGE.                                                                      |
-|==============================================================================|
-| The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
-| Portions created by Lukas Gebauer are Copyright (c)2002-2005.                |
-| All Rights Reserved.                                                         |
-|==============================================================================|
-| Contributor(s):                                                              |
-|==============================================================================|
-| FreePascal basic cleanup (original worked too): Ales Katona                  |
-|==============================================================================|
-| History: see HISTORY.HTM from distribution package                           |
-|          (Found at URL: http://www.ararat.cz/synapse/)                       |
-|==============================================================================}
-
-{
-Special thanks to Gregor Ibic <[email protected]>
- (Intelicom d.o.o., http://www.intelicom.si)
- for good inspiration about begin with SSL programming.
-}
-
-{$MODE DELPHI}{$H+}
-
-{:@abstract(OpenSSL support)
-
-This unit is Pascal interface to OpenSSL library (used by @link(ssl_openssl) unit).
-OpenSSL is loaded dynamicly on-demand. If this library is not found in system,
-requested OpenSSL function just return errorcode.
-}
-
-interface
-
-uses
-  DynLibs;
-
-var
-  {$IFDEF WINDOWS}
-  DLLSSLName: string = 'ssleay32.dll';
-  DLLSSLName2: string = 'libssl32.dll';
-  DLLUtilName: string = 'libeay32.dll';
-  {$ELSE}
-  DLLSSLName: string = 'libssl.so';
-  DLLUtilName: string = 'libcrypto.so';
-  {$ENDIF}
-
-type
-  SslPtr = Pointer;
-  PSslPtr = ^SslPtr;
-  PSSL_CTX = SslPtr;
-  PSSL = SslPtr;
-  PSSL_METHOD = SslPtr;
-  PX509 = SslPtr;
-  PX509_NAME = SslPtr;
-  PEVP_MD	= SslPtr;
-  PInteger = ^Integer;
-  PBIO_METHOD = SslPtr;
-  PBIO = SslPtr;
-  EVP_PKEY = SslPtr;
-  PRSA = SslPtr;
-  PASN1_UTCTIME = SslPtr;
-  PASN1_INTEGER = SslPtr;
-  PPasswdCb = SslPtr;
-  PFunction = procedure;
-
-  DES_cblock = array[0..7] of Byte;
-  PDES_cblock = ^DES_cblock;
-  des_ks_struct = packed record
-    ks: DES_cblock;
-    weak_key: Integer;
-  end;
-  des_key_schedule = array[1..16] of des_ks_struct;
-
-const
-  EVP_MAX_MD_SIZE = 16 + 20;
-
-  SSL_ERROR_NONE = 0;
-  SSL_ERROR_SSL = 1;
-  SSL_ERROR_WANT_READ = 2;
-  SSL_ERROR_WANT_WRITE = 3;
-  SSL_ERROR_WANT_X509_LOOKUP = 4;
-  SSL_ERROR_SYSCALL = 5; //look at error stack/return value/errno
-  SSL_ERROR_ZERO_RETURN = 6;
-  SSL_ERROR_WANT_CONNECT = 7;
-  SSL_ERROR_WANT_ACCEPT = 8;
-
-  SSL_OP_NO_SSLv2 = $01000000;
-  SSL_OP_NO_SSLv3 = $02000000;
-  SSL_OP_NO_TLSv1 = $04000000;
-  SSL_OP_ALL = $000FFFFF;
-  SSL_VERIFY_NONE = $00;
-  SSL_VERIFY_PEER = $01;
-
-  OPENSSL_DES_DECRYPT = 0;
-  OPENSSL_DES_ENCRYPT = 1;
-
-  X509_V_OK =	0;
-  X509_V_ILLEGAL = 1;
-  X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT = 2;
-  X509_V_ERR_UNABLE_TO_GET_CRL = 3;
-  X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE = 4;
-  X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE = 5;
-  X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY = 6;
-  X509_V_ERR_CERT_SIGNATURE_FAILURE = 7;
-  X509_V_ERR_CRL_SIGNATURE_FAILURE = 8;
-  X509_V_ERR_CERT_NOT_YET_VALID = 9;
-  X509_V_ERR_CERT_HAS_EXPIRED = 10;
-  X509_V_ERR_CRL_NOT_YET_VALID = 11;
-  X509_V_ERR_CRL_HAS_EXPIRED = 12;
-  X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD = 13;
-  X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD = 14;
-  X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD = 15;
-  X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD = 16;
-  X509_V_ERR_OUT_OF_MEM = 17;
-  X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT = 18;
-  X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN = 19;
-  X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY = 20;
-  X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21;
-  X509_V_ERR_CERT_CHAIN_TOO_LONG = 22;
-  X509_V_ERR_CERT_REVOKED = 23;
-  X509_V_ERR_INVALID_CA = 24;
-  X509_V_ERR_PATH_LENGTH_EXCEEDED = 25;
-  X509_V_ERR_INVALID_PURPOSE = 26;
-  X509_V_ERR_CERT_UNTRUSTED = 27;
-  X509_V_ERR_CERT_REJECTED = 28;
-  //These are 'informational' when looking for issuer cert
-  X509_V_ERR_SUBJECT_ISSUER_MISMATCH = 29;
-  X509_V_ERR_AKID_SKID_MISMATCH = 30;
-  X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH = 31;
-  X509_V_ERR_KEYUSAGE_NO_CERTSIGN = 32;
-  X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER = 33;
-  X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION = 34;
-  //The application is not happy
-  X509_V_ERR_APPLICATION_VERIFICATION = 50;
-
-  SSL_FILETYPE_ASN1	= 2;
-  SSL_FILETYPE_PEM = 1;
-  EVP_PKEY_RSA = 6;
-
-var
-  SSLLibHandle: TLibHandle = 0;
-  SSLUtilHandle: TLibHandle = 0;
-  SSLLibFile: string = '';
-  SSLUtilFile: string = '';
-
-// libssl.dll
-  function SslGetError(s: PSSL; ret_code: Integer):Integer;
-  function SslLibraryInit:Integer;
-  procedure SslLoadErrorStrings;
-//  function SslCtxSetCipherList(arg0: PSSL_CTX; str: PChar):Integer;
-  function SslCtxSetCipherList(arg0: PSSL_CTX; var str: String):Integer;
-  function SslCtxNew(meth: PSSL_METHOD):PSSL_CTX;
-  procedure SslCtxFree(arg0: PSSL_CTX);
-  function SslSetFd(s: PSSL; fd: Integer):Integer;
-  function SslMethodV2:PSSL_METHOD;
-  function SslMethodV3:PSSL_METHOD;
-  function SslMethodTLSV1:PSSL_METHOD;
-  function SslMethodV23:PSSL_METHOD;
-  function SslCtxUsePrivateKey(ctx: PSSL_CTX; pkey: SslPtr):Integer;
-  function SslCtxUsePrivateKeyASN1(pk: integer; ctx: PSSL_CTX; d: String; len: integer):Integer;
-//  function SslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: PChar; _type: Integer):Integer;
-  function SslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: String; _type: Integer):Integer;
-  function SslCtxUseCertificate(ctx: PSSL_CTX; x: SslPtr):Integer;
-  function SslCtxUseCertificateASN1(ctx: PSSL_CTX; len: integer; d: String):Integer;
-  function SslCtxUseCertificateFile(ctx: PSSL_CTX; const _file: String; _type: Integer):Integer;
-//  function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const _file: PChar):Integer;
-  function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const _file: String):Integer;
-  function SslCtxCheckPrivateKeyFile(ctx: PSSL_CTX):Integer;
-  procedure SslCtxSetDefaultPasswdCb(ctx: PSSL_CTX; cb: PPasswdCb);
-  procedure SslCtxSetDefaultPasswdCbUserdata(ctx: PSSL_CTX; u: SslPtr);
-//  function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; const CAfile: PChar; const CApath: PChar):Integer;
-  function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; const CAfile: String; const CApath: String):Integer;
-  function SslNew(ctx: PSSL_CTX):PSSL;
-  procedure SslFree(ssl: PSSL);
-  function SslAccept(ssl: PSSL):Integer;
-  function SslConnect(ssl: PSSL):Integer;
-  function SslShutdown(ssl: PSSL):Integer;
-  function SslRead(ssl: PSSL; buf: SslPtr; num: Integer):Integer;
-  function SslPeek(ssl: PSSL; buf: SslPtr; num: Integer):Integer;
-  function SslWrite(ssl: PSSL; buf: SslPtr; num: Integer):Integer;
-  function SslPending(ssl: PSSL):Integer;
-  function SslGetVersion(ssl: PSSL):String;
-  function SslGetPeerCertificate(ssl: PSSL):PX509;
-  procedure SslCtxSetVerify(ctx: PSSL_CTX; mode: Integer; arg2: PFunction);
-  function SSLGetCurrentCipher(s: PSSL):SslPtr;
-  function SSLCipherGetName(c: SslPtr): String;
-  function SSLCipherGetBits(c: SslPtr; var alg_bits: Integer):Integer;
-  function SSLGetVerifyResult(ssl: PSSL):Integer;
-
-// libeay.dll
-  function X509New: PX509;
-  procedure X509Free(x: PX509);
-  function X509NameOneline(a: PX509_NAME; var buf: String; size: Integer):String;
-  function X509GetSubjectName(a: PX509):PX509_NAME;
-  function X509GetIssuerName(a: PX509):PX509_NAME;
-  function X509NameHash(x: PX509_NAME):Cardinal;
-//  function SslX509Digest(data: PX509; _type: PEVP_MD; md: PChar; len: PInteger):Integer;
-  function X509Digest(data: PX509; _type: PEVP_MD; md: String; var len: Integer):Integer;
-  function X509print(b: PBIO; a: PX509): integer;
-  function X509SetVersion(x: PX509; version: integer): integer;
-  function X509SetPubkey(x: PX509; pkey: EVP_PKEY): integer;
-  function X509SetIssuerName(x: PX509; name: PX509_NAME): integer;
-  function X509NameAddEntryByTxt(name: PX509_NAME; field: string; _type: integer;
-    bytes: string; len, loc, _set: integer): integer;
-  function X509Sign(x: PX509; pkey: EVP_PKEY; const md: PEVP_MD): integer;
-  function X509GmtimeAdj(s: PASN1_UTCTIME; adj: integer): PASN1_UTCTIME;
-  function X509SetNotBefore(x: PX509; tm: PASN1_UTCTIME): integer;
-  function X509SetNotAfter(x: PX509; tm: PASN1_UTCTIME): integer;
-  function X509GetSerialNumber(x: PX509): PASN1_INTEGER;
-  function EvpPkeyNew: EVP_PKEY;
-  procedure EvpPkeyFree(pk: EVP_PKEY);
-  function EvpPkeyAssign(pkey: EVP_PKEY; _type: integer; key: Prsa): integer;
-  function EvpGetDigestByName(Name: String): PEVP_MD;
-  procedure EVPcleanup;
-//  function ErrErrorString(e: integer; buf: PChar): PChar;
-  function SSLeayversion(t: integer): string;
-  procedure ErrErrorString(e: integer; var buf: string; len: integer);
-  function ErrGetError: integer;
-  procedure ErrClearError;
-  procedure ErrFreeStrings;
-  procedure ErrRemoveState(pid: integer);
-  procedure OPENSSLaddallalgorithms;
-  procedure CRYPTOcleanupAllExData;
-  procedure RandScreen;
-  function BioNew(b: PBIO_METHOD): PBIO;
-  procedure BioFreeAll(b: PBIO);
-  function BioSMem: PBIO_METHOD;
-  function BioCtrlPending(b: PBIO): integer;
-  function BioRead(b: PBIO; var Buf: String; Len: integer): integer;
-  function BioWrite(b: PBIO; Buf: String; Len: integer): integer;
-  function d2iPKCS12bio(b:PBIO; Pkcs12: SslPtr): SslPtr;
-  function PKCS12parse(p12: SslPtr; pass: string; var pkey, cert, ca: SslPtr): integer;
-  procedure PKCS12free(p12: SslPtr);
-  function RsaGenerateKey(bits, e: integer; callback: PFunction; cb_arg: SslPtr): PRSA;
-  function Asn1UtctimeNew: PASN1_UTCTIME;
-  procedure Asn1UtctimeFree(a: PASN1_UTCTIME);
-  function Asn1IntegerSet(a: PASN1_INTEGER; v: integer): integer;
-  function i2dX509bio(b: PBIO; x: PX509): integer;
-  function i2dPrivateKeyBio(b: PBIO; pkey: EVP_PKEY): integer;
-
-  // 3DES functions
-  procedure DESsetoddparity(Key: des_cblock);
-  function DESsetkeychecked(key: des_cblock; schedule: des_key_schedule): Integer;
-  procedure DESecbencrypt(Input: des_cblock; output: des_cblock; ks: des_key_schedule; enc: Integer);
-
-function IsSSLloaded: Boolean;
-function InitSSLInterface: Boolean;
-function DestroySSLInterface: Boolean;
-
-implementation
-
-type
-// libssl.dll
-  TSslGetError = function(s: PSSL; ret_code: Integer):Integer; cdecl;
-  TSslLibraryInit = function:Integer; cdecl;
-  TSslLoadErrorStrings = procedure; cdecl;
-  TSslCtxSetCipherList = function(arg0: PSSL_CTX; str: PChar):Integer; cdecl;
-  TSslCtxNew = function(meth: PSSL_METHOD):PSSL_CTX; cdecl;
-  TSslCtxFree = procedure(arg0: PSSL_CTX); cdecl;
-  TSslSetFd = function(s: PSSL; fd: Integer):Integer; cdecl;
-  TSslMethodV2 = function:PSSL_METHOD; cdecl;
-  TSslMethodV3 = function:PSSL_METHOD; cdecl;
-  TSslMethodTLSV1 = function:PSSL_METHOD; cdecl;
-  TSslMethodV23 = function:PSSL_METHOD; cdecl;
-  TSslCtxUsePrivateKey = function(ctx: PSSL_CTX; pkey: sslptr):Integer; cdecl;
-  TSslCtxUsePrivateKeyASN1 = function(pk: integer; ctx: PSSL_CTX; d: sslptr; len: integer):Integer; cdecl;
-  TSslCtxUsePrivateKeyFile = function(ctx: PSSL_CTX; const _file: PChar; _type: Integer):Integer; cdecl;
-  TSslCtxUseCertificate = function(ctx: PSSL_CTX; x: SslPtr):Integer; cdecl;
-  TSslCtxUseCertificateASN1 = function(ctx: PSSL_CTX; len: Integer; d: SslPtr):Integer; cdecl;
-  TSslCtxUseCertificateFile = function(ctx: PSSL_CTX; const _file: PChar; _type: Integer):Integer; cdecl;
-  TSslCtxUseCertificateChainFile = function(ctx: PSSL_CTX; const _file: PChar):Integer; cdecl;
-  TSslCtxCheckPrivateKeyFile = function(ctx: PSSL_CTX):Integer; cdecl;
-  TSslCtxSetDefaultPasswdCb = procedure(ctx: PSSL_CTX; cb: SslPtr); cdecl;
-  TSslCtxSetDefaultPasswdCbUserdata = procedure(ctx: PSSL_CTX; u: SslPtr); cdecl;
-  TSslCtxLoadVerifyLocations = function(ctx: PSSL_CTX; const CAfile: PChar; const CApath: PChar):Integer; cdecl;
-  TSslNew = function(ctx: PSSL_CTX):PSSL; cdecl;
-  TSslFree = procedure(ssl: PSSL); cdecl;
-  TSslAccept = function(ssl: PSSL):Integer; cdecl;
-  TSslConnect = function(ssl: PSSL):Integer; cdecl;
-  TSslShutdown = function(ssl: PSSL):Integer; cdecl;
-  TSslRead = function(ssl: PSSL; buf: PChar; num: Integer):Integer; cdecl;
-  TSslPeek = function(ssl: PSSL; buf: PChar; num: Integer):Integer; cdecl;
-  TSslWrite = function(ssl: PSSL; const buf: PChar; num: Integer):Integer; cdecl;
-  TSslPending = function(ssl: PSSL):Integer; cdecl;
-  TSslGetVersion = function(ssl: PSSL):PChar; cdecl;
-  TSslGetPeerCertificate = function(ssl: PSSL):PX509; cdecl;
-  TSslCtxSetVerify = procedure(ctx: PSSL_CTX; mode: Integer; arg2: SslPtr); cdecl;
-  TSSLGetCurrentCipher = function(s: PSSL):SslPtr; cdecl;
-  TSSLCipherGetName = function(c: Sslptr):PChar; cdecl;
-  TSSLCipherGetBits = function(c: SslPtr; alg_bits: PInteger):Integer; cdecl;
-  TSSLGetVerifyResult = function(ssl: PSSL):Integer; cdecl;
-
-// libeay.dll
-  TX509New = function: PX509; cdecl;
-  TX509Free = procedure(x: PX509); cdecl;
-  TX509NameOneline = function(a: PX509_NAME; buf: PChar; size: Integer):PChar; cdecl;
-  TX509GetSubjectName = function(a: PX509):PX509_NAME; cdecl;
-  TX509GetIssuerName = function(a: PX509):PX509_NAME; cdecl;
-  TX509NameHash = function(x: PX509_NAME):Cardinal; cdecl;
-  TX509Digest = function(data: PX509; _type: PEVP_MD; md: PChar; len: PInteger):Integer; cdecl;
-  TX509print = function(b: PBIO; a: PX509): integer; cdecl;
-  TX509SetVersion = function(x: PX509; version: integer): integer; cdecl;
-  TX509SetPubkey = function(x: PX509; pkey: EVP_PKEY): integer; cdecl;
-  TX509SetIssuerName = function(x: PX509; name: PX509_NAME): integer; cdecl;
-  TX509NameAddEntryByTxt = function(name: PX509_NAME; field: PChar; _type: integer;
-    bytes: PChar; len, loc, _set: integer): integer; cdecl;
-  TX509Sign = function(x: PX509; pkey: EVP_PKEY; const md: PEVP_MD): integer; cdecl;
-  TX509GmtimeAdj = function(s: PASN1_UTCTIME; adj: integer): PASN1_UTCTIME; cdecl;
-  TX509SetNotBefore = function(x: PX509; tm: PASN1_UTCTIME): integer; cdecl;
-  TX509SetNotAfter = function(x: PX509; tm: PASN1_UTCTIME): integer; cdecl;
-  TX509GetSerialNumber = function(x: PX509): PASN1_INTEGER; cdecl;
-  TEvpPkeyNew = function: EVP_PKEY; cdecl;
-  TEvpPkeyFree = procedure(pk: EVP_PKEY); cdecl;
-  TEvpPkeyAssign = function(pkey: EVP_PKEY; _type: integer; key: Prsa): integer; cdecl;
-  TEvpGetDigestByName = function(Name: PChar): PEVP_MD; cdecl;
-  TEVPcleanup = procedure; cdecl;
-  TSSLeayversion = function(t: integer): PChar; cdecl;
-  TErrErrorString = procedure(e: integer; buf: PChar; len: integer); cdecl;
-  TErrGetError = function: integer; cdecl;
-  TErrClearError = procedure; cdecl;
-  TErrFreeStrings = procedure; cdecl;
-  TErrRemoveState = procedure(pid: integer); cdecl;
-  TOPENSSLaddallalgorithms = procedure; cdecl;
-  TCRYPTOcleanupAllExData = procedure; cdecl;
-  TRandScreen = procedure; cdecl;
-  TBioNew = function(b: PBIO_METHOD): PBIO; cdecl;
-  TBioFreeAll = procedure(b: PBIO); cdecl;
-  TBioSMem = function: PBIO_METHOD; cdecl;
-  TBioCtrlPending = function(b: PBIO): integer; cdecl;
-  TBioRead = function(b: PBIO; Buf: PChar; Len: integer): integer; cdecl;
-  TBioWrite = function(b: PBIO; Buf: PChar; Len: integer): integer; cdecl;
-  Td2iPKCS12bio = function(b:PBIO; Pkcs12: SslPtr): SslPtr; cdecl;
-  TPKCS12parse = function(p12: SslPtr; pass: PChar; var pkey, cert, ca: SslPtr): integer; cdecl;
-  TPKCS12free = procedure(p12: SslPtr); cdecl;
-  TRsaGenerateKey = function(bits, e: integer; callback: PFunction; cb_arg: SslPtr): PRSA; cdecl;
-  TAsn1UtctimeNew = function: PASN1_UTCTIME; cdecl;
-  TAsn1UtctimeFree = procedure(a: PASN1_UTCTIME); cdecl;
-  TAsn1IntegerSet = function(a: PASN1_INTEGER; v: integer): integer; cdecl;
-  Ti2dX509bio = function(b: PBIO; x: PX509): integer; cdecl;
-  Ti2dPrivateKeyBio= function(b: PBIO; pkey: EVP_PKEY): integer; cdecl;
-
-  // 3DES functions
-  TDESsetoddparity = procedure(Key: des_cblock); cdecl;
-  TDESsetkeychecked = function(key: des_cblock; schedule: des_key_schedule): Integer; cdecl;
-  TDESecbencrypt = procedure(Input: des_cblock; output: des_cblock; ks: des_key_schedule; enc: Integer); cdecl;
-  //thread lock functions
-  TCRYPTOnumlocks = function: integer; cdecl;
-  TCRYPTOSetLockingCallback = procedure(cb: Sslptr); cdecl;
-
-var
-// libssl.dll
-  _SslGetError: TSslGetError = nil;
-  _SslLibraryInit: TSslLibraryInit = nil;
-  _SslLoadErrorStrings: TSslLoadErrorStrings = nil;
-  _SslCtxSetCipherList: TSslCtxSetCipherList = nil;
-  _SslCtxNew: TSslCtxNew = nil;
-  _SslCtxFree: TSslCtxFree = nil;
-  _SslSetFd: TSslSetFd = nil;
-  _SslMethodV2: TSslMethodV2 = nil;
-  _SslMethodV3: TSslMethodV3 = nil;
-  _SslMethodTLSV1: TSslMethodTLSV1 = nil;
-  _SslMethodV23: TSslMethodV23 = nil;
-  _SslCtxUsePrivateKey: TSslCtxUsePrivateKey = nil;
-  _SslCtxUsePrivateKeyASN1: TSslCtxUsePrivateKeyASN1 = nil;
-  _SslCtxUsePrivateKeyFile: TSslCtxUsePrivateKeyFile = nil;
-  _SslCtxUseCertificate: TSslCtxUseCertificate = nil;
-  _SslCtxUseCertificateASN1: TSslCtxUseCertificateASN1 = nil;
-  _SslCtxUseCertificateFile: TSslCtxUseCertificateFile = nil;
-  _SslCtxUseCertificateChainFile: TSslCtxUseCertificateChainFile = nil;
-  _SslCtxCheckPrivateKeyFile: TSslCtxCheckPrivateKeyFile = nil;
-  _SslCtxSetDefaultPasswdCb: TSslCtxSetDefaultPasswdCb = nil;
-  _SslCtxSetDefaultPasswdCbUserdata: TSslCtxSetDefaultPasswdCbUserdata = nil;
-  _SslCtxLoadVerifyLocations: TSslCtxLoadVerifyLocations = nil;
-  _SslNew: TSslNew = nil;
-  _SslFree: TSslFree = nil;
-  _SslAccept: TSslAccept = nil;
-  _SslConnect: TSslConnect = nil;
-  _SslShutdown: TSslShutdown = nil;
-  _SslRead: TSslRead = nil;
-  _SslPeek: TSslPeek = nil;
-  _SslWrite: TSslWrite = nil;
-  _SslPending: TSslPending = nil;
-  _SslGetVersion: TSslGetVersion = nil;
-  _SslGetPeerCertificate: TSslGetPeerCertificate = nil;
-  _SslCtxSetVerify: TSslCtxSetVerify = nil;
-  _SSLGetCurrentCipher: TSSLGetCurrentCipher = nil;
-  _SSLCipherGetName: TSSLCipherGetName = nil;
-  _SSLCipherGetBits: TSSLCipherGetBits = nil;
-  _SSLGetVerifyResult: TSSLGetVerifyResult = nil;
-
-// libeay.dll
-  _X509New: TX509New = nil;
-  _X509Free: TX509Free = nil;
-  _X509NameOneline: TX509NameOneline = nil;
-  _X509GetSubjectName: TX509GetSubjectName = nil;
-  _X509GetIssuerName: TX509GetIssuerName = nil;
-  _X509NameHash: TX509NameHash = nil;
-  _X509Digest: TX509Digest = nil;
-  _X509print: TX509print = nil;
-  _X509SetVersion: TX509SetVersion = nil;
-  _X509SetPubkey: TX509SetPubkey = nil;
-  _X509SetIssuerName: TX509SetIssuerName = nil;
-  _X509NameAddEntryByTxt: TX509NameAddEntryByTxt = nil;
-  _X509Sign: TX509Sign = nil;
-  _X509GmtimeAdj: TX509GmtimeAdj = nil;
-  _X509SetNotBefore: TX509SetNotBefore = nil;
-  _X509SetNotAfter: TX509SetNotAfter = nil;
-  _X509GetSerialNumber: TX509GetSerialNumber = nil;
-  _EvpPkeyNew: TEvpPkeyNew = nil;
-  _EvpPkeyFree: TEvpPkeyFree = nil;
-  _EvpPkeyAssign: TEvpPkeyAssign = nil;
-  _EvpGetDigestByName: TEvpGetDigestByName = nil;
-  _EVPcleanup: TEVPcleanup = nil;
-  _SSLeayversion: TSSLeayversion = nil;
-  _ErrErrorString: TErrErrorString = nil;
-  _ErrGetError: TErrGetError = nil;
-  _ErrClearError: TErrClearError = nil;
-  _ErrFreeStrings: TErrFreeStrings = nil;
-  _ErrRemoveState: TErrRemoveState = nil;
-  _OPENSSLaddallalgorithms: TOPENSSLaddallalgorithms = nil;
-  _CRYPTOcleanupAllExData: TCRYPTOcleanupAllExData = nil;
-  _RandScreen: TRandScreen = nil;
-  _BioNew: TBioNew = nil;
-  _BioFreeAll: TBioFreeAll = nil;
-  _BioSMem: TBioSMem = nil;
-  _BioCtrlPending: TBioCtrlPending = nil;
-  _BioRead: TBioRead = nil;
-  _BioWrite: TBioWrite = nil;
-  _d2iPKCS12bio: Td2iPKCS12bio = nil;
-  _PKCS12parse: TPKCS12parse = nil;
-  _PKCS12free: TPKCS12free = nil;
-  _RsaGenerateKey: TRsaGenerateKey = nil;
-  _Asn1UtctimeNew: TAsn1UtctimeNew = nil;
-  _Asn1UtctimeFree: TAsn1UtctimeFree = nil;
-  _Asn1IntegerSet: TAsn1IntegerSet = nil;
-  _i2dX509bio: Ti2dX509bio = nil;
-  _i2dPrivateKeyBio: Ti2dPrivateKeyBio = nil;
-
-  // 3DES functions
-  _DESsetoddparity: TDESsetoddparity = nil;
-  _DESsetkeychecked: TDESsetkeychecked = nil;
-  _DESecbencrypt: TDESecbencrypt = nil;
-  //thread lock functions
-  _CRYPTOnumlocks: TCRYPTOnumlocks = nil;
-  _CRYPTOSetLockingCallback: TCRYPTOSetLockingCallback = nil;
-
-var
-  SSLloaded: boolean = false;
-
-// libssl.dll
-function SslGetError(s: PSSL; ret_code: Integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslGetError) then
-    Result := _SslGetError(s, ret_code)
-  else
-    Result := SSL_ERROR_SSL;
-end;
-
-function SslLibraryInit:Integer;
-begin
-  if InitSSLInterface and Assigned(_SslLibraryInit) then
-    Result := _SslLibraryInit
-  else
-    Result := 1;
-end;
-
-procedure SslLoadErrorStrings;
-begin
-  if InitSSLInterface and Assigned(_SslLoadErrorStrings) then
-    _SslLoadErrorStrings;
-end;
-
-//function SslCtxSetCipherList(arg0: PSSL_CTX; str: PChar):Integer;
-function SslCtxSetCipherList(arg0: PSSL_CTX; var str: String):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxSetCipherList) then
-    Result := _SslCtxSetCipherList(arg0, PChar(str))
-  else
-    Result := 0;
-end;
-
-function SslCtxNew(meth: PSSL_METHOD):PSSL_CTX;
-begin
-  if InitSSLInterface and Assigned(_SslCtxNew) then
-    Result := _SslCtxNew(meth)
-  else
-    Result := nil;
-end;
-
-procedure SslCtxFree(arg0: PSSL_CTX);
-begin
-  if InitSSLInterface and Assigned(_SslCtxFree) then
-    _SslCtxFree(arg0);
-end;
-
-function SslSetFd(s: PSSL; fd: Integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslSetFd) then
-    Result := _SslSetFd(s, fd)
-  else
-    Result := 0;
-end;
-
-function SslMethodV2:PSSL_METHOD;
-begin
-  if InitSSLInterface and Assigned(_SslMethodV2) then
-    Result := _SslMethodV2
-  else
-    Result := nil;
-end;
-
-function SslMethodV3:PSSL_METHOD;
-begin
-  if InitSSLInterface and Assigned(_SslMethodV3) then
-    Result := _SslMethodV3
-  else
-    Result := nil;
-end;
-
-function SslMethodTLSV1:PSSL_METHOD;
-begin
-  if InitSSLInterface and Assigned(_SslMethodTLSV1) then
-    Result := _SslMethodTLSV1
-  else
-    Result := nil;
-end;
-
-function SslMethodV23:PSSL_METHOD;
-begin
-  if InitSSLInterface and Assigned(_SslMethodV23) then
-    Result := _SslMethodV23
-  else
-    Result := nil;
-end;
-
-function SslCtxUsePrivateKey(ctx: PSSL_CTX; pkey: SslPtr):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxUsePrivateKey) then
-    Result := _SslCtxUsePrivateKey(ctx, pkey)
-  else
-    Result := 0;
-end;
-
-function SslCtxUsePrivateKeyASN1(pk: integer; ctx: PSSL_CTX; d: String; len: integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxUsePrivateKeyASN1) then
-    Result := _SslCtxUsePrivateKeyASN1(pk, ctx, Sslptr(d), len)
-  else
-    Result := 0;
-end;
-
-//function SslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: PChar; _type: Integer):Integer;
-function SslCtxUsePrivateKeyFile(ctx: PSSL_CTX; const _file: String; _type: Integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxUsePrivateKeyFile) then
-    Result := _SslCtxUsePrivateKeyFile(ctx, PChar(_file), _type)
-  else
-    Result := 0;
-end;
-
-function SslCtxUseCertificate(ctx: PSSL_CTX; x: SslPtr):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxUseCertificate) then
-    Result := _SslCtxUseCertificate(ctx, x)
-  else
-    Result := 0;
-end;
-
-function SslCtxUseCertificateASN1(ctx: PSSL_CTX; len: integer; d: String):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxUseCertificateASN1) then
-    Result := _SslCtxUseCertificateASN1(ctx, len, SslPtr(d))
-  else
-    Result := 0;
-end;
-
-function SslCtxUseCertificateFile(ctx: PSSL_CTX; const _file: String; _type: Integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxUseCertificateFile) then
-    Result := _SslCtxUseCertificateFile(ctx, PChar(_file), _type)
-  else
-    Result := 0;
-end;
-
-//function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const _file: PChar):Integer;
-function SslCtxUseCertificateChainFile(ctx: PSSL_CTX; const _file: String):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxUseCertificateChainFile) then
-    Result := _SslCtxUseCertificateChainFile(ctx, PChar(_file))
-  else
-    Result := 0;
-end;
-
-function SslCtxCheckPrivateKeyFile(ctx: PSSL_CTX):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxCheckPrivateKeyFile) then
-    Result := _SslCtxCheckPrivateKeyFile(ctx)
-  else
-    Result := 0;
-end;
-
-procedure SslCtxSetDefaultPasswdCb(ctx: PSSL_CTX; cb: PPasswdCb);
-begin
-  if InitSSLInterface and Assigned(_SslCtxSetDefaultPasswdCb) then
-    _SslCtxSetDefaultPasswdCb(ctx, cb);
-end;
-
-procedure SslCtxSetDefaultPasswdCbUserdata(ctx: PSSL_CTX; u: SslPtr);
-begin
-  if InitSSLInterface and Assigned(_SslCtxSetDefaultPasswdCbUserdata) then
-    _SslCtxSetDefaultPasswdCbUserdata(ctx, u);
-end;
-
-//function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; const CAfile: PChar; const CApath: PChar):Integer;
-function SslCtxLoadVerifyLocations(ctx: PSSL_CTX; const CAfile: String; const CApath: String):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslCtxLoadVerifyLocations) then
-    Result := _SslCtxLoadVerifyLocations(ctx, SslPtr(CAfile), SslPtr(CApath))
-  else
-    Result := 0;
-end;
-
-function SslNew(ctx: PSSL_CTX):PSSL;
-begin
-  if InitSSLInterface and Assigned(_SslNew) then
-    Result := _SslNew(ctx)
-  else
-    Result := nil;
-end;
-
-procedure SslFree(ssl: PSSL);
-begin
-  if InitSSLInterface and Assigned(_SslFree) then
-    _SslFree(ssl);
-end;
-
-function SslAccept(ssl: PSSL):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslAccept) then
-    Result := _SslAccept(ssl)
-  else
-    Result := -1;
-end;
-
-function SslConnect(ssl: PSSL):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslConnect) then
-    Result := _SslConnect(ssl)
-  else
-    Result := -1;
-end;
-
-function SslShutdown(ssl: PSSL):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslShutdown) then
-    Result := _SslShutdown(ssl)
-  else
-    Result := -1;
-end;
-
-//function SslRead(ssl: PSSL; buf: PChar; num: Integer):Integer;
-function SslRead(ssl: PSSL; buf: SslPtr; num: Integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslRead) then
-    Result := _SslRead(ssl, PChar(buf), num)
-  else
-    Result := -1;
-end;
-
-//function SslPeek(ssl: PSSL; buf: PChar; num: Integer):Integer;
-function SslPeek(ssl: PSSL; buf: SslPtr; num: Integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslPeek) then
-    Result := _SslPeek(ssl, PChar(buf), num)
-  else
-    Result := -1;
-end;
-
-//function SslWrite(ssl: PSSL; const buf: PChar; num: Integer):Integer;
-function SslWrite(ssl: PSSL; buf: SslPtr; num: Integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslWrite) then
-    Result := _SslWrite(ssl, PChar(buf), num)
-  else
-    Result := -1;
-end;
-
-function SslPending(ssl: PSSL):Integer;
-begin
-  if InitSSLInterface and Assigned(_SslPending) then
-    Result := _SslPending(ssl)
-  else
-    Result := 0;
-end;
-
-//function SslGetVersion(ssl: PSSL):PChar;
-function SslGetVersion(ssl: PSSL):String;
-begin
-  if InitSSLInterface and Assigned(_SslGetVersion) then
-    Result := _SslGetVersion(ssl)
-  else
-    Result := '';
-end;
-
-function SslGetPeerCertificate(ssl: PSSL):PX509;
-begin
-  if InitSSLInterface and Assigned(_SslGetPeerCertificate) then
-    Result := _SslGetPeerCertificate(ssl)
-  else
-    Result := nil;
-end;
-
-//procedure SslCtxSetVerify(ctx: PSSL_CTX; mode: Integer; arg2: SslPtr);
-procedure SslCtxSetVerify(ctx: PSSL_CTX; mode: Integer; arg2: PFunction);
-begin
-  if InitSSLInterface and Assigned(_SslCtxSetVerify) then
-    _SslCtxSetVerify(ctx, mode, @arg2);
-end;
-
-function SSLGetCurrentCipher(s: PSSL):SslPtr;
-begin
-  if InitSSLInterface and Assigned(_SSLGetCurrentCipher) then
-{$IFDEF CIL}
-{$ELSE}
-    Result := _SSLGetCurrentCipher(s)
-{$ENDIF}
-  else
-    Result := nil;
-end;
-
-//function SSLCipherGetName(c: SslPtr):PChar;
-function SSLCipherGetName(c: SslPtr):String;
-begin
-  if InitSSLInterface and Assigned(_SSLCipherGetName) then
-    Result := _SSLCipherGetName(c)
-  else
-    Result := '';
-end;
-
-//function SSLCipherGetBits(c: SslPtr; alg_bits: PInteger):Integer;
-function SSLCipherGetBits(c: SslPtr; var alg_bits: Integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_SSLCipherGetBits) then
-    Result := _SSLCipherGetBits(c, @alg_bits)
-  else
-    Result := 0;
-end;
-
-function SSLGetVerifyResult(ssl: PSSL):Integer;
-begin
-  if InitSSLInterface and Assigned(_SSLGetVerifyResult) then
-    Result := _SSLGetVerifyResult(ssl)
-  else
-    Result := X509_V_ERR_APPLICATION_VERIFICATION;
-end;
-
-// libeay.dll
-function X509New: PX509;
-begin
-  if InitSSLInterface and Assigned(_X509New) then
-    Result := _X509New
-  else
-    Result := nil;
-end;
-
-procedure X509Free(x: PX509);
-begin
-  if InitSSLInterface and Assigned(_X509Free) then
-    _X509Free(x);
-end;
-
-//function SslX509NameOneline(a: PX509_NAME; buf: PChar; size: Integer):PChar;
-function X509NameOneline(a: PX509_NAME; var buf: String; size: Integer):String;
-begin
-  if InitSSLInterface and Assigned(_X509NameOneline) then
-    Result := _X509NameOneline(a, PChar(buf),size)
-  else
-    Result := '';
-end;
-
-function X509GetSubjectName(a: PX509):PX509_NAME;
-begin
-  if InitSSLInterface and Assigned(_X509GetSubjectName) then
-    Result := _X509GetSubjectName(a)
-  else
-    Result := nil;
-end;
-
-function X509GetIssuerName(a: PX509):PX509_NAME;
-begin
-  if InitSSLInterface and Assigned(_X509GetIssuerName) then
-    Result := _X509GetIssuerName(a)
-  else
-    Result := nil;
-end;
-
-function X509NameHash(x: PX509_NAME):Cardinal;
-begin
-  if InitSSLInterface and Assigned(_X509NameHash) then
-    Result := _X509NameHash(x)
-  else
-    Result := 0;
-end;
-
-//function SslX509Digest(data: PX509; _type: PEVP_MD; md: PChar; len: PInteger):Integer;
-function X509Digest(data: PX509; _type: PEVP_MD; md: String; var len: Integer):Integer;
-begin
-  if InitSSLInterface and Assigned(_X509Digest) then
-    Result := _X509Digest(data, _type, PChar(md), @len)
-  else
-    Result := 0;
-end;
-
-function EvpPkeyNew: EVP_PKEY;
-begin
-  if InitSSLInterface and Assigned(_EvpPkeyNew) then
-    Result := _EvpPkeyNew
-  else
-    Result := nil;
-end;
-
-procedure EvpPkeyFree(pk: EVP_PKEY);
-begin
-  if InitSSLInterface and Assigned(_EvpPkeyFree) then
-    _EvpPkeyFree(pk);
-end;
-
-function SSLeayversion(t: integer): string;
-begin
-  if InitSSLInterface and Assigned(_SSLeayversion) then
-    Result := PChar(_SSLeayversion(t))
-  else
-    Result := '';
-end;
-
-procedure ErrErrorString(e: integer; var buf: string; len: integer);
-begin
-  if InitSSLInterface and Assigned(_ErrErrorString) then
-    _ErrErrorString(e, Pointer(buf), len);
-  buf := PChar(Buf);
-end;
-
-function ErrGetError: integer;
-begin
-  if InitSSLInterface and Assigned(_ErrGetError) then
-    Result := _ErrGetError
-  else
-    Result := SSL_ERROR_SSL;
-end;
-
-procedure ErrClearError;
-begin
-  if InitSSLInterface and Assigned(_ErrClearError) then
-    _ErrClearError;
-end;
-
-procedure ErrFreeStrings;
-begin
-  if InitSSLInterface and Assigned(_ErrFreeStrings) then
-    _ErrFreeStrings;
-end;
-
-procedure ErrRemoveState(pid: integer);
-begin
-  if InitSSLInterface and Assigned(_ErrRemoveState) then
-    _ErrRemoveState(pid);
-end;
-
-procedure OPENSSLaddallalgorithms;
-begin
-  if InitSSLInterface and Assigned(_OPENSSLaddallalgorithms) then
-    _OPENSSLaddallalgorithms;
-end;
-
-procedure EVPcleanup;
-begin
-  if InitSSLInterface and Assigned(_EVPcleanup) then
-    _EVPcleanup;
-end;
-
-procedure CRYPTOcleanupAllExData;
-begin
-  if InitSSLInterface and Assigned(_CRYPTOcleanupAllExData) then
-    _CRYPTOcleanupAllExData;
-end;
-
-procedure RandScreen;
-begin
-  if InitSSLInterface and Assigned(_RandScreen) then
-    _RandScreen;
-end;
-
-function BioNew(b: PBIO_METHOD): PBIO;
-begin
-  if InitSSLInterface and Assigned(_BioNew) then
-    Result := _BioNew(b)
-  else
-    Result := nil;
-end;
-
-procedure BioFreeAll(b: PBIO);
-begin
-  if InitSSLInterface and Assigned(_BioFreeAll) then
-    _BioFreeAll(b);
-end;
-
-function BioSMem: PBIO_METHOD;
-begin
-  if InitSSLInterface and Assigned(_BioSMem) then
-    Result := _BioSMem
-  else
-    Result := nil;
-end;
-
-function BioCtrlPending(b: PBIO): integer;
-begin
-  if InitSSLInterface and Assigned(_BioCtrlPending) then
-    Result := _BioCtrlPending(b)
-  else
-    Result := 0;
-end;
-
-//function BioRead(b: PBIO; Buf: PChar; Len: integer): integer;
-function BioRead(b: PBIO; var Buf: String; Len: integer): integer;
-begin
-  if InitSSLInterface and Assigned(_BioRead) then
-    Result := _BioRead(b, PChar(Buf), Len)
-  else
-    Result := -2;
-end;
-
-//function BioWrite(b: PBIO; Buf: PChar; Len: integer): integer;
-function BioWrite(b: PBIO; Buf: String; Len: integer): integer;
-begin
-  if InitSSLInterface and Assigned(_BioWrite) then
-    Result := _BioWrite(b, PChar(Buf), Len)
-  else
-    Result := -2;
-end;
-
-function X509print(b: PBIO; a: PX509): integer;
-begin
-  if InitSSLInterface and Assigned(_X509print) then
-    Result := _X509print(b, a)
-  else
-    Result := 0;
-end;
-
-function d2iPKCS12bio(b:PBIO; Pkcs12: SslPtr): SslPtr;
-begin
-  if InitSSLInterface and Assigned(_d2iPKCS12bio) then
-    Result := _d2iPKCS12bio(b, Pkcs12)
-  else
-    Result := nil;
-end;
-
-function PKCS12parse(p12: SslPtr; pass: string; var pkey, cert, ca: SslPtr): integer;
-begin
-  if InitSSLInterface and Assigned(_PKCS12parse) then
-    Result := _PKCS12parse(p12, SslPtr(pass), pkey, cert, ca)
-  else
-    Result := 0;
-end;
-
-procedure PKCS12free(p12: SslPtr);
-begin
-  if InitSSLInterface and Assigned(_PKCS12free) then
-    _PKCS12free(p12);
-end;
-
-function RsaGenerateKey(bits, e: integer; callback: PFunction; cb_arg: SslPtr): PRSA;
-begin
-  if InitSSLInterface and Assigned(_RsaGenerateKey) then
-    Result := _RsaGenerateKey(bits, e, callback, cb_arg)
-  else
-    Result := nil;
-end;
-
-function EvpPkeyAssign(pkey: EVP_PKEY; _type: integer; key: Prsa): integer;
-begin
-  if InitSSLInterface and Assigned(_EvpPkeyAssign) then
-    Result := _EvpPkeyAssign(pkey, _type, key)
-  else
-    Result := 0;
-end;
-
-function X509SetVersion(x: PX509; version: integer): integer;
-begin
-  if InitSSLInterface and Assigned(_X509SetVersion) then
-    Result := _X509SetVersion(x, version)
-  else
-    Result := 0;
-end;
-
-function X509SetPubkey(x: PX509; pkey: EVP_PKEY): integer;
-begin
-  if InitSSLInterface and Assigned(_X509SetPubkey) then
-    Result := _X509SetPubkey(x, pkey)
-  else
-    Result := 0;
-end;
-
-function X509SetIssuerName(x: PX509; name: PX509_NAME): integer;
-begin
-  if InitSSLInterface and Assigned(_X509SetIssuerName) then
-    Result := _X509SetIssuerName(x, name)
-  else
-    Result := 0;
-end;
-
-function X509NameAddEntryByTxt(name: PX509_NAME; field: string; _type: integer;
-  bytes: string; len, loc, _set: integer): integer;
-begin
-  if InitSSLInterface and Assigned(_X509NameAddEntryByTxt) then
-    Result := _X509NameAddEntryByTxt(name, PChar(field), _type, PChar(Bytes), len, loc, _set)
-  else
-    Result := 0;
-end;
-
-function X509Sign(x: PX509; pkey: EVP_PKEY; const md: PEVP_MD): integer;
-begin
-  if InitSSLInterface and Assigned(_X509Sign) then
-    Result := _X509Sign(x, pkey, md)
-  else
-    Result := 0;
-end;
-
-function Asn1UtctimeNew: PASN1_UTCTIME;
-begin
-  if InitSSLInterface and Assigned(_Asn1UtctimeNew) then
-    Result := _Asn1UtctimeNew
-  else
-    Result := nil;
-end;
-
-procedure Asn1UtctimeFree(a: PASN1_UTCTIME);
-begin
-  if InitSSLInterface and Assigned(_Asn1UtctimeFree) then
-    _Asn1UtctimeFree(a);
-end;
-
-function X509GmtimeAdj(s: PASN1_UTCTIME; adj: integer): PASN1_UTCTIME;
-begin
-  if InitSSLInterface and Assigned(_X509GmtimeAdj) then
-    Result := _X509GmtimeAdj(s, adj)
-  else
-    Result := nil;
-end;
-
-function X509SetNotBefore(x: PX509; tm: PASN1_UTCTIME): integer;
-begin
-  if InitSSLInterface and Assigned(_X509SetNotBefore) then
-    Result := _X509SetNotBefore(x, tm)
-  else
-    Result := 0;
-end;
-
-function X509SetNotAfter(x: PX509; tm: PASN1_UTCTIME): integer;
-begin
-  if InitSSLInterface and Assigned(_X509SetNotAfter) then
-    Result := _X509SetNotAfter(x, tm)
-  else
-    Result := 0;
-end;
-
-function i2dX509bio(b: PBIO; x: PX509): integer;
-begin
-  if InitSSLInterface and Assigned(_i2dX509bio) then
-    Result := _i2dX509bio(b, x)
-  else
-    Result := 0;
-end;
-
-function i2dPrivateKeyBio(b: PBIO; pkey: EVP_PKEY): integer;
-begin
-  if InitSSLInterface and Assigned(_i2dPrivateKeyBio) then
-    Result := _i2dPrivateKeyBio(b, pkey)
-  else
-    Result := 0;
-end;
-
-function EvpGetDigestByName(Name: String): PEVP_MD;
-begin
-  if InitSSLInterface and Assigned(_EvpGetDigestByName) then
-    Result := _EvpGetDigestByName(PChar(Name))
-  else
-    Result := nil;
-end;
-
-function Asn1IntegerSet(a: PASN1_INTEGER; v: integer): integer;
-begin
-  if InitSSLInterface and Assigned(_Asn1IntegerSet) then
-    Result := _Asn1IntegerSet(a, v)
-  else
-    Result := 0;
-end;
-
-function X509GetSerialNumber(x: PX509): PASN1_INTEGER;
-begin
-  if InitSSLInterface and Assigned(_X509GetSerialNumber) then
-    Result := _X509GetSerialNumber(x)
-  else
-    Result := nil;
-end;
-
-// 3DES functions
-procedure DESsetoddparity(Key: des_cblock);
-begin
-  if InitSSLInterface and Assigned(_DESsetoddparity) then
-    _DESsetoddparity(Key);
-end;
-
-function DESsetkeychecked(key: des_cblock; schedule: des_key_schedule): Integer;
-begin
-  if InitSSLInterface and Assigned(_DESsetkeychecked) then
-    Result := _DESsetkeychecked(key, schedule)
-  else
-    Result := -1;
-end;
-
-procedure DESecbencrypt(Input: des_cblock; output: des_cblock; ks: des_key_schedule; enc: Integer);
-begin
-  if InitSSLInterface and Assigned(_DESecbencrypt) then
-    _DESecbencrypt(Input, output, ks, enc);
-end;
-
-function LoadLib(const Value: String): HModule;
-begin
-  Result := LoadLibrary(Value);
-end;
-
-function GetProcAddr(module: HModule; const ProcName: string): SslPtr;
-begin
-  Result := GetProcAddress(module, PChar(ProcName));
-end;
-
-function InitSSLInterface: Boolean;
-{var
-  s: string;
-  x: integer;}
-begin
-    if not IsSSLloaded then
-    begin
-      SSLLibHandle := LoadLib(DLLSSLName);
-      SSLUtilHandle := LoadLib(DLLUtilName);
-  {$IFNDEF UNIX}
-      if (SSLLibHandle = 0) then
-        SSLLibHandle := LoadLib(DLLSSLName2);
-  {$ENDIF}
-      if (SSLLibHandle <> 0) and (SSLUtilHandle <> 0) then
-      begin
-        _SslGetError := GetProcAddr(SSLLibHandle, 'SSL_get_error');
-        _SslLibraryInit := GetProcAddr(SSLLibHandle, 'SSL_library_init');
-        _SslLoadErrorStrings := GetProcAddr(SSLLibHandle, 'SSL_load_error_strings');
-        _SslCtxSetCipherList := GetProcAddr(SSLLibHandle, 'SSL_CTX_set_cipher_list');
-        _SslCtxNew := GetProcAddr(SSLLibHandle, 'SSL_CTX_new');
-        _SslCtxFree := GetProcAddr(SSLLibHandle, 'SSL_CTX_free');
-        _SslSetFd := GetProcAddr(SSLLibHandle, 'SSL_set_fd');
-        _SslMethodV2 := GetProcAddr(SSLLibHandle, 'SSLv2_method');
-        _SslMethodV3 := GetProcAddr(SSLLibHandle, 'SSLv3_method');
-        _SslMethodTLSV1 := GetProcAddr(SSLLibHandle, 'TLSv1_method');
-        _SslMethodV23 := GetProcAddr(SSLLibHandle, 'SSLv23_method');
-        _SslCtxUsePrivateKey := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_PrivateKey');
-        _SslCtxUsePrivateKeyASN1 := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_PrivateKey_ASN1');
-        //use SSL_CTX_use_RSAPrivateKey_file instead SSL_CTX_use_PrivateKey_file,
-        //because SSL_CTX_use_PrivateKey_file not support DER format. :-O
-        _SslCtxUsePrivateKeyFile := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_RSAPrivateKey_file');
-        _SslCtxUseCertificate := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_certificate');
-        _SslCtxUseCertificateASN1 := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_certificate_ASN1');
-        _SslCtxUseCertificateFile := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_certificate_file');
-        _SslCtxUseCertificateChainFile := GetProcAddr(SSLLibHandle, 'SSL_CTX_use_certificate_chain_file');
-        _SslCtxCheckPrivateKeyFile := GetProcAddr(SSLLibHandle, 'SSL_CTX_check_private_key');
-        _SslCtxSetDefaultPasswdCb := GetProcAddr(SSLLibHandle, 'SSL_CTX_set_default_passwd_cb');
-        _SslCtxSetDefaultPasswdCbUserdata := GetProcAddr(SSLLibHandle, 'SSL_CTX_set_default_passwd_cb_userdata');
-        _SslCtxLoadVerifyLocations := GetProcAddr(SSLLibHandle, 'SSL_CTX_load_verify_locations');
-        _SslNew := GetProcAddr(SSLLibHandle, 'SSL_new');
-        _SslFree := GetProcAddr(SSLLibHandle, 'SSL_free');
-        _SslAccept := GetProcAddr(SSLLibHandle, 'SSL_accept');
-        _SslConnect := GetProcAddr(SSLLibHandle, 'SSL_connect');
-        _SslShutdown := GetProcAddr(SSLLibHandle, 'SSL_shutdown');
-        _SslRead := GetProcAddr(SSLLibHandle, 'SSL_read');
-        _SslPeek := GetProcAddr(SSLLibHandle, 'SSL_peek');
-        _SslWrite := GetProcAddr(SSLLibHandle, 'SSL_write');
-        _SslPending := GetProcAddr(SSLLibHandle, 'SSL_pending');
-        _SslGetPeerCertificate := GetProcAddr(SSLLibHandle, 'SSL_get_peer_certificate');
-        _SslGetVersion := GetProcAddr(SSLLibHandle, 'SSL_get_version');
-        _SslCtxSetVerify := GetProcAddr(SSLLibHandle, 'SSL_CTX_set_verify');
-        _SslGetCurrentCipher := GetProcAddr(SSLLibHandle, 'SSL_get_current_cipher');
-        _SslCipherGetName := GetProcAddr(SSLLibHandle, 'SSL_CIPHER_get_name');
-        _SslCipherGetBits := GetProcAddr(SSLLibHandle, 'SSL_CIPHER_get_bits');
-        _SslGetVerifyResult := GetProcAddr(SSLLibHandle, 'SSL_get_verify_result');
-
-        _X509New := GetProcAddr(SSLUtilHandle, 'X509_new');
-        _X509Free := GetProcAddr(SSLUtilHandle, 'X509_free');
-        _X509NameOneline := GetProcAddr(SSLUtilHandle, 'X509_NAME_oneline');
-        _X509GetSubjectName := GetProcAddr(SSLUtilHandle, 'X509_get_subject_name');
-        _X509GetIssuerName := GetProcAddr(SSLUtilHandle, 'X509_get_issuer_name');
-        _X509NameHash := GetProcAddr(SSLUtilHandle, 'X509_NAME_hash');
-        _X509Digest := GetProcAddr(SSLUtilHandle, 'X509_digest');
-        _X509print := GetProcAddr(SSLUtilHandle, 'X509_print');
-        _X509SetVersion := GetProcAddr(SSLUtilHandle, 'X509_set_version');
-        _X509SetPubkey := GetProcAddr(SSLUtilHandle, 'X509_set_pubkey');
-        _X509SetIssuerName := GetProcAddr(SSLUtilHandle, 'X509_set_issuer_name');
-        _X509NameAddEntryByTxt := GetProcAddr(SSLUtilHandle, 'X509_NAME_add_entry_by_txt');
-        _X509Sign := GetProcAddr(SSLUtilHandle, 'X509_sign');
-        _X509GmtimeAdj := GetProcAddr(SSLUtilHandle, 'X509_gmtime_adj');
-        _X509SetNotBefore := GetProcAddr(SSLUtilHandle, 'X509_set_notBefore');
-        _X509SetNotAfter := GetProcAddr(SSLUtilHandle, 'X509_set_notAfter');
-        _X509GetSerialNumber := GetProcAddr(SSLUtilHandle, 'X509_get_serialNumber');
-        _EvpPkeyNew := GetProcAddr(SSLUtilHandle, 'EVP_PKEY_new');
-        _EvpPkeyFree := GetProcAddr(SSLUtilHandle, 'EVP_PKEY_free');
-        _EvpPkeyAssign := GetProcAddr(SSLUtilHandle, 'EVP_PKEY_assign');
-        _EVPCleanup := GetProcAddr(SSLUtilHandle, 'EVP_cleanup');
-        _EvpGetDigestByName := GetProcAddr(SSLUtilHandle, 'EVP_get_digestbyname');
-        _SSLeayversion := GetProcAddr(SSLUtilHandle, 'SSLeay_version');
-        _ErrErrorString := GetProcAddr(SSLUtilHandle, 'ERR_error_string_n');
-        _ErrGetError := GetProcAddr(SSLUtilHandle, 'ERR_get_error');
-        _ErrClearError := GetProcAddr(SSLUtilHandle, 'ERR_clear_error');
-        _ErrFreeStrings := GetProcAddr(SSLUtilHandle, 'ERR_free_strings');
-        _ErrRemoveState := GetProcAddr(SSLUtilHandle, 'ERR_remove_state');
-        _OPENSSLaddallalgorithms := GetProcAddr(SSLUtilHandle, 'OPENSSL_add_all_algorithms_noconf');
-        _CRYPTOcleanupAllExData := GetProcAddr(SSLUtilHandle, 'CRYPTO_cleanup_all_ex_data');
-        _RandScreen := GetProcAddr(SSLUtilHandle, 'RAND_screen');
-        _BioNew := GetProcAddr(SSLUtilHandle, 'BIO_new');
-        _BioFreeAll := GetProcAddr(SSLUtilHandle, 'BIO_free_all');
-        _BioSMem := GetProcAddr(SSLUtilHandle, 'BIO_s_mem');
-        _BioCtrlPending := GetProcAddr(SSLUtilHandle, 'BIO_ctrl_pending');
-        _BioRead := GetProcAddr(SSLUtilHandle, 'BIO_read');
-        _BioWrite := GetProcAddr(SSLUtilHandle, 'BIO_write');
-        _d2iPKCS12bio := GetProcAddr(SSLUtilHandle, 'd2i_PKCS12_bio');
-        _PKCS12parse := GetProcAddr(SSLUtilHandle, 'PKCS12_parse');
-        _PKCS12free := GetProcAddr(SSLUtilHandle, 'PKCS12_free');
-        _RsaGenerateKey := GetProcAddr(SSLUtilHandle, 'RSA_generate_key');
-        _Asn1UtctimeNew := GetProcAddr(SSLUtilHandle, 'ASN1_UTCTIME_new');
-        _Asn1UtctimeFree := GetProcAddr(SSLUtilHandle, 'ASN1_UTCTIME_free');
-        _Asn1IntegerSet := GetProcAddr(SSLUtilHandle, 'ASN1_INTEGER_set');
-        _i2dX509bio := GetProcAddr(SSLUtilHandle, 'i2d_X509_bio');
-        _i2dPrivateKeyBio := GetProcAddr(SSLUtilHandle, 'i2d_PrivateKey_bio');
-
-        // 3DES functions
-        _DESsetoddparity := GetProcAddr(SSLUtilHandle, 'DES_set_odd_parity');
-        _DESsetkeychecked := GetProcAddr(SSLUtilHandle, 'DES_set_key_checked');
-        _DESecbencrypt := GetProcAddr(SSLUtilHandle, 'DES_ecb_encrypt');
-        //
-        _CRYPTOnumlocks := GetProcAddr(SSLUtilHandle, 'CRYPTO_num_locks');
-        _CRYPTOsetlockingcallback := GetProcAddr(SSLUtilHandle, 'CRYPTO_set_locking_callback');
-
-{        SetLength(s, 1024);
-        x := GetModuleFilename(SSLLibHandle,PChar(s),Length(s));
-        SetLength(s, x);
-        SSLLibFile := s;
-        SetLength(s, 1024);
-        x := GetModuleFilename(SSLUtilHandle,PChar(s),Length(s));
-        SetLength(s, x);
-        SSLUtilFile := s;}
-        //init library
-        if assigned(_SslLibraryInit) then
-          _SslLibraryInit;
-        if assigned(_SslLoadErrorStrings) then
-          _SslLoadErrorStrings;
-        if assigned(_OPENSSLaddallalgorithms) then
-          _OPENSSLaddallalgorithms;
-        if assigned(_RandScreen) then
-          _RandScreen;
-{$WARNING investigate if it REALLY needs to be done}
-{        if assigned(_CRYPTOnumlocks) and assigned(_CRYPTOsetlockingcallback) then
-          InitLocks;}
-
-        Result := True;
-        SSLloaded := True;
-      end
-      else
-      begin
-        //load failed!
-        if SSLLibHandle <> 0 then
-        begin
-          FreeLibrary(SSLLibHandle);
-          SSLLibHandle := 0;
-        end;
-        if SSLUtilHandle <> 0 then
-        begin
-          FreeLibrary(SSLUtilHandle);
-          SSLLibHandle := 0;
-        end;
-        Result := False;
-      end;
-    end
-    else
-      //loaded before...
-      Result := true;
-end;
-
-function DestroySSLInterface: Boolean;
-begin
-    if IsSSLLoaded then
-    begin
-{      //deinit library
-      if assigned(_CRYPTOnumlocks) and assigned(_CRYPTOsetlockingcallback) then
-        FreeLocks;}
-      EVPCleanup;
-      CRYPTOcleanupAllExData;
-      ErrRemoveState(0);
-    end;
-    SSLloaded := false;
-    if SSLLibHandle <> 0 then
-    begin
-      FreeLibrary(SSLLibHandle);
-      SSLLibHandle := 0;
-    end;
-    if SSLUtilHandle <> 0 then
-    begin
-      FreeLibrary(SSLUtilHandle);
-      SSLLibHandle := 0;
-    end;
-
-    _SslGetError := nil;
-    _SslLibraryInit := nil;
-    _SslLoadErrorStrings := nil;
-    _SslCtxSetCipherList := nil;
-    _SslCtxNew := nil;
-    _SslCtxFree := nil;
-    _SslSetFd := nil;
-    _SslMethodV2 := nil;
-    _SslMethodV3 := nil;
-    _SslMethodTLSV1 := nil;
-    _SslMethodV23 := nil;
-    _SslCtxUsePrivateKey := nil;
-    _SslCtxUsePrivateKeyASN1 := nil;
-    _SslCtxUsePrivateKeyFile := nil;
-    _SslCtxUseCertificate := nil;
-    _SslCtxUseCertificateASN1 := nil;
-    _SslCtxUseCertificateFile := nil;
-    _SslCtxUseCertificateChainFile := nil;
-    _SslCtxCheckPrivateKeyFile := nil;
-    _SslCtxSetDefaultPasswdCb := nil;
-    _SslCtxSetDefaultPasswdCbUserdata := nil;
-    _SslCtxLoadVerifyLocations := nil;
-    _SslNew := nil;
-    _SslFree := nil;
-    _SslAccept := nil;
-    _SslConnect := nil;
-    _SslShutdown := nil;
-    _SslRead := nil;
-    _SslPeek := nil;
-    _SslWrite := nil;
-    _SslPending := nil;
-    _SslGetPeerCertificate := nil;
-    _SslGetVersion := nil;
-    _SslCtxSetVerify := nil;
-    _SslGetCurrentCipher := nil;
-    _SslCipherGetName := nil;
-    _SslCipherGetBits := nil;
-    _SslGetVerifyResult := nil;
-
-    _X509New := nil;
-    _X509Free := nil;
-    _X509NameOneline := nil;
-    _X509GetSubjectName := nil;
-    _X509GetIssuerName := nil;
-    _X509NameHash := nil;
-    _X509Digest := nil;
-    _X509print := nil;
-    _X509SetVersion := nil;
-    _X509SetPubkey := nil;
-    _X509SetIssuerName := nil;
-    _X509NameAddEntryByTxt := nil;
-    _X509Sign := nil;
-    _X509GmtimeAdj := nil;
-    _X509SetNotBefore := nil;
-    _X509SetNotAfter := nil;
-    _X509GetSerialNumber := nil;
-    _EvpPkeyNew := nil;
-    _EvpPkeyFree := nil;
-    _EvpPkeyAssign := nil;
-    _EVPCleanup := nil;
-    _EvpGetDigestByName := nil;
-    _SSLeayversion := nil;
-    _ErrErrorString := nil;
-    _ErrGetError := nil;
-    _ErrClearError := nil;
-    _ErrFreeStrings := nil;
-    _ErrRemoveState := nil;
-    _OPENSSLaddallalgorithms := nil;
-    _CRYPTOcleanupAllExData := nil;
-    _RandScreen := nil;
-    _BioNew := nil;
-    _BioFreeAll := nil;
-    _BioSMem := nil;
-    _BioCtrlPending := nil;
-    _BioRead := nil;
-    _BioWrite := nil;
-    _d2iPKCS12bio := nil;
-    _PKCS12parse := nil;
-    _PKCS12free := nil;
-    _RsaGenerateKey := nil;
-    _Asn1UtctimeNew := nil;
-    _Asn1UtctimeFree := nil;
-    _Asn1IntegerSet := nil;
-    _i2dX509bio := nil;
-    _i2dPrivateKeyBio := nil;
-
-    // 3DES functions
-    _DESsetoddparity := nil;
-    _DESsetkeychecked := nil;
-    _DESecbencrypt := nil;
-    //
-    _CRYPTOnumlocks := nil;
-    _CRYPTOsetlockingcallback := nil;
-  Result := True;
-end;
-
-function IsSSLloaded: Boolean;
-begin
-  Result := SSLLoaded;
-end;
-
-finalization
-  DestroySSLInterface;
-
-end.