FIX: Zip - determine encoding when GetACP = GetOEMCP (Windows)

This commit is contained in:
Alexander Koblov 2020-04-25 09:25:58 +00:00
commit 7fc2f7db91
2 changed files with 130 additions and 59 deletions

View file

@ -1499,25 +1499,14 @@ begin
SetString(UnicodeName, XceedField.UnicodeName, XceedField.Length);
FFileName := Utf16ToUtf8(UnicodeName);
end
else
begin
else begin
SystemCode := HostOS;
{$IF DEFINED(MSWINDOWS)}
if (GetACP <> GetOEMCP) and (SystemCode = hosDOS) then
FFileName := CeOemToUtf8(FItemInfo.FileName)
else if (GetACP <> GetOEMCP) and CeTryDecode(FItemInfo.FileName, CP_OEMCP, UnicodeName) then
FFileName := Utf16ToUtf8(UnicodeName)
else if (SystemCode = hosNTFS) or (SystemCode = hosWinNT) then
FFileName := CeAnsiToUtf8(FItemInfo.FileName)
else
{$ELSEIF DEFINED(UNIX)}
if (SystemCode = hosDOS) then
FFileName := CeOemToUtf8(FItemInfo.FileName)
else if (SystemCode = hosNTFS) or (SystemCode = hosWinNT) then
FFileName := CeAnsiToUtf8(FItemInfo.FileName)
else
{$ENDIF}
FFileName := FItemInfo.FileName;
FFileName := CeSysToUtf8(FItemInfo.FileName);
end;
{ read ZIP64 extended header }
@ -1684,6 +1673,7 @@ procedure TAbZipItem.SetFileName(const Value : string );
var
{$IFDEF MSWINDOWS}
AnsiName : AnsiString;
UnicName : UnicodeString;
{$ENDIF}
UTF8Name : AnsiString;
FieldSize : Word;
@ -1695,9 +1685,10 @@ begin
{$IFDEF MSWINDOWS}
FItemInfo.IsUTF8 := False;
HostOS := hosDOS;
if CeTryEncode(UTF8Decode(Value), CP_OEMCP, False, AnsiName) then
UnicName := UTF8Decode(Value);
if CeTryEncode(UnicName, CP_OEMCP, False, AnsiName) then
{no-op}
else if (GetACP <> GetOEMCP) and CeTryEncode(UTF8Decode(Value), CP_ACP, False, AnsiName) then
else if (GetACP <> GetOEMCP) and CeTryEncode(UnicName, CP_ACP, False, AnsiName) then
HostOS := hosWinNT
else
FItemInfo.IsUTF8 := True;

View file

