본문으로 바로가기

필자는 전자우편 파일을 자동하는 도구를 만들면서 전자우편을 Plain Object로 만들어야 했습니다. 본 글에서는 EML 파일을 분석하여 전자우편 객체와 첨부파일 객체로 파싱 하는 내용을 설명합니다.

 

관련 라이브러리

  • mail.jar : 전자우편을 파싱하기 위한 라이브러리
  • Jsoup.jar : text/html을 평문으로 만들기 위한 라이브러리

 

데이터 저장 객체 선언

전자우편 객체

전자우편의 대부분의 필드를 저장합니다. 악성코드를 탐지하기 위해서 전자우편 작성 언어 메일 문서 형식과 포함된 첨부파일 및 연결 주소를 추가적으로 저장합니다.

	public class MailMessage {
		private String subject = null;
		private String from = null;
		private List<String> to = null;
		private List<String> cc = null;
		private List<String> bcc = null;
		private Date sentDate = null;
		private Date receivedDate;
		
		private String content = null;
		private String contentType = null;
		private String contentCharset = null;
		private String mailLanguage = null;
		private boolean hasLink = false;
		private Map<String, Integer> links = null;
		
		private Map<String, List<String>> headers = null;
		private List<MailAttachment> attachments = null;

		public MailMessage() {
			this.headers = new HashMap<String, List<String>>();
		}
		public MailMessage(String subject) {
			this();
			this.subject = subject;
		}
		
		public String getSubject() {
			return subject;
		}
		public void setSubject(String subject) {
			this.subject = subject;
		}
		public String getContent() {
			return content;
		}
		
		public void setContentType(String contentType) {
			this.contentType  = contentType;
			if(this.mailLanguage == null || "".equals(this.mailLanguage) && this.contentType.toLowerCase().indexOf("charset=") >= 0) {

				String regex = "charset=\"?([-a-zA-Z0-9+]*)\"?";

				Pattern p = Pattern.compile(regex);
				Matcher m = p.matcher(this.contentType);
				while (m.find()) {
//					if(result.indexOf(m.group()) < 0) result.add(m.group());
					this.mailLanguage = m.group(1);
				}
			}
		}
		
		public String getContentType() {
			return this.contentType;
		}
		
		public String getContentCharset() {
			return contentCharset;
		}
		public void setContentCharset(String contentCharset) {
			this.contentCharset = contentCharset;
		}
		public String getMailLanguage() {
			return mailLanguage;
		}
		public void setMailLanguage(String mailLanguage) {
			this.mailLanguage = mailLanguage;
		}

		public Date getSentDate() {
			return sentDate;
		}
		public void setSentDate(Date sentDate) {
			this.sentDate = sentDate;
		}

		public Date getReceivedDate() {
			return receivedDate;
		}
		public void setReceivedDate(Date receivedDate) {
			this.receivedDate = receivedDate;
		}
		
		public ArrayList<String> extractUrls(String content) {
			ArrayList<String> result = new ArrayList<String>();

			String regex = "(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";

			Pattern p = Pattern.compile(regex);
			Matcher m = p.matcher(content);
			while (m.find()) {
//				if(result.indexOf(m.group()) < 0) result.add(m.group());
				result.add(m.group());
			}

			return result;
		}
		
		public void setContent(String content) {
			this.content = content;
			
			if(content != null) {
				List<String> urls = extractUrls(content);
				this.links = new HashMap<String, Integer>();
				if(urls != null && urls.size() > 0) {
					this.hasLink = true;
					for(String url : urls) {
						if(this.links.containsKey(url)) {
							Integer i = this.links.get(url);
							this.links.put(url, new Integer(i + 1));
						} else {
							this.links.put(url, new Integer(1));
						}
					}
				}
			}
		}
		
		public Map<String, List<String>> getHeaders() {
			return headers;
		}
		public List<MailAttachment> getAttachments() {
			return attachments;
		}

		public String getFrom() {
			return from;
		}
		public void setFrom(String from) {
			this.from = from;
		}
		public List<String> getTo() {
			return to;
		}
		public List<String> getCc() {
			return cc;
		}
		public List<String> getBcc() {
			return bcc;
		}
		
		public void addTo(String addr) {
			if(this.to == null) this.to = new ArrayList<String>();
			this.to.add(addr);
		}
		public void addCc(String addr) {
			if(this.cc == null) this.cc = new ArrayList<String>();
			this.cc.add(addr);
		}
		public void addBcc(String addr) {
			if(this.bcc == null) this.bcc = new ArrayList<String>();
			this.bcc.add(addr);
		}
		
		public void addHeader(String key, String value) {
			List<String> values = this.headers.get(key);
			if(values == null) values = new ArrayList<String>();
			
			values.add(value);
			this.headers.put(key, values);
		}
		
		public void addAttachment(MailAttachment attm) {
			if(this.attachments == null) attachments = new ArrayList<MailAttachment>();
			this.attachments.add(attm);
		}
		
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("메일 제목 : ").append(this.subject).append(System.lineSeparator());
			builder.append("보낸 사람 : ").append(this.from).append(System.lineSeparator());
			
			if(this.to != null && this.to.size() > 0) {
				builder.append("받는 사람 : ");
				boolean first = true;
				for(String addr: this.to) {
					if(!first) builder.append(", ");
					builder.append(addr);
					
					first = false;
				}
				builder.append(System.lineSeparator());
			}
			
			if(this.cc != null && this.cc.size() > 0) {
				builder.append("참조 : ");
				boolean first = true;
				for(String addr: this.cc) {
					if(!first) builder.append(", ");
					builder.append(addr);
					
					first = false;
				}
				builder.append(System.lineSeparator());
			}
			
			if(this.bcc != null && this.bcc.size() > 0) {
				builder.append("숨은 참조 : ");
				boolean first = true;
				for(String addr: this.bcc) {
					if(!first) builder.append(", ");
					builder.append(addr);
					
					first = false;
				}
				builder.append(System.lineSeparator());
			}
			
			if(this.sentDate != null)		builder.append("보낸 시각 : ").append(this.sentDate).append(System.lineSeparator());
			if(this.receivedDate != null)	builder.append("받은 시각 : ").append(this.receivedDate).append(System.lineSeparator());
			
			if(this.headers != null && this.headers.size() > 0) {
				builder.append("메일 헤더 : ");
				builder.append(System.lineSeparator());

				List<String> list = new ArrayList(this.headers.keySet());
				Collections.sort(list);
				for(String key : list) {
					builder.append("\t" + key + " : ");
					
					List<String> values = this.headers.get(key);
					if(values.size() > 1) {
						builder.append(System.lineSeparator());
						boolean first = true;
						for(String value: values) {
							builder.append("\t\t");
							builder.append(value);
							builder.append(System.lineSeparator());
							
							first = false;
						}
					} else {
						builder.append(values.get(0));
						builder.append(System.lineSeparator());
					}
				}
			}

			if(this.mailLanguage != null) {
				builder.append("메일 언어 : ").append(this.mailLanguage).append(System.lineSeparator());
			}
			
			if(this.content != null) {
				String content = this.content;
				if(contentType != null && (
						contentType.toLowerCase().indexOf("text/html") >= 0
						|| contentType.toLowerCase().indexOf("application/rtf") >= 0
					)
				) {
					// https://stackoverflow.com/questions/3607965/how-to-convert-html-text-to-plain-text
//					String plainText= Jsoup.parse((String) obj).text();
//					System.out.println(plainText);
					
					content = br2nl(content);
				}
				if(content.length() > 500) {
					content = content.substring(0, 500) + System.lineSeparator() + "......이하 생략......";
				}
				
	//			content.replace("\r\n", "\r\n\t");
				content = content.replace("\n", "\n\t");
				builder.append("메일 형태 : ").append(contentType).append(System.lineSeparator());
				builder.append("메일 내용 :").append(System.lineSeparator());
				builder.append("\t").append(content).append(System.lineSeparator());
				
				if(this.hasLink) {
//					builder.append("링크 포함 여부 : 예").append(System.lineSeparator());
					builder.append("링크 목록 :").append(System.lineSeparator());

					List<String> list = new ArrayList(this.links.keySet());
					Collections.sort(list);
					for(String key : list) {
						Integer count = this.links.get(key);
						builder.append("\t").append(key);
						if(count > 1) {
							builder.append(" (").append(count).append("회").append(")");
						}
						builder.append(System.lineSeparator());
					}
				}
			}
			

			if(this.attachments != null && this.attachments.size() > 0) {
				builder.append("첨부 파일 : ");
				builder.append(System.lineSeparator());
				for(MailAttachment attm : this.attachments) {
					String attfile = attm.toString().replace("\n", "\n\t");
					builder.append("\t").append(attfile).append(System.lineSeparator());
					
				}
				
			}
			
			return builder.toString();
		}
	}

 

