home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 December / Chip_2001-12_cd1.bin / zkuste / delphi / unity / d23456 / SYNAPSE.ZIP / source / lib / MIMEpart.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2001-09-21  |  16.8 KB  |  586 lines

  1. {==============================================================================|
  2. | Project : Delphree - Synapse                                   | 001.005.002 |
  3. |==============================================================================|
  4. | Content: MIME support procedures and functions                               |
  5. |==============================================================================|
  6. | The contents of this file are subject to the Mozilla Public License Ver. 1.1 |
  7. | (the "License"); you may not use this file except in compliance with the     |
  8. | License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ |
  9. |                                                                              |
  10. | Software distributed under the License is distributed on an "AS IS" basis,   |
  11. | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
  12. | the specific language governing rights and limitations under the License.    |
  13. |==============================================================================|
  14. | The Original Code is Synapse Delphi Library.                                 |
  15. |==============================================================================|
  16. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  17. | Portions created by Lukas Gebauer are Copyright (c)2000,2001.                |
  18. | All Rights Reserved.                                                         |
  19. |==============================================================================|
  20. | Contributor(s):                                                              |
  21. |==============================================================================|
  22. | History: see HISTORY.HTM from distribution package                           |
  23. |          (Found at URL: http://www.ararat.cz/synapse/)                       |
  24. |==============================================================================}
  25.  
  26. unit MIMEpart;
  27.  
  28. interface
  29.  
  30. uses
  31.   SysUtils, Classes,
  32.   SynaChar, SynaCode, SynaUtil, MIMEinLn;
  33.  
  34. type
  35.  
  36.   TMimePrimary = (MP_TEXT, MP_MULTIPART,
  37.     MP_MESSAGE, MP_BINARY);
  38.  
  39.   TMimeEncoding = (ME_7BIT, ME_8BIT, ME_QUOTED_PRINTABLE,
  40.     ME_BASE64, ME_UU, ME_XX);
  41.  
  42.   TMimePart = class(TObject)
  43.   private
  44.     FPrimary: string;
  45.     FEncoding: string;
  46.     FCharset: string;
  47.     FPrimaryCode: TMimePrimary;
  48.     FEncodingCode: TMimeEncoding;
  49.     FCharsetCode: TMimeChar;
  50.     FTargetCharset: TMimeChar;
  51.     FSecondary: string;
  52.     FDescription: string;
  53.     FDisposition: string;
  54.     FContentID: string;
  55.     FBoundary: string;
  56.     FFileName: string;
  57.     FLines: TStringList;
  58.     FDecodedLines: TMemoryStream;
  59.     procedure SetPrimary(Value: string);
  60.     procedure SetEncoding(Value: string);
  61.     procedure SetCharset(Value: string);
  62.   public
  63.     constructor Create;
  64.     destructor Destroy; override;
  65.     procedure Clear;
  66.     function ExtractPart(Value: TStringList; BeginLine: Integer): Integer;
  67.     procedure DecodePart;
  68.     procedure EncodePart;
  69.     procedure MimeTypeFromExt(Value: string);
  70.   published
  71.     property Primary: string read FPrimary write SetPrimary;
  72.     property Encoding: string read FEncoding write SetEncoding;
  73.     property Charset: string read FCharset write SetCharset;
  74.     property PrimaryCode: TMimePrimary read FPrimaryCode Write FPrimaryCode;
  75.     property EncodingCode: TMimeEncoding read FEncodingCode Write FEncodingCode;
  76.     property CharsetCode: TMimeChar read FCharsetCode Write FCharsetCode;
  77.     property TargetCharset: TMimeChar read FTargetCharset Write FTargetCharset;
  78.     property Secondary: string read FSecondary Write FSecondary;
  79.     property Description: string read FDescription Write FDescription;
  80.     property Disposition: string read FDisposition Write FDisposition;
  81.     property ContentID: string read FContentID Write FContentID;
  82.     property Boundary: string read FBoundary Write FBoundary;
  83.     property FileName: string read FFileName Write FFileName;
  84.     property Lines: TStringList read FLines;
  85.     property DecodedLines: TMemoryStream read FDecodedLines;
  86.   end;
  87.  
  88. const
  89.   MaxMimeType = 25;
  90.   MimeType: array[0..MaxMimeType, 0..2] of string =
  91.   (
  92.     ('AU', 'audio', 'basic'),
  93.     ('AVI', 'video', 'x-msvideo'),
  94.     ('BMP', 'image', 'BMP'),
  95.     ('DOC', 'application', 'MSWord'),
  96.     ('EPS', 'application', 'Postscript'),
  97.     ('GIF', 'image', 'GIF'),
  98.     ('JPEG', 'image', 'JPEG'),
  99.     ('JPG', 'image', 'JPEG'),
  100.     ('MID', 'audio', 'midi'),
  101.     ('MOV', 'video', 'quicktime'),
  102.     ('MPEG', 'video', 'MPEG'),
  103.     ('MPG', 'video', 'MPEG'),
  104.     ('MP2', 'audio', 'mpeg'),
  105.     ('MP3', 'audio', 'mpeg'),
  106.     ('PDF', 'application', 'PDF'),
  107.     ('PNG', 'image', 'PNG'),
  108.     ('PS', 'application', 'Postscript'),
  109.     ('QT', 'video', 'quicktime'),
  110.     ('RA', 'audio', 'x-realaudio'),
  111.     ('RTF', 'application', 'RTF'),
  112.     ('SND', 'audio', 'basic'),
  113.     ('TIF', 'image', 'TIFF'),
  114.     ('TIFF', 'image', 'TIFF'),
  115.     ('WAV', 'audio', 'x-wav'),
  116.     ('WPD', 'application', 'Wordperfect5.1'),
  117.     ('ZIP', 'application', 'ZIP')
  118.     );
  119.  
  120. function NormalizeHeader(Value: TStringList; var Index: Integer): string;
  121. function GenerateBoundary: string;
  122.  
  123. implementation
  124.  
  125. function NormalizeHeader(Value: TStringList; var Index: Integer): string;
  126. var
  127.   s, t: string;
  128.   n: Integer;
  129. begin
  130.   s := Value[Index];
  131.   Inc(Index);
  132.   if s <> '' then
  133.     while (Value.Count - 1) > Index do
  134.     begin
  135.       t := Value[Index];
  136.       if t = '' then
  137.         Break;
  138.       for n := 1 to Length(t) do
  139.         if t[n] = #9 then
  140.           t[n] := ' ';
  141.       if t[1] <> ' ' then
  142.         Break
  143.       else
  144.       begin
  145.         s := s + ' ' + Trim(t);
  146.         Inc(Index);
  147.       end;
  148.     end;
  149.   Result := s;
  150. end;
  151.  
  152. {==============================================================================}
  153.  
  154. constructor TMIMEPart.Create;
  155. begin
  156.   inherited Create;
  157.   FLines := TStringList.Create;
  158.   FDecodedLines := TMemoryStream.Create;
  159.   FTargetCharset := GetCurCP;
  160. end;
  161.  
  162. destructor TMIMEPart.Destroy;
  163. begin
  164.   FDecodedLines.Free;
  165.   FLines.Free;
  166.   inherited Destroy;
  167. end;
  168.  
  169. {==============================================================================}
  170.  
  171. procedure TMIMEPart.Clear;
  172. begin
  173.   FPrimary := '';
  174.   FEncoding := '';
  175.   FCharset := '';
  176.   FPrimaryCode := MP_TEXT;
  177.   FEncodingCode := ME_7BIT;
  178.   FCharsetCode := ISO_8859_1;
  179.   FTargetCharset := GetCurCP;
  180.   FSecondary := '';
  181.   FDisposition := '';
  182.   FContentID := '';
  183.   FDescription := '';
  184.   FBoundary := '';
  185.   FFileName := '';
  186.   FLines.Clear;
  187.   FDecodedLines.Clear;
  188. end;
  189.  
  190. {==============================================================================}
  191.  
  192. function TMIMEPart.ExtractPart(Value: TStringList; BeginLine: Integer): Integer;
  193. var
  194.   n, x, x1, x2: Integer;
  195.   t: TStringList;
  196.   s, su, b: string;
  197.   st, st2: string;
  198.   e: Boolean;
  199.   fn: string;
  200. begin
  201.   t := TStringlist.Create;
  202.   try
  203.     { defaults }
  204.     FLines.Clear;
  205.     Primary := 'text';
  206.     FSecondary := 'plain';
  207.     FDescription := '';
  208.     Charset := 'US-ASCII';
  209.     FFileName := '';
  210.     Encoding := '7BIT';
  211.  
  212.     fn := '';
  213.     x := BeginLine;
  214.     b := FBoundary;
  215.     { if multipart - skip pre-part }
  216.     if b <> '' then
  217.       while Value.Count > x do
  218.       begin
  219.         s := Value[x];
  220.         Inc(x);
  221.         if Pos('--' + b, s) = 1 then
  222.           Break;
  223.       end;
  224.  
  225.     { parse header }
  226.     while Value.Count > x do
  227.     begin
  228.       s := NormalizeHeader(Value, x);
  229.       if s = '' then
  230.         Break;
  231.       su := UpperCase(s);
  232.       if Pos('CONTENT-TYPE:', su) = 1 then
  233.       begin
  234.         st := SeparateRight(su, ':');
  235.         st2 := SeparateLeft(st, ';');
  236.         Primary := SeparateLeft(st2, '/');
  237.         FSecondary := SeparateRight(st2, '/');
  238.         if (FSecondary = Primary) and (Pos('/', st2) < 1) then
  239.           FSecondary := '';
  240.         case FPrimaryCode of
  241.           MP_TEXT:
  242.             begin
  243.               Charset := UpperCase(GetParameter(s, 'charset='));
  244.               FFileName := GetParameter(s, 'name=');
  245.             end;
  246.           MP_MULTIPART:
  247.             FBoundary := GetParameter(s, 'Boundary=');
  248.           MP_MESSAGE:
  249.             begin
  250.             end;
  251.           MP_BINARY:
  252.             FFileName := GetParameter(s, 'name=');
  253.         end;
  254.       end;
  255.       if Pos('CONTENT-TRANSFER-ENCODING:', su) = 1 then
  256.         Encoding := SeparateRight(su, ':');
  257.       if Pos('CONTENT-DESCRIPTION:', su) = 1 then
  258.         FDescription := SeparateRight(s, ':');
  259.       if Pos('CONTENT-DISPOSITION:', su) = 1 then
  260.       begin
  261.         FDisposition := SeparateRight(su, ':');
  262.         FDisposition := Trim(SeparateLeft(FDisposition, ';'));
  263.         fn := GetParameter(s, 'FileName=');
  264.       end;
  265.       if Pos('CONTENT-ID:', su) = 1 then
  266.         FContentID := SeparateRight(s, ':');
  267.     end;
  268.  
  269.     if (PrimaryCode = MP_BINARY) and (FFileName = '') then
  270.       FFileName := fn;
  271.     FFileName := InlineDecode(FFileName, getCurCP);
  272.     FFileName := ExtractFileName(FFileName);
  273.  
  274.     { finding part content x1-begin x2-end }
  275.     x1 := x;
  276.     x2 := Value.Count - 1;
  277.     { if multipart - end is before next boundary }
  278.     if b <> '' then
  279.     begin
  280.       for n := x to Value.Count - 1 do
  281.       begin
  282.         x2 := n;
  283.         s := Value[n];
  284.         if Pos('--' + b, s) = 1 then
  285.         begin
  286.           Dec(x2);
  287.           Break;
  288.         end;
  289.       end;
  290.     end;
  291.     { if content is multipart - content is delimited by their boundaries }
  292.     if FPrimaryCode = MP_MULTIPART then
  293.     begin
  294.       for n := x to Value.Count - 1 do
  295.       begin
  296.         s := Value[n];
  297.         if Pos('--' + FBoundary, s) = 1 then
  298.         begin
  299.           x1 := n;
  300.           Break;
  301.         end;
  302.       end;
  303.       for n := Value.Count - 1 downto x do
  304.       begin
  305.         s := Value[n];
  306.         if Pos('--' + FBoundary, s) = 1 then
  307.         begin
  308.           x2 := n;
  309.           Break;
  310.         end;
  311.       end;
  312.     end;
  313.     { copy content }
  314.     for n := x1 to x2 do
  315.       FLines.Add(Value[n]);
  316.     Result := x2;
  317.     { if content is multipart - find real end }
  318.     if FPrimaryCode = MP_MULTIPART then
  319.     begin
  320.       e := False;
  321.       for n := x2 + 1 to Value.Count - 1 do
  322.         if Pos('--' + b, Value[n]) = 1 then
  323.         begin
  324.           e := True;
  325.           Break;
  326.         end;
  327.       if not e then
  328.         Result := Value.Count - 1;
  329.     end;
  330.     { if multipart - skip ending postpart}
  331.     if b <> '' then
  332.     begin
  333.       x1 := Result;
  334.       for n := x1 to Value.Count - 1 do
  335.       begin
  336.         s := Value[n];
  337.         if Pos('--' + b, s) = 1 then
  338.         begin
  339.           s := TrimRight(s);
  340.           x := Length(s);
  341.           if x > 4 then
  342.             if (s[x] = '-') and (S[x-1] = '-') then
  343.               Result := Value.Count - 1;
  344.           Break;
  345.         end;
  346.       end;
  347.     end;
  348.   finally
  349.     t.Free;
  350.   end;
  351. end;
  352.  
  353. {==============================================================================}
  354.  
  355. procedure TMIMEPart.DecodePart;
  356. const
  357.   CRLF = #13#10;
  358. var
  359.   n: Integer;
  360.   s: string;
  361. begin
  362.   FDecodedLines.Clear;
  363.   for n := 0 to FLines.Count - 1 do
  364.   begin
  365.     s := FLines[n];
  366.     case FEncodingCode of
  367.       ME_7BIT:
  368.         s := s + CRLF;
  369.       ME_8BIT:
  370.         begin
  371.           s := CharsetConversion(s, FCharsetCode, FTargetCharset);
  372.           s := s + CRLF;
  373.         end;
  374.       ME_QUOTED_PRINTABLE:
  375.         begin
  376.           if s = '' then
  377.             s := CRLF
  378.           else
  379.             if s[Length(s)] <> '=' then
  380.               s := s + CRLF;
  381.           s := DecodeQuotedPrintable(s);
  382.           if FPrimaryCode = MP_TEXT then
  383.             s := CharsetConversion(s, FCharsetCode, FTargetCharset);
  384.         end;
  385.       ME_BASE64:
  386.         begin
  387.           if s <> '' then
  388.             s := DecodeBase64(s);
  389.           if FPrimaryCode = MP_TEXT then
  390.             s := CharsetConversion(s, FCharsetCode, FTargetCharset);
  391.         end;
  392.       ME_UU:
  393.         if s <> '' then
  394.           s := DecodeUU(s);
  395.       ME_XX:
  396.         if s <> '' then
  397.           s := DecodeXX(s);
  398.     end;
  399.     FDecodedLines.Write(Pointer(s)^, Length(s));
  400.   end;
  401.   FDecodedLines.Seek(0, soFromBeginning);
  402. end;
  403.  
  404. {==============================================================================}
  405.  
  406. procedure TMIMEPart.EncodePart;
  407. var
  408.   l: TStringList;
  409.   s, buff: string;
  410.   n, x: Integer;
  411. begin
  412.   if (FEncodingCode = ME_UU) or (FEncodingCode = ME_XX) then
  413.     Encoding := 'base64';
  414.   l := TStringList.Create;
  415.   FLines.Clear;
  416.   FDecodedLines.Seek(0, soFromBeginning);
  417.   try
  418.     case FPrimaryCode of
  419.       MP_MULTIPART, MP_MESSAGE:
  420.         FLines.LoadFromStream(FDecodedLines);
  421.       MP_TEXT, MP_BINARY:
  422.         if FEncodingCode = ME_BASE64 then
  423.         begin
  424.           while FDecodedLines.Position < FDecodedLines.Size do
  425.           begin
  426.             Setlength(Buff, 54);
  427.             s := '';
  428.             x := FDecodedLines.Read(pointer(Buff)^, 54);
  429.             for n := 1 to x do
  430.               s := s + Buff[n];
  431.             if FPrimaryCode = MP_TEXT then
  432.               s := CharsetConversion(s, FTargetCharset, FCharsetCode);
  433.             s := EncodeBase64(s);
  434.             FLines.Add(s);
  435.           end;
  436.         end
  437.         else
  438.         begin
  439.           l.LoadFromStream(FDecodedLines);
  440.           for n := 0 to l.Count - 1 do
  441.           begin
  442.             s := l[n];
  443.             if FPrimaryCode = MP_TEXT then
  444.               s := CharsetConversion(s, FTargetCharset, FCharsetCode);
  445.             s := EncodeQuotedPrintable(s);
  446.             FLines.Add(s);
  447.           end;
  448.         end;
  449.  
  450.     end;
  451.     FLines.Add('');
  452.     FLines.Insert(0, '');
  453.     if FSecondary = '' then
  454.       case FPrimaryCode of
  455.         MP_TEXT:
  456.           FSecondary := 'plain';
  457.         MP_MULTIPART:
  458.           FSecondary := 'mixed';
  459.         MP_MESSAGE:
  460.           FSecondary := 'rfc822';
  461.         MP_BINARY:
  462.           FSecondary := 'octet-stream';
  463.       end;
  464.     if FDescription <> '' then
  465.       FLines.Insert(0, 'Content-Description: ' + FDescription);
  466.     if FDisposition <> '' then
  467.     begin
  468.       s := '';
  469.       if FFileName <> '' then
  470.         s := '; FileName="' + FFileName + '"';
  471.       FLines.Insert(0, 'Content-Disposition: ' + LowerCase(FDisposition) + s);
  472.     end;
  473.     if FContentID <> '' then
  474.       FLines.Insert(0, 'Content-ID: ' + FContentID);
  475.  
  476.     case FEncodingCode of
  477.       ME_7BIT:
  478.         s := '7bit';
  479.       ME_8BIT:
  480.         s := '8bit';
  481.       ME_QUOTED_PRINTABLE:
  482.         s := 'Quoted-printable';
  483.       ME_BASE64:
  484.         s := 'Base64';
  485.     end;
  486.     case FPrimaryCode of
  487.       MP_TEXT,
  488.         MP_BINARY: FLines.Insert(0, 'Content-Transfer-Encoding: ' + s);
  489.     end;
  490.     case FPrimaryCode of
  491.       MP_TEXT:
  492.         s := FPrimary + '/' + FSecondary + '; charset=' + GetIDfromCP(FCharsetCode);
  493.       MP_MULTIPART:
  494.         s := FPrimary + '/' + FSecondary + '; boundary="' + FBoundary + '"';
  495.       MP_MESSAGE:
  496.         s := FPrimary + '/' + FSecondary + '';
  497.       MP_BINARY:
  498.         s := FPrimary + '/' + FSecondary + '; name="' + FFileName + '"';
  499.     end;
  500.     FLines.Insert(0, 'Content-type: ' + s);
  501.   finally
  502.     l.Free;
  503.   end;
  504. end;
  505.  
  506. {==============================================================================}
  507.  
  508. procedure TMIMEPart.MimeTypeFromExt(Value: string);
  509. var
  510.   s: string;
  511.   n: Integer;
  512. begin
  513.   Primary := '';
  514.   FSecondary := '';
  515.   s := UpperCase(ExtractFileExt(Value));
  516.   if s = '' then
  517.     s := UpperCase(Value);
  518.   s := SeparateRight(s, '.');
  519.   for n := 0 to MaxMimeType do
  520.     if MimeType[n, 0] = s then
  521.     begin
  522.       Primary := MimeType[n, 1];
  523.       FSecondary := MimeType[n, 2];
  524.       Break;
  525.     end;
  526.   if Primary = '' then
  527.     Primary := 'application';
  528.   if FSecondary = '' then
  529.     FSecondary := 'octet-string';
  530. end;
  531.  
  532. {==============================================================================}
  533.  
  534. procedure TMIMEPart.SetPrimary(Value: string);
  535. var
  536.   s: string;
  537. begin
  538.   FPrimary := Value;
  539.   s := UpperCase(Value);
  540.   FPrimaryCode := MP_BINARY;
  541.   if Pos('TEXT', s) = 1 then
  542.     FPrimaryCode := MP_TEXT;
  543.   if Pos('MULTIPART', s) = 1 then
  544.     FPrimaryCode := MP_MULTIPART;
  545.   if Pos('MESSAGE', s) = 1 then
  546.     FPrimaryCode := MP_MESSAGE;
  547. end;
  548.  
  549. procedure TMIMEPart.SetEncoding(Value: string);
  550. var
  551.   s: string;
  552. begin
  553.   FEncoding := Value;
  554.   s := UpperCase(Value);
  555.   FEncodingCode := ME_7BIT;
  556.   if Pos('8BIT', s) = 1 then
  557.     FEncodingCode := ME_8BIT;
  558.   if Pos('QUOTED-PRINTABLE', s) = 1 then
  559.     FEncodingCode := ME_QUOTED_PRINTABLE;
  560.   if Pos('BASE64', s) = 1 then
  561.     FEncodingCode := ME_BASE64;
  562.   if Pos('X-UU', s) = 1 then
  563.     FEncodingCode := ME_UU;
  564.   if Pos('X-XX', s) = 1 then
  565.     FEncodingCode := ME_XX;
  566. end;
  567.  
  568. procedure TMIMEPart.SetCharset(Value: string);
  569. begin
  570.   FCharset := Value;
  571.   FCharsetCode := GetCPFromID(Value);
  572. end;
  573.  
  574. {==============================================================================}
  575.  
  576. function GenerateBoundary: string;
  577. var
  578.   x: Integer;
  579. begin
  580.   Randomize;
  581.   x := Random(MaxInt);
  582.   Result := '--' + IntToHex(x, 8) + '_Synapse_message_boundary--';
  583. end;
  584.  
  585. end.
  586.