# JavaMail: HTML Code einer Mail



## Danko (3. Jan 2012)

Hallo, 

ich bin grade dabei einen kleinen E-Mail Clienten zu schreiben, alles klappt soweit, nur möchte ich eine Message in einem JTextPanel anzeigen, dieses unterstützt, soweit ich weiß, HTML Code und kann diesen rendern. Im Moment benutze ich einen InputStream um den Mail-Content zu lesen, aber selbst wenn der Content-Type "text/html" ist wird bloßer Text angezeigt.


```
int tableRow = mailTable.rowAtPoint(e.getPoint());
Part messagePart = clickedMail[tableRow];
                
if (clickedMail[tableRow].getContent() instanceof Multipart) {
    messagePart = ((Multipart) clickedMail[tableRow].getContent()).getBodyPart(0);
}
                
String contentType = messagePart.getContentType();
String messageLine = new String();
                
StringBuilder message = new StringBuilder();
                
if (contentType.startsWith("text/plain") || contentType.startsWith("text/html")) {
    InputStream inputStream = messagePart.getInputStream();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                    
    while ((messageLine = bufferedReader.readLine()) != null) {
        message.append(messageLine);
        message.append(System.getProperty("line.separator"));
    }
}
                
mailWindow.htmlPane.setText(message.toString());
```

Gibt es mit JavaMail eine Möglichkeit den Content einer E-Mail mit zugehörigen HTML Tags zu lesen, oder mach ich hier schon irgendetwas falsch?


----------



## irgendjemand (3. Jan 2012)

an sich sieht der code richtig aus *wobei das mit dem [c]append(System.LineSep)[/c] overkill ist ... hier reicht [c]append("\n")[/c] bei allen systemen aus ...

was ich mir vorstellen könnte ist das der inhalt der e-mail wirklich text/plain ist ... also einfach keinen HTML-code enthält ...

das lesen der daten selbst stimmt soweit ... lasse diese mal zur sicherheit auf der console ausgeben *oder einfach DEBUG=TRUE setzen* ...
sollte da kein HTML auftauchen ist in der mail auch KEIN HTML drin ... das hat mit JavaMail nichts zu tun sondern damit das die mail dann nicht als HTML versendet wird ...


----------



## Danko (3. Jan 2012)

Habe mich mal in meinen E-Mail Account eingeloggt und einige Mails aufgerufen:


```
ContentType: text/plain; charset="UTF-8"
ContentType: text/plain; charset = "iso-8859-1"
ContentType: text/plain; charset=ascii
ContentType: text/plain; charset="UTF-8"
ContentType: text/plain; charset="ISO-8859-1"
ContentType: text/plain; charset=utf-8
ContentType: text/plain; charset="ISO-8859-1"
ContentType: text/plain; charset="ISO-8859-1"
ContentType: text/plain; charset="ISO-8859-1"
```

Darunter auch einige E-Mails von Facebook (Plain, UTF-8), ich verstehe aber dann nicht, wie das T-Online Mail Center in der Lage ist, die E-Mail so abzubilden.

Habe mal zum Beispiel eine Spam-Mail rausgenommen.
JavaMail: http://i.imgur.com/0h2iZ.png
E-Mail Center: http://i.imgur.com/Aw5ts.png


----------



## irgendjemand (3. Jan 2012)

es ist doch egal welcher content type ... viel wichtiger ist der "source" der mail ... also ob da wirklich [c]<html></html>[/c] drum rum ist oder eben nicht ...


----------



## musiKk (3. Jan 2012)

So egal ist der Content-Type nicht.

Emails, die sowohl plain text als auch HTML enthalten, sind als Content-Type multitype/alternative enkodiert. Dabei wird die Email in verschiedene Teile unterteilt, getrennt durch ein boundary, welches ebenfalls im Content-Type header steht. Diese parts enthalten selbst auch wieder Content-Type header, mit denen man zwischen den verschiedenen Darstellungen unterscheiden kann.


----------



## irgendjemand (3. Jan 2012)

hab mal eben meinen hals in die doc gehangen ...

