⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mmdecoder.java

📁 用于开发mms应用的Java库
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Tambur MMS library.
 *
 * The Initial Developer of the Original Code is FlyerOne Ltd.
 * Portions created by the Initial Developer are Copyright (C) 2005
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * 	Anders Lindh <alindh@flyerone.com>
 *
 * ***** END LICENSE BLOCK ***** */

package net.tambur.mms;

import java.io.ByteArrayInputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.ListIterator;
import java.util.Locale;
import java.util.SimpleTimeZone;

import org.apache.log4j.Logger;

/**
 * This class creates MMMessages from various formats (e.g. encapsulated, string, etc.)
 * 
 * @author Anders Lindh
 * @copyright Copyright FlyerOne Ltd 2005
 * @version $Revision: 1.1.1.1 $ $Date: 2005/04/14 09:04:10 $
 */
public class MMDecoder {

	/**
	 * logger 
	 */
	protected transient static Logger log = Logger.getLogger(MMDecoder.class);
		
	/**
	 * Decode message, i.e. build message from a byte array (decapsulate)
	 * 
	 * @param buf A byte[] containing a binary representation of the message
	 * @return whether successfull
	 */
	public static MMMessage decode(byte[] buf) throws MMDecodingException {
		ByteArrayInputStream in = new ByteArrayInputStream(buf);
		
		MMMessage res = new MMMessage();
		
		log.debug("Decoding message");
		
		try {
			// first X-Mms-Message-Type, X-Mms-Transaction-Id and X-Mms-Version (in this order)
			// these codes can be found in [MMSEncapsulation]
		
			// decode well-known headers
			while (decodeMessageHeader(res, in, null, null)) {};
			in.read();

			int cnt = 1;
			while (in.available() != 0) { // process any existing parts
				int hlen = decodeUintvar(in); // length of headers + content-type
				int dlen = decodeUintvar(in); // length of body								
	
				if ((hlen == 0) && (dlen == 0)) continue;

				log.debug("\tDecoding part: " + cnt++);
				log.debug("\t\tHeaders: " + hlen + ", content: " + dlen);
			
				if (hlen < 0 || hlen > 1024 * 5) { 	// max 5kb of headers				
					log.debug("Invalid size for headers: " + hlen);
					break;
				}

				String contenttype = null;
				byte[] headers = new byte[hlen];
				in.read(headers);

				ByteArrayInputStream his = new ByteArrayInputStream(headers);
				if (hlen != 0) contenttype = decodeContentType(his);
				if (contenttype == null) {
					log.error("Unknown contenttype for part; aborting");
					break;
				}
				log.debug("\t\tContent-type: " + contenttype);
			
				byte[] data = new byte[dlen];
				if (dlen > 0) in.read(data);
						
				ArrayList hNames = new ArrayList();
				ArrayList hValues = new ArrayList();
				while (decodeWSPHeader(his, hNames, hValues)) {}; // decode any additional headers
			
				res.addPart(contenttype, data, false, hNames, hValues);
				
				if (dlen == 0) break;
			}
						
		} catch (MMDecodingException e) {
			throw e;
		} catch (Exception ee) {
			ee.printStackTrace();
			throw new MMDecodingException(ee.toString());
		}
		
		return res;
	}

