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