첨부파일 객체

첨부파일 객체는 파일이름과 크기 MD5, SHA1, SHA2 해쉬를 저장합니다. 또한 추후 파일의 속성을 파악하기 위해 첨부파일 관련 헤더 파일을 저장합니다.

	public class MailAttachment {
		private String filename = null;
		private long size = 0;
		private String md5 = null;
		private String sha1 = null;
		private String sha2 = null;
		
		private Map<String, List<String>> headers = null;

		public MailAttachment() {
			this.headers = new HashMap<String, List<String>>();
		}
		public MailAttachment(String filename) {
			this();
			this.filename = filename;
		}

		public void setSize(long size) {
			this.size = size;
		}
		
		public void setMd5(String md5) {
			this.md5 = md5;
		}
		
		public String getFilename() {
			return filename;
		}

		public long getSize() {
			return size;
		}

		public String getMd5() {
			return md5;
		}
		
		public String getSha1() {
			return sha1;
		}
		public void setSha1(String sha1) {
			this.sha1 = sha1;
		}
		public String getSha2() {
			return sha2;
		}
		public void setSha2(String sha2) {
			this.sha2 = sha2;
		}
		public Map<String, List<String>> getHeaders() {
			return headers;
		}

		public void addHeader(String key, String value) {
			List<String> values = this.headers.get(key);
			if(values == null) values = new ArrayList<String>();
			
			values.add(value);
			this.headers.put(key, values);
		}
		
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("파일 이름 : ").append(this.filename).append(System.lineSeparator());
			builder.append("파일 크기 : ").append(this.size).append(System.lineSeparator());
			builder.append("파일 해쉬(MD5) : ").append(this.md5).append(System.lineSeparator());
			builder.append("파일 해쉬(SHA1) : ").append(this.sha1).append(System.lineSeparator());
			builder.append("파일 해쉬(SHA2) : ").append(this.sha2).append(System.lineSeparator());
			if(this.headers != null && this.headers.size() > 0) {
				builder.append("첨부파일 헤더 : ");
				builder.append(System.lineSeparator());

				List<String> list = new ArrayList(this.headers.keySet());
				Collections.sort(list);
				for(String key : list) {
					builder.append("\t" + key + " : ");
					
					List<String> values = this.headers.get(key);
					if(values.size() > 1) {
						builder.append(System.lineSeparator());
						boolean first = true;
						for(String value: values) {
							builder.append("\t\t");
							builder.append(value);
							builder.append(System.lineSeparator());
							
							first = false;
						}
					} else {
						builder.append(values.get(0));
						builder.append(System.lineSeparator());
					}
				}
			}
			
			return builder.toString();
		}
		
	}
	

 

