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