UNIT DelMime;

{$i platform.inc}

{ this unit contains most of the code used by the RFC->FTN gateways }
{ (e-mail to netmail, e-mail to echomail, news to netmail and news  }
{ to echomail) to remove all the MIME related information.          }

INTERFACE

USES CharSets;

FUNCTION  MimeHeaderToFTN (Line : STRING) : STRING;
PROCEDURE MimeBodyToFtn (InSetNr,OutSetNr : CharSetNr);


IMPLEMENTATION

USES Ramon,
     Msgs,
     Decode,
     Cfg,
     Logs,
     Routing;

TYPE MimeLineTypes = (mlt_Other,
                      mlt_ContentType,
                      mlt_ContentTransferEncoding,
                      mlt_ContentDisposition);

     MimeEncodingTypes = (met_Plain,
                          met_QuotedPrintable,
                          met_Base64);

VAR MimeIsMulti    : BOOLEAN; { if true, then is multi-part msg }
    MimeIsText     : BOOLEAN; { geen plaatjes gaan omzetten! }
    MimeIsAttach   : BOOLEAN;
    MimeInHeader   : BOOLEAN;
    MimeCharSetStr : CharSetNameStr;
    MimeLineType   : MimeLineTypes;
    MimeEncoding   : MimeEncodingTypes;
    MimeCharTable  : CharTableType;
    MimeInSetNr    : CharSetNr;
    MimeOutSetNr   : CharSetNr;

{--------------------------------------------------------------------------}
{ CheckForMimeHeaders                                                      }
{                                                                          }
{ Deze routine kijkt of de regel een MIME header of boundary indicatie     }
{ bevat. Zoja, dan worden de stuur vlaggen aangepast.                      }
{ NOTE: Do not modify OrigRegel!!                                          }
{                                                                          }
FUNCTION CheckForMimeHeaders (VAR OrigRegel : STRING) : BOOLEAN; FAR;

VAR P,P2  : BYTE;
    Regel : STRING;
    Temp  : STRING[80];