	/**
	 * Create the message from a String containing a valid MIME multipart encoded
	 * mms message
	 * 
	 * @throws MMDecodingException
	 */
	public static MMMessage fromString(String s) throws MMDecodingException {
		MMMessage msg = new MMMessage();
		
		MimeMessage mime = new MimeMessage(s);		
		msg.setContentType("application/vnd.wap.mms-message");
		msg.setVersion(MMConstants.MMS_VERSION_1_0);
		
		// encode headers
		ListIterator i = mime.getAttributes();
		while (i.hasNext()) {
			String name = (String) i.next();
			String value = (String) mime.getAttribute(name);

			// check if this is a well-known header (i.e. it should be mapped into some field)
			
			int c = 0;
			for (c = 0; c < MMConstants.knownMMSHeaders.length; c++) 
				if (((String)MMConstants.knownMMSHeaders[c][0]).equalsIgnoreCase(name)) break;
			
			//	check what type of field we're talking about
			switch (c) {							
				case 0:	// bcc				 
				 	msg.addBccAddress(value); break;						
				case 1:		// cc
					msg.addCcAddress(value); break;			
				case 2:		// x-mms-content-location
					msg.setContentLocation(value); break;			
				case 3:		// content-type					
					msg.setContentType(value); break;
				case 4:		// date
					msg.setDate(dateFromString(value));	break;
				case 5:		// x-mms-delivery-report					
					msg.setDeliveryReport(Boolean.valueOf(value));
					break;
				case 6:		// x-mms-delivery-time
					msg.setDeliveryTime(dateFromString(value)); break;
				case 7:		// x-mms-expiry
					msg.setExpiry(dateFromString(value)); break;
				case 8:		// from
					msg.setFrom(value); break;
				case 9:		// x-mms-message-class
					 msg.setMessageClass(getMessageClass(value));					 
					 break;				
				case 10:		// message-id
					 msg.setMessageId(value); break;				
				case 11:		// x-mms-message-type
					 msg.setMessageType(getMessageType(value)); break;				
				case 12:		// x-mms-version
					 //msg.setVersion(decodeInt(in));
					 //value = versionToString(msg.getVersion());
					 break;				
				case 13:		// message-size
					 msg.setMessageSize(Long.valueOf(value).longValue()); break;										
				case 14:		// x-mms-priority
					 msg.setPriority(getPriority(value)); break;						
				 case 15:		// x-mms-read-reply
				 	 msg.setReadReply(Boolean.valueOf(value)); break;						
				 case 16:		// x-mms-report-allowed
					 msg.setReportAllowed(Boolean.valueOf(value));
					 break;				
				 case 17:		// x-mms-response-status
					 msg.setResponseStatus(getResponseStatus(value)); break;				
				 case 18:		// x-mms-response-text
					 msg.setResponseText(value); break;				
				 case 19:		// x-mms-sender-visibility
					 msg.setSenderVisibility(Boolean.valueOf(value));
					 break;				
				 case 20:		// x-mms-status
					 msg.setStatus(getMessageStatus(value));
					 break;				
				 case 21:		// subject
					 msg.setSubject(value); break;				
				 case 22:		// to
					 msg.addToAddress(value); break;				
				 case 23:		// x-mms-transaction-id
					 msg.setTransactionId(value); break;				
				 default:		// unknown field, store textual representation
					 //throw new MMDecodingException("Invalid (unhandled) encoded header entry: " + String.valueOf(i));
					// skip all headers that start with a "X-"
					//if (!name.startsWith("X-"))
					//	msg.setAttribute(name, value);								
			 }
		}

		// add any parts
		if (!mime.parts.isEmpty()) {
			i = mime.parts.listIterator();
			while (i.hasNext())
				msg.addPart((MimeMessage.MimePart) i.next());
		}			
		
		return msg;
	} 

