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 com.android.messaging.mmslib.pdu;
19
20import android.net.Uri;
21import android.util.SparseArray;
22
23/**
24 * The pdu part.
25 */
26public class PduPart {
27    /**
28     * Well-Known Parameters.
29     */
30    public static final int P_Q                  = 0x80;
31    public static final int P_CHARSET            = 0x81;
32    public static final int P_LEVEL              = 0x82;
33    public static final int P_TYPE               = 0x83;
34    public static final int P_DEP_NAME           = 0x85;
35    public static final int P_DEP_FILENAME       = 0x86;
36    public static final int P_DIFFERENCES        = 0x87;
37    public static final int P_PADDING            = 0x88;
38    // This value of "TYPE" s used with Content-Type: multipart/related
39    public static final int P_CT_MR_TYPE         = 0x89;
40    public static final int P_DEP_START          = 0x8A;
41    public static final int P_DEP_START_INFO     = 0x8B;
42    public static final int P_DEP_COMMENT        = 0x8C;
43    public static final int P_DEP_DOMAIN         = 0x8D;
44    public static final int P_MAX_AGE            = 0x8E;
45    public static final int P_DEP_PATH           = 0x8F;
46    public static final int P_SECURE             = 0x90;
47    public static final int P_SEC                = 0x91;
48    public static final int P_MAC                = 0x92;
49    public static final int P_CREATION_DATE      = 0x93;
50    public static final int P_MODIFICATION_DATE  = 0x94;
51    public static final int P_READ_DATE          = 0x95;
52    public static final int P_SIZE               = 0x96;
53    public static final int P_NAME               = 0x97;
54    public static final int P_FILENAME           = 0x98;
55    public static final int P_START              = 0x99;
56    public static final int P_START_INFO         = 0x9A;
57    public static final int P_COMMENT            = 0x9B;
58    public static final int P_DOMAIN             = 0x9C;
59    public static final int P_PATH               = 0x9D;
60
61    /**
62     * Header field names.
63     */
64     public static final int P_CONTENT_TYPE       = 0x91;
65     public static final int P_CONTENT_LOCATION   = 0x8E;
66     public static final int P_CONTENT_ID         = 0xC0;
67     public static final int P_DEP_CONTENT_DISPOSITION = 0xAE;
68     public static final int P_CONTENT_DISPOSITION = 0xC5;
69    // The next header is unassigned header, use reserved header(0x48) value.
70    public static final int P_CONTENT_TRANSFER_ENCODING = 0xC8;
71
72    /**
73     * Content=Transfer-Encoding string.
74     */
75    public static final String CONTENT_TRANSFER_ENCODING =
76            "Content-Transfer-Encoding";
77
78    /**
79     * Value of Content-Transfer-Encoding.
80     */
81    public static final String P_BINARY = "binary";
82    public static final String P_7BIT = "7bit";
83    public static final String P_8BIT = "8bit";
84    public static final String P_BASE64 = "base64";
85    public static final String P_QUOTED_PRINTABLE = "quoted-printable";
86
87    /**
88     * Value of disposition can be set to PduPart when the value is octet in
89     * the PDU.
90     * "from-data" instead of Form-data<Octet 128>.
91     * "attachment" instead of Attachment<Octet 129>.
92     * "inline" instead of Inline<Octet 130>.
93     */
94    static final byte[] DISPOSITION_FROM_DATA = "from-data".getBytes();
95    static final byte[] DISPOSITION_ATTACHMENT = "attachment".getBytes();
96    static final byte[] DISPOSITION_INLINE = "inline".getBytes();
97
98    /**
99     * Content-Disposition value.
100     */
101    public static final int P_DISPOSITION_FROM_DATA = 0x80;
102    public static final int P_DISPOSITION_ATTACHMENT = 0x81;
103    public static final int P_DISPOSITION_INLINE = 0x82;
104
105    /**
106     * Header of part.
107     */
108    private SparseArray<Object> mPartHeader = null;
109
110    /**
111     * Data uri.
112     */
113    private Uri mUri = null;
114
115    /**
116     * Part data.
117     */
118    private byte[] mPartData = null;
119
120    private static final String TAG = "PduPart";
121
122    /**
123     * Empty Constructor.
124     */
125    public PduPart() {
126        mPartHeader = new SparseArray<Object>();
127    }
128
129    /**
130     * Set part data. The data are stored as byte array.
131     *
132     * @param data the data
133     */
134    public void setData(final byte[] data) {
135        mPartData = data;
136    }
137
138    /**
139     * @return The part data or null if the data wasn't set or
140     * the data is stored as Uri.
141     * @see #getDataUri
142     */
143    public byte[] getData() {
144        return mPartData;
145    }
146
147    /**
148     * Set data uri. The data are stored as Uri.
149     *
150     * @param uri the uri
151     */
152    public void setDataUri(final Uri uri) {
153        mUri = uri;
154    }
155
156    /**
157     * @return The Uri of the part data or null if the data wasn't set or
158     * the data is stored as byte array.
159     * @see #getData
160     */
161    public Uri getDataUri() {
162        return mUri;
163    }
164
165    /**
166     * Set Content-id value
167     *
168     * @param contentId the content-id value
169     * @throws NullPointerException if the value is null.
170     */
171    public void setContentId(final byte[] contentId) {
172        if ((contentId == null) || (contentId.length == 0)) {
173            throw new IllegalArgumentException(
174                    "Content-Id may not be null or empty.");
175        }
176
177        if ((contentId.length > 1)
178                && ((char) contentId[0] == '<')
179                && ((char) contentId[contentId.length - 1] == '>')) {
180            mPartHeader.put(P_CONTENT_ID, contentId);
181            return;
182        }
183
184        // Insert beginning '<' and trailing '>' for Content-Id.
185        final byte[] buffer = new byte[contentId.length + 2];
186        buffer[0] = (byte) (0xff & '<');
187        buffer[buffer.length - 1] = (byte) (0xff & '>');
188        System.arraycopy(contentId, 0, buffer, 1, contentId.length);
189        mPartHeader.put(P_CONTENT_ID, buffer);
190    }
191
192    /**
193     * Get Content-id value.
194     *
195     * @return the value
196     */
197    public byte[] getContentId() {
198        return (byte[]) mPartHeader.get(P_CONTENT_ID);
199    }
200
201    /**
202     * Set Char-set value.
203     *
204     * @param charset the value
205     */
206    public void setCharset(final int charset) {
207        mPartHeader.put(P_CHARSET, charset);
208    }
209
210    /**
211     * Get Char-set value
212     *
213     * @return the charset value. Return 0 if charset was not set.
214     */
215    public int getCharset() {
216        final Integer charset = (Integer) mPartHeader.get(P_CHARSET);
217        if (charset == null) {
218            return 0;
219        } else {
220            return charset.intValue();
221        }
222    }
223
224    /**
225     * Set Content-Location value.
226     *
227     * @param contentLocation the value
228     * @throws NullPointerException if the value is null.
229     */
230    public void setContentLocation(final byte[] contentLocation) {
231        if (contentLocation == null) {
232            throw new NullPointerException("null content-location");
233        }
234
235        mPartHeader.put(P_CONTENT_LOCATION, contentLocation);
236    }
237
238    /**
239     * Get Content-Location value.
240     *
241     * @return the value
242     * return PduPart.disposition[0] instead of <Octet 128> (Form-data).
243     * return PduPart.disposition[1] instead of <Octet 129> (Attachment).
244     * return PduPart.disposition[2] instead of <Octet 130> (Inline).
245     */
246    public byte[] getContentLocation() {
247        return (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
248    }
249
250    /**
251     * Set Content-Disposition value.
252     * Use PduPart.disposition[0] instead of <Octet 128> (Form-data).
253     * Use PduPart.disposition[1] instead of <Octet 129> (Attachment).
254     * Use PduPart.disposition[2] instead of <Octet 130> (Inline).
255     *
256     * @param contentDisposition the value
257     * @throws NullPointerException if the value is null.
258     */
259    public void setContentDisposition(final byte[] contentDisposition) {
260        if (contentDisposition == null) {
261            throw new NullPointerException("null content-disposition");
262        }
263
264        mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition);
265    }
266
267    /**
268     * Get Content-Disposition value.
269     *
270     * @return the value
271     */
272    public byte[] getContentDisposition() {
273        return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION);
274    }
275
276    /**
277     * Set Content-Type value.
278     *
279     * @param value the value
280     * @throws NullPointerException if the value is null.
281     */
282    public void setContentType(final byte[] contentType) {
283        if (contentType == null) {
284            throw new NullPointerException("null content-type");
285        }
286
287        mPartHeader.put(P_CONTENT_TYPE, contentType);
288    }
289
290    /**
291     * Get Content-Type value of part.
292     *
293     * @return the value
294     */
295    public byte[] getContentType() {
296        return (byte[]) mPartHeader.get(P_CONTENT_TYPE);
297    }
298
299    /**
300     * Set Content-Transfer-Encoding value
301     *
302     * @param contentId the content-id value
303     * @throws NullPointerException if the value is null.
304     */
305    public void setContentTransferEncoding(final byte[] contentTransferEncoding) {
306        if (contentTransferEncoding == null) {
307            throw new NullPointerException("null content-transfer-encoding");
308        }
309
310        mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding);
311    }
312
313    /**
314     * Get Content-Transfer-Encoding value.
315     *
316     * @return the value
317     */
318    public byte[] getContentTransferEncoding() {
319        return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING);
320    }
321
322    /**
323     * Set Content-type parameter: name.
324     *
325     * @param name the name value
326     * @throws NullPointerException if the value is null.
327     */
328    public void setName(final byte[] name) {
329        if (null == name) {
330            throw new NullPointerException("null content-id");
331        }
332
333        mPartHeader.put(P_NAME, name);
334    }
335
336    /**
337     * Get content-type parameter: name.
338     *
339     * @return the name
340     */
341    public byte[] getName() {
342        return (byte[]) mPartHeader.get(P_NAME);
343    }
344
345    /**
346     * Get Content-disposition parameter: filename
347     *
348     * @param fileName the filename value
349     * @throws NullPointerException if the value is null.
350     */
351    public void setFilename(final byte[] fileName) {
352        if (null == fileName) {
353            throw new NullPointerException("null content-id");
354        }
355
356        mPartHeader.put(P_FILENAME, fileName);
357    }
358
359    /**
360     * Set Content-disposition parameter: filename
361     *
362     * @return the filename
363     */
364    public byte[] getFilename() {
365        return (byte[]) mPartHeader.get(P_FILENAME);
366    }
367
368    public String generateLocation() {
369        // Assumption: At least one of the content-location / name / filename
370        // or content-id should be set. This is guaranteed by the PduParser
371        // for incoming messages and by MM composer for outgoing messages.
372        byte[] location = (byte[]) mPartHeader.get(P_NAME);
373        if (null == location) {
374            location = (byte[]) mPartHeader.get(P_FILENAME);
375
376            if (null == location) {
377                location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
378            }
379        }
380
381        if (null == location) {
382            final byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID);
383            return "cid:" + new String(contentId);
384        } else {
385            return new String(location);
386        }
387    }
388}
389
390