{
  $Project$
  $Workfile$
  $Revision$
  $DateUTC$
  $Id$

  This file is part of the Indy (Internet Direct) project, and is offered
  under the dual-licensing agreement described on the Indy website.
  (http://www.indyproject.org/)

  Copyright:
   (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
  $Log$
}
{
  Rev 1.73    3/23/2005 5:17:36 AM  JPMugaas
  Removed some unused stuff.

  Rev 1.72    2/23/2005 6:34:30 PM  JPMugaas
  New property for displaying permissions ina GUI column.  Note that this
  should not be used like a CHMOD because permissions are different on
  different platforms - you have been warned.

  Rev 1.71    10/26/2004 9:19:14 PM  JPMugaas
  Fixed references.

  Rev 1.70    6/14/2004 12:05:50 AM  JPMugaas
  Added support for the following Item types that appear in some Unix listings
  (particularly a /dev or /tmp dir):

  FIFO, Socket, Character Device, Block Device.

  Rev 1.69    4/19/2004 5:04:58 PM  JPMugaas
  Class rework Kudzu wanted.

  Rev 1.68    2004.02.03 5:44:44 PM  czhower
  Name changes

  Rev 1.67    24/01/2004 19:16:42  CCostelloe
  Cleaned up warnings

  Rev 1.66    1/22/2004 4:19:04 PM  SPerry
  fixed set problems

  Rev 1.65    11/26/2003 6:22:12 PM  JPMugaas
  IdFTPList can now support file creation time for MLSD servers which support
  that feature.  I also added support for a Unique identifier for an item so
  facilitate some mirroring software if the server supports unique ID with EPLF
  and MLSD.

  Rev 1.64    3/9/2003 12:01:14 PM  JPMugaas
  Now can report errors in recursive lists.
  Permissions work better.

  Rev 1.63    2/21/2003 06:54:26 PM  JPMugaas
  The FTP list processing has been restructured so that Directory output is not
  done by IdFTPList.  This now also uses the IdFTPListParserBase for parsing so
  that the code is more scalable.

  Rev 1.62    2/17/2003 11:10:12 PM  JPMugaas
  Cisco IOS now supported.

  Rev 1.61    2/17/2003 04:43:44 PM  JPMugaas
  TOPS20 support

  Rev 1.60    2/15/2003 10:29:16 AM  JPMugaas
  Added support for some Unix specific facts with MLSD and MLST.

  Rev 1.59    2/14/2003 05:41:44 PM  JPMugaas
  Moved everything from IdFTPUtils to IdFTPCommon at Kudzu's suggestion.

  Rev 1.58    2/13/2003 10:04:28 PM  JPMugaas
  Moved some routines out of this unit and into a separate place so the code
  could be reused better.

  Rev 1.57    2/13/2003 01:31:56 AM  JPMugaas
  Made sure MLSD output is valid.  I also reordered the facts in the MLSD
  output to match with the FEAT listing.

  Rev 1.56    2/12/2003 12:23:44 PM  JPMugaas
  Bug Fixes:
  1) Blank line starting a recursive list.
  2) The ADetails parameter was working in the opposit as it should have been.

  Rev 1.55    2/12/2003 07:06:06 AM  JPMugaas
  Now honors the following parameters:

  x -  display entrie in columns accross
  C - diplay entries in columns downward
  l - long format - standard for many FTP Servers
  1 - one entry per line.

  Rev 1.54    2/11/2003 06:50:34 PM  JPMugaas
  Now supports the following:

  -X sort by extension
  -S soft by file size.

  More consistant with NcFTP Deamon.

  Rev 1.53    2/11/2003 03:43:00 PM  JPMugaas
  Q - put filename in quotes
  m - list files and dirs in a comma separated list

  are now supported.

  Rev 1.52    2/10/2003 03:05:14 PM  JPMugaas
  Now can read Unix lists with both the owner and group missing.  This can if
  the use sent the -g and -o switches.

  Rev 1.51    2/10/2003 02:09:28 PM  JPMugaas
  Now can properly process dir output using -s and or -i switches.  Note that
  Inote and Block count may not be parsed properly but it shouldn't cause an
  error.

  Rev 1.50    2/8/2003 12:43:24 PM  JPMugaas
  BugFix:  In Unix, a time is only reported if the modified date is within 6
  monthes of the current date.  The code assumed that the time is always given
  if it's in the same year as the date (not always so).

  http://www.opengroup.org/onlinepubs/007908799/xbd/utilconv.html#usg

  Rev 1.49    2/8/2003 11:43:48 AM  JPMugaas
  Made note about LineSet appearing to have two '-'.  There are just two
  charactors that are sometimes rendered the same in some programs.

  Rev 1.48    2/8/2003 10:41:30 AM  JPMugaas
  inode support for /bin/ls output.

  Rev 1.47    2/8/2003 04:37:16 AM  JPMugaas
  Rewrote the dir sorting for recurive stuff so that it is more flexible and
  permits more sorting options.  Added support for the "t" parameter and the
  /bin/ls "f" parameter.

  Rev 1.46    2/5/2003 10:13:38 PM  JPMugaas
  Added -o, -g, -p parameter support and refined the -F parameter so executable
  file names are followed by a '*'.

  Rev 1.44    2/5/2003 02:42:10 PM  JPMugaas
  Now supports the -r parameter for reverse-sorting lists even with recursive
  dirs.

  Rev 1.43    2/5/2003 08:43:48 AM  JPMugaas
  Added a ExportLSSwitches property to control the output of Unix list data
  export.  I also added support for the following switches:

  s - print blocks in List
  F - add a / to the end of dirs and symbolic links pointing to dirs
  A - surpress . and .. entries.

  The switches are the same for the /bin/ls command but we may not support them
  all.

  More will follow.
  Fixed a bug where the file being pointed to was not exported.

  Rev 1.42    2/4/2003 05:32:52 PM  JPMugaas
  Added ability to export recursive lists in both Unix and MS-DOS styles.

  Rev 1.41    2/4/2003 09:35:40 AM  JPMugaas
  Now handles Microsoft IIS DIR style with recursive listing.
  Fix for Recursion with NcFTP and MIcrosoft IIS in Unix DIRSTYLE mode.
  Fixed Novell Unix Print Services detction and Unix detection.

  Rev 1.40    2/3/2003 11:03:36 AM  JPMugaas
  Now exports the directory listing into TStrings.  Also made it possible to
  export a "total x" line for Unix style dirs.

  Rev 1.39    1/27/2003 02:53:18 AM  JPMugaas
  Fixed bug that I mistakenly introduced.

  Rev 1.38    1/27/2003 02:09:08 AM  JPMugaas
  Now, all permission feilds are empty at creation.  If the Unix permission
  feilds are empty when emulating Unix, default permissions will be given.

  Rev 1.37    1/27/2003 01:07:44 AM  JPMugaas
  In Unix, a list item was exported with two spaces between a year and the
  filename instead of one space separating both items.

  Rev 1.35    1/27/2003 12:16:02 AM  JPMugaas
  Minor adjustment to the MS-DOS DIR output.  It turns out that <DIR> part of
  the listing and the file size were off by one because only a and p were
  returned instead of AM/PM.  In addition, the filenames were separated from
  the file size by two spaces instead of one space.

  Rev 1.34    1/26/2003 04:58:36 PM  JPMugaas
  Expanded HP3000 parser slightly.

  Rev 1.33    1/26/2003 02:36:22 AM  JPMugaas
  Removed RecType type.  It was getting too ownerous to manage for MUSIC,
  VM/CMS, and MVS systems.
  Added more MVS specific properties in case a developer wishes to use them in
  a properties dialog-box.

  Rev 1.32    1/25/2003 07:31:34 PM  JPMugaas
  Added support for MUSIC SP FTP Server.

  Rev 1.31    1/20/2003 04:27:24 PM  JPMugaas
  Now handles where JES Interface 1 indicates no jobs are available.

  Rev 1.30    1/20/2003 03:13:06 PM  JPMugaas
  Unix parser now works with a Axis NPS 53X FTP Printer Server.

  Rev 1.29    1/20/2003 12:55:06 PM  JPMugaas
  Fixed sybmolic link detection problem I introduced  when applying a patch for
  other file types in Unix.

  Rev 1.28    1/19/2003 11:10:12 PM  JPMugaas
  Changed LoadList so it does not do anything if the FormatType is flfNextLine.

  Changed the Total function so it can will ignore a "Total:" so a recursive
  Unix listing will not ignore a "total:" subdirectory indicator.

  Rev 1.27    1/14/2003 04:37:52 PM  JPMugaas
  Fixed misapplied patch for Unix parsing problem in /dev directories.

  Rev 1.26    1/13/2003 10:16:06 PM  JPMugaas
  Fixed Unix parser for a peculiarity in ls output with charactor devices in
  the /dev hierarchy.   Data submitted Jeff Eaton in
  Unix-ftp.netscape.com-4.txt illustrates this.  Unix format check function now
  recognizes the following:
  b - block device
  c - character device
  p - pipe
  s - socket

  Rev 1.25    1/13/2003 12:21:28 AM  JPMugaas
  Expanded comment in MVS parser to explain why we do not support file size in
  that format.

  Rev 1.24    1/12/2003 09:00:32 PM  JPMugaas
  Refined EPLF export.  The export now tries to use ModifiedDateGMT and if
  that's not available, it uses the ModifiedDate (assuming it's in the local
  timezone).  EPLF requires dates to be based on GMT time.

  Rev 1.22    1/6/2003 10:37:12 AM  JPMugaas
  Fixed bug with Novell Netware date interpretation.  If the month was 1 and
  the day was 16, the Netware parser would interpret it as 1-16-2003 (note that
  1-16-2003 has not yet arrived).

  Rev 1.21    1/4/2003 03:24:24 PM  JPMugaas
  MVS JES Interface 1 parser now does not use string positions at all.  Spacing
  is now used instead.  More reliable that way.

  Rev 1.19    1/4/2003 01:38:40 PM  JPMugaas
  Reworked HellSoft FTP Server for Novell Support and detection.  Now it is
  detected separately from Novell Netware although it uses the same parser.
  Expanded Novell Netware Print Services for UNIX so DOS namespace is
  supported.  It turned out that I mistakenly thaught that it was simply Novell
  Netware with a NFS namespace.  Renamed format types appropriately.

  Rev 1.18    1/4/2003 10:53:52 AM  JPMugaas
  Fixed parse bug in MVS.  Sometimes, the first char in a filename was
  mistakenly dropped.
  Fixed MS-DOS parser.  For a times such 12:15 AM, the date would be returned
  as 12:15 PM.
  Fixed a MVS Particianed Data bug.  A 0 in the 3rd colum was interprettted as
  current date with the year 2000 if there was only a "0" in that column.

  Rev 1.17    1/3/2003 6:55:36 PM  JPMugaas
  WinQVT/Net 3.98.15 support.

  Rev 1.16    12/30/2002 9:19:34 AM  JPMugaas
  Patch from Andrew P. Rybin for where the count column and the file size
  column Unix are rammed together.

  Rev 1.15    12/30/2002 2:36:04 AM  JPMugaas
  Renamed the DIstinctPermissions to DistinctAttributes because those are just
  the standard MS-DOS attributes (system, read-only, hidden, and archive) as
  verified in my notes.
  Refined the Distinct32 FTP server detection.

  Rev 1.14    12/29/2002 10:29:28 PM  JPMugaas
  Updated Distinct Parser for a sample dir list where no year was given.  Added
  ModifiedTimeGMT for cases where we can obtain a timestamp in GMT (Greenwhich
  Mean Time).  These cases currently are EPLF, Distinct32, and MLST/MLSD output.

  Rev 1.13    12/29/2002 7:23:56 AM  JPMugaas
  Modified VMS parser for UCX 3.3 support (VMS-Unknown-10.txt,
  VMS-Unknown-11.txt).

  Added support for Distinct TCP/IP FTP Server-32 v. 3.0.

  Rev 1.12    12/19/2002 03:58:16 PM  JPMugaas
  Fixed an AV problem with a VM/CMS sample dir (case 10).  It was due to a
  blank line that should not have been passed to the parser.

  Rev 1.11    12/19/2002 01:29:58 PM  JPMugaas
  Microware OS/9 support.

  Rev 1.9    12/11/2002 05:52:18 PM  JPMugaas
  Fixed MS-DOS parser.  A bug would be triggered with
  "MS-DOS-MicrosoftFTP5.0-1.txt".  The parser would locate the first 43 in a
  seconds portion of the dir entry instead of the file size column which also
  contained 43.  Thanks, Jeff Easton for reporting this little gem.  Also
  removed some unneeded variables from the MS-DOS parser.

  Rev 1.8    12/11/2002 03:37:06 PM  JPMugaas
  Added LocalFileName property and the parsers now set this.  This property is
  a suggested filename for saving the file in the local system.  Pathes are
  removed from the FileName property and the version mark is stripped with VMS
  FTP Servers.

  Rev 1.7    12/9/2002 09:34:38 PM  JPMugaas
  Novel Netware with NFS Volume namespace was not working as expected.  A space
  at position in Unknown 1 and 2 was throwing things off.  I simplified the
  logic and refined the detection further.

  Rev 1.6    12/9/2002 06:57:50 PM  JPMugaas
  Added a new symbolic type for cases where a Unix server would return a / at
  the the end of the LinkedTo file name for a dir (clarifying if a link points
  to a file or a dir).  If using the DIR -F, some dir names will have a / at
  the end and executable programs may have a * at the end.  Updated the UNIX
  parser for new -F param. support.  Note that the -F parameter is from the ls
  command.  Most servers get dir lists simply by piping output from the /bin/ls
  command.  NcFTP server will also simulate the ls output.

  Rev 1.5    12/7/2002 03:20:10 PM  JPMugaas
  NCSA FTP server for MS-DOS - I hope.  I think it is included in the Telnet
  package.

  Rev 1.4    12/6/2002 08:46:34 PM  JPMugaas
  KA9Q Support.  KA9Q is a set of Internet programs for MS-DOS including a FTP
  server.  This was popular in the late 1980's and early 1990's.   It's not in
  use much anymore but might be used by Ham radio operators.

  Rev 1.3    12/1/2002 04:20:56 PM  JPMugaas
  added flfNextLine to handle cases where we can't determine the format of a
  dir with a particular line returned by the server.  Expanded Unix Parser to
  also handle Unitree FTP servers.  We now handle Unitree servers and I have
  verified that Unix ls -l * output now works (note that many Unix servers
  simply pipe output from that program).

  Rev 1.0    11/13/2002 08:28:58 AM  JPMugaas
  Initial import from FTP VC.

  Apr.2002
  - Fixed bug with MSDos Listing format - space in front of file names.

  Sep.2001 & Jan.2002
  - Merged changes submitted by Andrew P.Rybin

  2002 - Aug-23 - J. Peter Mugaas
  - fixed a parsing bug in all parsers.  A file name begging with a space will
    throw off the parsers.  Modified VMS parser to permit file names containing spaces

  2002 - Aug-22 - J. Peter Mugaas
  - VM/CMS - now returns OwnerName - I think.
  - Added RecType for VM/CMS.
  - Renamed BlockSize to NumberBlocks. Note:  Block size in VMS is usually 512 anyway
    (we hard-code that for a constant) and in VM/CMS, the block size is either
    800, 512, 1024, 2048, or 4096 at the whim of the user and we can't get the
    block size from the DIR listing.  In other words, any block size property is
    useless.
  - Changed VMS behvioar to be consistant with this.
  - Insider Privillages property added to TIdFTPListItem.  This is the
    OwnerPermissions for Novell Netware.  Note that Novell Privillages are far different
    than Unix permissions so they belong in a different property.
  - added VMS file owner and group.
    See: http://seqaxp.bio.caltech.edu/www/vms_beginners_faq.html#FILE00
  - VMS file protections (permissions).
    See: http://www.djesys.com/vms/freevms/mentor/vms_prot.html#prvs

  2002 - Aug-20 - J. Peter Mugaas
  - Added Novell Netware directory parsing.
  - Rewrote IdFTPList Novell Netware parsing.  File names with spaces are now
    properly handled.  The code also has a side effect of stripping out a zero
    that occurred in a directory that was probably due to a quirk.

  2002 - Aug-19 - J. Peter Mugaas
  - Improved VMS Directory partsing.  It NO LONGER is dependant upon specific
    column widthes.
  - Fixed bugs in VM file parsing and determination.
  - Now handles multiline VMS file list entries.

  2002 - Aug-18 - J. Peter Mugaas
  - VM/CMS or VM/ESA Mainframe directory format parsing added
  - VMS parsing added

  February 2001
  - TFTPListItem now descends from TCollectionItem
  - TFTPList now descends from TCollection

  Jun 2001
  - Fixes in UNIX format parser

  Aug 2001
  - It is now used in the FTP server component
}