	/**
	 * Decode message headers (MMS), i.e. the ones that are represented in this class.
	 * if headers table isn't null, the headers added to the table also
	 *
	 * @return true if there still are any headers left, false otherwise
	 */
	protected static boolean decodeMessageHeader(MMMessage msg, ByteArrayInputStream in, ArrayList headerNames, ArrayList headerValues) throws Exception {
		int i = in.read();
		if (i == -1) return false; // end of stream

		if (i <= 30) 
			return false;

		if (i == 0x20) { // some weird bug
			while (i < 128) i = in.read();
		} 
		
		// string based content
		if ((i > 31) && (i < 128)) {
			String name = decodeString(in);
			String value = decodeString(in);
			if ((name != null) && (value != null)) {
				name = (char) i + name;
				//debug(DEBUG_ENABLED, "			o: " + name + ": " + value);

				if ((headerNames != null) && (headerValues != null)) 
					setAttribute(name, value, headerNames, headerValues);
			}

			return true;		
		}
			
		if ((i & 0xFF) > 128) i &= 127;
		i--; // our arrays are zero-based, compensate for this
				
		if (i > MMConstants.knownMMSHeaders.length) throw new MMDecodingException("Invalid encoded header entry: " + String.valueOf(i));
		
		String name = null;
		String value = null;
		Boolean b = null;
		
		name = (String) MMConstants.knownMMSHeaders[i][0];	
		
		// check what type of field we're talking about
		switch (i) {							
			case 0:		// bcc
				value = decodeString(in);
				msg.addBccAddress(value);
				break;						
			case 1:		// cc
				value = decodeString(in);
				msg.addCcAddress(value);
				break;			
			case 2:		// x-mms-content-location
				value = decodeString(in);
				msg.setContentLocation(value);
				break;			
			case 3:		// content-type
				value = decodeContentType(in);
				msg.setContentType(value);			// content type is always last				
				break;
			case 4:		// date
				msg.setDate(decodeDate(in));
				value = msg.getDateStr();
				break;
			case 5:		// x-mms-delivery-report
				b = decodeBoolean(in); 
				msg.setDeliveryReport(b);
				value = b.toString();
				break;
			case 6:		// x-mms-delivery-time
				Object[] tmp = decodeDateVariable(in);
				msg.setDeliveryTime((Date) tmp[0]);
				msg.setDeliveryTimeAbsolute(((Boolean) tmp[1]).booleanValue()); 
				value = msg.getDeliveryTimeStr();
				break;
			case 7:		// x-mms-expiry
				tmp = decodeDateVariable(in);
				msg.setExpiry((Date) tmp[0]);
				msg.setExpiryAbsolute(((Boolean) tmp[1]).booleanValue()); 
				value = msg.getExpiryStr();
				break;
			case 8:		// from
				msg.setFrom(decodeFrom(in));
				value = msg.getFrom();
				break;
			case 9:		// x-mms-message-class
				msg.setMessageClass(decodeInt(in));
				if (msg.getMessageClass() < MMConstants.MESSAGE_CLASSES.length) value = MMConstants.MESSAGE_CLASSES[msg.getMessageClass()];
				break;				
			case 10:		// message-id
				value = decodeString(in);
				msg.setMessageId(value);
				break;				
			case 11:		// x-mms-message-type
				msg.setMessageType(decodeInt(in));
				if (msg.getMessageType() < MMConstants.MESSAGE_TYPES.length) value = MMConstants.MESSAGE_TYPES[msg.getMessageType()];
				break;				
			case 12:		// x-mms-version
				msg.setVersion(decodeInt(in));
				value = versionToString(msg.getVersion());
				break;				
			case 13:		// message-size
				msg.setMessageSize(decodeLong(in));
				value = String.valueOf(msg.getMessageSize());
				break;										
			case 14:		// x-mms-priority
				msg.setPriority(decodeInt(in));
				if (msg.getPriority() < MMConstants.PRIORITIES.length) value = MMConstants.PRIORITIES[msg.getPriority()];				
				break;						
			case 15:		// x-mms-read-reply
				b = decodeBoolean(in); 			
				msg.setReadReply(b);
				value = b.toString();
				break;						
			case 16:		// x-mms-report-allowed
				b = decodeBoolean(in); 			
				msg.setReportAllowed(b);
				value = b.toString();
				break;				
			case 17:		// x-mms-response-status
				msg.setResponseStatus(decodeInt(in));
				if (msg.getResponseStatus() < MMConstants.RESPONSE_STATUSES.length) value = MMConstants.RESPONSE_STATUSES[msg.getResponseStatus()];				
				break;				
			case 18:		// x-mms-response-text
				value = decodeString(in);
				msg.setResponseText(value);
				break;				
			case 19:		// x-mms-sender-visibility
				b = decodeBoolean(in); 						
				msg.setSenderVisibility(b);
				value = b.toString();
				break;				
			case 20:		// x-mms-status
				msg.setStatus(decodeInt(in));
				if (msg.getStatus() < MMConstants.STATUSES.length) value = MMConstants.STATUSES[msg.getStatus()];				
				break;				
			case 21:		// subject
				value = decodeString(in);
				msg.setSubject(value);
				break;				
			case 22:		// to
				value = decodeString(in);
				msg.addToAddress(value);
				break;				
			case 23:		// x-mms-transaction-id
				value = decodeString(in);
				msg.setTransactionId(value);
				break;				
			default:		// unknown field, store textual representation
				throw new MMDecodingException("Invalid (unhandled) encoded header entry: " + String.valueOf(i));								
		}
		
		// don't add content-type
		if ((i != 3) && (headerNames != null) && (headerValues != null) && (value != null)) {
			setAttribute(name, value, headerNames, headerValues);
		}

		// debug(DEBUG_ENABLED, "			o: " + name + ": " + value);
		
		if (name.equalsIgnoreCase("Content-type")) {			
			return false; // content-type is last
		}  
		
		return true;	
	}	
	