전자우편 파싱하기

파싱 과정에서 text/html, text/rtf 형식으로 된 전자우편 형식의 경우에는 Plain Text로 변환하는 로직을 추가하였습니다.

package eml.parser;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Header;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.internet.InternetHeaders;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.swing.JEditorPane;
import javax.swing.text.EditorKit;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Whitelist;

import com.sun.mail.util.BASE64DecoderStream;

@SuppressWarnings({ "unchecked", "unused" })
public class MailParser {
	/**
	 * @param args
	 * @throws MessagingException
	 * @throws IOException
	 */
	public static void main(String[] args) throws MessagingException, IOException {
		File dir = new File("email");
		if(dir.isDirectory()) {
			FilenameFilter filter = new FilenameFilter() {
				@Override
				public boolean accept(File dir, String name) {
					return name.endsWith(".bin");
				}
			};

			File[] files = dir.listFiles(filter);
			for(File file : files) {
				System.out.println("=========================== " + file.getName() + " ===========================");
				
				MailMessage message = new MailParser().parse(file.getPath());
				System.out.println(message.toString());
				
				System.out.println();
				System.out.println();
				System.out.println();
			}
		}
		
	}
	
	private static String removeAllWhiteSpace(String str){
		StringTokenizer st = new StringTokenizer(str.trim());
		StringBuilder sb = new StringBuilder();
		while (st.hasMoreTokens()) {
			sb.append(st.nextToken());
		}
		return sb.toString();
	}


	
	private MailMessage parse(String filePath) throws MessagingException, IOException {
		MailMessage message = new MailMessage();
		
		Properties props = System.getProperties();
		props.put("mail.host", "used.mail.host.name");
		props.put("mail.transports.protocol", "smtp");

		Session mailSession = Session.getDefaultInstance(props, null);
		InputStream source = new FileInputStream(filePath);
		MimeMessage mmessage = new MimeMessage(mailSession, source);

//		System.out.println("Subject : " + mmessage.getSubject());
		message.setSubject(mmessage.getSubject());
		
		try {
			Address[] fromAddr = mmessage.getFrom();
	//		System.out.print("From : ");
			for (Address address : fromAddr) {
				String addr = MimeUtility.decodeText(removeAllWhiteSpace(address.toString()));
//				System.out.println(addr);
				message.setFrom(addr);
			}
		} catch(Exception e) {
//			e.printStackTrace();

			String[] fromAddr = mmessage.getHeader("From");
			if(fromAddr != null && fromAddr.length> 0) {
//				System.out.println("언어 : " + fromAddr[0]);
				for (String address : fromAddr) {
					String addr = MimeUtility.decodeText(removeAllWhiteSpace(address));
					message.setFrom(addr);
				}
			}
			return message;
		}
			
//			message.getContentType()
		Address[] toAddr = mmessage.getRecipients(RecipientType.TO);
		if(toAddr != null && toAddr.length > 0) {
//			System.out.print("To : ");
			for (Address address : toAddr) {
				try {
					String addr = MimeUtility.decodeText(removeAllWhiteSpace(address.toString()));
//					System.out.println(addr);
					message.addTo(addr);
				} catch(Exception e) {
					e.printStackTrace();
				}
			}
		}
		
		Address[] ccAddr = mmessage.getRecipients(RecipientType.CC);
		if(ccAddr != null && ccAddr.length > 0) {
//			System.out.print("CC : ");
			for (Address address : ccAddr) {
				try {
					String addr = MimeUtility.decodeText(removeAllWhiteSpace(address.toString()));
//					System.out.println(addr);
					message.addCc(addr);
				} catch(Exception e) {
					e.printStackTrace();
				}
			}
		}
		
		Address[] bccAddr = mmessage.getRecipients(RecipientType.BCC);
		if(bccAddr != null && bccAddr.length > 0) {
//			System.out.print("BCC : ");
			for (Address address : bccAddr) {
				try {
					String addr = MimeUtility.decodeText(removeAllWhiteSpace(address.toString()));
//					System.out.println(addr);
					message.addBcc(addr);
				} catch(Exception e) {
					e.printStackTrace();
				}
			}
		}

//		printMessageAllHeader(message);
		Enumeration<Header> headers = mmessage.getAllHeaders();
		while (headers.hasMoreElements()) {
			Header header = headers.nextElement();
			try {
				String value = MimeUtility.decodeText(removeAllWhiteSpace(header.getValue()));
				message.addHeader(header.getName(), value);
			} catch (Exception e) {
				message.addHeader(header.getName(), header.getValue());
			}
		}

		String[] contentLanguages = mmessage.getHeader("Content-Language");
		if(contentLanguages != null && contentLanguages.length > 0) {
//			System.out.println("언어 : " + contentLanguages[0]);
			message.setMailLanguage(contentLanguages[0]);
		}

		String[] mailDate = mmessage.getHeader("Date");
		if(mailDate != null && mailDate.length > 0) {
			message.setSentDate(mmessage.getSentDate());
			message.setReceivedDate(mmessage.getReceivedDate());
		}
		
		Object objContent = mmessage.getContent();
		if (objContent instanceof Multipart) {
			parseMultipart(message, (Multipart) objContent);
		} else {
//			System.out.println(objContent);
			message.setContent((String) objContent);
		}
		

		return message;
	}
	
