home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 November / Chip_2002-11_cd1.bin / zkuste / delphi / kompon / d3456 / SYNAPSE.ZIP / source / lib / MIMEpart.pas < prev    next >
Pascal/Delphi Source File  |  2002-07-09  |  25KB  |  838 lines

  1. {==============================================================================|
  2. | Project : Delphree - Synapse                                   | 002.001.002 |
  3. |==============================================================================|
  4. | Content: MIME support procedures and functions                               |
  5. |==============================================================================|
  6. | Copyright (c)1999-2002, Lukas Gebauer                                        |
  7. | All rights reserved.                                                         |
  8. |                                                                              |
  9. | Redistribution and use in source and binary forms, with or without           |
  10. | modification, are permitted provided that the following conditions are met:  |
  11. |                                                                              |
  12. | Redistributions of source code must retain the above copyright notice, this  |
  13. | list of conditions and the following disclaimer.                             |
  14. |                                                                              |
  15. | Redistributions in binary form must reproduce the above copyright notice,    |
  16. | this list of conditions and the following disclaimer in the documentation    |
  17. | and/or other materials provided with the distribution.                       |
  18. |                                                                              |
  19. | Neither the name of Lukas Gebauer nor the names of its contributors may      |
  20. | be used to endorse or promote products derived from this software without    |
  21. | specific prior written permission.                                           |
  22. |                                                                              |
  23. | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  |
  24. | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE    |
  25. | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE   |
  26. | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR  |
  27. | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL       |
  28. | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR   |
  29. | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER   |
  30. | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT           |
  31. | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY    |
  32. | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH  |
  33. | DAMAGE.                                                                      |
  34. |==============================================================================|
  35. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  36. | Portions created by Lukas Gebauer are Copyright (c)2000-2002.                |
  37. | All Rights Reserved.                                                         |
  38. |==============================================================================|
  39. | Contributor(s):                                                              |
  40. |==============================================================================|
  41. | History: see HISTORY.HTM from distribution package                           |
  42. |          (Found at URL: http://www.ararat.cz/synapse/)                       |
  43. |==============================================================================}
  44.  
  45. unit MIMEpart;
  46.  
  47. interface
  48.  
  49. uses
  50.   SysUtils, Classes,
  51. {$IFNDEF LINUX}
  52.   Windows,
  53. {$ENDIF}
  54.   SynaChar, SynaCode, SynaUtil, MIMEinLn;
  55.  
  56. type
  57.  
  58.   TMimePart = class;
  59.   THookWalkPart = procedure(const Sender: TMimePart) of object;
  60.  
  61.   TMimePrimary = (MP_TEXT, MP_MULTIPART,
  62.     MP_MESSAGE, MP_BINARY);
  63.  
  64.   TMimeEncoding = (ME_7BIT, ME_8BIT, ME_QUOTED_PRINTABLE,
  65.     ME_BASE64, ME_UU, ME_XX);
  66.  
  67.   TMimePart = class(TObject)
  68.   private
  69.     FPrimary: string;
  70.     FPrimaryCode: TMimePrimary;
  71.     FSecondary: string;
  72.     FEncoding: string;
  73.     FEncodingCode: TMimeEncoding;
  74.     FDefaultCharset: string;
  75.     FCharset: string;
  76.     FCharsetCode: TMimeChar;
  77.     FTargetCharset: TMimeChar;
  78.     FDescription: string;
  79.     FDisposition: string;
  80.     FContentID: string;
  81.     FBoundary: string;
  82.     FFileName: string;
  83.     FLines: TStringList;
  84.     FPartBody: TStringList;
  85.     FHeaders: TStringList;
  86.     FPrePart: TStringList;
  87.     FPostPart: TStringList;
  88.     FDecodedLines: TMemoryStream;
  89.     FSubParts: TList;
  90.     FOnWalkPart: THookWalkPart;
  91.     FMaxLineLength: integer;
  92.     procedure SetPrimary(Value: string);
  93.     procedure SetEncoding(Value: string);
  94.     procedure SetCharset(Value: string);
  95.   public
  96.     constructor Create;
  97.     destructor Destroy; override;
  98.     procedure Clear;
  99.     procedure DecodePart;
  100.     procedure DecodePartHeader;
  101.     procedure EncodePart;
  102.     procedure EncodePartHeader;
  103.     procedure MimeTypeFromExt(Value: string);
  104.     function GetSubPartCount: integer;
  105.     function GetSubPart(index: integer): TMimePart;
  106.     procedure ClearSubParts;
  107.     function AddSubPart: TMimePart;
  108.     procedure DecomposeParts;
  109.     procedure ComposeParts;
  110.     procedure WalkPart;
  111.   published
  112.     property Primary: string read FPrimary write SetPrimary;
  113.     property Encoding: string read FEncoding write SetEncoding;
  114.     property Charset: string read FCharset write SetCharset;
  115.     property DefaultCharset: string read FDefaultCharset write FDefaultCharset;
  116.     property PrimaryCode: TMimePrimary read FPrimaryCode Write FPrimaryCode;
  117.     property EncodingCode: TMimeEncoding read FEncodingCode Write FEncodingCode;
  118.     property CharsetCode: TMimeChar read FCharsetCode Write FCharsetCode;
  119.     property TargetCharset: TMimeChar read FTargetCharset Write FTargetCharset;
  120.     property Secondary: string read FSecondary Write FSecondary;
  121.     property Description: string read FDescription Write FDescription;
  122.     property Disposition: string read FDisposition Write FDisposition;
  123.     property ContentID: string read FContentID Write FContentID;
  124.     property Boundary: string read FBoundary Write FBoundary;
  125.     property FileName: string read FFileName Write FFileName;
  126.     property Lines: TStringList read FLines;
  127.     property PartBody: TStringList read FPartBody;
  128.     property Headers: TStringList read FHeaders;
  129.     property PrePart: TStringList read FPrePart;
  130.     property PostPart: TStringList read FPostPart;
  131.     property DecodedLines: TMemoryStream read FDecodedLines;
  132.     property OnWalkPart: THookWalkPart read FOnWalkPart write FOnWalkPart;
  133.     property MaxLineLength: integer read FMaxLineLength Write FMaxLineLength;
  134.   end;
  135.  
  136. const
  137.   MaxMimeType = 25;
  138.   MimeType: array[0..MaxMimeType, 0..2] of string =
  139.   (
  140.     ('AU', 'audio', 'basic'),
  141.     ('AVI', 'video', 'x-msvideo'),
  142.     ('BMP', 'image', 'BMP'),
  143.     ('DOC', 'application', 'MSWord'),
  144.     ('EPS', 'application', 'Postscript'),
  145.     ('GIF', 'image', 'GIF'),
  146.     ('JPEG', 'image', 'JPEG'),
  147.     ('JPG', 'image', 'JPEG'),
  148.     ('MID', 'audio', 'midi'),
  149.     ('MOV', 'video', 'quicktime'),
  150.     ('MPEG', 'video', 'MPEG'),
  151.     ('MPG', 'video', 'MPEG'),
  152.     ('MP2', 'audio', 'mpeg'),
  153.     ('MP3', 'audio', 'mpeg'),
  154.     ('PDF', 'application', 'PDF'),
  155.     ('PNG', 'image', 'PNG'),
  156.     ('PS', 'application', 'Postscript'),
  157.     ('QT', 'video', 'quicktime'),
  158.     ('RA', 'audio', 'x-realaudio'),
  159.     ('RTF', 'application', 'RTF'),
  160.     ('SND', 'audio', 'basic'),
  161.     ('TIF', 'image', 'TIFF'),
  162.     ('TIFF', 'image', 'TIFF'),
  163.     ('WAV', 'audio', 'x-wav'),
  164.     ('WPD', 'application', 'Wordperfect5.1'),
  165.     ('ZIP', 'application', 'ZIP')
  166.     );
  167.  
  168. function NormalizeHeader(Value: TStrings; var Index: Integer): string;
  169. function GenerateBoundary: string;
  170.  
  171. implementation
  172.  
  173. function NormalizeHeader(Value: TStrings; var Index: Integer): string;
  174. var
  175.   s, t: string;
  176.   n: Integer;
  177. begin
  178.   s := Value[Index];
  179.   Inc(Index);
  180.   if s <> '' then
  181.     while (Value.Count - 1) > Index do
  182.     begin
  183.       t := Value[Index];
  184.       if t = '' then
  185.         Break;
  186.       for n := 1 to Length(t) do
  187.         if t[n] = #9 then
  188.           t[n] := ' ';
  189.       if t[1] <> ' ' then
  190.         Break
  191.       else
  192.       begin
  193.         s := s + ' ' + Trim(t);
  194.         Inc(Index);
  195.       end;
  196.     end;
  197.   Result := TrimRight(s);
  198. end;
  199.  
  200. {==============================================================================}
  201.  
  202. constructor TMIMEPart.Create;
  203. begin
  204.   inherited Create;
  205.   FOnWalkPart := nil;
  206.   FLines := TStringList.Create;
  207.   FPartBody := TStringList.Create;
  208.   FHeaders := TStringList.Create;
  209.   FPrePart := TStringList.Create;
  210.   FPostPart := TStringList.Create;
  211.   FDecodedLines := TMemoryStream.Create;
  212.   FSubParts := TList.Create;
  213.   FTargetCharset := GetCurCP;
  214.   FDefaultCharset := 'US-ASCII';
  215.   FMaxLineLength := 78;
  216. end;
  217.  
  218. destructor TMIMEPart.Destroy;
  219. begin
  220.   ClearSubParts;
  221.   FSubParts.Free;
  222.   FDecodedLines.Free;
  223.   FPartBody.Free;
  224.   FLines.Free;
  225.   FHeaders.Free;
  226.   FPrePart.Free;
  227.   FPostPart.Free;
  228.   inherited Destroy;
  229. end;
  230.  
  231. {==============================================================================}
  232.  
  233. procedure TMIMEPart.Clear;
  234. begin
  235.   FPrimary := '';
  236.   FEncoding := '';
  237.   FCharset := '';
  238.   FPrimaryCode := MP_TEXT;
  239.   FEncodingCode := ME_7BIT;
  240.   FCharsetCode := ISO_8859_1;
  241.   FTargetCharset := GetCurCP;
  242.   FSecondary := '';
  243.   FDisposition := '';
  244.   FContentID := '';
  245.   FDescription := '';
  246.   FBoundary := '';
  247.   FFileName := '';
  248.   FPartBody.Clear;
  249.   FHeaders.Clear;
  250.   FPrePart.Clear;
  251.   FPostPart.Clear;
  252.   FDecodedLines.Clear;
  253.   ClearSubParts;
  254. end;
  255.  
  256. {==============================================================================}
  257.  
  258. function TMIMEPart.GetSubPartCount: integer;
  259. begin
  260.   Result :=  FSubParts.Count;
  261. end;
  262.  
  263. {==============================================================================}
  264.  
  265. function TMIMEPart.GetSubPart(index: integer): TMimePart;
  266. begin
  267.   Result := nil;
  268.   if Index < GetSubPartCount then
  269.     Result := TMimePart(FSubParts[Index]);
  270. end;
  271.  
  272. {==============================================================================}
  273.  
  274. procedure TMIMEPart.ClearSubParts;
  275. var
  276.   n: integer;
  277. begin
  278.   for n := 0 to GetSubPartCount - 1 do
  279.     TMimePart(FSubParts[n]).Free;
  280.   FSubParts.Clear;
  281. end;
  282.  
  283. {==============================================================================}
  284.  
  285. function TMIMEPart.AddSubPart: TMimePart;
  286. begin
  287.   Result := TMimePart.Create;
  288.   Result.DefaultCharset := FDefaultCharset;
  289.   FSubParts.Add(Result);
  290. end;
  291.  
  292. {==============================================================================}
  293.  
  294. procedure TMIMEPart.DecomposeParts;
  295. var
  296.   x: integer;
  297.   s: string;
  298.   Mime: TMimePart;
  299.  
  300.   procedure SkipEmpty;
  301.   begin
  302.     while FLines.Count > x do
  303.     begin
  304.       s := TrimRight(FLines[x]);
  305.       if s <> '' then
  306.         Break;
  307.       Inc(x);
  308.     end;
  309.   end;
  310.  
  311. begin
  312.   x := 0;
  313.   Clear;
  314.   //extract headers
  315.   while FLines.Count > x do
  316.   begin
  317.     s := NormalizeHeader(FLines, x);
  318.     if s = '' then
  319.       Break;
  320.     FHeaders.Add(s);
  321.   end;
  322.   StringsTrim(FHeaders);
  323.   DecodePartHeader;
  324.   //extract prepart
  325.   if FPrimaryCode = MP_MULTIPART then
  326.   begin
  327.     SkipEmpty;
  328.     while FLines.Count > x do
  329.     begin
  330.       s := TrimRight(FLines[x]);
  331.       Inc(x);
  332.       if s = '--' + FBoundary then
  333.         Break;
  334.       FPrePart.Add(s);
  335.     end;
  336.     StringsTrim(FPrePart);
  337.   end;
  338.   //extract body part
  339.   if FPrimaryCode = MP_MULTIPART then
  340.   begin
  341.     repeat
  342.       Mime := AddSubPart;
  343.       while FLines.Count > x do
  344.       begin
  345.         s := TrimRight(FLines[x]);
  346.         Inc(x);
  347.         if Pos('--' + FBoundary, s) = 1 then
  348.           Break;
  349.         Mime.Lines.Add(s);
  350.       end;
  351.       StringsTrim(Mime.Lines);
  352.       Mime.DecomposeParts;
  353.       if x >= FLines.Count then
  354.         break;
  355.     until s = '--' + FBoundary + '--';
  356.   end;
  357.   if FPrimaryCode = MP_MESSAGE then
  358.   begin
  359.     Mime := AddSubPart;
  360.     SkipEmpty;
  361.     while FLines.Count > x do
  362.     begin
  363.       s := TrimRight(FLines[x]);
  364.       Inc(x);
  365.       Mime.Lines.Add(s);
  366.     end;
  367.     StringsTrim(Mime.Lines);
  368.     Mime.DecomposeParts;
  369.   end
  370.   else
  371.   begin
  372.     SkipEmpty;
  373.     while FLines.Count > x do
  374.     begin
  375.       s := TrimRight(FLines[x]);
  376.       Inc(x);
  377.       FPartBody.Add(s);
  378.     end;
  379.     StringsTrim(FPartBody);
  380.   end;
  381.   //extract postpart
  382.   if FPrimaryCode = MP_MULTIPART then
  383.   begin
  384.     SkipEmpty;
  385.     while FLines.Count > x do
  386.     begin
  387.       s := TrimRight(FLines[x]);
  388.       Inc(x);
  389.       FPostPart.Add(s);
  390.     end;
  391.     StringsTrim(FPostPart);
  392.   end;
  393. end;
  394.  
  395. {==============================================================================}
  396.  
  397. procedure TMIMEPart.ComposeParts;
  398. var
  399.   n: integer;
  400.   mime: TMimePart;
  401.   s, t: string;
  402.   d1, d2, d3: integer;
  403.   x: integer;
  404. begin
  405.   FLines.Clear;
  406.   //add headers
  407.   for n := 0 to FHeaders.Count -1 do
  408.   begin
  409.     s := FHeaders[n];
  410.     repeat
  411.       if Length(s) < FMaxLineLength then
  412.       begin
  413.         t := s;
  414.         s := '';
  415.       end
  416.       else
  417.       begin
  418.         d1 := RPosEx('; ', s, FMaxLineLength);
  419.         d2 := RPosEx(' ', s, FMaxLineLength);
  420.         d3 := RPosEx(', ', s, FMaxLineLength);
  421.         if (d1 <= 1) and (d2 <= 1) and (d3 <= 1) then
  422.         begin
  423.           x := Pos(' ', Copy(s, 2, Length(s) - 1));
  424.           if x < 1 then
  425.             x := Length(s)
  426.           else
  427.             inc(x);
  428.         end
  429.         else
  430.           if d1 > 0 then
  431.             x := d1
  432.           else
  433.             if d3 > 0 then
  434.               x := d3
  435.             else
  436.               x := d2 - 1;
  437.         t := Copy(s, 1, x);
  438.         Delete(s, 1, x);
  439.       end;
  440.       Flines.Add(t);
  441.     until s = '';
  442.   end;
  443.  
  444.   Flines.Add('');
  445.   //add body
  446.   //if multipart
  447.   if FPrimaryCode = MP_MULTIPART then
  448.   begin
  449.     Flines.AddStrings(FPrePart);
  450.     Flines.Add('');
  451.     for n := 0 to GetSubPartCount - 1 do
  452.     begin
  453.       Flines.Add('--' + FBoundary);
  454.       mime := GetSubPart(n);
  455.       mime.ComposeParts;
  456.       FLines.AddStrings(mime.Lines);
  457.       Flines.Add('');
  458.     end;
  459.     Flines.Add('--' + FBoundary + '--');
  460.     Flines.AddStrings(FPostPart);
  461.   end;
  462.   //if message
  463.   if FPrimaryCode = MP_MESSAGE then
  464.   begin
  465.     if GetSubPartCount > 0 then
  466.     begin
  467.       mime := GetSubPart(0);
  468.       mime.ComposeParts;
  469.       FLines.AddStrings(mime.Lines);
  470.       Flines.Add('');
  471.     end;
  472.   end
  473.   else
  474.   //if normal part
  475.   begin
  476.     FLines.AddStrings(FPartBody);
  477.     Flines.Add('');
  478.   end;
  479. end;
  480.  
  481. {==============================================================================}
  482.  
  483. procedure TMIMEPart.DecodePart;
  484. const
  485.   CRLF = #13#10;
  486. var
  487.   n: Integer;
  488.   s: string;
  489. begin
  490.   FDecodedLines.Clear;
  491.   for n := 0 to FPartBody.Count - 1 do
  492.   begin
  493.     s := FPartBody[n];
  494.     case FEncodingCode of
  495.       ME_7BIT:
  496.         begin
  497.           if FPrimaryCode = MP_TEXT then
  498.             s := CharsetConversion(s, FCharsetCode, FTargetCharset);
  499.           s := s + CRLF;
  500.         end;
  501.       ME_8BIT:
  502.         begin
  503.           if FPrimaryCode = MP_TEXT then
  504.             s := CharsetConversion(s, FCharsetCode, FTargetCharset);
  505.           s := s + CRLF;
  506.         end;
  507.       ME_QUOTED_PRINTABLE:
  508.         begin
  509.           if s = '' then
  510.             s := CRLF
  511.           else
  512.             if s[Length(s)] <> '=' then
  513.               s := s + CRLF;
  514.           s := DecodeQuotedPrintable(s);
  515.           if FPrimaryCode = MP_TEXT then
  516.             s := CharsetConversion(s, FCharsetCode, FTargetCharset);
  517.         end;
  518.       ME_BASE64:
  519.         begin
  520.           if s <> '' then
  521.             s := DecodeBase64(s);
  522.           if FPrimaryCode = MP_TEXT then
  523.             s := CharsetConversion(s, FCharsetCode, FTargetCharset);
  524.         end;
  525.       ME_UU:
  526.         if s <> '' then
  527.           s := DecodeUU(s);
  528.       ME_XX:
  529.         if s <> '' then
  530.           s := DecodeXX(s);
  531.     end;
  532.     FDecodedLines.Write(Pointer(s)^, Length(s));
  533.   end;
  534.   FDecodedLines.Seek(0, soFromBeginning);
  535. end;
  536.  
  537. {==============================================================================}
  538.  
  539. procedure TMIMEPart.DecodePartHeader;
  540. var
  541.   n: integer;
  542.   s, su, fn: string;
  543.   st, st2: string;
  544. begin
  545.   Primary := 'text';
  546.   FSecondary := 'plain';
  547.   FDescription := '';
  548.   Charset := FDefaultCharset;
  549.   FFileName := '';
  550.   Encoding := '7BIT';
  551.   FDisposition := '';
  552.   FContentID := '';
  553.   fn := '';
  554.   for n := 0 to FHeaders.Count - 1 do
  555.     if FHeaders[n] <> '' then
  556.     begin
  557.       s := FHeaders[n];
  558.       su := UpperCase(s);
  559.       if Pos('CONTENT-TYPE:', su) = 1 then
  560.       begin
  561.         st := SeparateRight(su, ':');
  562.         st2 := SeparateLeft(st, ';');
  563.         Primary := SeparateLeft(st2, '/');
  564.         FSecondary := SeparateRight(st2, '/');
  565.         if (FSecondary = Primary) and (Pos('/', st2) < 1) then
  566.           FSecondary := '';
  567.         case FPrimaryCode of
  568.           MP_TEXT:
  569.             begin
  570.               Charset := UpperCase(GetParameter(s, 'charset='));
  571.               FFileName := GetParameter(s, 'name=');
  572.             end;
  573.           MP_MULTIPART:
  574.             FBoundary := GetParameter(s, 'Boundary=');
  575.           MP_MESSAGE:
  576.             begin
  577.             end;
  578.           MP_BINARY:
  579.             FFileName := GetParameter(s, 'name=');
  580.         end;
  581.       end;
  582.       if Pos('CONTENT-TRANSFER-ENCODING:', su) = 1 then
  583.         Encoding := SeparateRight(su, ':');
  584.       if Pos('CONTENT-DESCRIPTION:', su) = 1 then
  585.         FDescription := SeparateRight(s, ':');
  586.       if Pos('CONTENT-DISPOSITION:', su) = 1 then
  587.       begin
  588.         FDisposition := SeparateRight(su, ':');
  589.         FDisposition := Trim(SeparateLeft(FDisposition, ';'));
  590.         fn := GetParameter(s, 'FileName=');
  591.       end;
  592.       if Pos('CONTENT-ID:', su) = 1 then
  593.         FContentID := SeparateRight(s, ':');
  594.     end;
  595.   if (PrimaryCode = MP_BINARY) and (FFileName = '') then
  596.     FFileName := fn;
  597.   FFileName := InlineDecode(FFileName, getCurCP);
  598.   FFileName := ExtractFileName(FFileName);
  599. end;
  600.  
  601. {==============================================================================}
  602.  
  603. procedure TMIMEPart.EncodePart;
  604. var
  605.   l: TStringList;
  606.   s, t: string;
  607.   n, x: Integer;
  608.   d1, d2: integer;
  609. begin
  610.   if (FEncodingCode = ME_UU) or (FEncodingCode = ME_XX) then
  611.     Encoding := 'base64';
  612.   l := TStringList.Create;
  613.   FPartBody.Clear;
  614.   FDecodedLines.Seek(0, soFromBeginning);
  615.   try
  616.     case FPrimaryCode of
  617.       MP_MULTIPART, MP_MESSAGE:
  618.         FPartBody.LoadFromStream(FDecodedLines);
  619.       MP_TEXT, MP_BINARY:
  620.         if FEncodingCode = ME_BASE64 then
  621.         begin
  622.           while FDecodedLines.Position < FDecodedLines.Size do
  623.           begin
  624.             Setlength(s, 54);
  625.             x := FDecodedLines.Read(pointer(s)^, 54);
  626.             Setlength(s, x);
  627.             if FPrimaryCode = MP_TEXT then
  628.               s := CharsetConversion(s, FTargetCharset, FCharsetCode);
  629.             s := EncodeBase64(s);
  630.             FPartBody.Add(s);
  631.           end;
  632.         end
  633.         else
  634.         begin
  635.           l.LoadFromStream(FDecodedLines);
  636.           for n := 0 to l.Count - 1 do
  637.           begin
  638.             s := l[n];
  639.             if (FPrimaryCode = MP_TEXT) and (FEncodingCode <> ME_7BIT) then
  640.               s := CharsetConversion(s, FTargetCharset, FCharsetCode);
  641.             if FEncodingCode = ME_QUOTED_PRINTABLE then
  642.             begin
  643.               s := EncodeQuotedPrintable(s);
  644.               repeat
  645.                 if Length(s) < FMaxLineLength then
  646.                 begin
  647.                   t := s;
  648.                   s := '';
  649.                 end
  650.                 else
  651.                 begin
  652.                   d1 := RPosEx('=', s, FMaxLineLength);
  653.                   d2 := RPosEx(' ', s, FMaxLineLength);
  654.                   if (d1 = 0) and (d2 = 0) then
  655.                     x := FMaxLineLength
  656.                   else
  657.                     if d1 > d2 then
  658.                       x := d1 - 1
  659.                     else
  660.                       x := d2 - 1;
  661.                   if x = 0 then
  662.                     x := FMaxLineLength;
  663.                   t := Copy(s, 1, x);
  664.                   s := Copy(s, x + 1, Length(s) - x);
  665.                   if s <> '' then
  666.                     t := t + '=';
  667.                 end;
  668.                 FPartBody.Add(t);
  669.               until s = '';
  670.             end
  671.             else
  672.               FPartBody.Add(s);
  673.           end;
  674.         end;
  675.     end;
  676.   finally
  677.     l.Free;
  678.   end;
  679. end;
  680.  
  681. {==============================================================================}
  682.  
  683. procedure TMIMEPart.EncodePartHeader;
  684. var
  685.   s: string;
  686. begin
  687.   FHeaders.Clear;
  688.   if FSecondary = '' then
  689.     case FPrimaryCode of
  690.       MP_TEXT:
  691.         FSecondary := 'plain';
  692.       MP_MULTIPART:
  693.         FSecondary := 'mixed';
  694.       MP_MESSAGE:
  695.         FSecondary := 'rfc822';
  696.       MP_BINARY:
  697.         FSecondary := 'octet-stream';
  698.     end;
  699.   if FDescription <> '' then
  700.     FHeaders.Insert(0, 'Content-Description: ' + FDescription);
  701.   if FDisposition <> '' then
  702.   begin
  703.     s := '';
  704.     if FFileName <> '' then
  705.       s := '; FileName="' + FFileName + '"';
  706.     FHeaders.Insert(0, 'Content-Disposition: ' + LowerCase(FDisposition) + s);
  707.   end;
  708.   if FContentID <> '' then
  709.     FHeaders.Insert(0, 'Content-ID: ' + FContentID);
  710.  
  711.   case FEncodingCode of
  712.     ME_7BIT:
  713.       s := '7bit';
  714.     ME_8BIT:
  715.       s := '8bit';
  716.     ME_QUOTED_PRINTABLE:
  717.       s := 'Quoted-printable';
  718.     ME_BASE64:
  719.       s := 'Base64';
  720.   end;
  721.   case FPrimaryCode of
  722.     MP_TEXT,
  723.       MP_BINARY: FHeaders.Insert(0, 'Content-Transfer-Encoding: ' + s);
  724.   end;
  725.   case FPrimaryCode of
  726.     MP_TEXT:
  727.       s := FPrimary + '/' + FSecondary + '; charset=' + GetIDfromCP(FCharsetCode);
  728.     MP_MULTIPART:
  729.       s := FPrimary + '/' + FSecondary + '; boundary="' + FBoundary + '"';
  730.     MP_MESSAGE:
  731.       s := FPrimary + '/' + FSecondary + '';
  732.     MP_BINARY:
  733.       s := FPrimary + '/' + FSecondary + '; name="' + FFileName + '"';
  734.   end;
  735.   FHeaders.Insert(0, 'Content-type: ' + s);
  736. end;
  737.  
  738. {==============================================================================}
  739.  
  740. procedure TMIMEPart.MimeTypeFromExt(Value: string);
  741. var
  742.   s: string;
  743.   n: Integer;
  744. begin
  745.   Primary := '';
  746.   FSecondary := '';
  747.   s := UpperCase(ExtractFileExt(Value));
  748.   if s = '' then
  749.     s := UpperCase(Value);
  750.   s := SeparateRight(s, '.');
  751.   for n := 0 to MaxMimeType do
  752.     if MimeType[n, 0] = s then
  753.     begin
  754.       Primary := MimeType[n, 1];
  755.       FSecondary := MimeType[n, 2];
  756.       Break;
  757.     end;
  758.   if Primary = '' then
  759.     Primary := 'application';
  760.   if FSecondary = '' then
  761.     FSecondary := 'octet-string';
  762. end;
  763.  
  764. {==============================================================================}
  765.  
  766. procedure TMIMEPart.WalkPart;
  767. var
  768.   n: integer;
  769.   m: TMimepart;
  770. begin
  771.   if assigned(OnWalkPart) then
  772.   begin
  773.     OnWalkPart(self);
  774.     for n := 0 to GetSubPartCount - 1 do
  775.     begin
  776.       m := GetSubPart(n);
  777.       m.OnWalkPart := OnWalkPart;
  778.       m.WalkPart;
  779.     end;
  780.   end;
  781. end;
  782.  
  783. {==============================================================================}
  784.  
  785. procedure TMIMEPart.SetPrimary(Value: string);
  786. var
  787.   s: string;
  788. begin
  789.   FPrimary := Value;
  790.   s := UpperCase(Value);
  791.   FPrimaryCode := MP_BINARY;
  792.   if Pos('TEXT', s) = 1 then
  793.     FPrimaryCode := MP_TEXT;
  794.   if Pos('MULTIPART', s) = 1 then
  795.     FPrimaryCode := MP_MULTIPART;
  796.   if Pos('MESSAGE', s) = 1 then
  797.     FPrimaryCode := MP_MESSAGE;
  798. end;
  799.  
  800. procedure TMIMEPart.SetEncoding(Value: string);
  801. var
  802.   s: string;
  803. begin
  804.   FEncoding := Value;
  805.   s := UpperCase(Value);
  806.   FEncodingCode := ME_7BIT;
  807.   if Pos('8BIT', s) = 1 then
  808.     FEncodingCode := ME_8BIT;
  809.   if Pos('QUOTED-PRINTABLE', s) = 1 then
  810.     FEncodingCode := ME_QUOTED_PRINTABLE;
  811.   if Pos('BASE64', s) = 1 then
  812.     FEncodingCode := ME_BASE64;
  813.   if Pos('X-UU', s) = 1 then
  814.     FEncodingCode := ME_UU;
  815.   if Pos('X-XX', s) = 1 then
  816.     FEncodingCode := ME_XX;
  817. end;
  818.  
  819. procedure TMIMEPart.SetCharset(Value: string);
  820. begin
  821.   FCharset := Value;
  822.   FCharsetCode := GetCPFromID(Value);
  823. end;
  824.  
  825. {==============================================================================}
  826.  
  827. function GenerateBoundary: string;
  828. var
  829.   x: Integer;
  830. begin
  831.   Sleep(1);
  832.   Randomize;
  833.   x := Random(MaxInt);
  834.   Result := IntToHex(x, 8) + '_Synapse_message_boundary';
  835. end;
  836.  
  837. end.
  838.