AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke Delphi Indy TIdMessage TExt auslesen
Thema durchsuchen
Ansicht
Themen-Optionen

Indy TIdMessage TExt auslesen

Ein Thema von LokutusvB · begonnen am 28. Jan 2009 · letzter Beitrag vom 29. Jan 2009
Antwort Antwort
Seite 1 von 2  1 2      
LokutusvB

Registriert seit: 18. Jul 2006
277 Beiträge
 
Delphi XE6 Enterprise
 
#1

Indy TIdMessage TExt auslesen

  Alt 28. Jan 2009, 14:10
Hallo Leute,

ich experimentiere jetzt einige Zeit in der TIdMessage-Komponente von Indy. Das Laden einer Email, das Anzeigen von Betreff, Absender usw. klappt super. Was mir jedoch überhaupt nicht gelingen will, ist den normalen Nachrichteninhalt als einfachen Text anzuzeigen, egal ob HTML- oder Textmail. Keine der Body-Möglichkeiten greift hier, egal ob Text, GetText oder CommaText. Wie komme ich an den Inhalt einer normalen Mail heran um ihn mir anzeigen lassen zu können? Dokumentationen der Indy-Komponenten findet man ja leider nicht wirklich im Netz, selbst die Indyseite verlinkt fehlerhaft.

Zusatz:
Nach dem ich endlich direkt in der Indy-Installation eine Doku finden konnte, mußte ich einsehen, das Body der total falsche Anhaltspunkt ist. MessageParts ist das richtige Stichwort. Allerdings nützt mir das Herauslesen des TiDTextes auch nicht so viel, da mir unklar ist, wie ich den anzeigen kann.
MFG
LokutusvB
  Mit Zitat antworten Zitat
nahpets
(Gast)

n/a Beiträge
 
#2

Re: Indy TIdMessage TExt auslesen

  Alt 28. Jan 2009, 15:37
Hallo,

