All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.krakenapps.pcap.decoder.msn.MsnDecoder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2010 NCHOVY
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.krakenapps.pcap.decoder.msn;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;

import org.krakenapps.pcap.decoder.tcp.TcpProcessor;
import org.krakenapps.pcap.decoder.tcp.TcpSession;
import org.krakenapps.pcap.decoder.tcp.TcpSessionKey;
import org.krakenapps.pcap.util.Buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author mindori
 */
public class MsnDecoder implements TcpProcessor {
	private Logger logger = LoggerFactory.getLogger(MsnDecoder.class.getName());

	private Set callbacks;
	private Map sessionMap;

	public MsnDecoder() {
		callbacks = new HashSet();
		sessionMap = new HashMap();
	}

	public void register(MsnProcessor processor) {
		callbacks.add(processor);
	}

	public void unregister(MsnProcessor processor) {
		callbacks.remove(processor);
	}

	@Override
	public void handleTx(TcpSessionKey sessionKey, Buffer data) {
		MsnSession session = sessionMap.get(sessionKey);

		byte[] t = new byte[data.readableBytes()];
		data.gets(t);
		
		Buffer txBuffer = session.getTxBuffer();
		txBuffer.addLast(t);
		
		getChatContent(session, txBuffer, t);
	}

	@Override
	public void handleRx(TcpSessionKey sessionKey, Buffer data) {
		MsnSession session = sessionMap.get(sessionKey);
		
		byte[] t = new byte[data.readableBytes()];
		data.gets(t);
		
		Buffer rxBuffer = session.getRxBuffer();
		rxBuffer.addLast(t);
		
		getChatContent(session, rxBuffer, t);
	}

	@Override
	public boolean onEstablish(TcpSession session) {
		TcpSessionKey sessionKey = session.getKey();
		logger.debug("-> Msn Session Established: {} -> {}", sessionKey.getClientPort(), sessionKey.getServerPort());
		sessionMap.put(sessionKey, new MsnSession());
		return true;
	}

	@Override
	public void onFinish(TcpSessionKey session) {
		if (logger.isDebugEnabled())
			logger.debug("-> Msn Session Closed: \n" + "Client Port: "
					+ (int) session.getClientPort() + "\nServer Port: "
					+ (int) session.getServerPort());
		sessionMap.remove(session);
	}

	@Override
	public void onReset(TcpSessionKey session) {
		MsnSession msnSession = sessionMap.get(session);
		if (msnSession != null) {
			if (logger.isDebugEnabled())
				logger.debug("Deallocate tx, rx buffer and remove Msn session.");
			msnSession.clear();
			sessionMap.remove(session);
		}
	}

