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      * Set data uri. The data are stored as Uri.
162      *
163      * @param uri the uri
164      */
165     public void setDataUri(Uri uri) {
166         mUri = uri;
167     }
168
169     /**
170      * @return The Uri of the part data or null if the data wasn't set or
171      *         the data is stored as byte array.
172      * @see #getData
173      */
174     public Uri getDataUri() {
175         return mUri;
176     }
177
178     /**
179      * Set Content-id value
180      *
181      * @param contentId the content-id value
182      * @throws NullPointerException if the value is null.
183      */
184     public void setContentId(byte[] contentId) {
185         if((contentId == null) || (contentId.length == 0)) {
186             throw new IllegalArgumentException(
187                     "Content-Id may not be null or empty.");
188         }
189
190         if ((contentId.length > 1)
191                 && ((char) contentId[0] == '<')
192                 && ((char) contentId[contentId.length - 1] == '>')) {
193             mPartHeader.put(P_CONTENT_ID, contentId);
194             return;
195         }
196
197         // Insert beginning '<' and trailing '>' for Content-Id.
198         byte[] buffer = new byte[contentId.length + 2];
199         buffer[0] = (byte) (0xff & '<');
200         buffer[buffer.length - 1] = (byte) (0xff & '>');
201         System.arraycopy(contentId, 0, buffer, 1, contentId.length);
202         mPartHeader.put(P_CONTENT_ID, buffer);
203     }
204
205     /**
206      * Get Content-id value.
207      *
208      * @return the value
209      */
210     public byte[] getContentId() {
211         return (byte[]) mPartHeader.get(P_CONTENT_ID);
212     }
213
214     /**
215      * Set Char-set value.
216      *
217      * @param charset the value
218      */
219     public void setCharset(int charset) {
220         mPartHeader.put(P_CHARSET, charset);
221     }
222
223     /**
224      * Get Char-set value
225      *
226      * @return the charset value. Return 0 if charset was not set.
227      */
228     public int getCharset() {
229         Integer charset = (Integer) mPartHeader.get(P_CHARSET);
230         if(charset == null) {
231             return 0;
232         } else {
233             return charset.intValue();
234         }
235     }
236
237     /**
238      * Set Content-Location value.
239      *
240      * @param contentLocation the value
241      * @throws NullPointerException if the value is null.
242      */
243     public void setContentLocation(byte[] contentLocation) {
244         if(contentLocation == null) {
245             throw new NullPointerException("null content-location");
246         }
247
248         mPartHeader.put(P_CONTENT_LOCATION, contentLocation);
249     }
250
251     /**
252      * Get Content-Location value.
253      *
254      * @return the value
255      *     return PduPart.disposition[0] instead of <Octet 128> (Form-data).
256      *     return PduPart.disposition[1] instead of <Octet 129> (Attachment).
257      *     return PduPart.disposition[2] instead of <Octet 130> (Inline).
258      */
259     public byte[] getContentLocation() {
260         return (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
261     }
262
263     /**
264      * Set Content-Disposition value.
265      * Use PduPart.disposition[0] instead of <Octet 128> (Form-data).
266      * Use PduPart.disposition[1] instead of <Octet 129> (Attachment).
267      * Use PduPart.disposition[2] instead of <Octet 130> (Inline).
268      *
269      * @param contentDisposition the value
270      * @throws NullPointerException if the value is null.
271      */
272     public void setContentDisposition(byte[] contentDisposition) {
273         if(contentDisposition == null) {
274             throw new NullPointerException("null content-disposition");
275         }
276
277         mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition);
278     }
279
280     /**
281      * Get Content-Disposition value.
282      *
283      * @return the value
284      */
285     public byte[] getContentDisposition() {
286         return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION);
287     }
288
289     /**
290      *  Set Content-Type value.
291      *
292      *  @param value the value
293      *  @throws NullPointerException if the value is null.
294      */
295     public void setContentType(byte[] contentType) {
296         if(contentType == null) {
297             throw new NullPointerException("null content-type");
298         }
299
300         mPartHeader.put(P_CONTENT_TYPE, contentType);
301     }
302
303     /**
304      * Get Content-Type value of part.
305      *
306      * @return the value
307      */
308     public byte[] getContentType() {
309         return (byte[]) mPartHeader.get(P_CONTENT_TYPE);
310     }
311
312     /**
313      * Set Content-Transfer-Encoding value
314      *
315      * @param contentId the content-id value
316      * @throws NullPointerException if the value is null.
317      */
318     public void setContentTransferEncoding(byte[] contentTransferEncoding) {
319         if(contentTransferEncoding == null) {
320             throw new NullPointerException("null content-transfer-encoding");
321         }
322
323         mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding);
324     }
325
326     /**
327      * Get Content-Transfer-Encoding value.
328      *
329      * @return the value
330      */
331     public byte[] getContentTransferEncoding() {
332         return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING);
333     }
334
335     /**
336      * Set Content-type parameter: name.
337      *
338      * @param name the name value
339      * @throws NullPointerException if the value is null.
340      */
341     public void setName(byte[] name) {
342         if(null == name) {
343             throw new NullPointerException("null content-id");
344         }
345
346         mPartHeader.put(P_NAME, name);
347     }
348
349     /**
350      *  Get content-type parameter: name.
351      *
352      *  @return the name
353      */
354     public byte[] getName() {
355         return (byte[]) mPartHeader.get(P_NAME);
356     }
357
358     /**
359      * Get Content-disposition parameter: filename
360      *
361      * @param fileName the filename value
362      * @throws NullPointerException if the value is null.
363      */
364     public void setFilename(byte[] fileName) {
365         if(null == fileName) {
366             throw new NullPointerException("null content-id");
367         }
368
369         mPartHeader.put(P_FILENAME, fileName);
370     }
371
372     /**
373      * Set Content-disposition parameter: filename
374      *
375      * @return the filename
376      */
377     public byte[] getFilename() {
378         return (byte[]) mPartHeader.get(P_FILENAME);
379     }
380
381    public String generateLocation() {
382        // Assumption: At least one of the content-location / name / filename
383        // or content-id should be set. This is guaranteed by the PduParser
384        // for incoming messages and by MM composer for outgoing messages.
385        byte[] location = (byte[]) mPartHeader.get(P_NAME);
386        if(null == location) {
387            location = (byte[]) mPartHeader.get(P_FILENAME);
388
389            if (null == location) {
390                location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
391            }
392        }
393
394        if (null == location) {
395            byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID);
396            return "cid:" + new String(contentId);
397        } else {
398            return new String(location);
399        }
400    }
401}
402
403