?? mmdecoder.java
字號:
/* ***** 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 + -