	private void parseMultipart(MailMessage message, Multipart mp) throws IOException, MessagingException {
		MimeMultipart mm = (MimeMultipart) mp;

		int bodyCount = mm.getCount();
//		System.out.println("Multipart Count : " + mm.getCount());
		for (int i = 0; i < bodyCount; i++) {
			BodyPart bp = mm.getBodyPart(i);

//			System.out.println("Headers :");
//			printAllHeaders(bp.getAllHeaders());
			
//			System.out.println("Body :");
			parseBody(message, bp);
		}
		
	}
	
	
	public static String toHTML(File file) throws Exception {
		JEditorPane p = new JEditorPane();
		p.setContentType("text/rtf");

		EditorKit kitRtf = p.getEditorKitForContentType("text/rtf");
		kitRtf.read(new FileReader(file), p.getDocument(), 0);
		kitRtf = null;

		EditorKit kitHtml = p.getEditorKitForContentType("text/html");
		Writer writer = new StringWriter();
		kitHtml.write(writer, p.getDocument(), 0, p.getDocument().getLength());

		return writer.toString();
	}
	
	private static String hexEncode(byte[] bytes) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < bytes.length; i++) {
			sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
		}
		
		return sb.toString();
		
	}

	public static String[] makeFileHashes(String filename) throws Exception {
		InputStream fis = new FileInputStream(filename);

		byte[] buffer = new byte[1024];
		MessageDigest md5 = MessageDigest.getInstance("MD5");
		MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
		MessageDigest sha2 = MessageDigest.getInstance("SHA-256");
		int numRead = -1;

		do {
			numRead = fis.read(buffer);
			if (numRead > 0) {
				md5.update(buffer, 0, numRead);
				sha1.update(buffer, 0, numRead);
				sha2.update(buffer, 0, numRead);
			}
		} while (numRead != -1);

		fis.close();
		
		return new String[] {
				hexEncode(md5.digest()),
				hexEncode(sha1.digest()),
				hexEncode(sha2.digest())
		};
	}

	private void parseBody(MailMessage message, BodyPart bp) throws IOException, MessagingException {
		Object obj = bp.getContent();
		String contentType = bp.getContentType();
		
		if (obj instanceof BASE64DecoderStream) {
//			String[] aaa = bp.getHeader("Content-Disposition");
//			if(aaa != null && aaa.length > 0) {
//				System.out.println("\tContent-Disposition : " + aaa[0]);
//			}
			
			BASE64DecoderStream newObj = (BASE64DecoderStream) obj;
			
			String originfilename = bp.getFileName();
//			System.out.println("FileName : " + originfilename);
			if(originfilename == null || "".equals(originfilename)) { 
				originfilename = "unknown.attachments";
			}
			

			String filename = MimeUtility.decodeText(originfilename);
			File attachFile = new File("attachment" + File.separator + filename);
			FileOutputStream fos = new FileOutputStream(attachFile);
			byte[] buffer = new byte[1024];
			int read = 0;
			while ((read = newObj.read(buffer)) != -1) {
				fos.write(buffer, 0, read);
			}

			newObj.close();
			fos.close();
			
			
			if(contentType.indexOf("application/rtf") >= 0) {
				try {
					String html = toHTML(attachFile);
					message.setContentType(contentType);
					message.setContent(html);
					
					attachFile.delete();
					
//					System.out.println("Rtf => Html :" + html);
				} catch(Exception e) {
				}
			} else {
				long size = attachFile.length();
				
//				System.out.println("File Size :" + size);
				MailAttachment attm = new MailAttachment(filename);
				attm.setSize(size);
				try {
					String[] hashes = makeFileHashes(attachFile.getPath());
					attm.setMd5(hashes[0]);
					attm.setSha1(hashes[1]);
					attm.setSha2(hashes[2]);
				} catch(Exception e) {
					
				}

//				printAllHeaders(bp.getAllHeaders());
				Enumeration<Header> headers = bp.getAllHeaders();
				while (headers.hasMoreElements()) {
					Header header = headers.nextElement();
					try {
						String value = MimeUtility.decodeText(removeAllWhiteSpace(header.getValue()));
						attm.addHeader(header.getName(), value);
					} catch (Exception e) {
						attm.addHeader(header.getName(), header.getValue());
					}
				}
				
				message.addAttachment(attm);
				
				attachFile.delete();
		
//				System.out.println("\tFilename : " + filename);
			}
			
			
			
		} else if(obj instanceof Multipart) {
			parseMultipart(message, (Multipart) obj);
		} else {
//			System.out.println("\tBody Type : " + bp.getContentType());
			message.setContentType(contentType);
			message.setContent((String) obj);
		}
	}
	
	private static String br2nl(String html) {
		if (html == null)
			return html;
		
		Document document = Jsoup.parse(html);
		//makes html() preserve linebreaks and spacing
		document.outputSettings(new Document.OutputSettings().prettyPrint(false));
		document.select("br").append("\\n");
//		document.select("p").prepend("\\n\\n");
		document.select("p").prepend("\\n");
		return document.text().replaceAll("\\\\n", "\n");
//		String s = document.html().replaceAll("\\\\n", "\n");
//		return Jsoup.clean(s, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false));
	}

	private static void printAllHeaders(Enumeration<Header> enumHeaders) throws MessagingException {
//		Enumeration<Header> enumHeaders = bp.getAllHeaders();

		int index = 1;
		while (enumHeaders.hasMoreElements()) {
			Header aa = enumHeaders.nextElement();

			try {
				System.out.println("\t" + index + ". " + aa.getName() + " : " + MimeUtility.decodeText(aa.getValue()));
			} catch (Exception e) {
				System.out.println("\t" + index + ". "  + aa.getName() + " : " + aa.getValue());
			}
			index++;
		}
	}

}

 

