synapse/mimepart.pas

586 lines
17 KiB
ObjectPascal
Raw Normal View History

{==============================================================================|
| Project : Delphree - Synapse | 001.005.002 |
|==============================================================================|
| Content: MIME support procedures and functions |
|==============================================================================|
| The contents of this file are subject to the Mozilla Public License Ver. 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 Synapse Delphi Library. |
|==============================================================================|
| The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
| Portions created by Lukas Gebauer are Copyright (c)2000,2001. |
| All Rights Reserved. |
|==============================================================================|
| Contributor(s): |
|==============================================================================|
| History: see HISTORY.HTM from distribution package |
| (Found at URL: http://www.ararat.cz/synapse/) |
|==============================================================================}
unit MIMEpart;
interface
uses
SysUtils, Classes,
SynaChar, SynaCode, SynaUtil, MIMEinLn;
type
TMimePrimary = (MP_TEXT, MP_MULTIPART,
MP_MESSAGE, MP_BINARY);
TMimeEncoding = (ME_7BIT, ME_8BIT, ME_QUOTED_PRINTABLE,
ME_BASE64, ME_UU, ME_XX);
TMimePart = class(TObject)
private
FPrimary: string;
FEncoding: string;
FCharset: string;
FPrimaryCode: TMimePrimary;
FEncodingCode: TMimeEncoding;
FCharsetCode: TMimeChar;
FTargetCharset: TMimeChar;
FSecondary: string;
FDescription: string;
FDisposition: string;
FContentID: string;
FBoundary: string;
FFileName: string;
FLines: TStringList;
FDecodedLines: TMemoryStream;
procedure SetPrimary(Value: string);
procedure SetEncoding(Value: string);
procedure SetCharset(Value: string);
public
constructor Create;
destructor Destroy; override;
procedure Clear;
function ExtractPart(Value: TStringList; BeginLine: Integer): Integer;
procedure DecodePart;
procedure EncodePart;
procedure MimeTypeFromExt(Value: string);
published
property Primary: string read FPrimary write SetPrimary;
property Encoding: string read FEncoding write SetEncoding;
property Charset: string read FCharset write SetCharset;
property PrimaryCode: TMimePrimary read FPrimaryCode Write FPrimaryCode;
property EncodingCode: TMimeEncoding read FEncodingCode Write FEncodingCode;
property CharsetCode: TMimeChar read FCharsetCode Write FCharsetCode;
property TargetCharset: TMimeChar read FTargetCharset Write FTargetCharset;
property Secondary: string read FSecondary Write FSecondary;
property Description: string read FDescription Write FDescription;
property Disposition: string read FDisposition Write FDisposition;
property ContentID: string read FContentID Write FContentID;
property Boundary: string read FBoundary Write FBoundary;
property FileName: string read FFileName Write FFileName;
property Lines: TStringList read FLines;
property DecodedLines: TMemoryStream read FDecodedLines;
end;
const
MaxMimeType = 25;
MimeType: array[0..MaxMimeType, 0..2] of string =
(
('AU', 'audio', 'basic'),
('AVI', 'video', 'x-msvideo'),
('BMP', 'image', 'BMP'),
('DOC', 'application', 'MSWord'),
('EPS', 'application', 'Postscript'),
('GIF', 'image', 'GIF'),
('JPEG', 'image', 'JPEG'),
('JPG', 'image', 'JPEG'),
('MID', 'audio', 'midi'),
('MOV', 'video', 'quicktime'),
('MPEG', 'video', 'MPEG'),
('MPG', 'video', 'MPEG'),
('MP2', 'audio', 'mpeg'),
('MP3', 'audio', 'mpeg'),
('PDF', 'application', 'PDF'),
('PNG', 'image', 'PNG'),
('PS', 'application', 'Postscript'),
('QT', 'video', 'quicktime'),
('RA', 'audio', 'x-realaudio'),
('RTF', 'application', 'RTF'),
('SND', 'audio', 'basic'),
('TIF', 'image', 'TIFF'),
('TIFF', 'image', 'TIFF'),
('WAV', 'audio', 'x-wav'),
('WPD', 'application', 'Wordperfect5.1'),
('ZIP', 'application', 'ZIP')
);
function NormalizeHeader(Value: TStringList; var Index: Integer): string;
function GenerateBoundary: string;
implementation
function NormalizeHeader(Value: TStringList; var Index: Integer): string;
var
s, t: string;
n: Integer;
begin
s := Value[Index];
Inc(Index);
if s <> '' then
while (Value.Count - 1) > Index do
begin
t := Value[Index];
if t = '' then
Break;
for n := 1 to Length(t) do
if t[n] = #9 then
t[n] := ' ';
if t[1] <> ' ' then
Break
else
begin
s := s + ' ' + Trim(t);
Inc(Index);
end;
end;
Result := s;
end;
{==============================================================================}
constructor TMIMEPart.Create;
begin
inherited Create;
FLines := TStringList.Create;
FDecodedLines := TMemoryStream.Create;
FTargetCharset := GetCurCP;
end;
destructor TMIMEPart.Destroy;
begin
FDecodedLines.Free;
FLines.Free;
inherited Destroy;
end;
{==============================================================================}
procedure TMIMEPart.Clear;
begin
FPrimary := '';
FEncoding := '';
FCharset := '';
FPrimaryCode := MP_TEXT;
FEncodingCode := ME_7BIT;
FCharsetCode := ISO_8859_1;
FTargetCharset := GetCurCP;
FSecondary := '';
FDisposition := '';
FContentID := '';
FDescription := '';
FBoundary := '';
FFileName := '';
FLines.Clear;
FDecodedLines.Clear;
end;
{==============================================================================}
function TMIMEPart.ExtractPart(Value: TStringList; BeginLine: Integer): Integer;
var
n, x, x1, x2: Integer;
t: TStringList;
s, su, b: string;
st, st2: string;
e: Boolean;
fn: string;
begin
t := TStringlist.Create;
try
{ defaults }
FLines.Clear;
Primary := 'text';
FSecondary := 'plain';
FDescription := '';
Charset := 'US-ASCII';
FFileName := '';
Encoding := '7BIT';
fn := '';
x := BeginLine;
b := FBoundary;
{ if multipart - skip pre-part }
if b <> '' then
while Value.Count > x do
begin
s := Value[x];
Inc(x);
if Pos('--' + b, s) = 1 then
Break;
end;
{ parse header }
while Value.Count > x do
begin
s := NormalizeHeader(Value, x);
if s = '' then
Break;
su := UpperCase(s);
if Pos('CONTENT-TYPE:', su) = 1 then
begin
st := SeparateRight(su, ':');
st2 := SeparateLeft(st, ';');
Primary := SeparateLeft(st2, '/');
FSecondary := SeparateRight(st2, '/');
if (FSecondary = Primary) and (Pos('/', st2) < 1) then
FSecondary := '';
case FPrimaryCode of
MP_TEXT:
begin
Charset := UpperCase(GetParameter(s, 'charset='));
FFileName := GetParameter(s, 'name=');
end;
MP_MULTIPART:
FBoundary := GetParameter(s, 'Boundary=');
MP_MESSAGE:
begin
end;
MP_BINARY:
FFileName := GetParameter(s, 'name=');
end;
end;
if Pos('CONTENT-TRANSFER-ENCODING:', su) = 1 then
Encoding := SeparateRight(su, ':');
if Pos('CONTENT-DESCRIPTION:', su) = 1 then
FDescription := SeparateRight(s, ':');
if Pos('CONTENT-DISPOSITION:', su) = 1 then
begin
FDisposition := SeparateRight(su, ':');
FDisposition := Trim(SeparateLeft(FDisposition, ';'));
fn := GetParameter(s, 'FileName=');
end;
if Pos('CONTENT-ID:', su) = 1 then
FContentID := SeparateRight(s, ':');
end;
if (PrimaryCode = MP_BINARY) and (FFileName = '') then
FFileName := fn;
FFileName := InlineDecode(FFileName, getCurCP);
FFileName := ExtractFileName(FFileName);
{ finding part content x1-begin x2-end }
x1 := x;
x2 := Value.Count - 1;
{ if multipart - end is before next boundary }
if b <> '' then
begin
for n := x to Value.Count - 1 do
begin
x2 := n;
s := Value[n];
if Pos('--' + b, s) = 1 then
begin
Dec(x2);
Break;
end;
end;
end;
{ if content is multipart - content is delimited by their boundaries }
if FPrimaryCode = MP_MULTIPART then
begin
for n := x to Value.Count - 1 do
begin
s := Value[n];
if Pos('--' + FBoundary, s) = 1 then
begin
x1 := n;
Break;
end;
end;
for n := Value.Count - 1 downto x do
begin
s := Value[n];
if Pos('--' + FBoundary, s) = 1 then
begin
x2 := n;
Break;
end;
end;
end;
{ copy content }
for n := x1 to x2 do
FLines.Add(Value[n]);
Result := x2;
{ if content is multipart - find real end }
if FPrimaryCode = MP_MULTIPART then
begin
e := False;
for n := x2 + 1 to Value.Count - 1 do
if Pos('--' + b, Value[n]) = 1 then
begin
e := True;
Break;
end;
if not e then
Result := Value.Count - 1;
end;
{ if multipart - skip ending postpart}
if b <> '' then
begin
x1 := Result;
for n := x1 to Value.Count - 1 do
begin
s := Value[n];
if Pos('--' + b, s) = 1 then
begin
s := TrimRight(s);
x := Length(s);
if x > 4 then
if (s[x] = '-') and (S[x-1] = '-') then
Result := Value.Count - 1;
Break;
end;
end;
end;
finally
t.Free;
end;
end;
{==============================================================================}
procedure TMIMEPart.DecodePart;
const
CRLF = #13#10;
var
n: Integer;
s: string;
begin
FDecodedLines.Clear;
for n := 0 to FLines.Count - 1 do
begin
s := FLines[n];
case FEncodingCode of
ME_7BIT:
s := s + CRLF;
ME_8BIT:
begin
s := CharsetConversion(s, FCharsetCode, FTargetCharset);
s := s + CRLF;
end;
ME_QUOTED_PRINTABLE:
begin
if s = '' then
s := CRLF
else
if s[Length(s)] <> '=' then
s := s + CRLF;
s := DecodeQuotedPrintable(s);
if FPrimaryCode = MP_TEXT then
s := CharsetConversion(s, FCharsetCode, FTargetCharset);
end;
ME_BASE64:
begin
if s <> '' then
s := DecodeBase64(s);
if FPrimaryCode = MP_TEXT then
s := CharsetConversion(s, FCharsetCode, FTargetCharset);
end;
ME_UU:
if s <> '' then
s := DecodeUU(s);
ME_XX:
if s <> '' then
s := DecodeXX(s);
end;
FDecodedLines.Write(Pointer(s)^, Length(s));
end;
FDecodedLines.Seek(0, soFromBeginning);
end;
{==============================================================================}
procedure TMIMEPart.EncodePart;
var
l: TStringList;
s, buff: string;
n, x: Integer;
begin
if (FEncodingCode = ME_UU) or (FEncodingCode = ME_XX) then
Encoding := 'base64';
l := TStringList.Create;
FLines.Clear;
FDecodedLines.Seek(0, soFromBeginning);
try
case FPrimaryCode of
MP_MULTIPART, MP_MESSAGE:
FLines.LoadFromStream(FDecodedLines);
MP_TEXT, MP_BINARY:
if FEncodingCode = ME_BASE64 then
begin
while FDecodedLines.Position < FDecodedLines.Size do
begin
Setlength(Buff, 54);
s := '';
x := FDecodedLines.Read(pointer(Buff)^, 54);
for n := 1 to x do
s := s + Buff[n];
if FPrimaryCode = MP_TEXT then
s := CharsetConversion(s, FTargetCharset, FCharsetCode);
s := EncodeBase64(s);
FLines.Add(s);
end;
end
else
begin
l.LoadFromStream(FDecodedLines);
for n := 0 to l.Count - 1 do
begin
s := l[n];
if FPrimaryCode = MP_TEXT then
s := CharsetConversion(s, FTargetCharset, FCharsetCode);
s := EncodeQuotedPrintable(s);
FLines.Add(s);
end;
end;
end;
FLines.Add('');
FLines.Insert(0, '');
if FSecondary = '' then
case FPrimaryCode of
MP_TEXT:
FSecondary := 'plain';
MP_MULTIPART:
FSecondary := 'mixed';
MP_MESSAGE:
FSecondary := 'rfc822';
MP_BINARY:
FSecondary := 'octet-stream';
end;
if FDescription <> '' then
FLines.Insert(0, 'Content-Description: ' + FDescription);
if FDisposition <> '' then
begin
s := '';
if FFileName <> '' then
s := '; FileName="' + FFileName + '"';
FLines.Insert(0, 'Content-Disposition: ' + LowerCase(FDisposition) + s);
end;
if FContentID <> '' then
FLines.Insert(0, 'Content-ID: ' + FContentID);
case FEncodingCode of
ME_7BIT:
s := '7bit';
ME_8BIT:
s := '8bit';
ME_QUOTED_PRINTABLE:
s := 'Quoted-printable';
ME_BASE64:
s := 'Base64';
end;
case FPrimaryCode of
MP_TEXT,
MP_BINARY: FLines.Insert(0, 'Content-Transfer-Encoding: ' + s);
end;
case FPrimaryCode of
MP_TEXT:
s := FPrimary + '/' + FSecondary + '; charset=' + GetIDfromCP(FCharsetCode);
MP_MULTIPART:
s := FPrimary + '/' + FSecondary + '; boundary="' + FBoundary + '"';
MP_MESSAGE:
s := FPrimary + '/' + FSecondary + '';
MP_BINARY:
s := FPrimary + '/' + FSecondary + '; name="' + FFileName + '"';
end;
FLines.Insert(0, 'Content-type: ' + s);
finally
l.Free;
end;
end;
{==============================================================================}
procedure TMIMEPart.MimeTypeFromExt(Value: string);
var
s: string;
n: Integer;
begin
Primary := '';
FSecondary := '';
s := UpperCase(ExtractFileExt(Value));
if s = '' then
s := UpperCase(Value);
s := SeparateRight(s, '.');
for n := 0 to MaxMimeType do
if MimeType[n, 0] = s then
begin
Primary := MimeType[n, 1];
FSecondary := MimeType[n, 2];
Break;
end;
if Primary = '' then
Primary := 'application';
if FSecondary = '' then
FSecondary := 'octet-string';
end;
{==============================================================================}
procedure TMIMEPart.SetPrimary(Value: string);
var
s: string;
begin
FPrimary := Value;
s := UpperCase(Value);
FPrimaryCode := MP_BINARY;
if Pos('TEXT', s) = 1 then
FPrimaryCode := MP_TEXT;
if Pos('MULTIPART', s) = 1 then
FPrimaryCode := MP_MULTIPART;
if Pos('MESSAGE', s) = 1 then
FPrimaryCode := MP_MESSAGE;
end;
procedure TMIMEPart.SetEncoding(Value: string);
var
s: string;
begin
FEncoding := Value;
s := UpperCase(Value);
FEncodingCode := ME_7BIT;
if Pos('8BIT', s) = 1 then
FEncodingCode := ME_8BIT;
if Pos('QUOTED-PRINTABLE', s) = 1 then
FEncodingCode := ME_QUOTED_PRINTABLE;
if Pos('BASE64', s) = 1 then
FEncodingCode := ME_BASE64;
if Pos('X-UU', s) = 1 then
FEncodingCode := ME_UU;
if Pos('X-XX', s) = 1 then
FEncodingCode := ME_XX;
end;
procedure TMIMEPart.SetCharset(Value: string);
begin
FCharset := Value;
FCharsetCode := GetCPFromID(Value);
end;
{==============================================================================}
function GenerateBoundary: string;
var
x: Integer;
begin
Randomize;
x := Random(MaxInt);
Result := '--' + IntToHex(x, 8) + '_Synapse_message_boundary--';
end;
end.