| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858 |
- {
- $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.21 2/23/2005 6:34:28 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.20 10/26/2004 9:56:00 PM JPMugaas
- Updated refs.
- Rev 1.19 8/5/2004 11:18:16 AM JPMugaas
- Should fix a parsing problem I introeduced that caused errors with Unitree
- servers.
- Rev 1.18 8/4/2004 12:40:12 PM JPMugaas
- Fix for problem with total line.
- Rev 1.17 7/15/2004 4:02:48 AM JPMugaas
- Fix for some FTP servers. In a Unix listing, a : at the end of a filename
- was wrongly being interpretted as a subdirectory entry in a recursive
- listing.
- Rev 1.16 6/14/2004 12:05:54 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.15 6/13/2004 10:44:06 PM JPMugaas
- Fixed a problem with some servers returning additional columns in the owner
- and group feilds. Note that they will not be parsed correctly in all cases.
- That's life.
- drwx------ 1 BUILTIN NT AUTHORITY 0 Dec 7 2001
- System Volume Information
- Rev 1.14 4/20/2004 4:01:18 PM JPMugaas
- Fix for nasty typecasting error. The wrong create was being called.
- Rev 1.13 4/19/2004 5:05:20 PM JPMugaas
- Class rework Kudzu wanted.
- Rev 1.12 2004.02.03 5:45:18 PM czhower
- Name changes
- Rev 1.11 2004.01.23 9:53:32 PM czhower
- REmoved unneded check because of CharIsInSet functinoalty. Also was a short
- circuit which is not permitted.
- Rev 1.10 1/23/2004 12:49:52 PM SPerry
- fixed set problems
- Rev 1.9 1/22/2004 8:29:02 AM JPMugaas
- Removed Ansi*.
- Rev 1.8 1/22/2004 7:20:48 AM JPMugaas
- System.Delete changed to IdDelete so the code can work in NET.
- Rev 1.7 10/19/2003 3:48:10 PM DSiders
- Added localization comments.
- Rev 1.6 9/28/2003 03:02:30 AM JPMugaas
- Now can handle a few non-standard date types.
- Rev 1.5 9/3/2003 07:34:40 PM JPMugaas
- Parsing for /bin/ls with devices now should work again.
- Rev 1.4 4/7/2003 04:04:26 PM JPMugaas
- User can now descover what output a parser may give.
- Rev 1.3 4/3/2003 03:37:36 AM JPMugaas
- Fixed a bug in the Unix parser causing it not to work properly with Unix BSD
- servers using the -T switch. Note that when a -T switch s used on a FreeBSD
- server, the server outputs the millaseconds and an extra column giving the
- year instead of either the year or time (the regular /bin/ls standard
- behavior).
- Rev 1.2 3/3/2003 07:17:58 PM JPMugaas
- Now honors the FreeBSD -T flag and parses list output from a program using
- it. Minor changes to the File System component.
- Rev 1.1 2/19/2003 05:53:14 PM JPMugaas
- Minor restructures to remove duplicate code and save some work with some
- formats. The Unix parser had a bug that caused it to give a False positive
- for Xercom MicroRTOS.
- Rev 1.0 2/19/2003 02:02:02 AM JPMugaas
- Individual parsing objects for the new framework.
- }
- unit IdFTPListParseUnix;
- interface
- {$i IdCompilerDefines.inc}
- uses
- Classes,
- IdFTPList, IdFTPListParseBase, IdFTPListTypes;
- {
- Notes:
- - The Unitree and Unix parsers are closely tied together and share just
- about all of the same code. The reason is that Unitee is very similar to
- a Unix dir list except it has an extra column which the Unix line parser
- can handle in the Unitree type.
- - The Unix parser can parse MACOS - Peters server (no relationship to this
- author :-) ).
- - It is worth noting that the parser does handle /bin/ls -s and -i switches as
- well as -g and -o. This is important sometimes as the Unix format comes
- from FTP servers that simply piped output from the Unix /bin/ls command.
- - This parser also handles recursive lists which is good for mirroring software.
- }
- type
- {
- Note that for this, I am violating a convention.
- The violation is that I am putting parsers for two separate servers
- in the same unit.
- The reason is this, Unitree has two additional columns (a file family
- and a file migration status. The line parsing code is the same because
- I thought it was easier to do that way in this case.
- }
- TIdUnixFTPListItem = class(TIdUnixBaseFTPListItem)
- protected
- FNumberBlocks : Integer;
- FInode : Integer;
- public
- property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks;
- property Inode : Integer read FInode write FInode;
- end;
- TIdUnitreeFTPListItem = class(TIdUnixFTPListItem)
- protected
- FMigrated : Boolean;
- FFileFamily : String;
- public
- property Migrated : Boolean read FMigrated write FMigrated;
- property FileFamily : String read FFileFamily write FFileFamily;
- end;
- TIdFTPLPUnix = class(TIdFTPListBase)
- protected
- class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override;
- class function InternelChkUnix(const AData : String) : Boolean; virtual;
- class function IsUnitree(const AData: string): Boolean; virtual;
- class function IsUnitreeBanner(const AData: String): Boolean; virtual;
- class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override;
- public
- class function GetIdent : String; override;
- class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override;
- class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override;
- end;
- TIdFTPLPUnitree = class(TIdFTPLPUnix)
- protected
- class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override;
- public
- class function GetIdent : String; override;
- end;
- const
- UNIX = 'Unix'; {do not localize}
- UNITREE = 'Unitree'; {do not localize}
- // RLebeau 2/14/09: this forces C++Builder to link to this unit so
- // RegisterFTPListParser can be called correctly at program startup...
- {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT}
- {$HPPEMIT LINKUNIT}
- {$ELSE}
- {$HPPEMIT '#pragma link "IdFTPListParseUnix"'}
- {$ENDIF}
- implementation
- uses
- IdException,
- IdGlobal, IdFTPCommon, IdGlobalProtocols,
- {$IFDEF HAS_UNIT_DateUtils}DateUtils,{$ENDIF}
- SysUtils;
- { TIdFTPLPUnix }
- class function TIdFTPLPUnix.CheckListing(AListing: TStrings;
- const ASysDescript: String; const ADetails: Boolean): Boolean;
- var
- i : Integer;
- begin
- // TODO: return True if ASysDescript starts with 'Unix'?
- Result := False;
- for i := 0 to AListing.Count - 1 do
- begin
- if AListing[i] <> '' then begin
- //workaround for the XBox MediaCenter FTP Server
- //which returns something like this:
- //
- //dr-xr-xr-x 1 ftp ftp 1 Feb 23 00:00 D:
- //and the trailing : is falsely assuming that a ":" means
- //a subdirectory entry in a recursive list.
- if InternelChkUnix(AListing[i]) then begin
- if GetIdent = UNITREE then begin
- Result := IsUnitree(AListing[i]);
- end else begin
- Result := not IsUnitree(AListing[i]);
- end;
- Break;
- end;
- if not (IsTotalLine(AListing[i]) or IsSubDirContentsBanner(AListing[i])) then begin
- Break;
- end;
- end;
- end;
- end;
- class function TIdFTPLPUnix.GetIdent: String;
- begin
- Result := UNIX;
- end;
- class function TIdFTPLPUnix.InternelChkUnix(const AData: String): Boolean;
- var
- s : TStrings;
- LCData : String;
- begin
- //pos 1 values
- // d - dir
- // - - file
- // l - symbolic link
- // b - block device
- // c - charactor device
- // p - pipe (FIFO)
- // s - socket
- LCData := UpperCase(AData);
- Result := IsValidUnixPerms(AData);
- if Result then begin
- //Do NOT attempt to do Novell Netware Print Services for Unix FTPD in NFS
- //namespace if we have a block device.
- if CharIsInSet(LCData, 1, 'CB') then begin
- Exit;
- end;
- //This extra complexity is required to distinguish Unix from
- //a Novell Netware server in NFS namespace which is somewhat similar
- //to a Unix listing. Beware.
- s := TStringList.Create;
- try
- SplitDelimitedString(LCData, s, True);
- if s.Count > 9 then begin
- Result := PosInStrArray(s[9], ['AM', 'PM']) = -1; {do not localize}
- if Result then begin
- // allow localized months longer than 3 characters
- Result := not ((IndyPos(':', s[8]) = 0) and (StrToMonth(s[6]) > 0)); {do not localize}
- end;
- end;
- finally
- FreeAndNil(s);
- end;
- end else begin
- //we make an additional check for two additional rows before the
- //the permissions. These are the inode and block count for the item.
- //These are specified with the -i and -s parameters.
- s := TStringList.Create;
- try
- SplitDelimitedString(LCData, s, True);
- if s.Count > 3 then begin
- if IsNumeric(s[0]) then begin
- Result := IsValidUnixPerms(S[1]);
- if not Result then begin
- Result := IsNumeric(s[1]) and IsValidUnixPerms(S[2]);
- end;
- end;
- end;
- finally
- FreeAndNil(s);
- end;
- end;
- end;
- class function TIdFTPLPUnix.IsUnitree(const AData: string): Boolean;
- var
- s : TStrings;
- begin
- s := TStringList.Create;
- try
- SplitDelimitedString(AData, s, True);
- Result := (s.Count > 4) and (PosInStrArray(s[4], UnitreeStoreTypes) <> -1);
- if not Result then begin
- Result := IsUnitreeBanner(AData);
- end;
- finally
- FreeAndNil(s);
- end;
- end;
- class function TIdFTPLPUnix.IsUnitreeBanner(const AData: String): Boolean;
- begin
- Result := TextStartsWith(AData, '/') and TextEndsWith(AData, ').') and (IndyPos('(', AData) > 0); {do not localize}
- end;
- class function TIdFTPLPUnix.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem;
- begin
- Result := TIdUnixFTPListItem.Create(AOwner);
- end;
- class function TIdFTPLPUnix.ParseLine(const AItem: TIdFTPListItem;
- const APath: String): Boolean;
- {Note that we also use this parser for Unitree FTP Servers because that server
- is like Unix except that in Unitree, there's two additional columns before the size.
- Those are:
- Storage Type - AR - archived or migrated to tape and DK
- File family -
- }
- type
- TParseUnixSteps = (pusINode, pusBlocks, pusPerm, pusCount, pusOwner, pusGroup,
- pusSize, pusMonth, pusDay, pusYear, pusTime, pusName, pusDone);
- var
- LStep: TParseUnixSteps;
- LData, LTmp: String;
- LInode, LBlocks, LDir, LGPerm, LOPerm, LUPerm, LCount, LOwner, LGroup: String;
- LName, LSize, LLinkTo: String;
- wYear, wMonth, wDay: Word;
- wCurrYear, wCurrMonth, wCurrDay: Word;
- // wYear, LCurrentMonth, wMonth, wDay: Word;
- wHour, wMin, wSec, wMSec: Word;
- ADate: TDateTime;
- i: Integer;
- LI : TIdUnixFTPListItem;
- wDayStr: string;
- function IsGOSwitches(const AString : String) : Boolean;
- var
- s : TStrings;
- begin
- //check to see if both the -g and -o switches were used. Both
- //owner and group are surpressed in that case. We have to check
- //that so our interpretation does not cause an error.
- Result := False;
- s := TStringList.Create;
- try
- SplitDelimitedString(AString, s, True);
- if s.Count > 2 then begin
- //if either inode or block count were given
- if IsNumeric(s[0]) then begin
- s.Delete(0);
- end;
- //if both inode and block count were given
- if IsNumeric(s[0]) then begin
- s.Delete(0);
- end;
- if s.Count > 5 then begin
- if StrToMonth(s[3]) > 0 then begin
- Result := IsNumeric(s[4]) and (IsNumeric(s[5]) or (IndyPos(':', s[5]) > 0)); {do not localize}
- end;
- end;
- end;
- finally
- FreeAndNil(s);
- end;
- end;
- function FixBonkedYear(const AStrPart : String) : String;
- var
- LB : String;
- begin
- LB := AStrPart;
- Result := Fetch(LB);
- //TODO: use StringsReplace() instead
- //Result := StringsReplace(Result, ['-', '/'], [' ', ' ']); {do not localize}
- Result := ReplaceAll(Result, '-', ' '); {do not localize}
- Result := ReplaceAll(Result, '/', ' '); {do not localize}
- Result := Result + ' ' + LB; {do not localize}
- end;
- begin
- LI := AItem as TIdUnixFTPListItem;
- // Get defaults for modified date/time
- ADate := Now;
- DecodeDate(ADate, wYear, wMonth, wDay);
- DecodeTime(ADate, wHour, wMin, wSec, wMSec);
- LData := AItem.Data;
- LStep := pusINode;
- repeat
- case LStep of
- pusINode: begin
- //we do it this way because the column for inode is right justified
- //and we don't want to create a problem if the -i parameter was never used
- LTmp := TrimLeft(LData);
- LTmp := Fetch(LTmp);
- if IsValidUnixPerms(LTmp) then begin
- LStep := pusPerm;
- end else begin
- //the inode column is right justified
- LData := TrimLeft(LData);
- LTmp := Fetch(LData);
- LData := TrimLeft(LData);
- LInode := LTmp;
- LStep := pusBlocks;
- end;
- end;
- pusBlocks: begin
- //Note that there is an ambigioutity because this value could
- //be the inode if only the -i switch was used.
- LTmp := Fetch(LData, ' ', False); {do not localize}
- if not IsValidUnixPerms(LTmp) then begin
- LTmp := Fetch(LData);
- LData := TrimLeft(LData);
- LBlocks := LTmp;
- end;
- LStep := pusPerm;
- end;
- pusPerm: begin //1.-rw-rw-rw-
- LTmp := Fetch(LData);
- LData := TrimLeft(LData);
- // Copy the predictable pieces
- LI.PermissionDisplay := Copy(LTmp, 1, 10);
- LDir := UpperCase(Copy(LTmp, 1, 1));
- LOPerm := Copy(LTmp, 2, 3);
- LGPerm := Copy(LTmp, 5, 3);
- LUPerm := Copy(LTmp, 8, 3);
- LStep := pusCount;
- end;
- pusCount: begin
- LData := TrimLeft(LData);
- LTmp := Fetch(LData);
- LData := TrimLeft(LData);
- //Patch for NetPresenz
- // "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit" */
- // "drwxrwxr-x folder 2 May 10 1996 network" */
- if TextIsSame(LTmp, 'folder') then begin {do not localize}
- LStep := pusSize;
- end else begin
- //APR
- //Patch for overflow -r--r--r-- 0526478 128 Dec 30 2002 DE292000
- if (Length(LTmp) > 3) and (LTmp[1] = '0') then begin
- LData := Copy(LTmp, 2, MaxInt) + ' ' + LData;
- LCount := '0';
- end else begin
- LCount := LTmp;
- end;
- //this check is necessary if both the owner and group were surpressed.
- if IsGOSwitches(AItem.Data) then begin
- LStep := pusSize;
- end else begin
- LStep := pusOwner;
- end;
- end;
- LData := TrimLeft(LData);
- end;
- pusOwner: begin
- LTmp := Fetch(LData);
- LData := TrimLeft(LData);
- LOwner := LTmp;
- LStep := pusGroup;
- end;
- pusGroup: begin
- LTmp := Fetch(LData);
- LData := TrimLeft(LData);
- LGroup := LTmp;
- LStep := pusSize;
- end;
- pusSize: begin
- //Ericsson - Switch FTP returns empty owner
- //Do not apply Ericson patch to Unitree
- if IsAlpha(LData, 1, 1) and (GetIdent <> UNITREE) then begin
- LSize := LGroup;
- LGroup := LOwner;
- LOwner := '';
- //we do this just after the erickson patch because
- //a few servers might return additional columns.
- //
- //e.g.
- //
- //drwx------ 1 BUILTIN NT AUTHORITY 0 Dec 7 2001 System Volume Information
- if not IsNumeric(LSize) then begin
- //undo the Ericson patch
- LOwner := LGroup;
- LGroup := '';
- repeat
- LGroup := LGroup + ' ' + LSize;
- LOwner := LGroup;
- LData := TrimLeft(LData);
- LSize := Fetch(LData);
- until IsNumeric(LSize);
- //delete the initial space we had added in the repeat loop
- IdDelete(LGroup, 1, 1);
- end;
- end else begin
- LTmp := Fetch(LData);
- //This is necessary for cases where are char device is listed
- //e.g.
- //crw-rw-rw- 1 0 1 11, 42 Aug 8 2000 tcp
- //
- //Note sure what 11, 42 is so size is not returned.
- if IndyPos(',', LTmp) > 0 then begin {do not localize}
- LData := TrimLeft(LData);
- Fetch(LData);
- LData := TrimLeft(LData);
- LSize := '';
- end else begin
- LSize := LTmp;
- end;
- LData := TrimLeft(LData);
- case PosInStrArray(LSize, UnitreeStoreTypes) of
- 0 : //AR - archived to tape - migrated
- begin
- if AItem is TIdUnitreeFTPListItem then begin
- (LI as TIdUnitreeFTPListItem).Migrated := True;
- (LI as TIdUnitreeFTPListItem).FileFamily := Fetch(LData);
- end;
- LData := TrimLeft(LData);
- LSize := Fetch(LData);
- LData := TrimLeft(LData);
- end;
- 1 : //DK - disk
- begin
- if AItem is TIdUnitreeFTPListItem then begin
- (LI as TIdUnitreeFTPListItem).FileFamily := Fetch(LData);
- end;
- LData := TrimLeft(LData);
- LSize := Fetch(LData);
- LData := TrimLeft(LData);
- end;
- end;
- end;
- LStep := pusMonth;
- end;
- pusMonth: begin // Scan modified MMM
- // Handle Chinese listings; the month, day, and year may not have spaces between them
- if IndyPos(ChineseYear, LData) > 0 then begin
- wYear := IndyStrToInt(Fetch(LData, ChineseYear));
- LData := TrimLeft(LData);
- // Set time info to 00:00:00.999
- wHour := 0;
- wMin := 0;
- wSec := 0;
- wMSec := 999;
- LStep := pusName
- end;
- if IndyPos(ChineseDay, LData) > 0 then begin
- wMonth := IndyStrToInt(Fetch(LData, ChineseMonth));
- LData := TrimLeft(LData);
- wDay := IndyStrToInt(Fetch(LData, ChineseDay));
- LData := TrimLeft(LData);
- if LStep <> pusName then begin
- LTmp := Fetch(LData);
- LStep := pusTime;
- end;
- Continue;
- end;
- //fix up a bonked date such as:
- //-rw-r--r-- 1 root other 531 09-26 13:45 README3
- LData := FixBonkedYear(LData);
- //we do this in case there's a space
- LTmp := Fetch(LData);
- if (Length(LTmp) > 3) and IsNumeric(LTmp) then begin
- //must be a year
- wYear := IndyStrToInt(LTmp, wYear);
- LTmp := Fetch(LData);
- end;
- LData := TrimLeft(LData);
- // HPUX can output the dates like "28. Jan., 16:48", "5. Mai, 05:34" or
- // "7. Nov. 2004"
- if TextEndsWith(LTmp, '.') then begin
- Delete(LTmp, Length(LTmp), 1);
- end;
- // Korean listings will have the Korean "month" character
- DeleteSuffix(LTmp,KoreanMonth);
- // Just in case
- DeleteSuffix(LTmp,KoreanEUCMonth);
- { if IndyPos(KoreanMonth, LTmp) = Length(LTmp) - Length(KoreanMonth) + 1 then
- begin
- Delete(LTmp, Length(LTmp) - Length(KoreanMonth) + 1, Length(KoreanMonth));
- end;
- // Japanese listings will have the Japanese "month" character
- } DeleteSuffix(LTmp,JapaneseMonth);
- if IsNumeric(LTmp) then begin
- wMonth := IndyStrToInt(LTmp, wMonth);
- // HPUX
- LTmp := Fetch(LData, ' ', False);
- if TextEndsWith(LTmp, ',') then begin
- Delete(LTmp, Length(LTmp), 1);
- end;
- if TextEndsWith(LTmp, '.') then begin
- Delete(LTmp, Length(LTmp), 1);
- end;
- // Handle dates where the day preceeds a string month (French, Dutch)
- i := StrToMonth(LTmp);
- if i > 0 then begin
- wDay := wMonth;
- LTmp := Fetch(LData);
- LData := TrimLeft(LData);
- wMonth := i;
- LStep := pusYear;
- end else begin
- if wMonth > 12 then begin
- wDay := wMonth;
- LTmp := Fetch(LData);
- LData := TrimLeft(LData);
- wMonth := IndyStrToInt(LTmp, wMonth);
- LStep := pusYear;
- end else begin
- LStep := pusDay;
- end;
- end;
- end else begin
- wMonth := StrToMonth(LTmp);
- LStep := pusDay;
- // Korean listings can have dates in the form "2004.10.25"
- if wMonth = 0 then begin
- wYear := IndyStrToInt(Fetch(LTmp, '.'), wYear);
- wMonth := IndyStrToInt(Fetch(LTmp, '.'), 0);
- wDay := IndyStrToInt(LTmp);
- LStep := pusName;
- end;
- end;
- end;
- pusDay: begin // Scan DD
- LTmp := Fetch(LData);
- LData := TrimLeft(LData);
- // Korean dates can have their "Day" character as included
- { if IndyPos(KoreanDay, LTmp) = Length(LTmp) - Length(KoreanDay) + 1 then
- begin
- Delete(LTmp, Length(LTmp) - Length(KoreanDay) + 1, Length(KoreanDay));
- end; }
- DeleteSuffix(LTmp,KoreanDay);
- //Ditto for Japanese
- DeleteSuffix(LTmp,JapaneseDay);
- wDay := IndyStrToInt(LTmp, wDay);
- LStep := pusYear;
- end;
- pusYear: begin
- LTmp := Fetch(LData);
- //Some localized Japanese listings include a year sybmol
- DeleteSUffix(LTmp,JapaneseYear);
- // Not time info, scan year
- if IndyPos(':', LTmp) = 0 then begin {Do not Localize}
- wYear := IndyStrToInt(LTmp, wYear);
- // Set time info to 00:00:00.999
- wHour := 0;
- wMin := 0;
- wSec := 0;
- wMSec := 999;
- LStep := pusName;
- end else begin
- // Time info, scan hour, min
- LStep := pusTime;
- end;
- end;
- pusTime: begin
- // correct year and Scan hour
- wYear := AddMissingYear(wDay, wMonth);
- wHour:= IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not Localize}
- // Set sec and ms to 0.999 except for Serv-U or FreeBSD with the -T parameter
- //with the -T parameter, Serve-U returns something like this:
- //
- //drwxrwxrwx 1 user group 0 Mar 3 04:49:59 2003 upload
- //
- //instead of:
- //
- //drwxrwxrwx 1 user group 0 Mar 3 04:49 upload
- if (IndyPos(':', LTmp) > 0) and (IsNumeric(Fetch(LData, ' ', False))) then begin {Do not localize}
- // Scan minutes
- wMin := IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not localize}
- wSec := IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not localize}
- wMSec := IndyStrToInt(Fetch(LTmp,':'), 999); {Do not localize}
- LTmp := Fetch(LData);
- wYear := IndyStrToInt(LTmp, wYear);
- end else begin
- // Scan minutes
- wMin := IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not localize}
- wSec := IndyStrToInt(Fetch(LTmp, ':'), 0); {Do not localize}
- wMSec := IndyStrToInt(Fetch(LTmp), 999);
- end;
- LStep := pusName;
- end;
- pusName: begin
- LName := LData;
- LStep := pusDone;
- end;
- end;//case LStep
- until LStep = pusDone;
- AItem.ItemType := ditFile;
- if LDir <> '' then begin
- case LDir[1] of
- 'D' : AItem.ItemType := ditDirectory; {Do not Localize}
- 'L' : AItem.ItemType := ditSymbolicLink; {Do not Localize}
- 'B' : AItem.ItemType := ditBlockDev; {Do not Localize}
- 'C' : AItem.ItemType := ditCharDev; {Do not Localize}
- 'P' : AItem.ItemType := ditFIFO; {Do not Localize}
- 'S' : AItem.ItemType := ditSocket; {Do not Localize}
- end;
- end;
- LI.UnixOwnerPermissions := LOPerm;
- LI.UnixGroupPermissions := LGPerm;
- LI.UnixOtherPermissions := LUPerm;
- LI.LinkCount := IndyStrToInt(LCount, 0);
- LI.OwnerName := LOwner;
- LI.GroupName := LGroup;
- LI.Size := IndyStrToInt64(LSize, 0);
- if (wMonth = 2) and (wDay = 29) and (not IsLeapYear(wYear)) then
- begin
- {temporary workaround for Leap Year, February 29th. Encode with day - 1, but do NOT decrement wDay since this will give us the wrong day when we adjust/re-calculate the date later}
- LI.ModifiedDate := EncodeDate(wYear, wMonth, wDay - 1) + EncodeTime(wHour, wMin, wSec, wMSec);
- end else begin
- LI.ModifiedDate := EncodeDate(wYear, wMonth, wDay) + EncodeTime(wHour, wMin, wSec, wMSec);
- end;
- {PATCH: If Indy incorrectly decremented the year then it will be almost a year behind.
- Certainly well past 90 days and so we will have the day and year in the raw data.
- (Files that are from within the last 90 days do not show the year as part of the date.)}
- wdayStr := IntToStr(wDay);
- while Length(wDayStr) < 2 do begin
- wDayStr := '0' + wDayStr; {do not localize}
- end;
- DecodeDate(Now, wCurrYear, wCurrMonth, wCurrDay);
- if (wYear < wCurrYear) and ((Now-LI.ModifiedDate) > 90) and
- (Pos(IntToStr(wMonth) + ' ' + IntToStr(wYear), LI.Data) = 0) and
- (Pos(IntToStr(wMonth) + ' ' + wDayStr + ' ' + IntToStr(wYear), LI.Data) = 0) and
- (Pos(monthNames[wMonth] + ' ' + IntToStr(wYear), LI.Data) = 0) and
- (Pos(monthNames[wMonth] + ' ' + wDayStr + ' ' + IntToStr(wYear), LI.Data) = 0) then
- begin
- {sanity check to be sure we aren't making future dates!!}
- {$IFDEF VCL_6_OR_ABOVE}
- if IncYear(LI.ModifiedDate) <= (Now + 7) then
- {$ELSE}
- if IncMonth(LI.ModifiedDate,12) <= (Now + 7) then
- {$ENDIF}
- begin
- Inc(wYear);
- if (wMonth = 2) and (wDay = 29) and (not IsLeapYear(wYear)) then
- begin
- {temporary workaround for Leap Year, February 29th. Encode with day - 1, but do NOT decrement wDay since this will give us the wrong day when we adjust/re-calculate the date later}
- LI.ModifiedDate := EncodeDate(wYear, wMonth, wDay - 1) + EncodeTime(wHour, wMin, wSec, wMSec);
- end else begin
- LI.ModifiedDate := EncodeDate(wYear, wMonth, wDay) + EncodeTime(wHour, wMin, wSec, wMSec);
- end;
- end;
- end;
- if LI.ItemType = ditSymbolicLink then begin
- i := IndyPos(UNIX_LINKTO_SYM, LName);
- LLinkTo := Copy(LName, i + 4, Length(LName) - i - 3);
- LName := Copy(LName, 1, i - 1);
- //with ls -F (DIR -F in FTP, you will sometimes symbolic links with the linked
- //to item file name ending with a /. That indicates that the item being pointed to
- //is a directory
- if TextEndsWith(LLinkTo, PATH_FILENAME_SEP_UNIX) then begin
- LI.ItemType := ditSymbolicLinkDir;
- LLinkTo := Copy(LLinkTo, 1, Length(LLinkTo)-1);
- end;
- LI.LinkedItemName := LLinkTo;
- end;
- LI.NumberBlocks := IndyStrToInt(LBlocks, 0);
- LI.Inode := IndyStrToInt(LInode, 0);
- //with servers using ls -F, / is returned after the name of dir names and a *
- //will be returned at the end of a file name for an executable program.
- //Based on info at http://www.skypoint.com/help/tipgettingaround.html
- //Note that many FTP servers obtain their DIR lists by piping output from the /bin/ls -l command.
- //The -F parameter does work with ftp.netscape.com and I have also tested a NcFTP server
- //which simulates the output of the ls command.
- if CharIsInSet(LName, Length(LName), PATH_FILENAME_SEP_UNIX + '*') then begin {Do not localize}
- LName := Copy(LName, 1, Length(LName)-1);
- end;
- if APath <> '' then begin
- // a path can sometimes come into the form of:
- // pub:
- // or
- // ./pub
- //
- //Deal with both cases
- LI.LocalFileName := LName;
- LName := APath + PATH_FILENAME_SEP_UNIX + LName;
- if TextStartsWith(LName, UNIX_CURDIR) then begin
- IdDelete(LName, 1, Length(UNIX_CURDIR));
- if TextStartsWith(LName, PATH_FILENAME_SEP_UNIX) then begin
- IdDelete(LName, 1, Length(PATH_FILENAME_SEP_UNIX));
- end;
- end;
- end;
- LI.FileName := LName;
- Result := True;
- end;
- class function TIdFTPLPUnix.ParseListing(AListing: TStrings;
- ADir: TIdFTPListItems): Boolean;
- var
- i : Integer;
- LPathSpec : String;
- LItem : TIdFTPListItem;
- begin
- for i := 0 to AListing.Count-1 do begin
- if not ((AListing[i] = '') or IsTotalLine(AListing[i]) or IsUnixLsErr(AListing[i]) or IsUnitreeBanner(AListing[i])) then begin
- //workaround for the XBox MediaCenter FTP Server
- //which returns something like this:
- //
- //dr-xr-xr-x 1 ftp ftp 1 Feb 23 00:00 D:
- //and the trailing : is falsely assuming that a ":" means
- //a subdirectory entry in a recursive list.
- if (not InternelChkUnix(AListing[i])) and IsSubDirContentsBanner(AListing[i]) then begin
- LPathSpec := Copy(AListing[i], 1, Length(AListing[i])-1);
- end else begin
- LItem := MakeNewItem(ADir);
- LItem.Data := AListing[i];
- Result := ParseLine(LItem, LPathSpec);
- if not Result then begin
- FreeAndNil(LItem);
- Exit;
- end;
- end;
- end;
- end;
- Result := True;
- end;
- { TIdFTPLPUnitree }
- class function TIdFTPLPUnitree.GetIdent: String;
- begin
- Result := UNITREE;
- end;
- class function TIdFTPLPUnitree.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem;
- begin
- Result := TIdUnitreeFTPListItem.Create(AOwner);
- end;
- initialization
- RegisterFTPListParser(TIdFTPLPUnix);
- RegisterFTPListParser(TIdFTPLPUnitree);
- finalization
- UnRegisterFTPListParser(TIdFTPLPUnix);
- UnRegisterFTPListParser(TIdFTPLPUnitree);
- end.
|