출력된 값 예시

아래와 같이 메일 정보가 출력됩니다. 전자우편의 상세정보 및 첨부파일, 연결 주소 정보도 같이 출력됩니다.

========================== eff09767507dd41710d9c7806a7c9682.bin ===========================
메일 제목 : AW: wire confirmation
보낸 사람 : "MENUEL,Karine"<kmenu@kidilizgroup.com>
받는 사람 : kmenu@kidilizgroup.com
보낸 시각 : Wed Oct 21 14:42:24 KST 2020
메일 헤더 : 
	Content-Type : multipart/mixed;boundary="----=_NextPart_000_0076_01C2A9A6.421423E6"
	Date : Tue,20Oct202022:42:24-0700
	Delivered-To : banned-quarantine
	From : "MENUEL,Karine"<kmenu@kidilizgroup.com>
	MIME-Version : 1.0
	Message-ID : <4C15202F851E4E59B47AD133EEEE3306.MAI@home>
	Received : 
		fromunknownbylocalhost(amavisd-new,unixsocket)id4mLpTkgoGkflfor<isavu@electroputere.ro>;Thu,22Oct202020:13:41+0300(EEST)
		fromGUEST.home(unknown[45.63.67.54])byspin.electroputere.ro(amavisd-milter)withESMTPSid09MHDVUc018555;Thu,22Oct202005:00:04+0300(envelope-from<kmenu@kidilizgroup.com>)
		fromUser([23.92.220.201])byhomewithMailEnableESMTPA;Wed,21Oct202002:43:18-0300
	Reply-To : <kmenu@kidilizgroup.com>
	Return-Path : <>
	Subject : AW:wireconfirmation
	To : kmenu@kidilizgroup.com
	X-Amavis-Alert : BANNED,messagecontains.exe,.exe-ms,Wire.exe
	X-Envelope-From : <kmenu@kidilizgroup.com>
	X-Envelope-To : <isavu@electroputere.ro>
	X-Envelope-To-Blocked : <isavu@electroputere.ro>
	X-MSMail-Priority : Normal
	X-Mailer : MicrosoftOutlookExpress6.00.2600.0000
	X-MimeOLE : ProducedByMicrosoftMimeOLEV6.00.2600.0000
	X-Priority : 3
	X-Quarantine-ID : <4mLpTkgoGkfl>
	X-Spam-Flag : NO
	X-Spam-Level : 
	X-Spam-Score : 0
	X-Spam-Status : No,score=xtag=xtag2=xkill=xtests=[]autolearn=unavailable
