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