mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 10:39:53 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2916 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			2916 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{******************************************************************}
 | 
						|
{*   IPUTILS.PAS - Miscellaneous Constants, Types, and Routines   *}
 | 
						|
{******************************************************************}
 | 
						|
 | 
						|
(* ***** BEGIN LICENSE BLOCK *****
 | 
						|
 * Version: MPL 1.1
 | 
						|
 *
 | 
						|
 * The contents of this file are subject to the Mozilla Public License Version
 | 
						|
 * 1.1 (the "License"); you may not use this file except in compliance with
 | 
						|
 * the License. You may obtain a copy of the License at
 | 
						|
 * http://www.mozilla.org/MPL/
 | 
						|
 *
 | 
						|
 * Software distributed under the License is distributed on an "AS IS" basis,
 | 
						|
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 | 
						|
 * for the specific language governing rights and limitations under the
 | 
						|
 * License.
 | 
						|
 *
 | 
						|
 * The Original Code is TurboPower Internet Professional
 | 
						|
 *
 | 
						|
 * The Initial Developer of the Original Code is
 | 
						|
 * TurboPower Software
 | 
						|
 *
 | 
						|
 * Portions created by the Initial Developer are Copyright (C) 2000-2002
 | 
						|
 * the Initial Developer. All Rights Reserved.
 | 
						|
 *
 | 
						|
 * Contributor(s):
 | 
						|
 *
 | 
						|
 * ***** END LICENSE BLOCK ***** *)
 | 
						|
 | 
						|
{ Global defines potentially affecting this unit }
 | 
						|
{$I IPDEFINE.INC}
 | 
						|
 | 
						|
unit IpUtils;
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
  SysUtils,
 | 
						|
  {$IFDEF IP_LAZARUS}
 | 
						|
  LCLType,
 | 
						|
  GraphType,
 | 
						|
  LCLIntf,
 | 
						|
  LMessages,
 | 
						|
  FileUtil,
 | 
						|
  LCLProc,
 | 
						|
  {$ELSE}
 | 
						|
  Messages,
 | 
						|
  Windows,
 | 
						|
  ExtCtrls,
 | 
						|
  SyncObjs, //JMN
 | 
						|
  {$ENDIF}
 | 
						|
  Classes,
 | 
						|
  Controls,
 | 
						|
  Registry,
 | 
						|
  ComCtrls;
 | 
						|
 | 
						|
const
 | 
						|
  InternetProfessionalVersion = 1.15;
 | 
						|
 | 
						|
resourcestring
 | 
						|
  sLongVersion = 'Version %.2f';
 | 
						|
  sShortVersion = 'v%.2f';
 | 
						|
 | 
						|
const
 | 
						|
  IpMsgBase = WM_USER + $0E90;
 | 
						|
 | 
						|
  CM_IPASYNCRESULT      = IpMsgBase + 0;
 | 
						|
  CM_IPSOCKMESSAGE      = IpMsgBase + 1;
 | 
						|
  CM_IPSOCKETSTATUS     = IpMsgBase + 2;
 | 
						|
  CM_IPFREESOCKET       = IpMsgBase + 3;                               {!!.02}
 | 
						|
  CM_IPLINEMESSAGE      = IpMsgBase + 4;
 | 
						|
  CM_IPTERMDATA         = IpMsgBase + 5;
 | 
						|
  CM_IPTERMRESIZE       = IpMsgBase + 6;
 | 
						|
  CM_IPICMPECHO         = IpMsgBase + 7;
 | 
						|
  CM_IPHTTPGETREQUEST   = IpMsgBase + 8;
 | 
						|
  CM_IPTIMESERVER       = IpMsgBase + 9;
 | 
						|
  CM_IPTIMECLIENT       = IpMsgBase + 10;
 | 
						|
  CM_IPSNTPCLIENT       = IpMsgBase + 11;
 | 
						|
  CM_IPFTPREPLY         = IpMsgBase + 12;
 | 
						|
  CM_IPFTPSTATUS        = IpMsgBase + 13;
 | 
						|
  CM_IPFTPERROR         = IpMsgBase + 14;
 | 
						|
  CM_IPFTPTIMEOUT       = IpMsgBase + 15;
 | 
						|
  CM_IPTERMFORCESIZE    = IpMsgBase + 16;
 | 
						|
  CM_IPTERMSTUFF        = IpMsgBase + 17;
 | 
						|
  CM_IPRASSTATUS        = IpMsgBase + 18;
 | 
						|
  CM_IPFINWHOSERVER     = IpMsgBase + 19;
 | 
						|
  CM_IPUTILITYSERVER    = IpMsgBase + 20;
 | 
						|
  CM_IPSMTPEVENT        = IpMsgBase + 21;                              {!!.02}
 | 
						|
  CM_IPPOP3EVENT        = IpMsgBase + 22;                              {!!.02}
 | 
						|
  CM_IPNNTPEVENT        = IpMsgBase + 23;                              {!!.02}
 | 
						|
  {$IFDEF IP_LAZARUS}
 | 
						|
  CM_IPHOTINVOKE        = IpMsgBase + 24;
 | 
						|
  {$ENDIF}
 | 
						|
 | 
						|
type
 | 
						|
  { Hack for Delphi 3 compatibility -- THandle is defined as Integer }
 | 
						|
  { in D3, and Cardinal in others -- causing a problem with event    }
 | 
						|
  { properties for applications shared amongst the compilers, such   }
 | 
						|
  { as our examples. }
 | 
						|
  TIpHandle = Cardinal;
 | 
						|
 | 
						|
  TIpLineTerminator = (ltNone, ltCR, ltLF, ltCRLF, ltOther);
 | 
						|
 | 
						|
  TIpCRCByteArray = array[0..Pred(High(LongInt))] of Byte;
 | 
						|
 | 
						|
  TIpCharArray = array[0..Pred(High(LongInt))] of AnsiChar;
 | 
						|
 | 
						|
  TIpMD5StateArray = array[0..3] of DWORD;
 | 
						|
  TIpMD5CountArray = array[0..1] of DWORD;
 | 
						|
 | 
						|
  TIpMD5ByteBuf = array[0..63] of Byte;
 | 
						|
  TIpMD5LongBuf = array[0..15] of DWORD;
 | 
						|
 | 
						|
  TIpMD5Context = record
 | 
						|
    State : TIpMD5StateArray;
 | 
						|
    Count : TIpMD5CountArray;
 | 
						|
    case Integer of
 | 
						|
      0 : (ByteBuf : TIpMD5ByteBuf);
 | 
						|
      1 : (LongBuf : TIpMD5LongBuf);
 | 
						|
  end;
 | 
						|
 | 
						|
  TIpMD5Digest = array[0..15] of Byte;
 | 
						|
 | 
						|
  EIpBaseException = class(Exception);
 | 
						|
 | 
						|
  EIpAccessException = class(EIpBaseException);
 | 
						|
  EIpHtmlException = class(EIpBaseException);                  {!!.02}
 | 
						|
 | 
						|
  TIpBaseAccess = class
 | 
						|
  private
 | 
						|
    { Property variables }
 | 
						|
    FModule : TIpHandle;
 | 
						|
    { Internal variables }
 | 
						|
   {$IFDEF IP_LAZARUS}
 | 
						|
    baPropCS : TCriticalSection;
 | 
						|
   {$ELSE}
 | 
						|
    baPropCS : TRTLCriticalSection; //JMN
 | 
						|
   {$ENDIF}
 | 
						|
  protected
 | 
						|
    property Module : TIpHandle read FModule write FModule;
 | 
						|
  public
 | 
						|
    constructor Create; virtual;
 | 
						|
    destructor Destroy; override;
 | 
						|
    procedure LockProperties;
 | 
						|
    procedure UnlockProperties;
 | 
						|
  end;
 | 
						|
 | 
						|
  TIpBasePersistent = class(TPersistent)
 | 
						|
  private
 | 
						|
   {$IFDEF IP_LAZARUS}
 | 
						|
    bpPropCS : TCriticalSection;
 | 
						|
   {$ELSE}
 | 
						|
    bpPropCS : TRTLCriticalSection; //JMN
 | 
						|
   {$ENDIF}
 | 
						|
  public
 | 
						|
    constructor Create; virtual;
 | 
						|
    destructor Destroy; override;
 | 
						|
    procedure LockProperties;
 | 
						|
    procedure UnlockProperties;
 | 
						|
  end;
 | 
						|
 | 
						|
  TIpComponentClass = class of TIpBaseComponent;
 | 
						|
 | 
						|
  TIpBaseComponent = class(TComponent)
 | 
						|
  protected
 | 
						|
    function GetVersion : string;
 | 
						|
    procedure SetVersion(const Value : string);
 | 
						|
  public
 | 
						|
    class function GetLogString(const S, D1, D2, D3 : DWORD) : string; virtual;
 | 
						|
  published
 | 
						|
    property Version : string
 | 
						|
      read GetVersion write SetVersion stored False;
 | 
						|
  end;
 | 
						|
 | 
						|
  TIpBaseWinControl = class(TWinControl)
 | 
						|
  protected
 | 
						|
    function GetVersion : string;
 | 
						|
    procedure SetVersion(const Value : string);
 | 
						|
  published
 | 
						|
    property Version : string read GetVersion write SetVersion stored False;
 | 
						|
  end;
 | 
						|
 | 
						|
  { Misc utility routines }
 | 
						|
  function InClassA(Addr : LongInt) : Boolean;
 | 
						|
  function InClassB(Addr : LongInt) : Boolean;
 | 
						|
  function InClassC(Addr : LongInt) : Boolean;
 | 
						|
  function InClassD(Addr : LongInt) : Boolean;
 | 
						|
  function InMulticast(Addr : LongInt) : Boolean;
 | 
						|
 | 
						|
  function IpCharCount(const Buffer; BufSize : DWORD; C : AnsiChar) : DWORD;
 | 
						|
  function IpCompStruct(const S1, S2; Size : Cardinal) : Integer;
 | 
						|
  function IpMaxInt(A, B : Integer) : Integer;
 | 
						|
  function IpMinInt(A, B : Integer) : Integer;
 | 
						|
  procedure IpSafeFree(var Obj);
 | 
						|
  function IpShortVersion : string;
 | 
						|
 | 
						|
  { CRC routines }
 | 
						|
  function InternetSumPrim(var Data; DataSize, CurCrc : DWORD) : DWORD;
 | 
						|
  function InternetSumOfStream(Stream : TStream; CurCrc : DWORD) : DWORD;
 | 
						|
  function InternetSumOfFile(const FileName : string) : DWORD;
 | 
						|
  function MD5SumOfFile(const FileName : string) : string;
 | 
						|
  function MD5SumOfStream(Stream : TStream) : string;
 | 
						|
  function MD5SumOfStreamDigest(Stream : TStream) : TIpMD5Digest;
 | 
						|
  function MD5SumOfString(const S : string) : string;
 | 
						|
  function MD5SumOfStringDigest(const S : string) : TIpMD5Digest;
 | 
						|
 | 
						|
  function SafeYield : LongInt; {-Allow other processes a chance to run}
 | 
						|
  function AllTrimSpaces(Strng: string) : string;
 | 
						|
  function CharPos(C: AnsiChar; const S : string): Integer;
 | 
						|
  function CharPosIdx(C: AnsiChar; const S : string; Idx: Integer): Integer;
 | 
						|
  function NthCharPos(C: AnsiChar; const S : string; Nth: Integer): Integer;
 | 
						|
  function RCharPos(C: AnsiChar; const S : string): Integer;
 | 
						|
  function RCharPosIdx(C: AnsiChar; const S : string; Idx: Integer): Integer;
 | 
						|
  function RNthCharPos(C: AnsiChar; const S : string; Nth: Integer): Integer;
 | 
						|
  function RPos(const Substr: string; const S: string): Integer;
 | 
						|
  function PosIdx(const SubStr, S: string; Idx: Integer): Integer;
 | 
						|
 | 
						|
 | 
						|
{address handling}
 | 
						|