unit IdFTPList;

{
  NOTE:  For this class, I recommend that you read some sections in the

  Operating Systems Handbook

  The book is out of print but is freely available at:
  http://www.snee.com/bob/opsys.html

  - Fixes as per user request for parsing non-detailed lists (SP).
    [Added flfNoDetails list format].

  Initial version by
    D. Siders
    Integral Systems
    October 2000

  Additions and extensions
    A Neillans
    Doychin Bondzhev (doychin@dsoft-bg.com)
    dSoft-Bulgaria
}

interface

{$i IdCompilerDefines.inc}

uses
  Classes,
  IdGlobal,  IdException, IdFTPCommon;

{ Indy TIdFtp extensions to support automatic parsing of FTP directory listings }

type
  EIdInvalidFTPListingFormat = class(EIdException);

  TIdDirItemType = (ditDirectory, ditFile, ditSymbolicLink, ditSymbolicLinkDir,
    ditBlockDev, ditCharDev, ditFIFO, ditSocket);

  TIdFTPFileName = TIdUnicodeString;

  TIdFTPListItems = class;

  // TIdFTPListItem stores an item in the FTP directory listing
  TIdFTPListItem = class(TCollectionItem)
  protected
    FSize: Int64;
    FData: string;
    FFileName: TIdFTPFileName;
    FLocalFileName : TIdFTPFileName; //suggested file name for local file
    FSizeAvail : Boolean;
    FModifiedAvail : Boolean;
    FModifiedDate: TDateTime;

    //the item below is for cases such as MLST output, EPLF, and Distinct format
    //which usually reports dates in GMT
    FModifiedDateGMT : TDateTime;
    //Creation time values are for MLSD data output and can be returned by the
    //the MLSD parser in some cases

    FItemType: TIdDirItemType;
    //an error has been reported in the DIR listing itself for an item
    FDirError : Boolean;
    //Permission Display
    FPermissionDisplay : String;
    //this will very amoung platforms, do not use for CHMOD
    //The format veries amoung systems.  This is only provided for people
    //wanting to display a "permission column in their FTP listing
    //property set methods
    procedure SetFileName(const AValue : TIdFTPFileName);
    //may be used by some descendent classes
    property ModifiedDateGMT : TDateTime read FModifiedDateGMT write FModifiedDateGMT;
  public
    constructor Create(AOwner: TCollection); override;
    procedure Assign(Source: TPersistent); override;

    property Data: string read FData write FData;

    property Size: Int64 read FSize write FSize;
    property ModifiedDate: TDateTime read FModifiedDate write FModifiedDate;

    property FileName: TIdFTPFileName read FFileName write SetFileName;
    property LocalFileName : TIdFTPFileName read FLocalFileName write FLocalFileName;
    property ItemType: TIdDirItemType read FItemType write FItemType;
    property SizeAvail : Boolean read FSizeAvail write FSizeAvail;
    property ModifiedAvail : Boolean read FModifiedAvail write FModifiedAvail;

    //Permission Display
    property PermissionDisplay : String read FPermissionDisplay write FPermissionDisplay;
  end;

  TIdFTPListOnGetCustomListFormat = procedure(AItem: TIdFTPListItem; var VText: string) of object;
  TIdOnParseCustomListFormat = procedure(AItem: TIdFTPListItem) of object;

  // TFTPList is the container and parser for items in the directory listing
  TIdFTPListItems = class(TCollection)
  protected
    FDirectoryName: string;
    //
    procedure SetDirectoryName(const AValue: string);
    function GetItems(AIndex: Integer): TIdFTPListItem;
    procedure SetItems(AIndex: Integer; const Value: TIdFTPListItem);
  public
    function Add: TIdFTPListItem;
    constructor Create; reintroduce;
    function IndexOf(AItem: TIdFTPListItem): Integer;
    property DirectoryName: string read FDirectoryName write SetDirectoryName;
    property Items[AIndex: Integer]: TIdFTPListItem read GetItems write SetItems; default;
  end;

