
{******************************************}
{                                          }
{             FastReport v4.0              }
{         Network utilities unit           }
{                                          }
{         Copyright (c) 2006-2008          }
{         by Alexander Fediachov,          }
{            Fast Reports Inc.             }
{                                          }
{******************************************}

unit frxNetUtils;

{$I frx.inc}

interface

uses
  Windows, Messages, SysUtils, Classes, Registry;

function DateTimeToRFCDateTime(const D: TDateTime): String;
function GMTDateTimeToRFCDateTime(const D: TDateTime): String;
function PadRight(const S: String; const PadChar: Char; const Len: Integer): String;
function PadLeft(const S: String; const PadChar: Char; const Len: Integer): String;
function Base64Encode(const S: AnsiString): AnsiString;
function Base64Decode(const S: AnsiString): AnsiString;
function GetFileMIMEType(const FileName: String): String;
function GetSocketErrorText(const ErrorCode: integer):string;
function UpdateCookies(const Header: String; const Cookies: String): String;
function ParseHeaderField(const Field: AnsiString; const Header: AnsiString): AnsiString;
procedure PMessages;

implementation

const
  Base64Charset = AnsiString('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/');
  RFCDayNames : Array[1..7] of String = (
      'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
  RFCMonthNames : Array[1..12] of String = (
      'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');

function GMTDateTimeToRFCDateTime(const D: TDateTime): String;
var
  Ho, Mi, Se, Ms : Word;
  Ye, Mo, Da : Word;
begin
  DecodeTime(D, Ho, Mi, Se, Ms);
  DecodeDate(D, Ye, Mo, Da);
  Result := RFCDayNames[DayOfWeek(D)] + ', ';
  Result := Result + PadLeft(IntToStr(Da), '0', 2) + ' ' +
    RFCMonthNames[Mo] + ' ' + IntToStr(Ye) + ' ';
  Result := Result + PadLeft(IntToStr(Ho), '0', 2) + ':' + PadLeft(IntToStr(Mi), '0', 2);
  Result := Result + ':' + PadLeft(IntToStr(Se), '0', 2);
  Result := Result + ' GMT';
end;

function GMTBias : Integer;
var
  TZI : TTimeZoneInformation;
begin
  if GetTimeZoneInformation(TZI) = TIME_ZONE_ID_DAYLIGHT then
    Result := TZI.DaylightBias
  else
    Result := 0;
  Result := Result + TZI.Bias;
end;

function DateTimeToRFCDateTime(const D: TDateTime): String;
begin
  Result := GMTDateTimeToRFCDateTime(D + GMTBias / (24.0 * 60.0));
end;

function PadLeft(const S: String; const PadChar: Char; const Len: Integer): String;
var
  F, L, P, M : Integer;
  I, J: PChar;
begin
  if Len > 0 then
  begin
    M := Length(S);
    if Len <> M then
    begin
      L := Len;
      P := L - M;
      if P < 0 then
        P := 0;
      SetLength(Result, L);
      if P > 0 then
        FillChar(Pointer(Result)^, P, Ord(PadChar));
      if L > P then
      begin
        I := Pointer(Result);
        J := Pointer(S);
        Inc(I, P);
        for F := 1 to L - P do
        begin
          I^ := J^;
          Inc(I);
          Inc(J);
        end;
      end;
    end else
      Result := S;
  end else
    Result := '';
end;

function PadRight(const S: String; const PadChar: Char; const Len: Integer): String;
var
  F, L, P, M : Integer;
  I, J: PAnsiChar;
begin
  if Len > 0 then
  begin
    M := Length(S);
    if Len <> M then
    begin
      L := Len;
      P := L - M;
      if P < 0 then
        P := 0;
      SetLength(Result, L);
      if L > P then
      begin
        I := Pointer(Result);
        J := Pointer(S);
        for F := 1 to L - P do
        begin
          I^ := J^;
          Inc(I);
          Inc(J);
        end;
      end;
      if P > 0 then
        FillChar(Result[L - P + 1], P, Ord(PadChar));
    end else
      Result := S;
  end else
    Result := '';
end;

function Base64Encode(const S: AnsiString): AnsiString;
var
  R, C : Byte;
  F, L, M, N, U : Integer;
  P : PAnsiChar;
begin
  L := Length(S);
  if L > 0 then
  begin
    M := L mod 3;
    N := (L div 3) * 4 + M;
    if M > 0 then Inc(N);
    U := N mod 4;
    if U > 0 then
    begin
      U := 4 - U;
      Inc(N, U);
    end;
    SetLength(Result, N);
    P := Pointer(Result);
    R := 0;
    for F := 0 to L - 1 do
    begin
      C := Byte(S [F + 1]);
      case F mod 3 of
        0 : begin
              P^ := Base64Charset[C shr 2 + 1];
              Inc(P);
              R := (C and 3) shl 4;
            end;
        1 : begin
              P^ := Base64Charset[C shr 4 + R + 1];
              Inc(P);
              R := (C and $0F) shl 2;
            end;
        2 : begin
              P^ := Base64Charset[C shr 6 + R + 1];
              Inc(P);
              P^ := Base64Charset[C and $3F + 1];
              Inc(P);
            end;
      end;
    end;
    if M > 0 then
    begin
      P^ := Base64Charset[R + 1];
      Inc(P);
    end;
    for F := 1 to U do
    begin
      P^ := '=';
      Inc(P);
    end;
  end else
    Result := '';
end;

function Base64Decode(const S: AnsiString): AnsiString;
var
  F, L, M, P: Integer;
  B, OutPos: Byte;
  OutB: Array[1..3] of Byte;
  Lookup: Array[AnsiChar] of Byte;
  R: PAnsiChar;
begin
  L := Length(S);
  P := 0;
  while (L - P > 0) and (S[L - P] = '=') do Inc(P);
  M := L - P;
  if M <> 0 then
  begin
    SetLength(Result, (M * 3) div 4);
    FillChar(Lookup, Sizeof(Lookup), #0);
    for F := 0 to 63 do
      Lookup[Base64Charset[F + 1]] := F;
    R := Pointer(Result);
    OutPos := 0;
    for F := 1 to L - P do
    begin
      B := Lookup[S[F]];
      case OutPos of
          0 : OutB[1] := B shl 2;
          1 : begin
                OutB[1] := OutB[1] or (B shr 4);
                R^ := AnsiChar(OutB[1]);
                Inc(R);
                OutB[2] := (B shl 4) and $FF;
              end;
          2 : begin
                OutB[2] := OutB[2] or (B shr 2);
                R^ := AnsiChar(OutB[2]);
                Inc(R);
                OutB[3] := (B shl 6) and $FF;
              end;
          3 : begin
                OutB[3] := OutB[3] or B;
                R^ := AnsiChar(OutB[3]);
                Inc(R);
              end;
        end;
      OutPos := (OutPos + 1) mod 4;
    end;
    if (OutPos > 0) and (P = 0) then
      if OutB[OutPos] <> 0 then
        Result := Result + AnsiChar(OutB[OutPos]);
  end else
    Result := '';
end;

function GetFileMIMEType(const FileName: String): String;
var
  Registry: TRegistry;
  ext: String;
begin
  Result := 'application/octet-stream';
  ext := ExtractFileExt(FileName);
  Registry  := TRegistry.Create;
  try
    Registry.RootKey := HKEY_CLASSES_ROOT;
    if Registry.KeyExists(ext) then
    begin
      Registry.OpenKey(ext, false);
      Result := Registry.ReadString('Content Type');
      Registry.CloseKey;
    end;
  finally
    Registry.Free;
  end;
end;

function GetSocketErrorText(const ErrorCode: integer):string;
begin
  case errorcode of
    10004: result:=   'Interrupted system call.';
    10009: result:=   'Bad file number.';
    10013: result:=   'Access denied.';
    10014: result:=   'Bad address.';
    10022: result:=   'Invalid argument.';
    10024: result:=   'Too many open files.';
    10035: result:=   'Operation would block. Check also the DataToSend property of the component (if any).';
    10036: result:=   'Operation now in progress.';
    10037: result:=   'Operation already in progress.';
    10038: result:=   'Socket operation on non-socket.';
    10039: result:=   'Destination address required.';
    10040: result:=   'Message too long.';
    10041: result:=   'Protocol wrong type for socket.';
    10042: result:=   'Bad protocol option.';
    10043: result:=   'Protocol not supported.';
    10044: result:=   'Socket type not supported.';
    10045: result:=   'Operation not supported on socket.';
    10046: result:=   'Protocol family not supported.';
    10047: result:=   'Address family not supported by protocol family.';
    10048: result:=   'Address already in use.';
    10049: result:=   'Can''t assign requested address.';
    10050: result:=   'Network is down.';
    10051: result:=   'Network is unreachable.';
    10052: result:=   'Net dropped connection or reset.';
    10053: result:=   'Software caused connection abort.';
    10054: result:=   'Connection reset by peer.';
    10055: result:=   'No buffer space available.';
    10056: result:=   'Socket is already connected.';
    10057: result:=   'Socket is not connected.';
    10058: result:=   'Can''t send after socket shutdown.';
    10059: result:=   'Too many references, can''t splice.';
    10060: result:=   'Connection timed out.';
    10061: result:=   'Connection refused.';
    10062: result:=   'Too many levels of symbolic links.';
    10063: result:=   'File name too long.';
    10064: result:=   'Host is down.';
    10065: result:=   'No route to host.';
    10066: result:=   'Directory not empty.';
    10067: result:=   'Too many processes.';
    10068: result:=   'Too many users.';
    10069: result:=   'Disc Quota Exceeded.';
    10070: result:=   'Stale NFS file handle.';
    10071: result:=   'Too many levels of remote in path.';
    10091: result:=   'Network subsystem is unavailable.';
    10092: result:=   'WINSOCK DLL Version out of range.';
    10093: result:=   'Winsock not loaded yet.';
    11001: result:=   'Host not found.';
    11002: result:=   'Non-authoritative ''Host not found'' (try again or check DNS setup).';
    11003: result:=   'Non-recoverable errors: FORMERR, REFUSED, NOTIMP.';
    11004: result:=   'Valid name, no data record (check DNSsetup).';
    500:   result:=   'Exception occured with astadataset';
    501:   result:=   'Unauthorized remote control attempted'
    else
      Result := 'Unknown error'
  end;
  Result := Result + '(' + IntToStr(errorcode) + ')';
end;

procedure PMessages;
var
  Msg: TMsg;
begin
  while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
    if Msg.Message = WM_QUIT then exit;
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
  Sleep(1);
end;

function PosOffset(const SubStr, S: string; Offset: Cardinal = 1): Integer;
var
  I,X: Integer;
  Len, LenSubStr: Integer;
begin
  if Offset = 1 then
    Result := Pos(SubStr, S)
  else
  begin
    I := Offset;
    LenSubStr := Length(SubStr);
    Len := Length(S) - LenSubStr + 1;
    while I <= Len do
    begin
      if S[I] = SubStr[1] then
      begin
        X := 1;
        while (X < LenSubStr) and (S[I + X] = SubStr[X + 1]) do
          Inc(X);
        if (X = LenSubStr) then
        begin
          Result := I;
          exit;
        end;
      end;
      Inc(I);
    end;
    Result := 0;
  end;
end;

function UpdateCookies(const Header: String; const Cookies: String): String;
var
  s, c: String;
  i, j: Integer;
begin
  result := Cookies;
  i := 1;
  s := 'Set-Cookie: ';
  while i > 0 do
  begin
    i := PosOffset(s, Header, i);
    if i > 0 then
    begin
      i := i + Length(s);
      j := PosOffset(';', Header, i);
      if j > 0 then
      begin
        c := Copy(Header, i, j - i);
        if Pos(c, result) = 0 then
        begin
          if result <> '' then
            Result := Result + '; ';
          result := result + c;
        end;
        i := j;
      end;
    end;
  end;
end;

function ParseHeaderField(const Field: AnsiString; const Header: AnsiString): AnsiString;
var
  i: integer;
  s: Ansistring;
begin
  i := Pos(Field, Header);
  Result := '';
  if i > 0 then
  begin
    s := Copy(Header, i + Length(Field), Length(Header) - i + Length(Field));
    i := Pos(AnsiString(#13#10), s);
    if i > 0 then
      Result := Copy(s, 1, i - 1);
  end;
end;

end.