@ -2766,9 +2766,18 @@ Index: AbZipPrc.pas
Item.ExternalFileAttributes := LongWord(AttrEx.Mode) shl 16 + LongWord(AttrEx.Attr);
Index: AbZipTyp.pas
===================================================================
--- AbZipTyp.pas (revision 512)
--- AbZipTyp.pas (revision 539)
+++ AbZipTyp.pas (working copy)
@@ -169,7 +169,7 @@
@@ -67,6 +67,8 @@
AbLanguageEncodingFlag = $0800;
Ab_Zip64SubfieldID : Word = $0001;
+ Ab_NTFSSubfieldID : Word = $000A;
+ Ab_InfoZipTimestampSubfieldID : Word = $5455;
Ab_InfoZipUnicodePathSubfieldID : Word = $7075;
Ab_XceedUnicodePathSubfieldID : Word = $554E;
Ab_XceedUnicodePathSignature : LongWord= $5843554E;
@@ -169,7 +171,7 @@
(cmStored, cmShrunk, cmReduced1, cmReduced2, cmReduced3,
cmReduced4, cmImploded, cmTokenized, cmDeflated,
cmEnhancedDeflated, cmDCLImploded, cmBzip2 = 12, cmLZMA = 14,
@ -2777,7 +2786,7 @@ Index: AbZipTyp.pas
TAbZipSupportedMethod =
(smStored, smDeflated, smBestMethod);
@@ -206,6 +206,7 @@
@@ -206,6 +208,7 @@
FCompressedSize : Int64;
FUncompressedSize : Int64;
public {methods}
@ -2785,7 +2794,15 @@ Index: AbZipTyp.pas
procedure SaveToStream( Stream : TStream );
public {properties}
property CRC32 : Longint
@@ -407,6 +408,7 @@
@@ -369,6 +372,7 @@
FDiskNumberStart : LongWord;
FLFHExtraField : TAbExtraField;
FRelativeOffset : Int64;
+ FDateTime : TDateTime;
protected {methods}
function GetCompressionMethod : TAbZipCompressionMethod;
@@ -407,6 +411,7 @@
function GetLastModFileDate : Word; override;
function GetLastModFileTime : Word; override;
function GetNativeFileAttributes : LongInt; override;
@ -2793,7 +2810,7 @@ Index: AbZipTyp.pas
procedure SetCompressedSize( const Value : Int64 ); override;
procedure SetCRC32( const Value : Longint ); override;
procedure SetExternalFileAttributes( Value : LongWord ); override;
@@ -530,8 +532,8 @@
@@ -530,8 +535,8 @@
override;
destructor Destroy;
override;
@ -2804,7 +2821,7 @@ Index: AbZipTyp.pas
public {properties}
property CompressionMethodToUse : TAbZipSupportedMethod
@@ -620,11 +622,14 @@
@@ -620,11 +625,16 @@
{$ENDIF}
{$ENDIF}
Math,
@ -2816,12 +2833,14 @@ Index: AbZipTyp.pas
+ SysUtils,
+ LazUTF8,
+ DCOSUtils,
+ DCBasicTypes,
+ DCClassesUtf8,
+ DCDateTimeUtils,
+ DCConvertEncoding;
function VerifyZip(Strm : TStream) : TAbArchiveType;
{ determine if stream appears to be in PkZip format }
@@ -638,20 +643,31 @@
@@ -638,20 +648,31 @@
Result := atUnknown;
try
Strm.Position := 0;
@ -2867,7 +2886,7 @@ Index: AbZipTyp.pas
end;
end;
except
@@ -733,8 +749,7 @@
@@ -733,8 +754,7 @@
leaves stream positioned at start of structure or at original
position if not found }
const
@ -2877,7 +2896,7 @@ Index: AbZipTyp.pas
var
StartPos : Int64;
TailRec : TAbZipEndOfCentralDirectoryRecord;
@@ -741,7 +756,6 @@
@@ -741,7 +761,6 @@
Buffer : PAnsiChar;
Offset : Int64;
TestPos : PAnsiChar;
@ -2885,7 +2904,7 @@ Index: AbZipTyp.pas
BytesRead : Int64;
BufSize : Int64;
CommentLen: integer;
@@ -767,7 +781,7 @@
@@ -767,7 +786,7 @@
stream; we need to search for the tail signature}
{get a buffer}
@ -2894,7 +2913,7 @@ Index: AbZipTyp.pas
GetMem(Buffer, BufSize);
try
@@ -774,57 +788,41 @@
@@ -774,57 +793,41 @@
{start out searching backwards}
Offset := -BufSize;
@ -2979,7 +2998,7 @@ Index: AbZipTyp.pas
end;
{if we reach this point, the CD tail is not present}
@@ -914,6 +912,17 @@
@@ -914,6 +917,17 @@
end;
{============================================================================}
{ TAbZipDataDescriptor implementation ====================================== }
@ -2997,7 +3016,7 @@ Index: AbZipTyp.pas
procedure TAbZipDataDescriptor.SaveToStream( Stream : TStream );
begin
Stream.Write( Ab_ZipDataDescriptorSignature, sizeof( Ab_ZipDataDescriptorSignature ) );
@@ -1195,12 +1204,13 @@
@@ -1195,12 +1209,13 @@
{ TAbZipDirectoryFileFooter implementation ================================= }
function TAbZipDirectoryFileFooter.GetIsZip64: Boolean;
begin
@ -3017,7 +3036,7 @@ Index: AbZipTyp.pas
end;
{ -------------------------------------------------------------------------- }
procedure TAbZipDirectoryFileFooter.LoadFromStream( Stream : TStream );
@@ -1395,10 +1405,17 @@
@@ -1395,10 +1410,17 @@
Result := Byte(ExternalFileAttributes);
{$ENDIF}
{$IFDEF UNIX}
@ -3037,7 +3056,7 @@ Index: AbZipTyp.pas
{$ENDIF}
end;
{ -------------------------------------------------------------------------- }
@@ -1407,6 +1424,22 @@
@@ -1407,6 +1429,30 @@
Result := FItemInfo.FileName;
end;
{ -------------------------------------------------------------------------- }
@ -3049,18 +3068,34 @@ Index: AbZipTyp.pas
+begin
+ // Zip stores MS-DOS date/time.
+{$IFDEF UNIX}
+ DateTime := AbDosFileDateToDateTime(LastModFileDate, LastModFileTime);
+ Result := AbLocalDateTimeToUnixTime(DateTime);
+ if (FDateTime <> 0) then
+ DateTime := FDateTime
+ else begin
+ DateTime := AbDosFileDateToDateTime(LastModFileDate, LastModFileTime);
+ end;
+ Result := DateTimeToUnixFileTime(DateTime);
+{$ELSE}
+ LongRec(Result).Hi := LastModFileDate;
+ LongRec(Result).Lo := LastModFileTime;
+ if (FDateTime <> 0) then
+ Result := DateTimeToDosFileTime(FDateTime)
+ else begin
+ LongRec(Result).Hi := LastModFileDate;
+ LongRec(Result).Lo := LastModFileTime;
+ end;
+{$ENDIF}
+end;
+{ -------------------------------------------------------------------------- }
function TAbZipItem.GetShannonFanoTreeCount : Byte;
begin
Result := FItemInfo.ShannonFanoTreeCount;
@@ -1430,12 +1463,13 @@
@@ -1424,6 +1470,7 @@
{ -------------------------------------------------------------------------- }
procedure TAbZipItem.LoadFromStream( Stream : TStream );
var
+ Tag, TagSize,
FieldSize: Word;
FieldStream: TStream;
InfoZipField: PInfoZipUnicodePathRec;
@@ -1430,12 +1477,13 @@
UnicodeName: UnicodeString;
UTF8Name: AnsiString;
XceedField: PXceedUnicodePathRec;
@ -3076,7 +3111,7 @@ Index: AbZipTyp.pas
else if FItemInfo.ExtraField.Get(Ab_InfoZipUnicodePathSubfieldID, Pointer(InfoZipField), FieldSize) and
(FieldSize > SizeOf(TInfoZipUnicodePathRec)) and
(InfoZipField.Version = 1) and
@@ -1442,7 +1476,7 @@
@@ -1442,7 +1490,7 @@
(InfoZipField.NameCRC32 = AbCRC32Of(FItemInfo.FileName)) then begin
SetString(UTF8Name, InfoZipField.UnicodeName,
FieldSize - SizeOf(TInfoZipUnicodePathRec) + 1);
@ -3085,7 +3120,7 @@ Index: AbZipTyp.pas
end
else if FItemInfo.ExtraField.Get(Ab_XceedUnicodePathSubfieldID, Pointer(XceedField), FieldSize) and
(FieldSize > SizeOf(TXceedUnicodePathRec)) and
@@ -1449,16 +1483,28 @@
@@ -1449,16 +1497,17 @@
(XceedField.Signature = Ab_XceedUnicodePathSignature) and
(XceedField.Length * SizeOf(WideChar) = FieldSize - SizeOf(TXceedUnicodePathRec) + SizeOf(WideChar)) then begin
SetString(UnicodeName, XceedField.UnicodeName, XceedField.Length);
@ -3098,39 +3133,84 @@ Index: AbZipTyp.pas
- OemToCharBuff(PAnsiChar(FItemInfo.FileName), PChar(FFileName), Length(FFileName));
- end
- {$ENDIF}
else
- else
- FFileName := string(FItemInfo.FileName);
+ begin
+ else begin
+ SystemCode := HostOS;
+ {$IF DEFINED(MSWINDOWS)}
+ if (GetACP <> GetOEMCP) and (SystemCode = hosDOS) then
+ FFileName := CeOemToUtf8(FItemInfo.FileName)
+ else if (GetACP <> GetOEMCP) and CeTryDecode(FItemInfo.FileName, CP_OEMCP, UnicodeName) then
+ FFileName := Utf16ToUtf8(UnicodeName)
+ else if (SystemCode = hosNTFS) or (SystemCode = hosWinNT) then
+ FFileName := CeAnsiToUtf8(FItemInfo.FileName)
+ else
+ {$ELSEIF DEFINED(UNIX)}
+ if (SystemCode = hosDOS) then
+ FFileName := CeOemToUtf8(FItemInfo.FileName)
+ else if (SystemCode = hosNTFS) or (SystemCode = hosWinNT) then
+ FFileName := CeAnsiToUtf8(FItemInfo.FileName)
+ else
+ {$ENDIF}
+ FFileName := FItemInfo.FileName;
+ FFileName := CeSysToUtf8(FItemInfo.FileName);
+ end;
{ read ZIP64 extended header }
FUncompressedSize := FItemInfo.UncompressedSize;
@@ -1596,24 +1642,20 @@
@@ -1481,6 +1530,45 @@
LastModFileTime := FItemInfo.LastModFileTime;
LastModFileDate := FItemInfo.LastModFileDate;
+ // NTFS Extra Field
+ if FItemInfo.ExtraField.GetStream(Ab_NTFSSubfieldID, FieldStream) then
+ try
+ FieldSize:= FieldStream.Size;
+ if (FieldSize >= 32) then
+ begin
+ // Skip Reserved
+ Dec(FieldSize, 4);
+ FieldStream.Seek(4, soBeginning);
+ while (FieldSize > 4) do
+ begin
+ Dec(FieldSize, 4);
+ Tag:= FieldStream.ReadWord;
+ TagSize:= FieldStream.ReadWord;
+ TagSize:= Min(TagSize, FieldSize);
+ if (Tag = $0001) and (TagSize >= 24) then
+ begin
+ FDateTime:= WinFileTimeToDateTime(TWinFileTime(FieldStream.ReadQWord));
+ Break;
+ end;
+ Dec(FieldSize, TagSize);
+ end;
+ end;
+ finally
+ FieldStream.Free;
+ end
+ // Extended Timestamp Extra Field
+ else if FItemInfo.ExtraField.GetStream(Ab_InfoZipTimestampSubfieldID, FieldStream) then
+ try
+ FieldSize:= FieldStream.Size;
+ if (FieldSize >= 5) then
+ begin
+ Tag:= FieldStream.ReadByte;
+ if (Tag and $01 <> 0) then
+ FDateTime:= UnixFileTimeToDateTime(TUnixFileTime(FieldStream.ReadDWord));
+ end;
+ finally
+ FieldStream.Free;
+ end;
FDiskFileName := FileName;
AbUnfixName( FDiskFileName );
Action := aaNone;
@@ -1585,6 +1673,7 @@
var
{$IFDEF MSWINDOWS}
AnsiName : AnsiString;
+ UnicName : UnicodeString;
{$ENDIF}
UTF8Name : AnsiString;
FieldSize : Word;
@@ -1596,24 +1685,21 @@
{$IFDEF MSWINDOWS}
FItemInfo.IsUTF8 := False;
HostOS := hosDOS;
- if AbTryEncode(Value, CP_OEMCP, False, AnsiName) then
+ if CeTryEncode(UTF8Decode(Value), CP_OEMCP, False, AnsiName) then
+ UnicName := UTF8Decode(Value);
+ if CeTryEncode(UnicName, CP_OEMCP, False, AnsiName) then
{no-op}
- else if (GetACP <> GetOEMCP) and AbTryEncode(Value, CP_ACP, False, AnsiName) then
+ else if (GetACP <> GetOEMCP) and CeTryEncode(UTF8Decode(Value), CP_ACP, False, AnsiName) then
+ else if (GetACP <> GetOEMCP) and CeTryEncode(UnicName, CP_ACP, False, AnsiName) then
HostOS := hosWinNT
- else if AbTryEncode(Value, CP_OEMCP, True, AnsiName) then
- {no-op}
@ -3152,7 +3232,7 @@ Index: AbZipTyp.pas
{$ENDIF}
UseExtraField := False;
@@ -1626,7 +1668,7 @@
@@ -1626,7 +1712,7 @@
end;
if UseExtraField then begin
@ -3161,7 +3241,7 @@ Index: AbZipTyp.pas
FieldSize := SizeOf(TInfoZipUnicodePathRec) + Length(UTF8Name) - 1;
GetMem(InfoZipField, FieldSize);
try
@@ -1762,11 +1804,11 @@
@@ -1762,11 +1848,11 @@
inherited Destroy;
end;
{ -------------------------------------------------------------------------- }
@ -3176,7 +3256,7 @@ Index: AbZipTyp.pas
Result := TAbZipItem.Create;
with TAbZipItem( Result ) do begin
CompressionMethod := cmDeflated;
@@ -1773,9 +1815,17 @@
@@ -1773,9 +1859,17 @@
GeneralPurposeBitFlag := 0;
CompressedSize := 0;
CRC32 := 0;
@ -3196,7 +3276,7 @@ Index: AbZipTyp.pas
end;
end;
{ -------------------------------------------------------------------------- }
@@ -1930,8 +1980,8 @@
@@ -1930,8 +2024,8 @@
AbStripDots( lValue );
for i := 1 to Length( lValue ) do
@ -3207,7 +3287,7 @@ Index: AbZipTyp.pas
Result := lValue;
end;
{ -------------------------------------------------------------------------- }
@@ -1983,7 +2033,7 @@
@@ -1983,7 +2077,7 @@
FStatus := asInvalid; //TODO: Status updates are extremely inconsistent
raise EAbUserAbort.Create;
end;
@ -3216,7 +3296,7 @@ Index: AbZipTyp.pas
TailPosition := FindCentralDirectoryTail( FStream );
end;
end;
@@ -2302,7 +2352,7 @@
@@ -2302,7 +2396,7 @@
if FOwnsStream then begin
{need new stream to write}
FreeAndNil(FStream);