BEGIN
     CheckForMimeHeaders:=FALSE; { continue always }

     { boundary check hier toevoegen }

     Regel:=OrigRegel;

     { set MimeLineType to mlt_Other if this is not a continuation line }
     IF (MimeLineType <> mlt_Other) AND (NOT (OrigRegel[1] IN [' ',#9])) THEN
        MimeLineType:=mlt_Other;

     {LogMessage (liDebug, '[CheckForMimeHEaders] "'+OrigRegel+'"');}

     IF CaselessStartMatch (OrigRegel,'Content-Type: ') THEN
     BEGIN
          MimeLineType:=mlt_ContentType;
          Delete (Regel,1,14);
     END;

     IF CaselessStartMatch (OrigRegel,'Content-Transfer-Encoding: ') THEN
     BEGIN
          MimeLineType:=mlt_ContentTransferEncoding;
          Delete (Regel,1,27);
     END;

     IF CaselessStartMatch (OrigRegel,'Content-Disposition: ') THEN
     BEGIN
          MimeLineType:=mlt_ContentDisposition;
          Delete (Regel,1,21);
     END;

     IF (MimeLineType = mlt_Other) THEN
        Exit;

     Regel:=UpCaseString (Regel);
     
     IF (MimeLineType = mlt_ContentType) THEN
     BEGIN
          {LogMessage (liDebug, '[CheckForMimeHeaders] Content: "'+Regel+'"');}

          IF (Pos ('MULTIPART',Regel) > 0) THEN
             MimeIsMulti:=TRUE;

          (*
          { voorkom het vertalen van 8-bit encoded plaatjes! }
          { dus controleer op text. Anders niet eens verwerken }
          {## can break if filename contains this text. Should check for application/xxx}
          MimeIsText:=(Copy (Regel,1,4) = 'TEXT') OR (Copy (Regel,1,7) = 'MESSAGE');

          { AAT000317 can also be application/ }
          IF (Pos ('IMAGE/',Regel) > 0) OR (Pos ('APPLICATION/', Regel) > 0) THEN
             MimeIsAttach:=TRUE;
          *)

          { AAT 000326: Implemented type scanning from info in ROUTE.TDB   }

          { If the first word contains a '/', it is probably a type        }
          { specification.                                                 }
          P2 := Pos ('/', Regel);
          IF (P2 > 0) THEN
          BEGIN
               { Only accept it if it is in the first word }
               {AAT000621: Wasn't triggerring if there was no space in the }
               {           string.                                         }
               IF ((Pos (' ', Regel) > P2) OR (Pos (' ', Regel) = 0)) THEN
               BEGIN
                    Temp := Copy (Regel, 1, P2-1);

                    IF Config.LogDebug THEN
                       LogMessage (liDebug,'[DELMIME] MIME type: '+Temp);

                    IF (IsMimeAttachment (Temp)) THEN
                    BEGIN
                         IF Config.LogDebug THEN
                            LogMessage (liDebug,'[DELMIME] This is an attachment!');
                         MimeIsAttach:=TRUE;
                    END ELSE
                        IF Config.LogDebug THEN
                           LogMessage (liDebug,'[DELMIME] Not an attachment');

                    {## add MIME-TEXT to routing as well?}
                    MimeIsText:=(Temp = 'TEXT') OR (Temp = 'MESSAGE');
               END;
          END;

          { kijk of er een charset in voorkomt }
          { zoniet, neem dan de default }
          P:=Pos ('CHARSET',Regel);
          IF (P > 1) AND (Regel[P-1] IN [' ',#9,';',',']) THEN
          BEGIN
               { alles tot en met de charset verwijderen }
               Delete (Regel,1,P+6);

               { andere variabelen strippen }
               P:=Pos (';',Regel);
               IF (P > 0) THEN
                  Regel:=Copy (Regel,1,P-1);

               { spaties tot en met de = verwijderen }
               IF (Pos ('=',Regel) > 0) THEN
               BEGIN
                    WHILE (Regel[1] <> '=') DO
                          Delete (Regel,1,1);

                    { = zelf verwijderen }
                    Delete (Regel,1,1);

                    { nog meer spaties overslaan }
                    WHILE (Regel[1] = ' ') DO
                          Delete (Regel,1,1);

                    { CR aan het einde verwijderen }
                    P:=Length (Regel);
                    IF (Regel[P] = #13) THEN
                       Delete (Regel,P,1);

                    { kijk of er aanhalingstekens omheen staan }
                    IF (Regel[1] = '"') THEN
                       Delete (Regel,1,1);

                    P:=Length (Regel);
                    IF (Regel[P] = '"') THEN
                       Delete (Regel,P,1);

                    { laadt de karakterset }
                    MimeCharSetStr:=Regel;
               END;
          END;

          { hoeven niet verder te zoeken }
          Exit;
     END;

     IF (MimeLineType = mlt_ContentTransferEncoding) THEN
     BEGIN
          { kijk of we quoted-printable decoding moeten doen }
          IF (Pos ('QUOTED-PRINTABLE',Regel) > 0) THEN
             MimeEncoding:=met_QuotedPrintable;

          IF (Pos ('BASE64',Regel) > 0) THEN
             MimeEncoding:=met_Base64;

          Exit;
     END;

     IF (MimeLineType = mlt_ContentDisposition) THEN
     BEGIN
          IF (Pos ('ATTACHMENT',Regel) > 0) THEN
             MimeIsAttach:=TRUE;

          IF (Pos ('FILENAME',Regel) > 0) THEN
             MimeIsAttach:=TRUE;

          IF (Pos ('NAME',Regel) > 0) THEN
             MimeIsAttach:=TRUE;

          Exit;
     END;
END;


VAR InExceptHeader : BOOLEAN;

{--------------------------------------------------------------------------}
{ CopyExceptMimeEncodingHeader                                             }
{                                                                          }
FUNCTION CopyExceptMimeEncodingHeader (VAR OrigRegel : STRING) : BOOLEAN; FAR;
BEGIN
     CopyExceptMimeEncodingHeader:=FALSE; { continue always }

     IF InExceptHeader AND (NOT (OrigRegel[1] IN [' ',#9])) THEN
        InExceptHeader:=FALSE;

     IF CaselessStartMatch (OrigRegel,'Content-Transfer-Encoding: ') THEN
     BEGIN
          InExceptHeader:=TRUE;
          Exit;
     END;

     IF CaselessStartMatch (OrigRegel,'Content-Disposition: ') THEN
     BEGIN
          InExceptHeader:=TRUE;
          Exit;
     END;

     IF (NOT InExceptHeader) THEN
        MsgsAddLineToNoEOL (Header_U,OrigRegel);
END;


{--------------------------------------------------------------------------}
{ DeleteMimeEncodingHeaders                                                }
{                                                                          }
{ This routine is used to remove the MIME encoding related information     }
{ from the Header_U. This is used for single-part MIME messages that are   }
{ being quoted-printable or base64 decoded.                                }
{                                                                          }
PROCEDURE DeleteMimeEncodingHeaders;

VAR OldTop : TopRegelRecordPtr;

BEGIN
     OldTop:=Msg.HeaderTop_U;
     Msg.HeaderTop_U:=NIL;

     InExceptHeader:=FALSE;
     MsgsForEachKill (OldTop,CopyExceptMimeEncodingHeader);
END;


{--------------------------------------------------------------------------}
{ Mime_InterpretMimeHeaders                                                }
{                                                                          }
{ This routine checks for MIME headers in a multi-part message and stops   }
{ when it has found the empty line that separates the headers and body.    }
{                                                                          }
FUNCTION Mime_InterpretMimeHeaders (VAR OrigRegel : STRING) : BOOLEAN; FAR;
BEGIN
     Mime_InterpretMimeHeaders:=(OrigRegel = #13);
     CheckForMimeHeaders (OrigRegel);
END;


{--------------------------------------------------------------------------}
{ Mime_TranslateBodyLineToFtn                                              }
{                                                                          }
{ Deze routine verwerkt e'e'n regel van de body van een MIME bericht.      }
{                                                                          }
FUNCTION Mime_TranslateBodyLineToFtn (VAR OrigRegel : STRING) : BOOLEAN; FAR;

VAR Regel   : STRING;
    Lp      : BYTE;
    Getal   : BYTE;
    EOL     : STRING[2];
    InSetNr : CharSetNr;

BEGIN
     Mime_TranslateBodyLineToFtn:=FALSE; { continue always }

     IF MimeInHeader THEN
     BEGIN
          IF (OrigRegel = #13) THEN
          BEGIN
               MimeInHeader:=FALSE;

               IF (MimeCharSetStr = '') THEN
                  InSetNr:=MimeInSetNr
               ELSE
                   InSetNr:=CharSets_Load (MimeCharSetStr,stMime,FALSE{in});

               CharSets_BuildTransTable (MimeCharTable,InSetNr,MimeOutSetNr);

               (*
               LogMessage (liDebug,'MIME control fields:');
               LogExtraMessage ('MimeIsMulti='+Byte2String (Byte (MimeIsMulti)));
               LogExtraMessage ('MimeIsText='+Byte2String (Byte (MimeIsText)));
               LogExtraMessage ('MimeEncoding='+Byte2String (Byte (MimeEncoding)));
               LogExtraMessage ('MimeIsAttach='+Byte2String (Byte (MimeIsAttach)));
               *)
          END;

          { do not add a Content-Transfer-Encoding header }
          { unless we keep this as an attachment          }
          IF MimeIsAttach OR (NOT CaselessStartMatch (OrigRegel,'Content-Transfer-Encoding: ')) THEN
             MsgsAddLineToNoEOL (Body,OrigRegel);

          Exit;
     END;

     IF MimeIsAttach THEN
     BEGIN
          { this will be handled on the FTN side }
          MsgsAddLineToNoEOL (Body,OrigRegel);
          Exit;
     END;

     { niet direct de originele regel veranderen, want die is opgeslagen }
     { en dat geeft problememen bij het geheugen vrijgeven.              }
     Regel:=OrigRegel;

     IF (MimeEncoding = met_QuotedPrintable) THEN
        Regel:=DecodeMimeQP (Regel)
     ELSE
         IF (MimeEncoding = met_Base64) THEN
         BEGIN
              IF (Regel[Length (Regel)] = #13) THEN
                 Delete (Regel,Length (Regel),1);

              Regel:=DecodeMimeBase64 (Regel);
         END;

     FOR Lp:=1 TO Length (Regel) DO
     BEGIN
          Getal:=Byte (Regel[Lp]);

          { Getal nu in MIME character set }
          IF (Getal >= 128) THEN
             Regel[Lp]:=Char (MimeCharTable[Getal]);

          {## can cause problems with #0 or #13 in the body!}
     END; { for }

     { in case of BASE64 decoding, Regel can now contain multiple lines }
     { and line termination is #13#10 but also #10#13.                  }

     {## EOL inside Base64 decoded line, split over two lines, is not }
     {   supported yet!!                                              }

     EOL:=#13#10;
     IF (Pos (EOL,Regel) = 0) THEN
     BEGIN
          EOL:=#10#13;
          IF (Pos (EOL,Regel) = 0) THEN
          BEGIN
               { normal line }
               MsgsAddLineToNoEOL (Body,Regel);
               Exit; { ## EXIT ## }
          END;
     END;

     { split this line into multiple lines }
     REPEAT
           Lp:=Pos (EOL,Regel);
           IF (Lp > 0) THEN
           BEGIN
                MsgsAddLineTo (Body,Copy (Regel,1,Lp-1));
                Delete (Regel,1,Lp+1);
           END ELSE
               MsgsAddLineToNoEOL (Body,Regel);
     UNTIL (Lp = 0);
END;


VAR CopyMime_WasContent : BOOLEAN;

{--------------------------------------------------------------------------}
{ CopyMimeContentHeadersToBody                                             }
{                                                                          }
{ Deze routine kijkt of een header een MIME regel is die aan geeft dat de  }
{ body een encoded file bevat. Zoja, dan wordt deze regel in de body       }
{ gezet.                                                                   }
{                                                                          }
FUNCTION CopyMimeContentHeadersToBody (VAR OrigRegel : STRING) : BOOLEAN; FAR;

VAR UpRegel : STRING;

BEGIN
     CopyMimeContentHeadersToBody:=FALSE; { continue always }

     { check for continuation header }
     IF CopyMime_WasContent AND (NOT (OrigRegel[1] IN [' ',#9])) THEN
        CopyMime_WasContent:=FALSE;

     IF CaselessStartMatch (OrigRegel,'Content-') THEN
        CopyMime_WasContent:=TRUE; { for multi-line encoding }

     IF CopyMime_WasContent THEN
        MsgsAddLineToNoEOL (Body,OrigRegel);
END;


{--------------------------------------------------------------------------}
{ MimeBodyToFtn                                                            }
{                                                                          }
{ This routine is used by the RFC->FTN gateways for translating the entire }
{ message into non-MIME format. All traces of MIME must be removed.        }
{                                                                          }
{ This routine scans the body of a MIME message en set alle =XX en =<CR> }
{ codes om in 8-bit codes. Als de body al 8-bit was, dan blijft dat zo.    }
{ Na die vertaling wordt de karakterset omgezet naar een FTN karakter set. }
{                                                                          }
PROCEDURE MimeBodyToFtn (InSetNr,OutSetNr : CharSetNr);

VAR OldBody           : TopRegelRecordPtr;
    OldPartStatus     : PartStatusType;
    OldAttachmentInfo : STRING;
    Lp                : 1..MAX_BODY_PARTS;

BEGIN
     IF Config.LogDebug THEN
        LogMessage (liTrivial,'Translating MIME body to FTN');

     { initialiseer default variabelen }
     MimeIsText:=TRUE;
     MimeEncoding:=met_Plain;
     MimeCharSetStr:='';
     MimeIsMulti:=FALSE;
     MimeIsAttach:=FALSE;
     MimeInHeader:=TRUE;

     MimeInSetNr:=InSetNr;
     MimeOutSetNr:=OutSetNr;

     { check the RFC headers for MIME headers }
     { this fills in the above fields }
     MsgsForEach (Msg.HeaderTop_U,CheckForMimeHeaders);

     {RAWI991101: single-part MIME messages (not f/a) were not }
     {            processed properly, so change the below.     }
     IF (NOT MimeIsMulti) AND MimeIsAttach THEN
     BEGIN
          MsgsInsertBodyPartAtTop;

          CopyMime_WasContent:=FALSE;
          MsgsForEach (Msg.HeaderTop_U,CopyMimeContentHeadersToBody);

          { separate headers and body with an empty line }
          MsgsAddLineTo (Body,'');
          MsgsMergeFirstAndSecondBodyPart;
     END;

     IF (NOT MimeIsMulti) THEN
        CharSets_BuildTransTable (MimeCharTable,MimeInSetNr,MimeOutSetNr);

     { process the body parts and perform the translation }
     FOR Lp:=1 TO MAX_BODY_PARTS DO
     BEGIN
          Msg.CurrentBodyPart:=Lp;

          { copy the pointer to the body part and then clean it }
          OldBody:=Msg.BodyParts[Lp];
          IF (OldBody <> NIL) THEN
          BEGIN
               Msg.BodyParts[Lp]:=NIL;

               { save the old part status and attachment info }
               { info is otherwise lost because of body part reconstruction }
               OldPartStatus:=OldBody^.PartStatus;
               OldAttachmentInfo:=OldBody^.AttachmentInfo;

               { initialiseer default variabelen }
               IF MimeIsMulti THEN
               BEGIN
                    MimeIsText:=TRUE;
                    MimeEncoding:=met_Plain;
                    MimeCharSetStr:='';
                    {MimeIsMulti:=FALSE;} {RAWI991123}
                    MimeIsAttach:=FALSE;
                    MimeInHeader:=TRUE;
               END ELSE
               BEGIN
                    { not multi-part }
                    MimeInHeader:=FALSE; {RAWI991123}
               END;

               { pre-process the MIME headers }
               MsgsForEach (OldBody,Mime_InterpretMimeHeaders);

               { if we decode (qp,base64) a body part now, then we }
               { must remove the Content-transfer-encoding header. }
               { this is done in Mime_TranslateBodyLineToFtn.      }

               { translate all lines and store them again; kill processed lines }
               MsgsForEachKill (OldBody,Mime_TranslateBodyLineToFtn);

               IF (MimeIsAttach) THEN
               BEGIN
                    IF Config.LogDebug THEN
                       LogMessage (liDebug,'[DELMIME] Acknowledge part '+Integer2String (Lp)+' is attachment');
                    OldPartStatus:=psAttachment;
               END;

               { restore the part status and attachment info }
               Msg.BodyParts[Lp]^.PartStatus:=OldPartStatus;
               Msg.BodyParts[Lp]^.AttachmentInfo:=OldAttachmentInfo;
          END;
     END; { for }
END;


{--------------------------------------------------------------------------}
{ MimeHeaderToFtn                                                          }
{                                                                          }
{ This routine extract the real data from MIME encoded headers in the form }
{ of "=?iso-8859-1?Q?Sen=F5r_David?=" and translates it to an FTN set.     }
{ See RFC1342 for details.                                                 }
{                                                                          }
FUNCTION MimeHeaderToFtn (Line : STRING) : STRING;

VAR P           : WORD;
    DecodedLine,
    EncodedWord,
    DecodedWord : STRING;

    {----------------------------------------------------------------------}
    { DecodeWord                                                           }
    {                                                                      }
    FUNCTION DecodeWord (EncodedWord: STRING): STRING;

    VAR Encoding : CHAR;
        P        : WORD;
        Org      : STRING;
        S        : STRING;
        Digit    : CHAR;
        Getal    : BYTE;
        InSetNr  : CharSetNr;
        Lp       : BYTE;

    BEGIN
         Org:=EncodedWord;

         { remove the "=?" from the start and "?=" at the end }
         EncodedWord:=Copy (EncodedWord,3,Length (EncodedWord)-4);

         { extract the character set name }
         P:=Pos ('?',EncodedWord);
         S:=Copy (EncodedWord,1,P-1);
         Delete (EncodedWord,1,P);

         Encoding:=UpCase (EncodedWord[1]);
         IF (EncodedWord[2] <> '?') OR (NOT (Encoding IN ['B','Q'])) THEN
         BEGIN
              { not encoded, keep original }
              DecodeWord:=Org;
              Exit;                     { ## EXIT ## }
         END;

         { remove the encoding type and "?" }
         Delete (EncodedWord,1,2);

         IF (Pos ('?',EncodedWord) > 0) THEN
         BEGIN
              { not terminated properly }
              DecodeWord:=Org;
              Exit;                     { ## EXIT ## }
         END;

         IF (NOT CharSets_LoadIfExist (S,stMime,FALSE{in},InSetNr)) THEN
         BEGIN
              { character set is not known; stick to unencoded block }
              DecodeWord:=Org;
              Exit; { ## EXIT ## }
         END;

         CharSets_BuildTransTable (MimeCharTable,InSetNr,DefaultFtnOutSetNr);

         IF (Encoding = 'Q') THEN
         BEGIN
              { Quoted-printable encoding }

              { Start removing QP encoding }
              { "_" represents a space!    }

              P:=1;
              S:='';
              WHILE (P <= Length (EncodedWord)) DO
              BEGIN
                   { according to RFC1342!! }
                   { do this before decoding possibly encoded underscores! }
                   CASE EncodedWord[P] OF
                        '_':
                            S := S + ' ';

                        '=':
                            IF (P < Length (EncodedWord)-1) AND
                               (EncodedWord[P+1] IN ['0'..'9','A'..'F','a'..'f']) AND
                               (EncodedWord[P+2] IN ['0'..'9','A'..'F','a'..'f']) THEN
                            BEGIN
                                 { lokale hex vertaling voor hoge snelheid }
                                 Digit:=UpCase (EncodedWord[P+1]);

                                 IF (Digit IN ['0'..'9']) THEN
                                    Getal:=Ord (Digit)-Ord ('0')
                                 ELSE
                                     IF (Digit IN ['A'..'F']) THEN
                                        Getal:=Ord (Digit)-Ord ('A')+10;

                                 Getal:=Getal SHL 4;

                                 Digit:=UpCase (EncodedWord[P+2]);

                                 IF (Digit IN ['0'..'9']) THEN
                                    Getal:=Getal OR (Ord (Digit)-Ord ('0'))
                                 ELSE
                                     IF (Digit IN ['A'..'F']) THEN
                                        Getal:=Getal OR (Ord (Digit)-Ord ('A')+10);

                                 { opslaan als 8-bit data }
                                 S := S + Char (Getal);
                                 Inc(P,2);
                            END ELSE
                                S:=S+EncodedWord[P];

                       ELSE
                           S:=S+EncodedWord[P];

                   END; { case }

                   Inc (P);
              END; { while }

              { S contains output }
         END ELSE
         BEGIN
              { BASE64 encoding }
              S:=DecodeMimeBase64 (EncodedWord);

              { needs post-processing }
              { invalid characters can be present in the decoded string }
              { if this is the case then we should keep the original    }
              { (until we find a better solution for japanese ;)        }

              FOR Lp:=1 TO Length (S) DO
                 IF (S[Lp] IN [#0..#31]) THEN
                 BEGIN
                      S:=Org;
                      Break; { from the loop }
                 END;

              { output is in S }
         END;

         FOR Lp:=1 TO Length (S) DO
         BEGIN
              Getal:=Ord (S[Lp]);
              IF (Getal >= 128) THEN
                 S[Lp]:=Char (MimeCharTable[Getal]);
         END;

         DecodeWord:=S;
    END;

    {----------------------------------------------------------------------}
    { FindEndEncodedWord                                                   }
    {                                                                      }
    FUNCTION FindEndEncodedWord (Line: STRING): WORD;

    VAR P: WORD;

    BEGIN
         P:=3;

         WHILE (P <= Length(Line)) AND (Line[P] <> '?') DO
               Inc(P);

         Inc (P,3);

         WHILE (P < Length(Line)) AND ((Line[P] <> '?') OR (Line[P+1] <> '=')) DO
               Inc(P);

         IF (Copy(Line, P, 2) = '?=') THEN
            FindEndEncodedWord:=P+1
         ELSE
             FindEndEncodedWord:=0;
    END;

{MimeHeaderToFtn}

BEGIN
     IF (Pos ('=?',Line) = 0) THEN
     BEGIN
          MimeHeaderToFtn:=Line;
          Exit; { ## EXIT ## }
     END;

     DecodedLine:='';

     WHILE (Line <> '') DO
     BEGIN
          P:=Pos ('=?',Line);
          IF (P = 0) THEN
             P:=Length (Line)+1;

          DecodedLine:=DecodedLine+Copy (Line,1,P-1);
          Line:=Copy (Line,P,255);

          P:=FindEndEncodedWord (Line);
          IF (P = 0) THEN
          BEGIN
               DecodedLine:=DecodedLine+Line;
               Line:='';
          END ELSE
          BEGIN
               EncodedWord:=Copy (Line,1,P);
               Line:=Copy (Line,P+1,255);

               DecodedWord:=DecodeWord (EncodedWord);
               DecodedLine:=DecodedLine+DecodedWord;
          END;
     END; { while }

     { return the translated version }
     MimeHeaderToFTN:=DecodedLine;
END;


END.