type
 | 
						|
  CharSet = set of AnsiChar;
 | 
						|
 | 
						|
{ Structure to hold pieces of a URI (Uniform Resource Identifier) }
 | 
						|
{ field names are derived from terminology used in:               }
 | 
						|
{ RFC-2396 "Uniform Resource Identifiers (URI): Generic Syntax"   }
 | 
						|
 | 
						|
  TIpAddrRec = record
 | 
						|
    Scheme     : string;
 | 
						|
    UserName   : string;                                                 {!!.03}
 | 
						|
    Password   : string;                                                 {!!.03}
 | 
						|
    Authority  : string;
 | 
						|
    Port       : string;
 | 
						|
    Path       : string;
 | 
						|
    Fragment   : string;
 | 
						|
    Query      : string;
 | 
						|
    QueryDelim : AnsiChar;
 | 
						|
  end;
 | 
						|
  
 | 
						|
  {$IFDEF IP_LAZARUS}
 | 
						|
  procedure Initialize(var AddrRec: TIpAddrRec);
 | 
						|
  procedure Finalize(var AddrRec: TIpAddrRec);
 | 
						|
  {$ENDIF}
 | 
						|
 | 
						|
  function ExtractEntityName(const NamePath: string): string;
 | 
						|
  function ExtractEntityPath(const NamePath: string): string;
 | 
						|
  function IpParseURL(const URL : string; var Rslt : TIpAddrRec) : Boolean;
 | 
						|
  function BuildURL(const OldURL, NewURL: string): string;
 | 
						|
  function PutEscapes(const S : string; EscapeSet : CharSet) : string;
 | 
						|
  function RemoveEscapes(const S : string; EscapeSet : CharSet) : string;
 | 
						|
  procedure SplitParams(const Parms : string; Dest : TStrings);
 | 
						|
  function NetToDOSPath(const PathStr : string) : string;
 | 
						|
  function DOSToNetPath(const PathStr : string) : string;
 | 
						|
  procedure SplitHttpResponse(const S : string; var V, MsgID, Msg: string);
 | 
						|
  procedure FieldFix(Fields : TStrings);
 | 
						|
  function AppendSlash(APath : string) : string;
 | 
						|
  function RemoveSlash(APath : string) : string;
 | 
						|
  function GetParentPath(const Path : string) : string;
 | 
						|
 | 
						|
{ File/Directory Stuff }
 | 
						|
  function GetLocalContent(const TheFileName: string): string;
 | 
						|
  function DirExists(Dir : string): Boolean;
 | 
						|
  function GetTemporaryFile(const Path : string) : string;
 | 
						|
  function GetTemporaryPath: string;
 | 
						|
  function AppendBackSlash(APath : string) : string;
 | 
						|
  function RemoveBackSlash(APath: string) : string;
 | 
						|
 | 
						|
{ date stuff }
 | 
						|
 | 
						|
  { convert Net date (as spec'ed in RFC 2616) to Delphi TDateTime }
 | 
						|
  function INetDateStrToDateTime(const DateStr: string): TDateTime;
 | 
						|
  { convert Delphi TDateTime to Net date (as spec'ed in RFC 2616) }
 | 
						|
  function DateTimeToINetDateTimeStr(DateTime: TDateTime): string;
 | 
						|
  { return the current local TimeZone "bias" in minutes from UTC (GMT) }
 | 
						|
  function TimeZoneBias : Integer;
 | 
						|
 | 
						|
  procedure SplitCookieFields(const Data: string; Fields: TStrings);
 | 
						|
 | 
						|
implementation
 | 
						|
{ misc utility routines }
 | 
						|
 | 
						|
{ Allow other processes a chance to run }
 | 
						|
function SafeYield : LongInt;
 | 
						|
{$IFNDEF IP_LAZARUS}
 | 
						|
var
 | 
						|
  Msg : TMsg;
 | 
						|
{$ENDIF}
 | 
						|
begin
 | 
						|
  SafeYield := 0;
 | 
						|
  {$IFDEF IP_LAZARUS}
 | 
						|
  writeln('ToDo: IpUtils.SafeYield');
 | 
						|
  exit;
 | 
						|
  {$ELSE}
 | 
						|
  if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin
 | 
						|
    if Msg.Message = wm_Quit then
 | 
						|
      {Re-post quit message so main message loop will terminate}
 | 
						|
      PostQuitMessage(Msg.WParam)
 | 
						|
    else begin
 | 
						|
      TranslateMessage(Msg);
 | 
						|
      DispatchMessage(Msg);
 | 
						|
    end;
 | 
						|
    {Return message so caller can act on message if necessary}
 | 
						|
    SafeYield := MAKELONG(Msg.Message, Msg.hwnd);
 | 
						|
  end;
 | 
						|
  {$ENDIF}
 | 
						|
end;
 | 
						|
 | 
						|
{ Trim leading and trailing spaces from a string }
 | 
						|
function AllTrimSpaces(Strng: string) : string;
 | 
						|
var
 | 
						|
  StrStart, StrEnd: Cardinal;
 | 
						|
begin
 | 
						|
  StrEnd := Length(Strng);
 | 
						|
  if StrEnd = 0 then begin  { string is empty }
 | 
						|
    Result := '';
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  while (StrEnd > 0 ) and (Strng[StrEnd] = ' ') do begin
 | 
						|
  { find last non-space character }
 | 
						|
    Dec(StrEnd);
 | 
						|
  end;
 | 
						|
 | 
						|
  if StrEnd = 0 then begin  { string was all spaces }
 | 
						|
    Result := '';
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  StrStart := 1;
 | 
						|
  while (StrStart < StrEnd) and (Strng[StrStart] = ' ') do begin
 | 
						|
  { find first non-space character }
 | 
						|
    Inc(StrStart);
 | 
						|
  end;
 | 
						|
 | 
						|
  Result := Copy(Strng, StrStart, StrEnd - StrStart + 1);
 | 
						|
end;
 | 
						|
 | 
						|
{ Find leftmost occurence of character C in string S }
 | 
						|
{* If C not found returns 0 }
 | 
						|
function CharPos(C: AnsiChar; const S : string): Integer;
 | 
						|
var
 | 
						|
  i : Integer;
 | 
						|
begin
 | 
						|
  for i := 1 to length(S) do
 | 
						|
    if (S[i] = C) then begin
 | 
						|
      Result := i;
 | 
						|
      Exit;
 | 
						|
    end;
 | 
						|
  Result := 0;
 | 
						|
end;
 | 
						|
 | 
						|
{ Find leftmost occurrence of character C in string S past location Idx }
 | 
						|
{ * If C not found returns 0 }
 | 
						|
function CharPosIdx(C: AnsiChar; const S : string; Idx: Integer): Integer;
 | 
						|
var
 | 
						|
  Len : Integer;
 | 
						|
begin
 | 
						|
  Len := Length(S);
 | 
						|
  if (Idx > Len) or (Idx < 1) then begin
 | 
						|
    Result := 0;
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  Result := Idx;
 | 
						|
  while (Result <= Len) and (S[Result] <> C) do
 | 
						|
    Inc(Result);
 | 
						|
  if Result > Len then
 | 
						|
    Result := 0;
 | 
						|
end;
 | 
						|
 | 
						|
{ Find Nth occurrence of character C in string S }
 | 
						|
{ * If C not found returns 0 }
 | 
						|
function NthCharPos(C: AnsiChar; const S : string; Nth: Integer): Integer;
 | 
						|
var
 | 
						|
  Len, CharCt : Integer;
 | 
						|
begin
 | 
						|
  if Nth <= 0 then begin
 | 
						|
    Result := 0;
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
  Len := Length(S);
 | 
						|
  CharCt := 0;
 | 
						|
  Result := 1;
 | 
						|
 | 
						|
  while (Result <= Len) and (CharCt < Nth) do begin
 | 
						|
    if S[Result] = C then
 | 
						|
      Inc(CharCt);
 | 
						|
    if CharCt < Nth then
 | 
						|
      Inc(Result);
 | 
						|
  end;
 | 
						|
  if Result > Len then
 | 
						|
    Result := 0;
 | 
						|
end;
 | 
						|
 | 
						|
{ Find rightmost occurrence of character C in string S }
 | 
						|
{ * If C not found returns 0 }
 | 
						|
function RCharPos(C: AnsiChar; const S : string): Integer;
 | 
						|
begin
 | 
						|
  Result := Length(S);
 | 
						|
  while (Result > 0) and (S[Result] <> C) do
 | 
						|
    Dec(Result);
 | 
						|
  if (Result < 0) then
 | 
						|
    Result := 0;
 | 
						|
end;
 | 
						|
 | 
						|
{ Find rightmost occurrence of character C in string S prior to location Idx }
 | 
						|
{ * If C not found returns 0 }
 | 
						|
function RCharPosIdx(C: AnsiChar; const S : string; Idx: Integer): Integer;
 | 
						|
begin
 | 
						|
  Result := Length(S);
 | 
						|
 | 
						|
  if (Idx > Result) or (Idx < 1) then begin
 | 
						|
    Result := 0;
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  Result := Idx;
 | 
						|
  while (Result > 0) and (S[Result] <> C) do
 | 
						|
    Dec(Result);
 | 
						|
  if (Result < 0) then
 | 
						|
    Result := 0;
 | 
						|
end;
 | 
						|
 | 
						|
{ Find Nth from the rightmost occurance of character C in string S }
 | 
						|
{ * If C not found returns 0 }
 | 
						|
function RNthCharPos(C: AnsiChar; const S : string; Nth: Integer): Integer;
 | 
						|
var
 | 
						|
  CharCt : Integer;
 | 
						|
begin
 | 
						|
  if Nth <= 0 then begin
 | 
						|
    Result := 0;
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  CharCt := 0;
 | 
						|
  Result := Length(S);
 | 
						|
  while (Result > 0) and (CharCt < Nth) do begin
 | 
						|
    if S[Result] = C then
 | 
						|
      Inc(CharCt);
 | 
						|
    if CharCt < Nth then
 | 
						|
      Dec(Result);
 | 
						|
  end;
 | 
						|
  if (Result < 0) then
 | 
						|
    Result := 0;
 | 
						|
end;
 | 
						|
 | 
						|
{ Complement to RTL Pos() function, finds RIGHTmost }
 | 
						|
{ instance of a substring (SubStr) within a string (S) }
 | 
						|
{ * If Substr not found returns 0 }
 | 
						|
function RPos(const Substr: string; const S: string): Integer;
 | 
						|
var
 | 
						|
  SL, i : Integer;
 | 
						|
begin
 | 
						|
  SL := Length(Substr);
 | 
						|
  i := Length(S);
 | 
						|
  if (Substr = '') or (S = '') or (SL > i) then begin
 | 
						|
    Result := 0;
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  while i >= SL do begin
 | 
						|
    if S[i] = Substr[SL] then begin
 | 
						|
      if Copy(S, i - SL + 1, SL) = Substr then begin
 | 
						|
        Result := i - SL + 1;
 | 
						|
        Exit;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
    Dec(i);
 | 
						|
  end;
 | 
						|
  Result := i;
 | 
						|
end;
 | 
						|
 | 
						|
{ Find location of first occurence of a substring (SubStr) in a string (S) }
 | 
						|
{ past a particular index (Idx) }
 | 
						|
{ * Result is relative to the start of the entire original string }
 | 
						|
{ * Returns 0 if substring not found }
 | 
						|
function PosIdx(const SubStr, S: string; Idx: Integer): Integer;
 | 
						|
var
 | 
						|
  Temp : string;
 | 
						|
begin
 | 
						|
  Temp := Copy(S, Idx, Length(S) - Idx - 1);
 | 
						|
  Result := Pos(SubStr, Temp);
 | 
						|
  if Result > 0 then
 | 
						|
    Result := Result + (Idx - 1);
 | 
						|
end;
 | 
						|
 | 
						|
{$IFDEF IP_LAZARUS}
 | 
						|
procedure Initialize(var AddrRec: TIpAddrRec);
 | 
						|
begin
 | 
						|
  AddrRec.QueryDelim:=#0;
 | 
						|
end;
 | 
						|
 | 
						|
procedure Finalize(var AddrRec: TIpAddrRec);
 | 
						|
begin
 | 
						|
  with AddrRec do begin
 | 
						|
    Scheme     :='';
 | 
						|
    UserName   :='';                                                 {!!.03}
 | 
						|
    Password   :='';                                                 {!!.03}
 | 
						|
    Authority  :='';
 | 
						|
    Port       :='';
 | 
						|
    Path       :='';
 | 
						|
    Fragment   :='';
 | 
						|
    Query      :='';
 | 
						|
  end;
 | 
						|
end;
 | 
						|
{$ENDIF}
 | 
						|
 | 
						|
const
 | 
						|
  CrcBufSize = 2048;
 | 
						|
  CrcFileMode = fmOpenRead or fmShareDenyWrite;
 | 
						|
 | 
						|
{ Returns True if a given address is a Class A address }
 | 
						|
function InClassA(Addr : LongInt) : Boolean;
 | 
						|
begin
 | 
						|
  Result := (Addr and $80000000) = 0;
 | 
						|
end;
 | 
						|
 | 
						|
{ Returns True if a given address is a Class B address }
 | 
						|
function InClassB(Addr : LongInt) : Boolean;
 | 
						|
begin
 | 
						|
  Result := (Cardinal(Addr) and $C0000000) = $80000000;
 | 
						|
end;
 | 
						|
 | 
						|
{ Returns True if a given address is a Class C address }
 | 
						|
function InClassC(Addr : LongInt) : Boolean;
 | 
						|
begin
 | 
						|
  Result := (Cardinal(Addr) and $E0000000) = $C0000000;
 | 
						|
end;
 | 
						|
 | 
						|
{ Returns True if a given address is a Class D address }
 | 
						|
function InClassD(Addr : LongInt) : Boolean;
 | 
						|
begin
 | 
						|
  Result := (Cardinal(Addr) and $F0000000) = $E0000000;
 | 
						|
end;
 | 
						|
 | 
						|
{ Returns True if a given address is a multicast address }
 | 
						|
function InMulticast(Addr : LongInt) : Boolean;
 | 
						|
begin
 | 
						|
  Result := InClassD(Addr);
 | 
						|
end;
 | 
						|
 | 
						|
{ Calculates the Internet Checksum of a block }
 | 
						|
function InternetSumPrim(var Data; DataSize, CurCrc : DWORD) : DWORD;
 | 
						|
var
 | 
						|
  I : Integer;
 | 
						|
begin
 | 
						|
  Result := CurCrc;
 | 
						|
  if DataSize = 0 then Exit;
 | 
						|
  for I := 0 to (DataSize - 1) do begin
 | 
						|
    if Odd(I) then
 | 
						|
      Result := Result + (cardinal(TIpCRCByteArray(Data)[I]) shl 8)
 | 
						|
    else
 | 
						|
      Result := Result + TIpCRCByteArray(Data)[I];
 | 
						|
  end;
 | 
						|
  Result := (not((Result and $FFFF) + (Result shr 16))) and $FFFF;
 | 
						|
end;
 | 
						|
 | 
						|
{ Calculates the Internet Checksum of a stream }
 | 
						|
function InternetSumOfStream(Stream : TStream; CurCrc : DWORD) : DWORD;
 | 
						|
var
 | 
						|
  BufArray : array[0..(CrcBufSize-1)] of Byte;
 | 
						|
  Res      : LongInt;
 | 
						|
begin
 | 
						|
  {Initialize Crc}
 | 
						|
  Result := CurCrc;
 | 
						|
  repeat
 | 
						|
    Res := Stream.Read(BufArray, CrcBufSize);
 | 
						|
    Result := InternetSumPrim(BufArray, Res, Result);
 | 
						|
  until (Res <> CrcBufSize);
 | 
						|
end;
 | 
						|
 | 
						|
{ Calculates the Internet Checksum of a file }
 | 
						|
function InternetSumOfFile(const FileName : string) : DWORD;
 | 
						|
var
 | 
						|
  FileSt : TFileStream;
 | 
						|
begin
 | 
						|
  FileSt := TFileStream.Create(UTF8ToSys(FileName), CrcFileMode);
 | 
						|
  try
 | 
						|
    Result := InternetSumOfStream(FileSt, 0);
 | 
						|
  finally
 | 
						|
    FileSt.Free;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
{ Initialize the MD5 context record }
 | 
						|
procedure MD5Init(var Context : TIpMD5Context);
 | 
						|
begin
 | 
						|
  { Zero out context }
 | 
						|
  FillChar(Context, SizeOf(TIpMD5Context), #0);
 | 
						|
 | 
						|
  { Load magic initialization constants }
 | 
						|
  Context.State[0] := DWORD($67452301);
 | 
						|
  Context.State[1] := DWORD($efcdab89);
 | 
						|
  Context.State[2] := DWORD($98badcfe);
 | 
						|
  Context.State[3] := DWORD($10325476);
 | 
						|
end;
 | 
						|
 | 
						|
{ MD5 Basic Transformation -- Transforms State based on Buf }
 | 
						|
procedure MD5Transform(var State : TIpMD5StateArray; const Buf : TIpMD5LongBuf);
 | 
						|
const
 | 
						|
  S11 = 7; S12 = 12; S13 = 17; S14 = 22; S21 = 5; S22 = 9; S23 = 14;
 | 
						|
  S24 = 20; S31 = 4; S32 = 11; S33 = 16; S34 = 23; S41 = 6; S42 = 10;
 | 
						|
  S43 = 15; S44 = 21;
 | 
						|
var
 | 
						|
  a, b, c, d : DWORD;
 | 
						|
 | 
						|
  { Round 1 processing }
 | 
						|
  procedure FF(var W : DWORD; X, Y, Z : DWORD; S : Byte; Data : DWORD);
 | 
						|
  begin
 | 
						|
    Inc(W, (Z xor (X and (Y xor Z))) + Data);
 | 
						|
    W := (W shl S) or (W shr (32 - S));
 | 
						|
    Inc(W, X);
 | 
						|
  end;
 | 
						|
 | 
						|
  { Round 2 processing }
 | 
						|
  procedure GG(var W : DWORD; X, Y, Z : DWORD; S : Byte; Data : DWORD);
 | 
						|
  begin
 | 
						|
    Inc(W, (Y xor (Z and (X xor Y))) + Data);
 | 
						|
    W := (W shl S) or (W shr (32 - S));
 | 
						|
    Inc(W, X);
 | 
						|
  end;
 | 
						|
 | 
						|
  { Round 3 processing }
 | 
						|
  procedure HH(var W : DWORD; X, Y, Z : DWORD; S : Byte; Data : DWORD);
 | 
						|
  begin
 | 
						|
    Inc(W, (X xor Y xor Z) + Data);
 | 
						|
    W := (W shl S) or (W shr (32 - S));
 | 
						|
    Inc(W, X);
 | 
						|
  end;
 | 
						|
 | 
						|
  { Round 4 processing }
 | 
						|
  procedure II(var W : DWORD; X, Y, Z : DWORD; S : Byte; Data : DWORD);
 | 
						|
  begin
 | 
						|
    Inc(W, (Y xor (X or not Z)) + Data);
 | 
						|
    W := (W shl S) or (W shr (32 - S));
 | 
						|
    Inc(W, X);
 | 
						|
  end;
 | 
						|
 | 
						|
begin
 | 
						|
  a := State[0];
 | 
						|
  b := State[1];
 | 
						|
  c := State[2];
 | 
						|
  d := State[3];
 | 
						|
 | 
						|
  { Round 1 }
 | 
						|
  FF(a, b, c, d, S11, Buf[ 0] + DWORD($d76aa478)); { 1 }
 | 
						|
  FF(d, a, b, c, S12, Buf[ 1] + DWORD($e8c7b756)); { 2 }
 | 
						|
  FF(c, d, a, b, S13, Buf[ 2] + DWORD($242070db)); { 3 }
 | 
						|
  FF(b, c, d, a, S14, Buf[ 3] + DWORD($c1bdceee)); { 4 }
 | 
						|
  FF(a, b, c, d, S11, Buf[ 4] + DWORD($f57c0faf)); { 5 }
 | 
						|
  FF(d, a, b, c, S12, Buf[ 5] + DWORD($4787c62a)); { 6 }
 | 
						|
  FF(c, d, a, b, S13, Buf[ 6] + DWORD($a8304613)); { 7 }
 | 
						|
  FF(b, c, d, a, S14, Buf[ 7] + DWORD($fd469501)); { 8 }
 | 
						|
  FF(a, b, c, d, S11, Buf[ 8] + DWORD($698098d8)); { 9 }
 | 
						|
  FF(d, a, b, c, S12, Buf[ 9] + DWORD($8b44f7af)); { 10 }
 | 
						|
  FF(c, d, a, b, S13, Buf[10] + DWORD($ffff5bb1)); { 11 }
 | 
						|
  FF(b, c, d, a, S14, Buf[11] + DWORD($895cd7be)); { 12 }
 | 
						|
  FF(a, b, c, d, S11, Buf[12] + DWORD($6b901122)); { 13 }
 | 
						|
  FF(d, a, b, c, S12, Buf[13] + DWORD($fd987193)); { 14 }
 | 
						|
  FF(c, d, a, b, S13, Buf[14] + DWORD($a679438e)); { 15 }
 | 
						|
  FF(b, c, d, a, S14, Buf[15] + DWORD($49b40821)); { 16 }
 | 
						|
 | 
						|
  { Round 2 }
 | 
						|
  GG(a, b, c, d, S21, Buf[ 1] + DWORD($f61e2562)); { 17 }
 | 
						|
  GG(d, a, b, c, S22, Buf[ 6] + DWORD($c040b340)); { 18 }
 | 
						|
  GG(c, d, a, b, S23, Buf[11] + DWORD($265e5a51)); { 19 }
 | 
						|
  GG(b, c, d, a, S24, Buf[ 0] + DWORD($e9b6c7aa)); { 20 }
 | 
						|
  GG(a, b, c, d, S21, Buf[ 5] + DWORD($d62f105d)); { 21 }
 | 
						|
  GG(d, a, b, c, S22, Buf[10] + DWORD($02441453)); { 22 }
 | 
						|
  GG(c, d, a, b, S23, Buf[15] + DWORD($d8a1e681)); { 23 }
 | 
						|
  GG(b, c, d, a, S24, Buf[ 4] + DWORD($e7d3fbc8)); { 24 }
 | 
						|
  GG(a, b, c, d, S21, Buf[ 9] + DWORD($21e1cde6)); { 25 }
 | 
						|
  GG(d, a, b, c, S22, Buf[14] + DWORD($c33707d6)); { 26 }
 | 
						|
  GG(c, d, a, b, S23, Buf[ 3] + DWORD($f4d50d87)); { 27 }
 | 
						|
  GG(b, c, d, a, S24, Buf[ 8] + DWORD($455a14ed)); { 28 }
 | 
						|
  GG(a, b, c, d, S21, Buf[13] + DWORD($a9e3e905)); { 29 }
 | 
						|
  GG(d, a, b, c, S22, Buf[ 2] + DWORD($fcefa3f8)); { 30 }
 | 
						|
  GG(c, d, a, b, S23, Buf[ 7] + DWORD($676f02d9)); { 31 }
 | 
						|
  GG(b, c, d, a, S24, Buf[12] + DWORD($8d2a4c8a)); { 32 }
 | 
						|
 | 
						|
  { Round 3 }
 | 
						|
  HH(a, b, c, d, S31, Buf[ 5] + DWORD($fffa3942)); { 33 }
 | 
						|
  HH(d, a, b, c, S32, Buf[ 8] + DWORD($8771f681)); { 34 }
 | 
						|
  HH(c, d, a, b, S33, Buf[11] + DWORD($6d9d6122)); { 35 }
 | 
						|
  HH(b, c, d, a, S34, Buf[14] + DWORD($fde5380c)); { 36 }
 | 
						|
  HH(a, b, c, d, S31, Buf[ 1] + DWORD($a4beea44)); { 37 }
 | 
						|
  HH(d, a, b, c, S32, Buf[ 4] + DWORD($4bdecfa9)); { 38 }
 | 
						|
  HH(c, d, a, b, S33, Buf[ 7] + DWORD($f6bb4b60)); { 39 }
 | 
						|
  HH(b, c, d, a, S34, Buf[10] + DWORD($bebfbc70)); { 40 }
 | 
						|
  HH(a, b, c, d, S31, Buf[13] + DWORD($289b7ec6)); { 41 }
 | 
						|
  HH(d, a, b, c, S32, Buf[ 0] + DWORD($eaa127fa)); { 42 }
 | 
						|
  HH(c, d, a, b, S33, Buf[ 3] + DWORD($d4ef3085)); { 43 }
 | 
						|
  HH(b, c, d, a, S34, Buf[ 6] + DWORD($04881d05)); { 44 }
 | 
						|
  HH(a, b, c, d, S31, Buf[ 9] + DWORD($d9d4d039)); { 45 }
 | 
						|
  HH(d, a, b, c, S32, Buf[12] + DWORD($e6db99e5)); { 46 }
 | 
						|
  HH(c, d, a, b, S33, Buf[15] + DWORD($1fa27cf8)); { 47 }
 | 
						|
  HH(b, c, d, a, S34, Buf[ 2] + DWORD($c4ac5665)); { 48 }
 | 
						|
 | 
						|
  { Round 4 }
 | 
						|
  II(a, b, c, d, S41, Buf[ 0] + DWORD($f4292244)); { 49 }
 | 
						|
  II(d, a, b, c, S42, Buf[ 7] + DWORD($432aff97)); { 50 }
 | 
						|
  II(c, d, a, b, S43, Buf[14] + DWORD($ab9423a7)); { 51 }
 | 
						|
  II(b, c, d, a, S44, Buf[ 5] + DWORD($fc93a039)); { 52 }
 | 
						|
  II(a, b, c, d, S41, Buf[12] + DWORD($655b59c3)); { 53 }
 | 
						|
  II(d, a, b, c, S42, Buf[ 3] + DWORD($8f0ccc92)); { 54 }
 | 
						|
  II(c, d, a, b, S43, Buf[10] + DWORD($ffeff47d)); { 55 }
 | 
						|
  II(b, c, d, a, S44, Buf[ 1] + DWORD($85845dd1)); { 56 }
 | 
						|
  II(a, b, c, d, S41, Buf[ 8] + DWORD($6fa87e4f)); { 57 }
 | 
						|
  II(d, a, b, c, S42, Buf[15] + DWORD($fe2ce6e0)); { 58 }
 | 
						|
  II(c, d, a, b, S43, Buf[ 6] + DWORD($a3014314)); { 59 }
 | 
						|
  II(b, c, d, a, S44, Buf[13] + DWORD($4e0811a1)); { 60 }
 | 
						|
  II(a, b, c, d, S41, Buf[ 4] + DWORD($f7537e82)); { 61 }
 | 
						|
  II(d, a, b, c, S42, Buf[11] + DWORD($bd3af235)); { 62 }
 | 
						|
  II(c, d, a, b, S43, Buf[ 2] + DWORD($2ad7d2bb)); { 63 }
 | 
						|
  II(b, c, d, a, S44, Buf[ 9] + DWORD($eb86d391)); { 64 }
 | 
						|
 | 
						|
  Inc(State[0], a);
 | 
						|
  Inc(State[1], b);
 | 
						|
  Inc(State[2], c);
 | 
						|
  Inc(State[3], d);
 | 
						|
end;
 | 
						|
 | 
						|
{ MD5 finalization. Ends an MD5 message-digest operation, }
 | 
						|
{ writing the message digest and zeroing the context.     }
 | 
						|
procedure MD5Final(var Digest : TIpMD5Digest; var Context : TIpMD5Context);
 | 
						|
var
 | 
						|
  I : Integer;
 | 
						|
  P : Byte;
 | 
						|
begin
 | 
						|
  I := (Context.Count[0] shr 3) and $3F;
 | 
						|
  Context.ByteBuf[I] := $80;
 | 
						|
  P := Succ(I);
 | 
						|
  I := Pred(64)-I;
 | 
						|
 | 
						|
  { Pad appropriately }
 | 
						|
  if I < 8 then begin
 | 
						|
    FillChar(Context.ByteBuf[P], I, #0);
 | 
						|
    MD5Transform(Context.State, Context.LongBuf);
 | 
						|
    FillChar(Context.ByteBuf, 56, #0);
 | 
						|
  end else begin
 | 
						|
    FillChar(Context.ByteBuf[P], I-8, #0);
 | 
						|
  end;
 | 
						|
 | 
						|
  { Set count in context }
 | 
						|
  Context.LongBuf[14] := Context.Count[0];
 | 
						|
  Context.LongBuf[15] := Context.Count[1];
 | 
						|
 | 
						|
  MD5Transform(Context.State, Context.LongBuf);
 | 
						|
  Move(Context.State, Digest, 16);
 | 
						|
 | 
						|
  { Zero out Context }
 | 
						|
  FillChar(Context, SizeOf(TIpMD5Context), #0);
 | 
						|
end;
 | 
						|
 | 
						|
{ Calculates the MD5 Digest of a block -- RFC 1321 }
 | 
						|
procedure MD5SumPrim(const Data; DataSize : DWORD; var Context : TIpMD5Context);
 | 
						|
var
 | 
						|
  I, J : DWORD;
 | 
						|
begin
 | 
						|
  J := Context.Count[0];
 | 
						|
  Inc(Context.Count[0], DWORD(DataSize) shl 3);
 | 
						|
  if Context.Count[0] < J then
 | 
						|
    Inc(Context.Count[1]);
 | 
						|
  Inc(Context.Count[1], DataSize shr 29);
 | 
						|
 | 
						|
  J := (J shr 3) and $3F;
 | 
						|
  if J <> 0 then begin
 | 
						|
    I := J;
 | 
						|
    J := 64 - J;
 | 
						|
    if DataSize < J then begin
 | 
						|
      Move(Data, Context.ByteBuf[I], DataSize);
 | 
						|
      Exit;
 | 
						|
    end;
 | 
						|
    Move(Data, Context.ByteBuf[I], J);
 | 
						|
    MD5Transform(Context.State, Context.LongBuf);
 | 
						|
    Dec(DataSize, J);
 | 
						|
  end;
 | 
						|
 | 
						|
  I := J;
 | 
						|
  while DataSize >= 64 do begin
 | 
						|
    Move(TByteArray(Data)[I], Context.ByteBuf, 64);
 | 
						|
    MD5Transform(Context.State, Context.LongBuf);
 | 
						|
    Inc(I, 64);
 | 
						|
    Dec(DataSize, 64);
 | 
						|
  end;
 | 
						|
 | 
						|
  Move(TByteArray(Data)[I], Context.ByteBuf, DataSize);
 | 
						|
end;
 | 
						|
 | 
						|
{ Calculates the MD5 Digest of a file }
 | 
						|
function MD5SumOfFile(const FileName : string) : string;
 | 
						|
var
 | 
						|
  FileSt : TFileStream;
 | 
						|
begin
 | 
						|
  FileSt := TFileStream.Create(UTF8ToSys(FileName), CrcFileMode);
 | 
						|
  try
 | 
						|
    Result := MD5SumOfStream(FileSt);
 | 
						|
  finally
 | 
						|
    FileSt.Free;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
{ Return hex string representing MD5 digest }
 | 
						|
function HexDigest(Digest : TIpMD5Digest) : string;
 | 
						|
const
 | 
						|
  HexDigits : array[0..$F] of AnsiChar = '0123456789abcdef';
 | 
						|
var
 | 
						|
  I : Integer;
 | 
						|
begin
 | 
						|
  SetLength(Result, 32);
 | 
						|
 | 
						|
  { Generate output string }
 | 
						|
  for I := 0 to 15 do begin
 | 
						|
    Result[(I shl 1) + 1] := HexDigits[Digest[I] shr 4];
 | 
						|
    Result[(I shl 1) + 2] := HexDigits[Digest[I] and $F];
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
{ Calculates the MD5 Digest of a stream }
 | 
						|
function MD5SumOfStream(Stream : TStream) : string;
 | 
						|
begin
 | 
						|
  Result := HexDigest(MD5SumOfStreamDigest(Stream));
 | 
						|
end;
 | 
						|
 | 
						|
{ Calculates the MD5 Digest of a stream }
 | 
						|
function MD5SumOfStreamDigest(Stream : TStream) : TIpMD5Digest;
 | 
						|
var
 | 
						|
  BufArray : array[0..(CrcBufSize-1)] of Byte;
 | 
						|
  Context  : TIpMD5Context;
 | 
						|
  I, Res   : Integer;
 | 
						|
begin
 | 
						|
  { Init Digest }
 | 
						|
  for I := 0 to 15 do
 | 
						|
    Byte(Result[I]) := Succ(I);
 | 
						|
 | 
						|
  { Init Context }
 | 
						|
  MD5Init(Context);
 | 
						|
  repeat
 | 
						|
    Res := Stream.Read(BufArray, CrcBufSize);
 | 
						|
    MD5SumPrim(BufArray, Res, Context);
 | 
						|
  until (Res <> CrcBufSize);
 | 
						|
 | 
						|
  { Finalize }
 | 
						|
  MD5Final(Result, Context);
 | 
						|
end;
 | 
						|
 | 
						|
{ Calculates the MD5 Digest of a string }
 | 
						|
function MD5SumOfString(const S : string) : string;
 | 
						|
var
 | 
						|
  Context  : TIpMD5Context;
 | 
						|
  Digest   : TIpMD5Digest;
 | 
						|
  I : Byte;
 | 
						|
begin
 | 
						|
  Result := '';
 | 
						|
 | 
						|
  { Init Digest }
 | 
						|
  for I := 0 to 15 do
 | 
						|
    Digest[I] := Succ(I);
 | 
						|
 | 
						|
  { Init Context }
 | 
						|
  MD5Init(Context);
 | 
						|
  MD5SumPrim(S[1], Length(S), Context);
 | 
						|
 | 
						|
  { Finalize }
 | 
						|
  MD5Final(Digest, Context);
 | 
						|
 | 
						|
  { Generate output string }
 | 
						|
  Result := HexDigest(Digest);
 | 
						|
end;
 | 
						|
 | 
						|
{ Calculates the MD5 Digest of a string }
 | 
						|
function MD5SumOfStringDigest(const S : string) : TIpMD5Digest;
 | 
						|
var
 | 
						|
  Context  : TIpMD5Context;      
 | 
						|
  I : Byte;
 | 
						|
begin
 | 
						|
  { Init Digest }
 | 
						|
  for I := 0 to 15 do
 | 
						|
    Result[I] := Succ(I);
 | 
						|
 | 
						|
  { Init Context }
 | 
						|
  MD5Init(Context);
 | 
						|
  MD5SumPrim(S[1], Length(S), Context);
 | 
						|
 | 
						|
  { Finalize }
 | 
						|
  MD5Final(Result, Context);
 | 
						|
end;
 | 
						|
 | 
						|
{ Compares two fixed size structures }
 | 
						|
function IpCompStruct(const S1, S2; Size : Cardinal) : Integer;
 | 
						|
{$IFDEF IP_LAZARUS}
 | 
						|
{$IFDEF CPUI386}
 | 
						|
asm
 | 
						|
  push   edi
 | 
						|
  push   esi
 | 
						|
  mov    esi, eax
 | 
						|
  mov    edi, edx
 | 
						|
  xor    eax, eax
 | 
						|
  or     ecx, ecx
 | 
						|
  jz     @@CSDone
 | 
						|
 | 
						|
  repe   cmpsb
 | 
						|
  je     @@CSDone
 | 
						|
 | 
						|
  inc    eax
 | 
						|
  ja     @@CSDone
 | 
						|
  or     eax, -1
 | 
						|
 | 
						|
@@CSDone:
 | 
						|
  pop    esi
 | 
						|
  pop    edi
 | 
						|
end;
 | 
						|
{$ELSE}
 | 
						|
begin
 | 
						|
  Result := CompareMemRange(@S1, @S2, Size);
 | 
						|
end;
 | 
						|
{$ENDIF}
 | 
						|
{$ELSE}
 | 
						|
{$IFDEF CPU386}
 | 
						|
asm
 | 
						|
  push   edi
 | 
						|
  push   esi
 | 
						|
  mov    esi, eax
 | 
						|
  mov    edi, edx
 | 
						|
  xor    eax, eax
 | 
						|
  or     ecx, ecx
 | 
						|
  jz     @@CSDone
 | 
						|
 | 
						|
  repe   cmpsb
 | 
						|
  je     @@CSDone
 | 
						|
 | 
						|
  inc    eax
 | 
						|
  ja     @@CSDone
 | 
						|
  or     eax, -1
 | 
						|
 | 
						|
@@CSDone:
 | 
						|
  pop    esi
 | 
						|
  pop    edi
 | 
						|
end;
 | 
						|
{$ELSE}
 | 
						|
begin
 | 
						|
  Result := CompareMemRange(@S1, @S2, Size);
 | 
						|
end;
 | 
						|
{$ENDIF}
 | 
						|
{$ENDIF}
 | 
						|
 | 
						|
function IpCharCount(const Buffer; BufSize : DWORD; C : AnsiChar) : DWORD;
 | 
						|
  register;
 | 
						|
{$IFDEF CPUI386}
 | 
						|
asm
 | 
						|
  push  ebx
 | 
						|
  xor   ebx, ebx
 | 
						|
  or    edx, edx
 | 
						|
  jz    @@Done
 | 
						|
  jmp   @@5
 | 
						|
 | 
						|
@@Loop:
 | 
						|
  cmp   cl, [eax+3]
 | 
						|
  jne   @@1
 | 
						|
  inc   ebx
 | 
						|
 | 
						|
@@1:
 | 
						|
  cmp   cl, [eax+2]
 | 
						|
  jne   @@2
 | 
						|
  inc   ebx
 | 
						|
 | 
						|
@@2:
 | 
						|
  cmp   cl, [eax+1]
 | 
						|
  jne   @@3
 | 
						|
  inc   ebx
 | 
						|
 | 
						|
@@3:
 | 
						|
  cmp   cl, [eax+0]
 | 
						|
  jne   @@4
 | 
						|
  inc   ebx
 | 
						|
 | 
						|
@@4:
 | 
						|
  add   eax, 4
 | 
						|
  sub   edx, 4
 | 
						|
 | 
						|
@@5:
 | 
						|
  cmp   edx, 4
 | 
						|
  jge   @@Loop
 | 
						|
 | 
						|
  cmp   edx, 3
 | 
						|
  je    @@1
 | 
						|
 | 
						|
  cmp   edx, 2
 | 
						|
  je    @@2
 | 
						|
 | 
						|
  cmp   edx, 1
 | 
						|
  je    @@3
 | 
						|
 | 
						|
@@Done:
 | 
						|
  mov   eax, ebx
 | 
						|
  pop   ebx
 | 
						|
end;
 | 
						|
{$ELSE}
 | 
						|
var
 | 
						|
X: Integer;
 | 
						|
begin
 | 
						|
  Result := 0;
 | 
						|
  for X := 0 to Bufsize-1 do begin
 | 
						|
    if PChar(@Buffer)[X] = C then Inc(Result);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
{$ENDIF}
 | 
						|
 | 
						|
 | 
						|
function IpMaxInt(A, B : Integer) : Integer;
 | 
						|
begin
 | 
						|
  if A >= B then
 | 
						|
    Result := A
 | 
						|
  else
 | 
						|
    Result := B;
 | 
						|
end;
 | 
						|
 | 
						|
function IpMinInt(A, B : Integer) : Integer;
 | 
						|
begin
 | 
						|
  if A <= B then
 | 
						|
    Result := A
 | 
						|
  else
 | 
						|
    Result := B;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
{ Thread safe object free }
 | 
						|
procedure IpSafeFree(var Obj);
 | 
						|
var
 | 
						|
  P : TObject;
 | 
						|
begin
 | 
						|
  P := TObject(Obj);
 | 
						|
  { Clear reference }
 | 
						|
  TObject(Obj) := nil;
 | 
						|
  { Destroy object }
 | 
						|
  P.Free;
 | 
						|
end;
 | 
						|
 | 
						|
{ Return short version string }
 | 
						|
function IpShortVersion : string;
 | 
						|
begin
 | 
						|
  Result := Format(sShortVersion, [InternetProfessionalVersion]);
 | 
						|
end;
 | 
						|
 | 
						|
{ TIpBaseAccess }
 | 
						|
 | 
						|
{ Create instance of TIpBaseAccess }
 | 
						|
constructor TIpBaseAccess.Create;
 | 
						|
begin
 | 
						|
  inherited;
 | 
						|
  InitializeCriticalSection(baPropCS);
 | 
						|
end;
 | 
						|
 | 
						|
{ Destroy instance of TIpBaseAccess }
 | 
						|
destructor TIpBaseAccess.Destroy;
 | 
						|
begin
 | 
						|
  DeleteCriticalSection(baPropCS);
 | 
						|
  inherited;
 | 
						|
end;
 | 
						|
 | 
						|
{ Enters TIpBaseAccess critical section }
 | 
						|
procedure TIpBaseAccess.LockProperties;
 | 
						|
begin
 | 
						|
  if IsMultiThread then
 | 
						|
    EnterCriticalSection(baPropCS);
 | 
						|
end;
 | 
						|
 | 
						|
{ Leaves TIpBaseAccess critical section }
 | 
						|
procedure TIpBaseAccess.UnlockProperties;
 | 
						|
begin
 | 
						|
  if IsMultiThread then
 | 
						|
    LeaveCriticalSection(baPropCS);
 | 
						|
end;
 | 
						|
 | 
						|
{ TIpBasePersistent }
 | 
						|
 | 
						|
{ Create instance of TIpBasePersistent }
 | 
						|
constructor TIpBasePersistent.Create;
 | 
						|
begin
 | 
						|
  inherited;
 | 
						|
  InitializeCriticalSection(bpPropCS);
 | 
						|
end;
 | 
						|
 | 
						|
{ Destroy instance of TIpBasePersistent }
 | 
						|
destructor TIpBasePersistent.Destroy;
 | 
						|
begin
 | 
						|
  DeleteCriticalSection(bpPropCS);
 | 
						|
  inherited;
 | 
						|
end;
 | 
						|
 | 
						|
{ Enters TIpBasePersistent critical section }
 | 
						|
procedure TIpBasePersistent.LockProperties;
 | 
						|
begin
 | 
						|
  if IsMultiThread then
 | 
						|
    EnterCriticalSection(bpPropCS);
 | 
						|
end;
 | 
						|
 | 
						|
{ Leaves TIpBasePersistent critical section }
 | 
						|
procedure TIpBasePersistent.UnlockProperties;
 | 
						|
begin
 | 
						|
  if IsMultiThread then
 | 
						|
    LeaveCriticalSection(bpPropCS);
 | 
						|
end;
 | 
						|
 | 
						|
{ TIpBaseComponent }
 | 
						|
 | 
						|
function TIpBaseComponent.GetVersion: string;
 | 
						|
begin
 | 
						|
  Result := IpShortVersion;
 | 
						|
end;
 | 
						|
 | 
						|
{ Returns an appropriate string for the given parameters }
 | 
						|
class function TIpBaseComponent.GetLogString(const S, D1, D2, D3: DWORD): string;
 | 
						|
begin
 | 
						|
  {$IFDEF IP_LAZARUS}
 | 
						|
  if (S=0) or (D1=0) or (D2=0) or (D3=0) then ; // avoid hints
 | 
						|
  {$ENDIF}
 | 
						|
  Result := '!!!! Unhandled log entry'#10#13;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TIpBaseComponent.SetVersion(const Value: string);
 | 
						|
begin
 | 
						|
  {$IFDEF IP_LAZARUS}
 | 
						|
  if (Value='') then ; // avoid hints
 | 
						|
  {$ENDIF}
 | 
						|
  { Intentionally empty }
 | 
						|
end;
 | 
						|
 | 
						|
{ TIpBaseWinControl }
 | 
						|
 | 
						|
function TIpBaseWinControl.GetVersion : string;
 | 
						|
begin
 | 
						|
  Result := IpShortVersion;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TIpBaseWinControl.SetVersion(const Value : string);
 | 
						|
begin
 | 
						|
  {$IFDEF IP_LAZARUS}
 | 
						|
  if (Value='') then ; // avoid hints
 | 
						|
  {$ENDIF}
 | 
						|
  { Intentionally empty }
 | 
						|
end;
 | 
						|
 | 
						|
{ address handling }
 | 
						|
 | 
						|
{ Apply Internet escaping (%nn) to characters in EscapeSet found in S }
 | 
						|
function PutEscapes(const S : string; EscapeSet : CharSet) : string;
 | 
						|
var
 | 
						|
  Temp, Rep : string;
 | 
						|
  i : Integer;
 | 
						|
begin
 | 
						|
  Temp := S;
 | 
						|
 | 
						|
  i := 1;
 | 
						|
  while i <= Length(Temp) do begin
 | 
						|
    if Temp[i] in EscapeSet then begin
 | 
						|
      { Internet escapes of the form %nn where }
 | 
						|
      { n is the ASCII character number in Hex }
 | 
						|
      Rep := '%' + Format('%2x', [Ord(Temp[i])]);
 | 
						|
      Delete(Temp, i, 1);
 | 
						|
      Insert(Rep, Temp, i);
 | 
						|
      Inc(i, 3);
 | 
						|
    end
 | 
						|
    else
 | 
						|
      Inc(i);
 | 
						|
  end;
 | 
						|
  Result := Temp;
 | 
						|
end;
 | 
						|
 | 
						|
{ Convert Internet escapes to ASCII equivalents }
 | 
						|
function RemoveEscapes(const S : string; EscapeSet : CharSet) : string;
 | 
						|
var
 | 
						|
  Temp, Start, EscStr : string;
 | 
						|
  P : Integer;
 | 
						|
  C : AnsiChar;
 | 
						|
begin
 | 
						|
  Temp := S;
 | 
						|
  Start := '';
 | 
						|
 | 
						|
  P := CharPos('%', Temp);
 | 
						|
 | 
						|
  while P > 0 do begin
 | 
						|
    Start := Start + Copy(Temp, 1, P-1);
 | 
						|
    EscStr := Copy(Temp, P + 1, 2);
 | 
						|
    C := Chr(StrToInt('$' + EscStr));
 | 
						|
 | 
						|
    if C in EscapeSet then begin
 | 
						|
      Start := Start + C;
 | 
						|
    end
 | 
						|
    else begin
 | 
						|
      Start := Start + EscStr;
 | 
						|
    end;
 | 
						|
 | 
						|
    Temp := Copy(Temp, P + 3, Length(Temp) - 3);
 | 
						|
    P := CharPos('%', Temp);
 | 
						|
  end;
 | 
						|
 | 
						|
  Result := Start + Temp;
 | 
						|
end;
 | 
						|
 | 
						|
{ Convert Internet file characters to DOS }
 | 
						|
{ * maps '|' -> ':' }
 | 
						|
{        '/' -> '\' }
 | 
						|
function NetToDOSPath(const PathStr : string) : string;
 | 
						|
var
 | 
						|
  i : Integer;
 | 
						|
begin
 | 
						|
  Result := PathStr;
 | 
						|
  for i := 1 to Length(Result) do begin
 | 
						|
    case Result[i] of
 | 
						|
      '|': Result[i] := ':';
 | 
						|
      {$IFDEF IP_LAZARUS}
 | 
						|
      '/': Result[i] := DirectorySeparator;
 | 
						|
      {$ELSE}
 | 
						|
      '/': Result[i] := '\';
 | 
						|
      {$ENDIF}
 | 
						|
    else
 | 
						|
      { leave it alone };
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
 | 
						|
  if (CharPos('\', Result) = 1) and (CharPos(':', Result) > 0) then
 | 
						|
    Result := Copy(Result, 2, Length(Result) - 1);
 | 
						|
end;
 | 
						|
 | 
						|
function DOSToNetPath(const PathStr : string) : string;
 | 
						|
{ Convert DOS file characters to Internet }
 | 
						|
{ * maps ':' -> '|' }
 | 
						|
{       '\' -> '/'  }
 | 
						|
var
 | 
						|
  i : Integer;
 | 
						|
begin
 | 
						|
  Result := PathStr;
 | 
						|
  for i := 1 to Length(Result) do begin
 | 
						|
    case Result[i] of
 | 
						|
      ':': Result[i] := '|';
 | 
						|
      {$IFDEF IP_LAZARUS}
 | 
						|
      DirectorySeparator: Result[i] := '/';
 | 
						|
      {$ELSE}
 | 
						|
      '\': Result[i] := '/';
 | 
						|
      {$ENDIF}
 | 
						|
    else
 | 
						|
      { leave it alone };
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
function IpParseURL(const URL : string; var Rslt : TIpAddrRec) : Boolean;
 | 
						|
{ Splits URL into components }
 | 
						|
 | 
						|
{!!.03 -- rewritten
 | 
						|
  - Parsing UserName and Password fields out of Mailto: urls of the form:
 | 
						|
      mailto:user:pass@myserver.net
 | 
						|
  - Username and Password fields added to TIpAddrRec in support of
 | 
						|
    additional IpParseUrl capabilities
 | 
						|
  - Handling URL Fragments and Queries on local files
 | 
						|
  - Improved recognition of relative paths
 | 
						|
  - Improved recognition of "LocalHost" style Authorities
 | 
						|
}
 | 
						|
 | 
						|
{
 | 
						|
Algorithm:
 | 
						|
1. Leading spaces ignored
 | 
						|
2. Start of string:
 | 
						|
   - Any starting alphabetic character is accumulated into a "Potential
 | 
						|
     Authority" (PA) string
 | 
						|
   - If the first character is a digit URL is assumed to be starting with a
 | 
						|
     numeric format IP address
 | 
						|
   - If the first character is a period ('.') or a slash ('/', '\') the URL is
 | 
						|
     considered to be a relative path
 | 
						|
4. If a PA has been started:
 | 
						|
   - alphanumeric characters are accumulated into the PA
 | 
						|
   - if a ':' or '|' are encountered and there is only one character in
 | 
						|
     the preceding PA, the PA is assumed to be a drive letter for a local
 | 
						|
     file and the rest of the URL is handled accordingly
 | 
						|
   - if there is more than one character in the PA when the ':' is encountered,
 | 
						|
     and if the PA contains at least one period ('.') it is assumed to be an
 | 
						|
     authority, otherwise it is assumed to be a scheme (e.g. HTTP), the ':' is
 | 
						|
     assumed to be delimiting between an authority and a port ID and the PA
 | 
						|
     string is handled accordingly
 | 
						|
   - if a '.' is encountered prior to seeing a '/' then the PA is assumed to be
 | 
						|
     an authority.
 | 
						|
   - if a '/' is encountered, the PA is assumed to be an authority
 | 
						|
   - if a '@' is encountered the present PA is assumed to be a username, and
 | 
						|
     PA accumulation is re-started
 | 
						|
   - any other non-specified character is assumed to indicate an Authority
 | 
						|
5. If a character indicating the end of the PA has been encountered:
 | 
						|
   - if numeric characters are seen after a ':' these are assumed to be a port ID
 | 
						|
   - if alphabetic characters are seen they are assumed to be part of a password
 | 
						|
   - if a slash is encountered the PA is assumed to be a scheme
 | 
						|
   - if an '@' or ':' is encountered the PA is assumed to be a UserName.
 | 
						|
       On '@' the assumption is the Authority is starting.
 | 
						|
       On ':' the assumption is a password is starting.
 | 
						|
6. Slashes following a scheme:
 | 
						|
   - all forward slashes (if any) following a scheme are ignored
 | 
						|
   - if a '.' or '\' is found immediately after the scheme slashes, it's assumed
 | 
						|
     to indicate the start of a local relative path
 | 
						|
7. Password accumulation:
 | 
						|
   - non-'@' characters are considered part of the password
 | 
						|
   - if an '@' is encountered it's considered the start of the authority and
 | 
						|
     actual authority accumulatino is started
 | 
						|
8. Authority Accumulation:
 | 
						|
   - characters in the set ['A'..'Z', 'a'..'z', '0'..'9', '.', '-', '_'] are
 | 
						|
     accumulated into the authority
 | 
						|
   - a ':' is assumed to indicate the end of the authority and the start of a
 | 
						|
     Port ID
 | 
						|
   - a '/' is assumed to indicate the end of the authority and the start of a
 | 
						|
     path
 | 
						|
   - a space (' ') is assumed to indicate trailing spaces on the URL
 | 
						|
9. Port has started:
 | 
						|
   - numeric characters are accumulated into the Port
 | 
						|
   - a '/' is assumed to indicate the start of a path
 | 
						|
   - a ' ' is assumed to indicate trailing spaces on the url
 | 
						|
10. Path has started:
 | 
						|
   - characters not in the set ['#', '?', '&'] are accumulated into the Path
 | 
						|
   - a '#' is assumed to indicate the start of a Fragment
 | 
						|
   - a '?' or '&' is assumed to indicate the start of a Query
 | 
						|
11. Fragment has started:
 | 
						|
   - characters not in the set ['?', '&', ' '] are accumulated into the Fragment
 | 
						|
   - a '?' or '&' is assumed to indicate the start of a Query
 | 
						|
   - a ' ' is assumed to indicate trailing spaces on the url
 | 
						|
12. Query has started:
 | 
						|
   - non space characters are accumulated into the Fragment
 | 
						|
   - a ' ' is assumed to indicate trailing spaces on the url
 | 
						|
13. Trailing spaces
 | 
						|
   - ignored
 | 
						|
}
 | 
						|
 | 
						|
type
 | 
						|
  TUrlParseState = (
 | 
						|
    psStart, psError, psStartSp, psPotAuth, psEoPotAuth, psSchemeSlashes,
 | 
						|
    psLocalPath, psAuthority, psUserName, psPassword, psPort, psPath,
 | 
						|
    psFragment, psQuery, psEndSp
 | 
						|
  );
 | 
						|
const
 | 
						|
  UrlStops : set of TUrlParseState = [psPath, psLocalPath, psAuthority, psPort,
 | 
						|
    psFragment, psQuery, psEndSp];
 | 
						|
 | 
						|
var
 | 
						|
  P : PChar;
 | 
						|
  i : Integer;
 | 
						|
  State : TUrlParseState;
 | 
						|
  PotAuth, PotPath : string;
 | 
						|
  SchemeSeen: Boolean;
 | 
						|
  {$IFDEF IP_LAZARUS}
 | 
						|
  SlashCount: integer;
 | 
						|
  {$ENDIF}
 | 
						|
 | 
						|
procedure ProcessChar;
 | 
						|
begin
 | 
						|
  case State of
 | 
						|
    psStart: begin
 | 
						|
      case P^ of
 | 
						|
        ' ': begin
 | 
						|
          State := psStartSp;
 | 
						|
        end;
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z': begin
 | 
						|
          PotAuth := PotAuth + P^;
 | 
						|
          State := psPotAuth;
 | 
						|
        end;
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          Rslt.Authority := Rslt.Authority + P^;
 | 
						|
          State := psAuthority;
 | 
						|
        end;
 | 
						|
 | 
						|
        '.', '/', '\' : begin
 | 
						|
          PotPath := PotPath + P^;
 | 
						|
          State := psPath;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := psError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    psStartSp: begin
 | 
						|
      case P^ of
 | 
						|
        ' ': { ignore };
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z', '-', '_': begin
 | 
						|
          PotAuth := PotAuth + P^;
 | 
						|
          State := psPotAuth;
 | 
						|
        end;
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          Rslt.Authority := Rslt.Authority + P^;
 | 
						|
          State := psAuthority;
 | 
						|
        end;
 | 
						|
 | 
						|
        '.', '/', '\' : begin
 | 
						|
          PotPath := PotPath + P^;
 | 
						|
          State := psPath;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := psError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    psPotAuth: begin
 | 
						|
      case P^ of
 | 
						|
        'A'..'Z', 'a'..'z', '0'..'9', '.', '-', '_': begin
 | 
						|
          PotAuth := PotAuth + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        ':', '|': begin
 | 
						|
          if Length(PotAuth) = 1 then begin
 | 
						|
            PotPath := PotAuth + P^;
 | 
						|
            PotAuth := '';
 | 
						|
            State := psLocalPath;
 | 
						|
          end
 | 
						|
          else begin
 | 
						|
 | 
						|
            if Pos('.', PotAuth) > 0 then begin
 | 
						|
              Rslt.Authority := PotAuth;
 | 
						|
              State := psPort;
 | 
						|
            end
 | 
						|
            else
 | 
						|
            if (Rslt.Scheme = '') then begin
 | 
						|
              Rslt.Scheme := PotAuth;
 | 
						|
              SchemeSeen := True;
 | 
						|
              PotAuth := '';
 | 
						|
              State := psSchemeSlashes;
 | 
						|
              {$IFDEF IP_LAZARUS}
 | 
						|
              SlashCount := 0;
 | 
						|
              {$ENDIF}
 | 
						|
            end
 | 
						|
            else begin
 | 
						|
 | 
						|
              State := psEoPotAuth;
 | 
						|
            end;
 | 
						|
          end;
 | 
						|
        end;
 | 
						|
 | 
						|
 | 
						|
        '/', '\': begin
 | 
						|
          if SchemeSeen then
 | 
						|
            Rslt.Authority := PotAuth
 | 
						|
          else begin
 | 
						|
            if Pos('.', PotAuth) > 0 then                              {!!.12}
 | 
						|
              Rslt.Authority := PotAuth                                {!!.12}
 | 
						|
            else                                                       {!!.12}
 | 
						|
              PotPath := PotAuth;
 | 
						|
          end;
 | 
						|
          PotAuth := '';
 | 
						|
          PotPath := PotPath + P^;
 | 
						|
          State := psPath;
 | 
						|
        end;
 | 
						|
 | 
						|
        '@': begin
 | 
						|
          Rslt.UserName := PotAuth;
 | 
						|
          PotAuth := '';
 | 
						|
          State := psAuthority;
 | 
						|
        end;
 | 
						|
 | 
						|
        else begin
 | 
						|
          Rslt.Authority := PotAuth;
 | 
						|
          PotAuth := '';
 | 
						|
          State := psAuthority;
 | 
						|
        end;
 | 
						|
 | 
						|
      end;
 | 
						|
 | 
						|
    end;
 | 
						|
 | 
						|
    psEoPotAuth: begin
 | 
						|
      case P^ of
 | 
						|
        '0'..'9': begin
 | 
						|
          Rslt.Authority := PotAuth;
 | 
						|
          PotAuth := '';
 | 
						|
          Rslt.Port := Rslt.Port + P^;
 | 
						|
          State := psPort;
 | 
						|
        end;
 | 
						|
 | 
						|
        '/', '\': begin
 | 
						|
          Rslt.Scheme := PotAuth;
 | 
						|
          SchemeSeen := True;
 | 
						|
          PotAuth := '';
 | 
						|
          State := psSchemeSlashes;
 | 
						|
          {$IFDEF IP_LAZARUS}
 | 
						|
          SlashCount := 0;
 | 
						|
          {$ENDIF}
 | 
						|
        end;
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z': begin
 | 
						|
          Rslt.UserName := PotAuth;
 | 
						|
          PotAuth := '';
 | 
						|
          Rslt.Password := Rslt.Password + P^;
 | 
						|
          State := psPassword;
 | 
						|
        end;
 | 
						|
 | 
						|
        '@': begin
 | 
						|
          Rslt.UserName := PotAuth;
 | 
						|
          PotAuth := '';
 | 
						|
          State := psAuthority;
 | 
						|
        end;
 | 
						|
 | 
						|
        ':': begin
 | 
						|
          Rslt.UserName := PotAuth;
 | 
						|
          PotAuth := '';
 | 
						|
          State := psPassword;
 | 
						|
        end;
 | 
						|
 | 
						|
      end;
 | 
						|
 | 
						|
    end;
 | 
						|
 | 
						|
    psSchemeSlashes: begin
 | 
						|
      {$IFDEF IP_LAZARUS}
 | 
						|
      inc(SlashCount);
 | 
						|
      if (p^ <> '/') or (SlashCount > 2) then
 | 
						|
      {$ENDIF}
 | 
						|
      case P^ of
 | 
						|
        {$IFNDEF IP_LAZARUS}
 | 
						|
        '/': { ignore };
 | 
						|
        {$ENDIF}
 | 
						|
        '.', '\'{$IFDEF IP_LAZARUS},'/'{$ENDIF}: begin { start of a local path }                      {!!.12}  //JMN
 | 
						|
          PotPath := PotPath + P^;                                     {!!.12}
 | 
						|
          State := psLocalPath;                                        {!!.12}
 | 
						|
        end;                                                           {!!.12}
 | 
						|
 | 
						|
        else begin
 | 
						|
          if CharPos('@', URL) > 0 then begin
 | 
						|
            PotAuth := P^;
 | 
						|
            State := psUserName;
 | 
						|
          end
 | 
						|
          else begin
 | 
						|
            PotAuth := P^;
 | 
						|
            State := psPotAuth;
 | 
						|
          end;
 | 
						|
        end;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
 | 
						|
    psLocalPath: begin
 | 
						|
      case P^ of
 | 
						|
        '#': begin
 | 
						|
          if PotPath <> '' then
 | 
						|
            Rslt.Path := AllTrimSpaces(PotPath);
 | 
						|
          State := psFragment;
 | 
						|
        end;
 | 
						|
 | 
						|
        '?', '&': begin
 | 
						|
          if PotPath <> '' then
 | 
						|
            Rslt.Path := AllTrimSpaces(PotPath);
 | 
						|
          Rslt.QueryDelim := P^;
 | 
						|
          State := psQuery;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          PotPath := PotPath + P^;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    psAuthority: begin
 | 
						|
      case P^ of
 | 
						|
        'A'..'Z', 'a'..'z', '0'..'9', '.', '-', '_' : begin
 | 
						|
          Rslt.Authority := Rslt.Authority + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        ':': begin
 | 
						|
          State := psPort;
 | 
						|
        end;
 | 
						|
 | 
						|
        ' ': begin
 | 
						|
          State := psEndSp;
 | 
						|
        end;
 | 
						|
 | 
						|
        '/', '\': begin
 | 
						|
          PotPath := PotPath + P^;
 | 
						|
          State := psPath;
 | 
						|
        end;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    psUserName: begin
 | 
						|
      case P^ of
 | 
						|
        '@': begin
 | 
						|
          Rslt.UserName := PotAuth;
 | 
						|
          PotAuth := '';
 | 
						|
          State := psAuthority;
 | 
						|
        end;
 | 
						|
 | 
						|
        ':', '|': begin
 | 
						|
          if Length(PotAuth) = 1 then begin
 | 
						|
            PotPath := PotAuth + P^;
 | 
						|
            PotAuth := '';
 | 
						|
            State := psLocalPath;
 | 
						|
          end
 | 
						|
          else begin
 | 
						|
            Rslt.UserName := PotAuth;
 | 
						|
            PotAuth := '';
 | 
						|
            State := psPassword;
 | 
						|
          end;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          PotAuth := PotAuth + P^;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    psPassword: begin
 | 
						|
      case P^ of
 | 
						|
        '@': begin
 | 
						|
          State := psAuthority;
 | 
						|
        end;
 | 
						|
 | 
						|
        else begin                                                     {!!.12}
 | 
						|
          Rslt.Password := Rslt.Password + P^;                         {!!.12}
 | 
						|
        end;                                                           {!!.12}
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    psPort: begin
 | 
						|
      case P^ of
 | 
						|
        '0'..'9': begin
 | 
						|
          Rslt.Port := Rslt.Port + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        '/', '\': begin
 | 
						|
          PotPath := PotPath + P^;
 | 
						|
          State := psPath;
 | 
						|
        end;
 | 
						|
 | 
						|
        ' ': begin
 | 
						|
          State := psEndSp;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := psError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    psPath: begin
 | 
						|
      case P^ of
 | 
						|
        '#': begin
 | 
						|
          if PotPath <> '' then begin
 | 
						|
            Rslt.Path := AllTrimSpaces(PotPath);
 | 
						|
            PotPath := '';
 | 
						|
          end;
 | 
						|
          State := psFragment;
 | 
						|
        end;
 | 
						|
 | 
						|
        '?', '&' : begin
 | 
						|
          if PotPath <> '' then begin
 | 
						|
            Rslt.Path := AllTrimSpaces(PotPath);
 | 
						|
            PotPath := '';
 | 
						|
          end;
 | 
						|
          Rslt.QueryDelim := P^;
 | 
						|
          State := psQuery;
 | 
						|
        end;
 | 
						|
 | 
						|
        ' ': begin
 | 
						|
          State := psEndSp;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          PotPath := PotPath + P^;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
{ Extract "Fragment" (in-page reference) portion of URL }
 | 
						|
 | 
						|
{ - If URL contains an Entity name then Fragment should be delimited by a '#' }
 | 
						|
{ - If URL does not contain Entity name then Fragment may immediately follow a }
 | 
						|
{  final slash in the URL's "Path" component, but must still be delimited by }
 | 
						|
{  a '#' to indicate that it is a Fragment.  In this case the assumption is }
 | 
						|
{  that the Fragment refers to the current page }
 | 
						|
 | 
						|
    psFragment: begin
 | 
						|
      case P^ of
 | 
						|
        '?', '&': begin
 | 
						|
          if PotPath <> '' then begin
 | 
						|
            Rslt.Path := AllTrimSpaces(PotPath);
 | 
						|
            PotPath := '';
 | 
						|
          end;
 | 
						|
          Rslt.QueryDelim := P^;
 | 
						|
          State := psQuery;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          Rslt.Fragment := Rslt.Fragment + P^;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
{ Extract "Query" portion of URL }
 | 
						|
 | 
						|
{ - If URL contains an Entity name and/or Fragment then Query should }
 | 
						|
{  be delimited by a '?' }
 | 
						|
{ - If URL does not contain Entity name and/or Fragment then Query may or may }
 | 
						|
{  not be delimited by a '?' }
 | 
						|
{ - Individual elements/parameters within the query typically appear in }
 | 
						|
{  <name>=<value> pairs separated by '&' characters }
 | 
						|
{ See also: SplitParams() and FieldFix() routines }
 | 
						|
 | 
						|
    psQuery: begin
 | 
						|
      case P^ of
 | 
						|
        ' ': begin
 | 
						|
          State := psEndSp;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          Rslt.Query := Rslt.Query + P^;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    psEndSp: begin
 | 
						|
      case P^ of
 | 
						|
        ' ' : { ignore };
 | 
						|
 | 
						|
        else
 | 
						|
          State := psError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    psError: begin
 | 
						|
    end;
 | 
						|
  end {case State };
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
begin
 | 
						|
  Rslt.Scheme    := '';
 | 
						|
  Rslt.Authority := '';
 | 
						|
  Rslt.UserName  := '';
 | 
						|
  Rslt.Password  := '';
 | 
						|
  Rslt.Port      := '';
 | 
						|
  Rslt.Path      := '';
 | 
						|
  Rslt.Fragment  := '';
 | 
						|
  Rslt.Query     := '';
 | 
						|
 | 
						|
  P := @URL[1];
 | 
						|
  State := psStart;
 | 
						|
 | 
						|
//  Result := False;
 | 
						|
  PotAuth := '';
 | 
						|
  PotPath := '';
 | 
						|
 | 
						|
  SchemeSeen := False;
 | 
						|
  for i := 1 to Length(URL) do begin
 | 
						|
    ProcessChar;
 | 
						|
    if State = psError then
 | 
						|
      Break;
 | 
						|
    Inc(P);
 | 
						|
  end;
 | 
						|
 | 
						|
  if PotAuth <> '' then
 | 
						|
    Rslt.Authority := PotAuth;
 | 
						|
 | 
						|
 | 
						|
  if Rslt.Path = '' then begin
 | 
						|
    if PotPath <> '' then
 | 
						|
      Rslt.Path := AllTrimSpaces(PotPath)
 | 
						|
    else
 | 
						|
      Rslt.Path := '/';
 | 
						|
  end;
 | 
						|
 | 
						|
  Result := State in UrlStops;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{ Build absolute URL from a starting URL (Old) and a new URL (New) }
 | 
						|
 | 
						|
{ * Old may be empty }
 | 
						|
{ * New may be a full address or a path relative to Old }
 | 
						|
{ * "FILE://" references are converted for Internet (':'=>'|', '\'=>'/') }
 | 
						|
{ * Attempts to handle relative paths containing one or more "../" references }
 | 
						|
{   intelligently, but does no error checking that there are sufficient higher }
 | 
						|
{   levels in Old to account for the number of "../" levels in New }
 | 
						|
{ Change for FPC: renamed Old, New to OldURL, NewURL }
 | 
						|
function BuildURL(const OldURL, NewURL: string): string;
 | 
						|
var
 | 
						|
  OldAddrRec : TIpAddrRec;
 | 
						|
  NewAddrRec : TIpAddrRec;
 | 
						|
  FoundPos : Integer;
 | 
						|
  RelPos : Integer;
 | 
						|
  ParentPos : Integer;
 | 
						|
  Path : string;
 | 
						|
  Scheme : string;                                                     {!!.12}
 | 
						|
  Port : string;                                                       {!!.12}
 | 
						|
begin
 | 
						|
  Result := '';
 | 
						|
  Path := '';
 | 
						|
 | 
						|
  { sanity checks }
 | 
						|
  if (OldURL = '') and (NewURL = '') then begin
 | 
						|
    Result := '';
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  if (OldURL = '') and (NewURL <> '') then begin
 | 
						|
    Result := NewURL;
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  if (OldURL <> '') and (NewURL = '') then begin
 | 
						|
    Result := OldURL;
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  { Main processing }
 | 
						|
  Result := DOSToNetPath(OldURL);
 | 
						|
 | 
						|
  Initialize(OldAddrRec);
 | 
						|
  Initialize(NewAddrRec);
 | 
						|
 | 
						|
  IpParseURL(OldURL, OldAddrRec);
 | 
						|
  IpParseURL(NewURL, NewAddrRec);
 | 
						|
 | 
						|
  if OldAddrRec.Scheme = '' then                                       {!!.12}
 | 
						|
    Scheme := ''                                                       {!!.12}
 | 
						|
  else                                                                 {!!.12}
 | 
						|
    Scheme := OldAddrRec.Scheme + '://';                               {!!.12}
 | 
						|
 | 
						|
  if OldAddrRec.Port = '' then                                         {!!.12}
 | 
						|
    Port := ''                                                         {!!.12}
 | 
						|
  else                                                                 {!!.12}
 | 
						|
    Port := ':' + OldAddrRec.Port;                                     {!!.12}
 | 
						|
 | 
						|
  if UpperCase(NewAddrRec.Scheme) = 'FILE' then begin
 | 
						|
    { New is a local file }
 | 
						|
    Result := NewAddrRec.Scheme + '://' + NewAddrRec.Path;             {!!.14}
 | 
						|
  end else if NewAddrRec.Scheme <> '' then begin
 | 
						|
    { New is a full address in its own right }
 | 
						|
    Result := NewURL;  { so just return that }
 | 
						|
  end else if (NewAddrRec.Scheme = '') and (NewURL[1] = '/') then begin
 | 
						|
    { New is probably a direct path off the Root }
 | 
						|
    Result := Scheme + OldAddrRec.Authority + Port; { build Root }     {!!.12}
 | 
						|
    if (NewURL <> '') and (NewURL[1] <> '/') then
 | 
						|
      Result := Result + '/';
 | 
						|
    Result := Result + NewURL;  { just append }
 | 
						|
  end else if (NewAddrRec.Scheme = '') and (NewURL[1] <> '.') then begin
 | 
						|
    { New is probably a direct path off the current path }
 | 
						|
{Begin !!.14}
 | 
						|
    if UpperCase(OldAddrRec.Scheme) = 'FILE' then begin
 | 
						|
      Path := ExtractFilePath(OldAddrRec.Path);
 | 
						|
      Result := Scheme + Path;
 | 
						|
    end
 | 
						|
    else begin
 | 
						|
      Path := ExtractEntityPath(DosToNetPath(OldAddrRec.Path));
 | 
						|
      if (Path <> '') and (Path[1] = '/') then
 | 
						|
        Path := Copy(Path, 2, Length(Path) - 1);
 | 
						|
      Result := Scheme;                                                {!!.12}
 | 
						|
 | 
						|
      if OldAddrRec.Authority <> '' then
 | 
						|
        Result := Result + OldAddrRec.Authority + Port + '/';
 | 
						|
 | 
						|
      if Path <> '' then
 | 
						|
        Result := Result + AppendSlash(Path);
 | 
						|
    end;
 | 
						|
{End !!.14}
 | 
						|
 | 
						|
    Result := Result + NewURL;
 | 
						|
 | 
						|
    Exit;
 | 
						|
  end else begin
 | 
						|
    { otherwise New should be a relative path of Old }
 | 
						|
    Path := AppendSlash(ExtractEntityPath(DOSToNetPath(OldAddrRec.Path)));
 | 
						|
    FoundPos := PosIdx('../', NewURL, 1);
 | 
						|
    RelPos := FoundPos + 3;
 | 
						|
    ParentPos := RCharPosIdx('/', Path, Length(Path));
 | 
						|
    while (FoundPos > 0) do begin
 | 
						|
      FoundPos := PosIdx('../', NewURL, FoundPos + 3);
 | 
						|
      if FoundPos > 0 then
 | 
						|
        RelPos := FoundPos + 3;
 | 
						|
      ParentPos := RCharPosIdx('/', Path, ParentPos - 1);
 | 
						|
    end;
 | 
						|
 | 
						|
    Path := AppendSlash(Copy(Path, 1, ParentPos));
 | 
						|
    Result := Scheme + OldAddrRec.Authority + Path +                     {!!.12}
 | 
						|
      Copy(NewURL, RelPos, Length(NewURL) - RelPos + 1);
 | 
						|
 | 
						|
    { remove shorthand for current directory if it exists }
 | 
						|
    FoundPos := Pos('/./', Result);
 | 
						|
    if FoundPos > 0 then
 | 
						|
      Delete(Result, FoundPos, 2);
 | 
						|
  end;
 | 
						|
 | 
						|
  Path := OldURL;
 | 
						|
  Finalize(OldAddrRec);
 | 
						|
  Finalize(NewAddrRec);
 | 
						|
end;
 | 
						|
 | 
						|
{ Split Internet formated (ampersand '&' separated) parameters }
 | 
						|
{ from Parms into Dest }
 | 
						|
procedure SplitParams(const Parms : string; Dest : TStrings);
 | 
						|
var
 | 
						|
  P : Integer;
 | 
						|
  Temp : string;
 | 
						|
begin
 | 
						|
  if not Assigned(Dest) then
 | 
						|
    Exit;
 | 
						|
 | 
						|
  Dest.Clear;
 | 
						|
 | 
						|
  Temp := Parms;
 | 
						|
 | 
						|
  P := CharPos('&', Temp);
 | 
						|
  while P > 0 do begin
 | 
						|
    Dest.Add(Copy(Temp, 1, P - 1));
 | 
						|
    Temp := Copy(Temp, P + 1, Length(Temp) - P);
 | 
						|
    P := CharPos('&', Temp);
 | 
						|
  end;
 | 
						|
  Dest.Add(Temp);
 | 
						|
end;
 | 
						|
 | 
						|
{ Divide HTTP response header line into individual fields }
 | 
						|
{ - HTTP response in the form of: }
 | 
						|
{      "HTTP/"<HTTP Version><SP><HTTP Message ID#><SP><HTTP Message String> }
 | 
						|
{   for example, if "HTTP/1.1 200 OK" passed in S, procedure returns }
 | 
						|
{     "1.1" in V }
 | 
						|
{     "200" in MsgID }
 | 
						|
{     "OK"  in Msg }
 | 
						|
procedure SplitHttpResponse(const S: string; var V, MsgID, Msg: string);
 | 
						|
var
 | 
						|
  P: Integer;
 | 
						|
  Temp: string;
 | 
						|
begin
 | 
						|
  Temp := S;
 | 
						|
  P := CharPos(' ', Temp);
 | 
						|
  V := Copy(Temp, 6, P - 6);
 | 
						|
  Temp := Copy(Temp, P + 1, Length(Temp) - P);
 | 
						|
  P := CharPos(' ', Temp);
 | 
						|
  MsgID := Copy(Temp, 1, P - 1);
 | 
						|
  Msg := Copy(Temp, P + 1, Length(Temp) - P);
 | 
						|
end;
 | 
						|
 | 
						|
{ Convert HTTP Header into TStrings parseable by Name=Value mechanism      }
 | 
						|
{ - Basically just converts HTTP header fields of the form <NAME>: <VALUE> }
 | 
						|
{   pairs into <NAME>=<VALUE> pairs.    }
 | 
						|
{ - Also parses HTTP header associating }
 | 
						|
{     Full header ->       "FullHead="  }
 | 
						|
{     HTTP version ->      "Version="   }
 | 
						|
{     HTTP Message ID# ->  "MsgID="     }
 | 
						|
{     HTTP Message Text -> "Message="   }
 | 
						|
procedure FieldFix(Fields : TStrings);
 | 
						|
var
 | 
						|
  i, P : Integer;
 | 
						|
  S, Ver, ID, Msg : string;
 | 
						|
begin
 | 
						|
  if Fields.Count > 0 then begin
 | 
						|
    S := Fields[0];
 | 
						|
    Fields.Delete(0);
 | 
						|
 | 
						|
    SplitHttpResponse(S, Ver, ID, Msg);
 | 
						|
    Fields.Insert(0, 'Message=' + Msg);
 | 
						|
    Fields.Insert(0, 'MsgID=' + ID);
 | 
						|
    Fields.Insert(0, 'Version=' + Ver);
 | 
						|
    Fields.Insert(0, 'FullHead=' + S);
 | 
						|
 | 
						|
 | 
						|
    for i := 4 to Pred(Fields.Count) do begin
 | 
						|
      P := CharPos(':', Fields[i]);
 | 
						|
      if P > 0 then begin
 | 
						|
        S := Fields[i];
 | 
						|
        Delete(S, P, 1);
 | 
						|
        Insert('=', S, P);
 | 
						|
        Fields.Delete(i);
 | 
						|
        Fields.Insert(i,S);
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
{ Append slash to Internet path if needed }
 | 
						|
function AppendSlash(APath : string) : string;
 | 
						|
begin
 | 
						|
  Result := APath;
 | 
						|
  if (Result <> '') and (Result[Length(APath)] <> '/') then
 | 
						|
    Result := Result + '/';
 | 
						|
end;
 | 
						|
 | 
						|
{ Drop trailing slash from Internet path if needed }
 | 
						|
function RemoveSlash(APath : string) : string;
 | 
						|
begin
 | 
						|
  Result := APath;
 | 
						|
  if Result[Length(Result)] = '/' then
 | 
						|
    Delete(Result, Length(Result), 1);
 | 
						|
end;
 | 
						|
 | 
						|
{ Extract Entity (Filename) portion of Internet Path }
 | 
						|
{ Parallel to SysUtils.ExtractFileName for Internet Paths }
 | 
						|
function ExtractEntityName(const NamePath : string) : string;
 | 
						|
var
 | 
						|
  P : Integer;
 | 
						|
  Temp : string;
 | 
						|
begin
 | 
						|
  Result := '';
 | 
						|
  P := RCharPos('/', NamePath);
 | 
						|
  if P > 0 then begin
 | 
						|
    Temp := Copy(NamePath, P + 1, Length(NamePath) - P);
 | 
						|
 | 
						|
    if CharPos('.', Temp) > 0 then
 | 
						|
      Result := Temp
 | 
						|
    else
 | 
						|
      Result := '';
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
{ Extract Path (non-filename) portion of Internet Path }
 | 
						|
{ Parallel to SysUtils.ExtractFilePath for Internet Paths }
 | 
						|
function ExtractEntityPath(const NamePath: string): string;
 | 
						|
var
 | 
						|
  P : Integer;
 | 
						|
begin
 | 
						|
  P := RCharPos('/', NamePath);
 | 
						|
  if P = Length(NamePath) then { no file name on Path }
 | 
						|
    Result := NamePath
 | 
						|
  else
 | 
						|
    Result := Copy(NamePath, 1, P);
 | 
						|
end;
 | 
						|
 | 
						|
{ Return next highest level in Internet path }
 | 
						|
{ e.g. if Path parameter contains "/default/pub/pics/jpgs" }
 | 
						|
{ function would return "/default/pub/pics" }
 | 
						|
function GetParentPath(const Path : string) : string;
 | 
						|
var
 | 
						|
  P : Integer;
 | 
						|
begin
 | 
						|
  if Path = '/' then begin
 | 
						|
    Result := Path;
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
  P := Length(Path);
 | 
						|
  if Path[P] = '/' then
 | 
						|
    Dec(P);
 | 
						|
  while Path[P] <> '/' do
 | 
						|
    Dec(P);
 | 
						|
  Result := Copy(Path, 1, P);
 | 
						|
end;
 | 
						|
 | 
						|
{ date stuff }
 | 
						|
const
 | 
						|
  EpochYear = 70;  { UNIX Julian time count starts in 1970 }
 | 
						|
  EpochLowStr = '19';
 | 
						|
  EpochHiStr  = '20';
 | 
						|
  CanonicalDate = '"%s", dd "%s" yyyy hh:mm:ss "%s00"';
 | 
						|
 | 
						|
{
 | 
						|
Note: The following strings and string arrays are used for
 | 
						|
interpreting/building canonical Internet dates and should
 | 
						|
NOT be internationalized!
 | 
						|
}
 | 
						|
 | 
						|
  DayString : string =
 | 
						|
    'SUNDAY   ' +
 | 
						|
    'MONDAY   ' +
 | 
						|
    'TUESDAY  ' +
 | 
						|
    'WEDNESDAY' +
 | 
						|
    'THURSDAY ' +
 | 
						|
    'FRIDAY   ' +
 | 
						|
    'SATURDAY ';
 | 
						|
 | 
						|
  MonthString : string =
 | 
						|
    'JANUARY  ' +
 | 
						|
    'FEBRUARY ' +
 | 
						|
    'MARCH    ' +
 | 
						|
    'APRIL    ' +
 | 
						|
    'MAY      ' +
 | 
						|
    'JUNE     ' +
 | 
						|
    'JULY     ' +
 | 
						|
    'AUGUST   ' +
 | 
						|
    'SEPTEMBER' +
 | 
						|
    'OCTOBER  ' +
 | 
						|
    'NOVEMBER ' +
 | 
						|
    'DECEMBER ';
 | 
						|
 | 
						|
  IpMonthsStrings: array[1..12] of string = (
 | 
						|
    'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
 | 
						|
    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
 | 
						|
  IpDOWStrings: array[1..7] of string = (
 | 
						|
    'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
 | 
						|
 | 
						|
type
 | 
						|
  THttpDateType = (hdtUnknown, hdtRFC822, hdtRFC850, hdtANSIC);
 | 
						|
 | 
						|
{ Returns numeric month index [1..12] from any unique string }
 | 
						|
{ abbreviation of English month name, returns 0 if no match }
 | 
						|
function MonStrToInt(MonStr : string) : Integer;
 | 
						|
var
 | 
						|
  P : Integer;
 | 
						|
begin
 | 
						|
  P := Pos(UpperCase(MonStr), MonthString);
 | 
						|
  if P > 0 then
 | 
						|
    Result := (P div 9) + 1
 | 
						|
  else
 | 
						|
    Result := 0;
 | 
						|
end;
 | 
						|
 | 
						|
{ For two digit year string passed in returns }
 | 
						|
{ four digit year string based on EpochYear constant. }
 | 
						|
{ If converting YrStr to an integer yields > 99 then }
 | 
						|
{ YrStr is returned unchanged }
 | 
						|
function EpochStr(YrStr: string) : string;
 | 
						|
var
 | 
						|
  Yr: Word;
 | 
						|
begin
 | 
						|
  Yr := StrToInt(YrStr);
 | 
						|
  if (Yr > 99) then begin { not a 2 digit year }
 | 
						|
    Result := YrStr;
 | 
						|
    Exit;
 | 
						|
  end;
 | 
						|
 | 
						|
  if (Yr < EpochYear) then begin
 | 
						|
    Result :=  EpochHiStr + YrStr;
 | 
						|
  end
 | 
						|
  else begin
 | 
						|
    Result := EpochLowStr + YrStr;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{
 | 
						|
Convert an Internet Date string to a TDateTime
 | 
						|
 | 
						|
If the string isn't in one of the canonical formats (see below)
 | 
						|
the Internet start date of Jan 1, 1970 0:0:0:0 is returned
 | 
						|
 | 
						|
Canonical Internet header date strings are in one of three standard formats:
 | 
						|
  Sun, 06 Nov 1994 08:49:37 GMT   ; RFC 822, updated by RFC 1123
 | 
						|
  Sunday, 06-Nov-94 08:49:37 GMT  ; RFC 850, obsoleted by RFC 1036
 | 
						|
  Sun Nov  6 08:49:37 1994        ; ANSI C's asctime() format
 | 
						|
}
 | 
						|
 | 
						|
{!!.03 -- rewritten to handle common variants such as the Day or Month name
 | 
						|
to be fully spelled out where they are not in the canonical form, or to have
 | 
						|
a 4 digit year in the RFC 850 style
 | 
						|
}
 | 
						|
function INetDateStrToDateTime(const DateStr: string): TDateTime;
 | 
						|
type
 | 
						|
  TINetDateState = (idStart, idStartSp, idDow, idDowSp, idDay1, idDay1Sp,
 | 
						|
  idMon1, idMon1Sp, idMon2, idMon2Sp, idDay2, idYr1,
 | 
						|
  idPreTimeSp, idHrs, idMin, idSec, idPostTimeSp, {idGMT,} idYr2,        {!!.12}
 | 
						|
  idEndSp, idAM, idPM, idDaySpace1, IdTimeZoneNum, IdTimeZoneAlpha,      {!!.12}
 | 
						|
  idError);
 | 
						|
 | 
						|
const
 | 
						|
  AcceptStates: set of TINetDateState = [{idGMT,} idYr2, idSec,          {!!.12}
 | 
						|
                                         idPostTimeSp, idEndSp,          {!!.12}
 | 
						|
                                         idTimeZoneAlpha,                {!!.12}
 | 
						|
                                         idTimeZoneNum];                 {!!.12}
 | 
						|
var
 | 
						|
  Dow, Day, Mon, Year, Hrs, Min, Sec: string;
 | 
						|
  Dy, Mo, Yr: Word;
 | 
						|
  Hr, Mn, Sc: SmallInt;
 | 
						|
  State: TINetDateState;
 | 
						|
  P: PChar;
 | 
						|
  i : Integer;
 | 
						|
  AMPM : Boolean;
 | 
						|
  PM : Boolean;
 | 
						|
  TimeZone : string;                                                     {!!.12}
 | 
						|
 | 
						|
procedure ParseDate;
 | 
						|
begin
 | 
						|
  case State of
 | 
						|
    idStart: begin
 | 
						|
      case P^ of
 | 
						|
        ' ' : State := idStartSp;
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z' : begin
 | 
						|
          State := idDow;
 | 
						|
          Dow := Dow + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        '0'..'9': begin                                                  {!!.12}
 | 
						|
          State := idDay1;                                               {!!.12}
 | 
						|
          Day := Day + P^;                                               {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idStartSp: begin  { ignore initial spaces }
 | 
						|
      case P^ of
 | 
						|
        ' ': { ignore };
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z' : begin
 | 
						|
          State := idDow;
 | 
						|
          Dow := Dow + P^;
 | 
						|
        end;
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idDow: begin  { all formats start with a DOW string }
 | 
						|
      case P^ of
 | 
						|
        'A'..'Z', 'a'..'z' : begin
 | 
						|
          Dow := Dow + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        ',', ' ': begin
 | 
						|
          State := idDowSp;
 | 
						|
        end;
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idDowSp: begin  { ignore spaces following DOW }
 | 
						|
      case P^ of
 | 
						|
        ' ': { ignore };
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          State := idDay1;
 | 
						|
          Day := Day + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z' : begin
 | 
						|
          State := idMon1;
 | 
						|
          Mon := Mon + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idDay1: begin  { RFC 822 and 850 formats start with day digit }
 | 
						|
      case P^ of
 | 
						|
        ' ': begin
 | 
						|
          State := idDay1Sp;
 | 
						|
        end;
 | 
						|
 | 
						|
        '-': begin
 | 
						|
          State := idMon2;
 | 
						|
        end;
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          Day := Day + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idDay1Sp: begin { ignore spaces following day digit }
 | 
						|
      case P^ of
 | 
						|
        ' ': { ignore };
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z' : begin
 | 
						|
          State := idMon2;
 | 
						|
          Mon := Mon + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idMon1: begin  { ANSI C format starts with month string }
 | 
						|
      case P^ of
 | 
						|
        ' ': begin
 | 
						|
          State := idMon1Sp;
 | 
						|
        end;
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z' : begin
 | 
						|
          Mon := Mon + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idMon1Sp: begin { ignore spaces after ANSI C month string }
 | 
						|
      case P^ of
 | 
						|
        ' ': { ignore };
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          State := idDay2;
 | 
						|
          Day := Day + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idMon2: begin  { RFC 822 and 850 month string }
 | 
						|
      case P^ of
 | 
						|
        ' ' : begin
 | 
						|
          State := idMon2Sp;
 | 
						|
        end;
 | 
						|
 | 
						|
        '-' : begin
 | 
						|
          State := idYr1;
 | 
						|
        end;
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z' : begin
 | 
						|
          Mon := Mon + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idMon2Sp: begin   {ignore spaces after month string }
 | 
						|
      case P^ of
 | 
						|
        ' ': { ignore };
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          State := idYr1;
 | 
						|
          Year := Year + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idDay2: begin   { ANSI C format Day string }
 | 
						|
      case P^ of
 | 
						|
        '0'..'9': begin
 | 
						|
          Day := Day + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        ',' : begin                                                      {!!.12}
 | 
						|
          State := idDaySpace1;                                          {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        ' ': begin
 | 
						|
          State := idPreTimeSp;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idDaySpace1 : begin                                                  {!!.12}
 | 
						|
      case P^ of                                                         {!!.12}
 | 
						|
        ' ' : begin                                                      {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        '0'..'9' : begin                                                 {!!.12}
 | 
						|
          Year := Year + P^;                                             {!!.12}
 | 
						|
          State := idYr1;                                                {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        else                                                             {!!.12}
 | 
						|
          State := idError;                                              {!!.12}
 | 
						|
      end;                                                               {!!.12}
 | 
						|
    end;                                                                 {!!.12}
 | 
						|
 | 
						|
    idYr1: begin    { RFC 822 and 850 year string }
 | 
						|
      case P^ of
 | 
						|
        '0'..'9': begin
 | 
						|
          Year := Year + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        ' ': begin
 | 
						|
          State := idPreTimeSp;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idPreTimeSp: begin  { ignore spaces before start of time string }
 | 
						|
      case P^ of
 | 
						|
        ' ': { ignore };
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          State := idHrs;
 | 
						|
          Hrs := Hrs + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idHrs: begin  { hours string }
 | 
						|
      case P^ of
 | 
						|
        ':': begin
 | 
						|
          State := idMin;
 | 
						|
        end;
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          Hrs := Hrs + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idMin: begin { minutes string }
 | 
						|
      case P^ of
 | 
						|
        ':': begin
 | 
						|
          State := idSec;
 | 
						|
        end;
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          Min := Min + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        ' ' : begin                                                      {!!.12}
 | 
						|
          State := idPostTimeSp;                                         {!!.12}
 | 
						|
          Sec := '00';
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idSec: begin { seconds string }
 | 
						|
      case P^ of
 | 
						|
        ' ': begin
 | 
						|
          State := idPostTimeSp;
 | 
						|
        end;
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          Sec := Sec + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        'A', 'a' : begin                                                 {!!.12}
 | 
						|
          AMPM := True;                                                  {!!.12}
 | 
						|
          PM := False;                                                   {!!.12}
 | 
						|
          State := idAM;                                                 {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        'P', 'p' : begin                                                 {!!.12}
 | 
						|
          AMPM := True;                                                  {!!.12}
 | 
						|
          PM := True;                                                    {!!.12} 
 | 
						|
          State := idPM;                                                 {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idAM : begin { AM string }                                           {!!.12}
 | 
						|
      case P^ of                                                         {!!.12}
 | 
						|
        ' ' : begin                                                      {!!.12}
 | 
						|
          State := idPostTimeSp                                          {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        'M', 'm' : begin                                                 {!!.12}
 | 
						|
          State := idPostTimeSp;                                         {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        else                                                             {!!.12}
 | 
						|
          State := idError;                                              {!!.12}
 | 
						|
      end;                                                               {!!.12}
 | 
						|
    end;                                                                 {!!.12}
 | 
						|
 | 
						|
    idPM : begin { PM string }                                           {!!.12}
 | 
						|
      case P^ of                                                         {!!.12}
 | 
						|
        ' ' : begin                                                      {!!.12}
 | 
						|
          State := idPostTimeSp                                          {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        'M', 'm' : begin                                                 {!!.12}
 | 
						|
          State := idPostTimeSp;                                         {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        else                                                             {!!.12}
 | 
						|
          State := idError;                                              {!!.12}
 | 
						|
      end;                                                               {!!.12}
 | 
						|
    end;                                                                 {!!.12}
 | 
						|
 | 
						|
    idPostTimeSp: begin   { ignore spaces before after time string }
 | 
						|
      case P^ of
 | 
						|
        ' ': { ignore };
 | 
						|
 | 
						|
        '0'..'9': begin
 | 
						|
          State := idYr2;
 | 
						|
          Year := Year + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        {'G', 'g': begin                                               } {!!.12}
 | 
						|
        {  State := idGMT;                                             } {!!.12}
 | 
						|
        {end;                                                          } {!!.12}
 | 
						|
 | 
						|
        '-' : begin                                                      {!!.12}
 | 
						|
          TimeZone := TimeZone + P^;                                     {!!.12}
 | 
						|
          State := IdTimeZoneNum;                                        {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        '+' : begin                                                      {!!.12}
 | 
						|
          TimeZone := TimeZone + P^;                                     {!!.12}
 | 
						|
          State := IdTimeZoneNum;                                        {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        'A'..'Z', 'a'..'z' : begin                                       {!!.12}
 | 
						|
          TimeZone := TimeZone + P^;                                     {!!.12}
 | 
						|
          State := IdTimeZoneAlpha;                                      {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idTimeZoneNum : begin                                                {!!.12}
 | 
						|
      case P^ of                                                         {!!.12}
 | 
						|
        '0'..'9' : begin                                                 {!!.12}
 | 
						|
          TimeZone := TimeZone + P^;                                     {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        ' ' : begin                                                      {!!.12}
 | 
						|
          State := idEndSp;                                              {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        else                                                             {!!.12}
 | 
						|
          State := idError;                                              {!!.12}
 | 
						|
      end;                                                               {!!.12}
 | 
						|
    end;                                                                 {!!.12}
 | 
						|
 | 
						|
    idTimeZoneAlpha : begin                                              {!!.12}
 | 
						|
      case P^ of                                                         {!!.12}
 | 
						|
        'A'..'Z', 'a'..'z' : begin                                       {!!.12}
 | 
						|
          TimeZone := TimeZone + P^;                                     {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        ' ' : begin                                                      {!!.12}
 | 
						|
          if UpperCase (TimeZone) = 'AM' then begin                      {!!.12}
 | 
						|
            AMPM := True;                                                {!!.12}
 | 
						|
            PM := False;                                                 {!!.12}
 | 
						|
            State := IdTimeZoneAlpha;                                    {!!.12}
 | 
						|
            TimeZone := '';                                              {!!.12}
 | 
						|
          end else if UpperCase (TimeZone) = 'PM' then begin             {!!.12}
 | 
						|
            AMPM := True;                                                {!!.12}
 | 
						|
            PM := True;                                                  {!!.12}
 | 
						|
            State := IdTimeZoneAlpha;                                    {!!.12}
 | 
						|
            TimeZone := '';                                              {!!.12}
 | 
						|
          end else                                                       {!!.12}
 | 
						|
            State := idEndSp;                                            {!!.12}
 | 
						|
        end;                                                             {!!.12}
 | 
						|
 | 
						|
        else                                                             {!!.12}
 | 
						|
          State := idError;                                              {!!.12}
 | 
						|
      end;                                                               {!!.12}
 | 
						|
    end;                                                                 {!!.12}
 | 
						|
 | 
						|
    {idGMT: begin }   { RFC 822 and 850 should end with "GMT" }          {!!.12}
 | 
						|
    {  case P^ of                                                      } {!!.12}
 | 
						|
    {    'M', 'T': begin                                               } {!!.12}
 | 
						|
    {    end;                                                          } {!!.12}
 | 
						|
    {                                                                  } {!!.12}
 | 
						|
    {    ' ': begin                                                    } {!!.12}
 | 
						|
    {      State := idEndSp;                                           } {!!.12}
 | 
						|
    {    end;                                                          } {!!.12}
 | 
						|
    {                                                                  } {!!.12}
 | 
						|
    {    else                                                          } {!!.12}
 | 
						|
    {      State := idError;                                           } {!!.12}
 | 
						|
    {  end;                                                            } {!!.12}
 | 
						|
    {end;                                                              } {!!.12}
 | 
						|
 | 
						|
    idYr2: begin    { ANSI C time ends with Year }
 | 
						|
      case P^ of
 | 
						|
        '0'..'9': begin
 | 
						|
          Year := Year + P^;
 | 
						|
        end;
 | 
						|
 | 
						|
        ' ': begin
 | 
						|
          State := idEndSp;
 | 
						|
        end;
 | 
						|
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idEndSp: begin  { ignore trailing spaces }
 | 
						|
      case P^ of
 | 
						|
        ' ': {ignore};
 | 
						|
        else
 | 
						|
          State := idError;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
 | 
						|
    idError: begin
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
begin
 | 
						|
  Result := EncodeDate(1970, 1, 1);
 | 
						|
  if DateStr = '' then Exit;
 | 
						|
 | 
						|
  { clear parse strings }
 | 
						|
  Dow := '';
 | 
						|
  Day := '';
 | 
						|
  Mon := '';
 | 
						|
  Year := '';
 | 
						|
  Hrs := '';
 | 
						|
  Min := '';
 | 
						|
  Sec := '';
 | 
						|
  AMPM := False;                                                         {!!.12}
 | 
						|
  PM := False;                                                           {!!.12}
 | 
						|
  TimeZone := '';                                                        {!!.12}
 | 
						|
 | 
						|
  { start at first character }
 | 
						|
  P := @DateStr[1];
 | 
						|
 | 
						|
  { iterate characters }
 | 
						|
  for i := 1 to Length(DateStr) do begin
 | 
						|
    ParseDate;
 | 
						|
    if State = idError then
 | 
						|
      Exit { error in date format, give up }
 | 
						|
    else
 | 
						|
      Inc(P);
 | 
						|
  end;
 | 
						|
 | 
						|
  if State = idTimeZoneAlpha then begin                                  {!!.12}
 | 
						|
    if UpperCase (TimeZone) = 'AM' then begin                            {!!.12}
 | 
						|
      AMPM := True;                                                      {!!.12}
 | 
						|
      PM := False;                                                       {!!.12}
 | 
						|
      TimeZone := '';                                                    {!!.12}
 | 
						|
    end else if UpperCase (TimeZone) = 'PM' then begin                   {!!.12}
 | 
						|
      AMPM := True;                                                      {!!.12}
 | 
						|
      PM := True;                                                        {!!.12}
 | 
						|
      TimeZone := '';                                                    {!!.12}
 | 
						|
    end;                                                                 {!!.12}
 | 
						|
  end;                                                                   {!!.12}
 | 
						|
 | 
						|
  if State = idMin then begin                                            {!!.12}
 | 
						|
    Sec := '00';                                                         {!!.12}
 | 
						|
    State := idSec;                                                      {!!.12}
 | 
						|
  end;                                                                   {!!.12}
 | 
						|
 | 
						|
  { date string terminated prematurely }
 | 
						|
  if not (State in AcceptStates) then Exit;
 | 
						|
 | 
						|
  { validate day of week and Month name vs. expected }
 | 
						|
//  if not ((Pos(UpperCase(Dow), DayString)   mod 9) = 1) then Exit; // !!!
 | 
						|
  if not ((Pos(UpperCase(Mon), MonthString) mod 9) = 1) then Exit;
 | 
						|
 | 
						|
  { correct two digit years }
 | 
						|
  Year := EpochStr(Year);
 | 
						|
 | 
						|
  { convert D-M-Y string representations to integers }
 | 
						|
  Dy := StrToIntDef(Day, 0);
 | 
						|
  Mo := MonStrToInt(Mon);
 | 
						|
  Yr := StrToIntDef(Year, 0);
 | 
						|
 | 
						|
  { check for errors or out of range }
 | 
						|
  if (Dy = 0) or (Mo = 0) or (Yr = 0) then Exit;
 | 
						|
  if (Dy > 31) or (Mo > 12) then Exit;
 | 
						|
 | 
						|
  { convert H-M-S string representations to integers }
 | 
						|
  Hr := StrToIntDef(Hrs, -1);
 | 
						|
  Mn := StrToIntDef(Min, -1);
 | 
						|
  Sc := StrToIntDef(Sec, -1);
 | 
						|
 | 
						|
  if AMPM then begin                                                     {!!.12}
 | 
						|
    if (Hr < 12) and (PM) then                                           {!!.12}
 | 
						|
      Hr := Hr + 12;                                                     {!!.12}
 | 
						|
    if (Hr = 12) and (not PM) then                                       {!!.12}
 | 
						|
      Hr := 0;                                                           {!!.12}
 | 
						|
  end;                                                                   {!!.12}
 | 
						|
 | 
						|
  { check for errors or out of range }
 | 
						|
  if (Hr = -1) or (Mn = -1) or (Sc = -1) then Exit;
 | 
						|
  if (Hr > 24) or (Mn > 60) or (Sc > 60) then Exit;
 | 
						|
 | 
						|
  { tests passed, generate final result }
 | 
						|
  Result := EncodeDate(Yr, Mo, Dy) + EncodeTime(Hr, Mn, Sc, 0);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{ increment TDateTime by supplied number of minutes }
 | 
						|
function IncMins(const Date: TDateTime; NumberOfMins: Integer): TDateTime;
 | 
						|
begin
 | 
						|
  Result := Date + NumberOfMins / 1440.0;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{ returns the current local TimeZone "bias" in minutes from UTC (GMT) }
 | 
						|
function TimeZoneBias : Integer;
 | 
						|
{$IFDEF IP_LAZARUS}
 | 
						|
begin
 | 
						|
  Result:=0;
 | 
						|
  writeln('TimeZoneBias ToDo');
 | 
						|
end;
 | 
						|
{$ELSE}
 | 
						|
{$IFDEF VERSION3}
 | 
						|
const
 | 
						|
  TIME_ZONE_ID_UNKNOWN  = 0;
 | 
						|
  TIME_ZONE_ID_STANDARD = 1;
 | 
						|
  TIME_ZONE_ID_DAYLIGHT = 2;
 | 
						|
{$ENDIF}
 | 
						|
var
 | 
						|
  TZI : TTimeZoneInformation;
 | 
						|
begin
 | 
						|
  Result := 0;
 | 
						|
  case GetTimeZoneInformation(TZI) of
 | 
						|
    TIME_ZONE_ID_UNKNOWN :  Result := 0;
 | 
						|
    TIME_ZONE_ID_STANDARD : Result := TZI.Bias + TZI.StandardBias;
 | 
						|
    TIME_ZONE_ID_DAYLIGHT : Result := TZI.Bias + TZI.DaylightBias;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
{$ENDIF}
 | 
						|
 | 
						|
{ Format TDateTime to standard HTTP date string }
 | 
						|
function DateTimeToINetDateTimeStr(DateTime: TDateTime): string;
 | 
						|
var
 | 
						|
  Yr, Mo, Dy: Word;
 | 
						|
  s: String;
 | 
						|
begin
 | 
						|
  DecodeDate(DateTime, Yr, Mo, Dy);
 | 
						|
  s := Format('%g', [Abs(TimeZoneBias/60)]);
 | 
						|
  if Length(s) = 1 then
 | 
						|
    s := '0' + s;
 | 
						|
  if TimeZoneBias < 0 then s := '-' + s;
 | 
						|
 | 
						|
  Result := FormatDateTime(CanonicalDate, DateTime);
 | 
						|
  Result := Format(Result, [IpDOWStringS[DayOfWeek(DateTime)], IpMonthsStrings[Mo], s]);
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
{ File/Directory Stuff }
 | 
						|
 | 
						|
{ Retreive Windows "MIME" type for a particular file extension }
 | 
						|
{$IFDEF IP_LAZARUS}
 | 
						|
{$ifndef MSWindows}
 | 
						|
{define some basic mime types}
 | 
						|
const MimeTypeExt : Array[0..4] of String = ('.htm','.html','.txt','.jpg','.png');
 | 
						|
      MimeTypes   : Array[0..4] of String = ('text/html','text/html','text/plain','image/jpeg','image/png');
 | 
						|
{$endif}
 | 
						|
 | 
						|
function GetLocalContent(const TheFileName: string): string;
 | 
						|
var
 | 
						|
  Reg : TRegistry;
 | 
						|
  Ext : string;
 | 
						|
  {$ifndef MSWindows}
 | 
						|
  ExtU: string;
 | 
						|
  i : integer;
 | 
						|
  {$ENDIF}
 | 
						|
begin
 | 
						|
  Result := '';
 | 
						|
  Ext := ExtractFileExt(TheFileName);
 | 
						|
  {$ifndef MSWindows}
 | 
						|
  ExtU := AnsiLowerCase(Ext);
 | 
						|
  for i := 0 to high(MimeTypeExt) do
 | 
						|
    if MimeTypeExt[i] = ExtU then
 | 
						|
    begin
 | 
						|
      result := MimeTypes[i];
 | 
						|
      break;
 | 
						|
    end;
 | 
						|
  {$endif}
 | 
						|
  if result = '' then
 | 
						|
  begin
 | 
						|
    Reg := nil;
 | 
						|
    try
 | 
						|
      Reg := TRegistry.Create;
 | 
						|
      Reg.RootKey := HKEY_CLASSES_ROOT;
 | 
						|
      if Reg.OpenKeyReadOnly(Ext) then
 | 
						|
        Result := Reg.ReadString('Content Type');
 | 
						|
    finally
 | 
						|
      Reg.CloseKey;
 | 
						|
      Reg.Free;
 | 
						|
    end;
 | 
						|
  end;
 | 
						|
  //DebugLn('IpUtils.GetLocalContent File:'+TheFileName+' Result:'+result);
 | 
						|
end;
 | 
						|
 | 
						|
{$ELSE}
 | 
						|
{ Retreive Windows "MIME" type for a particular file extension }
 | 
						|
function GetLocalContent(const TheFileName: string): string;
 | 
						|
var
 | 
						|
  Reg : TRegistry;
 | 
						|
  Ext : string;
 | 
						|
begin
 | 
						|
  Result := '';
 | 
						|
  Ext := ExtractFileExt(TheFileName);
 | 
						|
 | 
						|
  Reg := nil;
 | 
						|
  try
 | 
						|
    Reg := TRegistry.Create;
 | 
						|
    Reg.RootKey := HKEY_CLASSES_ROOT;
 | 
						|
    if Reg.OpenKey(Ext, True) then
 | 
						|
      Result := Reg.ReadString('Content Type');
 | 
						|
  finally
 | 
						|
    Reg.CloseKey;
 | 
						|
    Reg.Free;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
{$ENDIF}
 | 
						|
 | 
						|
{ Determine if a directory exists }
 | 
						|
function DirExists(Dir : string): Boolean;
 | 
						|
{$IFDEF IP_LAZARUS}
 | 
						|
begin
 | 
						|
  Result:=DirPathExists(Dir);
 | 
						|
end;
 | 
						|
{$ELSE}
 | 
						|
var
 | 
						|
  Attributes : Integer;
 | 
						|
begin
 | 
						|
  Attributes := GetFileAttributes(PAnsiChar(Dir));
 | 
						|
  Result := (Attributes <> -1) and
 | 
						|
    (Attributes and FILE_ATTRIBUTE_DIRECTORY <> 0);
 | 
						|
end;
 | 
						|
{$ENDIF}
 | 
						|
 | 
						|
{Begin !!.12}
 | 
						|
{ Get temporary filename as string }
 | 
						|
function GetTemporaryFile(const Path : string) : string;
 | 
						|
{$IFDEF IP_LAZARUS}
 | 
						|
begin
 | 
						|
  Result:=FileUtil.GetTempFileName(Path,'IP_');
 | 
						|
end;
 | 
						|
{$ELSE}
 | 
						|
var
 | 
						|
  TempFileName : array [0..MAX_PATH] of AnsiChar;
 | 
						|
begin
 | 
						|
  { Create a new uniquely named file in that folder. }
 | 
						|
  GetTempFileName(PAnsiChar(Path), 'IP_', 0, TempFileName);
 | 
						|
  Result := TempFileName;
 | 
						|
end;
 | 
						|
{$ENDIF}
 | 
						|
{End !!.12}
 | 
						|
 | 
						|
{ Get Windows system TEMP path in a string }
 | 
						|
function GetTemporaryPath: string;
 | 
						|
{$IFDEF IP_LAZARUS}
 | 
						|
begin
 | 
						|
  writeln('ToDo: IpUtils.GetTemporaryPath');
 | 
						|
  Result:='';
 | 
						|
end;
 | 
						|
{$ELSE}
 | 
						|
var
 | 
						|
  PathBuf : array [0..MAX_PATH] of char;
 | 
						|
begin
 | 
						|
  GetTempPath(MAX_PATH + 1, PathBuf);
 | 
						|
  Result := StrPas(PathBuf);
 | 
						|
end;
 | 
						|
{$ENDIF}
 | 
						|
 | 
						|
{ Append backslash to DOS path if needed }
 | 
						|
function AppendBackSlash(APath : string) : string;
 | 
						|
begin
 | 
						|
{$IFDEF IP_LAZARUS}
 | 
						|
  Result := AppendPathDelim(APath);
 | 
						|
{$ELSE}
 | 
						|
  Result := APath;
 | 
						|
  if (Result <> '') and (Result[Length(APath)] <> '\') then
 | 
						|
    Result := Result + '\';
 | 
						|
{$ENDIF}
 | 
						|
end;
 | 
						|
 | 
						|
{ Remove trailing backslash from a DOS path if needed }
 | 
						|
function RemoveBackSlash(APath: string) : string;
 | 
						|
begin
 | 
						|
{$IFDEF IP_LAZARUS}
 | 
						|
  Result := ChompPathDelim(APath);
 | 
						|
{$ELSE}
 | 
						|
  Result := APath;
 | 
						|
  if Result[Length(Result)] = '\' then
 | 
						|
    Delete(Result, Length(Result), 1);
 | 
						|
{$ENDIF}
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
{***********************************************}
 | 
						|
 | 
						|
{cookie support}                                                         {!!.02}
 | 
						|
 | 
						|
const
 | 
						|
  CookieDefaults: array [1..5] of string[8] =
 | 
						|
    ('Version=',
 | 
						|
     'Path=',
 | 
						|
     'Domain=',
 | 
						|
     'Max-Age=',
 | 
						|
     'Path=');
 | 
						|
function FixDefaults(const S: string): string;
 | 
						|
var
 | 
						|
  i : Integer;
 | 
						|
begin
 | 
						|
  Result := S;
 | 
						|
  for i := 1 to 5 do
 | 
						|
    if Pos(CookieDefaults[i], S) = 1 then
 | 
						|
      Result := '$' + S;
 | 
						|
end;
 | 
						|
 | 
						|
procedure SplitCookieFields(const Data: string; Fields: TStrings);
 | 
						|
{
 | 
						|
Split Cookie data fields into items in a TStrings instance, Cookie fields will
 | 
						|
be in Name="Value" pairs easily accessed via the associated TStrings properties
 | 
						|
routine automatically prepends '$' to default Cookie fields for response header
 | 
						|
}
 | 
						|
var
 | 
						|
  P1, P2 : Integer;
 | 
						|
  S, Temp : string;
 | 
						|
begin
 | 
						|
  Temp := Data + ';';
 | 
						|
  P1 := 1;
 | 
						|
  P2 := CharPosIdx(';', Temp, P1);
 | 
						|
  while P2 > 0 do begin
 | 
						|
    S := Trim(Copy(Temp, P1, P2 - P1));
 | 
						|
    Fields.Add(FixDefaults(S));
 | 
						|
    P1 := P2 + 1;
 | 
						|
    P2 := CharPosIdx(';', Temp, P1);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
 | 
						|
end.
 |