1001 lines
36 KiB
ObjectPascal
1001 lines
36 KiB
ObjectPascal
{**************************************************************************************************}
|
|
{ }
|
|
{ Project JEDI Code Library (JCL) }
|
|
{ }
|
|
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
|
|
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
|
|
{ License at http://www.mozilla.org/MPL/ }
|
|
{ }
|
|
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
|
|
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
|
|
{ and limitations under the License. }
|
|
{ }
|
|
{ The Original Code is JclMime.pas. }
|
|
{ }
|
|
{ The Initial Developer of the Original Code is Ralf Junker. }
|
|
{ Portions created by Ralf Junker are Copyright (C) Ralf Junker. All rights reserved. }
|
|
{ }
|
|
{ Contributors: }
|
|
{ Marcel van Brakel }
|
|
{ Ralf Junker }
|
|
{ Robert Marquardt (marquardt) }
|
|
{ Robert Rossmair (rrossmair) }
|
|
{ Matthias Thoma (mthoma) }
|
|
{ Petr Vones (pvones) }
|
|
{ }
|
|
{**************************************************************************************************}
|
|
{ }
|
|
{ Lightning fast Mime (Base64) Encoding and Decoding routines. Coded by Ralf Junker }
|
|
{ (ralfjunker att gmx dott de). }
|
|
{ }
|
|
{**************************************************************************************************}
|
|
{ Migration Guide from JCL 1.90 and older: }
|
|
{ }
|
|
{ These new functions now support line breaks (CRLF) as required by RFC 2045. }
|
|
{ Inserting line breaks is the default behaviour in RFC 2045 therefor the encoding functions now }
|
|
{ encode with line breaks. }
|
|
{ }
|
|
{ This may require changes to your code: }
|
|
{ Encoding without inserting line breaks is possible using the corresponding NoCRLF procedures: }
|
|
{ }
|
|
{ MimeEncode => MimeEncodeNoCRLF }
|
|
{ MimeEncodeString => MimeEncodeStringNoCRLF }
|
|
{ MimeEncodeStream => MimeEncodeStreamNoCRLF }
|
|
{ MimeEncodedSize => MimeEncodedSizeNoCRLF }
|
|
{ }
|
|
{**************************************************************************************************}
|
|
|
|
// Last modified: $Date: 2005/05/05 20:08:43 $
|
|
// For history see end of file
|
|
|
|
unit JclMime;
|
|
|
|
{$I jcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF CLR}
|
|
System.Text,
|
|
{$ENDIF CLR}
|
|
SysUtils, Classes,
|
|
JclBase;
|
|
|
|
function MimeEncodeString(const S: AnsiString): AnsiString;
|
|
function MimeEncodeStringNoCRLF(const S: AnsiString): AnsiString;
|
|
function MimeDecodeString(const S: AnsiString): AnsiString;
|
|
function MimeEncodedSize(const InputSize: Cardinal): Cardinal;
|
|
function MimeEncodedSizeNoCRLF(const InputSize: Cardinal): Cardinal;
|
|
function MimeDecodedSize(const InputSize: Cardinal): Cardinal;
|
|
procedure DecodeHttpBasicAuthentication(const BasicCredentials: string;
|
|
out UserId, PassWord: string);
|
|
{$IFDEF CLR}
|
|
procedure MimeEncode(const InputBuffer: TDynByteArray; InputOffset: Cardinal;
|
|
const InputByteCount: Cardinal; out OutputBuffer: TDynByteArray; OutputOffset: Cardinal = 0); overload;
|
|
procedure MimeEncodeNoCRLF(const InputBuffer: TDynByteArray; InputOffset: Cardinal;
|
|
const InputByteCount: Cardinal; out OutputBuffer: TDynByteArray; OutputOffset: Cardinal = 0); overload;
|
|
procedure MimeEncodeFullLines(const InputBuffer: TDynByteArray; InputOffset: Cardinal;
|
|
const InputByteCount: Cardinal; out OutputBuffer: TDynByteArray; OutputOffset: Cardinal = 0); overload;
|
|
function MimeDecode(const InputBuffer: TDynByteArray; InputOffset: Cardinal;
|
|
const InputByteCount: Cardinal; out OutputBuffer: TDynByteArray; OutputOffset: Cardinal = 0): Cardinal; overload;
|
|
function MimeDecodePartial(const InputBuffer: TDynByteArray; InputOffset: Cardinal;
|
|
const InputByteCount: Cardinal; out OutputBuffer: TDynByteArray; OutputOffset: Cardinal;
|
|
var ByteBuffer: Cardinal; var ByteBufferSpace: Cardinal): Cardinal; overload;
|
|
function MimeDecodePartialEnd(out OutputBuffer: TDynByteArray; OutputOffset: Cardinal;
|
|
const ByteBuffer: Cardinal; const ByteBufferSpace: Cardinal): Cardinal; overload;
|
|
|
|
procedure MimeEncode(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray); overload;
|
|
procedure MimeEncodeNoCRLF(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray); overload;
|
|
procedure MimeEncodeFullLines(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray); overload;
|
|
function MimeDecode(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray): Cardinal; overload;
|
|
function MimeDecodePartial(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray; var ByteBuffer: Cardinal; var ByteBufferSpace: Cardinal): Cardinal; overload;
|
|
function MimeDecodePartialEnd(out OutputBuffer: TDynByteArray; const ByteBuffer: Cardinal;
|
|
const ByteBufferSpace: Cardinal): Cardinal; overload;
|
|
|
|
{$ELSE}
|
|
procedure MimeEncode(const InputBuffer; const InputByteCount: Cardinal; out OutputBuffer);
|
|
procedure MimeEncodeNoCRLF(const InputBuffer; const InputByteCount: Cardinal; out OutputBuffer);
|
|
procedure MimeEncodeFullLines(const InputBuffer; const InputByteCount: Cardinal; out OutputBuffer);
|
|
function MimeDecode(const InputBuffer; const InputByteCount: Cardinal; out OutputBuffer): Cardinal;
|
|
function MimeDecodePartial(const InputBuffer; const InputByteCount: Cardinal; out OutputBuffer;
|
|
var ByteBuffer: Cardinal; var ByteBufferSpace: Cardinal): Cardinal;
|
|
function MimeDecodePartialEnd(out OutputBuffer; const ByteBuffer: Cardinal;
|
|
const ByteBufferSpace: Cardinal): Cardinal;
|
|
{$ENDIF CLR}
|
|
procedure MimeEncodeFile(const InputFileName, OutputFileName: AnsiString);
|
|
procedure MimeEncodeFileNoCRLF(const InputFileName, OutputFileName: AnsiString);
|
|
procedure MimeDecodeFile(const InputFileName, OutputFileName: AnsiString);
|
|
procedure MimeEncodeStream(const InputStream: TStream; const OutputStream: TStream);
|
|
procedure MimeEncodeStreamNoCRLF(const InputStream: TStream; const OutputStream: TStream);
|
|
procedure MimeDecodeStream(const InputStream: TStream; const OutputStream: TStream);
|
|
|
|
const
|
|
MIME_ENCODED_LINE_BREAK = 76;
|
|
MIME_DECODED_LINE_BREAK = MIME_ENCODED_LINE_BREAK div 4 * 3;
|
|
MIME_BUFFER_SIZE = MIME_DECODED_LINE_BREAK * 3 * 4 * 4;
|
|
|
|
implementation
|
|
|
|
// Caution: For MimeEncodeStream and all other kinds of multi-buffered
|
|
// Mime encodings (i.e. Files etc.), BufferSize must be set to a multiple of 3.
|
|
// Even though the implementation of the Mime decoding routines below
|
|
// do not require a particular buffer size, they work fastest with sizes of
|
|
// multiples of four. The chosen size is a multiple of 3 and of 4 as well.
|
|
// The following numbers are, in addition, also divisible by 1024:
|
|
// $2400, $3000, $3C00, $4800, $5400, $6000, $6C00.
|
|
|
|
const
|
|
BUFFER_SIZE = $3000;
|
|
EqualSign = Byte('=');
|
|
|
|
const
|
|
{ The mime encoding table. Do not alter. }
|
|
MIME_ENCODE_TABLE: array [0..63] of Byte = (
|
|
065, 066, 067, 068, 069, 070, 071, 072, // 00 - 07
|
|
073, 074, 075, 076, 077, 078, 079, 080, // 08 - 15
|
|
081, 082, 083, 084, 085, 086, 087, 088, // 16 - 23
|
|
089, 090, 097, 098, 099, 100, 101, 102, // 24 - 31
|
|
103, 104, 105, 106, 107, 108, 109, 110, // 32 - 39
|
|
111, 112, 113, 114, 115, 116, 117, 118, // 40 - 47
|
|
119, 120, 121, 122, 048, 049, 050, 051, // 48 - 55
|
|
052, 053, 054, 055, 056, 057, 043, 047); // 56 - 63
|
|
|
|
MIME_PAD_CHAR = Byte('=');
|
|
|
|
MIME_DECODE_TABLE: array [Byte] of Cardinal = (
|
|
255, 255, 255, 255, 255, 255, 255, 255, // 0 - 7
|
|
255, 255, 255, 255, 255, 255, 255, 255, // 8 - 15
|
|
255, 255, 255, 255, 255, 255, 255, 255, // 16 - 23
|
|
255, 255, 255, 255, 255, 255, 255, 255, // 24 - 31
|
|
255, 255, 255, 255, 255, 255, 255, 255, // 32 - 39
|
|
255, 255, 255, 062, 255, 255, 255, 063, // 40 - 47
|
|
052, 053, 054, 055, 056, 057, 058, 059, // 48 - 55
|
|
060, 061, 255, 255, 255, 255, 255, 255, // 56 - 63
|
|
255, 000, 001, 002, 003, 004, 005, 006, // 64 - 71
|
|
007, 008, 009, 010, 011, 012, 013, 014, // 72 - 79
|
|
015, 016, 017, 018, 019, 020, 021, 022, // 80 - 87
|
|
023, 024, 025, 255, 255, 255, 255, 255, // 88 - 95
|
|
255, 026, 027, 028, 029, 030, 031, 032, // 96 - 103
|
|
033, 034, 035, 036, 037, 038, 039, 040, // 104 - 111
|
|
041, 042, 043, 044, 045, 046, 047, 048, // 112 - 119
|
|
049, 050, 051, 255, 255, 255, 255, 255, // 120 - 127
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255,
|
|
255, 255, 255, 255, 255, 255, 255, 255);
|
|
|
|
{$IFDEF CLR}
|
|
procedure MimeEncode(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray);
|
|
begin
|
|
MimeEncode(InputBuffer, 0, InputByteCount, OutputBuffer, 0);
|
|
end;
|
|
|
|
procedure MimeEncodeNoCRLF(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray);
|
|
begin
|
|
MimeEncodeNoCRLF(InputBuffer, 0, InputByteCount, OutputBuffer, 0);
|
|
end;
|
|
|
|
procedure MimeEncodeFullLines(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray);
|
|
begin
|
|
MimeEncodeFullLines(InputBuffer, 0, InputByteCount, OutputBuffer, 0);
|
|
end;
|
|
|
|
function MimeDecode(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray): Cardinal;
|
|
begin
|
|
Result := MimeDecode(InputBuffer, 0, InputByteCount, OutputBuffer, 0);
|
|
end;
|
|
|
|
function MimeDecodePartial(const InputBuffer: TDynByteArray; const InputByteCount: Cardinal;
|
|
out OutputBuffer: TDynByteArray; var ByteBuffer: Cardinal; var ByteBufferSpace: Cardinal): Cardinal;
|
|
begin
|
|
Result := MimeDecodePartial(InputBuffer, 0, InputByteCount, OutputBuffer, 0, ByteBuffer, ByteBufferSpace);
|
|
end;
|
|
|
|
function MimeDecodePartialEnd(out OutputBuffer: TDynByteArray; const ByteBuffer: Cardinal;
|
|
const ByteBufferSpace: Cardinal): Cardinal;
|
|
begin
|
|
Result := MimeDecodePartialEnd(OutputBuffer, 0, ByteBuffer, ByteBufferSpace);
|
|
end;
|
|
{$ELSE}
|
|
type
|
|
PByte4 = ^TByte4;
|
|
TByte4 = packed record
|
|
B1: Byte;
|
|
B2: Byte;
|
|
B3: Byte;
|
|
B4: Byte;
|
|
end;
|
|
|
|
PByte3 = ^TByte3;
|
|
TByte3 = packed record
|
|
B1: Byte;
|
|
B2: Byte;
|
|
B3: Byte;
|
|
end;
|
|
{$ENDIF CLR}
|
|
|
|
// Wrapper functions & procedures
|
|
function MimeEncodeString(const S: AnsiString): AnsiString;
|
|
var
|
|
L: Cardinal;
|
|
{$IFDEF CLR}
|
|
Bytes: TDynByteArray;
|
|
{$ENDIF CLR}
|
|
begin
|
|
if S <> '' then
|
|
begin
|
|
{$IFDEF CLR}
|
|
L := Length(S);
|
|
SetLength(Bytes, MimeEncodedSize(L));
|
|
MimeEncode(BytesOf(S), 0, L, Bytes, 0);
|
|
Result := Bytes;
|
|
{$ELSE}
|
|
L := PCardinal(Cardinal(S) - 4)^;
|
|
SetLength(Result, MimeEncodedSize(L));
|
|
MimeEncode(Pointer(S)^, L, Pointer(Result)^);
|
|
{$ENDIF CLR}
|
|
end
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
function MimeEncodeStringNoCRLF(const S: AnsiString): AnsiString;
|
|
var
|
|
L: Cardinal;
|
|
{$IFDEF CLR}
|
|
Bytes: TDynByteArray;
|
|
{$ENDIF CLR}
|
|
begin
|
|
if S <> '' then
|
|
begin
|
|
{$IFDEF CLR}
|
|
L := Length(S);
|
|
SetLength(Bytes, MimeEncodedSizeNoCRLF(L));
|
|
MimeEncodeNoCRLF(BytesOf(S), 0, L, Bytes, 0);
|
|
Result := Bytes;
|
|
{$ELSE}
|
|
L := PCardinal(Cardinal(S) - 4)^;
|
|
SetLength(Result, MimeEncodedSizeNoCRLF(L));
|
|
MimeEncodeNoCRLF(Pointer(S)^, L, Pointer(Result)^);
|
|
{$ENDIF CLR}
|
|
end
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
function MimeDecodeString(const S: AnsiString): AnsiString;
|
|
var
|
|
ByteBuffer, ByteBufferSpace: Cardinal;
|
|
L: Cardinal;
|
|
{$IFDEF CLR}
|
|
Bytes: TDynByteArray;
|
|
{$ENDIF CLR}
|
|
begin
|
|
if S <> '' then
|
|
begin
|
|
{$IFDEF CLR}
|
|
L := Length(S);
|
|
SetLength(Bytes, MimeEncodedSize(L));
|
|
ByteBuffer := 0;
|
|
ByteBufferSpace := 4;
|
|
L := MimeDecodePartial(BytesOf(S), 0, L, Bytes, 0, ByteBuffer, ByteBufferSpace);
|
|
Inc(L, MimeDecodePartialEnd(Bytes, 0 + L, ByteBuffer, ByteBufferSpace));
|
|
SetLength(Bytes, L);
|
|
Result := Bytes;
|
|
{$ELSE}
|
|
L := PCardinal(Cardinal(S) - 4)^;
|
|
SetLength(Result, MimeDecodedSize(L));
|
|
ByteBuffer := 0;
|
|
ByteBufferSpace := 4;
|
|
L := MimeDecodePartial(Pointer(S)^, L, Pointer(Result)^, ByteBuffer, ByteBufferSpace);
|
|
Inc(L, MimeDecodePartialEnd(Pointer(Cardinal(Result) + L)^, ByteBuffer, ByteBufferSpace));
|
|
SetLength(Result, L);
|
|
{$ENDIF CLR}
|
|
end
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
procedure DecodeHttpBasicAuthentication(const BasicCredentials: string; out UserId, PassWord: string);
|
|
const
|
|
LBasic = 6; { Length ('Basic ') }
|
|
{$IFDEF CLR}
|
|
var
|
|
Index: Cardinal;
|
|
Decoded: TDynByteArray;
|
|
I, L: Cardinal;
|
|
begin
|
|
UserId := '';
|
|
PassWord := '';
|
|
L := Length(BasicCredentials);
|
|
if L < LBasic then // includes "L = 0"
|
|
Exit;
|
|
Dec(L, LBasic);
|
|
Index := LBasic;
|
|
|
|
SetLength(Decoded, MimeDecodedSize(L));
|
|
L := MimeDecode(BytesOf(BasicCredentials), Index, L, Decoded, 0);
|
|
|
|
{ Look for colon (':'). }
|
|
I := 0;
|
|
while (L > 0) and (Char(Decoded[I]) <> ':') do
|
|
begin
|
|
Inc(I);
|
|
Dec(L);
|
|
end;
|
|
|
|
{ Store UserId and Password. }
|
|
UserId := Copy(Decoded, 0, I);
|
|
if L > 1 then
|
|
PassWord := Copy(Decoded, I + 1, L - 1)
|
|
else
|
|
PassWord := '';
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
DecodedPtr, P: PAnsiChar;
|
|
I, L: Cardinal;
|
|
begin
|
|
UserId := '';
|
|
PassWord := '';
|
|
|
|
P := Pointer(BasicCredentials);
|
|
if P = nil then
|
|
Exit;
|
|
|
|
L := Cardinal(Pointer(P - 4)^);
|
|
if L <= LBasic then
|
|
Exit;
|
|
|
|
Dec(L, LBasic);
|
|
Inc(P, LBasic);
|
|
|
|
GetMem(DecodedPtr, MimeDecodedSize(L));
|
|
L := MimeDecode(P^, L, DecodedPtr^);
|
|
|
|
{ Look for colon (':'). }
|
|
I := 0;
|
|
P := DecodedPtr;
|
|
while (L > 0) and (P[I] <> ':') do
|
|
begin
|
|
Inc(I);
|
|
Dec(L);
|
|
end;
|
|
|
|
{ Store UserId and Password. }
|
|
SetString(UserId, DecodedPtr, I);
|
|
if L > 1 then
|
|
SetString(PassWord, DecodedPtr + I + 1, L - 1)
|
|
else
|
|
PassWord := '';
|
|
|
|
FreeMem(DecodedPtr);
|
|
end;
|
|
{$ENDIF CLR}
|
|
|
|
// Helper functions
|
|
function MimeEncodedSize(const InputSize: Cardinal): Cardinal;
|
|
begin
|
|
if InputSize > 0 then
|
|
Result := (InputSize + 2) div 3 * 4 + (InputSize - 1) div MIME_DECODED_LINE_BREAK * 2
|
|
else
|
|
Result := InputSize;
|
|
end;
|
|
|
|
function MimeEncodedSizeNoCRLF(const InputSize: Cardinal): Cardinal;
|
|
begin
|
|
Result := (InputSize + 2) div 3 * 4;
|
|
end;
|
|
|
|
function MimeDecodedSize(const InputSize: Cardinal): Cardinal;
|
|
begin
|
|
Result := (InputSize + 3) div 4 * 3;
|
|
end;
|
|
|
|
|
|
// Primary functions & procedures
|
|
procedure MimeEncode(const InputBuffer {$IFDEF CLR}: TDynByteArray; InputOffset: Cardinal {$ENDIF CLR};
|
|
const InputByteCount: Cardinal;
|
|
out OutputBuffer {$IFDEF CLR}: TDynByteArray; OutputOffset: Cardinal {$ENDIF CLR});
|
|
var
|
|
IDelta, ODelta: Cardinal;
|
|
begin
|
|
{$IFDEF CLR}
|
|
MimeEncodeFullLines(InputBuffer, InputOffset, InputByteCount, OutputBuffer, OutputOffset);
|
|
{$ELSE}
|
|
MimeEncodeFullLines(InputBuffer, InputByteCount, OutputBuffer);
|
|
{$ENDIF CLR}
|
|
IDelta := InputByteCount div MIME_DECODED_LINE_BREAK; // Number of lines processed so far.
|
|
ODelta := IDelta * (MIME_ENCODED_LINE_BREAK + 2);
|
|
IDelta := IDelta * MIME_DECODED_LINE_BREAK;
|
|
{$IFDEF CLR}
|
|
MimeEncodeNoCRLF(InputBuffer, InputOffset + IDelta, InputByteCount - IDelta, OutputBuffer, OutputOffset + ODelta);
|
|
{$ELSE}
|
|
MimeEncodeNoCRLF(Pointer(Cardinal(@InputBuffer) + IDelta)^, InputByteCount - IDelta, Pointer(Cardinal(@OutputBuffer) + ODelta)^);
|
|
{$ENDIF CLR}
|
|
end;
|
|
|
|
{$IFDEF CLR}
|
|
procedure MimeEncodeFullLines(const InputBuffer: TDynByteArray; InputOffset: Cardinal;
|
|
const InputByteCount: Cardinal; out OutputBuffer: TDynByteArray; OutputOffset: Cardinal);
|
|
var
|
|
B, InnerLimit, OuterLimit: Cardinal;
|
|
InIndex: Cardinal;
|
|
OutIndex: Cardinal;
|
|
begin
|
|
{ Do we have enough input to encode a full line? }
|
|
if InputByteCount < MIME_DECODED_LINE_BREAK then
|
|
Exit;
|
|
|
|
InIndex := InputOffset;
|
|
OutIndex := OutputOffset;
|
|
|
|
InnerLimit := InIndex;
|
|
Inc(InnerLimit, MIME_DECODED_LINE_BREAK);
|
|
|
|
OuterLimit := InIndex;
|
|
Inc(OuterLimit, InputByteCount);
|
|
|
|
{ Multiple line loop. }
|
|
repeat
|
|
{ Single line loop. }
|
|
repeat
|
|
{ Read 3 bytes from InputBuffer. }
|
|
B := InputBuffer[InIndex + 0];
|
|
B := B shl 8;
|
|
B := B or InputBuffer[InIndex + 1];
|
|
B := B shl 8;
|
|
B := B or InputBuffer[InIndex + 2];
|
|
Inc(InIndex);
|
|
{ Write 4 bytes to OutputBuffer (in reverse order). }
|
|
OutputBuffer[OutIndex + 3] := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutputBuffer[OutIndex + 2] := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutputBuffer[OutIndex + 1] := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutputBuffer[OutIndex + 0] := MIME_ENCODE_TABLE[B];
|
|
Inc(OutIndex);
|
|
until InIndex >= InnerLimit;
|
|
|
|
{ Write line break (CRLF). }
|
|
OutputBuffer[OutIndex + 0] := 13;
|
|
OutputBuffer[OutIndex + 1] := 10;
|
|
Inc(OutIndex, 2);
|
|
|
|
Inc(InnerLimit, MIME_DECODED_LINE_BREAK);
|
|
until InnerLimit > OuterLimit;
|
|
end;
|
|
{$ELSE}
|
|
procedure MimeEncodeFullLines(const InputBuffer; const InputByteCount: Cardinal; out OutputBuffer);
|
|
var
|
|
B, InnerLimit, OuterLimit: Cardinal;
|
|
InPtr: PByte3;
|
|
OutPtr: PByte4;
|
|
begin
|
|
{ Do we have enough input to encode a full line? }
|
|
if InputByteCount < MIME_DECODED_LINE_BREAK then
|
|
Exit;
|
|
|
|
InPtr := @InputBuffer;
|
|
OutPtr := @OutputBuffer;
|
|
|
|
InnerLimit := Cardinal(InPtr);
|
|
Inc(InnerLimit, MIME_DECODED_LINE_BREAK);
|
|
|
|
OuterLimit := Cardinal(InPtr);
|
|
Inc(OuterLimit, InputByteCount);
|
|
|
|
{ Multiple line loop. }
|
|
repeat
|
|
{ Single line loop. }
|
|
repeat
|
|
{ Read 3 bytes from InputBuffer. }
|
|
B := InPtr^.B1;
|
|
B := B shl 8;
|
|
B := B or InPtr^.B2;
|
|
B := B shl 8;
|
|
B := B or InPtr^.B3;
|
|
Inc(InPtr);
|
|
{ Write 4 bytes to OutputBuffer (in reverse order). }
|
|
OutPtr^.B4 := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutPtr^.B3 := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutPtr^.B2 := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutPtr^.B1 := MIME_ENCODE_TABLE[B];
|
|
Inc(OutPtr);
|
|
until Cardinal(InPtr) >= InnerLimit;
|
|
|
|
{ Write line break (CRLF). }
|
|
OutPtr^.B1 := 13;
|
|
OutPtr^.B2 := 10;
|
|
Inc(Cardinal(OutPtr), 2);
|
|
|
|
Inc(InnerLimit, MIME_DECODED_LINE_BREAK);
|
|
until InnerLimit > OuterLimit;
|
|
end;
|
|
{$ENDIF CLR}
|
|
|
|
{$IFDEF CLR}
|
|
procedure MimeEncodeNoCRLF(const InputBuffer: TDynByteArray; InputOffset: Cardinal;
|
|
const InputByteCount: Cardinal; out OutputBuffer: TDynByteArray; OutputOffset: Cardinal);
|
|
var
|
|
B, InnerLimit, OuterLimit: Cardinal;
|
|
InIndex: Cardinal;
|
|
OutIndex: Cardinal;
|
|
begin
|
|
if InputByteCount = 0 then
|
|
Exit;
|
|
|
|
InIndex := InputOffset;
|
|
OutIndex := OutputOffset;
|
|
|
|
OuterLimit := InputByteCount div 3 * 3;
|
|
|
|
InnerLimit := InIndex;
|
|
Inc(InnerLimit, OuterLimit);
|
|
|
|
{ Last line loop. }
|
|
while InIndex < InnerLimit do
|
|
begin
|
|
{ Read 3 bytes from InputBuffer. }
|
|
B := InputBuffer[InIndex + 0];
|
|
B := B shl 8;
|
|
B := B or InputBuffer[InIndex + 1];
|
|
B := B shl 8;
|
|
B := B or InputBuffer[InIndex + 2];
|
|
Inc(InIndex);
|
|
{ Write 4 bytes to OutputBuffer (in reverse order). }
|
|
OutputBuffer[OutIndex + 3] := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutputBuffer[OutIndex + 2] := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutputBuffer[OutIndex + 1] := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutputBuffer[OutIndex + 0] := MIME_ENCODE_TABLE[B];
|
|
Inc(OutIndex);
|
|
end;
|
|
|
|
{ End of data & padding. }
|
|
case InputByteCount - OuterLimit of
|
|
1:
|
|
begin
|
|
B := InputBuffer[InIndex + 0];
|
|
B := B shl 4;
|
|
OutputBuffer[OutIndex + 1] := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutputBuffer[OutIndex + 0] := MIME_ENCODE_TABLE[B];
|
|
OutputBuffer[OutIndex + 2] := MIME_PAD_CHAR; { Pad remaining 2 bytes. }
|
|
OutputBuffer[OutIndex + 3] := MIME_PAD_CHAR;
|
|
end;
|
|
2:
|
|
begin
|
|
B := InputBuffer[InIndex + 0];
|
|
B := B shl 8;
|
|
B := B or InputBuffer[InIndex + 1];
|
|
B := B shl 2;
|
|
OutputBuffer[OutIndex + 2] := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutputBuffer[OutIndex + 1] := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutputBuffer[OutIndex + 0] := MIME_ENCODE_TABLE[B];
|
|
OutputBuffer[OutIndex + 3] := MIME_PAD_CHAR; { Pad remaining byte. }
|
|
end;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
procedure MimeEncodeNoCRLF(const InputBuffer; const InputByteCount: Cardinal; out OutputBuffer);
|
|
var
|
|
B, InnerLimit, OuterLimit: Cardinal;
|
|
InPtr: PByte3;
|
|
OutPtr: PByte4;
|
|
begin
|
|
if InputByteCount = 0 then
|
|
Exit;
|
|
|
|
InPtr := @InputBuffer;
|
|
OutPtr := @OutputBuffer;
|
|
|
|
OuterLimit := InputByteCount div 3 * 3;
|
|
|
|
InnerLimit := Cardinal(InPtr);
|
|
Inc(InnerLimit, OuterLimit);
|
|
|
|
{ Last line loop. }
|
|
while Cardinal(InPtr) < InnerLimit do
|
|
begin
|
|
{ Read 3 bytes from InputBuffer. }
|
|
B := InPtr^.B1;
|
|
B := B shl 8;
|
|
B := B or InPtr^.B2;
|
|
B := B shl 8;
|
|
B := B or InPtr^.B3;
|
|
Inc(InPtr);
|
|
{ Write 4 bytes to OutputBuffer (in reverse order). }
|
|
OutPtr^.B4 := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutPtr^.B3 := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutPtr^.B2 := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutPtr^.B1 := MIME_ENCODE_TABLE[B];
|
|
Inc(OutPtr);
|
|
end;
|
|
|
|
{ End of data & padding. }
|
|
case InputByteCount - OuterLimit of
|
|
1:
|
|
begin
|
|
B := InPtr^.B1;
|
|
B := B shl 4;
|
|
OutPtr.B2 := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutPtr.B1 := MIME_ENCODE_TABLE[B];
|
|
OutPtr.B3 := MIME_PAD_CHAR; { Pad remaining 2 bytes. }
|
|
OutPtr.B4 := MIME_PAD_CHAR;
|
|
end;
|
|
2:
|
|
begin
|
|
B := InPtr^.B1;
|
|
B := B shl 8;
|
|
B := B or InPtr^.B2;
|
|
B := B shl 2;
|
|
OutPtr.B3 := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutPtr.B2 := MIME_ENCODE_TABLE[B and $3F];
|
|
B := B shr 6;
|
|
OutPtr.B1 := MIME_ENCODE_TABLE[B];
|
|
OutPtr.B4 := MIME_PAD_CHAR; { Pad remaining byte. }
|
|
end;
|
|
end;
|
|
end;
|
|
{$ENDIF CLR}
|
|
|
|
// Decoding Core
|
|
function MimeDecode(const InputBuffer {$IFDEF CLR}: TDynByteArray; InputOffset: Cardinal {$ENDIF CLR};
|
|
const InputByteCount: Cardinal; out OutputBuffer {$IFDEF CLR}: TDynByteArray; OutputOffset: Cardinal {$ENDIF CLR}): Cardinal;
|
|
var
|
|
ByteBuffer, ByteBufferSpace: Cardinal;
|
|
begin
|
|
ByteBuffer := 0;
|
|
ByteBufferSpace := 4;
|
|
{$IFDEF CLR}
|
|
Result := MimeDecodePartial(InputBuffer, InputOffset, InputByteCount, OutputBuffer, OutputOffset, ByteBuffer, ByteBufferSpace);
|
|
Inc(Result, MimeDecodePartialEnd(OutputBuffer, OutputOffset + Result, ByteBuffer, ByteBufferSpace));
|
|
{$ELSE}
|
|
Result := MimeDecodePartial(InputBuffer, InputByteCount, OutputBuffer, ByteBuffer, ByteBufferSpace);
|
|
Inc(Result, MimeDecodePartialEnd(Pointer(Cardinal(@OutputBuffer) + Result)^, ByteBuffer, ByteBufferSpace));
|
|
{$ENDIF CLR}
|
|
end;
|
|
|
|
{$IFDEF CLR}
|
|
function MimeDecodePartial(const InputBuffer: TDynByteArray; InputOffset: Cardinal;
|
|
const InputByteCount: Cardinal; out OutputBuffer: TDynByteArray; OutputOffset: Cardinal;
|
|
var ByteBuffer: Cardinal; var ByteBufferSpace: Cardinal): Cardinal;
|
|
var
|
|
LByteBuffer, LByteBufferSpace, C: Cardinal;
|
|
InIndex, OuterLimit: Cardinal;
|
|
OutIndex: Cardinal;
|
|
begin
|
|
if InputByteCount > 0 then
|
|
begin
|
|
InIndex := InputOffset;
|
|
OuterLimit := InIndex + InputByteCount;
|
|
OutIndex := OutputOffset;
|
|
LByteBuffer := ByteBuffer;
|
|
LByteBufferSpace := ByteBufferSpace;
|
|
while InIndex < OuterLimit do
|
|
begin
|
|
{ Read from InputBuffer. }
|
|
C := MIME_DECODE_TABLE[InputBuffer[InIndex]];
|
|
Inc(InIndex);
|
|
if C = $FF then
|
|
Continue;
|
|
LByteBuffer := LByteBuffer shl 6;
|
|
LByteBuffer := LByteBuffer or C;
|
|
Dec(LByteBufferSpace);
|
|
{ Have we read 4 bytes from InputBuffer? }
|
|
if LByteBufferSpace <> 0 then
|
|
Continue;
|
|
|
|
{ Write 3 bytes to OutputBuffer (in reverse order). }
|
|
OutputBuffer[OutIndex + 2] := Byte(LByteBuffer);
|
|
LByteBuffer := LByteBuffer shr 8;
|
|
OutputBuffer[OutIndex + 1] := Byte(LByteBuffer);
|
|
LByteBuffer := LByteBuffer shr 8;
|
|
OutputBuffer[OutIndex + 0] := Byte(LByteBuffer);
|
|
LByteBuffer := 0;
|
|
Inc(OutINdex);
|
|
LByteBufferSpace := 4;
|
|
end;
|
|
ByteBuffer := LByteBuffer;
|
|
ByteBufferSpace := LByteBufferSpace;
|
|
Result := OutIndex - OutputOffset;
|
|
end
|
|
else
|
|
Result := 0;
|
|
end;
|
|
{$ELSE}
|
|
function MimeDecodePartial(const InputBuffer; const InputByteCount: Cardinal; out OutputBuffer;
|
|
var ByteBuffer: Cardinal; var ByteBufferSpace: Cardinal): Cardinal;
|
|
var
|
|
LByteBuffer, LByteBufferSpace, C: Cardinal;
|
|
InPtr, OuterLimit: ^Byte;
|
|
OutPtr: PByte3;
|
|
begin
|
|
if InputByteCount > 0 then
|
|
begin
|
|
InPtr := @InputBuffer;
|
|
Cardinal(OuterLimit) := Cardinal(InPtr) + InputByteCount;
|
|
OutPtr := @OutputBuffer;
|
|
LByteBuffer := ByteBuffer;
|
|
LByteBufferSpace := ByteBufferSpace;
|
|
while InPtr <> OuterLimit do
|
|
begin
|
|
{ Read from InputBuffer. }
|
|
C := MIME_DECODE_TABLE[InPtr^];
|
|
Inc(InPtr);
|
|
if C = $FF then
|
|
Continue;
|
|
LByteBuffer := LByteBuffer shl 6;
|
|
LByteBuffer := LByteBuffer or C;
|
|
Dec(LByteBufferSpace);
|
|
{ Have we read 4 bytes from InputBuffer? }
|
|
if LByteBufferSpace <> 0 then
|
|
Continue;
|
|
|
|
{ Write 3 bytes to OutputBuffer (in reverse order). }
|
|
OutPtr^.B3 := Byte(LByteBuffer);
|
|
LByteBuffer := LByteBuffer shr 8;
|
|
OutPtr^.B2 := Byte(LByteBuffer);
|
|
LByteBuffer := LByteBuffer shr 8;
|
|
OutPtr^.B1 := Byte(LByteBuffer);
|
|
LByteBuffer := 0;
|
|
Inc(OutPtr);
|
|
LByteBufferSpace := 4;
|
|
end;
|
|
ByteBuffer := LByteBuffer;
|
|
ByteBufferSpace := LByteBufferSpace;
|
|
Result := Cardinal(OutPtr) - Cardinal(@OutputBuffer);
|
|
end
|
|
else
|
|
Result := 0;
|
|
end;
|
|
{$ENDIF CLR}
|
|
|
|
function MimeDecodePartialEnd(out OutputBuffer {$IFDEF CLR}: TDynByteArray; OutputOffset: Cardinal {$ENDIF CLR};
|
|
const ByteBuffer: Cardinal; const ByteBufferSpace: Cardinal): Cardinal;
|
|
var
|
|
LByteBuffer: Cardinal;
|
|
begin
|
|
case ByteBufferSpace of
|
|
1:
|
|
begin
|
|
LByteBuffer := ByteBuffer shr 2;
|
|
{$IFDEF CLR}
|
|
OutputBuffer[OutputOffset + 1] := Byte(LByteBuffer);
|
|
LByteBuffer := LByteBuffer shr 8;
|
|
OutputBuffer[OutputOffset + 0] := Byte(LByteBuffer);
|
|
{$ELSE}
|
|
PByte3(@OutputBuffer)^.B2 := Byte(LByteBuffer);
|
|
LByteBuffer := LByteBuffer shr 8;
|
|
PByte3(@OutputBuffer)^.B1 := Byte(LByteBuffer);
|
|
{$ENDIF CLR}
|
|
Result := 2;
|
|
end;
|
|
2:
|
|
begin
|
|
LByteBuffer := ByteBuffer shr 4;
|
|
{$IFDEF CLR}
|
|
OutputBuffer[OutputOffset + 0] := Byte(LByteBuffer);
|
|
{$ELSE}
|
|
PByte3(@OutputBuffer)^.B1 := Byte(LByteBuffer);
|
|
{$ENDIF CLR}
|
|
Result := 1;
|
|
end;
|
|
else
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
|
|
// File Encoding & Decoding
|
|
procedure MimeEncodeFile(const InputFileName, OutputFileName: AnsiString);
|
|
var
|
|
InputStream, OutputStream: TFileStream;
|
|
begin
|
|
InputStream := TFileStream.Create(InputFileName, fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
OutputStream := TFileStream.Create(OutputFileName, fmCreate);
|
|
try
|
|
MimeEncodeStream(InputStream, OutputStream);
|
|
finally
|
|
OutputStream.Free;
|
|
end;
|
|
finally
|
|
InputStream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure MimeEncodeFileNoCRLF(const InputFileName, OutputFileName: AnsiString);
|
|
var
|
|
InputStream, OutputStream: TFileStream;
|
|
begin
|
|
InputStream := TFileStream.Create(InputFileName, fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
OutputStream := TFileStream.Create(OutputFileName, fmCreate);
|
|
try
|
|
MimeEncodeStreamNoCRLF(InputStream, OutputStream);
|
|
finally
|
|
OutputStream.Free;
|
|
end;
|
|
finally
|
|
InputStream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure MimeDecodeFile(const InputFileName, OutputFileName: AnsiString);
|
|
var
|
|
InputStream, OutputStream: TFileStream;
|
|
begin
|
|
InputStream := TFileStream.Create(InputFileName, fmOpenRead or fmShareDenyWrite);
|
|
try
|
|
OutputStream := TFileStream.Create(OutputFileName, fmCreate);
|
|
try
|
|
MimeDecodeStream(InputStream, OutputStream);
|
|
finally
|
|
OutputStream.Free;
|
|
end;
|
|
finally
|
|
InputStream.Free;
|
|
end;
|
|
end;
|
|
|
|
// Stream Encoding & Decoding
|
|
procedure MimeEncodeStream(const InputStream: TStream; const OutputStream: TStream);
|
|
var
|
|
InputBuffer: array [0..MIME_BUFFER_SIZE - 1] of Byte;
|
|
{$IFDEF CLR}
|
|
OutputBuffer: array of Byte;
|
|
{$ELSE}
|
|
OutputBuffer: array [0..(MIME_BUFFER_SIZE + 2) div 3 * 4 + MIME_BUFFER_SIZE div MIME_DECODED_LINE_BREAK * 2 - 1] of Byte;
|
|
{$ENDIF CLR}
|
|
BytesRead: Cardinal;
|
|
IDelta, ODelta: Cardinal;
|
|
begin
|
|
BytesRead := InputStream.Read(InputBuffer, SizeOf(InputBuffer));
|
|
{$IFDEF CLR}
|
|
SetLength(OutputBuffer, (MIME_BUFFER_SIZE + 2) div 3 * 4 + MIME_BUFFER_SIZE div MIME_DECODED_LINE_BREAK * 2);
|
|
{$ENDIF CLR}
|
|
|
|
while BytesRead = Cardinal(Length(InputBuffer)) do
|
|
begin
|
|
MimeEncodeFullLines(InputBuffer, Length(InputBuffer), OutputBuffer);
|
|
OutputStream.Write(OutputBuffer, Length(OutputBuffer));
|
|
BytesRead := InputStream.Read(InputBuffer, Length(InputBuffer));
|
|
end;
|
|
|
|
MimeEncodeFullLines(InputBuffer, BytesRead, OutputBuffer);
|
|
|
|
IDelta := BytesRead div MIME_DECODED_LINE_BREAK; // Number of lines processed.
|
|
ODelta := IDelta * (MIME_ENCODED_LINE_BREAK + 2);
|
|
IDelta := IDelta * MIME_DECODED_LINE_BREAK;
|
|
{$IFDEF ClR}
|
|
MimeEncodeNoCRLF(InputBuffer, IDelta, BytesRead - IDelta, OutputBuffer, ODelta);
|
|
{$ELSE}
|
|
MimeEncodeNoCRLF(Pointer(Cardinal(@InputBuffer) + IDelta)^, BytesRead - IDelta, Pointer(Cardinal(@OutputBuffer) + ODelta)^);
|
|
{$ENDIF CLR}
|
|
|
|
OutputStream.Write(OutputBuffer, MimeEncodedSize(BytesRead));
|
|
end;
|
|
|
|
procedure MimeEncodeStreamNoCRLF(const InputStream: TStream; const OutputStream: TStream);
|
|
var
|
|
InputBuffer: array [0..MIME_BUFFER_SIZE - 1] of Byte;
|
|
{$IFDEF CLR}
|
|
OutputBuffer: array of Byte;
|
|
{$ELSE}
|
|
OutputBuffer: array [0..((MIME_BUFFER_SIZE + 2) div 3) * 4 - 1] of Byte;
|
|
{$ENDIF CLR}
|
|
BytesRead: Cardinal;
|
|
begin
|
|
BytesRead := InputStream.Read(InputBuffer, SizeOf(InputBuffer));
|
|
{$IFDEF CLR}
|
|
SetLength(OutputBuffer, ((MIME_BUFFER_SIZE + 2) div 3) * 4);
|
|
{$ENDIF CLR}
|
|
|
|
while BytesRead = Cardinal(Length(InputBuffer)) do
|
|
begin
|
|
MimeEncodeNoCRLF(InputBuffer, Length(InputBuffer), OutputBuffer);
|
|
OutputStream.Write(OutputBuffer, Length(OutputBuffer));
|
|
BytesRead := InputStream.Read(InputBuffer, Length(InputBuffer));
|
|
end;
|
|
|
|
MimeEncodeNoCRLF(InputBuffer, BytesRead, OutputBuffer);
|
|
OutputStream.Write(OutputBuffer, MimeEncodedSizeNoCRLF(BytesRead));
|
|
end;
|
|
|
|
procedure MimeDecodeStream(const InputStream: TStream; const OutputStream: TStream);
|
|
var
|
|
ByteBuffer, ByteBufferSpace: Cardinal;
|
|
InputBuffer: array [0..MIME_BUFFER_SIZE - 1] of Byte;
|
|
{$IFDEF CLR}
|
|
OutputBuffer: array of Byte;
|
|
{$ELSE}
|
|
OutputBuffer: array [0..(MIME_BUFFER_SIZE + 3) div 4 * 3 - 1] of Byte;
|
|
{$ENDIF CLR}
|
|
BytesRead: Cardinal;
|
|
begin
|
|
ByteBuffer := 0;
|
|
ByteBufferSpace := 4;
|
|
BytesRead := InputStream.Read(InputBuffer, SizeOf(InputBuffer));
|
|
{$IFDEF CLR}
|
|
SetLength(OutputBuffer, (MIME_BUFFER_SIZE + 3) div 4 * 3);
|
|
{$ENDIF CLR}
|
|
|
|
while BytesRead > 0 do
|
|
begin
|
|
OutputStream.Write(OutputBuffer, MimeDecodePartial(InputBuffer, BytesRead, OutputBuffer, ByteBuffer, ByteBufferSpace));
|
|
BytesRead := InputStream.Read(InputBuffer, Length(InputBuffer));
|
|
end;
|
|
OutputStream.Write(OutputBuffer, MimeDecodePartialEnd(OutputBuffer, ByteBuffer, ByteBufferSpace));
|
|
end;
|
|
|
|
// History:
|
|
|
|
// $Log: JclMime.pas,v $
|
|
// Revision 1.14 2005/05/05 20:08:43 ahuser
|
|
// JCL.NET support
|
|
//
|
|
// Revision 1.13 2005/02/24 16:34:40 marquardt
|
|
// remove divider lines, add section lines (unfinished)
|
|
//
|
|
// Revision 1.12 2005/02/13 22:24:25 rrossmair
|
|
// moved PCardinal declaration from JclMime to JclBase
|
|
//
|
|
// Revision 1.11 2004/10/13 06:58:19 marquardt
|
|
// normal style cleaning
|
|
//
|
|
// Revision 1.10 2004/10/12 18:29:52 rrossmair
|
|
// cleanup
|
|
//
|
|
// Revision 1.9 2004/07/28 18:00:51 marquardt
|
|
// various style cleanings, some minor fixes
|
|
//
|
|
// Revision 1.8 2004/07/07 22:34:25 mthoma
|
|
// Added Ralf MimeStreams and MimeFile utilities...
|
|
//
|
|
// Revision 1.7 2004/06/14 13:05:18 marquardt
|
|
// style cleaning ENDIF, Tabs
|
|
//
|
|
// Revision 1.6 2004/05/05 00:09:59 mthoma
|
|
// Updated headers: Added donors as contributors, adjusted the initial authors, added cvs names when they were not obvious. Changed $data to $date where necessary,
|
|
//
|
|
// Revision 1.5 2004/04/09 20:21:11 mthoma
|
|
// Updated to Ralfs latest release. That also fixed 0000406.
|
|
//
|
|
|
|
end.
|