hilft Dir das (soeben aus 'nem Programm geklaut):
Delphi-Quellcode:
 
begin
  sl.Clear;
  sl.Add('');
  sl.Add(IdMessage.Headers.Text);
  sl.Add('');
  sl.Add(idMessage.Body.Text);
  sl.Add('');
  For i := 0 To IDMessage.MessageParts.Count - 1 Do Begin
    If IdMessage.MessageParts.Items[i] Is TIDText Then with IdMessage.MessageParts.Items[i] As TIDText do Begin
      sl.Add('');
      // ContentType: text/html
      If AnsiContainsText(IdMessage.MessageParts.Items[i].ContentType,'html') Then Begin
        sl.Add(AnsiReplaceText(WrapText(Body.Text, 80),#13#10#13#10,#13#10));
      End else Begin
        sl.Add(Body.Text);
      End;
    End;
  End;
End;
  Mit Zitat antworten Zitat
LokutusvB

Registriert seit: 18. Jul 2006
277 Beiträge
 
Delphi XE6 Enterprise
 
#3

Re: Indy TIdMessage TExt auslesen

  Alt 28. Jan 2009, 15:49
Danke dir, ich schaue mir das mal an.

ich habe es so gemacht:
Delphi-Quellcode:
if (mail.MessageParts.Count > 0) then begin
  for ii := 0 to mail.MessageParts.Count - 1 do begin
    //text := mail.MessageParts.I
    if (mail.MessageParts.Items[ii].ClassName = 'TIdText') then begin
      text := TIdText(mail.MessageParts.Items[ii]);
      memoText.Lines.Add(text.Body.Text);
    end;
  end;
end;
Allerdings bekomme ich so, warum auch immer, nur reine Textmails angezeigt. Das kann mir doch einfach all die html-Tags einfach niederschreiben, anstatt gar nichts zu bringen. Wieso wird der Text mancher Emails einfach nicht angezeigt?

Ich werde deine Methode gleich mal probieren.
MFG
LokutusvB
  Mit Zitat antworten Zitat
nahpets
(Gast)

n/a Beiträge
 
#4

Re: Indy TIdMessage TExt auslesen

  Alt 28. Jan 2009, 16:52
Hallo,

die beiden Routinen sollten eigentlich identischen Ergebnissen führen.
  Mit Zitat antworten Zitat
LokutusvB

Registriert seit: 18. Jul 2006
277 Beiträge
 
Delphi XE6 Enterprise
 
#5

Re: Indy TIdMessage TExt auslesen

  Alt 28. Jan 2009, 17:05
Ich bin noch basteln, aber logisch betrachtet hast du recht. Wenn in TiDText nichts steht, kann ich manipulieren wie ich will, Body.Text bleibt trotzdem leer. Wie kann ich mir denn den Text einer jeden eMail laden, egal welches Format die Mail besitzt?
MFG
LokutusvB
  Mit Zitat antworten Zitat
nahpets
(Gast)

n/a Beiträge
 
#6

Re: Indy TIdMessage TExt auslesen

  Alt 28. Jan 2009, 17:59
Hallo,

mit dem folgenden Code lese ich Email-Dateien aus 'nem Verzeichnis "am laufenden Meter" ein.
So wie das ist, wirst Du damit nichts anfangen können, aber das Wesentliche solltest Du Dir da raussuchen können.
Das Ganze sieht an einigen Stellen sehr verworren aus, nur so ist es mir gelungen, auch fehlerhafte Mails weitgehend "vernünftig" einzulesen.
Delphi-Quellcode:
procedure TfmMain.rdUCEFile(sDirectory: String; sr: TSearchRec);
var
          sl : TStringList;
          st : TMemoryStream;
          i : Integer;
          iIdx : Integer;
          sFileName : String;
          sAtDomain : String;
          sIndexName : String;
          sXSCL : String;
          bFound : Boolean;
          bCheckOk : Boolean;
          sLevel : String;
          iLevel : Integer;
          sReceived : String;
begin
  sIndexName := tbUCE.IndexName;
  tbUCE.IndexName := 'FileName';
  bFound := tbUCE.FindKey([sr.Name]);
  tbUCE.IndexName := sIndexName;
  // Mail noch nicht in der Datenbank?
  if not bFound then begin
    sl := TStringList.Create;
    st := TMemoryStream.Create;
    Try
      Try
        sFileName := sr.FindData.cFileName;
        sl.LoadFromFile(sDirectory + '\' + sFileName);
        sl.Add('');
        sl.Add('.');
        sl.Add(' ');
        sl.Add('');
        sl.Add('');
        sl.SaveToStream(st);
        st.Position := 0;
        Try
          IdMessage.LoadFromStream(st);
        except
          on e : Exception do Begin
            IDMessage.NoEncode := Not IDMessage.NoEncode;
            IDMessage.NoDecode := Not IDMessage.NoDecode;
            st.Position := 0;
            Try
              IdMessage.LoadFromStream(st);
            except
              on e : Exception do Begin
                WriteToLogFile(FormatFileName(sFileName) + ' | Verarbeitungsfehler (IdMessage.LoadFromStream(st)): ' + e.Message);
              end;
            end;
            IDMessage.NoEncode := Not IDMessage.NoEncode;
            IDMessage.NoDecode := Not IDMessage.NoDecode;
          end;
        end;
        bCheckOk := False;
        sXSCL := FormatXSCL(IdMessage.Headers.Values['X-SCL']);
        sLevel := Copy(Trim(sXSCL),1,1);
        iLevel := StrToIntDef(sLevel,0);
        // IP des absendenden Host (IP) holen.
        sReceived := GetReceived(IdMessage.Headers.Values['Received']);

        // Hier muss die Überprüfung der Mails anhand der Kriterien in den Eingabefeldern
        // geprüft werden
        // Muss die Mail zugestellt werden?
        iIdx := slZustellen.IndexOf(IdMessage.Headers.Values['X-Sender']);
        If iIdx = -1 Then begin
          sAtDomain := Copy(IdMessage.Headers.Values['X-Sender'],Pos('@',IdMessage.Headers.Values['X-Sender']),255);
          iIdx := slZustellen.IndexOf(sAtDomain);
          If iIdx = -1 Then begin
            // Hier müssen wir dann noch einen(?) Exoten abarbeiten.
            sAtDomain := 'xxx.xxx.xxx'; // <- den muss keiner wissen ;-)
            If Pos(sAtDomain,IdMessage.Headers.Values['X-Sender']) <> 0 then begin
              iIdx := slZustellen.IndexOf(sAtDomain);
            end;
          end;
        End;

        If iIdx > -1 Then Begin
          WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | zugestellt: ' + slZustellen[iIdx] + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | ' + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
          RenameFile(sDirectory + '\' + sFileName,edPickUp.Text + '\' + sFileName);
          bCheckOk := True;
        End;
        if Not bCheckOk then begin
          iIdx := slZustellen.IndexOf(IdMessage.From.Address);
          If iIdx = -1 Then begin
            sAtDomain := Copy(IdMessage.From.Address,Pos('@',IdMessage.From.Address),255);
            iIdx := slZustellen.IndexOf(sAtDomain);
          End;
          If iIdx > -1 Then begin
            WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | zugestellt: ' + slZustellen[iIdx] + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | ' + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
            RenameFile(sDirectory + '\' + sFileName,edPickUp.Text + '\' + sFileName);
            bCheckOk := True;
          end;
        end;
        if Not bCheckOk then begin
          iIdx := slZustellen.IndexOf(IdMessage.Recipients.EMailAddresses);
          If iIdx > -1 Then begin
            WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | zugestellt: ' + slZustellen[iIdx] + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | ' + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
            RenameFile(sDirectory + '\' + sFileName,edPickUp.Text + '\' + sFileName);
            bCheckOk := True;
          end;
        end;
        if Not bCheckOk then begin
          i := 0;
          iIdx := -1;
          If slZustellBetreff.Count > 0 then Repeat
            iIdx := Pos(slZustellBetreff[i],IdMessage.Subject);
            Inc(i);
          Until (i > slZustellBetreff.Count - 1) or (iIdx > 0);
          If iIdx > 0 Then begin
            WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | zugestellt: ' + slZustellBetreff[i - 1] + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | ' + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
            RenameFile(sDirectory + '\' + sFileName,edPickUp.Text + '\' + sFileName);
            bCheckOk := True;
          end;
        end;
        if not bCheckOk Then Begin
          // Haben wir einen Absender, der immer ignoriert wird?
          iIdx := slIgnore.IndexOf(IdMessage.From.Address);
          If iIdx > -1 Then Begin
            WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | gelöscht (abzulehnender Absender): ' + slIgnore[iIdx] + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | ' + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
            RenameFile(sDirectory + '\' + sFileName,edDeleted.Text + '\' + sFileName);
            bCheckOk := True;
          End;
          If Not bCheckOk Then begin
            iIdx := slIgnore.IndexOf(IdMessage.Headers.Values['X-Sender']);
            If iIdx > -1 Then Begin
              WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | gelöscht (abzulehnender Absender): ' + slIgnore[iIdx] + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | ' + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
              RenameFile(sDirectory + '\' + sFileName,edDeleted.Text + '\' + sFileName);
              bCheckOk := True;
            End;
          End;
          // Haben wir einen Betreff, der zum Löschen führt?
          If Not bCheckOk Then begin
            iIdx := slSubject.IndexOf(IdMessage.Subject);
            If iIdx > -1 Then Begin
              WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | gelöscht (Betreff): ' + IdMessage.Subject + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | ' + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
              RenameFile(sDirectory + '\' + sFileName,edDeleted.Text + '\' + sFileName);
              bCheckOk := True;
            End;
          end;
          // Gibt es irgendeine Phrase aus der Wortliste?
          If Not bCheckOk Then begin
            sl.Clear;
            sl.Add('');
            sl.Add(IdMessage.Headers.Text);
            sl.Add('');
            sl.Add(idMessage.Body.Text);
            sl.Add('');
            for i := 0 To IDMessage.MessageParts.Count - 1 Do Begin
              If IdMessage.MessageParts.Items[i] Is TIDText Then with IdMessage.MessageParts.Items[i] As TIDText do Begin
                sl.Add('');
                // ContentType: text/html
                if AnsiContainsText(IdMessage.MessageParts.Items[i].ContentType,'html') Then Begin
                  WebIndex.MaxLineSize := 80;
                  WebIndex.HTML.Text := Body.Text;
                  WebIndex.PrepareHtmlText;
                  sl.Add(AnsiReplaceText(WrapText(WebIndex.HTML.Text, 80),#13#10#13#10,#13#10));
                End else Begin
                  sl.Add(Body.Text);
                end;
              End;
            End;
            // Hier wird noch nicht der Teil mit den Message-Informationen berücksichtigt,
            // dies könnte aber wesentlich für die Erkennung von Spam mit Anhängen sein.
            // Wie z. B.: Sperrung.zip, Hinweis.zip...
            sl.Add('');
            for i := 0 To IdMessage.MessageParts.Count - 1 Do Begin
              sl.Add('');
              sl.Add('StoredPathName: ' + IdMessage.MessageParts.Items[i].StoredPathName);
              sl.Add('ContentTransfer: ' + IdMessage.MessageParts.Items[i].ContentTransfer);
              sl.Add('ContentType: ' + IdMessage.MessageParts.Items[i].ContentType);
              sl.Add('Headers.Text: ' + IdMessage.MessageParts.Items[i].Headers.Text);
            end;
            For iIdx := 0 To slWortliste.Count - 1 Do begin
              bCheckOk := AnsiContainsText(sl.Text,slWortliste[iIdx]);
              if bCheckOk Then Begin
                WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | gelöscht (Wortliste): ' + slWortliste[iIdx] + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | '  + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
                RenameFile(sDirectory + '\' + sFileName,edDeleted.Text + '\' + sFileName);
                Break;
              End;
            end;
          end;
          // Ist ein regulärer Ausdruck in der Mail zu finden?
          If Not bCheckOk Then Begin
            For iIdx := 0 To slRegular.Count - 1 Do begin
              Try
                bCheckOk := FindRegExpr(sl.Text,slRegular[iIdx]);
                if bCheckOk Then Begin
                  WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | gelöscht (regulärer Ausdruck): ' + slRegular[iIdx] + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | '  + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
                  RenameFile(sDirectory + '\' + sFileName,edDeleted.Text + '\' + sFileName);
                  Break;
                end;
              except
                on e : Exception Do begin
                  WriteToLogFile(FormatFileName(sFileName) + ' | ' + sXSCL + ' | Fehler im regulären Ausdruck: ' + slRegular[iIdx] + ' | ' + IdMessage.Subject + ' | ' + sReceived + ' | '  + IdMessage.From.Address + ' | ' + IdMessage.Headers.Values['TO']);
                end;
              end;
            end;
          end;
          If not bCheckOk then Begin
            tbUCE.Append;
            for i := 0 To tbUCE.FieldCount - 1 Do begin
              tbUCE.Fields[i].AsString := IdMessage.Headers.Values[tbUCE.Fields[i].FieldName];
            end;
            tbUCE.FieldByName('FileName').AsString := sFileName;
            tbUCE.FieldByName('From').AsString := IdMessage.From.Address;
            tbUCE.FieldByName('CountParts').AsInteger := IdMessage.MessageParts.Count;
            tbUCE.FieldByName('BODY').AsString := sl.Text;
            Case IdMessage.ReplyTo.Count Of
              0 : ;
            else
              tbUCE.FieldByName('Reply-To').AsString := IdMessage.ReplyTo[0].Address;
            End;
            tbUCE.FieldByName('FileSize').AsInteger := sr.Size;
            tbUCE.Post;
          end;
        end;
      Except
        on e : Exception Do begin
          WriteToLogFile(FormatFileName(sFileName) + ' | Verarbeitungsfehler (rdUCEFile(sDirectory: String; sr: TSearchRec)): ' + e.Message);
        end;
      End;
    Finally
      st.Free;
      sl.Free;
    end;
  end;
end;
  Mit Zitat antworten Zitat
LokutusvB

Registriert seit: 18. Jul 2006
277 Beiträge
 
Delphi XE6 Enterprise
 
#7

Re: Indy TIdMessage TExt auslesen

  Alt 29. Jan 2009, 10:07
Ich bin langsam am verzweifeln. So wie du das einliest, kann ich es leider nicht machen, da mein Delphi 5 die StrUtils noch nicht inne hat. Vielleicht ist zusätzlich meine Indy-Komponente auch zu alt.

Ich habe jetzt, da ich dachte, dass es vielleicht am De- oder Encodireren liegt, mit den beiden Klassen TIdDecoderQuotedPrintable und TIdEncoderQuotedPrintable experimentiert, mit dem Ergebnis, das nun sogar der Betreff nicht mehr gelesen und angezeigt werden kann. Kann ich der TidMessage-Komponente nicht einfach irgendwie sagen, sie soll Anzeigen ohne irgend welche Filterungen? Oder kann ich das auch irgendwie ohne TidMessage lösen ohne größeren Aufwand?
MFG
LokutusvB
  Mit Zitat antworten Zitat
nahpets
(Gast)

n/a Beiträge
 
#8

Re: Indy TIdMessage TExt auslesen

  Alt 29. Jan 2009, 11:44
Hallo,

bevor ich Dir die Fragen beantworten kann:

In welcher Form liegen Dir die EMails vor?

Als Textdatei?

Mails sind grundsätzlich erstmal reine Textdateien, Du könntest daher diese einfach in ein Memo, eine Stringliste... einlesen.

Wenn Du in dem eingelesenen Text nun nach <html suchst, bist Du (bei erfolgreicher Suche) am Anfang des HTML's. Nun liest Du ab dort solange weiter, bis Du auf </html> stößt.

Eventuell gehst Du vor der Suche her und ersetzt alle < durch Zeilenvorschub+< und alle > durch >+Zeilenvorschub, dann hast Du alle Tags in einer einzelnen Zeile und kannst dann per IndexOf nach <html> und </html> suchen.

'ne Mail sieht prinzipell etwa so aus:
Code:
x-sender: [email]absender@irgendwas.nix[/email]
x-receiver: [email]xxxx.xxx@xxxx.xxx[/email]
X-SCL: 8 92.51%
Received: from [123.123.123.123] ([123.123.123.123]) by welcher.server.xxxx.xxx with Microsoft SMTPSVC(6.0.3790.3959);
    Wed, 35 May 4711 07:44:35 +0100
To: <xxxx.xxx@xxxx.xxx>
Subject: Meds Discount for [email]xxxx.xxx@xxxx.xxx[/email]
From: <absender@irgendwas.nix>
MIME-Version: 1.0
Importance: High
Content-Type: text/html
Return-Path: [email]absender@irgendwas.nix[/email]
Message-ID: <DASISTWOHLDERSERVERstPGtPrPr00000134@welcher.server.xxxx.xxx>
X-OriginalArrivalTime: 35 May 4711 06:44:35.0894 (UTC) FILETIME=[E25DF560:01C98113]
Date: 35 May 4711 07:44:35 +0100

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body> <style>yrakzcmrhycljoiwvdwinykwxxebhiemcaau</style>
[url="http://ich.bin.ein.arger.spammer.cn/"]Your link[/url]<style>anxjjfifzasgpcqxnaaujtdltgxyskquzmrwlyuxxnaccseyypmsjdcahkivzo</style>


Your Discount code #wrrqx
<style>ekjegveqtfitxhjfxzlfopbstfzwbsnxnyvwoviedammphcnb</style>
</body>
</html>
Durch zeilenweises Lesen kannst Du an alle von Dir gewünschten Informationen kommen. Indy tut da letztlich auch nichts anderes.
  Mit Zitat antworten Zitat
LokutusvB

Registriert seit: 18. Jul 2006
277 Beiträge
 
Delphi XE6 Enterprise
 
#9

Re: Indy TIdMessage TExt auslesen

  Alt 29. Jan 2009, 11:57
Ja, die Emails liegen mir in einfacher Textdatei mit verschiedenen Endungen vor.

Mails, die z.B. nicht angezeigt werden können, besitzen den Content-Type text/plain; charset us-ascii.

Das sind doch einfachste Textmails, wieso kann TIdMessage da nichts rauslesen? Das verstehe ich nicht.

Muß ich diese immer erst, wie auch immer das gehen soll, in den Content-Type text/html wandeln dass TIdMessage etwas damit anfangen kann? Andere Emails komischerweise mit Kontent-Type text/html lassen sich problemlos öffnen. Und dann gibt es wieder Mails, mit Content-Type: text/plain; charset=iso-8859-1, und diese können nicht gelesen werden. Aber warum nicht?
MFG
LokutusvB
  Mit Zitat antworten Zitat
nahpets
(Gast)

n/a Beiträge
 
#10

Re: Indy TIdMessage TExt auslesen

  Alt 29. Jan 2009, 12:08
Hallo,

wie soll ich Deine Frage jetzt beantworten?

Vermutlich ist Jain jetzt richtig.

Mails können reiner Text sein.
Mails können HTML sein.
In den einzelnen Messageparts können unterschiedliche Typen sein, ein Text-Part und ein HTML-Part sind nicht ungewöhnlich.

Kannst Du mir per PN nochmal ein paar von Deinen Problemkandidaten schicken? Die würde ich mir dann mal anschauen, wenn's bei meinem Delphi 7 klapp, haben wir ein Versionsproblem, andernfalls ein Mailproblem.
So kommen wir glaub' ich nicht weiter. Theoretisch muss TIDMessage mit allen Mails zurechtkommen. Wenn das nicht klappt, liegt der Fehler eventuell in einer nicht standardkonformen Struktur der Mails.
An den unterschiedlichen Zeichensätze kann es (eigentlich) nicht liegen.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:16 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz