home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Freelog 11
/
Freelog011.iso
/
BestOf
/
PhoenixMail
/
Source
/
phoenix
/
ParserSup.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1999-02-20
|
23KB
|
779 lines
{*****************************************************************************
*
* ParserSup.pas - E-mail Parser Support Routines (27-July-1998)
*
* Copyright (c) 1998-99 Michael Haller
*
* Author: Michael Haller
* E-mail: michael@discountdrive.com
* Homepage: http://www.discountdrive.com/sunrise
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
*----------------------------------------------------------------------------
*
* Revision history:
*
* DATE REV DESCRIPTION
* ----------- --- ----------------------------------------------------------
*
*****************************************************************************}
unit ParserSup;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Base64Sup, ComCtrls;
function SkipChar(S: String; C: Char): String;
procedure CommataStringToStringList(S: String; var SL: TStringList);
function StringListToCommataString(SL: TStringList; Decoded: Boolean): String;
procedure SaveMIMEToFile(var SL: TStringList; ContentType, ContentEncoding: String);
procedure GetFieldArguments(S: String; var S1, S2: String);
procedure MakeFields(var SL: TStringList; ClearEmpties: Boolean);
function GetContentType(Variable, ContentType: String): String;
function FromQuotedPrintable(S: String): String;
function ToQuotedPrintable(S: String): String;
function GetMIMEFilename(Dir, ContentType: String): String;
procedure GetMIMEType(Filename: String; var Content, Encoding: String; var Format: Byte);
function GetMIMEHeaderField(Field: String): String;
function SetMIMEHeaderField(Field: String): String;
function BreakString(S: String): String;
procedure AttachFileToTextfile(var F: Text; Filename: String; Format: Byte);
procedure SkipSpaces(var S: String);
function GetToken(var S: String): String;
function ApplyFilter(Msg, Filter: String; var Points: Integer): Boolean;
function GetUniqueMailName: String;
function ExtractEMailAddress(S: String): String;
function ExtractEMailName(S: String): String;
procedure GetOutcomingMailFileList(Node: TTreeNode; var SL: TStringList);
function StretchString(S: String; Count: Integer): String;
function SkipSpacesAtEnd(S: String): String;
function SoftLineBreak(S: String; Quoted: Boolean): String;
function GetCommataStringReceiver: String;
procedure SetPXTreeNodeName(Node: TTreeNode);
function GetPXTreeNodeName(Node: TTreeNode): String;
implementation
uses
PXStuff, MailParser;
var
FT: Text;
FF: File;
procedure SetPXTreeNodeName(Node: TTreeNode);
var
SearchRec: TSearchRec;
Found, Count, WholeCount: Integer;
Dir, Name: String;
B: Boolean;
begin
if Node = nil then Exit;
if Node.StateIndex = 1 then begin
Dir := PAccountData(Node.Data)^.Path;
Name := PAccountData(Node.Data)^.Name;
end;
if Node.StateIndex = 2 then begin
Dir := PFolderData(Node.Data)^.Path;
Name := PFolderData(Node.Data)^.Name;
end;
if Node.StateIndex = 3 then begin
Dir := sTrashFolder;
Name := Node.Text;
end;
if Node.StateIndex = 4 then Exit;
if Node.StateIndex = 5 then begin
Dir := sRepositoryFolder;
Name := Node.Text;
end;
Screen.Cursor := crHourGlass;
Count := 0;
WholeCount := 0;
Found := FindFirst(Dir+'*.MSG', faAnyFile, SearchRec);
while Found = 0 do begin
if FileGetAttr(Dir+SearchRec.Name) and faArchive = 0 then Inc(Count);
Inc(WholeCount);
Found := FindNext(SearchRec);
end;
FindClose(SearchRec);
B := False;
if bShowCntInBrackets then begin
if (Count > 0) or (WholeCount > 0) then begin
Node.Text := Name + ' ('+IntToStr(Count)+'/'+IntToStr(WholeCount)+')';
B := True;
end;
end else begin
if Count > 0 then begin
Node.Text := Name + ' ('+IntToStr(Count)+')';
B := True;
end;
end;
if B = False then Node.Text := Name;
Screen.Cursor := crDefault;
end;
function GetPXTreeNodeName(Node: TTreeNode): String;
begin
if Node = nil then Exit;
if Node.StateIndex = 1 then Result := PAccountData(Node.Data)^.Name;
if Node.StateIndex = 2 then Result := PFolderData(Node.Data)^.Name;
if Node.StateIndex = 3 then Result := sTrashBagCaption;
if Node.StateIndex = 4 then Result := Node.Text;
if Node.StateIndex = 5 then Result := sRepositoryCaption;
end;
function GetCommataStringReceiver: String;
var
S, S1, S2, S3: String;
begin
S1 := StringListToCommataString(CurrentEMail.ToReceiver, False);
S2 := StringListToCommataString(CurrentEMail.CC, False);
S3 := StringListToCommataString(CurrentEMail.BCC, False);
S := '';
if S1 <> '' then S := S1;
if S2 <> '' then if S = '' then S := S2 else S := S + ', ' + S2;
if S3 <> '' then if S = '' then S := S3 else S := S + ', ' + S3;
Result := S;
end;
function SoftLineBreak(S: String; Quoted: Boolean): String;
const
C = 76;
var
I, E: Integer;
B: Boolean;
function IsNotAlreadyQuoted: Boolean;
begin
Result := True;
if Length(S) > 0 then
if S[1] = '>' then Result := False;
end;
begin
Result := #13+#10+'*** Phoenix Mail Error: Lines have been cut off! ***'+#13+#10;
try
I := C;
if Quoted and IsNotAlreadyQuoted then S := '>' + S;
//while text longer than 76
while Length(S) > I do begin
B := False;
//Search space char
for E := I downto (I-C+2) do begin
if Ord(S[E]) <= 32 then begin
I := E+1;
B := True;
Break;
end;
end;
//found space char
if B then begin
if Quoted then begin
Insert(#13+#10+'>', S, I);
Inc(I, C+1);
end else begin
Insert(#13+#10, S, I);
Inc(I, C+1);
end;
end else begin
if Quoted then begin
Insert(#13+#10+'>', S, I+1);
Inc(I, C+1);
end else begin
Insert(#13+#10+#32, S, I+1);
Inc(I, C+1);
end;
end;
end;
Result := S;
except end;
end;
procedure AttachFileToTextfile(var F: Text; Filename: String; Format: Byte);
const
L = 57;
var
S: String;
I, E, K: Integer;
FF: File;
begin
SetFileAttr(Filename, False, True, False, False);
//Quoted printable
if Format = 0 then begin
AssignFile(FT, Filename);
Reset(FT);
while not EoF(FT) do begin
ReadLn(FT, S);
S := SoftLineBreak(ToQuotedPrintable(S), False);
WriteLn(F, S);
end;
CloseFile(FT);
end;
//Base64
if Format = 1 then begin
AssignFile(FF, Filename);
Reset(FF, 1);
E := Trunc(FileSize(FF) / L);
K := FileSize(FF) - (E * L);
SetLength(S, L);
for I := 1 to E do begin
BlockRead(FF, S[1], L);
WriteLn(F, StringToBase64(S));
end;
if K > 0 then begin
SetLength(S, K);
BlockRead(FF, S[1], K);
WriteLn(F, StringToBase64(S));
end;
CloseFile(FF);
end;
end;
function BreakString(S: String): String;
var
I: Integer;
begin
I := 77;
while Length(S) > I do begin
Insert(#13+#10+#32, S, I);
Inc(I, 78);
end;
Result := S;
end;
procedure SaveMIMEToFile(var SL: TStringList; ContentType, ContentEncoding: String);
var
I: Integer;
S, T, G, H: String;
BL: TStringList;
begin
S := GetContentType('name', ContentType);
S := SkipChar(S, '"');
T := LowerCase(GetContentType('', ContentEncoding));
G := LowerCase(GetContentType('', ContentType));
if T = '' then T := '7bit';
if G = '' then G := 'text/plain';
if S = '' then begin
if not FileExists(sTempMessageFile) then begin
CurrentEMail.MsgType := StandardMsgType;
if G = 'text/html' then begin CurrentEMail.MsgType := mtHtml; S := sTempMessageFile; end else
if G = 'text/plain' then begin CurrentEMail.MsgType := mtText; S := sTempMessageFile; end else
if G = 'text/richtext' then begin CurrentEMail.MsgType := mtRich; S := sTempMessageFile; end else
S := GetMIMEFilename(sTemporaryFolder, G);
end else
S := GetMIMEFilename(sTemporaryFolder, G);
end else
S := sTemporaryFolder+S;
if (T = '7bit') or (T = '8bit') or (T = 'Binary') then begin
//Text file
AssignFile(FT, S);
Rewrite(FT);
for I := 0 to SL.Count-1 do begin
H := SL.Strings[I];
if (I = SL.Count-1) and (H = '') then Break;
WriteLn(FT, H);
end;
CloseFile(FT);
end;
if T = 'quoted-printable' then begin
//Quoted-printable text file
AssignFile(FT, S);
Rewrite(FT);
BL := TStringList.Create;
I := 0;
while I <= SL.Count-1 do begin
H := SL.Strings[I];
if (H <> '') and (H[Length(H)] = '=') then begin
Delete(H, Length(H), 1);
if I < SL.Count-1 then begin
H := H + SL.Strings[I+1];
Inc(I);
end;
end;
H := FromQuotedPrintable(H);
if (I = SL.Count-1) and (H = '') then Break;
WriteLn(FT, H);
Inc(I);
end;
SL.Clear;
SL.Assign(BL);
BL.Free;
{for I := 0 to SL.Count-1 do begin
H := FromQuotedPrintable(SL.Strings[I]);
if (I = SL.Count-1) and (H = '') then Break;
WriteLn(FT, H);
end; }
CloseFile(FT);
end;
if T = 'base64' then begin
//BASE64 binary file
AssignFile(FF, S);
Rewrite(FF, 1);
for I := 0 to SL.Count-1 do
if SL.Strings[I] <> '' then begin
G := Base64ToString(SL.Strings[I]);
BlockWrite(FF, G[1], Length(G));
end;
CloseFile(FF);
end;
end;
procedure GetMIMEType(Filename: String; var Content, Encoding: String; var Format: Byte);
var
S: String;
begin
S := ExtractFileExt(ExtractFilename(Filename));
if S = '.txt' then begin Content := 'text/txt; charset=ISO-8859-1'; Encoding := 'Quoted-printable'; Format := 0; end else
if S = '.rtf' then begin Content := 'text/rtf; charset=ISO-8859-1'; Encoding := 'Quoted-printable'; Format := 0; end else
if S = '.htm' then begin Content := 'text/htm; charset=ISO-8859-1'; Encoding := 'Quoted-printable'; Format := 0; end else
if S = '.html' then begin Content := 'text/html'; Encoding := 'BASE64'; Format := 1; end else
if S = '.pdf' then begin Content := 'application/pdf'; Encoding := 'BASE64'; Format := 1; end else
if S = '.zip' then begin Content := 'application/zip'; Encoding := 'BASE64'; Format := 1; end else
if S = '.doc' then begin Content := 'application/msword'; Encoding := 'BASE64'; Format := 1; end else
if S = '.bmp' then begin Content := 'application/x-bmp'; Encoding := 'BASE64'; Format := 1; end else
if S = '.jpg' then begin Content := 'application/jpeg'; Encoding := 'BASE64'; Format := 1; end else
if S = '.jpeg' then begin Content := 'application/jpeg'; Encoding := 'BASE64'; Format := 1; end else
if S = '.gif' then begin Content := 'image/gif'; Encoding := 'BASE64'; Format := 1; end else
if S = '.ief' then begin Content := 'image/ief'; Encoding := 'BASE64'; Format := 1; end else
if S = '.tif' then begin Content := 'image/tiff'; Encoding := 'BASE64'; Format := 1; end else
if S = '.tiff' then begin Content := 'image/tiff'; Encoding := 'BASE64'; Format := 1; end else
if S = '.mov' then begin Content := 'video/quicktime'; Encoding := 'BASE64'; Format := 1; end else
begin Content := 'application/octet-stream'; Encoding := 'BASE64'; Format := 1; end;
Content := Content+'; Name='+ExtractFilename(Filename);
end;
function GetMIMEFilename(Dir, ContentType: String): String;
var
I: Integer;
S: String;
begin
S := LowerCase(GetContentType('', ContentType));
if S = 'text/plain' then S := 'txt' else
if S = 'text/richtext' then S := 'rtf' else
if S = 'text/html' then S := 'htm' else
if S = 'message/rfc822' then S := 'msg' else
if S = 'message/partial' then S := 'txt' else
if S = 'message/external-body' then S := 'txt' else
if S = 'message/news' then S := 'msg' else
if S = 'application/rtf' then S := 'rtf' else
if S = 'application/pdf' then S := 'pdf' else
if S = 'application/zip' then S := 'zip' else
if S = 'application/msword' then S := 'doc' else
if S = 'application/x-bmp' then S := 'bmp' else
if S = 'image/jpeg' then S := 'jpg' else
if S = 'image/gif' then S := 'gif' else
if S = 'image/ief' then S := 'ief' else
if S = 'image/tiff' then S := 'tif' else
if S = 'video/mpeg' then S := 'mpg' else
if S = 'video/quicktime' then S := 'mov' else
if S = 'multipart/parallel' then S := 'txt' else
if S = 'multipart/alternative' then S := 'txt' else
S := 'bin';
I := 0;
while FileExists(Dir+'Noname'+IntToStr(I)+'.'+S) do
Inc(I);
Result := Dir+'Noname'+IntToStr(I)+'.'+S;
end;
function GetMIMEHeaderField(Field: String): String;
var
I, E: Integer;
S1, S2, S3: String;
begin
Result := '';
S3 := Field;
try
while Pos('=?', Field) > 0 do begin
//Find =?
I := Pos('=?', Field);
Result := Result + Copy(Field, 1, I-1);
Delete(Field, 1, I+1);
//find ?Q?
I := Pos('?', Field);
S1 := Field[I+1];
Delete(Field, 1, I+2);
//find ?=
I := Pos('?=', Field);
S2 := Copy(Field, 1, I-1);
Delete(Field, 1, I+1);
//encode
if UpperCase(S1) = 'Q' then begin
S2 := FromQuotedPrintable(S2);
for E := 1 to Length(S2) do
if S2[E] = '_' then S2[E] := ' ';
end else begin
S2 := Base64ToString(S2);
end;
Result := Result + S2;
end;
Result := Result + Field;
except
Result := S3;
end;
end;
function SetMIMEHeaderField(Field: String): String;
var
I: Integer;
S1, S2: String;
B: Boolean;
begin
Result := Field;
B := False;
for I := 1 to Length(Field) do
if Ord(Field[I]) > 127 then B := True;
if B then begin
I := Pos('<', Field);
S2 := ''; S1 := '';
if I > 0 then begin
S1 := Copy(Field, 1, I-1);
S2 := Copy(Field, I, Length(Field)-I+1);
end else
S1 := Field;
Result := '=?ISO-8859-1?Q?'+ToQuotedPrintable(S1)+'?='+S2;
end;
end;
function FromQuotedPrintable(S: String): String;
var
I, E: Integer;
begin
I := 1;
Result := '';
while I <= Length(S) do begin
if (S[I] = '=') and (I < Length(S)-1) then begin
E := HexToInt(S[I+1]+S[I+2]);
Inc(I, 3);
Result := Result+Chr(E);
end else begin
Result := Result+S[I];
Inc(I);
end;
end;
end;
function ToQuotedPrintable(S: String): String;
var
I: Integer;
begin
Result := '';
for I := 1 to Length(S) do
if (Ord(S[I]) > 127) or (S[I] = '?') or (S[I] = '=') then
Result := Result+'='+IntToHex(Ord(S[I]), 2)
else
Result := Result+S[I];
end;
procedure GetFieldArguments(S: String; var S1, S2: String);
var
E: Integer;
begin
// parse line in S1 and S2 and the ':' between them
E := Pos(':', S);
S1 := Copy(S, 0, E-1);
while (Length(S1) > 0) and (S1[1] = ' ') do Delete(S1, 1, 1);
S2 := Copy(S, E+1, Length(S)-E);
while (Length(S2) > 0) and (S2[1] = ' ') do Delete(S2, 1, 1);
S2 := GetMIMEHeaderField(S2);
end;
procedure MakeFields(var SL: TStringList; ClearEmpties: Boolean);
var
H: TStringList;
I, E: Integer;
S: String;
begin
H := TStringList.Create;
I := -1;
for E := 0 to SL.Count-1 do begin
S := SL.Strings[E];
if ClearEmpties = True then begin
if S <> '' then begin // if not empty line
if S[1] in [#32, #9] then begin // if Tab or Space
Delete(S, 1, 1); // manual linebreak
if I = -1 then I := H.Add(''); // add to previous line
H[I] := H[I]+S;
end else // if normal line
I := H.Add(S);
end;
end;
if ClearEmpties = False then begin
if (Length(S) > 1) and (S[1] in [#32, #9]) then begin // if Tab or Space
Delete(S, 1, 1); // manual linebreak
if I = -1 then I := H.Add(''); // add to previous line
H[I] := H[I]+S;
end else // if normal line
I := H.Add(S);
end;
end;
SL.Clear;
SL.Assign(H);
H.Free;
end;
function GetContentType(Variable, ContentType: String): String;
var
I: Integer;
S: String;
begin
Result := '';
SkipChar(ContentType, ' ');
I := 1; S := '';
while (I <= Length(ContentType)) and (ContentType[I] <> ';') do begin
S := S + ContentType[I];
Inc(I);
end;
if Variable = '' then begin
Result := S;
Exit;
end;
I := Pos(Lowercase(Variable), LowerCase(ContentType));
if I = 0 then Exit;
Delete(ContentType, 1, I+Length(Variable));
I := Pos(';', ContentType);
if I = 0 then I := Length(ContentType)+1;
Result := Copy(ContentType, 1, I-1);
end;
function SkipChar(S: String; C: Char): String;
var
I: Integer;
begin
Result := '';
for I := 1 to Length(S) do
if S[I] <> C then
Result := Result + S[I];
end;
procedure CommataStringToStringList(S: String; var SL: TStringList);
var
T: String;
I: Integer;
begin
SL.Clear;
T := '';
S := S+',';
for I := 1 to Length(S) do
if S[I] = ',' then begin
if Length(T) > 1 then while (T[1] = ' ') or (T[1] = #9) do Delete(T, 1, 1);
T := GetMIMEHeaderField(T);
if T <> '' then SL.Add(T);
T := '';
end else begin
if S[I] <> '"' then T := T+S[I];
end;
end;
function StringListToCommataString(SL: TStringList; Decoded: Boolean): String;
var
S: String;
I, E: Integer;
begin
S := '';
E := 0;
for I := 0 to SL.Count-1 do
if SL.Strings[I] <> '' then begin
if Decoded then SL.Strings[I] := SetMIMEHeaderField(SL.Strings[I]);
if E = 0 then
S := S+SL.Strings[I]
else
S := S+', '+SL.Strings[I];
Inc(E);
end;
Result := S;
end;
procedure SkipSpaces(var S: String);
begin
while (Length(S) > 1) and ((S[1] = #32) or (S[1] = #9)) do
Delete(S, 1, 1);
end;
function GetToken(var S: String): String;
var
I: Integer;
begin
SkipSpaces(S);
Result := '';
I := 1;
while (I <= Length(S)) and (S[I] <> #32) and (S[I] <> #9) do begin
Result := Result+S[I];
Inc(I);
end;
Delete(S, 1, I-1);
end;
function ApplyFilter(Msg, Filter: String; var Points: Integer): Boolean;
var
SL: TStringList;
S, LMsg: String;
C: Char;
I: Integer;
B: Boolean;
{ 1 = normal
2 = plus
3 = minus
4 = normal quoted
5 = plus quoted
6 = minus quoted }
function ExtractQuoted(var S: String): Boolean;
begin
Result := False;
if S[1] = '"' then begin
Delete(S, 1, 1);
Filter := S + Filter;
S := '';
I := 1;
while (I <= Length(Filter)) and (Filter[I] <> '"') do begin
S := S+Filter[I];
Inc(I);
end;
Delete(Filter, 1, I);
Result := True;
end;
end;
begin
Points := 0;
if Filter = '' then begin Result := True; Exit; end;
SL := TStringList.Create;
S := GetToken(Filter);
while S <> '' do begin
if S[1] = '+' then begin
Delete(S, 1, 1);
if ExtractQuoted(S) then SL.Add('5'+S) else
SL.Add(LowerCase('2'+S));
end else
if S[1] = '-' then begin
Delete(S, 1, 1);
if ExtractQuoted(S) then SL.Add('6'+S) else
SL.Add(LowerCase('3'+S));
end else begin
if ExtractQuoted(S) then SL.Add('4'+S) else
SL.Add(LowerCase('1'+S));
end;
S := GetToken(Filter);
end;
LMsg := LowerCase(Msg);
B := True;
Points := 0;
for I := 0 to SL.Count-1 do begin
S := SL.Strings[I];
C := S[1];
Delete(S, 1, 1);
if C = '1' then
if Pos(S, LMsg) > 0 then Inc(Points);
if C = '2' then
if Pos(S, LMsg) = 0 then B := False else Inc(Points);
if C = '3' then
if Pos(S, LMsg) > 0 then B := False else Inc(Points);
if C = '4' then
if Pos(S, Msg) > 0 then Inc(Points);
if C = '5' then
if Pos(S, Msg) = 0 then B := False else Inc(Points);
if C = '6' then
if Pos(S, Msg) > 0 then B := False else Inc(Points);
end;
if B = False then Points := -1;
Result := B;
SL.Free;
end;
function GetUniqueMailName: String;
var
Ho, Mi, Se, Ms: Word;
begin
DecodeTime(Time, Ho, Mi, Se, Ms);
Result := Chr(Random(26)+97) + Chr(Random(26)+97) +
IntToStr(Mi) + IntToStr(Se) + IntToStr(Random(100));
end;
function ExtractEMailAddress(S: String): String;
var
I, E: Integer;
begin
Result := S;
I := Pos('<', S);
E := Pos('>', S);
if (I = 0) or (E = 0) then Exit;
Result := Copy(S, I+1, E-I-1);
end;
function ExtractEMailName(S: String): String;
var
I, E: Integer;
begin
Result := '';
I := Pos('<', S);
E := Pos('>', S);
if (I = 0) or (E = 0) then Exit;
Result := Copy(S, 1, I-1);
end;
procedure GetOutcomingMailFileList(Node: TTreeNode; var SL: TStringList);
procedure GetFiles(Dir: String);
var
SearchRec: TSearchRec;
Found: Integer;
begin
Found := FindFirst(Dir+'*.msg', faAnyFile, SearchRec);
while Found = 0 do begin
SL.Add(Dir+SearchRec.Name);
Found := FindNext(SearchRec);
end;
FindClose(SearchRec);
end;
procedure GetSubNotes(Node: TTreeNode);
var
I: Integer;
begin
for I := 0 to Node.Count-1 do begin
if PFolderData(Node.Item[I].Data)^.OutBox then
GetFiles(PFolderData(Node.Item[I].Data)^.Path);
if Node.Count > 0 then GetSubNotes(Node.Item[I]);
end;
end;
begin
GetSubNotes(Node);
end;
function StretchString(S: String; Count: Integer): String;
begin
Result := S;
while Length(Result) < Count do Result := Result+' ';
end;
function SkipSpacesAtEnd(S: String): String;
var
I: Integer;
begin
I := Length(S);
while (I > 0) and (S[I] = ' ') do begin Dec(I); SetLength(S, I); end;
Result := S;
end;
end.