메일 언어 : Windows-1251
메일 형태 : text/html;
	charset="Windows-1251"
메일 내용 :
	
	Dear Sir, 
	 
	Hope you are fine? 
	 
	Attached is the Payment updated sheet of All Open POs ,Pls kindly check. 
	Pls kindly see the wire confirmation of the shippment for LOT#6288 1*40HQ .thanks. 
	 
	Best regards 
	 
	 
	 
	 Karine MENUEL/Head of licences 18 Rue Emile COUE 
	10000 TROYES-FRANCE +33 (0)3 25 42 53 91 | +33 (0)6 07 09 81 31 kmenul@kidilizgroup.com kidilizgroup.com
링크 목록 :
	http://www.kidilizgroup.com/?utm_source=signature_kg
	https://ci5.googleusercontent.com/proxy/lRib70HBR-upD_d69XB4aQ8Nc0gK-nKXqFnxOyRm44SyVWsucWN3FK5LOOHaV2DDU2r_uGW6Q2MwD6gzBxd0uJ4UZScITmFYCZyKH-pINw=s0-d-e1-ft#http://www.kidilizgroup.com/signature/images/logo-ico-fb.jpg
	https://ci6.googleusercontent.com/proxy/c9F7KmSX20gZuyX3kJWtWQAeaIPX3OPYnVmSSNbp2Ds_CjYHQwy0mcqlOZ2tL-DVK6AM0OrzPUySIIBe5NWYNaRUH6EyoC0j_H6PU_dK=s0-d-e1-ft#https://www.kidilizgroup.com/signature/images/logo-anim.gif
	https://www.facebook.com/kidilizgroup/
	https://www.google.com/url?q=http://www.kidilizgroup.com/?utm_source%3Dsignature_kg&amp;source=gmail&amp;ust=1589336665669000&amp;usg=AFQjCNFzJGx4zAC__TwIiMcs4ccEN_4Xjg
	https://www.google.com/url?q=https://www.facebook.com/kidilizgroup/&amp;source=gmail&amp;ust=1589336665669000&amp;usg=AFQjCNFY34DjlJNyJvWM5wv06t0a_ylk4w
첨부 파일 : 
	파일 이름 : Wire.rar
	파일 크기 : 206245
	파일 해쉬(MD5) : 70ab3a298edc963db01c4dd24a077a73
	파일 해쉬(SHA1) : 1ffdb110ed22f6897a390e5551a4560a2e92b4ef
	파일 해쉬(SHA2) : 1ab26e8d8331d1b03920c2e48c513b237f7dcd72f5c41aae686da5266c99cff0
	첨부파일 헤더 : 
		Content-Disposition : attachment;filename="Wire.rar"
		Content-Transfer-Encoding : base64
		Content-Type : application/octet-stream;name="Wire.rar"
	

 

 

 

참고자료

[Email] Java로 .eml 파일 읽기

 

 

MailParser.java
0.02MB

 

728x90