{ $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: 10097: IdCoderQuotedPrintable.pas { { Rev 1.1 12.6.2003 ã. 11:46:58 DBondzhev { Fix for '.' when it is going to be alone on the next line. This breaks the { message parts and messagess are truncated or incorrectly recived. } { { Rev 1.0 2002.11.12 10:32:46 PM czhower } unit IdCoderQuotedPrintable; {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} interface uses Classes, IdCoder; type TIdDecoderQuotedPrintable = class(TIdDecoder) public procedure DecodeToStream(AIn: string; ADest: TStream); override; end; TIdEncoderQuotedPrintable = class(TIdEncoder) public function Encode(ASrcStream: TStream; const ABytes: integer = MaxInt): string; override; end; implementation uses IdGlobal, SysUtils; { TIdDecoderQuotedPrintable } procedure TIdDecoderQuotedPrintable.DecodeToStream(AIn: string; ADest: TStream); var Buffer, Line, Hex : String; i : Integer; b : Byte; const Numbers = '01234567890ABCDEF'; {Do not Localize} procedure StripEOLChars; var j : Integer; begin for j := 1 to 2 do begin if (Length(Buffer) > 0) and (Pos(Buffer[1],EOL) > 0) then begin Delete(Buffer,1,1); end else begin break; end; end; end; function TrimRightWhiteSpace(const Str : String) : String; var i : integer; LSaveStr : String; begin SetLength(LSaveStr,0); i := Length(Str); while (i > 0) and (Str[i] in [#9,#32]+[#10,#13]) do begin if Str[i] in [#10,#13] then begin Insert(Str[i],LSaveStr,1); end; dec(i); end; result := Copy(Str,1,i) + LSaveStr; end; begin Line := ''; {Do not Localize} { when decoding a Quoted-Printable body, any trailing white space on a line must be deleted, - RFC 1521} Buffer := TrimRightWhiteSpace(AIn); while Length(Buffer) > 0 do begin Line := Line + Fetch(Buffer,'='); {Do not Localize} // process any following hexidecimal represntation if Length(Buffer) > 0 then begin Hex := ''; {Do not Localize} for i := 0 to 1 do begin If IndyPos(UpperCase(Buffer[1]),Numbers) <> 0 then begin Hex := Hex + Copy(Buffer,1,1); Delete(Buffer,1,1); end else begin break; end; end; if (Length(Hex) > 0) then begin b := StrToInt('$'+Hex); {Do not Localize} //if =20 + EOL, this is a hard line break after a space if (b = 32) and (Length(Buffer) > 0) and (Pos(Buffer[1],EOL) > 0) then begin Line := Line + Char(b) + EOL; StripEOLChars; end else begin Line := Line + Char(b); end; end else begin //ignore soft line breaks - StripEOLChars; end; end; end; if Length(Line) > 0 then begin ADest.Write(Line[1],Length(Line)); end; end; { TIdEncoderQuotedPrintable } function TIdEncoderQuotedPrintable.Encode(ASrcStream: TStream; const ABytes: integer): string; //TODO: Change this to be more efficient - dont read the whole data in ahead of time as it may // be quite large const BUF_SIZE = 8192; var i, LDataSize, LBytesRead, LBufSize : Integer; Buffer : Array [1..BUF_SIZE] of char; Line : String; st : TStrings; s : String; Procedure NewLine; begin Line := Line + '='; {Do not Localize} st.Add(Line); Line := ''; {Do not Localize} end; Function QPHex(c : Char) : String; begin Result := '='+ IntToHex(Ord(c),2); {Do not Localize} end; begin st := TStringList.Create; try Result := ''; {Do not Localize} Line := ''; {Do not Localize} LBytesRead := 0; LDataSize := ASrcStream.Size - ASrcStream.Position; if LDataSize > ABytes then begin LDataSize := ABytes; end; if (LDataSize > 0) then begin while LBytesRead < LDataSize do begin if (LDataSize - LBytesRead) > BUF_SIZE then begin LBufSize := BUF_SIZE end else begin LBufSize := LDataSize - LBytesRead; end; ASrcStream.Read(Buffer[1],LBufSize); LBytesRead := LBytesRead + LBufSize; For i := 1 to LBufSize do begin case Buffer[i] of // Special case when '.' is about to be alone on the next line. '.': begin if Line = '' then begin s := QPHex(Buffer[i]); Line := Line + s; end else begin Line := Line + Buffer[i]; end; end; ' ', TAB: {Do not Localize} If (i < Length(Buffer)) and (Buffer[i+1] in [#10,#13]) then begin //Modified by Dennies Chang. // Line := Line + QPHex(Buffer[i]); s := QPHex(Buffer[i]); Line := Line + s; end else Line := Line + Buffer[i]; '=' : {Do not Localize} begin //Modified by Dennies Chang. //Line := Line + QPHex(Buffer[i]); s := QPHex(Buffer[i]); Line := Line + s; end else begin if ((Buffer[i] >= #33 ) and (Buffer[i] <= #60 )) or ((Buffer[i] >= #62) and (Buffer[i] <= #126 )) then begin Line := Line + Buffer[i]; end else begin if Buffer[i] in [#10,#13] then begin Line := Line + Buffer[i] end else begin Line := Line + QPHex(Buffer[i]); end; end; //...else end; //..else end; //case buffer[i] of if Length(Line) > 71 then begin NewLine; end; //if Length(Line > 71 then end; //For i := 1 to LBufSize do end; //while LBytesRead < LDataSize do end; //if (LDataSize > 0) then {This ensures that the remaining is added to the TStrings} if Length(Line) >0 then begin st.Add(Line); end; Result := st.Text; //Delete an extra system EOL that was added by the TStrings itself //The EOL varies from system to system i := Length(sLineBreak); if (Length(Result)>i) then begin Delete(Result,Length(Result) - i+1,i); end; finally FreeAndNil(st); end; end; end.