	private void getChatContent(MsnSession session, Buffer buffer, byte[] data) {
		String content = new String(data);
		String[] token = content.split("\r\n");
		
		boolean isFindCommand = false;
		int tokenIndex = 0;

		while (!isFindCommand && tokenIndex < token.length) {
			if (token[tokenIndex].matches("^USR.+")) {
				// USR: normal case. ANS: called by opponent.
				String[] usrCommand = token[tokenIndex].split(";");
				String[] userAddress = usrCommand[0].split(" ");
				session.setMsnUserAddress(userAddress[userAddress.length - 1]);
				isFindCommand = true;
			}

			else if (token[tokenIndex].matches("^ANS.+") && session.getMsnUserAddress() == null) {
				String[] usrCommand = token[0].split(";");
				String[] userAddress = usrCommand[0].split(" ");
				session.setMsnUserAddress(userAddress[2]);
				isFindCommand = true;
			}

			else if (token[tokenIndex].matches("^MSG.+")) {
				// get declared msnPayload length. split method of String is
				// incomplete.
				int k = 0;
				int spaceIndex = 0;
				
				while (data[k] != 0x0d) {
					if (data[k] == 0x20)
						spaceIndex = k;
					k++;
				}
				byte[] payloadLenBytes = new byte[(k - 1) - spaceIndex];
				for (int i = 0; i < payloadLenBytes.length; i++)
					payloadLenBytes[i] = data[(spaceIndex + 1) + i];

				int msnPayloadLen = session.getMsnPayloadLength();
				if (msnPayloadLen == -1) {
					// recording declared msnPayload length
					msnPayloadLen = Integer.parseInt(new String(payloadLenBytes));
					session.setMsnPayloadLength(msnPayloadLen);
				}

				int arrivedPayloadLen = 0;
				if (!session.isGetDeclaredPayloadLength()) {
					// token[k(k is MSG Header index)].length() + 2(\r\n) is MSG
					// Header length.
					arrivedPayloadLen = data.length - (token[0].length() + 2);
					// session.setGetDeclaredPayloadLength(true);
				} else
					arrivedPayloadLen = data.length;

				int beforeArrivedPayloadLength = session.getArrivedPayloadLength();
				if (session.getMsnPayloadLength() > beforeArrivedPayloadLength + arrivedPayloadLen) {
					// declared payload length as many as real payload length.
					// recording arrived msnPayload length.
					session.setArrivedPayloadLength(beforeArrivedPayloadLength + arrivedPayloadLen);
					return;
				} else {
					int capacity = buffer.readableBytes();
					byte[] msnData = new byte[capacity];
					buffer.gets(msnData);

					int index = 0;
					int contentLength = 0;

					while (index < msnData.length) {
						if (msnData[index] == 0x0d) {
							// real content length.
							contentLength = msnData.length - (index + 2);
							break;
						}
						index++;
					}

					String msnDataStr = new String(msnData);
					String[] lines = msnDataStr.split("\r\n");
					String[] param = lines[0].split(" ");

					String fromAddr = "";
					if (param[1].matches("^[_a-z0-9-]+(.[_a-z0-9-]+)*@(?:\\w+\\.)+\\w+$"))
						fromAddr = param[1];
					else
						fromAddr = session.getMsnUserAddress();

					int declaredPayloadLength = session.getMsnPayloadLength();

					if (contentLength > declaredPayloadLength && tokenIndex == 0) {
						if (session.isTruncated()) {
							int remainDataLength = session.getRemainDataLength();
							byte[] truncatedMsnData = session.getTruncatedMsgData();
							int recordIndex = session.getMsnPayloadLength() - remainDataLength;
							int offset = 0;
							while (offset < msnData.length) {
								truncatedMsnData[offset + recordIndex] = msnData[offset];
								offset++;
							}
							Session mailSession = Session.getDefaultInstance(new Properties());
							InputStream is = new ByteArrayInputStream(truncatedMsnData, 0,
									truncatedMsnData.length);
							handlingMsnContent(fromAddr, mailSession, is);

							session.initTruncatedInstance();
							getMsgMesssage(session, msnData, offset, fromAddr);
						} else {
							getMsgMesssage(session, msnData, 0, fromAddr);
						}
					} else {
						if (session.isTruncated()) {
							int remainDataLength = session.getRemainDataLength();
							byte[] truncatedMsnData = session.getTruncatedMsgData();
							int recordIndex = session.getMsnPayloadLength() - remainDataLength;
							int offset = 0;
							while (offset < msnData.length) {
								truncatedMsnData[offset + recordIndex] = msnData[offset];
								offset++;
							}

							if (remainDataLength > msnData.length) {
								int newRemainDataLength = remainDataLength - msnData.length;
								session.setRemainDataLength(newRemainDataLength);
								session.setTruncatedMsgData(truncatedMsnData);
								return;
							} else {
								Session mailSession = Session.getDefaultInstance(new Properties());
								InputStream is = new ByteArrayInputStream(truncatedMsnData, 0,
										truncatedMsnData.length);
								handlingMsnContent(fromAddr, mailSession, is);

								session.initTruncatedInstance();
								getMsgMesssage(session, msnData, offset, fromAddr);
							}
						} else {
							Session mailSession = Session.getDefaultInstance(new Properties());
							InputStream is = new ByteArrayInputStream(msnData, 0, msnData.length);
							handlingMsnContent(fromAddr, mailSession, is);
						}
					}
				}
				isFindCommand = true;
			}
			tokenIndex++;
		}
		session.clear();
	}
	
