318 lines
8.9 KiB
Plaintext
318 lines
8.9 KiB
Plaintext
|
{
|
|||
|
$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.20 10/26/2004 11:08:10 PM JPMugaas
|
|||
|
Updated refs.
|
|||
|
|
|||
|
Rev 1.19 27.08.2004 22:03:22 Andreas Hausladen
|
|||
|
Optimized encoders
|
|||
|
speed optimization ("const" for string parameters)
|
|||
|
|
|||
|
Rev 1.18 24/08/2004 10:33:44 CCostelloe
|
|||
|
Was too slow (~45 mins for 2MB down to ~1sec)
|
|||
|
|
|||
|
Rev 1.17 2004.05.20 1:39:22 PM czhower
|
|||
|
Last of the IdStream updates
|
|||
|
|
|||
|
Rev 1.16 2004.05.20 11:37:10 AM czhower
|
|||
|
IdStreamVCL
|
|||
|
|
|||
|
Rev 1.15 2004.05.20 11:13:14 AM czhower
|
|||
|
More IdStream conversions
|
|||
|
|
|||
|
Rev 1.14 2004.05.19 3:06:54 PM czhower
|
|||
|
IdStream / .NET fix
|
|||
|
|
|||
|
Rev 1.13 2/19/2004 11:52:02 PM JPMugaas
|
|||
|
Removed IFDEF's. Moved some functions into IdGlobalProtocols for reuse
|
|||
|
elsewhere.
|
|||
|
|
|||
|
Rev 1.12 2004.02.03 5:45:00 PM czhower
|
|||
|
Name changes
|
|||
|
|
|||
|
Rev 1.11 2004.02.03 2:12:06 PM czhower
|
|||
|
$I path change
|
|||
|
|
|||
|
Rev 1.10 1/22/2004 3:59:14 PM SPerry
|
|||
|
fixed set problems
|
|||
|
|
|||
|
Rev 1.9 11/10/2003 7:41:30 PM BGooijen
|
|||
|
Did all todo's ( TStream to TIdStream mainly )
|
|||
|
|
|||
|
Rev 1.8 2003.10.17 6:14:44 PM czhower
|
|||
|
Fix to match new IdStream
|
|||
|
|
|||
|
Rev 1.7 2003.10.12 3:38:26 PM czhower
|
|||
|
Added path to .inc
|
|||
|
|
|||
|
Rev 1.6 10/12/2003 1:33:42 PM BGooijen
|
|||
|
Compiles on D7 now too
|
|||
|
|
|||
|
Rev 1.5 10/12/2003 12:02:50 PM BGooijen
|
|||
|
DotNet
|
|||
|
|
|||
|
Rev 1.4 6/13/2003 12:07:44 PM JPMugaas
|
|||
|
QP was broken again.
|
|||
|
|
|||
|
Rev 1.3 6/13/2003 07:58:50 AM JPMugaas
|
|||
|
Should now compile with new decoder design.
|
|||
|
|
|||
|
Rev 1.2 6/13/2003 06:17:06 AM JPMugaas
|
|||
|
Should now compil,e.
|
|||
|
|
|||
|
Rev 1.1 12.6.2003 <20>. 12:00:28 DBondzhev
|
|||
|
Fix for . at the begining of new line
|
|||
|
|
|||
|
Rev 1.0 11/14/2002 02:15:00 PM JPMugaas
|
|||
|
|
|||
|
2002-08-13/14 - Johannes Berg
|
|||
|
completely rewrote the Encoder. May do the Decoder later.
|
|||
|
The encoder will add an EOL to the end of the file if it had no EOL
|
|||
|
at start. I can't avoid this due to the design of IdStream.ReadLn,
|
|||
|
but its also no problem, because in transmission this would happen
|
|||
|
anyway.
|
|||
|
|
|||
|
9-17-2001 - J. Peter Mugaas
|
|||
|
made the interpretation of =20 + EOL to mean a hard line break
|
|||
|
soft line breaks are now ignored. It does not make much sense
|
|||
|
in plain text. Soft breaks do not indicate the end of paragraphs unlike
|
|||
|
hard line breaks that do end paragraphs.
|
|||
|
|
|||
|
3-24-2001 - J. Peter Mugaas
|
|||
|
Rewrote the Decoder according to a new design.
|
|||
|
|
|||
|
3-25-2001 - J. Peter Mugaas
|
|||
|
Rewrote the Encoder according to the new design
|
|||
|
}
|
|||
|
|
|||
|
unit IdCoderQuotedPrintable;
|
|||
|
|
|||
|
interface
|
|||
|
|
|||
|
{$i IdCompilerDefines.inc}
|
|||
|
|
|||
|
uses
|
|||
|
Classes,
|
|||
|
IdCoder,
|
|||
|
IdStream,
|
|||
|
SysUtils;
|
|||
|
|
|||
|
type
|
|||
|
TIdDecoderQuotedPrintable = class(TIdDecoder)
|
|||
|
public
|
|||
|
procedure Decode(ASrcStream: TStream; const ABytes: Integer = -1); override;
|
|||
|
end;
|
|||
|
|
|||
|
TIdEncoderQuotedPrintable = class(TIdEncoder)
|
|||
|
public
|
|||
|
procedure Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1); override;
|
|||
|
end;
|
|||
|
|
|||
|
implementation
|
|||
|
|
|||
|
uses
|
|||
|
IdException,
|
|||
|
IdGlobal,
|
|||
|
IdGlobalProtocols;
|
|||
|
|
|||
|
{ TIdDecoderQuotedPrintable }
|
|||
|
|
|||
|
procedure TIdDecoderQuotedPrintable.Decode(ASrcStream: TStream; const ABytes: Integer = -1);
|
|||
|
var
|
|||
|
LBuffer: TIdBytes;
|
|||
|
i : Integer;
|
|||
|
B, DecodedByte : Byte;
|
|||
|
LBufferLen: Integer;
|
|||
|
LBufferIndex: Integer;
|
|||
|
LPos: integer;
|
|||
|
|
|||
|
procedure StripEOLChars;
|
|||
|
var
|
|||
|
j: Integer;
|
|||
|
begin
|
|||
|
for j := 1 to 2 do begin
|
|||
|
if (LBufferIndex >= LBufferLen) or (not ByteIsInEOL(LBuffer, LBufferIndex)) then begin
|
|||
|
Break;
|
|||
|
end;
|
|||
|
Inc(LBufferIndex);
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
function TrimRightWhiteSpace(const ABuf: TIdBytes): TIdBytes;
|
|||
|
var
|
|||
|
LSaveBytes: TIdBytes;
|
|||
|
li, LLen: Integer;
|
|||
|
begin
|
|||
|
SetLength(LSaveBytes, 0);
|
|||
|
LLen := Length(ABuf);
|
|||
|
for li := Length(ABuf)-1 downto 0 do begin
|
|||
|
case ABuf[li] of
|
|||
|
9, 32: ;
|
|||
|
10, 13:
|
|||
|
begin
|
|||
|
//BGO: TODO: Change this
|
|||
|
InsertByte(LSaveBytes, ABuf[li], 0);
|
|||
|
end;
|
|||
|
else
|
|||
|
begin
|
|||
|
Break;
|
|||
|
end;
|
|||
|
end;
|
|||
|
Dec(LLen);
|
|||
|
end;
|
|||
|
SetLength(Result, LLen + Length(LSaveBytes));
|
|||
|
if LLen > 0 then begin
|
|||
|
CopyTIdBytes(ABuf, 0, Result, 0, LLen);
|
|||
|
end;
|
|||
|
if Length(LSaveBytes) > 0 then begin
|
|||
|
CopyTIdBytes(LSaveBytes, 0, Result, LLen, Length(LSaveBytes));
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
procedure WriteByte(AValue: Byte; AWriteEOL: Boolean);
|
|||
|
var
|
|||
|
LTemp: TIdBytes;
|
|||
|
begin
|
|||
|
SetLength(LTemp, iif(AWriteEOL, 3, 1));
|
|||
|
LTemp[0] := AValue;
|
|||
|
if AWriteEOL then begin
|
|||
|
LTemp[1] := Ord(CR);
|
|||
|
LTemp[2] := Ord(LF);
|
|||
|
end;
|
|||
|
TIdStreamHelper.Write(FStream, LTemp);
|
|||
|
end;
|
|||
|
|
|||
|
begin
|
|||
|
LBufferLen := IndyLength(ASrcStream, ABytes);
|
|||
|
if LBufferLen <= 0 then begin
|
|||
|
Exit;
|
|||
|
end;
|
|||
|
SetLength(LBuffer, LBufferLen);
|
|||
|
TIdStreamHelper.ReadBytes(ASrcStream, LBuffer, LBufferLen);
|
|||
|
{ when decoding a Quoted-Printable body, any trailing
|
|||
|
white space on a line must be deleted, - RFC 1521}
|
|||
|
LBuffer := TrimRightWhiteSpace(LBuffer);
|
|||
|
LBufferLen := Length(LBuffer);
|
|||
|
LBufferIndex := 0;
|
|||
|
while LBufferIndex < LBufferLen do begin
|
|||
|
LPos := ByteIndex(Ord('='), LBuffer, LBufferIndex);
|
|||
|
if LPos = -1 then begin
|
|||
|
if Assigned(FStream) then begin
|
|||
|
TIdStreamHelper.Write(FStream, LBuffer, -1, LBufferIndex);
|
|||
|
end;
|
|||
|
Break;
|
|||
|
end;
|
|||
|
if Assigned(FStream) then begin
|
|||
|
TIdStreamHelper.Write(FStream, LBuffer, LPos-LBufferIndex, LBufferIndex);
|
|||
|
end;
|
|||
|
LBufferIndex := LPos+1;
|
|||
|
// process any following hexidecimal representation
|
|||
|
if LBufferIndex < LBufferLen then begin
|
|||
|
i := 0;
|
|||
|
DecodedByte := 0;
|
|||
|
while LBufferIndex < LBufferLen do begin
|
|||
|
B := LBuffer[LBufferIndex];
|
|||
|
case B of
|
|||
|
48..57: //0-9 {Do not Localize}
|
|||
|
DecodedByte := (DecodedByte shl 4) or (B - 48); {Do not Localize}
|
|||
|
65..70: //A-F {Do not Localize}
|
|||
|
DecodedByte := (DecodedByte shl 4) or (B - 65 + 10); {Do not Localize}
|
|||
|
97..102://a-f {Do not Localize}
|
|||
|
DecodedByte := (DecodedByte shl 4) or (B - 97 + 10); {Do not Localize}
|
|||
|
else
|
|||
|
Break;
|
|||
|
end;
|
|||
|
Inc(i);
|
|||
|
Inc(LBufferIndex);
|
|||
|
if i > 1 then begin
|
|||
|
Break;
|
|||
|
end;
|
|||
|
end;
|
|||
|
if i > 0 then begin
|
|||
|
//if =20 + EOL, this is a hard line break after a space
|
|||
|
if (DecodedByte = 32) and (LBufferIndex < LBufferLen) and ByteIsInEOL(LBuffer, LBufferIndex) then begin
|
|||
|
if Assigned(FStream) then begin
|
|||
|
WriteByte(DecodedByte, True);
|
|||
|
end;
|
|||
|
StripEOLChars;
|
|||
|
end else begin
|
|||
|
if Assigned(FStream) then begin
|
|||
|
WriteByte(DecodedByte, False);
|
|||
|
end;
|
|||
|
end;
|
|||
|
end else begin
|
|||
|
//ignore soft line breaks -
|
|||
|
StripEOLChars;
|
|||
|
end;
|
|||
|
end;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
{ TIdEncoderQuotedPrintable }
|
|||
|
|
|||
|
function CharToHex(const AChar: Char): String;
|
|||
|
begin
|
|||
|
Result := '=' + ByteToHex(Ord(AChar)); {do not localize}
|
|||
|
end;
|
|||
|
|
|||
|
procedure TIdEncoderQuotedPrintable.Encode(ASrcStream, ADestStream: TStream; const ABytes: Integer = -1);
|
|||
|
const
|
|||
|
SafeChars = '!"#$%&''()*+,-./0123456789:;<>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmonpqrstuvwxyz{|}~';
|
|||
|
HalfSafeChars = #9' ';
|
|||
|
// Rule #2, #3
|
|||
|
var
|
|||
|
I, CurrentLen: Integer;
|
|||
|
LSourceSize: TIdStreamSize;
|
|||
|
S, SourceLine: String;
|
|||
|
LEncoding: IIdTextEncoding;
|
|||
|
begin
|
|||
|
//ie while not eof
|
|||
|
LSourceSize := ASrcStream.Size;
|
|||
|
if ASrcStream.Position < LSourceSize then begin
|
|||
|
LEncoding := IndyTextEncoding_8Bit;
|
|||
|
repeat
|
|||
|
SourceLine := ReadLnFromStream(ASrcStream, -1, False, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF});
|
|||
|
CurrentLen := 0;
|
|||
|
for I := 1 to Length(SourceLine) do begin
|
|||
|
if not CharIsInSet(SourceLine, I, SafeChars) then
|
|||
|
begin
|
|||
|
if CharIsInSet(SourceLine, I, HalfSafeChars) and (I < Length(SourceLine)) then begin
|
|||
|
S := SourceLine[I];
|
|||
|
end else begin
|
|||
|
S := CharToHex(SourceLine[I]);
|
|||
|
end;
|
|||
|
end
|
|||
|
else if ((CurrentLen = 0) or (CurrentLen >= 70)) and (SourceLine[I] = '.') then begin {do not localize}
|
|||
|
S := CharToHex(SourceLine[I]);
|
|||
|
end else begin
|
|||
|
S := SourceLine[I];
|
|||
|
end;
|
|||
|
WriteStringToStream(ADestStream, S);
|
|||
|
Inc(CurrentLen, Length(S));
|
|||
|
if CurrentLen >= 70 then begin
|
|||
|
WriteStringToStream(ADestStream, '='+EOL); {Do not Localize}
|
|||
|
CurrentLen := 0;
|
|||
|
end;
|
|||
|
end;
|
|||
|
WriteStringToStream(ADestStream, EOL);
|
|||
|
until ASrcStream.Position >= LSourceSize;
|
|||
|
end;
|
|||
|
end;
|
|||
|
|
|||
|
end.
|