implementation

uses
  IdContainers, IdResourceStrings, IdStrings, SysUtils;

{ TFTPListItem }

constructor TIdFTPListItem.Create(AOwner: TCollection);
begin
  inherited Create(AOwner);
  Data := '';    {Do not Localize}
  FItemType := ditFile;
  Size := 0;
  ModifiedDate := 0.0;
  FFileName := '';    {Do not Localize}
  FLocalFileName := '';
  FSizeAvail := True;
  FModifiedAvail := True;
end;

procedure TIdFTPListItem.Assign(Source: TPersistent);
var
  LSource: TIdFTPListItem;
begin
  if Source is TIdFTPListItem then begin
    LSource := TIdFTPListItem(Source);
    Data := LSource.Data;
    ItemType := LSource.ItemType;
    Size := LSource.Size;
    ModifiedDate := LSource.ModifiedDate;
    ModifiedDateGMT := LSource.ModifiedDateGMT;
    FFileName := LSource.FileName;
    FLocalFileName := LSource.LocalFileName;
    SizeAvail := LSource.SizeAvail;
    ModifiedAvail := LSource.ModifiedAvail;
    PermissionDisplay := LSource.PermissionDisplay;
    FDirError := LSource.FDirError;
  end else begin
    inherited Assign(Source);
  end;