	private void getMsgMesssage(MsnSession session, byte[] msnData, int msnDataStartPoint,
			String fromAddr) {
		Session mailSession = Session.getDefaultInstance(new Properties());
		InputStream is;

		int lengthIndex = getLengthIndex(msnData, msnDataStartPoint);
		int declaredPayloadLength;
		if (session.isGetDeclaredPayloadLength()) {
			declaredPayloadLength = session.getDeclaredPayloadLength();
		} else {
			declaredPayloadLength = getDeclaredLength(msnData, lengthIndex);
			session.setDeclaredPayloadLength(declaredPayloadLength);
			session.setGetDeclaredPayloadLength(true);
		}

		while (true) {
			// read range: MSG header of MSN Data + declaredPayloadLength.
			while (msnData[lengthIndex] != 0x0d)
				lengthIndex++;

			int readLength = ((lengthIndex + 2) - msnDataStartPoint) + declaredPayloadLength;
			if (readLength > msnData.length - msnDataStartPoint) {
				// remainDataLength is remain length of truncated data.
				// so, cannot call chat callback method until next packet(valid
				// sequence) arrived.
				// and record bytes in MsnSession.
				int remainDataLength = readLength - (msnData.length - msnDataStartPoint);
				byte[] truncatedMsnData = new byte[readLength];

				int offset = 0;
				while (offset < (msnData.length - msnDataStartPoint)) {
					truncatedMsnData[offset] = msnData[msnDataStartPoint + offset];
					offset++;
				}
				session.setRemainDataLength(remainDataLength);
				session.setTruncatedMsgData(truncatedMsnData);
				return;
			}
			byte[] oneOfMsnData = new byte[readLength];

			int offset = 0;
			while (offset < readLength) {
				oneOfMsnData[offset] = msnData[msnDataStartPoint + offset];
				offset++;
			}

			is = new ByteArrayInputStream(oneOfMsnData, 0, oneOfMsnData.length);
			handlingMsnContent(fromAddr, mailSession, is);
			msnDataStartPoint = msnDataStartPoint + readLength;

			// If start point is EOB(end of buffer), then escape loop.
			if (msnDataStartPoint >= msnData.length - 1)
				break;
			else {
				// Is remain data compose the one of MSG Message?
				lengthIndex = getLengthIndex(msnData, msnDataStartPoint);
				declaredPayloadLength = session.getDeclaredPayloadLength();
				if (declaredPayloadLength > (msnData.length - msnDataStartPoint))
					return;
			}
		}
	}

	private int getLengthIndex(byte[] msnData, int startPoint) {
		int numOfSpace = 0;
		for (int i = startPoint; i < msnData.length; i++) {
			if (numOfSpace == 3) {
				return i;
			} else if (msnData[i] == 0x20)
				numOfSpace++;
		}
		return -1;
	}

	private int getDeclaredLength(byte[] msnData, int lengthIndex) {
		int declaredLength = -1;
		int tempIndex = lengthIndex;

		while (msnData[tempIndex] != 0x0d)
			tempIndex++;

		byte[] declaredLengthBytes = new byte[tempIndex - lengthIndex];
		for (int j = 0; j < declaredLengthBytes.length; j++)
			declaredLengthBytes[j] = msnData[lengthIndex + j];
		try {
			declaredLength = Integer.parseInt(new String(declaredLengthBytes));
		} catch (NumberFormatException e) {
			e.printStackTrace();
		}
		return declaredLength;
	}

	private void handlingMsnContent(String fromAddr, Session mailSession, InputStream is) {
		try {
			MimeMessage msg = new MimeMessage(mailSession, is);
			if (msg.getHeader("X-MMS-IM-Format") != null) {
				dispatchChat(fromAddr, (String) msg.getContent());
			}
		} catch (MessagingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void dispatchChat(String fromAddr, String chatContent) {
		for (MsnProcessor processor : callbacks) {
			processor.onChat(fromAddr, chatContent);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy