Home | History | Annotate | Download | only in pdu
      1 /*
      2  * Copyright (C) 2007-2008 Esmertec AG.
      3  * Copyright (C) 2007-2008 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package android.support.v7.mms.pdu;
     19 
     20 import android.util.Log;
     21 
     22 import java.io.ByteArrayInputStream;
     23 import java.io.ByteArrayOutputStream;
     24 import java.io.UnsupportedEncodingException;
     25 import java.util.Arrays;
     26 import java.util.HashMap;
     27 
     28 public class PduParser {
     29     /**
     30      *  The next are WAP values defined in WSP specification.
     31      */
     32     private static final int QUOTE = 127;
     33     private static final int LENGTH_QUOTE = 31;
     34     private static final int TEXT_MIN = 32;
     35     private static final int TEXT_MAX = 127;
     36     private static final int SHORT_INTEGER_MAX = 127;
     37     private static final int SHORT_LENGTH_MAX = 30;
     38     private static final int LONG_INTEGER_LENGTH_MAX = 8;
     39     private static final int QUOTED_STRING_FLAG = 34;
     40     private static final int END_STRING_FLAG = 0x00;
     41     //The next two are used by the interface "parseWapString" to
     42     //distinguish Text-String and Quoted-String.
     43     private static final int TYPE_TEXT_STRING = 0;
     44     private static final int TYPE_QUOTED_STRING = 1;
     45     private static final int TYPE_TOKEN_STRING = 2;
     46 
     47     /**
     48      * Specify the part position.
     49      */
     50     private static final int THE_FIRST_PART = 0;
     51     private static final int THE_LAST_PART = 1;
     52 
     53     /**
     54      * The pdu data.
     55      */
     56     private ByteArrayInputStream mPduDataStream = null;
     57 
     58     /**
     59      * Store pdu headers
     60      */
     61     private PduHeaders mHeaders = null;
     62 
     63     /**
     64      * Store pdu parts.
     65      */
     66     private PduBody mBody = null;
     67 
     68     /**
     69      * Store the "type" parameter in "Content-Type" header field.
     70      */
     71     private static byte[] mTypeParam = null;
     72 
     73     /**
     74      * Store the "start" parameter in "Content-Type" header field.
     75      */
     76     private static byte[] mStartParam = null;
     77 
     78     /**
     79      * The log tag.
     80      */
     81     private static final String LOG_TAG = "PduParser";
     82     private static final boolean DEBUG = false;
     83     private static final boolean LOCAL_LOGV = false;
     84 
     85     /**
     86      * Whether to parse content-disposition part header
     87      */
     88     private final boolean mParseContentDisposition;
     89 
     90     /**
     91      * Constructor.
     92      *
     93      * @param pduDataStream pdu data to be parsed
     94      * @param parseContentDisposition whether to parse the Content-Disposition part header
     95      */
     96     public PduParser(byte[] pduDataStream, boolean parseContentDisposition) {
     97         mPduDataStream = new ByteArrayInputStream(pduDataStream);
     98         mParseContentDisposition = parseContentDisposition;
     99     }
    100 
    101     /**
    102      * Parse the pdu.
    103      *
    104      * @return the pdu structure if parsing successfully.
    105      *         null if parsing error happened or mandatory fields are not set.
    106      */
    107     public GenericPdu parse(){
    108         if (mPduDataStream == null) {
    109             return null;
    110         }
    111 
    112         /* parse headers */
    113         mHeaders = parseHeaders(mPduDataStream);
    114         if (null == mHeaders) {
    115             // Parse headers failed.
    116             return null;
    117         }
    118 
    119         /* get the message type */
    120         int messageType = mHeaders.getOctet(PduHeaders.MESSAGE_TYPE);
    121 
    122         /* check mandatory header fields */
    123         if (false == checkMandatoryHeader(mHeaders)) {
    124             log("check mandatory headers failed!");
    125             return null;
    126         }
    127 
    128         if ((PduHeaders.MESSAGE_TYPE_SEND_REQ == messageType) ||
    129                 (PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF == messageType)) {
    130             /* need to parse the parts */
    131             mBody = parseParts(mPduDataStream);
    132             if (null == mBody) {
    133                 // Parse parts failed.
    134                 return null;
    135             }
    136         }
    137 
    138         switch (messageType) {
    139             case PduHeaders.MESSAGE_TYPE_SEND_REQ:
    140                 if (LOCAL_LOGV) {
    141                     Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_REQ");
    142                 }
    143                 SendReq sendReq = new SendReq(mHeaders, mBody);
    144                 return sendReq;
    145             case PduHeaders.MESSAGE_TYPE_SEND_CONF:
    146                 if (LOCAL_LOGV) {
    147                     Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_CONF");
    148                 }
    149                 SendConf sendConf = new SendConf(mHeaders);
    150                 return sendConf;
    151             case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
    152                 if (LOCAL_LOGV) {
    153                     Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFICATION_IND");
    154                 }
    155                 NotificationInd notificationInd =
    156                     new NotificationInd(mHeaders);
    157                 return notificationInd;
    158             case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
    159                 if (LOCAL_LOGV) {
    160                     Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFYRESP_IND");
    161                 }
    162                 NotifyRespInd notifyRespInd =
    163                     new NotifyRespInd(mHeaders);
    164                 return notifyRespInd;
    165             case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
    166                 if (LOCAL_LOGV) {
    167                     Log.v(LOG_TAG, "parse: MESSAGE_TYPE_RETRIEVE_CONF");
    168                 }
    169                 RetrieveConf retrieveConf =
    170                     new RetrieveConf(mHeaders, mBody);
    171 
    172                 byte[] contentType = retrieveConf.getContentType();
    173                 if (null == contentType) {
    174                     return null;
    175                 }
    176                 String ctTypeStr = new String(contentType);
    177                 if (ctTypeStr.equals(ContentType.MULTIPART_MIXED)
    178                         || ctTypeStr.equals(ContentType.MULTIPART_RELATED)
    179                         || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
    180                     // The MMS content type must be "application/vnd.wap.multipart.mixed"
    181                     // or "application/vnd.wap.multipart.related"
    182                     // or "application/vnd.wap.multipart.alternative"
    183                     return retrieveConf;
    184                 } else if (ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
    185                     // "application/vnd.wap.multipart.alternative"
    186                     // should take only the first part.
    187                     PduPart firstPart = mBody.getPart(0);
    188                     mBody.removeAll();
    189                     mBody.addPart(0, firstPart);
    190                     return retrieveConf;
    191                 }
    192                 return null;
    193             case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
    194                 if (LOCAL_LOGV) {
    195                     Log.v(LOG_TAG, "parse: MESSAGE_TYPE_DELIVERY_IND");
    196                 }
    197                 DeliveryInd deliveryInd =
    198                     new DeliveryInd(mHeaders);
    199                 return deliveryInd;
    200             case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
    201                 if (LOCAL_LOGV) {
    202                     Log.v(LOG_TAG, "parse: MESSAGE_TYPE_ACKNOWLEDGE_IND");
    203                 }
    204                 AcknowledgeInd acknowledgeInd =
    205                     new AcknowledgeInd(mHeaders);
    206                 return acknowledgeInd;
    207             case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
    208                 if (LOCAL_LOGV) {
    209                     Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_ORIG_IND");
    210                 }
    211                 ReadOrigInd readOrigInd =
    212                     new ReadOrigInd(mHeaders);
    213                 return readOrigInd;
    214             case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
    215                 if (LOCAL_LOGV) {
    216                     Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_REC_IND");
    217                 }
    218                 ReadRecInd readRecInd =
    219                     new ReadRecInd(mHeaders);
    220                 return readRecInd;
    221             default:
    222                 log("Parser doesn't support this message type in this version!");
    223             return null;
    224         }
    225     }
    226 
    227     /**
    228      * Parse pdu headers.
    229      *
    230      * @param pduDataStream pdu data input stream
    231      * @return headers in PduHeaders structure, null when parse fail
    232      */
    233     protected PduHeaders parseHeaders(ByteArrayInputStream pduDataStream){
    234         if (pduDataStream == null) {
    235             return null;
    236         }
    237         boolean keepParsing = true;
    238         PduHeaders headers = new PduHeaders();
    239 
    240         while (keepParsing && (pduDataStream.available() > 0)) {
    241             pduDataStream.mark(1);
    242             int headerField = extractByteValue(pduDataStream);
    243             /* parse custom text header */
    244             if ((headerField >= TEXT_MIN) && (headerField <= TEXT_MAX)) {
    245                 pduDataStream.reset();
    246                 byte [] bVal = parseWapString(pduDataStream, TYPE_TEXT_STRING);
    247                 if (LOCAL_LOGV) {
    248                     Log.v(LOG_TAG, "TextHeader: " + new String(bVal));
    249                 }
    250                 /* we should ignore it at the moment */
    251                 continue;
    252             }
    253             switch (headerField) {
    254                 case PduHeaders.MESSAGE_TYPE:
    255                 {
    256                     int messageType = extractByteValue(pduDataStream);
    257                     if (LOCAL_LOGV) {
    258                         Log.v(LOG_TAG, "parseHeaders: messageType: " + messageType);
    259                     }
    260                     switch (messageType) {
    261                         // We don't support these kind of messages now.
    262                         case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
    263                         case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
    264                         case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
    265                         case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
    266                         case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
    267                         case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
    268                         case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
    269                         case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
    270                         case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
    271                         case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
    272                         case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
    273                         case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
    274                         case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
    275                         case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
    276                         case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
    277                             return null;
    278                     }
    279                     try {
    280                         headers.setOctet(messageType, headerField);
    281                     } catch(InvalidHeaderValueException e) {
    282                         log("Set invalid Octet value: " + messageType +
    283                                 " into the header filed: " + headerField);
    284                         return null;
    285                     } catch(RuntimeException e) {
    286                         log(headerField + "is not Octet header field!");
    287                         return null;
    288                     }
    289                     break;
    290                 }
    291                 /* Octect value */
    292                 case PduHeaders.REPORT_ALLOWED:
    293                 case PduHeaders.ADAPTATION_ALLOWED:
    294                 case PduHeaders.DELIVERY_REPORT:
    295                 case PduHeaders.DRM_CONTENT:
    296                 case PduHeaders.DISTRIBUTION_INDICATOR:
    297                 case PduHeaders.QUOTAS:
    298                 case PduHeaders.READ_REPORT:
    299                 case PduHeaders.STORE:
    300                 case PduHeaders.STORED:
    301                 case PduHeaders.TOTALS:
    302                 case PduHeaders.SENDER_VISIBILITY:
    303                 case PduHeaders.READ_STATUS:
    304                 case PduHeaders.CANCEL_STATUS:
    305                 case PduHeaders.PRIORITY:
    306                 case PduHeaders.STATUS:
    307                 case PduHeaders.REPLY_CHARGING:
    308                 case PduHeaders.MM_STATE:
    309                 case PduHeaders.RECOMMENDED_RETRIEVAL_MODE:
    310                 case PduHeaders.CONTENT_CLASS:
    311                 case PduHeaders.RETRIEVE_STATUS:
    312                 case PduHeaders.STORE_STATUS:
    313                     /**
    314                      * The following field has a different value when
    315                      * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
    316                      * For now we ignore this fact, since we do not support these PDUs
    317                      */
    318                 case PduHeaders.RESPONSE_STATUS:
    319                 {
    320                     int value = extractByteValue(pduDataStream);
    321                     if (LOCAL_LOGV) {
    322                         Log.v(LOG_TAG, "parseHeaders: byte: " + headerField + " value: " +
    323                                 value);
    324                     }
    325 
    326                     try {
    327                         headers.setOctet(value, headerField);
    328                     } catch(InvalidHeaderValueException e) {
    329                         log("Set invalid Octet value: " + value +
    330                                 " into the header filed: " + headerField);
    331                         return null;
    332                     } catch(RuntimeException e) {
    333                         log(headerField + "is not Octet header field!");
    334                         return null;
    335                     }
    336                     break;
    337                 }
    338 
    339                 /* Long-Integer */
    340                 case PduHeaders.DATE:
    341                 case PduHeaders.REPLY_CHARGING_SIZE:
    342                 case PduHeaders.MESSAGE_SIZE:
    343                 {
    344                     try {
    345                         long value = parseLongInteger(pduDataStream);
    346                         if (LOCAL_LOGV) {
    347                             Log.v(LOG_TAG, "parseHeaders: longint: " + headerField + " value: " +
    348                                     value);
    349                         }
    350                         headers.setLongInteger(value, headerField);
    351                     } catch(RuntimeException e) {
    352                         log(headerField + "is not Long-Integer header field!");
    353                         return null;
    354                     }
    355                     break;
    356                 }
    357 
    358                 /* Integer-Value */
    359                 case PduHeaders.MESSAGE_COUNT:
    360                 case PduHeaders.START:
    361                 case PduHeaders.LIMIT:
    362                 {
    363                     try {
    364                         long value = parseIntegerValue(pduDataStream);
    365                         if (LOCAL_LOGV) {
    366                             Log.v(LOG_TAG, "parseHeaders: int: " + headerField + " value: " +
    367                                     value);
    368                         }
    369                         headers.setLongInteger(value, headerField);
    370                     } catch(RuntimeException e) {
    371                         log(headerField + "is not Long-Integer header field!");
    372                         return null;
    373                     }
    374                     break;
    375                 }
    376 
    377                 /* Text-String */
    378                 case PduHeaders.TRANSACTION_ID:
    379                 case PduHeaders.REPLY_CHARGING_ID:
    380                 case PduHeaders.AUX_APPLIC_ID:
    381                 case PduHeaders.APPLIC_ID:
    382                 case PduHeaders.REPLY_APPLIC_ID:
    383                     /**
    384                      * The next three header fields are email addresses
    385                      * as defined in RFC2822,
    386                      * not including the characters "<" and ">"
    387                      */
    388                 case PduHeaders.MESSAGE_ID:
    389                 case PduHeaders.REPLACE_ID:
    390                 case PduHeaders.CANCEL_ID:
    391                     /**
    392                      * The following field has a different value when
    393                      * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
    394                      * For now we ignore this fact, since we do not support these PDUs
    395                      */
    396                 case PduHeaders.CONTENT_LOCATION:
    397                 {
    398                     byte[] value = parseWapString(pduDataStream, TYPE_TEXT_STRING);
    399                     if (null != value) {
    400                         try {
    401                             if (LOCAL_LOGV) {
    402                                 Log.v(LOG_TAG, "parseHeaders: string: " + headerField + " value: " +
    403                                         new String(value));
    404                             }
    405                             headers.setTextString(value, headerField);
    406                         } catch(NullPointerException e) {
    407                             log("null pointer error!");
    408                         } catch(RuntimeException e) {
    409                             log(headerField + "is not Text-String header field!");
    410                             return null;
    411                         }
    412                     }
    413                     break;
    414                 }
    415 
    416                 /* Encoded-string-value */
    417                 case PduHeaders.SUBJECT:
    418                 case PduHeaders.RECOMMENDED_RETRIEVAL_MODE_TEXT:
    419                 case PduHeaders.RETRIEVE_TEXT:
    420                 case PduHeaders.STATUS_TEXT:
    421                 case PduHeaders.STORE_STATUS_TEXT:
    422                     /* the next one is not support
    423                      * M-Mbox-Delete.conf and M-Delete.conf now */
    424                 case PduHeaders.RESPONSE_TEXT:
    425                 {
    426                     EncodedStringValue value =
    427                         parseEncodedStringValue(pduDataStream);
    428                     if (null != value) {
    429                         try {
    430                             if (LOCAL_LOGV) {
    431                                 Log.v(LOG_TAG, "parseHeaders: encoded string: " + headerField
    432                                         + " value: " + value.getString());
    433                             }
    434                             headers.setEncodedStringValue(value, headerField);
    435                         } catch(NullPointerException e) {
    436                             log("null pointer error!");
    437                         } catch (RuntimeException e) {
    438                             log(headerField + "is not Encoded-String-Value header field!");
    439                             return null;
    440                         }
    441                     }
    442                     break;
    443                 }
    444 
    445                 /* Addressing model */
    446                 case PduHeaders.BCC:
    447                 case PduHeaders.CC:
    448                 case PduHeaders.TO:
    449                 {
    450                     EncodedStringValue value =
    451                         parseEncodedStringValue(pduDataStream);
    452                     if (null != value) {
    453                         byte[] address = value.getTextString();
    454                         if (null != address) {
    455                             String str = new String(address);
    456                             if (LOCAL_LOGV) {
    457                                 Log.v(LOG_TAG, "parseHeaders: (to/cc/bcc) address: " + headerField
    458                                         + " value: " + str);
    459                             }
    460                             int endIndex = str.indexOf("/");
    461                             if (endIndex > 0) {
    462                                 str = str.substring(0, endIndex);
    463                             }
    464                             try {
    465                                 value.setTextString(str.getBytes());
    466                             } catch(NullPointerException e) {
    467                                 log("null pointer error!");
    468                                 return null;
    469                             }
    470                         }
    471 
    472                         try {
    473                             headers.appendEncodedStringValue(value, headerField);
    474                         } catch(NullPointerException e) {
    475                             log("null pointer error!");
    476                         } catch(RuntimeException e) {
    477                             log(headerField + "is not Encoded-String-Value header field!");
    478                             return null;
    479                         }
    480                     }
    481                     break;
    482                 }
    483 
    484                 /* Value-length
    485                  * (Absolute-token Date-value | Relative-token Delta-seconds-value) */
    486                 case PduHeaders.DELIVERY_TIME:
    487                 case PduHeaders.EXPIRY:
    488                 case PduHeaders.REPLY_CHARGING_DEADLINE:
    489                 {
    490                     /* parse Value-length */
    491                     parseValueLength(pduDataStream);
    492 
    493                     /* Absolute-token or Relative-token */
    494                     int token = extractByteValue(pduDataStream);
    495 
    496                     /* Date-value or Delta-seconds-value */
    497                     long timeValue;
    498                     try {
    499                         timeValue = parseLongInteger(pduDataStream);
    500                     } catch(RuntimeException e) {
    501                         log(headerField + "is not Long-Integer header field!");
    502                         return null;
    503                     }
    504                     if (PduHeaders.VALUE_RELATIVE_TOKEN == token) {
    505                         /* need to convert the Delta-seconds-value
    506                          * into Date-value */
    507                         timeValue = System.currentTimeMillis()/1000 + timeValue;
    508                     }
    509 
    510                     try {
    511                         if (LOCAL_LOGV) {
    512                             Log.v(LOG_TAG, "parseHeaders: time value: " + headerField
    513                                     + " value: " + timeValue);
    514                         }
    515                         headers.setLongInteger(timeValue, headerField);
    516                     } catch(RuntimeException e) {
    517                         log(headerField + "is not Long-Integer header field!");
    518                         return null;
    519                     }
    520                     break;
    521                 }
    522 
    523                 case PduHeaders.FROM: {
    524                     /* From-value =
    525                      * Value-length
    526                      * (Address-present-token Encoded-string-value | Insert-address-token)
    527                      */
    528                     EncodedStringValue from = null;
    529                     parseValueLength(pduDataStream); /* parse value-length */
    530 
    531                     /* Address-present-token or Insert-address-token */
    532                     int fromToken = extractByteValue(pduDataStream);
    533 
    534                     /* Address-present-token or Insert-address-token */
    535                     if (PduHeaders.FROM_ADDRESS_PRESENT_TOKEN == fromToken) {
    536                         /* Encoded-string-value */
    537                         from = parseEncodedStringValue(pduDataStream);
    538                         if (null != from) {
    539                             byte[] address = from.getTextString();
    540                             if (null != address) {
    541                                 String str = new String(address);
    542                                 int endIndex = str.indexOf("/");
    543                                 if (endIndex > 0) {
    544                                     str = str.substring(0, endIndex);
    545                                 }
    546                                 try {
    547                                     from.setTextString(str.getBytes());
    548                                 } catch(NullPointerException e) {
    549                                     log("null pointer error!");
    550                                     return null;
    551                                 }
    552                             }
    553                         }
    554                     } else {
    555                         try {
    556                             from = new EncodedStringValue(
    557                                     PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes());
    558                         } catch(NullPointerException e) {
    559                             log(headerField + "is not Encoded-String-Value header field!");
    560                             return null;
    561                         }
    562                     }
    563 
    564                     try {
    565                         if (LOCAL_LOGV) {
    566                             Log.v(LOG_TAG, "parseHeaders: from address: " + headerField
    567                                     + " value: " + from.getString());
    568                         }
    569                         headers.setEncodedStringValue(from, PduHeaders.FROM);
    570                     } catch(NullPointerException e) {
    571                         log("null pointer error!");
    572                     } catch(RuntimeException e) {
    573                         log(headerField + "is not Encoded-String-Value header field!");
    574                         return null;
    575                     }
    576                     break;
    577                 }
    578 
    579                 case PduHeaders.MESSAGE_CLASS: {
    580                     /* Message-class-value = Class-identifier | Token-text */
    581                     pduDataStream.mark(1);
    582                     int messageClass = extractByteValue(pduDataStream);
    583                     if (LOCAL_LOGV) {
    584                         Log.v(LOG_TAG, "parseHeaders: MESSAGE_CLASS: " + headerField
    585                                 + " value: " + messageClass);
    586                     }
    587 
    588                     if (messageClass >= PduHeaders.MESSAGE_CLASS_PERSONAL) {
    589                         /* Class-identifier */
    590                         try {
    591                             if (PduHeaders.MESSAGE_CLASS_PERSONAL == messageClass) {
    592                                 headers.setTextString(
    593                                         PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes(),
    594                                         PduHeaders.MESSAGE_CLASS);
    595                             } else if (PduHeaders.MESSAGE_CLASS_ADVERTISEMENT == messageClass) {
    596                                 headers.setTextString(
    597                                         PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes(),
    598                                         PduHeaders.MESSAGE_CLASS);
    599                             } else if (PduHeaders.MESSAGE_CLASS_INFORMATIONAL == messageClass) {
    600                                 headers.setTextString(
    601                                         PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes(),
    602                                         PduHeaders.MESSAGE_CLASS);
    603                             } else if (PduHeaders.MESSAGE_CLASS_AUTO == messageClass) {
    604                                 headers.setTextString(
    605                                         PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes(),
    606                                         PduHeaders.MESSAGE_CLASS);
    607                             }
    608                         } catch(NullPointerException e) {
    609                             log("null pointer error!");
    610                         } catch(RuntimeException e) {
    611                             log(headerField + "is not Text-String header field!");
    612                             return null;
    613                         }
    614                     } else {
    615                         /* Token-text */
    616                         pduDataStream.reset();
    617                         byte[] messageClassString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
    618                         if (null != messageClassString) {
    619                             try {
    620                                 headers.setTextString(messageClassString, PduHeaders.MESSAGE_CLASS);
    621                             } catch(NullPointerException e) {
    622                                 log("null pointer error!");
    623                             } catch(RuntimeException e) {
    624                                 log(headerField + "is not Text-String header field!");
    625                                 return null;
    626                             }
    627                         }
    628                     }
    629                     break;
    630                 }
    631 
    632                 case PduHeaders.MMS_VERSION: {
    633                     int version = parseShortInteger(pduDataStream);
    634 
    635                     try {
    636                         if (LOCAL_LOGV) {
    637                             Log.v(LOG_TAG, "parseHeaders: MMS_VERSION: " + headerField
    638                                     + " value: " + version);
    639                         }
    640                         headers.setOctet(version, PduHeaders.MMS_VERSION);
    641                     } catch(InvalidHeaderValueException e) {
    642                         log("Set invalid Octet value: " + version +
    643                                 " into the header filed: " + headerField);
    644                         return null;
    645                     } catch(RuntimeException e) {
    646                         log(headerField + "is not Octet header field!");
    647                         return null;
    648                     }
    649                     break;
    650                 }
    651 
    652                 case PduHeaders.PREVIOUSLY_SENT_BY: {
    653                     /* Previously-sent-by-value =
    654                      * Value-length Forwarded-count-value Encoded-string-value */
    655                     /* parse value-length */
    656                     parseValueLength(pduDataStream);
    657 
    658                     /* parse Forwarded-count-value */
    659                     try {
    660                         parseIntegerValue(pduDataStream);
    661                     } catch(RuntimeException e) {
    662                         log(headerField + " is not Integer-Value");
    663                         return null;
    664                     }
    665 
    666                     /* parse Encoded-string-value */
    667                     EncodedStringValue previouslySentBy =
    668                         parseEncodedStringValue(pduDataStream);
    669                     if (null != previouslySentBy) {
    670                         try {
    671                             if (LOCAL_LOGV) {
    672                                 Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_BY: " + headerField
    673                                         + " value: " + previouslySentBy.getString());
    674                             }
    675                             headers.setEncodedStringValue(previouslySentBy,
    676                                     PduHeaders.PREVIOUSLY_SENT_BY);
    677                         } catch(NullPointerException e) {
    678                             log("null pointer error!");
    679                         } catch(RuntimeException e) {
    680                             log(headerField + "is not Encoded-String-Value header field!");
    681                             return null;
    682                         }
    683                     }
    684                     break;
    685                 }
    686 
    687                 case PduHeaders.PREVIOUSLY_SENT_DATE: {
    688                     /* Previously-sent-date-value =
    689                      * Value-length Forwarded-count-value Date-value */
    690                     /* parse value-length */
    691                     parseValueLength(pduDataStream);
    692 
    693                     /* parse Forwarded-count-value */
    694                     try {
    695                         parseIntegerValue(pduDataStream);
    696                     } catch(RuntimeException e) {
    697                         log(headerField + " is not Integer-Value");
    698                         return null;
    699                     }
    700 
    701                     /* Date-value */
    702                     try {
    703                         long perviouslySentDate = parseLongInteger(pduDataStream);
    704                         if (LOCAL_LOGV) {
    705                             Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_DATE: " + headerField
    706                                     + " value: " + perviouslySentDate);
    707                         }
    708                         headers.setLongInteger(perviouslySentDate,
    709                                 PduHeaders.PREVIOUSLY_SENT_DATE);
    710                     } catch(RuntimeException e) {
    711                         log(headerField + "is not Long-Integer header field!");
    712                         return null;
    713                     }
    714                     break;
    715                 }
    716 
    717                 case PduHeaders.MM_FLAGS: {
    718                     /* MM-flags-value =
    719                      * Value-length
    720                      * ( Add-token | Remove-token | Filter-token )
    721                      * Encoded-string-value
    722                      */
    723                     if (LOCAL_LOGV) {
    724                         Log.v(LOG_TAG, "parseHeaders: MM_FLAGS: " + headerField
    725                                 + " NOT REALLY SUPPORTED");
    726                     }
    727 
    728                     /* parse Value-length */
    729                     parseValueLength(pduDataStream);
    730 
    731                     /* Add-token | Remove-token | Filter-token */
    732                     extractByteValue(pduDataStream);
    733 
    734                     /* Encoded-string-value */
    735                     parseEncodedStringValue(pduDataStream);
    736 
    737                     /* not store this header filed in "headers",
    738                      * because now PduHeaders doesn't support it */
    739                     break;
    740                 }
    741 
    742                 /* Value-length
    743                  * (Message-total-token | Size-total-token) Integer-Value */
    744                 case PduHeaders.MBOX_TOTALS:
    745                 case PduHeaders.MBOX_QUOTAS:
    746                 {
    747                     if (LOCAL_LOGV) {
    748                         Log.v(LOG_TAG, "parseHeaders: MBOX_TOTALS: " + headerField);
    749                     }
    750                     /* Value-length */
    751                     parseValueLength(pduDataStream);
    752 
    753                     /* Message-total-token | Size-total-token */
    754                     extractByteValue(pduDataStream);
    755 
    756                     /*Integer-Value*/
    757                     try {
    758                         parseIntegerValue(pduDataStream);
    759                     } catch(RuntimeException e) {
    760                         log(headerField + " is not Integer-Value");
    761                         return null;
    762                     }
    763 
    764                     /* not store these headers filed in "headers",
    765                     because now PduHeaders doesn't support them */
    766                     break;
    767                 }
    768 
    769                 case PduHeaders.ELEMENT_DESCRIPTOR: {
    770                     if (LOCAL_LOGV) {
    771                         Log.v(LOG_TAG, "parseHeaders: ELEMENT_DESCRIPTOR: " + headerField);
    772                     }
    773                     parseContentType(pduDataStream, null);
    774 
    775                     /* not store this header filed in "headers",
    776                     because now PduHeaders doesn't support it */
    777                     break;
    778                 }
    779 
    780                 case PduHeaders.CONTENT_TYPE: {
    781                     HashMap<Integer, Object> map =
    782                         new HashMap<Integer, Object>();
    783                     byte[] contentType =
    784                         parseContentType(pduDataStream, map);
    785 
    786                     if (null != contentType) {
    787                         try {
    788                             if (LOCAL_LOGV) {
    789                                 Log.v(LOG_TAG, "parseHeaders: CONTENT_TYPE: " + headerField +
    790                                         Arrays.toString(contentType));
    791                             }
    792                             headers.setTextString(contentType, PduHeaders.CONTENT_TYPE);
    793                         } catch(NullPointerException e) {
    794                             log("null pointer error!");
    795                         } catch(RuntimeException e) {
    796                             log(headerField + "is not Text-String header field!");
    797                             return null;
    798                         }
    799                     }
    800 
    801                     /* get start parameter */
    802                     mStartParam = (byte[]) map.get(PduPart.P_START);
    803 
    804                     /* get charset parameter */
    805                     mTypeParam= (byte[]) map.get(PduPart.P_TYPE);
    806 
    807                     keepParsing = false;
    808                     break;
    809                 }
    810 
    811                 case PduHeaders.CONTENT:
    812                 case PduHeaders.ADDITIONAL_HEADERS:
    813                 case PduHeaders.ATTRIBUTES:
    814                 default: {
    815                     if (LOCAL_LOGV) {
    816                         Log.v(LOG_TAG, "parseHeaders: Unknown header: " + headerField);
    817                     }
    818                     log("Unknown header");
    819                 }
    820             }
    821         }
    822 
    823         return headers;
    824     }
    825 
    826     /**
    827      * Parse pdu parts.
    828      *
    829      * @param pduDataStream pdu data input stream
    830      * @return parts in PduBody structure
    831      */
    832     protected PduBody parseParts(ByteArrayInputStream pduDataStream) {
    833         if (pduDataStream == null) {
    834             return null;
    835         }
    836 
    837         int count = parseUnsignedInt(pduDataStream); // get the number of parts
    838         PduBody body = new PduBody();
    839 
    840         for (int i = 0 ; i < count ; i++) {
    841             int headerLength = parseUnsignedInt(pduDataStream);
    842             int dataLength = parseUnsignedInt(pduDataStream);
    843             PduPart part = new PduPart();
    844             int startPos = pduDataStream.available();
    845             if (startPos <= 0) {
    846                 // Invalid part.
    847                 return null;
    848             }
    849 
    850             /* parse part's content-type */
    851             HashMap<Integer, Object> map = new HashMap<Integer, Object>();
    852             byte[] contentType = parseContentType(pduDataStream, map);
    853             if (null != contentType) {
    854                 part.setContentType(contentType);
    855             } else {
    856                 part.setContentType((PduContentTypes.contentTypes[0]).getBytes()); //"*/*"
    857             }
    858 
    859             /* get name parameter */
    860             byte[] name = (byte[]) map.get(PduPart.P_NAME);
    861             if (null != name) {
    862                 part.setName(name);
    863             }
    864 
    865             /* get charset parameter */
    866             Integer charset = (Integer) map.get(PduPart.P_CHARSET);
    867             if (null != charset) {
    868                 part.setCharset(charset);
    869             }
    870 
    871             /* parse part's headers */
    872             int endPos = pduDataStream.available();
    873             int partHeaderLen = headerLength - (startPos - endPos);
    874             if (partHeaderLen > 0) {
    875                 if (false == parsePartHeaders(pduDataStream, part, partHeaderLen)) {
    876                     // Parse part header faild.
    877                     return null;
    878                 }
    879             } else if (partHeaderLen < 0) {
    880                 // Invalid length of content-type.
    881                 return null;
    882             }
    883 
    884             /* FIXME: check content-id, name, filename and content location,
    885              * if not set anyone of them, generate a default content-location
    886              */
    887             if ((null == part.getContentLocation())
    888                     && (null == part.getName())
    889                     && (null == part.getFilename())
    890                     && (null == part.getContentId())) {
    891                 part.setContentLocation(Long.toOctalString(
    892                         System.currentTimeMillis()).getBytes());
    893             }
    894 
    895             /* get part's data */
    896             if (dataLength > 0) {
    897                 byte[] partData = new byte[dataLength];
    898                 String partContentType = new String(part.getContentType());
    899                 pduDataStream.read(partData, 0, dataLength);
    900                 if (partContentType.equalsIgnoreCase(ContentType.MULTIPART_ALTERNATIVE)) {
    901                     // parse "multipart/vnd.wap.multipart.alternative".
    902                     PduBody childBody = parseParts(new ByteArrayInputStream(partData));
    903                     // take the first part of children.
    904                     part = childBody.getPart(0);
    905                 } else {
    906                     // Check Content-Transfer-Encoding.
    907                     byte[] partDataEncoding = part.getContentTransferEncoding();
    908                     if (null != partDataEncoding) {
    909                         String encoding = new String(partDataEncoding);
    910                         if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) {
    911                             // Decode "base64" into "binary".
    912                             partData = Base64.decodeBase64(partData);
    913                         } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) {
    914                             // Decode "quoted-printable" into "binary".
    915                             partData = QuotedPrintable.decodeQuotedPrintable(partData);
    916                         } else {
    917                             // "binary" is the default encoding.
    918                         }
    919                     }
    920                     if (null == partData) {
    921                         log("Decode part data error!");
    922                         return null;
    923                     }
    924                     part.setData(partData);
    925                 }
    926             }
    927 
    928             /* add this part to body */
    929             if (THE_FIRST_PART == checkPartPosition(part)) {
    930                 /* this is the first part */
    931                 body.addPart(0, part);
    932             } else {
    933                 /* add the part to the end */
    934                 body.addPart(part);
    935             }
    936         }
    937 
    938         return body;
    939     }
    940 
    941     /**
    942      * Log status.
    943      *
    944      * @param text log information
    945      */
    946     private static void log(String text) {
    947         if (LOCAL_LOGV) {
    948             Log.v(LOG_TAG, text);
    949         }
    950     }
    951 
    952     /**
    953      * Parse unsigned integer.
    954      *
    955      * @param pduDataStream pdu data input stream
    956      * @return the integer, -1 when failed
    957      */
    958     protected static int parseUnsignedInt(ByteArrayInputStream pduDataStream) {
    959         /**
    960          * From wap-230-wsp-20010705-a.pdf
    961          * The maximum size of a uintvar is 32 bits.
    962          * So it will be encoded in no more than 5 octets.
    963          */
    964         assert(null != pduDataStream);
    965         int result = 0;
    966         int temp = pduDataStream.read();
    967         if (temp == -1) {
    968             return temp;
    969         }
    970 
    971         while((temp & 0x80) != 0) {
    972             result = result << 7;
    973             result |= temp & 0x7F;
    974             temp = pduDataStream.read();
    975             if (temp == -1) {
    976                 return temp;
    977             }
    978         }
    979 
    980         result = result << 7;
    981         result |= temp & 0x7F;
    982 
    983         return result;
    984     }
    985 
    986     /**
    987      * Parse value length.
    988      *
    989      * @param pduDataStream pdu data input stream
    990      * @return the integer
    991      */
    992     protected static int parseValueLength(ByteArrayInputStream pduDataStream) {
    993         /**
    994          * From wap-230-wsp-20010705-a.pdf
    995          * Value-length = Short-length | (Length-quote Length)
    996          * Short-length = <Any octet 0-30>
    997          * Length-quote = <Octet 31>
    998          * Length = Uintvar-integer
    999          * Uintvar-integer = 1*5 OCTET
   1000          */
   1001         assert(null != pduDataStream);
   1002         int temp = pduDataStream.read();
   1003         assert(-1 != temp);
   1004         int first = temp & 0xFF;
   1005 
   1006         if (first <= SHORT_LENGTH_MAX) {
   1007             return first;
   1008         } else if (first == LENGTH_QUOTE) {
   1009             return parseUnsignedInt(pduDataStream);
   1010         }
   1011 
   1012         throw new RuntimeException("Value length > LENGTH_QUOTE!");
   1013     }
   1014 
   1015     /**
   1016      * Parse encoded string value.
   1017      *
   1018      * @param pduDataStream pdu data input stream
   1019      * @return the EncodedStringValue
   1020      */
   1021     protected static EncodedStringValue parseEncodedStringValue(ByteArrayInputStream pduDataStream){
   1022         /**
   1023          * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf
   1024          * Encoded-string-value = Text-string | Value-length Char-set Text-string
   1025          */
   1026         assert(null != pduDataStream);
   1027         pduDataStream.mark(1);
   1028         EncodedStringValue returnValue = null;
   1029         int charset = 0;
   1030         int temp = pduDataStream.read();
   1031         assert(-1 != temp);
   1032         int first = temp & 0xFF;
   1033         if (first == 0) {
   1034             return new EncodedStringValue("");
   1035         }
   1036 
   1037         pduDataStream.reset();
   1038         if (first < TEXT_MIN) {
   1039             parseValueLength(pduDataStream);
   1040 
   1041             charset = parseShortInteger(pduDataStream); //get the "Charset"
   1042         }
   1043 
   1044         byte[] textString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1045 
   1046         try {
   1047             if (0 != charset) {
   1048                 returnValue = new EncodedStringValue(charset, textString);
   1049             } else {
   1050                 returnValue = new EncodedStringValue(textString);
   1051             }
   1052         } catch(Exception e) {
   1053             return null;
   1054         }
   1055 
   1056         return returnValue;
   1057     }
   1058 
   1059     /**
   1060      * Parse Text-String or Quoted-String.
   1061      *
   1062      * @param pduDataStream pdu data input stream
   1063      * @param stringType TYPE_TEXT_STRING or TYPE_QUOTED_STRING
   1064      * @return the string without End-of-string in byte array
   1065      */
   1066     protected static byte[] parseWapString(ByteArrayInputStream pduDataStream,
   1067             int stringType) {
   1068         assert(null != pduDataStream);
   1069         /**
   1070          * From wap-230-wsp-20010705-a.pdf
   1071          * Text-string = [Quote] *TEXT End-of-string
   1072          * If the first character in the TEXT is in the range of 128-255,
   1073          * a Quote character must precede it.
   1074          * Otherwise the Quote character must be omitted.
   1075          * The Quote is not part of the contents.
   1076          * Quote = <Octet 127>
   1077          * End-of-string = <Octet 0>
   1078          *
   1079          * Quoted-string = <Octet 34> *TEXT End-of-string
   1080          *
   1081          * Token-text = Token End-of-string
   1082          */
   1083 
   1084         // Mark supposed beginning of Text-string
   1085         // We will have to mark again if first char is QUOTE or QUOTED_STRING_FLAG
   1086         pduDataStream.mark(1);
   1087 
   1088         // Check first char
   1089         int temp = pduDataStream.read();
   1090         assert(-1 != temp);
   1091         if ((TYPE_QUOTED_STRING == stringType) &&
   1092                 (QUOTED_STRING_FLAG == temp)) {
   1093             // Mark again if QUOTED_STRING_FLAG and ignore it
   1094             pduDataStream.mark(1);
   1095         } else if ((TYPE_TEXT_STRING == stringType) &&
   1096                 (QUOTE == temp)) {
   1097             // Mark again if QUOTE and ignore it
   1098             pduDataStream.mark(1);
   1099         } else {
   1100             // Otherwise go back to origin
   1101             pduDataStream.reset();
   1102         }
   1103 
   1104         // We are now definitely at the beginning of string
   1105         /**
   1106          * Return *TOKEN or *TEXT (Text-String without QUOTE,
   1107          * Quoted-String without QUOTED_STRING_FLAG and without End-of-string)
   1108          */
   1109         return getWapString(pduDataStream, stringType);
   1110     }
   1111 
   1112     /**
   1113      * Check TOKEN data defined in RFC2616.
   1114      * @param ch checking data
   1115      * @return true when ch is TOKEN, false when ch is not TOKEN
   1116      */
   1117     protected static boolean isTokenCharacter(int ch) {
   1118         /**
   1119          * Token      = 1*<any CHAR except CTLs or separators>
   1120          * separators = "("(40) | ")"(41) | "<"(60) | ">"(62) | "@"(64)
   1121          *            | ","(44) | ";"(59) | ":"(58) | "\"(92) | <">(34)
   1122          *            | "/"(47) | "["(91) | "]"(93) | "?"(63) | "="(61)
   1123          *            | "{"(123) | "}"(125) | SP(32) | HT(9)
   1124          * CHAR       = <any US-ASCII character (octets 0 - 127)>
   1125          * CTL        = <any US-ASCII control character
   1126          *            (octets 0 - 31) and DEL (127)>
   1127          * SP         = <US-ASCII SP, space (32)>
   1128          * HT         = <US-ASCII HT, horizontal-tab (9)>
   1129          */
   1130         if((ch < 33) || (ch > 126)) {
   1131             return false;
   1132         }
   1133 
   1134         switch(ch) {
   1135             case '"': /* '"' */
   1136             case '(': /* '(' */
   1137             case ')': /* ')' */
   1138             case ',': /* ',' */
   1139             case '/': /* '/' */
   1140             case ':': /* ':' */
   1141             case ';': /* ';' */
   1142             case '<': /* '<' */
   1143             case '=': /* '=' */
   1144             case '>': /* '>' */
   1145             case '?': /* '?' */
   1146             case '@': /* '@' */
   1147             case '[': /* '[' */
   1148             case '\\': /* '\' */
   1149             case ']': /* ']' */
   1150             case '{': /* '{' */
   1151             case '}': /* '}' */
   1152                 return false;
   1153         }
   1154 
   1155         return true;
   1156     }
   1157 
   1158     /**
   1159      * Check TEXT data defined in RFC2616.
   1160      * @param ch checking data
   1161      * @return true when ch is TEXT, false when ch is not TEXT
   1162      */
   1163     protected static boolean isText(int ch) {
   1164         /**
   1165          * TEXT = <any OCTET except CTLs,
   1166          *      but including LWS>
   1167          * CTL  = <any US-ASCII control character
   1168          *      (octets 0 - 31) and DEL (127)>
   1169          * LWS  = [CRLF] 1*( SP | HT )
   1170          * CRLF = CR LF
   1171          * CR   = <US-ASCII CR, carriage return (13)>
   1172          * LF   = <US-ASCII LF, linefeed (10)>
   1173          */
   1174         if(((ch >= 32) && (ch <= 126)) || ((ch >= 128) && (ch <= 255))) {
   1175             return true;
   1176         }
   1177 
   1178         switch(ch) {
   1179             case '\t': /* '\t' */
   1180             case '\n': /* '\n' */
   1181             case '\r': /* '\r' */
   1182                 return true;
   1183         }
   1184 
   1185         return false;
   1186     }
   1187 
   1188     protected static byte[] getWapString(ByteArrayInputStream pduDataStream,
   1189             int stringType) {
   1190         assert(null != pduDataStream);
   1191         ByteArrayOutputStream out = new ByteArrayOutputStream();
   1192         int temp = pduDataStream.read();
   1193         assert(-1 != temp);
   1194         while((-1 != temp) && ('\0' != temp)) {
   1195             // check each of the character
   1196             if (stringType == TYPE_TOKEN_STRING) {
   1197                 if (isTokenCharacter(temp)) {
   1198                     out.write(temp);
   1199                 }
   1200             } else {
   1201                 if (isText(temp)) {
   1202                     out.write(temp);
   1203                 }
   1204             }
   1205 
   1206             temp = pduDataStream.read();
   1207             assert(-1 != temp);
   1208         }
   1209 
   1210         if (out.size() > 0) {
   1211             return out.toByteArray();
   1212         }
   1213 
   1214         return null;
   1215     }
   1216 
   1217     /**
   1218      * Extract a byte value from the input stream.
   1219      *
   1220      * @param pduDataStream pdu data input stream
   1221      * @return the byte
   1222      */
   1223     protected static int extractByteValue(ByteArrayInputStream pduDataStream) {
   1224         assert(null != pduDataStream);
   1225         int temp = pduDataStream.read();
   1226         assert(-1 != temp);
   1227         return temp & 0xFF;
   1228     }
   1229 
   1230     /**
   1231      * Parse Short-Integer.
   1232      *
   1233      * @param pduDataStream pdu data input stream
   1234      * @return the byte
   1235      */
   1236     protected static int parseShortInteger(ByteArrayInputStream pduDataStream) {
   1237         /**
   1238          * From wap-230-wsp-20010705-a.pdf
   1239          * Short-integer = OCTET
   1240          * Integers in range 0-127 shall be encoded as a one
   1241          * octet value with the most significant bit set to one (1xxx xxxx)
   1242          * and with the value in the remaining least significant bits.
   1243          */
   1244         assert(null != pduDataStream);
   1245         int temp = pduDataStream.read();
   1246         assert(-1 != temp);
   1247         return temp & 0x7F;
   1248     }
   1249 
   1250     /**
   1251      * Parse Long-Integer.
   1252      *
   1253      * @param pduDataStream pdu data input stream
   1254      * @return long integer
   1255      */
   1256     protected static long parseLongInteger(ByteArrayInputStream pduDataStream) {
   1257         /**
   1258          * From wap-230-wsp-20010705-a.pdf
   1259          * Long-integer = Short-length Multi-octet-integer
   1260          * The Short-length indicates the length of the Multi-octet-integer
   1261          * Multi-octet-integer = 1*30 OCTET
   1262          * The content octets shall be an unsigned integer value
   1263          * with the most significant octet encoded first (big-endian representation).
   1264          * The minimum number of octets must be used to encode the value.
   1265          * Short-length = <Any octet 0-30>
   1266          */
   1267         assert(null != pduDataStream);
   1268         int temp = pduDataStream.read();
   1269         assert(-1 != temp);
   1270         int count = temp & 0xFF;
   1271 
   1272         if (count > LONG_INTEGER_LENGTH_MAX) {
   1273             throw new RuntimeException("Octet count greater than 8 and I can't represent that!");
   1274         }
   1275 
   1276         long result = 0;
   1277 
   1278         for (int i = 0 ; i < count ; i++) {
   1279             temp = pduDataStream.read();
   1280             assert(-1 != temp);
   1281             result <<= 8;
   1282             result += (temp & 0xFF);
   1283         }
   1284 
   1285         return result;
   1286     }
   1287 
   1288     /**
   1289      * Parse Integer-Value.
   1290      *
   1291      * @param pduDataStream pdu data input stream
   1292      * @return long integer
   1293      */
   1294     protected static long parseIntegerValue(ByteArrayInputStream pduDataStream) {
   1295         /**
   1296          * From wap-230-wsp-20010705-a.pdf
   1297          * Integer-Value = Short-integer | Long-integer
   1298          */
   1299         assert(null != pduDataStream);
   1300         pduDataStream.mark(1);
   1301         int temp = pduDataStream.read();
   1302         assert(-1 != temp);
   1303         pduDataStream.reset();
   1304         if (temp > SHORT_INTEGER_MAX) {
   1305             return parseShortInteger(pduDataStream);
   1306         } else {
   1307             return parseLongInteger(pduDataStream);
   1308         }
   1309     }
   1310 
   1311     /**
   1312      * To skip length of the wap value.
   1313      *
   1314      * @param pduDataStream pdu data input stream
   1315      * @param length area size
   1316      * @return the values in this area
   1317      */
   1318     protected static int skipWapValue(ByteArrayInputStream pduDataStream, int length) {
   1319         assert(null != pduDataStream);
   1320         byte[] area = new byte[length];
   1321         int readLen = pduDataStream.read(area, 0, length);
   1322         if (readLen < length) { //The actually read length is lower than the length
   1323             return -1;
   1324         } else {
   1325             return readLen;
   1326         }
   1327     }
   1328 
   1329     /**
   1330      * Parse content type parameters. For now we just support
   1331      * four parameters used in mms: "type", "start", "name", "charset".
   1332      *
   1333      * @param pduDataStream pdu data input stream
   1334      * @param map to store parameters of Content-Type field
   1335      * @param length length of all the parameters
   1336      */
   1337     protected static void parseContentTypeParams(ByteArrayInputStream pduDataStream,
   1338             HashMap<Integer, Object> map, Integer length) {
   1339         /**
   1340          * From wap-230-wsp-20010705-a.pdf
   1341          * Parameter = Typed-parameter | Untyped-parameter
   1342          * Typed-parameter = Well-known-parameter-token Typed-value
   1343          * the actual expected type of the value is implied by the well-known parameter
   1344          * Well-known-parameter-token = Integer-value
   1345          * the code values used for parameters are specified in the Assigned Numbers appendix
   1346          * Typed-value = Compact-value | Text-value
   1347          * In addition to the expected type, there may be no value.
   1348          * If the value cannot be encoded using the expected type, it shall be encoded as text.
   1349          * Compact-value = Integer-value |
   1350          * Date-value | Delta-seconds-value | Q-value | Version-value |
   1351          * Uri-value
   1352          * Untyped-parameter = Token-text Untyped-value
   1353          * the type of the value is unknown, but it shall be encoded as an integer,
   1354          * if that is possible.
   1355          * Untyped-value = Integer-value | Text-value
   1356          */
   1357         assert(null != pduDataStream);
   1358         assert(length > 0);
   1359 
   1360         int startPos = pduDataStream.available();
   1361         int tempPos = 0;
   1362         int lastLen = length;
   1363         while(0 < lastLen) {
   1364             int param = pduDataStream.read();
   1365             assert(-1 != param);
   1366             lastLen--;
   1367 
   1368             switch (param) {
   1369                 /**
   1370                  * From rfc2387, chapter 3.1
   1371                  * The type parameter must be specified and its value is the MIME media
   1372                  * type of the "root" body part. It permits a MIME user agent to
   1373                  * determine the content-type without reference to the enclosed body
   1374                  * part. If the value of the type parameter and the root body part's
   1375                  * content-type differ then the User Agent's behavior is undefined.
   1376                  *
   1377                  * From wap-230-wsp-20010705-a.pdf
   1378                  * type = Constrained-encoding
   1379                  * Constrained-encoding = Extension-Media | Short-integer
   1380                  * Extension-media = *TEXT End-of-string
   1381                  */
   1382                 case PduPart.P_TYPE:
   1383                 case PduPart.P_CT_MR_TYPE:
   1384                     pduDataStream.mark(1);
   1385                     int first = extractByteValue(pduDataStream);
   1386                     pduDataStream.reset();
   1387                     if (first > TEXT_MAX) {
   1388                         // Short-integer (well-known type)
   1389                         int index = parseShortInteger(pduDataStream);
   1390 
   1391                         if (index < PduContentTypes.contentTypes.length) {
   1392                             byte[] type = (PduContentTypes.contentTypes[index]).getBytes();
   1393                             map.put(PduPart.P_TYPE, type);
   1394                         } else {
   1395                             //not support this type, ignore it.
   1396                         }
   1397                     } else {
   1398                         // Text-String (extension-media)
   1399                         byte[] type = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1400                         if ((null != type) && (null != map)) {
   1401                             map.put(PduPart.P_TYPE, type);
   1402                         }
   1403                     }
   1404 
   1405                     tempPos = pduDataStream.available();
   1406                     lastLen = length - (startPos - tempPos);
   1407                     break;
   1408 
   1409                     /**
   1410                      * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.3.
   1411                      * Start Parameter Referring to Presentation
   1412                      *
   1413                      * From rfc2387, chapter 3.2
   1414                      * The start parameter, if given, is the content-ID of the compound
   1415                      * object's "root". If not present the "root" is the first body part in
   1416                      * the Multipart/Related entity. The "root" is the element the
   1417                      * applications processes first.
   1418                      *
   1419                      * From wap-230-wsp-20010705-a.pdf
   1420                      * start = Text-String
   1421                      */
   1422                 case PduPart.P_START:
   1423                 case PduPart.P_DEP_START:
   1424                     byte[] start = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1425                     if ((null != start) && (null != map)) {
   1426                         map.put(PduPart.P_START, start);
   1427                     }
   1428 
   1429                     tempPos = pduDataStream.available();
   1430                     lastLen = length - (startPos - tempPos);
   1431                     break;
   1432 
   1433                     /**
   1434                      * From oma-ts-mms-conf-v1_3.pdf
   1435                      * In creation, the character set SHALL be either us-ascii
   1436                      * (IANA MIBenum 3) or utf-8 (IANA MIBenum 106)[Unicode].
   1437                      * In retrieval, both us-ascii and utf-8 SHALL be supported.
   1438                      *
   1439                      * From wap-230-wsp-20010705-a.pdf
   1440                      * charset = Well-known-charset|Text-String
   1441                      * Well-known-charset = Any-charset | Integer-value
   1442                      * Both are encoded using values from Character Set
   1443                      * Assignments table in Assigned Numbers
   1444                      * Any-charset = <Octet 128>
   1445                      * Equivalent to the special RFC2616 charset value "*"
   1446                      */
   1447                 case PduPart.P_CHARSET:
   1448                     pduDataStream.mark(1);
   1449                     int firstValue = extractByteValue(pduDataStream);
   1450                     pduDataStream.reset();
   1451                     //Check first char
   1452                     if (((firstValue > TEXT_MIN) && (firstValue < TEXT_MAX)) ||
   1453                             (END_STRING_FLAG == firstValue)) {
   1454                         //Text-String (extension-charset)
   1455                         byte[] charsetStr = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1456                         try {
   1457                             int charsetInt = CharacterSets.getMibEnumValue(
   1458                                     new String(charsetStr));
   1459                             map.put(PduPart.P_CHARSET, charsetInt);
   1460                         } catch (UnsupportedEncodingException e) {
   1461                             // Not a well-known charset, use "*".
   1462                             Log.e(LOG_TAG, Arrays.toString(charsetStr), e);
   1463                             map.put(PduPart.P_CHARSET, CharacterSets.ANY_CHARSET);
   1464                         }
   1465                     } else {
   1466                         //Well-known-charset
   1467                         int charset = (int) parseIntegerValue(pduDataStream);
   1468                         if (map != null) {
   1469                             map.put(PduPart.P_CHARSET, charset);
   1470                         }
   1471                     }
   1472 
   1473                     tempPos = pduDataStream.available();
   1474                     lastLen = length - (startPos - tempPos);
   1475                     break;
   1476 
   1477                     /**
   1478                      * From oma-ts-mms-conf-v1_3.pdf
   1479                      * A name for multipart object SHALL be encoded using name-parameter
   1480                      * for Content-Type header in WSP multipart headers.
   1481                      *
   1482                      * From wap-230-wsp-20010705-a.pdf
   1483                      * name = Text-String
   1484                      */
   1485                 case PduPart.P_DEP_NAME:
   1486                 case PduPart.P_NAME:
   1487                     byte[] name = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1488                     if ((null != name) && (null != map)) {
   1489                         map.put(PduPart.P_NAME, name);
   1490                     }
   1491 
   1492                     tempPos = pduDataStream.available();
   1493                     lastLen = length - (startPos - tempPos);
   1494                     break;
   1495                 default:
   1496                     if (LOCAL_LOGV) {
   1497                         Log.v(LOG_TAG, "Not supported Content-Type parameter");
   1498                     }
   1499                 if (-1 == skipWapValue(pduDataStream, lastLen)) {
   1500                     Log.e(LOG_TAG, "Corrupt Content-Type");
   1501                 } else {
   1502                     lastLen = 0;
   1503                 }
   1504                 break;
   1505             }
   1506         }
   1507 
   1508         if (0 != lastLen) {
   1509             Log.e(LOG_TAG, "Corrupt Content-Type");
   1510         }
   1511     }
   1512 
   1513     /**
   1514      * Parse content type.
   1515      *
   1516      * @param pduDataStream pdu data input stream
   1517      * @param map to store parameters in Content-Type header field
   1518      * @return Content-Type value
   1519      */
   1520     protected static byte[] parseContentType(ByteArrayInputStream pduDataStream,
   1521             HashMap<Integer, Object> map) {
   1522         /**
   1523          * From wap-230-wsp-20010705-a.pdf
   1524          * Content-type-value = Constrained-media | Content-general-form
   1525          * Content-general-form = Value-length Media-type
   1526          * Media-type = (Well-known-media | Extension-Media) *(Parameter)
   1527          */
   1528         assert(null != pduDataStream);
   1529 
   1530         byte[] contentType = null;
   1531         pduDataStream.mark(1);
   1532         int temp = pduDataStream.read();
   1533         assert(-1 != temp);
   1534         pduDataStream.reset();
   1535 
   1536         int cur = (temp & 0xFF);
   1537 
   1538         if (cur < TEXT_MIN) {
   1539             int length = parseValueLength(pduDataStream);
   1540             int startPos = pduDataStream.available();
   1541             pduDataStream.mark(1);
   1542             temp = pduDataStream.read();
   1543             assert(-1 != temp);
   1544             pduDataStream.reset();
   1545             int first = (temp & 0xFF);
   1546 
   1547             if ((first >= TEXT_MIN) && (first <= TEXT_MAX)) {
   1548                 contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1549             } else if (first > TEXT_MAX) {
   1550                 int index = parseShortInteger(pduDataStream);
   1551 
   1552                 if (index < PduContentTypes.contentTypes.length) { //well-known type
   1553                     contentType = (PduContentTypes.contentTypes[index]).getBytes();
   1554                 } else {
   1555                     pduDataStream.reset();
   1556                     contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1557                 }
   1558             } else {
   1559                 Log.e(LOG_TAG, "Corrupt content-type");
   1560                 return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
   1561             }
   1562 
   1563             int endPos = pduDataStream.available();
   1564             int parameterLen = length - (startPos - endPos);
   1565             if (parameterLen > 0) {//have parameters
   1566                 parseContentTypeParams(pduDataStream, map, parameterLen);
   1567             }
   1568 
   1569             if (parameterLen < 0) {
   1570                 Log.e(LOG_TAG, "Corrupt MMS message");
   1571                 return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
   1572             }
   1573         } else if (cur <= TEXT_MAX) {
   1574             contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1575         } else {
   1576             contentType =
   1577                 (PduContentTypes.contentTypes[parseShortInteger(pduDataStream)]).getBytes();
   1578         }
   1579 
   1580         return contentType;
   1581     }
   1582 
   1583     /**
   1584      * Parse part's headers.
   1585      *
   1586      * @param pduDataStream pdu data input stream
   1587      * @param part to store the header informations of the part
   1588      * @param length length of the headers
   1589      * @return true if parse successfully, false otherwise
   1590      */
   1591     protected boolean parsePartHeaders(ByteArrayInputStream pduDataStream,
   1592             PduPart part, int length) {
   1593         assert(null != pduDataStream);
   1594         assert(null != part);
   1595         assert(length > 0);
   1596 
   1597         /**
   1598          * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.
   1599          * A name for multipart object SHALL be encoded using name-parameter
   1600          * for Content-Type header in WSP multipart headers.
   1601          * In decoding, name-parameter of Content-Type SHALL be used if available.
   1602          * If name-parameter of Content-Type is not available,
   1603          * filename parameter of Content-Disposition header SHALL be used if available.
   1604          * If neither name-parameter of Content-Type header nor filename parameter
   1605          * of Content-Disposition header is available,
   1606          * Content-Location header SHALL be used if available.
   1607          *
   1608          * Within SMIL part the reference to the media object parts SHALL use
   1609          * either Content-ID or Content-Location mechanism [RFC2557]
   1610          * and the corresponding WSP part headers in media object parts
   1611          * contain the corresponding definitions.
   1612          */
   1613         int startPos = pduDataStream.available();
   1614         int tempPos = 0;
   1615         int lastLen = length;
   1616         while(0 < lastLen) {
   1617             int header = pduDataStream.read();
   1618             assert(-1 != header);
   1619             lastLen--;
   1620 
   1621             if (header > TEXT_MAX) {
   1622                 // Number assigned headers.
   1623                 switch (header) {
   1624                     case PduPart.P_CONTENT_LOCATION:
   1625                         /**
   1626                          * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
   1627                          * Content-location-value = Uri-value
   1628                          */
   1629                         byte[] contentLocation = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1630                         if (null != contentLocation) {
   1631                             part.setContentLocation(contentLocation);
   1632                         }
   1633 
   1634                         tempPos = pduDataStream.available();
   1635                         lastLen = length - (startPos - tempPos);
   1636                         break;
   1637                     case PduPart.P_CONTENT_ID:
   1638                         /**
   1639                          * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
   1640                          * Content-ID-value = Quoted-string
   1641                          */
   1642                         byte[] contentId = parseWapString(pduDataStream, TYPE_QUOTED_STRING);
   1643                         if (null != contentId) {
   1644                             part.setContentId(contentId);
   1645                         }
   1646 
   1647                         tempPos = pduDataStream.available();
   1648                         lastLen = length - (startPos - tempPos);
   1649                         break;
   1650                     case PduPart.P_DEP_CONTENT_DISPOSITION:
   1651                     case PduPart.P_CONTENT_DISPOSITION:
   1652                         /**
   1653                          * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
   1654                          * Content-disposition-value = Value-length Disposition *(Parameter)
   1655                          * Disposition = Form-data | Attachment | Inline | Token-text
   1656                          * Form-data = <Octet 128>
   1657                          * Attachment = <Octet 129>
   1658                          * Inline = <Octet 130>
   1659                          */
   1660 
   1661                         /*
   1662                          * some carrier mmsc servers do not support content_disposition
   1663                          * field correctly
   1664                          */
   1665                         if (mParseContentDisposition) {
   1666                             int len = parseValueLength(pduDataStream);
   1667                             pduDataStream.mark(1);
   1668                             int thisStartPos = pduDataStream.available();
   1669                             int thisEndPos = 0;
   1670                             int value = pduDataStream.read();
   1671 
   1672                             if (value == PduPart.P_DISPOSITION_FROM_DATA ) {
   1673                                 part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA);
   1674                             } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) {
   1675                                 part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT);
   1676                             } else if (value == PduPart.P_DISPOSITION_INLINE) {
   1677                                 part.setContentDisposition(PduPart.DISPOSITION_INLINE);
   1678                             } else {
   1679                                 pduDataStream.reset();
   1680                                 /* Token-text */
   1681                                 part.setContentDisposition(parseWapString(pduDataStream
   1682                                         , TYPE_TEXT_STRING));
   1683                             }
   1684 
   1685                             /* get filename parameter and skip other parameters */
   1686                             thisEndPos = pduDataStream.available();
   1687                             if (thisStartPos - thisEndPos < len) {
   1688                                 value = pduDataStream.read();
   1689                                 if (value == PduPart.P_FILENAME) { //filename is text-string
   1690                                     part.setFilename(parseWapString(pduDataStream
   1691                                             , TYPE_TEXT_STRING));
   1692                                 }
   1693 
   1694                                 /* skip other parameters */
   1695                                 thisEndPos = pduDataStream.available();
   1696                                 if (thisStartPos - thisEndPos < len) {
   1697                                     int last = len - (thisStartPos - thisEndPos);
   1698                                     byte[] temp = new byte[last];
   1699                                     pduDataStream.read(temp, 0, last);
   1700                                 }
   1701                             }
   1702 
   1703                             tempPos = pduDataStream.available();
   1704                             lastLen = length - (startPos - tempPos);
   1705                         }
   1706                         break;
   1707                     default:
   1708                         if (LOCAL_LOGV) {
   1709                             Log.v(LOG_TAG, "Not supported Part headers: " + header);
   1710                         }
   1711                     if (-1 == skipWapValue(pduDataStream, lastLen)) {
   1712                         Log.e(LOG_TAG, "Corrupt Part headers");
   1713                         return false;
   1714                     }
   1715                     lastLen = 0;
   1716                     break;
   1717                 }
   1718             } else if ((header >= TEXT_MIN) && (header <= TEXT_MAX)) {
   1719                 // Not assigned header.
   1720                 byte[] tempHeader = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1721                 byte[] tempValue = parseWapString(pduDataStream, TYPE_TEXT_STRING);
   1722 
   1723                 // Check the header whether it is "Content-Transfer-Encoding".
   1724                 if (true ==
   1725                     PduPart.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(new String(tempHeader))) {
   1726                     part.setContentTransferEncoding(tempValue);
   1727                 }
   1728 
   1729                 tempPos = pduDataStream.available();
   1730                 lastLen = length - (startPos - tempPos);
   1731             } else {
   1732                 if (LOCAL_LOGV) {
   1733                     Log.v(LOG_TAG, "Not supported Part headers: " + header);
   1734                 }
   1735                 // Skip all headers of this part.
   1736                 if (-1 == skipWapValue(pduDataStream, lastLen)) {
   1737                     Log.e(LOG_TAG, "Corrupt Part headers");
   1738                     return false;
   1739                 }
   1740                 lastLen = 0;
   1741             }
   1742         }
   1743 
   1744         if (0 != lastLen) {
   1745             Log.e(LOG_TAG, "Corrupt Part headers");
   1746             return false;
   1747         }
   1748 
   1749         return true;
   1750     }
   1751 
   1752     /**
   1753      * Check the position of a specified part.
   1754      *
   1755      * @param part the part to be checked
   1756      * @return part position, THE_FIRST_PART when it's the
   1757      * first one, THE_LAST_PART when it's the last one.
   1758      */
   1759     private static int checkPartPosition(PduPart part) {
   1760         assert(null != part);
   1761         if ((null == mTypeParam) &&
   1762                 (null == mStartParam)) {
   1763             return THE_LAST_PART;
   1764         }
   1765 
   1766         /* check part's content-id */
   1767         if (null != mStartParam) {
   1768             byte[] contentId = part.getContentId();
   1769             if (null != contentId) {
   1770                 if (true == Arrays.equals(mStartParam, contentId)) {
   1771                     return THE_FIRST_PART;
   1772                 }
   1773             }
   1774             // This is not the first part, so append to end (keeping the original order)
   1775             // Check b/19607294 for details of this change
   1776             return THE_LAST_PART;
   1777         }
   1778 
   1779         /* check part's content-type */
   1780         if (null != mTypeParam) {
   1781             byte[] contentType = part.getContentType();
   1782             if (null != contentType) {
   1783                 if (true == Arrays.equals(mTypeParam, contentType)) {
   1784                     return THE_FIRST_PART;
   1785                 }
   1786             }
   1787         }
   1788 
   1789         return THE_LAST_PART;
   1790     }
   1791 
   1792     /**
   1793      * Check mandatory headers of a pdu.
   1794      *
   1795      * @param headers pdu headers
   1796      * @return true if the pdu has all of the mandatory headers, false otherwise.
   1797      */
   1798     protected static boolean checkMandatoryHeader(PduHeaders headers) {
   1799         if (null == headers) {
   1800             return false;
   1801         }
   1802 
   1803         /* get message type */
   1804         int messageType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
   1805 
   1806         /* check Mms-Version field */
   1807         int mmsVersion = headers.getOctet(PduHeaders.MMS_VERSION);
   1808         if (0 == mmsVersion) {
   1809             // Every message should have Mms-Version field.
   1810             return false;
   1811         }
   1812 
   1813         /* check mandatory header fields */
   1814         switch (messageType) {
   1815             case PduHeaders.MESSAGE_TYPE_SEND_REQ:
   1816                 // Content-Type field.
   1817                 byte[] srContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
   1818                 if (null == srContentType) {
   1819                     return false;
   1820                 }
   1821 
   1822                 // From field.
   1823                 EncodedStringValue srFrom = headers.getEncodedStringValue(PduHeaders.FROM);
   1824                 if (null == srFrom) {
   1825                     return false;
   1826                 }
   1827 
   1828                 // Transaction-Id field.
   1829                 byte[] srTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
   1830                 if (null == srTransactionId) {
   1831                     return false;
   1832                 }
   1833 
   1834                 break;
   1835             case PduHeaders.MESSAGE_TYPE_SEND_CONF:
   1836                 // Response-Status field.
   1837                 int scResponseStatus = headers.getOctet(PduHeaders.RESPONSE_STATUS);
   1838                 if (0 == scResponseStatus) {
   1839                     return false;
   1840                 }
   1841 
   1842                 // Transaction-Id field.
   1843                 byte[] scTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
   1844                 if (null == scTransactionId) {
   1845                     return false;
   1846                 }
   1847 
   1848                 break;
   1849             case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
   1850                 // Content-Location field.
   1851                 byte[] niContentLocation = headers.getTextString(PduHeaders.CONTENT_LOCATION);
   1852                 if (null == niContentLocation) {
   1853                     return false;
   1854                 }
   1855 
   1856                 // Expiry field.
   1857                 long niExpiry = headers.getLongInteger(PduHeaders.EXPIRY);
   1858                 if (-1 == niExpiry) {
   1859                     return false;
   1860                 }
   1861 
   1862                 // Message-Class field.
   1863                 byte[] niMessageClass = headers.getTextString(PduHeaders.MESSAGE_CLASS);
   1864                 if (null == niMessageClass) {
   1865                     return false;
   1866                 }
   1867 
   1868                 // Message-Size field.
   1869                 long niMessageSize = headers.getLongInteger(PduHeaders.MESSAGE_SIZE);
   1870                 if (-1 == niMessageSize) {
   1871                     return false;
   1872                 }
   1873 
   1874                 // Transaction-Id field.
   1875                 byte[] niTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
   1876                 if (null == niTransactionId) {
   1877                     return false;
   1878                 }
   1879 
   1880                 break;
   1881             case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
   1882                 // Status field.
   1883                 int nriStatus = headers.getOctet(PduHeaders.STATUS);
   1884                 if (0 == nriStatus) {
   1885                     return false;
   1886                 }
   1887 
   1888                 // Transaction-Id field.
   1889                 byte[] nriTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
   1890                 if (null == nriTransactionId) {
   1891                     return false;
   1892                 }
   1893 
   1894                 break;
   1895             case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
   1896                 // Content-Type field.
   1897                 byte[] rcContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
   1898                 if (null == rcContentType) {
   1899                     return false;
   1900                 }
   1901 
   1902                 // Date field.
   1903                 long rcDate = headers.getLongInteger(PduHeaders.DATE);
   1904                 if (-1 == rcDate) {
   1905                     return false;
   1906                 }
   1907 
   1908                 break;
   1909             case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
   1910                 // Date field.
   1911                 long diDate = headers.getLongInteger(PduHeaders.DATE);
   1912                 if (-1 == diDate) {
   1913                     return false;
   1914                 }
   1915 
   1916                 // Message-Id field.
   1917                 byte[] diMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
   1918                 if (null == diMessageId) {
   1919                     return false;
   1920                 }
   1921 
   1922                 // Status field.
   1923                 int diStatus = headers.getOctet(PduHeaders.STATUS);
   1924                 if (0 == diStatus) {
   1925                     return false;
   1926                 }
   1927 
   1928                 // To field.
   1929                 EncodedStringValue[] diTo = headers.getEncodedStringValues(PduHeaders.TO);
   1930                 if (null == diTo) {
   1931                     return false;
   1932                 }
   1933 
   1934                 break;
   1935             case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
   1936                 // Transaction-Id field.
   1937                 byte[] aiTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
   1938                 if (null == aiTransactionId) {
   1939                     return false;
   1940                 }
   1941 
   1942                 break;
   1943             case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
   1944                 // Date field.
   1945                 long roDate = headers.getLongInteger(PduHeaders.DATE);
   1946                 if (-1 == roDate) {
   1947                     return false;
   1948                 }
   1949 
   1950                 // From field.
   1951                 EncodedStringValue roFrom = headers.getEncodedStringValue(PduHeaders.FROM);
   1952                 if (null == roFrom) {
   1953                     return false;
   1954                 }
   1955 
   1956                 // Message-Id field.
   1957                 byte[] roMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
   1958                 if (null == roMessageId) {
   1959                     return false;
   1960                 }
   1961 
   1962                 // Read-Status field.
   1963                 int roReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
   1964                 if (0 == roReadStatus) {
   1965                     return false;
   1966                 }
   1967 
   1968                 // To field.
   1969                 EncodedStringValue[] roTo = headers.getEncodedStringValues(PduHeaders.TO);
   1970                 if (null == roTo) {
   1971                     return false;
   1972                 }
   1973 
   1974                 break;
   1975             case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
   1976                 // From field.
   1977                 EncodedStringValue rrFrom = headers.getEncodedStringValue(PduHeaders.FROM);
   1978                 if (null == rrFrom) {
   1979                     return false;
   1980                 }
   1981 
   1982                 // Message-Id field.
   1983                 byte[] rrMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
   1984                 if (null == rrMessageId) {
   1985                     return false;
   1986                 }
   1987 
   1988                 // Read-Status field.
   1989                 int rrReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
   1990                 if (0 == rrReadStatus) {
   1991                     return false;
   1992                 }
   1993 
   1994                 // To field.
   1995                 EncodedStringValue[] rrTo = headers.getEncodedStringValues(PduHeaders.TO);
   1996                 if (null == rrTo) {
   1997                     return false;
   1998                 }
   1999 
   2000                 break;
   2001             default:
   2002                 // Parser doesn't support this message type in this version.
   2003                 return false;
   2004         }
   2005 
   2006         return true;
   2007     }
   2008 }
   2009