kann es vllt sein das JTextPane vllt das falsche ist ? versuch es erstmal mit nem JEditorPane ... weil nur weil JTextPane von JEditorPane erbt heißt das nicht das auch automatisch HTML aktiv ist ... bzw das das gleiche Document verwendet wird ...


----------



## Danko (3. Jan 2012)

Und wie könnte man erreichen, dass man aus den Parts einer Multipart-Mail eine zusammenhängende Seite anzeigt? Ich habe schon probiert die Parts auseinander zu nehmen und den Text wieder zusammenzusetzen, was aber auch keinen Erfolg zeigt.

// Edit: Mit einem JEditPane wird der selbe Plain-Text ausgegeben.


----------



## GUI-Programmer (3. Jan 2012)

Danko hat gesagt.:


> Und wie könnte man erreichen, dass man aus den Parts einer Multipart-Mail eine zusammenhängende Seite anzeigt? Ich habe schon probiert die Parts auseinander zu nehmen und den Text wieder zusammenzusetzen, was aber auch keinen Erfolg zeigt.
> 
> // Edit: Mit einem JEditPane wird der selbe Plain-Text ausgegeben.



Genau die selbe Frage hat mich auch schon beschäftigt. Mein Thema dazu ist dieses hier: Java Mail - Multipart anzeigen Und mein Programm kannst du hier runterladen.

Was ich vorweg bereits sagen kann:
Bei meinen Programm hab ich es wieder so geändert, dass nur Plain-Text angezeigt wird, da dies ja funtktioniert hat. Zuvor hatte ich die "\n" Zeilenumbrüche einfach durch "</br>" Zeilenumbrüche ersetzt und den ContentType der JEditorPane auf "text/html" gesetzt. Dabei kamen allerding dann Exceptions. Wichtig für dich wird wohl die Klasse MailTool sein.

Und mal vorweg: Hiermit will ich nicht extra für mein Programm "Werbung machen" oder gar "damit angeben" (dafür ist es mir noch zu schlecht) sondern lediglich dem TO helfen.



mfg GUI-Programmer


----------



## GUI-Programmer (3. Jan 2012)

Die Exceptions traten auf, da mehrere HTML Bodies zu einem zusammengefasst wurden, und der HTML Text nicht mehr dem "HTML - Regeln" entsprach, und somit nicht mehr HTML war. Als Grund hierfür vermute ich, dass manche HTML Parts von den Mails mit dem "!DOCTYPE html PUBLIC..." beginnen und manche nicht. Mails von Amazon z.B. wurden perfekt dargestellt, da nur HTML und nur eine Sorte von HTML und kein Plain-Text.


----------



## GUI-Programmer (4. Jan 2012)

Habe es nun gelöst!!! Es lag nicht etwa daran, dass manche HTML Parts von den Mails mit dem "!DOCTYPE html PUBLIC..." beginnen und manche nicht, oder dass ich nicht in der Lage gewäsen wäre Plaintext mit einzubinden. Ich habe ganz einfach mal um dem gesamten Text, der in die JEditorPane kommt ein weiteres "<html> </html>" Tag gesetzt und es funktioniert!!! Unglaublich, dass es an so einem banalen Ding gelegen hat. (Ich habe mir bereits den Kopf zerbrochen und lange bei Google gesucht, wie man mehere HTMLs zu einer basteln kann).

Hier nun die MailTool Klasse für die Nachwelt (inbegriffen dem TO):