	/**
	 * Decode headers, and add them to given hashtable
	 * 
	 * @return true if there still are any headers left, false otherwise
	 */
	protected static boolean decodeWSPHeader(ByteArrayInputStream in, ArrayList names, ArrayList values) throws Exception {
		String name = null;
		String value = null;
	
		if (in.available() == 0) return false; // end-of-stream
	
		int i = in.read();
	
		if (i < 128) { // string
			if (i != 127) name = (char) i + decodeString(in); else
			  name = decodeString(in);
			value = decodeString(in);
			  
		} else {	
			if ((i & 0xFF) > 128) i &= 127;
				
			if (i > MMConstants.knownWSPHeaders.length) throw new MMDecodingException("Invalid WSP header entry: " + String.valueOf(i));
			
			name = (String) MMConstants.knownWSPHeaders[i][0];	
			int type = ((Integer) MMConstants.knownWSPHeaders[i][1]).intValue();
			
			value = decodeToken(in, type);			
		}

		// debug(DEBUG_ENABLED, "			o: " + name + ": " + value);

		setAttribute(name, value, names, values);

		return true;	
	}

	/**
	 * Decode well-known parameters, return as String
	 * 
	 * @return true if there still are any headers left, false otherwise
	 */
	protected static String decodeParameters(ByteArrayInputStream in) throws Exception {
		String res = "";
		byte[] buf = new byte[1];
		
		while (in.available() != 0) {
			in.read(buf);
			int i = (int) (buf[0] & 0xFF);
	  			
			if (i < 128) { // string
				res += decodeString(in);
				continue;
			} else i &= 127; // short form
				
			if (i > MMConstants.WELLKNOWN_PARAMETERS.length) continue; // invalid headers entry			
			String name = (String) MMConstants.WELLKNOWN_PARAMETERS[i][0];	
			int type = ((Integer) MMConstants.WELLKNOWN_PARAMETERS[i][1]).intValue();
						
			String value = decodeToken(in, type);
			res += "; " + name + "=\"" + value + "\"";		
		}		

		return res;
	}
	
	/**
	 * decode next token from stream
	 */
	protected static String decodeToken(ByteArrayInputStream in, int type) throws Exception {
		String value = null;
		// check what type of field we're talking about
		switch (type) {				
			case 0:		// TYPE_STRING
				value = decodeString(in);
				break;
			case 1:		// TYPE_SHORTINT
				value = String.valueOf(decodeInt(in));
				break;
			case 2:		// TYPE_UINTVAR
				value = String.valueOf(decodeUintvar(in));
				break;
			case 3:		// TYPE_LONG
				value = String.valueOf(decodeLong(in));				
				break;	
			case 4:		// TYPE_BOOLEAN
				value = String.valueOf(decodeBoolean(in));			
				break;	
			case 5:		// TYPE_DATE

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -