1 /*
2  * Copyright (C) 2007-2008 Esmertec AG.
3  * Copyright (C) 2007-2008 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.google.android.mms.pdu;
19 
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.text.TextUtils;
24 
25 import java.io.ByteArrayOutputStream;
26 import java.io.FileNotFoundException;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.util.Arrays;
30 import java.util.HashMap;
31 
32 public class PduComposer {
33     /**
34      * Address type.
35      */
36     static private final int PDU_PHONE_NUMBER_ADDRESS_TYPE = 1;
37     static private final int PDU_EMAIL_ADDRESS_TYPE = 2;
38     static private final int PDU_IPV4_ADDRESS_TYPE = 3;
39     static private final int PDU_IPV6_ADDRESS_TYPE = 4;
40     static private final int PDU_UNKNOWN_ADDRESS_TYPE = 5;
41 
42     /**
43      * Address regular expression string.
44      */
45     static final String REGEXP_PHONE_NUMBER_ADDRESS_TYPE = "\\+?[0-9|\\.|\\-]+";
46     static final String REGEXP_EMAIL_ADDRESS_TYPE = "[a-zA-Z| ]*\\<{0,1}[a-zA-Z| ]+@{1}" +
47             "[a-zA-Z| ]+\\.{1}[a-zA-Z| ]+\\>{0,1}";
48     static final String REGEXP_IPV6_ADDRESS_TYPE =
49         "[a-fA-F]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
50         "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
51         "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}";
52     static final String REGEXP_IPV4_ADDRESS_TYPE = "[0-9]{1,3}\\.{1}[0-9]{1,3}\\.{1}" +
53             "[0-9]{1,3}\\.{1}[0-9]{1,3}";
54 
55     /**
56      * The postfix strings of address.
57      */
58     static final String STRING_PHONE_NUMBER_ADDRESS_TYPE = "/TYPE=PLMN";
59     static final String STRING_IPV4_ADDRESS_TYPE = "/TYPE=IPV4";
60     static final String STRING_IPV6_ADDRESS_TYPE = "/TYPE=IPV6";
61 
62     /**
63      * Error values.
64      */
65     static private final int PDU_COMPOSE_SUCCESS = 0;
66     static private final int PDU_COMPOSE_CONTENT_ERROR = 1;
67     static private final int PDU_COMPOSE_FIELD_NOT_SET = 2;
68     static private final int PDU_COMPOSE_FIELD_NOT_SUPPORTED = 3;
69 
70     /**
71      * WAP values defined in WSP spec.
72      */
73     static private final int QUOTED_STRING_FLAG = 34;
74     static private final int END_STRING_FLAG = 0;
75     static private final int LENGTH_QUOTE = 31;
76     static private final int TEXT_MAX = 127;
77     static private final int SHORT_INTEGER_MAX = 127;
78     static private final int LONG_INTEGER_LENGTH_MAX = 8;
79 
80     /**
81      * Block size when read data from InputStream.
82      */
83     static private final int PDU_COMPOSER_BLOCK_SIZE = 1024;
84 
85     /**
86      * The output message.
87      */
88     @UnsupportedAppUsage
89     protected ByteArrayOutputStream mMessage = null;
90 
91     /**
92      * The PDU.
93      */
94     @UnsupportedAppUsage
95     private GenericPdu mPdu = null;
96 
97     /**
98      * Current visiting position of the mMessage.
99      */
100     @UnsupportedAppUsage
101     protected int mPosition = 0;
102 
103     /**
104      * Message compose buffer stack.
105      */
106     @UnsupportedAppUsage
107     private BufferStack mStack = null;
108 
109     /**
110      * Content resolver.
111      */
112     @UnsupportedAppUsage
113     private final ContentResolver mResolver;
114 
115     /**
116      * Header of this pdu.
117      */
118     @UnsupportedAppUsage
119     private PduHeaders mPduHeader = null;
120 
121     /**
122      * Map of all content type
123      */
124     @UnsupportedAppUsage
125     private static HashMap<String, Integer> mContentTypeMap = null;
126 
127     static {
128         mContentTypeMap = new HashMap<String, Integer>();
129 
130         int i;
131         for (i = 0; i < PduContentTypes.contentTypes.length; i++) {
mContentTypeMap.put(PduContentTypes.contentTypes[i], i)132             mContentTypeMap.put(PduContentTypes.contentTypes[i], i);
133         }
134     }
135 
136     /**
137      * Constructor.
138      *
139      * @param context the context
140      * @param pdu the pdu to be composed
141      */
142     @UnsupportedAppUsage
PduComposer(Context context, GenericPdu pdu)143     public PduComposer(Context context, GenericPdu pdu) {
144         mPdu = pdu;
145         mResolver = context.getContentResolver();
146         mPduHeader = pdu.getPduHeaders();
147         mStack = new BufferStack();
148         mMessage = new ByteArrayOutputStream();
149         mPosition = 0;
150     }
151 
152     /**
153      * Make the message. No need to check whether mandatory fields are set,
154      * because the constructors of outgoing pdus are taking care of this.
155      *
156      * @return OutputStream of maked message. Return null if
157      *         the PDU is invalid.
158      */
159     @UnsupportedAppUsage
make()160     public byte[] make() {
161         // Get Message-type.
162         int type = mPdu.getMessageType();
163 
164         /* make the message */
165         switch (type) {
166             case PduHeaders.MESSAGE_TYPE_SEND_REQ:
167             case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
168                 if (makeSendRetrievePdu(type) != PDU_COMPOSE_SUCCESS) {
169                     return null;
170                 }
171                 break;
172             case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
173                 if (makeNotifyResp() != PDU_COMPOSE_SUCCESS) {
174                     return null;
175                 }
176                 break;
177             case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
178                 if (makeAckInd() != PDU_COMPOSE_SUCCESS) {
179                     return null;
180                 }
181                 break;
182             case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
183                 if (makeReadRecInd() != PDU_COMPOSE_SUCCESS) {
184                     return null;
185                 }
186                 break;
187             default:
188                 return null;
189         }
190 
191         return mMessage.toByteArray();
192     }
193 
194     /**
195      *  Copy buf to mMessage.
196      */
197     @UnsupportedAppUsage
arraycopy(byte[] buf, int pos, int length)198     protected void arraycopy(byte[] buf, int pos, int length) {
199         mMessage.write(buf, pos, length);
200         mPosition = mPosition + length;
201     }
202 
203     /**
204      * Append a byte to mMessage.
205      */
append(int value)206     protected void append(int value) {
207         mMessage.write(value);
208         mPosition ++;
209     }
210 
211     /**
212      * Append short integer value to mMessage.
213      * This implementation doesn't check the validity of parameter, since it
214      * assumes that the values are validated in the GenericPdu setter methods.
215      */
216     @UnsupportedAppUsage
appendShortInteger(int value)217     protected void appendShortInteger(int value) {
218         /*
219          * From WAP-230-WSP-20010705-a:
220          * Short-integer = OCTET
221          * ; Integers in range 0-127 shall be encoded as a one octet value
222          * ; with the most significant bit set to one (1xxx xxxx) and with
223          * ; the value in the remaining least significant bits.
224          * In our implementation, only low 7 bits are stored and otherwise
225          * bits are ignored.
226          */
227         append((value | 0x80) & 0xff);
228     }
229 
230     /**
231      * Append an octet number between 128 and 255 into mMessage.
232      * NOTE:
233      * A value between 0 and 127 should be appended by using appendShortInteger.
234      * This implementation doesn't check the validity of parameter, since it
235      * assumes that the values are validated in the GenericPdu setter methods.
236      */
237     @UnsupportedAppUsage
appendOctet(int number)238     protected void appendOctet(int number) {
239         append(number);
240     }
241 
242     /**
243      * Append a short length into mMessage.
244      * This implementation doesn't check the validity of parameter, since it
245      * assumes that the values are validated in the GenericPdu setter methods.
246      */
appendShortLength(int value)247     protected void appendShortLength(int value) {
248         /*
249          * From WAP-230-WSP-20010705-a:
250          * Short-length = <Any octet 0-30>
251          */
252         append(value);
253     }
254 
255     /**
256      * Append long integer into mMessage. it's used for really long integers.
257      * This implementation doesn't check the validity of parameter, since it
258      * assumes that the values are validated in the GenericPdu setter methods.
259      */
260     @UnsupportedAppUsage
appendLongInteger(long longInt)261     protected void appendLongInteger(long longInt) {
262         /*
263          * From WAP-230-WSP-20010705-a:
264          * Long-integer = Short-length Multi-octet-integer
265          * ; The Short-length indicates the length of the Multi-octet-integer
266          * Multi-octet-integer = 1*30 OCTET
267          * ; The content octets shall be an unsigned integer value with the
268          * ; most significant octet encoded first (big-endian representation).
269          * ; The minimum number of octets must be used to encode the value.
270          */
271         int size;
272         long temp = longInt;
273 
274         // Count the length of the long integer.
275         for(size = 0; (temp != 0) && (size < LONG_INTEGER_LENGTH_MAX); size++) {
276             temp = (temp >>> 8);
277         }
278 
279         // Set Length.
280         appendShortLength(size);
281 
282         // Count and set the long integer.
283         int i;
284         int shift = (size -1) * 8;
285 
286         for (i = 0; i < size; i++) {
287             append((int)((longInt >>> shift) & 0xff));
288             shift = shift - 8;
289         }
290     }
291 
292     /**
293      * Append text string into mMessage.
294      * This implementation doesn't check the validity of parameter, since it
295      * assumes that the values are validated in the GenericPdu setter methods.
296      */
297     @UnsupportedAppUsage
appendTextString(byte[] text)298     protected void appendTextString(byte[] text) {
299         /*
300          * From WAP-230-WSP-20010705-a:
301          * Text-string = [Quote] *TEXT End-of-string
302          * ; If the first character in the TEXT is in the range of 128-255,
303          * ; a Quote character must precede it. Otherwise the Quote character
304          * ;must be omitted. The Quote is not part of the contents.
305          */
306         if (((text[0])&0xff) > TEXT_MAX) { // No need to check for <= 255
307             append(TEXT_MAX);
308         }
309 
310         arraycopy(text, 0, text.length);
311         append(0);
312     }
313 
314     /**
315      * Append text string into mMessage.
316      * This implementation doesn't check the validity of parameter, since it
317      * assumes that the values are validated in the GenericPdu setter methods.
318      */
319     @UnsupportedAppUsage
appendTextString(String str)320     protected void appendTextString(String str) {
321         /*
322          * From WAP-230-WSP-20010705-a:
323          * Text-string = [Quote] *TEXT End-of-string
324          * ; If the first character in the TEXT is in the range of 128-255,
325          * ; a Quote character must precede it. Otherwise the Quote character
326          * ;must be omitted. The Quote is not part of the contents.
327          */
328         appendTextString(str.getBytes());
329     }
330 
331     /**
332      * Append encoded string value to mMessage.
333      * This implementation doesn't check the validity of parameter, since it
334      * assumes that the values are validated in the GenericPdu setter methods.
335      */
336     @UnsupportedAppUsage
appendEncodedString(EncodedStringValue enStr)337     protected void appendEncodedString(EncodedStringValue enStr) {
338         /*
339          * From OMA-TS-MMS-ENC-V1_3-20050927-C:
340          * Encoded-string-value = Text-string | Value-length Char-set Text-string
341          */
342         assert(enStr != null);
343 
344         int charset = enStr.getCharacterSet();
345         byte[] textString = enStr.getTextString();
346         if (null == textString) {
347             return;
348         }
349 
350         /*
351          * In the implementation of EncodedStringValue, the charset field will
352          * never be 0. It will always be composed as
353          * Encoded-string-value = Value-length Char-set Text-string
354          */
355         mStack.newbuf();
356         PositionMarker start = mStack.mark();
357 
358         appendShortInteger(charset);
359         appendTextString(textString);
360 
361         int len = start.getLength();
362         mStack.pop();
363         appendValueLength(len);
364         mStack.copy();
365     }
366 
367     /**
368      * Append uintvar integer into mMessage.
369      * This implementation doesn't check the validity of parameter, since it
370      * assumes that the values are validated in the GenericPdu setter methods.
371      */
372     @UnsupportedAppUsage
appendUintvarInteger(long value)373     protected void appendUintvarInteger(long value) {
374         /*
375          * From WAP-230-WSP-20010705-a:
376          * To encode a large unsigned integer, split it into 7-bit fragments
377          * and place them in the payloads of multiple octets. The most significant
378          * bits are placed in the first octets with the least significant bits
379          * ending up in the last octet. All octets MUST set the Continue bit to 1
380          * except the last octet, which MUST set the Continue bit to 0.
381          */
382         int i;
383         long max = SHORT_INTEGER_MAX;
384 
385         for (i = 0; i < 5; i++) {
386             if (value < max) {
387                 break;
388             }
389 
390             max = (max << 7) | 0x7fl;
391         }
392 
393         while(i > 0) {
394             long temp = value >>> (i * 7);
395             temp = temp & 0x7f;
396 
397             append((int)((temp | 0x80) & 0xff));
398 
399             i--;
400         }
401 
402         append((int)(value & 0x7f));
403     }
404 
405     /**
406      * Append date value into mMessage.
407      * This implementation doesn't check the validity of parameter, since it
408      * assumes that the values are validated in the GenericPdu setter methods.
409      */
appendDateValue(long date)410     protected void appendDateValue(long date) {
411         /*
412          * From OMA-TS-MMS-ENC-V1_3-20050927-C:
413          * Date-value = Long-integer
414          */
415         appendLongInteger(date);
416     }
417 
418     /**
419      * Append value length to mMessage.
420      * This implementation doesn't check the validity of parameter, since it
421      * assumes that the values are validated in the GenericPdu setter methods.
422      */
423     @UnsupportedAppUsage
appendValueLength(long value)424     protected void appendValueLength(long value) {
425         /*
426          * From WAP-230-WSP-20010705-a:
427          * Value-length = Short-length | (Length-quote Length)
428          * ; Value length is used to indicate the length of the value to follow
429          * Short-length = <Any octet 0-30>
430          * Length-quote = <Octet 31>
431          * Length = Uintvar-integer
432          */
433         if (value < LENGTH_QUOTE) {
434             appendShortLength((int) value);
435             return;
436         }
437 
438         append(LENGTH_QUOTE);
439         appendUintvarInteger(value);
440     }
441 
442     /**
443      * Append quoted string to mMessage.
444      * This implementation doesn't check the validity of parameter, since it
445      * assumes that the values are validated in the GenericPdu setter methods.
446      */
447     @UnsupportedAppUsage
appendQuotedString(byte[] text)448     protected void appendQuotedString(byte[] text) {
449         /*
450          * From WAP-230-WSP-20010705-a:
451          * Quoted-string = <Octet 34> *TEXT End-of-string
452          * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
453          * ;quotation-marks <"> removed.
454          */
455         append(QUOTED_STRING_FLAG);
456         arraycopy(text, 0, text.length);
457         append(END_STRING_FLAG);
458     }
459 
460     /**
461      * Append quoted string to mMessage.
462      * This implementation doesn't check the validity of parameter, since it
463      * assumes that the values are validated in the GenericPdu setter methods.
464      */
465     @UnsupportedAppUsage
appendQuotedString(String str)466     protected void appendQuotedString(String str) {
467         /*
468          * From WAP-230-WSP-20010705-a:
469          * Quoted-string = <Octet 34> *TEXT End-of-string
470          * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
471          * ;quotation-marks <"> removed.
472          */
473         appendQuotedString(str.getBytes());
474     }
475 
appendAddressType(EncodedStringValue address)476     private EncodedStringValue appendAddressType(EncodedStringValue address) {
477         EncodedStringValue temp = null;
478 
479         try {
480             int addressType = checkAddressType(address.getString());
481             temp = EncodedStringValue.copy(address);
482             if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) {
483                 // Phone number.
484                 temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes());
485             } else if (PDU_IPV4_ADDRESS_TYPE == addressType) {
486                 // Ipv4 address.
487                 temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes());
488             } else if (PDU_IPV6_ADDRESS_TYPE == addressType) {
489                 // Ipv6 address.
490                 temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes());
491             }
492         } catch (NullPointerException e) {
493             return null;
494         }
495 
496         return temp;
497     }
498 
499     /**
500      * Append header to mMessage.
501      */
502     @UnsupportedAppUsage
appendHeader(int field)503     private int appendHeader(int field) {
504         switch (field) {
505             case PduHeaders.MMS_VERSION:
506                 appendOctet(field);
507 
508                 int version = mPduHeader.getOctet(field);
509                 if (0 == version) {
510                     appendShortInteger(PduHeaders.CURRENT_MMS_VERSION);
511                 } else {
512                     appendShortInteger(version);
513                 }
514 
515                 break;
516 
517             case PduHeaders.MESSAGE_ID:
518             case PduHeaders.TRANSACTION_ID:
519                 byte[] textString = mPduHeader.getTextString(field);
520                 if (null == textString) {
521                     return PDU_COMPOSE_FIELD_NOT_SET;
522                 }
523 
524                 appendOctet(field);
525                 appendTextString(textString);
526                 break;
527 
528             case PduHeaders.TO:
529             case PduHeaders.BCC:
530             case PduHeaders.CC:
531                 EncodedStringValue[] addr = mPduHeader.getEncodedStringValues(field);
532 
533                 if (null == addr) {
534                     return PDU_COMPOSE_FIELD_NOT_SET;
535                 }
536 
537                 EncodedStringValue temp;
538                 for (int i = 0; i < addr.length; i++) {
539                     temp = appendAddressType(addr[i]);
540                     if (temp == null) {
541                         return PDU_COMPOSE_CONTENT_ERROR;
542                     }
543 
544                     appendOctet(field);
545                     appendEncodedString(temp);
546                 }
547                 break;
548 
549             case PduHeaders.FROM:
550                 // Value-length (Address-present-token Encoded-string-value | Insert-address-token)
551                 appendOctet(field);
552 
553                 EncodedStringValue from = mPduHeader.getEncodedStringValue(field);
554                 if ((from == null)
555                         || TextUtils.isEmpty(from.getString())
556                         || new String(from.getTextString()).equals(
557                                 PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) {
558                     // Length of from = 1
559                     append(1);
560                     // Insert-address-token = <Octet 129>
561                     append(PduHeaders.FROM_INSERT_ADDRESS_TOKEN);
562                 } else {
563                     mStack.newbuf();
564                     PositionMarker fstart = mStack.mark();
565 
566                     // Address-present-token = <Octet 128>
567                     append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN);
568 
569                     temp = appendAddressType(from);
570                     if (temp == null) {
571                         return PDU_COMPOSE_CONTENT_ERROR;
572                     }
573 
574                     appendEncodedString(temp);
575 
576                     int flen = fstart.getLength();
577                     mStack.pop();
578                     appendValueLength(flen);
579                     mStack.copy();
580                 }
581                 break;
582 
583             case PduHeaders.READ_STATUS:
584             case PduHeaders.STATUS:
585             case PduHeaders.REPORT_ALLOWED:
586             case PduHeaders.PRIORITY:
587             case PduHeaders.DELIVERY_REPORT:
588             case PduHeaders.READ_REPORT:
589             case PduHeaders.RETRIEVE_STATUS:
590                 int octet = mPduHeader.getOctet(field);
591                 if (0 == octet) {
592                     return PDU_COMPOSE_FIELD_NOT_SET;
593                 }
594 
595                 appendOctet(field);
596                 appendOctet(octet);
597                 break;
598 
599             case PduHeaders.DATE:
600                 long date = mPduHeader.getLongInteger(field);
601                 if (-1 == date) {
602                     return PDU_COMPOSE_FIELD_NOT_SET;
603                 }
604 
605                 appendOctet(field);
606                 appendDateValue(date);
607                 break;
608 
609             case PduHeaders.SUBJECT:
610             case PduHeaders.RETRIEVE_TEXT:
611                 EncodedStringValue enString =
612                     mPduHeader.getEncodedStringValue(field);
613                 if (null == enString) {
614                     return PDU_COMPOSE_FIELD_NOT_SET;
615                 }
616 
617                 appendOctet(field);
618                 appendEncodedString(enString);
619                 break;
620 
621             case PduHeaders.MESSAGE_CLASS:
622                 byte[] messageClass = mPduHeader.getTextString(field);
623                 if (null == messageClass) {
624                     return PDU_COMPOSE_FIELD_NOT_SET;
625                 }
626 
627                 appendOctet(field);
628                 if (Arrays.equals(messageClass,
629                         PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes())) {
630                     appendOctet(PduHeaders.MESSAGE_CLASS_ADVERTISEMENT);
631                 } else if (Arrays.equals(messageClass,
632                         PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes())) {
633                     appendOctet(PduHeaders.MESSAGE_CLASS_AUTO);
634                 } else if (Arrays.equals(messageClass,
635                         PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes())) {
636                     appendOctet(PduHeaders.MESSAGE_CLASS_PERSONAL);
637                 } else if (Arrays.equals(messageClass,
638                         PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes())) {
639                     appendOctet(PduHeaders.MESSAGE_CLASS_INFORMATIONAL);
640                 } else {
641                     appendTextString(messageClass);
642                 }
643                 break;
644 
645             case PduHeaders.EXPIRY:
646                 long expiry = mPduHeader.getLongInteger(field);
647                 if (-1 == expiry) {
648                     return PDU_COMPOSE_FIELD_NOT_SET;
649                 }
650 
651                 appendOctet(field);
652 
653                 mStack.newbuf();
654                 PositionMarker expiryStart = mStack.mark();
655 
656                 append(PduHeaders.VALUE_RELATIVE_TOKEN);
657                 appendLongInteger(expiry);
658 
659                 int expiryLength = expiryStart.getLength();
660                 mStack.pop();
661                 appendValueLength(expiryLength);
662                 mStack.copy();
663                 break;
664 
665             default:
666                 return PDU_COMPOSE_FIELD_NOT_SUPPORTED;
667         }
668 
669         return PDU_COMPOSE_SUCCESS;
670     }
671 
672     /**
673      * Make ReadRec.Ind.
674      */
makeReadRecInd()675     private int makeReadRecInd() {
676         if (mMessage == null) {
677             mMessage = new ByteArrayOutputStream();
678             mPosition = 0;
679         }
680 
681         // X-Mms-Message-Type
682         appendOctet(PduHeaders.MESSAGE_TYPE);
683         appendOctet(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
684 
685         // X-Mms-MMS-Version
686         if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
687             return PDU_COMPOSE_CONTENT_ERROR;
688         }
689 
690         // Message-ID
691         if (appendHeader(PduHeaders.MESSAGE_ID) != PDU_COMPOSE_SUCCESS) {
692             return PDU_COMPOSE_CONTENT_ERROR;
693         }
694 
695         // To
696         if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_SUCCESS) {
697             return PDU_COMPOSE_CONTENT_ERROR;
698         }
699 
700         // From
701         if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
702             return PDU_COMPOSE_CONTENT_ERROR;
703         }
704 
705         // Date Optional
706         appendHeader(PduHeaders.DATE);
707 
708         // X-Mms-Read-Status
709         if (appendHeader(PduHeaders.READ_STATUS) != PDU_COMPOSE_SUCCESS) {
710             return PDU_COMPOSE_CONTENT_ERROR;
711         }
712 
713         // X-Mms-Applic-ID Optional(not support)
714         // X-Mms-Reply-Applic-ID Optional(not support)
715         // X-Mms-Aux-Applic-Info Optional(not support)
716 
717         return PDU_COMPOSE_SUCCESS;
718     }
719 
720     /**
721      * Make NotifyResp.Ind.
722      */
makeNotifyResp()723     private int makeNotifyResp() {
724         if (mMessage == null) {
725             mMessage = new ByteArrayOutputStream();
726             mPosition = 0;
727         }
728 
729         //    X-Mms-Message-Type
730         appendOctet(PduHeaders.MESSAGE_TYPE);
731         appendOctet(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
732 
733         // X-Mms-Transaction-ID
734         if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
735             return PDU_COMPOSE_CONTENT_ERROR;
736         }
737 
738         // X-Mms-MMS-Version
739         if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
740             return PDU_COMPOSE_CONTENT_ERROR;
741         }
742 
743         //  X-Mms-Status
744         if (appendHeader(PduHeaders.STATUS) != PDU_COMPOSE_SUCCESS) {
745             return PDU_COMPOSE_CONTENT_ERROR;
746         }
747 
748         // X-Mms-Report-Allowed Optional
749         appendHeader(PduHeaders.REPORT_ALLOWED);
750 
751         return PDU_COMPOSE_SUCCESS;
752     }
753 
754     /**
755      * Make Acknowledge.Ind.
756      */
makeAckInd()757     private int makeAckInd() {
758         if (mMessage == null) {
759             mMessage = new ByteArrayOutputStream();
760             mPosition = 0;
761         }
762 
763         //    X-Mms-Message-Type
764         appendOctet(PduHeaders.MESSAGE_TYPE);
765         appendOctet(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
766 
767         // X-Mms-Transaction-ID
768         if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
769             return PDU_COMPOSE_CONTENT_ERROR;
770         }
771 
772         //     X-Mms-MMS-Version
773         if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
774             return PDU_COMPOSE_CONTENT_ERROR;
775         }
776 
777         // X-Mms-Report-Allowed Optional
778         appendHeader(PduHeaders.REPORT_ALLOWED);
779 
780         return PDU_COMPOSE_SUCCESS;
781     }
782 
783     /**
784      * Make Send.req.
785      */
makeSendRetrievePdu(int type)786     private int makeSendRetrievePdu(int type) {
787         if (mMessage == null) {
788             mMessage = new ByteArrayOutputStream();
789             mPosition = 0;
790         }
791 
792         // X-Mms-Message-Type
793         appendOctet(PduHeaders.MESSAGE_TYPE);
794         appendOctet(type);
795 
796         // X-Mms-Transaction-ID
797         appendOctet(PduHeaders.TRANSACTION_ID);
798 
799         byte[] trid = mPduHeader.getTextString(PduHeaders.TRANSACTION_ID);
800         if (trid == null) {
801             // Transaction-ID should be set(by Transaction) before make().
802             throw new IllegalArgumentException("Transaction-ID is null.");
803         }
804         appendTextString(trid);
805 
806         //  X-Mms-MMS-Version
807         if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
808             return PDU_COMPOSE_CONTENT_ERROR;
809         }
810 
811         // Date Date-value Optional.
812         appendHeader(PduHeaders.DATE);
813 
814         // From
815         if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
816             return PDU_COMPOSE_CONTENT_ERROR;
817         }
818 
819         boolean recipient = false;
820 
821         // To
822         if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_CONTENT_ERROR) {
823             recipient = true;
824         }
825 
826         // Cc
827         if (appendHeader(PduHeaders.CC) != PDU_COMPOSE_CONTENT_ERROR) {
828             recipient = true;
829         }
830 
831         // Bcc
832         if (appendHeader(PduHeaders.BCC) != PDU_COMPOSE_CONTENT_ERROR) {
833             recipient = true;
834         }
835 
836         // Need at least one of "cc", "bcc" and "to".
837         if (false == recipient) {
838             return PDU_COMPOSE_CONTENT_ERROR;
839         }
840 
841         // Subject Optional
842         appendHeader(PduHeaders.SUBJECT);
843 
844         // X-Mms-Message-Class Optional
845         // Message-class-value = Class-identifier | Token-text
846         appendHeader(PduHeaders.MESSAGE_CLASS);
847 
848         // X-Mms-Expiry Optional
849         appendHeader(PduHeaders.EXPIRY);
850 
851         // X-Mms-Priority Optional
852         appendHeader(PduHeaders.PRIORITY);
853 
854         // X-Mms-Delivery-Report Optional
855         appendHeader(PduHeaders.DELIVERY_REPORT);
856 
857         // X-Mms-Read-Report Optional
858         appendHeader(PduHeaders.READ_REPORT);
859 
860         if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
861             // X-Mms-Retrieve-Status Optional
862             appendHeader(PduHeaders.RETRIEVE_STATUS);
863             // X-Mms-Retrieve-Text Optional
864             appendHeader(PduHeaders.RETRIEVE_TEXT);
865         }
866 
867 
868         //    Content-Type
869         appendOctet(PduHeaders.CONTENT_TYPE);
870 
871         //  Message body
872         return makeMessageBody(type);
873     }
874 
875     /**
876      * Make message body.
877      */
makeMessageBody(int type)878     private int makeMessageBody(int type) {
879         // 1. add body informations
880         mStack.newbuf();  // Switching buffer because we need to
881 
882         PositionMarker ctStart = mStack.mark();
883 
884         // This contentTypeIdentifier should be used for type of attachment...
885         String contentType = new String(mPduHeader.getTextString(PduHeaders.CONTENT_TYPE));
886         Integer contentTypeIdentifier = mContentTypeMap.get(contentType);
887         if (contentTypeIdentifier == null) {
888             // content type is mandatory
889             return PDU_COMPOSE_CONTENT_ERROR;
890         }
891 
892         appendShortInteger(contentTypeIdentifier.intValue());
893 
894         // content-type parameter: start
895         PduBody body;
896         if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
897             body = ((RetrieveConf) mPdu).getBody();
898         } else {
899             body = ((SendReq) mPdu).getBody();
900         }
901         if (null == body || body.getPartsNum() == 0) {
902             // empty message
903             appendUintvarInteger(0);
904             mStack.pop();
905             mStack.copy();
906             return PDU_COMPOSE_SUCCESS;
907         }
908 
909         PduPart part;
910         try {
911             part = body.getPart(0);
912 
913             byte[] start = part.getContentId();
914             if (start != null) {
915                 appendOctet(PduPart.P_DEP_START);
916                 if (('<' == start[0]) && ('>' == start[start.length - 1])) {
917                     appendTextString(start);
918                 } else {
919                     appendTextString("<" + new String(start) + ">");
920                 }
921             }
922 
923             // content-type parameter: type
924             appendOctet(PduPart.P_CT_MR_TYPE);
925             appendTextString(part.getContentType());
926         }
927         catch (ArrayIndexOutOfBoundsException e){
928             e.printStackTrace();
929         }
930 
931         int ctLength = ctStart.getLength();
932         mStack.pop();
933         appendValueLength(ctLength);
934         mStack.copy();
935 
936         // 3. add content
937         int partNum = body.getPartsNum();
938         appendUintvarInteger(partNum);
939         for (int i = 0; i < partNum; i++) {
940             part = body.getPart(i);
941             mStack.newbuf();  // Leaving space for header lengh and data length
942             PositionMarker attachment = mStack.mark();
943 
944             mStack.newbuf();  // Leaving space for Content-Type length
945             PositionMarker contentTypeBegin = mStack.mark();
946 
947             byte[] partContentType = part.getContentType();
948 
949             if (partContentType == null) {
950                 // content type is mandatory
951                 return PDU_COMPOSE_CONTENT_ERROR;
952             }
953 
954             // content-type value
955             Integer partContentTypeIdentifier =
956                 mContentTypeMap.get(new String(partContentType));
957             if (partContentTypeIdentifier == null) {
958                 appendTextString(partContentType);
959             } else {
960                 appendShortInteger(partContentTypeIdentifier.intValue());
961             }
962 
963             /* Content-type parameter : name.
964              * The value of name, filename, content-location is the same.
965              * Just one of them is enough for this PDU.
966              */
967             byte[] name = part.getName();
968 
969             if (null == name) {
970                 name = part.getFilename();
971 
972                 if (null == name) {
973                     name = part.getContentLocation();
974 
975                     if (null == name) {
976                         /* at lease one of name, filename, Content-location
977                          * should be available.
978                          */
979                         return PDU_COMPOSE_CONTENT_ERROR;
980                     }
981                 }
982             }
983             appendOctet(PduPart.P_DEP_NAME);
984             appendTextString(name);
985 
986             // content-type parameter : charset
987             int charset = part.getCharset();
988             if (charset != 0) {
989                 appendOctet(PduPart.P_CHARSET);
990                 appendShortInteger(charset);
991             }
992 
993             int contentTypeLength = contentTypeBegin.getLength();
994             mStack.pop();
995             appendValueLength(contentTypeLength);
996             mStack.copy();
997 
998             // content id
999             byte[] contentId = part.getContentId();
1000 
1001             if (null != contentId) {
1002                 appendOctet(PduPart.P_CONTENT_ID);
1003                 if (('<' == contentId[0]) && ('>' == contentId[contentId.length - 1])) {
1004                     appendQuotedString(contentId);
1005                 } else {
1006                     appendQuotedString("<" + new String(contentId) + ">");
1007                 }
1008             }
1009 
1010             // content-location
1011             byte[] contentLocation = part.getContentLocation();
1012             if (null != contentLocation) {
1013             	appendOctet(PduPart.P_CONTENT_LOCATION);
1014             	appendTextString(contentLocation);
1015             }
1016 
1017             // content
1018             int headerLength = attachment.getLength();
1019 
1020             int dataLength = 0; // Just for safety...
1021             byte[] partData = part.getData();
1022 
1023             if (partData != null) {
1024                 arraycopy(partData, 0, partData.length);
1025                 dataLength = partData.length;
1026             } else {
1027                 InputStream cr = null;
1028                 try {
1029                     byte[] buffer = new byte[PDU_COMPOSER_BLOCK_SIZE];
1030                     cr = mResolver.openInputStream(part.getDataUri());
1031                     int len = 0;
1032                     while ((len = cr.read(buffer)) != -1) {
1033                         mMessage.write(buffer, 0, len);
1034                         mPosition += len;
1035                         dataLength += len;
1036                     }
1037                 } catch (FileNotFoundException e) {
1038                     return PDU_COMPOSE_CONTENT_ERROR;
1039                 } catch (IOException e) {
1040                     return PDU_COMPOSE_CONTENT_ERROR;
1041                 } catch (RuntimeException e) {
1042                     return PDU_COMPOSE_CONTENT_ERROR;
1043                 } finally {
1044                     if (cr != null) {
1045                         try {
1046                             cr.close();
1047                         } catch (IOException e) {
1048                         }
1049                     }
1050                 }
1051             }
1052 
1053             if (dataLength != (attachment.getLength() - headerLength)) {
1054                 throw new RuntimeException("BUG: Length correctness check failed");
1055             }
1056 
1057             mStack.pop();
1058             appendUintvarInteger(headerLength);
1059             appendUintvarInteger(dataLength);
1060             mStack.copy();
1061         }
1062 
1063         return PDU_COMPOSE_SUCCESS;
1064     }
1065 
1066     /**
1067      *  Record current message informations.
1068      */
1069     static private class LengthRecordNode {
1070         ByteArrayOutputStream currentMessage = null;
1071         public int currentPosition = 0;
1072 
1073         public LengthRecordNode next = null;
1074     }
1075 
1076     /**
1077      * Mark current message position and stact size.
1078      */
1079     private class PositionMarker {
1080         private int c_pos;   // Current position
1081         private int currentStackSize;  // Current stack size
1082 
1083         @UnsupportedAppUsage
getLength()1084         int getLength() {
1085             // If these assert fails, likely that you are finding the
1086             // size of buffer that is deep in BufferStack you can only
1087             // find the length of the buffer that is on top
1088             if (currentStackSize != mStack.stackSize) {
1089                 throw new RuntimeException("BUG: Invalid call to getLength()");
1090             }
1091 
1092             return mPosition - c_pos;
1093         }
1094     }
1095 
1096     /**
1097      * This implementation can be OPTIMIZED to use only
1098      * 2 buffers. This optimization involves changing BufferStack
1099      * only... Its usage (interface) will not change.
1100      */
1101     private class BufferStack {
1102         private LengthRecordNode stack = null;
1103         private LengthRecordNode toCopy = null;
1104 
1105         int stackSize = 0;
1106 
1107         /**
1108          *  Create a new message buffer and push it into the stack.
1109          */
1110         @UnsupportedAppUsage
newbuf()1111         void newbuf() {
1112             // You can't create a new buff when toCopy != null
1113             // That is after calling pop() and before calling copy()
1114             // If you do, it is a bug
1115             if (toCopy != null) {
1116                 throw new RuntimeException("BUG: Invalid newbuf() before copy()");
1117             }
1118 
1119             LengthRecordNode temp = new LengthRecordNode();
1120 
1121             temp.currentMessage = mMessage;
1122             temp.currentPosition = mPosition;
1123 
1124             temp.next = stack;
1125             stack = temp;
1126 
1127             stackSize = stackSize + 1;
1128 
1129             mMessage = new ByteArrayOutputStream();
1130             mPosition = 0;
1131         }
1132 
1133         /**
1134          *  Pop the message before and record current message in the stack.
1135          */
1136         @UnsupportedAppUsage
pop()1137         void pop() {
1138             ByteArrayOutputStream currentMessage = mMessage;
1139             int currentPosition = mPosition;
1140 
1141             mMessage = stack.currentMessage;
1142             mPosition = stack.currentPosition;
1143 
1144             toCopy = stack;
1145             // Re using the top element of the stack to avoid memory allocation
1146 
1147             stack = stack.next;
1148             stackSize = stackSize - 1;
1149 
1150             toCopy.currentMessage = currentMessage;
1151             toCopy.currentPosition = currentPosition;
1152         }
1153 
1154         /**
1155          *  Append current message to the message before.
1156          */
1157         @UnsupportedAppUsage
copy()1158         void copy() {
1159             arraycopy(toCopy.currentMessage.toByteArray(), 0,
1160                     toCopy.currentPosition);
1161 
1162             toCopy = null;
1163         }
1164 
1165         /**
1166          *  Mark current message position
1167          */
1168         @UnsupportedAppUsage
mark()1169         PositionMarker mark() {
1170             PositionMarker m = new PositionMarker();
1171 
1172             m.c_pos = mPosition;
1173             m.currentStackSize = stackSize;
1174 
1175             return m;
1176         }
1177     }
1178 
1179     /**
1180      * Check address type.
1181      *
1182      * @param address address string without the postfix stinng type,
1183      *        such as "/TYPE=PLMN", "/TYPE=IPv6" and "/TYPE=IPv4"
1184      * @return PDU_PHONE_NUMBER_ADDRESS_TYPE if it is phone number,
1185      *         PDU_EMAIL_ADDRESS_TYPE if it is email address,
1186      *         PDU_IPV4_ADDRESS_TYPE if it is ipv4 address,
1187      *         PDU_IPV6_ADDRESS_TYPE if it is ipv6 address,
1188      *         PDU_UNKNOWN_ADDRESS_TYPE if it is unknown.
1189      */
checkAddressType(String address)1190     protected static int checkAddressType(String address) {
1191         /**
1192          * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf, section 8.
1193          * address = ( e-mail / device-address / alphanum-shortcode / num-shortcode)
1194          * e-mail = mailbox; to the definition of mailbox as described in
1195          * section 3.4 of [RFC2822], but excluding the
1196          * obsolete definitions as indicated by the "obs-" prefix.
1197          * device-address = ( global-phone-number "/TYPE=PLMN" )
1198          * / ( ipv4 "/TYPE=IPv4" ) / ( ipv6 "/TYPE=IPv6" )
1199          * / ( escaped-value "/TYPE=" address-type )
1200          *
1201          * global-phone-number = ["+"] 1*( DIGIT / written-sep )
1202          * written-sep =("-"/".")
1203          *
1204          * ipv4 = 1*3DIGIT 3( "." 1*3DIGIT ) ; IPv4 address value
1205          *
1206          * ipv6 = 4HEXDIG 7( ":" 4HEXDIG ) ; IPv6 address per RFC 2373
1207          */
1208 
1209         if (null == address) {
1210             return PDU_UNKNOWN_ADDRESS_TYPE;
1211         }
1212 
1213         if (address.matches(REGEXP_IPV4_ADDRESS_TYPE)) {
1214             // Ipv4 address.
1215             return PDU_IPV4_ADDRESS_TYPE;
1216         }else if (address.matches(REGEXP_PHONE_NUMBER_ADDRESS_TYPE)) {
1217             // Phone number.
1218             return PDU_PHONE_NUMBER_ADDRESS_TYPE;
1219         } else if (address.matches(REGEXP_EMAIL_ADDRESS_TYPE)) {
1220             // Email address.
1221             return PDU_EMAIL_ADDRESS_TYPE;
1222         } else if (address.matches(REGEXP_IPV6_ADDRESS_TYPE)) {
1223             // Ipv6 address.
1224             return PDU_IPV6_ADDRESS_TYPE;
1225         } else {
1226             // Unknown address.
1227             return PDU_UNKNOWN_ADDRESS_TYPE;
1228         }
1229     }
1230 }
1231