package cn.edu.tsinghua.hpc.syncbroker; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import org.apache.commons.codec.binary.Base64; /** * NOTE: toVSMS() can export the current SMSRecord to vCard format, but we do * not have a corresponding importer, please use the Android vCard parser to get * each field, please implement postInit(). * * @author xrn * */ public class SMSRecord extends SyncRecord { // inherit guid, tag, and data private String from; private String to; private Date date; private SMSType type = SMSType.RECEIVE; private SMSSubType subType = SMSSubType.FRIEND; private boolean read = true; private boolean secret = false; private String pdu = null; private String body = null; private MessageType mtype = MessageType.SMS; /** * we now use vcard format to present vsms. Example: * * BEGIN:VCARD * VERSION:2.1 * FROM:xxx * TO:xxx * TYPE:xxx * SUBTYPE:xxx * READ:[0|1] * BODY;ENCODING=BASE64;CHARSET=UTF-8:xxx * MSG_TYPE: [mms|sms] * PDU;ENCODING=BASE64;CHARSET=UTF-8:xxx * END:VCARD * */ private static final String vSMSTemplate = "BEGIN:VCARD\r\n" + "VERSION:2.1\r\n" + "N:Name;Faked\r\n" + // XXX: this is for vobject module "FN:Faked Name\r\n" + // XXX: this is for vobject module "%s:%s\r\n" + // from "%s:%s\r\n" + // to "%s:%d\r\n" + // date "%s:%s\r\n" + // type "%s:%s\r\n" + // subtype "%s:%d\r\n" + // read "%s;ENCODING=BASE64;CHARSET=UTF-8:%s\r\n" + // XXX: message body. vcard 2.1 uses BASE64, vcard 3.0 uses b "%s:%s\r\n" + // message type mms | sms "%s;ENCODING=BASE64;CHARSET=UTF-8:%s\r\n" + // XXX: pdu is pure binary, client should encode it to string first. "END:VCARD\r\n"; /** * @param guid * @param tag * @param data : the full vCard data. */ public SMSRecord(int guid, SyncTag tag, String data) { super(guid, tag, data); from = null; to = null; date = null; type = SMSType.UNKNOWN; subType = SMSSubType.UNKNOWN; secret = false; read = true; body = null; pdu = null; postInit(); } /** * @param from * @param to * @param date * @param type * @param subType * @param secret * @param body */ public SMSRecord(String from, String to, Date date, SMSType type, SMSSubType subType, boolean read, boolean secret, String body) { super(); this.from = from; this.to = to; this.date = date; this.type = type; this.subType = subType; this.read = read; this.secret = secret; this.body = body; this.pdu = body; this.mtype = MessageType.SMS; } public SMSRecord(String from, String to, Date date, SMSType type, SMSSubType subType, boolean read, boolean secret, String body, String pdu) { super(); this.from = from; this.to = to; this.date = date; this.type = type; this.subType = subType; this.read = read; this.secret = secret; this.body = body; this.pdu = pdu; this.mtype = MessageType.MMS; } public String getFrom() { return from; } public void setFrom(String from) { this.from = from; } public String getTo() { return to; } public void setTo(String to) { this.to = to; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public SMSType getType() { return type; } public void setType(SMSType type) { this.type = type; } public SMSSubType getSubType() { return subType; } public void setSubType(SMSSubType subType) { this.subType = subType; } public boolean isRead() { return read; } public void setRead(boolean read) { this.read = read; } public boolean isSecret() { return secret; } public void setSecret(boolean secret) { this.secret = secret; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public void setMtype(MessageType mtype) { this.mtype = mtype; } public MessageType getMtype() { return mtype; } public void setPdu(String pdu) { this.pdu = pdu; } public String getPdu() { return pdu; } /** * export an sms record to vsms format (vcard format actually now). * * @return */ public String toVSMS() { return String.format(vSMSTemplate, SMSProperty.FROM.name(), from, SMSProperty.TO.name(), to, SMSProperty.DATE.name(), date.getTime(), SMSProperty.TYPE.name(), type.name(), SMSProperty.SUBTYPE.name(), subType.name(), SMSProperty.READ.name(), ((read == true) ? 1 : 0), SMSProperty.BODY.name(), VCardUtils.foldingString(new String(Base64.encodeBase64Chunked(body.getBytes())), VCardUtils.VERSION_VCARD21_INT), SMSProperty.MSG_TYPE.name(), mtype.name(), SMSProperty.PDU.name(), VCardUtils.foldingString(new String(Base64.encodeBase64Chunked(pdu.getBytes())), VCardUtils.VERSION_VCARD21_INT)); } /** * * fill each member by parsing the whole bunch of "data" member. * * TODO: silly implementation now, improve with Android's vcard facility. */ public void postInit() { if ((data == null) || (data.length() == 0)) { // TODO: raise exception return; } VCardParser parser = new VCardParser(); VDataBuilder builder = new VDataBuilder(); try { parser.parse(this.data, builder); } catch (VCardException e) { return; } catch (IOException e) { return; } VNode smsNode = builder.vNodeList.get(0); ArrayList<PropertyNode> props = smsNode.propList; for (PropertyNode prop : props) { if (prop.propName.equalsIgnoreCase(SMSProperty.FROM.name())) { this.from = prop.propValue; } else if (prop.propName.equalsIgnoreCase(SMSProperty.TO.name())) { this.to = prop.propValue; } else if (prop.propName.equalsIgnoreCase(SMSProperty.DATE.name())) { this.date = new Date(Long.parseLong(prop.propValue)); } else if (prop.propName.equalsIgnoreCase(SMSProperty.TYPE.name())) { this.type = SMSType.valueOf(prop.propValue.toUpperCase()); } else if (prop.propName.equalsIgnoreCase(SMSProperty.SUBTYPE.name())) { this.subType = SMSSubType.valueOf(prop.propValue.toUpperCase()); } else if (prop.propName.equalsIgnoreCase(SMSProperty.READ.name())) { this.read = prop.propValue.equals("1") ? true : false; } else if (prop.propName.equalsIgnoreCase(SMSProperty.MSG_TYPE.name())) { this.mtype = MessageType.valueOf(prop.propValue.toUpperCase()); } else if (prop.propName.equalsIgnoreCase(SMSProperty.PDU.name())) { /** * NOTE: if the data is encoded in BASE64, VDataBuilder would * store the un-decoded data in propValue, and decoded data (in * bytes) in propValue_bytes. */ this.pdu = new String(prop.propValue_bytes); } else if (prop.propName.equalsIgnoreCase(SMSProperty.BODY.name())) { /** * NOTE: if the data is encoded in BASE64, VDataBuilder would * store the un-decoded data in propValue, and decoded data (in * bytes) in propValue_bytes. */ this.body = new String(prop.propValue_bytes); } else { // nothing serious } } } @Override public String toString() { return "SMSRecord [body=" + body + ", date=" + date + ", from=" + from + ", read=" + read + ", secret=" + secret + ", subType=" + subType + ", to=" + to + ", type=" + type + ", msg_type=" + mtype + ", summary = " + pdu + ", data=" + data + ", guid=" + guid + ", tag=" + tag + "]"; } } enum SMSProperty { FROM, TO, DATE, TYPE, SUBTYPE, READ, SECRET, BODY, MSG_TYPE, PDU }