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