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