Emails aus einem Postfach abrufen ... wieder mal mit SQL!
Emails aus der Datenbank heraus zu versenden, ist keine allzu kompilizierte Aufgabe - es wird tagtäglich mit Hilfe der bekannten PL/SQL-Pakete wie UTL_SMTP oder UTL_MAIL gemacht. Eine interessante Aufgabe wäre dagegen das Abrufen von Emails aus einem Postfach. Wozu kann sowas gut sein - man könnte bspw. Prozesse durch Emails antriggern - die Datenbank holt die Mails ab und starten dann PL/SQL-Logik ...
Doch wie macht man das? Schließlich gibt es kein PL/SQL-Pakete zum Abrufen von Emails. Die Lösung lautet Java in der Datenbank. Die Java Mail API ist in der Java Welt sehr bekannt und kann nicht nur Mails senden, sondern auch aus Postfächern (POP3 und IMAP) abrufen. Also schreiben wir eine Java Stored Procedure: Diese verbindet sich auf den Mailserver, ruft die Mails ab und gibt Sie als Array zurück. Das Array wird dann auf ein PL/SQL VARRAY abgebildet, so dass die Java Stored Procedure wie eine Table Function verwendet werden kann. Doch genug der Vorrede - hier kommt der Code (dieses Codebeispiel ist nur mit IMAP-Mailservern getestet - eim Umschreiben auf POP3-Postfäche sollte aber nicht so kompliziert sein)
set define off
drop procedure get_mail_headers
/
drop type mail_header_ct
/
drop type mail_header_t
/
create or replace type mail_header_t as object(
subject varchar2(4000),
sender varchar2(100),
sender_email varchar2(100),
sent_date date,
deleted char(1),
read char(1),
recent char(1),
answered char(1)
)
/
create or replace type mail_header_ct as table of mail_header_t
/
create or replace java source named "GetMailHeaders" as
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
import java.util.Vector;
import javax.mail.Folder;
import javax.mail.FetchProfile;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Flags;
import javax.mail.internet.InternetAddress;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;
class getMails {
public static ARRAY getMails(String host, int port, String user, String pass, int maxHeaders)
throws Exception {
Connection con = DriverManager.getConnection("jdbc:default:connection:");
ArrayDescriptor aDescr = ArrayDescriptor.createDescriptor("MAIL_HEADER_CT", con);
StructDescriptor rDescr = StructDescriptor.createDescriptor("MAIL_HEADER_T", con);
Object[] mailHeader = new Object[8];
STRUCT oraMail = null;
Vector vMails = new Vector();
Properties props = new Properties();
props.setProperty("mail.store.protocol", "imap");
Session s = Session.getDefaultInstance(props);
Store store = s.getStore();
store.connect(host, port, user, pass);
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);
Message message[] = folder.getMessages();
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(FetchProfile.Item.FLAGS);
folder.fetch(message, fp);
String sPersonal = null;
for (int i=message.length-1; i>=0 && ((i>=(message.length - maxHeaders)) || maxHeaders == -1);i--) {
mailHeader[0] = new String(message[i].getSubject());
sPersonal = ((InternetAddress)(message[i].getFrom()[0])).getPersonal();
if (sPersonal == null) {
mailHeader[1] = new String("");
} else {
mailHeader[1] = new String(sPersonal);
}
mailHeader[2] = new String(((InternetAddress)(message[i].getFrom()[0])).getAddress());
mailHeader[3] = new java.sql.Timestamp(message[i].getSentDate().getTime());
mailHeader[4] = (message[i].isSet(Flags.Flag.DELETED)?"Y":"N");
mailHeader[5] = (message[i].isSet(Flags.Flag.SEEN)?"Y":"N");
mailHeader[6] = (message[i].isSet(Flags.Flag.RECENT)?"Y":"N");
mailHeader[7] = (message[i].isSet(Flags.Flag.ANSWERED)?"Y":"N");
oraMail = new STRUCT(rDescr, con, mailHeader);
vMails.add(oraMail);
}
folder.close(false);
store.close();
return new ARRAY(aDescr, con, vMails.toArray());
}
}
/
alter java source "GetMailHeaders" compile
/
sho err
Hier ist der PL/SQL Wrapper, welcher die Java-Klasse auf eine PL/SQL Table Function abbildet.
create or replace function get_mail_headers( p_mailhost in varchar2, p_mailport in number, p_username in varchar2, p_password in varchar2, p_max_mails in number ) return MAIL_HEADER_CT is language java name 'getMails.getMails( java.lang.String, int, java.lang.String, java.lang.String, int ) return oracle.sql.ARRAY'; /
Die Funktion GET_MAIL_HEADERS hat folgende Parameter:
- p_mailhost: Der Mailserver-Hostname oder die IP-Adresse
- p_mailport: Der TCP/IP-Port des Mailservers (normalerweise 143 für IMAP-Postfächer)
- p_username: Username zum Login in den Mailserver
- p_password: Passwort zum Login in den Mailserver
- p_max_mails: Maximal abzurufende Mails, "-1" ruft alle ab.
begin dbms_java.grant_permission( grantee => 'SCOTT', permission_type => 'SYS:java.net.SocketPermission', permission_name => 'mailserver.mydomain.com', permission_action => 'connect,resolve' ); end; / commit /
Ausprobieren ist dann ganz einfach ...
select sender, subject from table( get_mail_headers( 'mailserver.domain.com', 143, 'carsten.czarski', 'ganzgeheim', -1 ) ) / SENDER SUBJECT -------------------------------------- ---------------------------------------- Max.Muster@maildomain.com Hallo Carsten : :
Und was bringt das? Sowie die Mail-Header als (virtuelle) Tabelle mit SQL abrufbar sind, kann man jedes beliebige Web-Framework nutzen, um sie als Tabelle auf eine Webseite zu bringen - Ein Beispiel für die Darstellung mit Application Express findet sich in der Application Express-Community - einfach mal reinschauen.
Kommentare:
Wie schon auf der DOAG gesagt, sehr nett!
Lg
Patrick
Hi Patrick,
Thanks for posting this on your blogsite. I will really appreciate if you please send me the english verison of this code to achieve this. I have tried to translate your this page (via google) but did not work. I will really appreciate if you please send or make this available in Eng version also. Many thanks & regards, Kavin
love9k@sify.com
I got
java.lang.ClassCastException
at oracle.jdbc.driver.PhysicalConnection.putDescriptor(PhysicalConnection.java:3374)
at oracle.sql.ArrayDescriptor.createDescriptor(ArrayDescriptor.java:114)
at getMails.getMails(GetMailHeaders:31)
the error occurs in the ArrayDescriptor aDescr = ArrayDescriptor.createDescriptor("MAIL_HEADER_CT", con); line.
How can I solve that error?
hmm ... which database version did you test on ...?
I tested on 10.2 as well as on 11.1 ans it compiles and runs without problems.
Best regards
-Carsten
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bits
I tried it in a clean instance and it worked!
Thanks!
great code!
I am suer I can find this info but maybe you already have it. How can I change a message to "read" status after I "read" it?
the java class "Message" contains a method for this - so you'd have again to code this in java and to "wrap" the java method as a PL/SQL stored procedure.
I think it would be a good idea to have some kind of DBMS_MAILCLIENT package which uses the java API to expose more functionality to PL/SQL - but coding this requires some time ... and as always: time is a rare good ...
Best regards
-Carsten
Kommentar veröffentlichen