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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (show annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 9 months ago) by torben
File size: 53358 byte(s)
Added tpsystools component
1 // 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