```
import java.io.File;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.ContentType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

/**
 * Klasse MailTool
 * 
 * @author GUI-Programmer
 * @version 04.01.2012
 */
public class MailTool {
	// Attribute
	private String username, password;
	private Session session1;
	private Transport transport;
	private Store store;
	private Message[] message;
	private ArrayList<DataHandler> datahandlers;
	private ArrayList<Integer> bodyPartSizes;
	private Folder folder;
	private boolean showDialog;
	private int mailNumber;
	private String separator;
	
	// Konstruktor
	public MailTool() {
		username = null;
		password = null;
		session1 = null;
		transport = null;
		store = null;
		datahandlers = new ArrayList<DataHandler>();
		bodyPartSizes = new ArrayList<Integer>();
		showDialog = false;
		mailNumber = -1;
		separator = System.getProperty("file.separator");
	}
	
	// Methoden
	/**
	 * Es wird versucht eine Verbindung zu Googlemail aufzubauen und sich
	 * dort mit den Benutzernamen und den Passwort einzuloggen.
	 * @param username
	 * @param password
	 * @throws Exception
	 */
	public void login(final String username, final String password) throws Exception {
		this.username = username;
		this.password = password;
		
		Properties props1=new Properties();
		props1.put("mail.smtp.port", "587");
        props1.put("mail.smtp.auth", "true");
        props1.put("mail.smtp.starttls.enable", "true");
        session1 = Session.getInstance(props1);
        transport = session1.getTransport("smtp");
		transport.connect("smtp.googlemail.com", username, password);
		
		Properties props2 = System.getProperties();
		props2.setProperty("mail.store.protocol", "imaps");
		props2.setProperty("mail.imaps.partialfetch", "false");
    	props2.setProperty("mail.imap.host", "imap.gmail.com");
    	props2.setProperty("mail.imap.port", "993");
    	props2.setProperty("mail.imap.connectiontimeout", "5000");
    	props2.setProperty("mail.imap.timeout", "5000");
    	Session session2 = Session.getDefaultInstance(props2, null);
    	store = session2.getStore("imaps");
    	store.connect("imap.gmail.com", username, password);
    	folder = store.getFolder("INBOX");
    	folder.open(Folder.READ_WRITE);
	}
	
	/**
	 * Versucht eine E-Mail an die angegebene Addresse/-n zu senden.
	 * @param recipientAddresses
	 * @param subject
	 * @param mailText
	 * @throws Exception
	 */
	public void sendMail(String recipientAddresses, String subject, String mailText, ArrayList<File> filelist) throws Exception {
		login(username, password);
    	Address[] addresses = InternetAddress.parse(recipientAddresses);
		Message message = new MimeMessage(session1);
        message.setFrom(new InternetAddress(username));
        message.setRecipients(Message.RecipientType.TO, addresses);
        message.setSubject(subject);
        // create the message part 
        Multipart multipart = new MimeMultipart();
        MimeBodyPart textBodyPart = new MimeBodyPart();
        textBodyPart.setText(mailText);
        multipart.addBodyPart(textBodyPart);
        for(int i=0; i<filelist.size(); i++) {
        	MimeBodyPart attachementBodyPart = new MimeBodyPart();
            DataSource source = new FileDataSource(filelist.get(i));
            attachementBodyPart.setDataHandler(new DataHandler(source));
            attachementBodyPart.setFileName(filelist.get(i).getName());
            multipart.addBodyPart(attachementBodyPart);
        }
        message.setContent(multipart);
        
        transport.sendMessage(message, addresses);
        transport.close();
	}
	
	public void deleteMail(int number) throws Exception {
		message = folder.getMessages();
		int index = message.length - number-1;
		message[index].setFlag(Flags.Flag.DELETED, true);
		folder.close(true);
		folder.open(Folder.READ_WRITE);
	}
	
	/**
	 * Die Anhänge der ausgewählten E-Mail werden ermittelt und als Strings zurückgegeben.
	 * @param number
	 * @return
	 * @throws Exception
	 */
	public ArrayList<String> getAttachments(int number) throws Exception {
		final ArrayList<String> attachments = new ArrayList<String>();
		datahandlers.removeAll(datahandlers);
		bodyPartSizes.removeAll(bodyPartSizes);
		
		login(username, password);
		message = folder.getMessages();
		int index = message.length - number;
		
		if(message[index].getContentType().toString().startsWith("multipart")) {
			Multipart content = (Multipart) message[index].getContent();
			for(int i=0; i<content.getCount(); i++) {
				BodyPart part = (BodyPart) content.getBodyPart(i);
				String fileName = part.getFileName();
				if(isAttachement(part.getContentType(), fileName)) {
					fileName = encordeAttachement(fileName);
					attachments.add(fileName);
					
					datahandlers.add(part.getDataHandler());
					bodyPartSizes.add(part.getSize());
				}
			}
		}
		return attachments;
	}
	
	private boolean isAttachement(String contentType, String fileName) {
		boolean isAttachement = false;
		if(!contentType.startsWith("text/html")
		&& !contentType.startsWith("text/rtf")
		&& !contentType.startsWith("text/plain")
		&& fileName != null) {
			isAttachement = true;
		}
		return isAttachement;
	}
	
	private String encordeAttachement(String s) throws UnsupportedEncodingException {
		String temp = MimeUtility.decodeText(s);
		String decoded = Normalizer.normalize(temp, Normalizer.Form.NFC);
		return decoded;
	}
	
	/**
	 * Der Text der ausgewählten E-Mail wird ermittelt und als String zurückgegeben.
	 * @param number
	 * @return
	 * @throws Exception
	 */
	public String readMail(int number) throws Exception {
		mailNumber = number;
		String text = null;
		
		message = folder.getMessages();
		int index = message.length - number;
		ContentType contentType = new ContentType(message[index].getContentType());
		String type = contentType.getPrimaryType()+"/"+contentType.getSubType();
		if(type.equals("TEXT/PLAIN")) {
			text = message[index].getContent().toString().replaceAll("\n", "<br />");
		} else if(type.equals("TEXT/HTML")) {
			text = message[index].getContent().toString();
		} else if(contentType.toString().startsWith("multipart")) {
			text = getMultiPartText(message[index].getContent());
		}
		
		text = "<html>"+text+"</html>";
		
		System.out.println(type.toString());
		
		return text;
	}
	
	/**
	 * Behandelt den Inhalt einer E-Mail als multipart-Typ und versucht
	 * ihn als String zu konvertieren.
	 * @param m
	 * @return
	 * @throws Exception
	 */
	private String getMultiPartText(Object content) throws Exception {
		String text = null;
		StringBuffer buffer = new StringBuffer();
		Multipart multipart = (Multipart) content;
	     for(int i=0; i<multipart.getCount(); i++) {
	      BodyPart part = multipart.getBodyPart( i );
	      ContentType ct = new ContentType(part.getContentType());
	      String type = ct.getPrimaryType()+"/"+ct.getSubType();
	      if(type.equals("TEXT/PLAIN")) {
	    	  buffer.append(part.getContent().toString().replaceAll("\n", "<br />"));
	      } else if(type.equals("TEXT/HTML")) {
	    	  buffer.append(part.getContent().toString());
	      } else if(type.startsWith("multipart")) {
	    	  buffer.append(getMultiPartText(part.getContent()));
	      }
	      System.out.println(type.toString());
	      text = buffer.toString();
	     }
		return text;
	}
	
	/**
	 * Gibt den Benutzernamen das Nutzers zurück.
	 * @return
	 */
	public String getUsername() {
		String user;
		if(username.endsWith("@googlemail.com")) {
			user = username.replaceAll("@googlemail.com", "");
		} else {
			user = username;
		}
		return user;
	}
	
	/**
	 * Liefert 2 String-Arrays. Das 1. enthält die Namen aller Absender
	 * der E-Mails, das 2. die dazugehörigen Betreffe.
	 * @return
	 */
	public String[][] getMails(final JProgressBar progress) {
		progress.setValue(0);
		String[][] mails = null;
		try {
			message = folder.getMessages();
			mails = new String[message.length][message.length];
			final String[] from = new String[message.length];
			final String[] subject = new String[message.length];
			progress.setMaximum(message.length);
			
			for (int i=message.length-1; i>0; i--) {
				try {
					int temp = message.length-i;
					Address[] froms = message[i].getFrom();
					String email = froms == null ? null : ((InternetAddress) froms[0]).getAddress();
					from[temp] = email;
					subject[temp] = message[i].getSubject();
					if(subject[temp] == null) subject[temp] = "<Kein Betreff>";
					progress.setValue(temp);
				} catch(Exception ignore) { }
			}
			progress.setValue(progress.getMaximum());
			
			mails = new String[][] {from, subject};
		} catch(Exception ex) {
			ex.printStackTrace();
		}
		return mails;
	}
	
	public void downloadFile(final String filename, final int attachmentNumber, final Downloads dialogDownloads, final File directory) {
		dialogDownloads.setTitle("Aktiver Download");
		dialogDownloads.setVisible(true);
		dialogDownloads.lblFileName.setText(filename+" wird heruntergeladen...");
		dialogDownloads.prgrsbr.setValue(0);
		
		final SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
			@Override
			protected Void doInBackground() throws Exception {
				showDialog = true;
				
				Thread.sleep(200);
				dialogDownloads.prgrsbr.setIndeterminate(true);
				
				File file = new File(directory.getAbsolutePath()+separator+filename);
				if(!file.exists()) file.createNewFile();
				
				FileOutputStream fos = new FileOutputStream(file);
				datahandlers.get(attachmentNumber).writeTo(fos);
				fos.close();
				return null;
			}
			
			@Override
			public void done() {
				showDialog = false;
				dialogDownloads.prgrsbr.setIndeterminate(false);
				dialogDownloads.prgrsbr.setValue(100);
				dialogDownloads.setTitle("Download abgeschlossen");
				
				Thread t = new Thread() {
					boolean run = true;
					public void run() {
						while(run) {
							try {
								sleep(3000);
							}
							catch(InterruptedException ex) {
								ex.printStackTrace();
							}
							run = false;
							dialogDownloads.setVisible(showDialog);
						}
					}
				};
				t.start();
			}
		};
		worker.execute();
	}
	
	public void downloadAllFiles(final Downloads dialogDownloads, final File directory) {
		System.out.println(directory.getAbsolutePath());
		dialogDownloads.setTitle("Aktiver Download");
		dialogDownloads.prgrsbr.setValue(0);
		dialogDownloads.setVisible(true);
		SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
			private ArrayList<String> fileList = null;
			@Override
			protected Void doInBackground() throws Exception {
				Thread.sleep(200);
				dialogDownloads.prgrsbr.setIndeterminate(true);
				
				Thread t1 = new Thread(new Runnable() {
					public void run() {
						try { fileList = getAttachments(mailNumber); }
						catch(Exception ex) { ex.printStackTrace(); }
					}
				}); t1.start(); t1.join();
				
				for(int i=0; i<fileList.size(); i++) {
					dialogDownloads.lblFileName.setText(fileList.get(i)+" wird heruntergeladen...");
					final int k = i;
					Thread t2 = new Thread(new Runnable() {
						public void run() {
							try {
								File file = new File(directory.getAbsolutePath()+separator+fileList.get(k));
								if(!file.exists()) file.createNewFile();
								
								FileOutputStream fos = new FileOutputStream(file);
								datahandlers.get(k).writeTo(fos);
								fos.close();
							}
							catch(Exception ex) { ex.printStackTrace(); }
						}
					}); t2.start(); t2.join();
					dialogDownloads.prgrsbr.setIndeterminate(false);
					dialogDownloads.prgrsbr.setValue(100);
					Thread.sleep(100);
				}
				return null;
			}
			
			@Override
			protected void done() {
				dialogDownloads.setTitle("Downloads abgeschlossen");
				Thread t3 = new Thread(new Runnable() {
					public void run() {
						try {
							Thread.sleep(3000);
							dialogDownloads.setVisible(false);
						}
						catch(InterruptedException ex) { ex.printStackTrace(); }
					}
				}); t3.start();
			}
		};
		worker.execute();
	}
	
	public boolean logout() {
		boolean logout = false;
		Thread t = new Thread(new Runnable() {
			public void run() {
				try {
					store.close();
				}
				catch(MessagingException ex) {
					ex.printStackTrace();
				}
			}
		});
		t.start();
		try {
			t.join();
			logout = true;
		} catch(InterruptedException ex) {
			ex.printStackTrace();
		}
		return logout;
	}
	
}
```

Allerdings muss ich auch gleich dazu sagen, dass nicht alles dargestellt, bzw. schön dargestellt wird. Beispielsweise XML oder teilweise stimmt das Layout der HTMLs nicht ganz. Dennoch wird nun HTML angezeigt.


----------

