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