IdCoder3to4 fixed for wrong attachments
Download fixed idcoder3to4.pas
{ $HDR$}
{**********************************************************************}
{ Unit archived using Team Coherence }
{ Team Coherence is Copyright 2002 by Quality Software Components }
{ }
{ For further information / comments, visit our WEB site at }
{ http://www.TeamCoherence.com }
{**********************************************************************}
{}
{ $Log: 13754: IdCoder3to4.pas
{
{ Rev 1.30 15.09.2004 22:38:22 Andreas Hausladen
{ Added "Delphi 7.1 compiler warning bug" fix code
}
{
{ Rev 1.29 27.08.2004 22:03:22 Andreas Hausladen
{ Optimized encoders
{ speed optimization ("const" for string parameters)
}
{
{ Rev 1.28 7/8/04 5:09:04 PM RLebeau
{ Updated Encode() to remove use of local TIdBytes variable
}
{
{ Rev 1.27 2004.05.20 1:39:20 PM czhower
{ Last of the IdStream updates
}
{
{ Rev 1.26 2004.05.20 11:37:08 AM czhower
{ IdStreamVCL
}
{
{ Rev 1.25 2004.05.20 11:13:12 AM czhower
{ More IdStream conversions
}
{
{ Rev 1.24 2004.05.19 3:06:54 PM czhower
{ IdStream / .NET fix
}
{
{ Rev 1.23 2004.03.12 7:54:18 PM czhower
{ Removed old commented out code.
}
{
{ Rev 1.22 11/03/2004 22:36:14 CCostelloe
{ Bug fix (1 to 3 spurious extra characters at the end of UUE encoded messages,
{ see comment starting CC3.
}
{
{ Rev 1.21 2004.02.03 5:44:56 PM czhower
{ Name changes
}
{
{ Rev 1.20 28/1/2004 6:22:16 PM SGrobety
{ Removed base 64 encoding stream length check is stream size was provided
}
{
{ Rev 1.19 16/01/2004 17:47:48 CCostelloe
{ Restructured slightly to allow IdCoderBinHex4 reuse some of its code
}
{
{ Rev 1.18 02/01/2004 20:59:28 CCostelloe
{ Fixed bugs to get ported code to work in Delphi 7 (changes marked CC2)
}
{
{ Rev 1.17 11/10/2003 7:54:14 PM BGooijen
{ Did all todo's ( TStream to TIdStream mainly )
}
{
{ Rev 1.16 2003.10.24 10:43:02 AM czhower
{ TIdSTream to dos
}
{
{ Rev 1.15 22/10/2003 12:25:36 HHariri
{ Stephanes changes
}
{
Rev 1.14 10/16/2003 11:10:18 PM DSiders
Added localization comments, whitespace.
}
{
{ Rev 1.13 2003.10.11 10:00:12 PM czhower
{ Compiles again
}
{
{ Rev 1.12 10/5/2003 4:31:02 PM GGrieve
{ use ToBytes for Cardinal to Bytes conversion
}
{
{ Rev 1.11 10/4/2003 9:12:18 PM GGrieve
{ DotNet
}
{
{ Rev 1.10 2003.06.24 12:02:10 AM czhower
{ Coders now decode properly again.
}
{
{ Rev 1.9 2003.06.23 10:53:16 PM czhower
{ Removed unused overriden methods.
}
{
{ Rev 1.8 2003.06.13 6:57:10 PM czhower
{ Speed improvement
}
{
{ Rev 1.7 2003.06.13 3:41:18 PM czhower
{ Optimizaitions.
}
{
{ Rev 1.6 2003.06.13 2:24:08 PM czhower
{ Speed improvement
}
{
{ Rev 1.5 10/6/2003 5:37:02 PM SGrobety
{ Bug fix in decoders.
}
{
{ Rev 1.4 6/6/2003 4:50:30 PM SGrobety
{ Reworked the 3to4decoder for performance and stability.
{ Note that encoders haven't been touched. Will come later. Another problem:
{ input is ALWAYS a string. Should be a TStream.
{
{ 1/ Fix: added filtering for #13,#10 and #32 to the decoding mechanism.
{ 2/ Optimization: Speed the decoding by a factor 7-10 AND added filtering ;)
{ Could still do better by using a pointer and a stiding window by a factor 2-3.
{ 3/ Improvement: instead of writing everything to the output stream, there is
{ an internal buffer of 4k. It should speed things up when working on large
{ data (no large chunk of memory pre-allocated while keeping a decent perf by
{ not requiring every byte to be written separately).
}
{
{ Rev 1.3 28/05/2003 10:06:56 CCostelloe
{ StripCRLFs changes stripped out at the request of Chad
}
{
{ Rev 1.2 20/05/2003 02:01:00 CCostelloe
}
{
{ Rev 1.1 20/05/2003 01:44:12 CCostelloe
{ Bug fix: decoder code altered to ensure that any CRLFs inserted by an MTA are
{ removed
}
{
{ Rev 1.0 11/14/2002 02:14:36 PM JPMugaas
}
unit IdCoder3to4;
interface
uses
Classes,
IdCoder, IdGlobal, IdStreamRandomAccess;
type
TIdDecodeTable = array[1..127] of Byte;
TIdEncoder3to4 = class(TIdEncoder)
protected
FCodingTable: string;
FFillChar: Char;
function EncodeIdBytes(ABuffer: TIdBytes): TIdBytes;
public
function Encode(ASrcStream: TIdStreamRandomAccess;
const ABytes: Integer = MaxInt): string; override;
//procedure EncodeUnit(const AIn1, AIn2, AIn3: Byte; var VOut: TIdBytes);
published
property CodingTable: string read FCodingTable;
property FillChar: Char read FFillChar write FFillChar;
end;
TIdEncoder3to4Class = class of TIdEncoder3to4;
TIdDecoder4to3 = class(TIdDecoder)
protected
FCodingTable: string;
FDecodeTable: TIdDecodeTable;
FFillChar: Char;
function InternalDecode(const LIn: TIdBytes; const AStartPos: Integer = 1; const ABytes: Integer = -1): TIdBytes;
public
class procedure ConstructDecodeTable(const ACodingTable: string;
var ADecodeArray: TIdDecodeTable);
procedure Decode(const AIn: string; const AStartPos: Integer = 1;
const ABytes: Integer = -1); override;
published
property FillChar: Char read FFillChar write FFillChar;
end;
implementation
uses
WStrUtils,
IdException, IdResourceStrings,
SysUtils;
{ TIdDecoder4to3 }
class procedure TIdDecoder4to3.ConstructDecodeTable(const ACodingTable: string;
var ADecodeArray: TIdDecodeTable);
var
i: integer;
begin
//TODO: See if we can find an efficient way, or maybe an option to see if the requested
//decode char is valid, that is it returns a 255 from the DecodeTable, or at maybe
//check its presence in the encode table.
for i := Low(ADecodeArray) to High(ADecodeArray) do begin
ADecodeArray[i] := 255;
end;
for i := 1 to Length(ACodingTable) do begin
ADecodeArray[Ord(ACodingTable[i])] := i - 1;
end;
end;
procedure TIdDecoder4to3.Decode(const AIn: string; const AStartPos: Integer = 1; const ABytes: Integer = -1);
var
LIn: TIdBytes;
LOut: TIdBytes;
begin
if AIn <> '' then begin
if ((length(AIn) mod 4) <> 0) or ContainsAltChars(AIn) then exit;
SetLength(LIn, 0); // Delphi 7.1 first edition warning bug
SetLength(LOut, 0); // Delphi 7.1 first edition warning bug
LIn := ToBytes(AIn); // if in dotnet, convert to serialisable format
LOut := InternalDecode(LIn, AStartPos, ABytes);
// Write out data to stream
FStream.Write(LOut);
end;
end;
function TIdDecoder4to3.InternalDecode(const LIn: TIdBytes; const AStartPos: Integer = 1; const ABytes: Integer = -1): TIdBytes;
const
LInBytesLen = 4;
var
LEmptyBytes: Integer;
LInBytes: array[0..LInBytesLen - 1] of Byte;
LWorkBytes: TIdBytes;
LOutPos: Integer;
LOutSize: Integer;
LInLimit: Integer;
LInPos: Integer;
LWhole: Cardinal;
LFillChar: Char; // local copy of FFillChar
begin
LFillChar := FillChar;
SetLength(LWorkBytes, 4);
//TODO: Change output to a TMemoryStream
LEmptyBytes := 0;
// Presize output buffer
//CC2, bugfix: was LOutPos := 1;
LOutPos := 0;
if ABytes = -1 then begin
//LOutSize := (Length(AIn) div 4) * 3;
LOutSize := (Length(LIn) div 4) * 3;
end else begin
// Need to make sure we have space as we always write out 3 and then trim
// because it requires less checking in the loop
if ABytes mod 3 > 0 then begin
LOutSize := (ABytes div 3) * 3 + 3;
end else begin
LOutSize := ABytes;
end;
end;
SetLength(Result, LOutSize);
//
LInPos := AStartPos;
// +1 because LInPos is 1 based
LInLimit := Length(LIn) - LInBytesLen + 1;
while LInPos <= LInLimit do begin
// Read 4 bytes in for processing
//CC2 bugfix: was CopyTIdBytes(LIn, LInPos, LInBytes, 0, LInBytesLen);
//CopyTIdBytes(LIn, LInPos-1, LInBytes, 0, LInBytesLen);
// Faster than CopyTIdBytes
LInBytes[0] := LIn[LInPos - 1];
LInBytes[1] := LIn[LInPos - 1 + 1];
LInBytes[2] := LIn[LInPos - 1 + 2];
LInBytes[3] := LIn[LInPos - 1 + 3];
// Inc pointer
Inc(LInPos, LInBytesLen);
// Reduce to 3 bytes
LWhole :=
(FDecodeTable[LInBytes[0]] shl 18)
or (FDecodeTable[LInBytes[1]] shl 12)
or (FDecodeTable[LInBytes[2]] shl 6)
or FDecodeTable[LInBytes[3]];
ToBytesF(LWorkBytes, LWhole);
//TODO: Temp - Change the above to reconstruct in our order if possible
// Then we can call a move on all 3 bytes
Result[LOutPos] := LWorkBytes[2];
Result[LOutPos + 1] := LWorkBytes[1];
Result[LOutPos + 2] := LWorkBytes[0];
Inc(LOutPos, 3);
// If we dont know how many bytes we need to watch for fill chars. MIME
// is this way.
//
// In best case, the end is not before the end of the input, but the input
// may be right padded with spaces, or even contain the EOL chars.
//
// Because of this we watch for early ends beyond what we originally
// estimated.
if ABytes = -1 then begin
// Must check 3 before 4, if 3 is FillChar, 4 will also be FillChar
if LInBytes[2] = ord(LFillChar) then begin
LEmptyBytes := 2;
Break;
end else if LInBytes[3] = ord(LFillChar) then begin
LEmptyBytes := 1;
Break;
end;
// But with 00E's, we have a length signal for each line so we know
end else if LOutPos > ABytes then begin
LEmptyBytes := LOutPos - ABytes;
Break;
end;
end;
if LEmptyBytes > 0 then
SetLength(Result, LOutSize - LEmptyBytes);
end;
{ TIdEncoder3to4 }
function TIdEncoder3to4.Encode(ASrcStream: TIdStreamRandomAccess; const ABytes: Integer = MaxInt): string;
//TODO: Make this more efficient. Profile it to test, but maybe make single
// calls to ReadBuffer then pull from memory
var
LBuffer: TIdBytes;
LBufSize: Integer;
begin
//CC2: generated "never used" hint: LIn3 := 0;
// SG 28.01.04: removed that check: it's only there to "optimize" the output strin
// SG 28.01.04: and creates more trouble than it solves.
// if (ABytes <> MaxInt) and ((ABytes mod 3) > 0) then begin
// raise EIdException.Create(RSUnevenSizeInEncodeStream);
// end;
// No no - this will read the whole thing into memory and what if its MBs?
// need to load it in smaller buffered chunks MaxInt is WAY too big....
LBufSize := Min(ASrcStream.Size - ASrcStream.Position, ABytes);
if LBufSize > 0 then begin
SetLength(LBuffer, LBufSize);
ASrcStream.ReadBytes(LBuffer, LBufSize);
Result := BytesToString(EncodeIdBytes(LBuffer));
end else begin
Result := '';
end;
end;
function TIdEncoder3to4.EncodeIdBytes(ABuffer: TIdBytes): TIdBytes;
var
LOutSize: Integer;
LLen: integer;
LPos: Integer;
LBufSize: Integer;
LBufDataLen: Integer;
LIn1, LIn2, LIn3: Byte;
LSize: Integer;
LUnit: array[0..3] of Byte; // TIdBytes;
begin
LBufSize := Length(ABuffer);
LOutSize := ((LBufSize + 2) div 3) * 4;
SetLength(Result, LOutSize); // we know that the string will grow by 4/3 adjusted to 3 boundary
//SetLength(LUnit, 4);
LLen := 0;
LPos := 0;
// S.G. 21/10/2003: Copy the relevant bytes into the temporary buffer.
// S.G. 21/10/2003: Record the data length and force exit loop when necessary
while (LPos <= LBufSize) do
begin
LBufDataLen := LBufSize - LPos;
if LBufDataLen > 3 then
begin
LIn1 := ABuffer[LPos];
LIn2 := ABuffer[LPos + 1];
LIn3 := ABuffer[LPos + 2];
LSize := 3;
inc(LPos, 3);
end
else
begin
if LBufDataLen > 2 then
begin
LIn1 := ABuffer[LPos];
LIn2 := ABuffer[LPos + 1];
LIn3 := ABuffer[LPos + 2];
LSize := 3;
LPos := LBufSize + 1; // Make sure we break at end of loop
end
else
begin
if LBufDataLen > 1 then
begin
LIn1 := ABuffer[LPos];
LIn2 := ABuffer[LPos + 1];
LIn3 := 0;
LSize := 2;
LPos := LBufSize + 1; // Make sure we break at end of loop
end
else
begin
LIn1 := ABuffer[LPos];
LIn2 := 0;
LIn3 := 0;
LSize := 1;
LPos := LBufSize + 1; // Make sure we break at end of loop
end;
end;
end;
//EncodeUnit(LIn1, LIn2, LIn3, LUnit);
// inline
LUnit[0] := Ord(FCodingTable[((LIn1 shr 2) and 63) + 1]);
LUnit[1] := Ord(FCodingTable[(((LIn1 shl 4) or (LIn2 shr 4)) and 63) + 1]);
LUnit[2] := Ord(FCodingTable[(((LIn2 shl 2) or (LIn3 shr 6)) and 63) + 1]);
LUnit[3] := Ord(FCodingTable[(Ord(LIn3) and 63) + 1]);
assert(LLen + 4 <= length(Result),
'TIdEncoder3to4.Encode: Calculated length exceeded (expected ' + {do not localize}
inttostr(4 * trunc((LBufSize + 2) / 3)) +
', about to go ' + {do not localize}
inttostr(LLen + 4) +
' at offset ' + {do not localize}
inttostr(LPos) +
' of ' + {do not localize}
inttostr(LBufSize));
//CopyTIdBytes(LUnit, 0, Result, LLen, 4);
Result[LLen] := LUnit[0];
Result[LLen + 1] := LUnit[1];
Result[LLen + 2] := LUnit[2];
Result[LLen + 3] := LUnit[3];
inc(LLen, 4);
if LSize < 3 then begin
Result[LLen - 1] := ord(FillChar);
if LSize = 1 then begin
Result[LLen - 2] := ord(FillChar);
end;
end;
end;
assert(LLen = (4 * trunc((LBufSize + 2) / 3)),
'TIdEncoder3to4.Encode: Calculated length not met (expected ' + {do not localize}
inttostr(4 * trunc((LBufSize + 2) / 3)) +
', finished at ' + {do not localize}
inttostr(LLen + 4) +
', Bufsize = ' + {do not localize}
inttostr(LBufSize));
end;
(*procedure TIdEncoder3to4.EncodeUnit(const AIn1, AIn2, AIn3: Byte; var VOut: TIdBytes);
begin
SetLength(VOut, 4);
VOut[0] := Ord(FCodingTable[((AIn1 shr 2) and 63) + 1]);
VOut[1] := Ord(FCodingTable[(((AIn1 shl 4) or (AIn2 shr 4)) and 63) + 1]);
VOut[2] := Ord(FCodingTable[(((AIn2 shl 2) or (AIn3 shr 6)) and 63) + 1]);
VOut[3] := Ord(FCodingTable[(Ord(AIn3) and 63) + 1]);
end;*)
end.
See also:
INDY (Internet Direct) changes
WideIdEMailAddress is same IdEMailAddress unit but works with unicode strings
IdAttachmentMemory fixed for unicode file names
IdCoderHeader fixed and added decoding headers to unicode
IdMessageCoderMIME fixed RemoveInvalidCharsFromFilename function
IdEMailAddress deleted exception on wrong addresses
|