From 1092e83befab9f9d2fd6c5f995a7fd2d6641ca27 Mon Sep 17 00:00:00 2001 From: geby Date: Fri, 25 Jun 2021 10:59:09 +0000 Subject: [PATCH] ftpsend.pas - MLSD support and directory parsing improvements (Jan Fiala) git-svn-id: https://svn.code.sf.net/p/synalist/code/trunk@235 7c85be65-684b-0410-a082-b2ed4fbef004 --- ftpsend.pas | 170 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 150 insertions(+), 20 deletions(-) diff --git a/ftpsend.pas b/ftpsend.pas index 0d36835..d175398 100644 --- a/ftpsend.pas +++ b/ftpsend.pas @@ -1,5 +1,5 @@ {==============================================================================| -| Project : Ararat Synapse | 004.000.000 | +| Project : Ararat Synapse | 004.001.000 | |==============================================================================| | Content: FTP client | |==============================================================================| @@ -34,10 +34,12 @@ |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c) 1999-2010. | +| Portions created by Jan Fiala are Copyright (c) 2019. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | | Petr Esner | +| Jan Fiala | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | @@ -173,6 +175,10 @@ type @link(TFTPListRec).} procedure ParseLines; virtual; + {:try to parse MLSD directory listing in @link(lines) to list of + @link(TFTPListRec).} + procedure ParseMLSDLines; virtual; + {:By this property you have access to list of @link(TFTPListRec). This is for compatibility only. Please, use @link(Items) instead.} property List: TList read FList; @@ -229,6 +235,7 @@ type FIsDataTLS: Boolean; FTLSonData: Boolean; FFullSSL: Boolean; + FUseMLSDList: Boolean; function Auth(Mode: integer): Boolean; virtual; function Connect: Boolean; virtual; function InternalStor(const Command: string; RestoreAt: int64): Boolean; virtual; @@ -446,6 +453,9 @@ type {:If @true (default), then try to use SSL/TLS on data transfers too. If @false, then SSL/TLS is used only for control connection.} property TLSonData: Boolean read FTLSonData write FTLSonData; + + {:Enable MLSD support for directory list.} + property UseMLSDList: Boolean read FUseMLSDList write FUseMLSDList; end; {:A very useful function, and example of use can be found in the TFtpSend object. @@ -466,6 +476,8 @@ function FtpInterServerTransfer( implementation +uses DateUtils, StrUtils; + constructor TFTPSend.Create; begin inherited Create; @@ -497,6 +509,7 @@ begin FIsTLS := False; FIsDataTLS := False; FTLSonData := True; + UseMLSDList := false; end; destructor TFTPSend.Destroy; @@ -986,7 +999,10 @@ begin if NameList then x := FTPCommand('NLST' + Directory) else - x := FTPCommand('LIST' + Directory); + if FUseMLSDList then + x := FTPCommand('MLSD' + Directory) + else + x := FTPCommand('LIST' + Directory); if (x div 100) <> 1 then Exit; Result := DataRead(FDataStream); @@ -994,7 +1010,10 @@ begin begin FDataStream.Position := 0; FFTPList.Lines.LoadFromStream(FDataStream); - FFTPList.ParseLines; + if FUseMLSDList then + FFTPList.ParseMLSDLines + else + FFTPList.ParseLines; end; FDataStream.Position := 0; end; @@ -1222,8 +1241,8 @@ begin FMasks := TStringList.Create; FUnparsedLines := TStringList.Create; //various UNIX - FMasks.add('pppppppppp $!!!S*$TTT$DD$hh mm ss$YYYY$n*'); - FMasks.add('pppppppppp $!!!S*$DD$TTT$hh mm ss$YYYY$n*'); + FMasks.add('pppppppppp $!!!S*$TTT$DD$hh:mm:ss$YYYY$n*'); //Fiala - pridany dvojtecky do casu + FMasks.add('pppppppppp $!!!S*$DD$TTT$hh:mm:ss$YYYY$n*'); //Fiala - pridany dvojtecky do casu FMasks.add('pppppppppp $!!!S*$TTT$DD$UUUUU$n*'); //mostly used UNIX format FMasks.add('pppppppppp $!!!S*$DD$TTT$UUUUU$n*'); //MacOS @@ -1240,8 +1259,20 @@ begin FMasks.add('DD MM YYYY hh mmH $ d!n*'); //VMS FMasks.add('v*$ DD TTT YYYY hh mm'); + FMasks.add('v*$DD TTT YYYY hh mm ss'); + FMasks.add('v*$D TTT YYYY hh mm'); //Fiala + FMasks.add('v*$!DD TTT YYYY hh mm ss'); + //sample: ABB.DIR;1 1/35 18-SEP-2007 10:46:39 [STEVEH] (RWE,RWE,RWE,RWE) + FMasks.add('v*\$!DD TTT YYYY hh mm ss'); + //sample: DELMAS.SQL;7 0/0 6-DEC-2007 10:43:44 [STEVEH] (RWED,RWED,RWED,RWED) + FMasks.add('v*$!D TTT YYYY hh mm ss'); //Fiala + FMasks.add('v*\$!D TTT YYYY hh mm ss'); //Fiala + FMasks.add('v*$!D TTT YYYY hh mm ss'); //Fiala + FMasks.add('n*$ YYYY MM DD hh mm$S*'); //Fiala + //sample: STANS_DIFF.DIR;1 1 13-APR-2006 13:27 [AGR4] (RWE,RWE,RE,E) FMasks.add('v*$!DD TTT YYYY hh mm'); - FMasks.add('n*$ YYYY MM DD hh mm$S*'); +// FMasks.add('n*$ YYYY MM DD hh mm$S*'); + FMasks.add('$!: :n*'); //AS400 FMasks.add('!S*$MM DD YY hh mm ss !n*'); FMasks.add('!S*$DD MM YY hh mm ss !n*'); @@ -1272,9 +1303,35 @@ begin //BullGCOS8 FMasks.add(' $S* MM DD YY hh mm ss !n*'); FMasks.add('d $S* MM DD YY !n*'); + //IBM AIXs + // sample: -rw---- 1 ITINERA DAT 58 JAN 09 2008 TSITIS009VAJ_COB + FMasks.add('ppppppp $!!!S*$TTT$DD$YYYY$n*'); //Fiala + + FMasks.add('pppppppppp SSSSSSSSSS DD !YYYY n*'); //Fiala + FMasks.add('pppppppppp SSSSSSSSSS DD !hh mm n*'); //Fiala + // drwxr-xr-x 10 hol prog 45056 20 8 10:00 adis30161 + FMasks.add('pppppppppp SSSSSSSSSS DD MM!hh mm n*'); //Fiala + // drwxr-xr-x 2 hol prog 4096 20 1 2005 bin + FMasks.add('pppppppppp SSSSSSSSSS DD MM YYYY n*'); //Fiala + + //IBM VM //Fiala + // MQ_REPTS TESTVIEW V 72 139 1 2009-01-28 11:58:07 - + // NEW DIR - - - 2009-11-04 18:31:50 - + FMasks.add('n*.$n*$ SSSSSSSSSS YYYY-MM-DD hh:mm:ss -'); + FMasks.add('nnnnnnnnnnnnnnnnn d - - - YYYY-MM-DD hh:mm:ss -'); + //sample: Migrated $SRC.AFLG + FMasks.add(' dxx n*'); //Fiala + //VMS - new untouched files (name only) + // ADR10AI2 + FMasks.Add('n*§'); //Fiala + //IBM VM + // MQ_REPTS TESTVIEW V 72 139 1 2009-01-28 11:58:07 - + // NEW DIR - - - 2009-11-04 18:31:50 - + FMasks.add('nnnnnnnnnnnnnnnnn x SSSSSSSSSS YYYY-MM-DD hh:mm:ss -'); + FMasks.add('nnnnnnnnnnnnnnnnn d - - - YYYY-MM-DD hh:mm:ss -'); //BullGCOS7 - FMasks.add(' TTT DD YYYY n*'); - FMasks.add(' d n*'); +// FMasks.add(' TTT DD YYYY n*'); +// FMasks.add(' d n*'); end; destructor TFTPList.Destroy; @@ -1366,16 +1423,17 @@ begin IMask := 1; Result := 1; LastMaskC := ' '; + Value := TrimRight(Value); //Fiala while Imask <= Length(mask) do begin - if (Mask[Imask] <> '*') and (Ivalue > Length(Value)) then + if not (Mask[Imask] in ['*', '\', '§']) and (Ivalue > Length(Value)) then //Fiala begin Result := 0; Exit; end; MaskC := Mask[Imask]; - if Ivalue > Length(Value) then - Exit; +// if Ivalue > Length(Value) then +// Exit; c := Value[Ivalue]; case MaskC of 'n': @@ -1422,6 +1480,8 @@ begin Result := 0; Exit; end; + 'y': //Fiala + if c <> ' ' then Result := 0; '*': begin s := ''; @@ -1474,6 +1534,12 @@ begin end; Dec(IValue); end; + '§': //Fiala + if IValue < Length(Value) then + begin + Result := 0; + Break; + end; '$': begin while IValue <= Length(Value) do @@ -1512,6 +1578,12 @@ begin end; end; end; + ':': //Fiala + if c <> ':' then + begin + Result := 0; + Exit; + end; '\': begin Value := NextValue; @@ -1530,6 +1602,7 @@ var x, n: integer; begin Result := false; + if (Trim(FileName) = '') and (Trim(VMSFileName) = '') then Exit; //Fiala if FileName <> '' then begin if pos('?', VMSFilename) > 0 then @@ -1540,15 +1613,15 @@ begin if VMSFileName <> '' then if pos(';', VMSFilename) <= 0 then Exit; - if (FileName = '') and (VMSFileName = '') then - Exit; +// if (FileName = '') and (VMSFileName = '') then +// Exit; if Permissions <> '' then begin - if length(Permissions) <> 10 then + if (length(Permissions) <> 10) and (length(Permissions) <> 7) then //Fiala Exit; - for n := 1 to 10 do - if not(Permissions[n] in - ['a', 'b', 'c', 'd', 'h', 'l', 'p', 'r', 's', 't', 'w', 'x', 'y', '-']) then + for n := 1 to length(Permissions) do //Fiala + if not (Permissions[n] in + ['a', 'b', 'c', 'd', 'h', 'l', 'p', 'r', 's', 't', 'w', 'x', 'y', '-', 'S']) then //Fiala Exit; end; if Day <> '' then @@ -1690,6 +1763,9 @@ begin {$ENDIF} DecodeDate(Date,myear,mmonth,mday); + myear := YearOf(Date); //Fiala + mMonth := 1; //Fiala + mDay := 1; //Fiala mhours := 0; mminutes := 0; mseconds := 0; @@ -1718,9 +1794,13 @@ begin YearTime := TrimSP(YearTime); mhours := StrToIntDef(Separateleft(YearTime, ':'), 0); mminutes := StrToIntDef(SeparateRight(YearTime, ':'), 0); - if (Encodedate(myear, mmonth, mday) - + EncodeTime(mHours, mminutes, 0, 0)) > now then - Dec(mYear); + try { osetreni spatneho formatu data } //Fiala + if (Encodedate(myear, mmonth, mday) //tohle kvuli spatnemu casu na FTP serveru + + EncodeTime(mHours, mminutes, 0, 0)) > now then + Dec(mYear); + except //Fiala + mYear := YearOf(Date()); //Fiala + end; end else myear := StrToIntDef(YearTime, 0); @@ -1737,6 +1817,10 @@ begin if mHours <> 12 then mHours := MHours + 12; end; + { osetrime prechodne roky } //Fiala + if (mday = 29) and (mmonth = 2) and not IsLeapYear(myear) then + Dec(Mday); + Value.FileTime := Encodedate(myear, mmonth, mday) + EncodeTime(mHours, mminutes, mseconds, 0); if Permissions <> '' then @@ -1961,4 +2045,50 @@ begin end; end; +procedure TFTPList.ParseMLSDLines; +var + flr: TFTPListRec; + i: Integer; + s: string; + ye,mo,da,ho,mi,se: Word; + + function GetPart(const ALine, AName: string): string; + var + i, j: Integer; + begin + i := Pos(AnsiUpperCase(AName), AnsiUpperCase(ALine)); + i := i + Length(AName); + j := PosEx(';', ALine, i); + if j < 1 then j := MaxInt; + Result := Copy(ALine, i, j-i); + end; + +begin + for i := 0 to Lines.Count - 1 do + begin + s := GetPart(Lines[i], 'modify='); + ye := StrToIntDef(Copy(s, 1, 4), 1970); + mo := StrToIntDef(Copy(s, 5, 2), 1); + da := StrToIntDef(Copy(s, 7, 2), 1); + ho := StrToIntDef(Copy(s, 9, 2), 0); + mi := StrToIntDef(Copy(s, 11, 2), 0); + se := StrToIntDef(Copy(s, 13, 2), 0); + + flr := TFTPListRec.create; + flr.OriginalLine := Lines[i]; + { osetrime kraviny, protoze autori FTP serveru nerespektuji RFC, tykajici se MLSD prikazu } + try + flr.FFileTime := EncodeDateTime(ye,mo,da,ho,mi,se, 0); + except + flr.FFileTime := EncodeDateTime(1970,1,1,0,0,0, 0); + end; + flr.FDirectory := AnsiSameText(GetPart(Lines[i], 'type='), 'dir') or AnsiSameText(GetPart(Lines[i], 'type='), 'cdir'); + flr.FFileSize := StrToInt64Def(GetPart(Lines[i], 'size='), 0); + flr.FPermission := GetPart(Lines[i], 'mode='); + s := flr.FPermission; + flr.FFileName := GetPart(Lines[i], '; '); + Flist.Add(flr); + end; +end; + end.