/[projects]/dao/DelphiScanner/Components/tpsystools_4.04/source/StMime.pas
ViewVC logotype

Annotation of /dao/DelphiScanner/Components/tpsystools_4.04/source/StMime.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (hide annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 10 months ago) by torben
File size: 53358 byte(s)
Added tpsystools component
1 torben 2671 // Upgraded to Delphi 2009: Sebastian Zierer
2    
3     (* ***** BEGIN LICENSE BLOCK *****
4     * Version: MPL 1.1
5     *
6     * The contents of this file are subject to the Mozilla Public License Version
7     * 1.1 (the "License"); you may not use this file except in compliance with
8     * the License. You may obtain a copy of the License at
9     * http://www.mozilla.org/MPL/
10     *
11     * Software distributed under the License is distributed on an "AS IS" basis,
12     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13     * for the specific language governing rights and limitations under the
14     * License.
15     *
16     * The Original Code is TurboPower SysTools
17     *
18     * The Initial Developer of the Original Code is
19     * TurboPower Software
20     *
21     * Portions created by the Initial Developer are Copyright (C) 1996-2002
22     * the Initial Developer. All Rights Reserved.
23     *
24     * Contributor(s):
25     *
26     * ***** END LICENSE BLOCK ***** *)
27    
28     {*********************************************************}
29     {* SysTools: StMime.pas 4.04 *}
30     {*********************************************************}
31     {* SysTools: Internet Conversion unit for SysTools *}
32     {*********************************************************}
33    
34     {$I StDefine.inc}
35    
36     {
37     Note: Some Mime routines rely on overflows for their results,
38     so these need to be off:
39     }
40     {$R-}
41     {$Q-}
42    
43     unit StMime;
44    
45     interface
46    
47     uses
48     Windows,
49     SysUtils,
50     Classes,
51     StConst,
52     StBase,
53     StStrZ,
54     StStrL,
55     StOStr;
56    
57     const
58     AttachmentFileMode = (fmOpenRead or fmShareDenyWrite);
59     CRLFStr : array[0..1] of AnsiChar = #13#10;
60     DefStContentDisposition : string[11] = 'attachment';
61     DefStContentType : string[27] = 'application/octet-stream';
62     DefStMimeEncoding : string[7] = 'base64';
63     ExtractFileMode = (fmOpenReadWrite or fmShareExclusive);
64     MaxMimeLine = 78;
65    
66     type
67    
68     TStConvertState = (csStarted, csProgressing, csFinished);
69    
70     TStProgressEvent =
71     procedure(Sender : TObject; Status : TStConvertState; PercentDone : Byte) of object;
72    
73     TStSaveAsEvent = procedure(Sender : TObject; var FileName : string) of object;
74    
75     {.Z+}
76     TStMimeConverter = class;
77    
78     { Base conversion stream }
79     TStConvertStream = class
80     protected {private}
81     FCurrentFile : string;
82     FOwner : TStMimeConverter;
83     FOnProgress : TStProgressEvent;
84     public
85     constructor Create(Owner : TStMimeConverter); virtual;
86     procedure DecodeToStream(InStream, OutStream : TStream); virtual; abstract;
87     procedure EncodeToStream(InStream, OutStream : TStream); virtual; abstract;
88     procedure Progress(Status : TStConvertState; PercentDone : Byte); virtual;
89     property CurrentFile : string
90     read FCurrentFile write FCurrentFile;
91     property OnProgress : TStProgressEvent
92     read FOnProgress write FOnProgress;
93     end;
94    
95     { Conversion stream for raw copying }
96     TStRawStream = class(TStConvertStream)
97     public
98     constructor Create(Owner : TStMimeConverter); override;
99     procedure DecodeToStream(InStream, OutStream : TStream); override;
100     procedure EncodeToStream(InStream, OutStream : TStream); override;
101     end;
102    
103     { Conversion stream for Quoted-Printable }
104     TStQuotedStream = class(TStConvertStream)
105     public
106     constructor Create(Owner : TStMimeConverter); override;
107     procedure DecodeToStream(InStream, OutStream : TStream); override;
108     procedure EncodeToStream(InStream, OutStream : TStream); override;
109     end;
110    
111     { Conversion stream for UUEncoding }
112     TStUUStream = class(TStConvertStream)
113     public
114     constructor Create(Owner : TStMimeConverter); override;
115     procedure DecodeToStream(InStream, OutStream : TStream); override;
116     procedure EncodeToStream(InStream, OutStream : TStream); override;
117     end;
118    
119     { Conversion stream for Base64 }
120     TStBase64Stream = class(TStConvertStream)
121     public
122     constructor Create(Owner : TStMimeConverter); override;
123     procedure DecodeToStream(InStream, OutStream : TStream); override;
124     procedure EncodeToStream(InStream, OutStream : TStream); override;
125     end;
126    
127     {.Z-}
128    
129     TStConverterClass = class of TStConvertStream;
130    
131     TStAttachment = class
132     protected {private}
133     FContentDescription : string;
134     FContentDisposition : string;
135     FContentType : string;
136     FEncoding : string;
137     FFileName : string;
138     FOldStyle : Boolean;
139     FSize : LongInt;
140     FStreamOffset : LongInt;
141     public
142     { Description of this attachment }
143     property atContentDescription : string
144     read FContentDescription write FContentDescription;
145    
146     { Disposition of this attachment }
147     property atContentDisposition : string
148     read FContentDisposition write FContentDisposition;
149    
150     { Content type of this attachment }
151     property atContentType : string
152     read FContentType write FContentType;
153    
154     { Encoding used for this attachment }
155     property atEncoding : string
156     read FEncoding write FEncoding;
157    
158     { Filename for this attachment }
159     property atFilename : string
160     read FFileName write FFileName;
161    
162     { Old style (non-mime) attachment }
163     property atOldStyle : Boolean
164     read FOldStyle write FOldStyle;
165    
166     { Size of attachment (in the unencoded state) }
167     property atSize : LongInt
168     read FSize write FSize;
169    
170     { Offset of attachment in message }
171     property atStreamOffset : LongInt
172     read FStreamOffset write FStreamOffset;
173     end;
174    
175     TStMimeConverter = class
176     protected {private}
177     {.Z+}
178     FAttachments : TStringList;
179     FBoundary : string;
180     FBoundaryUsed : Boolean;
181     FContentDescription : string;
182     FContentDisposition : string;
183     FContentType : string;
184     FConverter : TStConvertStream;
185     FDirectory : string;
186     FEncoding : string;
187     FEndBoundaryOffset : LongInt;
188     FMimeHeaders : Boolean;
189     FStream : TStream;
190     FInternalStream : TMemoryStream;
191     FOnProgress : TStProgressEvent;
192     FOnSaveAs : TStSaveAsEvent;
193     procedure AddMimeFooters;
194     procedure AddMimeHeaders(const AFileName : string);
195     procedure DeleteAttachments;
196     procedure ForceType(ConverterType : TStConverterClass);
197     function GetBoundary : string;
198     function GetStream : TStream;
199     procedure InitConverter;
200     procedure SetBoundary(Value : string);
201     procedure SetConverter(Value : TStConvertStream);
202     procedure SetEncoding(Value : string);
203     procedure SetStream(Value : TStream);
204     {.Z-}
205     protected
206     procedure FindOldAttachment;
207     function GenerateBoundary : string; dynamic;
208     procedure PositionForExtract(Att : TStAttachment); dynamic;
209     procedure Progress(Sender : TObject; Status : TStConvertState;
210     PercentDone : Byte); dynamic;
211     procedure SaveAs(var FileName : string);
212     procedure ScanAttachments;
213     public
214     constructor Create;
215     constructor CreateInit(AStream : TStream); virtual;
216     destructor Destroy; override;
217     procedure AddFileAttachment(const AFileName : string);
218     procedure AddStreamAttachment(AStream : TStream; const AFileName : string); dynamic;
219     procedure ExtractAttachment(const Attachment : string); dynamic;
220     procedure ExtractAttachmentIndex(Index : Integer); dynamic;
221     procedure ExtractToStream(Index : Integer; AStream : TStream); dynamic;
222     procedure ExtractAttachments;
223     procedure FillConverterList(List : TStrings);
224     function GetTag(const Description : string): string;
225     class procedure RegisterConverter(const ATag, ADesc : string;
226     AClass : TStConverterClass);
227     class procedure UnRegisterConverterClass(AClass : TStConverterClass);
228    
229     { List of attachments in current stream }
230     property Attachments : TStringList
231     read FAttachments;
232    
233     { Boundary being used for attachments }
234     property Boundary : string
235     read GetBoundary write SetBoundary;
236    
237     { Default encoding to use for attachments }
238     property Encoding : string
239     read FEncoding write SetEncoding;
240    
241     { Default Content Description to use for attachments }
242     property ContentDescription : string
243     read FContentDescription write FContentDescription;
244    
245     { Default Content Disposition to use for attachments }
246     property ContentDisposition : string
247     read FContentDisposition write FContentDisposition;
248    
249     { Default Content Type to use for attachments }
250     property ContentType : string
251     read FContentType write FContentType;
252    
253     { Instance of converter to be used with current encoding method }
254     property Converter : TStConvertStream
255     read FConverter write SetConverter;
256    
257     { Default directory used for ExtractAttachments }
258     property Directory : string
259     read FDirectory write FDirectory;
260    
261     { Determines whether Mime boundaries/headers are added to attachments }
262     property MimeHeaders : Boolean
263     read FMimeHeaders write FMimeHeaders default True;
264    
265     { Access to internal stream }
266     property Stream : TStream
267     read GetStream write SetStream;
268    
269     { Progress event -- optional for converters to support this }
270     property OnProgress : TStProgressEvent
271     read FOnProgress write FOnProgress;
272    
273     { SaveAs event -- fired when extracting an attachment }
274     property OnSaveAs : TStSaveAsEvent
275     read FOnSaveAs write FOnSaveAs;
276     end;
277    
278     implementation
279    
280     const
281     StUUTable : array[0..63] of AnsiChar = (#96, #33, #34, #35, #36, #37,
282     #38, #39, #40, #41, #42, #43, #44, #45, #46, #47, #48, #49,
283     #50, #51, #52, #53, #54, #55, #56, #57, #58, #59, #60, #61,
284     #62, #63, #64, #65, #66, #67, #68, #69, #70, #71, #72, #73,
285     #74, #75, #76, #77, #78, #79, #80, #81, #82, #83, #84, #85,
286     #86, #87, #88, #89, #90, #91, #92, #93, #94, #95);
287    
288     const
289     St64Table : array[0..63] of AnsiChar = ( #65, #66, #67, #68, #69,
290     #70, #71, #72, #73, #74, #75, #76, #77, #78, #79,
291     #80, #81, #82, #83, #84, #85, #86, #87, #88, #89,
292     #90, #97, #98, #99, #100, #101, #102, #103, #104, #105,
293     #106, #107, #108, #109, #110, #111, #112, #113, #114, #115,
294     #116, #117, #118, #119, #120, #121, #122, #48, #49, #50,
295     #51, #52, #53, #54, #55, #56, #57, #43, #47);
296    
297     const
298     StD64Table : array[43..122] of Byte = ($3E, $7F, $7F, $7F, $3F, $34,
299     $35, $36, $37, $38, $39, $3A, $3B, $3C, $3D, $7F, $7F, $7F, $7F,
300     $7F, $7F, $7F, $00, $01, $02, $03, $04, $05, $06, $07, $08, $09,
301     $0A, $0B, $0C, $0D, $0E, $0F, $10, $11, $12, $13, $14, $15, $16,
302     $17, $18, $19, $7F, $7F, $7F, $7F, $7F, $7F, $1A, $1B, $1C, $1D,
303     $1E, $1F, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $2A,
304     $2B, $2C, $2D, $2E, $2F, $30, $31, $32, $33);
305    
306    
307     var
308     CvtLock : TRTLCriticalSection;
309    
310     type
311     TStContentTag = (ctType, ctEncoding, ctDescription, ctDisposition);
312    
313     PStTernaryNode = ^TStTernaryNode;
314     TStTernaryNode = record
315     SplitChar : AnsiChar;
316     LoKid, EqKid, HiKid : PStTernaryNode;
317     end;
318    
319     TStTernaryTree = class
320     private
321     Root : PStTernaryNode;
322     pData : Pointer;
323     function Insert(P : PStTernaryNode; C : PAnsiChar) : PStTernaryNode;
324     class procedure DeleteSubTree(Root : PStTernaryNode);
325     function NewNode : PStTernaryNode;
326     public
327     destructor Destroy; override;
328     procedure InsertStr(C : PAnsiChar; Data : Pointer);
329     function SearchUC(C : PAnsiChar; var Data : Pointer) : Boolean;
330     end;
331    
332     { TStTernaryTree }
333     class procedure TStTernaryTree.DeleteSubTree(Root : PStTernaryNode);
334     begin
335     if Root <> nil then begin
336     DeleteSubTree(Root^.LoKid);
337     if Root^.SplitChar <> #0 then
338     DeleteSubTree(Root^.EqKid);
339     DeleteSubTree(Root^.HiKid);
340     Dispose(Root);
341     end;
342     end;
343    
344     destructor TStTernaryTree.Destroy;
345     begin
346     DeleteSubTree(Root);
347     inherited Destroy;
348     end;
349    
350     function TStTernaryTree.NewNode : PStTernaryNode;
351     begin
352     Result := AllocMem(SizeOf(TStTernaryNode));
353     end;
354    
355     function TStTernaryTree.Insert(P : PStTernaryNode; C : PAnsiChar) : PStTernaryNode;
356     begin
357     if P = nil then begin
358     P := NewNode;
359     P^.SplitChar := C^;
360     if C^ <> #0 then begin
361     Inc(C);
362     P^.EqKid := Insert(P^.EqKid,C);
363     end else
364     P^.EqKid := pData;
365     Result := P;
366     Exit;
367     end;
368     if C^ < P^.SplitChar then
369     P^.LoKid := Insert(P^.LoKid,C)
370     else if C^ = P^.SplitChar then
371     if C^ <> #0 then begin
372     Inc(C);
373     P^.EqKid := Insert(P^.EqKid,C);
374     end else
375     RaiseStError(EStMimeError, stscDupeString)
376     else
377     P^.HiKid := Insert(P^.HiKid,C);
378     Result := P;
379     end;
380    
381     procedure TStTernaryTree.InsertStr(C : PAnsiChar; Data : Pointer);
382     begin
383     pData := Data;
384     Root := Insert(Root, C);
385     end;
386    
387     function TStTernaryTree.SearchUC(C : PAnsiChar; var Data : Pointer) : Boolean;
388     var
389     P : PStTernaryNode;
390     CU : AnsiChar;
391     begin
392     P := Root;
393     while P <> nil do begin
394     CU := System.UpCase(C^);
395     if CU < P^.SplitChar then
396     P := P^.LoKid
397     else if CU = P^.SplitChar then begin
398     Inc(C);
399     if C^ = #0 then begin
400     Data := P^.EqKid^.EqKid;
401     Result := True;
402     Exit;
403     end;
404     P := P^.EqKid;
405     end else
406     P := P^.HiKid;
407     end;
408     Result := False;
409     end;
410    
411     { TStConvertStream }
412     constructor TStConvertStream.Create(Owner : TStMimeConverter);
413     begin
414     FOwner := Owner;
415     inherited Create;
416     end;
417    
418     procedure TStConvertStream.Progress(Status : TStConvertState; PercentDone : Byte);
419     begin
420     if Assigned(FOnProgress) then
421     OnProgress(Self, Status, PercentDone);
422     end;
423    
424     { TStRawStream }
425     constructor TStRawStream.Create(Owner : TStMimeConverter);
426     begin
427     inherited Create(Owner);
428     end;
429    
430     procedure TStRawStream.DecodeToStream(InStream, OutStream : TStream);
431     begin
432     Progress(csStarted, 0);
433     try
434     OutStream.CopyFrom(InStream, InStream.Size-InStream.Position);
435     except
436     Progress(csFinished, 0);
437     raise;
438     end;
439     Progress(csFinished, 100);
440     end;
441    
442     procedure TStRawStream.EncodeToStream(InStream, OutStream : TStream);
443     begin
444     Progress(csStarted, 0);
445     try
446     OutStream.CopyFrom(InStream, InStream.Size);
447     except
448     Progress(csFinished, 0);
449     raise;
450     end;
451     Progress(csFinished, 100);
452     end;
453    
454     { TStQuotedStream }
455     constructor TStQuotedStream.Create(Owner : TStMimeConverter);
456     begin
457     inherited Create(Owner);
458     end;
459    
460     procedure TStQuotedStream.DecodeToStream(InStream, OutStream : TStream);
461     var
462     I, O, Count, WS : Byte;
463     InBuf : array[0..85] of Byte;
464     OutBuf : array[0..85] of Byte;
465     Decoding : Boolean;
466     Keeper : Boolean;
467     begin
468     FillChar(InBuf, SizeOf(InBuf), #0);
469     WS := $FF;
470     Decoding := True;
471     Keeper := False;
472    
473     { Skip any CR/LF's to get to the encoded stuff }
474     while True do begin
475     Count := InStream.Read(InBuf, 1);
476     { End of stream -- exit assuming we're done }
477     if Count <> 1 then Exit;
478     if ((InBuf[0] <> $0D) and (InBuf[0] <> $0A)) then begin
479     Keeper := True;
480     Break;
481     end;
482     end;
483    
484     while Decoding do begin
485     { Initialize }
486     if Keeper then begin
487     I := 1;
488     Keeper := False;
489     end else begin
490     I := 0;
491     end;
492     O := 0;
493    
494     { Read in one line at a time - skipping over bad characters }
495     while True do begin
496     if InStream.Read(InBuf[I], 1) = 0 then Break;
497     case InBuf[I] of
498     $0A : Continue;
499     $0D : begin
500     Inc(I);
501     Break;
502     end;
503     { Test for potential end of data }
504     { '--' is probably the next Mime boundary }
505     $2D : if (I = 1) and (InBuf[0] = $2D) then Exit;
506     end;
507     Inc(I);
508     end;
509    
510     if I = 0 then Exit;
511     Count := I;
512     I := 0;
513    
514     { Decode data to output stream }
515     while I < Count do begin
516     case InBuf[I] of
517     9 : begin
518     if WS = $FF then
519     WS := O;
520     OutBuf[O] := InBuf[I];
521     Inc(O);
522     Inc(I);
523     end;
524     13 : if WS = $FF then begin
525     OutBuf[O] := 13;
526     OutBuf[O+1] := 10;
527     Inc(O, 2);
528     Inc(I);
529     end else begin
530     OutBuf[WS] := 13;
531     OutBuf[WS+1] := 10;
532     O := WS+2;
533     Inc(I);
534     end;
535     32 : begin
536     if WS = $FF then
537     WS := O;
538     OutBuf[O] := InBuf[I];
539     Inc(O);
540     Inc(I);
541     end;
542     33..60 : begin
543     WS := $FF;
544     OutBuf[O] := InBuf[I];
545     Inc(O);
546     Inc(I);
547     end;
548     61 : begin
549     WS := $FF;
550     if I+2 >= Count then Break;
551     case InBuf[I+1] of
552     48 : OutBuf[O] := 0; {0}
553     49 : OutBuf[O] := 16; {1}
554     50 : OutBuf[O] := 32; {2}
555     51 : OutBuf[O] := 48; {3}
556     52 : OutBuf[O] := 64; {4}
557     53 : OutBuf[O] := 80; {5}
558     54 : OutBuf[O] := 96; {6}
559     55 : OutBuf[O] := 112; {7}
560     56 : OutBuf[O] := 128; {8}
561     57 : OutBuf[O] := 144; {9}
562     65 : OutBuf[O] := 160; {A}
563     66 : OutBuf[O] := 176; {B}
564     67 : OutBuf[O] := 192; {C}
565     68 : OutBuf[O] := 208; {D}
566     69 : OutBuf[O] := 224; {E}
567     70 : OutBuf[O] := 240; {F}
568     97 : OutBuf[O] := 160; {a}
569     98 : OutBuf[O] := 176; {b}
570     99 : OutBuf[O] := 192; {c}
571     100 : OutBuf[O] := 208; {d}
572     101 : OutBuf[O] := 224; {e}
573     102 : OutBuf[O] := 240; {f}
574     end;
575     case InBuf[I+2] of
576     48 : ; {0}
577     49 : OutBuf[O] := OutBuf[O] + 1; {1}
578     50 : OutBuf[O] := OutBuf[O] + 2; {2}
579     51 : OutBuf[O] := OutBuf[O] + 3; {3}
580     52 : OutBuf[O] := OutBuf[O] + 4; {4}
581     53 : OutBuf[O] := OutBuf[O] + 5; {5}
582     54 : OutBuf[O] := OutBuf[O] + 6; {6}
583     55 : OutBuf[O] := OutBuf[O] + 7; {7}
584     56 : OutBuf[O] := OutBuf[O] + 8; {8}
585     57 : OutBuf[O] := OutBuf[O] + 9; {9}
586     65 : OutBuf[O] := OutBuf[O] + 10; {A}
587     66 : OutBuf[O] := OutBuf[O] + 11; {B}
588     67 : OutBuf[O] := OutBuf[O] + 12; {C}
589     68 : OutBuf[O] := OutBuf[O] + 13; {D}
590     69 : OutBuf[O] := OutBuf[O] + 14; {E}
591     70 : OutBuf[O] := OutBuf[O] + 15; {F}
592     97 : OutBuf[O] := OutBuf[O] + 10; {a}
593     98 : OutBuf[O] := OutBuf[O] + 11; {b}
594     99 : OutBuf[O] := OutBuf[O] + 12; {c}
595     100 : OutBuf[O] := OutBuf[O] + 13; {d}
596     101 : OutBuf[O] := OutBuf[O] + 14; {e}
597     102 : OutBuf[O] := OutBuf[O] + 15; {f}
598     end;
599     Inc(I, 3);
600     Inc(O);
601     end;
602     62..126 : begin
603     WS := $FF;
604     OutBuf[O] := InBuf[I];
605     Inc(O);
606     Inc(I);
607     end;
608     else
609     Inc(I);
610     end;
611     end;
612    
613     if O>0 then
614     OutStream.Write(OutBuf, O)
615     else
616     Break; { OutBuf is empty }
617     end;
618     end;
619    
620     procedure TStQuotedStream.EncodeToStream(InStream, OutStream : TStream);
621     var
622     O, W : Integer;
623     WordBuf, OutBuf : array[0..80] of AnsiChar;
624     CurChar : AnsiChar;
625    
626     procedure SendLine;
627     begin
628     if (OutBuf[O-1] = #9) or (OutBuf[O-1] = #32) then begin
629     OutBuf[O] := '=';
630     Inc(O);
631     end;
632     OutBuf[O] := #13;
633     OutBuf[O+1] := #10;
634     Inc(O, 2);
635     OutStream.Write(OutBuf, O);
636     O := 0;
637     end;
638    
639     procedure AddWordToOutBuf;
640     var
641     J : Integer;
642     begin
643     if (O + W) > 74 then SendLine;
644     for J := 0 to (W - 1) do begin
645     OutBuf[O] := WordBuf[J];
646     Inc(O);
647     end;
648     W := 0;
649     end;
650    
651     procedure AddHexToWord(B : Byte);
652     begin
653     if W > 73 then AddWordToOutBuf;
654     WordBuf[W] := '=';
655     WordBuf[W+1] := StHexDigits[B shr 4];
656     WordBuf[W+2] := StHexDigits[B and $F];
657     Inc(W, 3)
658     end;
659    
660     begin
661     O := 0;
662     W := 0;
663     while InStream.Read(CurChar, 1) <> 0 do begin
664     if (Ord(CurChar) in [33..60, 62..126]) then begin
665     WordBuf[W] := CurChar;
666     Inc(W);
667     if W > 74 then AddWordToOutBuf;
668     end else if (CurChar = ' ') or (CurChar = #9) then begin
669     WordBuf[W] := CurChar;
670     Inc(W);
671     AddWordToOutBuf;
672     end else if (CurChar = #13) then begin
673     AddWordToOutBuf;
674     SendLine;
675     end else if (CurChar = #10) then begin
676     { Do nothing }
677     end else begin
678     AddHexToWord(Byte(CurChar));
679     end;
680     end;
681     end;
682    
683     { TStUUStream }
684     constructor TStUUStream.Create(Owner : TStMimeConverter);
685     begin
686     inherited Create(Owner);
687     end;
688    
689     procedure TStUUStream.DecodeToStream(InStream, OutStream : TStream);
690     var
691     I, O, Len, Count : Byte;
692     InBuf : array[0..85] of Byte;
693     OutBuf : array[0..65] of Byte;
694     FirstLine : Boolean;
695     begin
696     FirstLine := True;
697    
698     while True do begin
699     { Initialize }
700     I := 0;
701     O := 0;
702    
703     { Skip any CR/LF's to get to the encoded stuff }
704     while True do begin
705     Count := InStream.Read(InBuf, 1);
706     { End of stream -- bail assuming we're done }
707     if Count <> 1 then Exit;
708     if FirstLine then begin
709     if ((InBuf[0] <> $0D) and (InBuf[0] <> $0A)) then begin
710     FirstLine := False;
711     Break;
712     end;
713     end else begin
714     if ((InBuf[0] = $0D) or (InBuf[0] = $0A)) then FirstLine := True;
715     end;
716     end;
717    
718     { We're done }
719     if AnsiChar(InBuf[0]) = '`' then Exit;
720    
721     { Get count for this line }
722     Len := (((InBuf[0] - $20) and $3F) * 4) div 3;
723     if (((InBuf[0] - $20) and $3F) * 4) mod 3 <> 0 then
724     Inc(Len);
725    
726     Count := InStream.Read(InBuf, Len);
727    
728     { Unexpected situation }
729     if (Count <> Len) or (Count > 63) then
730     RaiseStError(EStMimeError, stscInStream);
731     { Decode buffer }
732     while (I < Count) do begin
733     if ((Count - I) >= 4) then begin
734     {!!.01 -- Changed}
735     {Note: indexing inverted to avoid compiler problem with D6upd2 and BCB6}
736     OutBuf[O+2] := (((InBuf[I+2] - $20) and $3F) shl 6) or
737     (((InBuf[I+3] - $20) and $3F));
738     OutBuf[O+1] := (((InBuf[I+1] - $20) and $3F) shl 4) or
739     (((InBuf[I+2] - $20) and $3F) shr 2);
740     OutBuf[O] := (((InBuf[I] - $20) and $3F) shl 2) or
741     (((InBuf[I+1] - $20) and $3F) shr 4);
742     {!!.01 -- End Changed}
743     Inc(O, 3);
744     end else begin
745     if (Count >= 2) then begin
746     OutBuf[O] := (((InBuf[I] - $20) and $3F) shl 2) or
747     (((InBuf[I+1] - $20) and $3F) shr 4);
748     Inc(O);
749     end;
750     if (Count >= 3) then begin
751     OutBuf[O+1] := (((InBuf[I+1] - $20) and $3F) shl 4) or
752     (((InBuf[I+2] - $20) and $3F) shr 2);
753     Inc(O);
754     end;
755     end;
756     Inc(I, 4);
757     end;
758     OutStream.Write(OutBuf, O);
759    
760     end;
761     end;
762    
763     procedure TStUUStream.EncodeToStream(InStream, OutStream : TStream);
764     var
765     I, O, Count, Temp : Byte;
766     InBuf : array[1..45] of Byte;
767     OutBuf : array[0..63] of AnsiChar;
768     S : AnsiString;
769     begin
770     S := Format('begin 600 %s'#13#10, [FCurrentFile]);
771     OutStream.Write(S[1], Length(S));
772    
773     { Encode and stream the attachment }
774     repeat
775     Count := InStream.Read(InBuf, SizeOf(InBuf));
776     if Count <= 0 then Break;
777     I := 1;
778     O := 0;
779     OutBuf[O] := AnsiChar(StUUTable[Count and $3F]);
780     Inc(O);
781     while I+2 <= Count do begin
782     { Encode 1st byte }
783     Temp := (InBuf[I] shr 2);
784     OutBuf[O] := AnsiChar(StUUTable[Temp and $3F]);
785    
786     { Encode 1st/2nd byte }
787     Temp := (InBuf[I] shl 4) or (InBuf[I+1] shr 4);
788     OutBuf[O+1] := AnsiChar(StUUTable[Temp and $3F]);
789    
790     { Encode 2nd/3rd byte }
791     Temp := (InBuf[I+1] shl 2) or (InBuf[I+2] shr 6);
792     OutBuf[O+2] := AnsiChar(StUUTable[Temp and $3F]);
793    
794     { Encode 3rd byte }
795     Temp := (InBuf[I+2] and $3F);
796     OutBuf[O+3] := AnsiChar(StUUTable[Temp]);
797    
798     Inc(I, 3);
799     Inc(O, 4);
800     end;
801    
802     { Are there odd bytes to add? }
803     if (I <= Count) then begin
804     Temp := (InBuf[I] shr 2);
805     OutBuf[O] := AnsiChar(StUUTable[Temp and $3F]);
806    
807     { One odd byte }
808     if (I = Count) then begin
809     Temp := (InBuf[I] shl 4) and $30;
810     OutBuf[O+1] := AnsiChar(StUUTable[Temp and $3F]);
811     Inc(O, 2);
812     { Two odd bytes }
813     end else begin
814     Temp := ((InBuf[I] shl 4) and $30) or ((InBuf[I+1] shr 4) and $0F);
815     OutBuf[O+1] := AnsiChar(StUUTable[Temp and $3F]);
816     Temp := (InBuf[I+1] shl 2) and $3C;
817     OutBuf[O+2] := AnsiChar(StUUTable[Temp and $3F]);
818     Inc(O, 3);
819     end;
820     end;
821    
822     { Add CR/LF }
823     OutBuf[O] := #13;
824     OutBuf[O+1] := #10;
825    
826     { Write line to stream }
827     OutStream.Write(OutBuf, (O + 2));
828     until Count < SizeOf(InBuf);
829    
830     { Add terminating end }
831     StrCopy(StrECopy(OutBuf, '`'#13#10), 'end'#13#10);
832     OutStream.Write(OutBuf, StrLen(OutBuf));
833     end;
834    
835     { TStBase64Stream }
836     constructor TStBase64Stream.Create(Owner : TStMimeConverter);
837     begin
838     inherited Create(Owner);
839     end;
840    
841     procedure TStBase64Stream.DecodeToStream(InStream, OutStream : TStream);
842     var
843     I, O, Count, c1, c2, c3 : Byte;
844     InBuf : array[0..85] of Byte;
845     OutBuf : array[0..65] of Byte;
846     Decoding : Boolean;
847     begin
848     Decoding := True;
849     while Decoding do begin
850     { Initialize }
851     O := 0;
852     I := 0;
853    
854     { Skip any CR/LF's to get to the encoded stuff }
855     while True do begin
856     Count := InStream.Read(InBuf, 1);
857     { End of stream -- exit assuming we're done }
858     if Count <> 1 then Exit;
859     if ((InBuf[0] <> $0D) and (InBuf[0] <> $0A)) then begin
860     Inc(I);
861     Break;
862     end;
863     end;
864    
865     { Read in a line at a time - skipping over bad characters }
866     while True do begin
867     if InStream.Read(InBuf[I], 1) = 0 then Break;
868     case InBuf[I] of
869     $00..$09 : Continue;
870     $0A : Break;
871     $0B..$0C : Continue;
872     $0D : Break;
873     $0E..$2A : Continue;
874     { Test for potential end of data }
875     { '--' is probably the next Mime boundary }
876     $2D : begin
877     if I = 1 then
878     if (InBuf[0] = $2D) then
879     Exit;
880     Continue;
881     end;
882     $3D : begin
883     Inc(I);
884     Break;
885     end;
886     $7B..$FF : Continue;
887     end;
888    
889     if (StD64Table[InBuf[I]] = $7F) then Continue;
890     Inc(I);
891     end;
892    
893     { 'end' can mean end of data for base64 }
894     if (InBuf[0] = $65) and (InBuf[1] = $6E) and
895     (InBuf[2] = $64) and (InBuf[3] = $0D) then Exit;
896    
897     Count := I;
898     I := 0;
899    
900     { Decode data to output stream }
901     while I < Count do begin
902     c1 := StD64Table[InBuf[I]];
903     c2 := StD64Table[InBuf[I+1]];
904     c3 := StD64Table[InBuf[I+2]];
905     OutBuf[O] := ((c1 shl 2) or (c2 shr 4));
906     Inc(O);
907     if AnsiChar(InBuf[I+2]) <> '=' then begin
908     OutBuf[O] := ((c2 shl 4) or (c3 shr 2));
909     Inc(O);
910     if AnsiChar(InBuf[I+3]) <> '=' then begin
911     OutBuf[O] := ((c3 shl 6) or StD64Table[InBuf[I+3]]);
912     Inc(O);
913     end else
914     Decoding := False;
915     end else
916     Decoding := False;
917     Inc(I, 4);
918     end;
919     OutStream.Write(OutBuf, O);
920     end;
921     end;
922    
923     procedure TStBase64Stream.EncodeToStream(InStream, OutStream : TStream);
924     var
925     I, O, Count : Integer;
926     InBuf : array[1..45] of Byte;
927     OutBuf : array[0..62] of AnsiChar;
928     Temp : Byte;
929     S : AnsiString;
930     begin
931     FillChar(OutBuf, Sizeof(OutBuf), #0);
932    
933     if not FOwner.MimeHeaders then begin
934     S := Format('begin-base64 600 %s'#13#10, [FCurrentFile]);
935     OutStream.Write(S[1], Length(S));
936     end;
937    
938     { Encode and stream the attachment }
939     repeat
940     Count := InStream.Read(InBuf, SizeOf(InBuf));
941     if Count = 0 then Break;
942     I := 1;
943     O := 0;
944     while I <= (Count-2) do begin
945     { Encode 1st byte }
946     Temp := (InBuf[I] shr 2);
947     OutBuf[O] := AnsiChar(St64Table[Temp and $3F]);
948    
949     { Encode 1st/2nd byte }
950     Temp := (InBuf[I] shl 4) or (InBuf[I+1] shr 4);
951     OutBuf[O+1] := AnsiChar(St64Table[Temp and $3F]);
952    
953     { Encode 2nd/3rd byte }
954     Temp := (InBuf[I+1] shl 2) or (InBuf[I+2] shr 6);
955     OutBuf[O+2] := AnsiChar(St64Table[Temp and $3F]);
956    
957     { Encode 3rd byte }
958     Temp := (InBuf[I+2] and $3F);
959     OutBuf[O+3] := AnsiChar(St64Table[Temp]);
960    
961     Inc(I, 3);
962     Inc(O, 4);
963     end;
964    
965     { Are there odd bytes to add? }
966     if (I <= Count) then begin
967     Temp := (InBuf[I] shr 2);
968     OutBuf[O] := AnsiChar(St64Table[Temp and $3F]);
969    
970     { One odd byte }
971     if I = Count then begin
972     Temp := (InBuf[I] shl 4) and $30;
973     OutBuf[O+1] := AnsiChar(St64Table[Temp and $3F]);
974     OutBuf[O+2] := '=';
975     { Two odd bytes }
976     end else begin
977     Temp := ((InBuf[I] shl 4) and $30) or ((InBuf[I+1] shr 4) and $0F);
978     OutBuf[O+1] := AnsiChar(St64Table[Temp and $3F]);
979     Temp := (InBuf[I+1] shl 2) and $3C;
980     OutBuf[O+2] := AnsiChar(St64Table[Temp and $3F]);
981     end;
982     { Add padding }
983     OutBuf[O+3] := '=';
984     Inc(O, 4);
985     end;
986    
987     { Add CR/LF }
988     OutBuf[O] := #13;
989     OutBuf[O+1] := #10;
990    
991     { Write line to stream }
992     OutStream.Write(OutBuf, (O + 2));
993     until Count < SizeOf(InBuf);
994    
995     { Add terminating end if necessary }
996     if not FOwner.MimeHeaders then begin
997     StrCopy(OutBuf, 'end'#13#10);
998     OutStream.Write(OutBuf, StrLen(OutBuf));
999     end;
1000     end;
1001    
1002    
1003     { TConverterList }
1004    
1005     type
1006     TCvtFormat = class
1007     ConverterClass : TStConverterClass;
1008     Description : string;
1009     end;
1010    
1011     TConverterList = class(TStringList)
1012     protected
1013     procedure LockList;
1014     procedure UnlockList;
1015     public
1016     constructor Create;
1017     destructor Destroy; override;
1018     procedure AddConverter(const ATag, ADesc : string; AClass : TStConverterClass);
1019     procedure Remove(AClass: TStConverterClass);
1020     end;
1021    
1022     constructor TConverterList.Create;
1023     begin
1024     inherited Create;
1025     Sorted := True;
1026     Duplicates := dupError;
1027     AddConverter('raw', 'Raw Copy', TStRawStream);
1028     AddConverter('base64', 'Base64', TStBase64Stream);
1029     AddConverter('quoted-printable', 'Quoted-Printable', TStQuotedStream);
1030     AddConverter('uuencoded', 'UUEncoded', TStUUStream);
1031     end;
1032    
1033     destructor TConverterList.Destroy;
1034     var
1035     I: Integer;
1036     begin
1037     LockList;
1038     try
1039     for I := 0 to Count-1 do
1040     TCvtFormat(Objects[I]).Free;
1041     inherited Destroy;
1042     finally
1043     UnlockList;
1044     end;
1045     end;
1046    
1047     procedure TConverterList.LockList;
1048     begin
1049     EnterCriticalSection(CvtLock);
1050     end;
1051    
1052     procedure TConverterList.UnlockList;
1053     begin
1054     LeaveCriticalSection(CvtLock);
1055     end;
1056    
1057     procedure TConverterList.AddConverter(const ATag, ADesc : string; AClass : TStConverterClass);
1058     var
1059     Temp : TCvtFormat;
1060     begin
1061     LockList;
1062     try
1063     Temp := TCvtFormat.Create;
1064     with Temp do begin
1065     ConverterClass := AClass;
1066     Description := ADesc;
1067     end;
1068     AddObject(ATag, Temp);
1069     finally
1070     UnlockList;
1071     end;
1072     end;
1073    
1074     procedure TConverterList.Remove(AClass: TStConverterClass);
1075     var
1076     I : Integer;
1077     Cvt : TCvtFormat;
1078     begin
1079     LockList;
1080     try
1081     for I := Count-1 downto 0 do begin
1082     Cvt := TCvtFormat(Objects[I]);
1083     if Cvt.ConverterClass.InheritsFrom(AClass) then begin
1084     Cvt.Free;
1085     Delete(I);
1086     end;
1087     end;
1088     finally
1089     UnlockList;
1090     end;
1091     end;
1092    
1093     const
1094     Converters : TConverterList = nil;
1095    
1096     function GetConverters : TConverterList;
1097     begin
1098     EnterCriticalSection(CvtLock);
1099     try
1100     if Converters = nil then
1101     Converters := TConverterList.Create;
1102     Result := Converters;
1103     finally
1104     LeaveCriticalSection(CvtLock);
1105     end;
1106     end;
1107    
1108     { TStMimeConverter }
1109     constructor TStMimeConverter.Create;
1110     begin
1111     inherited Create;
1112     InitConverter;
1113     end;
1114    
1115     constructor TStMimeConverter.CreateInit(AStream : TStream);
1116     begin
1117     inherited Create;
1118     InitConverter;
1119     if Assigned(AStream) then
1120     Stream := AStream;
1121     end;
1122    
1123     destructor TStMimeConverter.Destroy;
1124     begin
1125     DeleteAttachments;
1126     FAttachments.Free;
1127     FInternalStream.Free;
1128     FConverter.Free;
1129     inherited Destroy;
1130     end;
1131    
1132     procedure TStMimeConverter.AddFileAttachment(const AFileName : string);
1133     var
1134     F : TFileStream;
1135     begin
1136     F := TFileStream.Create(AFileName, AttachmentFileMode);
1137     try
1138     AddStreamAttachment(F, AFileName);
1139     finally
1140     F.Free;
1141     end;
1142     end;
1143    
1144     procedure TStMimeConverter.AddMimeFooters;
1145     var
1146     SavePos : LongInt;
1147     Temp : AnsiString;
1148     begin
1149     SavePos := Stream.Position;
1150     Stream.Write(CRLFStr, SizeOf(CRLFStr));
1151     Temp := '--' + Boundary + '--';
1152     Stream.Write(Temp[1], Length(Temp));
1153     Stream.Write(CRLFStr, SizeOf(CRLFStr));
1154     Stream.Position := SavePos;
1155     end;
1156    
1157     procedure TStMimeConverter.AddMimeHeaders(const AFileName : string);
1158     var
1159     Temp, Descr : AnsiString;
1160     begin
1161     Stream.Write(CRLFStr, SizeOf(CRLFStr));
1162     Temp := '--' + Boundary;
1163     Stream.Write(Temp[1], Length(Temp));
1164     Stream.Write(CRLFStr, SizeOf(CRLFStr));
1165    
1166     Temp := Format('Content-Type: %s; name="%s"'#13#10,
1167     [ContentType, ExtractFileName(AFileName)]);
1168     Stream.Write(Temp[1], Length(Temp));
1169    
1170     Temp := Format('Content-Transfer-Encoding: %s'#13#10, [Encoding]);
1171     Stream.Write(Temp[1], Length(Temp));
1172    
1173     if ContentDescription = '' then
1174     Descr := ExtractFileName(AFileName)
1175     else
1176     Descr := ContentDescription;
1177     Temp := Format('Content-Description: %s'#13#10, [Descr]);
1178     Stream.Write(Temp[1], Length(Temp));
1179    
1180     Temp := Format('Content-Disposition: %s; filename="%s"'#13#10#13#10,
1181     [ContentDisposition, ExtractFileName(AFileName)]);
1182     Stream.Write(Temp[1], Length(Temp));
1183     end;
1184    
1185     procedure TStMimeConverter.AddStreamAttachment(AStream : TStream; const AFileName : string);
1186     var
1187     I : Integer;
1188     AttObj : TStAttachment;
1189     SavePos : LongInt;
1190     begin
1191     if Converters.Find(FEncoding, I) then
1192     ForceType(TCvtFormat(Converters.Objects[I]).ConverterClass)
1193     else
1194     RaiseStError(EStMimeError, stscBadEncodeFmt);
1195    
1196     SavePos := Stream.Position;
1197    
1198     if FMimeHeaders then
1199     AddMimeHeaders(AFileName);
1200    
1201     FConverter.CurrentFile := ExtractFilename(AFileName);
1202     FConverter.EncodeToStream(AStream, Stream);
1203    
1204     if MimeHeaders then
1205     AddMimeFooters;
1206    
1207     AttObj := TStAttachment.Create;
1208     with AttObj do begin
1209     atContentDescription := ContentDescription;
1210     atContentDisposition := ContentDisposition;
1211     atContentType := ContentType;
1212     atEncoding := Encoding;
1213     atSize := AStream.Size;
1214     atStreamOffset := SavePos;
1215     end;
1216    
1217     FAttachments.AddObject(ExtractFilename(AFileName), AttObj);
1218     end;
1219    
1220     procedure TStMimeConverter.DeleteAttachments;
1221     var
1222     I : Integer;
1223     begin
1224     if not Assigned(FAttachments) then Exit;
1225     for I := 0 to (FAttachments.Count - 1) do
1226     TStAttachment(FAttachments.Objects[I]).Free;
1227     FAttachments.Clear;
1228     end;
1229    
1230     procedure TStMimeConverter.ExtractAttachment(const Attachment : string);
1231     var
1232     I : Integer;
1233     begin
1234     if FAttachments.Find(Attachment, I) then
1235     ExtractAttachmentIndex(I)
1236     else
1237     RaiseStError(EStMimeError, stscBadAttachment);
1238     end;
1239    
1240     procedure TStMimeConverter.ExtractAttachmentIndex(Index : Integer);
1241     var
1242     F : TFileStream;
1243     S : string;
1244     begin
1245     if (Index < 0) or (Index > (FAttachments.Count - 1)) then
1246     RaiseStError(EStMimeError, stscBadAttachment);
1247    
1248     if FDirectory <> '' then begin
1249     S := JustPathNameL(FDirectory);
1250     S := AddBackSlashL(S);
1251     S := S + TStAttachment(FAttachments.Objects[Index]).atFileName;
1252     end else begin
1253     S := TStAttachment(FAttachments.Objects[Index]).atFileName;
1254     end;
1255    
1256     SaveAs(S);
1257     F := TFileStream.Create(S, fmCreate);
1258     try
1259     ExtractToStream(Index, F);
1260     finally
1261     F.Free;
1262     end;
1263     end;
1264    
1265     procedure TStMimeConverter.ExtractToStream(Index : Integer; AStream : TStream);
1266     var
1267     I : Integer;
1268     SaveEncoding : string;
1269     begin
1270     SaveEncoding := FEncoding;
1271     try
1272     { Position stream to beginning of data }
1273     if (Index < 0) or (Index > (FAttachments.Count - 1)) then
1274     RaiseStError(EStMimeError, stscBadAttachment);
1275     PositionForExtract(TStAttachment(FAttachments.Objects[Index]));
1276    
1277     { Find matching converter type and use it }
1278     if Converters.Find(TStAttachment(FAttachments.Objects[Index]).atEncoding, I) then
1279     ForceType(TCvtFormat(Converters.Objects[I]).ConverterClass)
1280     else
1281     { If we don't have a matching converter, save as a raw stream }
1282     ForceType(TStRawStream);
1283    
1284     FConverter.DecodeToStream(Stream, AStream);
1285     finally
1286     FEncoding := SaveEncoding;
1287     end;
1288     end;
1289    
1290     procedure TStMimeConverter.ExtractAttachments;
1291     var
1292     I : Integer;
1293     begin
1294     for I := 0 to (FAttachments.Count - 1) do
1295     ExtractAttachmentIndex(I);
1296     end;
1297    
1298     procedure TStMimeConverter.FillConverterList(List : TStrings);
1299     var
1300     I : Integer;
1301     begin
1302     List.Clear;
1303     for I := 0 to (Converters.Count - 1) do
1304     List.Add(TCvtFormat(Converters.Objects[I]).Description);
1305     end;
1306    
1307     procedure TStMimeConverter.FindOldAttachment;
1308     const
1309     StmSize = 32*1024;
1310     type
1311     MemArray = array[0..(StmSize-1)] of AnsiChar;
1312     var
1313     I, Pos, ScanSize, StmOffset : LongInt;
1314     NewAtt : TStAttachment;
1315     ScanStream : TMemoryStream;
1316     FoundPos : Cardinal;
1317     SearchString : AnsiString;//array[0..80] of Char;
1318     TempBuf : AnsiString; //array[0..80] of Char;
1319     TokenBuf : AnsiString; //array[0..80] of Char;
1320     TempWord : Word;
1321     BMT : BTable;
1322    
1323     function Min(A, B : LongInt) : LongInt;
1324     begin
1325     Result := A;
1326     if A > B then
1327     Result := B;
1328     end;
1329    
1330     begin
1331     NewAtt := nil;
1332    
1333     { Position stream to beginning }
1334     Stream.Seek(0, soFromBeginning);
1335    
1336     { Create memory stream for search }
1337     ScanStream := TMemoryStream.Create;
1338     try
1339     ScanStream.SetSize(StmSize);
1340     StmOffset := Stream.Position;
1341     ScanSize := ScanStream.CopyFrom(Stream, Min(StmSize,
1342     (Stream.Size - Stream.Position)));
1343    
1344     SearchString := #13#10'begin'; // StrPCopy(SearchString, #13#10'begin');
1345     BMMakeTableZ(PAnsiChar(SearchString), BMT);
1346     ScanStream.Position := 0;
1347    
1348     while True do begin
1349     { Look for an old style attachment -- process appropriately }
1350     if BMSearchZ(ScanStream.Memory^, ScanSize, BMT, PAnsiChar(SearchString), FoundPos) then begin
1351    
1352     FillChar(TempBuf, SizeOf(TempBuf), #0);
1353     Pos := FoundPos + 2;
1354    
1355     { Collect line containing potential begin marker }
1356     for I := 0 to 79 do begin
1357     if MemArray(ScanStream.Memory^)[Pos+I] = #13 then Break;
1358     TempBuf[I] := MemArray(ScanStream.Memory^)[Pos+I];
1359     end;
1360    
1361     { Grab second word -- should be a number if this is an attachment }
1362     TokenBuf := ExtractWordL(2, TempBuf, ' ');
1363     if Str2WordL(TokenBuf, TempWord) then begin
1364     { We've got an attachment }
1365     NewAtt := TStAttachment.Create;
1366     NewAtt.atStreamOffset := Pos;
1367     TokenBuf := ExtractWordL(1, TempBuf, ' ');
1368     if CompStringL(TokenBuf, 'begin') = 0 then
1369     NewAtt.atEncoding := 'uuencoded'
1370     else
1371     NewAtt.atEncoding := 'base64';
1372     TokenBuf := ExtractWordL(3, TempBuf, ' ');
1373     NewAtt.atFilename := TokenBuf;
1374     NewAtt.atOldStyle := True;
1375     Attachments.AddObject(NewAtt.atFileName, NewAtt);
1376     NewAtt := nil;
1377     Break;
1378     end else begin
1379     Stream.Position := (StmOffset + LongInt(FoundPos) + LongInt(Length(SearchString)));
1380     StmOffset := Stream.Position;
1381     ScanStream.Position := 0;
1382     ScanSize := ScanStream.CopyFrom(Stream,
1383     Min(StmSize, (Stream.Size - Stream.Position)));
1384     end;
1385    
1386     end else begin
1387     if (ScanSize < StmSize) then Exit;
1388     Stream.Seek(-Length(SearchString), soFromCurrent);
1389     StmOffset := Stream.Position;
1390     ScanStream.Position := 0;
1391     ScanSize := ScanStream.CopyFrom(Stream,
1392     Min(StmSize, (Stream.Size - Stream.Position)));
1393     end;
1394     end;
1395     finally
1396     ScanStream.Free;
1397     NewAtt.Free;
1398     end;
1399     end;
1400    
1401     procedure TStMimeConverter.ForceType(ConverterType : TStConverterClass);
1402     begin
1403     if not (Converter is ConverterType) then begin
1404     FConverter.Free;
1405     FConverter := nil;
1406     FConverter := ConverterType.Create(Self);
1407     FConverter.OnProgress := Progress;
1408     end;
1409     end;
1410    
1411     function TStMimeConverter.GenerateBoundary : string;
1412     var
1413     Temp : TDateTime;
1414     begin
1415     Temp := Now;
1416     Randomize;
1417     Result := 'StMime-' + IntToHex(Trunc(Temp), 8) + '-' +
1418     IntToHex(Trunc(Frac(Temp) * 10000), 8) + '-' +
1419     IntToHex(GetTickCount, 8) + '-' + IntToHex(Random($FFFF), 4);
1420     end;
1421    
1422     function TStMimeConverter.GetBoundary : string;
1423     begin
1424     if FBoundary = '' then
1425     FBoundary := GenerateBoundary;
1426     Result := FBoundary;
1427     end;
1428    
1429     function TStMimeConverter.GetStream : TStream;
1430     begin
1431     if not Assigned(FStream) then begin
1432     if not Assigned(FInternalStream) then
1433     FInternalStream := TMemoryStream.Create;
1434     FStream := FInternalStream;
1435     end;
1436     Result := FStream;
1437     end;
1438    
1439     function TStMimeConverter.GetTag(const Description : string): string;
1440     var
1441     I : Integer;
1442     begin
1443     for I := 0 to (Converters.Count - 1) do begin
1444     if CompareStr(Description,
1445     TCvtFormat(Converters.Objects[I]).Description) = 0 then begin
1446     Result := Converters[I];
1447     Exit;
1448     end;
1449     end;
1450     end;
1451    
1452     procedure TStMimeConverter.InitConverter;
1453     begin
1454     FAttachments := TStringList.Create;
1455     FContentType := DefStContentType;
1456     FContentDisposition := DefStContentDisposition;
1457     FEncoding := DefStMimeEncoding;
1458     FMimeHeaders := True;
1459     GetConverters;
1460     end;
1461    
1462     procedure TStMimeConverter.PositionForExtract(Att : TStAttachment);
1463     const
1464     BufSize = 1024;
1465     var
1466     I : Integer;
1467     Ptr : PAnsiChar;
1468     TempBuf : array[0..BufSize] of AnsiChar;
1469     begin
1470     FillChar(TempBuf, SizeOf(TempBuf), #0);
1471     Stream.Position := Att.atStreamOffset;
1472     Stream.Read(TempBuf, BufSize);
1473     if Att.atOldStyle then begin
1474     for I := 0 to BufSize do begin
1475     if TempBuf[I] = #13 then begin
1476     Stream.Position := (Att.atStreamOffset + I);
1477     Exit;
1478     end;
1479     end;
1480     end else begin
1481     Ptr := StrPos(TempBuf, #13#10#13#10'');
1482     Stream.Position := (Att.atStreamOffset + (Ptr - TempBuf));
1483     end;
1484     end;
1485    
1486     procedure TStMimeConverter.Progress(Sender : TObject; Status : TStConvertState;
1487     PercentDone : Byte);
1488     begin
1489     if Assigned(FOnProgress) then
1490     FOnProgress(Sender, Status, PercentDone);
1491     end;
1492    
1493     class procedure TStMimeConverter.RegisterConverter(const ATag, ADesc : string;
1494     AClass : TStConverterClass);
1495     begin
1496     GetConverters.AddConverter(ATag, ADesc, AClass);
1497     end;
1498    
1499     procedure TStMimeConverter.SaveAs(var FileName : string);
1500     begin
1501     if Assigned(FOnSaveAs) then FOnSaveAs(Self, FileName);
1502     end;
1503    
1504     procedure TStMimeConverter.ScanAttachments;
1505     const
1506     StmSize = 32*1024;
1507     type
1508     MemArray = array[0..(StmSize-1)] of AnsiChar;
1509     var
1510     I, Pos, ScanSize, StmOffset : LongInt;
1511     TTree : TStTernaryTree;
1512     TTag : Pointer;
1513     NewAtt : TStAttachment;
1514     ScanStream : TMemoryStream;
1515     OStr : TStString;
1516     FoundPos, BoundPos : Cardinal;
1517     SearchString : array[0..80] of AnsiChar;
1518     TempBuf : array[0..1024] of AnsiChar;
1519     AttToken : array[0..MaxMimeLine] of AnsiChar;
1520     BMT : BTable;
1521    
1522     function Min(A, B : LongInt) : LongInt;
1523     begin
1524     Result := A;
1525     if A > B then
1526     Result := B;
1527     end;
1528    
1529     procedure InitTree;
1530     begin
1531     TTree := TStTernaryTree.Create;
1532     with TTree do begin
1533     InsertStr('CONTENT-TYPE', Pointer(ctType));
1534     InsertStr('CONTENT-TRANSFER-ENCODING', Pointer(ctEncoding));
1535     InsertStr('CONTENT-DESCRIPTION', Pointer(ctDescription));
1536     InsertStr('CONTENT-DISPOSITION', Pointer(ctDisposition));
1537     end;
1538     end;
1539    
1540     begin
1541     NewAtt := nil;
1542     TTree := nil;
1543    
1544     DeleteAttachments;
1545    
1546     { Position stream to beginning }
1547     Stream.Seek(0, soFromBeginning);
1548    
1549     { Create memory stream for search }
1550     ScanStream := TMemoryStream.Create;
1551     try
1552     ScanStream.SetSize(StmSize);
1553     StmOffset := Stream.Position;
1554     ScanSize := ScanStream.CopyFrom(Stream, Min(StmSize,
1555     (Stream.Size - Stream.Position)));
1556    
1557     { If we have a boundary, use it -- if not, look for one }
1558     if FBoundary = '' then
1559     StrCopy(SearchString, #13#10'--')
1560     else begin
1561     FBoundaryUsed := True;
1562     StrPCopy(SearchString, '--' + Boundary);
1563     end;
1564     BMMakeTableZ(SearchString, BMT);
1565     ScanStream.Position := 0;
1566    
1567     while True do begin
1568     { Look for a Mime boundary -- process appropriately }
1569     if BMSearchZ(ScanStream.Memory^, ScanSize, BMT, SearchString, FoundPos) then begin
1570    
1571     Pos := FoundPos + StrLen(SearchString);
1572    
1573     { Add add'l checks here -- look for the Boundary header entry first }
1574     { if that method fails, beef up this method against false positives a bit }
1575     { maybe checking for 'Content-' shortly following this potential boundary }
1576    
1577     { Do we have a boundary? If not, assume this might be it and collect }
1578     if FBoundary = '' then begin
1579     FillChar(TempBuf, SizeOf(TempBuf), #0);
1580     for I := 0 to (MaxMimeLine - 1) do begin
1581     if MemArray(ScanStream.Memory^)[Pos+I] = #13 then begin
1582     Pos := Pos+I;
1583     Break;
1584     end;
1585     TempBuf[I] := MemArray(ScanStream.Memory^)[Pos+I];
1586     end;
1587     Boundary := StrPas(TempBuf);
1588     StrCopy(StrECopy(SearchString, '--'), TempBuf);
1589    
1590     { Adjust to account for CR/LF searched on this go around }
1591     Inc(FoundPos, 2);
1592    
1593     { Get this out of the way for subsequent searches }
1594     BMMakeTableZ(SearchString, BMT);
1595     end;
1596    
1597     if not Assigned(TTree) then InitTree;
1598    
1599     { Check to see if this was an 'ending' boundary }
1600     if (MemArray(ScanStream.Memory^)[Pos] = '-') and
1601     (MemArray(ScanStream.Memory^)[Pos+1] = '-') then begin
1602     { Position the stream to the beginning of the end marker }
1603     FEndBoundaryOffset := (StmOffset + LongInt(FoundPos) - 2);
1604     Stream.Position := FEndBoundaryOffset;
1605     Exit;
1606     end else begin
1607     if not Assigned(NewAtt) then NewAtt := TStAttachment.Create;
1608     { Go ahead and reposition here -- won't lose us much, and it }
1609     { guarantees all tags for this attachment will be within the buffer }
1610     NewAtt.atStreamOffset := (StmOffset + LongInt(FoundPos));
1611     Stream.Position := (StmOffset + LongInt(FoundPos) + Length(FBoundary) + 2);
1612     StmOffset := Stream.Position;
1613     ScanStream.Position := 0;
1614     if Stream.Position >= Stream.Size then Exit;
1615     ScanSize := ScanStream.CopyFrom(Stream,
1616     Min(StmSize, (Stream.Size - Stream.Position)));
1617     end;
1618    
1619     { Init for token search }
1620     OStr := TStString.CreateZ(StrLCopy(TempBuf, ScanStream.Memory, SizeOf(TempBuf)-1));
1621     try
1622     with OStr do begin
1623     { Check for another boundary in buffer }
1624     if not BMSearchUC(FBoundary, BoundPos) then
1625     BoundPos := SizeOf(TempBuf);
1626     Delimiters := ' :;='#13#10;
1627     Quote := '"';
1628     EnableCursor := True;
1629     RepeatValue := 10;
1630     BMSearchUC('Content-', FoundPos);
1631     RepeatValue := 1;
1632     end;
1633    
1634     for I := 0 to OStr.Items.Count-1 do begin
1635    
1636     OStr.CursorPos := Cardinal(OStr.Items.Objects[I]);
1637    
1638     { These tokens belong to the next section }
1639     if OStr.CursorPos > BoundPos then Break;
1640    
1641     OStr.GetWordAtCursorZ(AttToken);
1642    
1643     { Process tag appropriately }
1644     if TTree.SearchUC(AttToken, TTag) then begin
1645     case TStContentTag(TTag) of
1646     ctType :
1647     begin
1648     OStr.CursorNextWord;
1649     NewAtt.atContentType := OStr.GetAsciiAtCursor;
1650     OStr.CursorNextWord;
1651     if CompareText(OStr.GetAsciiAtCursor, 'name') = 0 then begin
1652     OStr.Delimiters := ' :;="'#13#10;
1653     OStr.CursorNextWord;
1654     NewAtt.atFileName := OStr.GetWordAtCursor;
1655     OStr.Delimiters := ' :;='#13#10;
1656     end;
1657     end;
1658     ctEncoding :
1659     begin
1660     OStr.CursorNextWord;
1661     NewAtt.atEncoding := OStr.GetAsciiAtCursor;
1662     end;
1663     ctDescription :
1664     begin
1665     OStr.CursorNextWord;
1666     NewAtt.atContentDescription := OStr.GetAsciiAtCursor;
1667     end;
1668     ctDisposition :
1669     begin
1670     OStr.CursorNextWord;
1671     NewAtt.atContentDisposition := OStr.GetAsciiAtCursor;
1672     OStr.CursorNextWord;
1673     if CompareText(OStr.GetAsciiAtCursor, 'filename') = 0 then begin
1674     OStr.Delimiters := ' :;="'#13#10;
1675     OStr.CursorNextWord;
1676     NewAtt.atFileName := OStr.GetWordAtCursor;
1677     OStr.Delimiters := ' :;='#13#10;
1678     end;
1679     end;
1680     end;
1681     end;
1682     end;
1683     { If it's an 'attachment' -- add it to the list }
1684     if CompareText(NewAtt.atContentDisposition, 'attachment') = 0 then begin
1685     if NewAtt.atFilename = '' then
1686     NewAtt.atFileName := 'attach' + IntToStr(FAttachments.Count) + '.att';
1687     Attachments.AddObject(NewAtt.atFileName, NewAtt);
1688     NewAtt := nil;
1689     end else if CompareText(NewAtt.atContentDisposition, 'inline') = 0 then begin
1690     if NewAtt.atFilename = '' then
1691     NewAtt.atFileName := 'attach' + IntToStr(FAttachments.Count) + '.att';
1692     Attachments.AddObject(NewAtt.atFileName, NewAtt);
1693     NewAtt := nil;
1694     end;
1695     finally
1696     OStr.Free;
1697     end;
1698    
1699     end else begin
1700     if (ScanSize < StmSize) then Exit;
1701     Stream.Seek(-StrLen(SearchString), soFromCurrent);
1702     StmOffset := Stream.Position;
1703     ScanStream.Position := 0;
1704     ScanSize := ScanStream.CopyFrom(Stream,
1705     Min(StmSize, (Stream.Size - Stream.Position)));
1706     end;
1707     end;
1708     finally
1709     ScanStream.Free;
1710     NewAtt.Free;
1711     TTree.Free;
1712     if FAttachments.Count = 0 then FindOldAttachment;
1713     end;
1714     end;
1715    
1716     procedure TStMimeConverter.SetBoundary(Value : string);
1717     begin
1718     if CompareStr(FBoundary, Value) <> 0 then begin
1719     FBoundary := Value;
1720     FBoundaryUsed := False;
1721     if Length(Value) > 74 then
1722     SetLength(FBoundary, 74);
1723     end;
1724     end;
1725    
1726     procedure TStMimeConverter.SetConverter(Value : TStConvertStream);
1727     var
1728     NewConverter : TStConvertStream;
1729     begin
1730     NewConverter := nil;
1731     if Value <> nil then begin
1732     NewConverter := TStConverterClass(Value.ClassType).Create(Self);
1733     NewConverter.OnProgress := Progress;
1734     end;
1735     try
1736     FConverter.Free;
1737     FConverter := NewConverter;
1738     except
1739     NewConverter.Free;
1740     raise;
1741     end;
1742     end;
1743    
1744     procedure TStMimeConverter.SetEncoding(Value : string);
1745     var
1746     I : Integer;
1747     begin
1748     if FEncoding <> Value then begin
1749     if Converters.Find(Value, I) then begin
1750     FEncoding := Value;
1751     ForceType(TCvtFormat(Converters.Objects[I]).ConverterClass);
1752     end else
1753     RaiseStError(EStMimeError, stscBadEncodeFmt);
1754     end;
1755     end;
1756    
1757     procedure TStMimeConverter.SetStream(Value : TStream);
1758     begin
1759     {if FStream <> Value then begin}
1760     FStream := Value;
1761     if FBoundaryUsed then
1762     Boundary := '';
1763     ScanAttachments;
1764     {end;}
1765     end;
1766    
1767     class procedure TStMimeConverter.UnRegisterConverterClass(AClass : TStConverterClass);
1768     begin
1769     if Assigned(Converters) then Converters.Remove(AClass);
1770     end;
1771    
1772     initialization
1773     InitializeCriticalSection(CvtLock);
1774    
1775     finalization
1776     Converters.Free;
1777     DeleteCriticalSection(CvtLock);
1778     end.

  ViewVC Help
Powered by ViewVC 1.1.20