PageRenderTime 151ms CodeModel.GetById 29ms app.highlight 105ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ws/com/google/android/mms/pdu/PduComposer.java

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