end;

{ TFTPList }

constructor TIdFTPListItems.Create;
begin
  inherited Create(TIdFTPListItem);
end;

function TIdFTPListItems.Add: TIdFTPListItem;
begin
  Result := TIdFTPListItem(inherited Add);
end;

function TIdFTPListItems.GetItems(AIndex: Integer): TIdFTPListItem;
begin
  Result := TIdFTPListItem(inherited Items[AIndex]);
end;

function TIdFTPListItems.IndexOf(AItem: TIdFTPListItem): Integer;
Var
  i: Integer;
begin
  for i := 0 to Count - 1 do
  begin
    if AItem = Items[i] then
    begin
      Result := i;
      Exit;
    end;
  end;
  Result := -1;
end;

procedure TIdFTPListItems.SetItems(AIndex: Integer; const Value: TIdFTPListItem);
begin
  inherited Items[AIndex] := Value;
end;

procedure TIdFTPListItems.SetDirectoryName(const AValue: string);
begin
  if not TextIsSame(FDirectoryName, AValue) then begin       
    FDirectoryName := AValue;
    Clear;
  end;
end;

procedure TIdFTPListItem.SetFileName(const AValue: TIdFTPFileName);
var
  i : Integer;
  LDoLowerCase : Boolean;
const
  LLowCase = 'abcdefghijklmnpqrstuvwxyz';   {do not localize}
begin
  if (FLocalFileName = '') or TextIsSame(FFileName, FLocalFileName) then begin
    //we do things this way because some file systems use all capital letters or are
    //case insensivite.  The Unix file is case sensitive and Unix users tend to
    //prefer lower case filenames.  We do not want to force lowercase if a file
    //has both uppercase and lowercase because the uppercase letters are probably intentional
    LDoLowerCase := True;
    // TODO: add IsLower() functions in IdGlobal/Protocol?
    for i := 1 to Length(AValue) do begin
      if CharIsInSet(AValue, i, LLowCase) then begin
        LDoLowerCase := False;
        Break;
      end;
    end;
    if LDoLowerCase then begin
      FLocalFileName := LowerCase(AValue);
    end else begin
      FLocalFileName := AValue;
    end;
  end;
  FFileName := AValue;
end;

end.
