dom/mobilemessage/src/gonk/MmsPduHelper.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/mobilemessage/src/gonk/MmsPduHelper.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1677 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
    1.11 +
    1.12 +let WSP = {};
    1.13 +Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP);
    1.14 +
    1.15 +Cu.import("resource://gre/modules/mms_consts.js");
    1.16 +
    1.17 +Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
    1.18 +
    1.19 +let DEBUG; // set to true to see debug messages
    1.20 +
    1.21 +this.MMS_VERSION = (function() {
    1.22 +  Cu.import("resource://gre/modules/Services.jsm");
    1.23 +
    1.24 +  try {
    1.25 +    return Services.prefs.getIntPref("dom.mms.version");
    1.26 +  } catch(ex) {}
    1.27 +
    1.28 +  return MMS_VERSION_1_3;
    1.29 +})();
    1.30 +
    1.31 +this.translatePduErrorToStatus = function translatePduErrorToStatus(error) {
    1.32 +  if (error == MMS_PDU_ERROR_OK) {
    1.33 +    return MMS_PDU_STATUS_RETRIEVED;
    1.34 +  }
    1.35 +
    1.36 +  if ((error >= MMS_PDU_ERROR_TRANSIENT_FAILURE)
    1.37 +      && (error < MMS_PDU_ERROR_PERMANENT_FAILURE)) {
    1.38 +    return MMS_PDU_STATUS_DEFERRED;
    1.39 +  }
    1.40 +
    1.41 +  return MMS_PDU_STATUS_UNRECOGNISED;
    1.42 +}
    1.43 +
    1.44 +function defineLazyRegExp(obj, name, pattern) {
    1.45 +  obj.__defineGetter__(name, function() {
    1.46 +    delete obj[name];
    1.47 +    return obj[name] = new RegExp(pattern);
    1.48 +  });
    1.49 +}
    1.50 +
    1.51 +function RangedValue(name, min, max) {
    1.52 +  this.name = name;
    1.53 +  this.min = min;
    1.54 +  this.max = max;
    1.55 +}
    1.56 +RangedValue.prototype = {
    1.57 +  name: null,
    1.58 +  min: null,
    1.59 +  max: null,
    1.60 +
    1.61 +  /**
    1.62 +   * @param data
    1.63 +   *        A wrapped object containing raw PDU data.
    1.64 +   *
    1.65 +   * @return A decoded integer.
    1.66 +   *
    1.67 +   * @throws CodeError if decoded value is not in the range [this.min, this.max].
    1.68 +   */
    1.69 +  decode: function(data) {
    1.70 +    let value = WSP.Octet.decode(data);
    1.71 +    if ((value >= this.min) && (value <= this.max)) {
    1.72 +      return value;
    1.73 +    }
    1.74 +
    1.75 +    throw new WSP.CodeError(this.name + ": invalid value " + value);
    1.76 +  },
    1.77 +
    1.78 +  /**
    1.79 +   * @param data
    1.80 +   *        A wrapped object to store encoded raw data.
    1.81 +   * @param value
    1.82 +   *        An integer value within thr range [this.min, this.max].
    1.83 +   */
    1.84 +  encode: function(data, value) {
    1.85 +    if ((value < this.min) || (value > this.max)) {
    1.86 +      throw new WSP.CodeError(this.name + ": invalid value " + value);
    1.87 +    }
    1.88 +
    1.89 +    WSP.Octet.encode(data, value);
    1.90 +  },
    1.91 +};
    1.92 +
    1.93 +/**
    1.94 + * Internal decoding function for boolean values.
    1.95 + *
    1.96 + * Boolean-value = Yes | No
    1.97 + * Yes = <Octet 128>
    1.98 + * No = <Octet 129>
    1.99 + */
   1.100 +this.BooleanValue = {
   1.101 +  /**
   1.102 +   * @param data
   1.103 +   *        A wrapped object containing raw PDU data.
   1.104 +   *
   1.105 +   * @return Boolean true or false.
   1.106 +   *
   1.107 +   * @throws CodeError if read octet equals to neither 128 nor 129.
   1.108 +   */
   1.109 +  decode: function(data) {
   1.110 +    let value = WSP.Octet.decode(data);
   1.111 +    if ((value != 128) && (value != 129)) {
   1.112 +      throw new WSP.CodeError("Boolean-value: invalid value " + value);
   1.113 +    }
   1.114 +
   1.115 +    return value == 128;
   1.116 +  },
   1.117 +
   1.118 +  /**
   1.119 +   * @param data
   1.120 +   *        A wrapped object to store encoded raw data.
   1.121 +   * @param value
   1.122 +   *        A boolean value to be encoded.
   1.123 +   */
   1.124 +  encode: function(data, value) {
   1.125 +    WSP.Octet.encode(data, value ? 128 : 129);
   1.126 +  },
   1.127 +};
   1.128 +
   1.129 +/**
   1.130 + * MMS Address
   1.131 + *
   1.132 + * address = email | device-address | alphanum-shortcode | num-shortcode
   1.133 + *
   1.134 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A section 8
   1.135 + */
   1.136 +this.Address = {
   1.137 +  /**
   1.138 +   * @param data
   1.139 +   *        A wrapped object to store encoded raw data.
   1.140 +   *
   1.141 +   * @return An object of two string-typed attributes: address and type.
   1.142 +   */
   1.143 +  decode: function(data) {
   1.144 +    let str = EncodedStringValue.decode(data);
   1.145 +
   1.146 +    let result;
   1.147 +    if (((result = str.match(this.REGEXP_DECODE_PLMN)) != null)
   1.148 +        || ((result = str.match(this.REGEXP_DECODE_IPV4)) != null)
   1.149 +        || ((result = str.match(this.REGEXP_DECODE_IPV6)) != null)
   1.150 +        || ((result = str.match(this.REGEXP_DECODE_CUSTOM)) != null)) {
   1.151 +      return {address: result[1], type: result[2]};
   1.152 +    }
   1.153 +
   1.154 +    let type;
   1.155 +    if (str.match(this.REGEXP_NUM)) {
   1.156 +      type = "num";
   1.157 +    } else if (str.match(this.REGEXP_ALPHANUM)) {
   1.158 +      type = "alphanum";
   1.159 +    } else if (str.indexOf("@") > 0) {
   1.160 +      // E-mail should match the definition of `mailbox` as described in section
   1.161 +      // 3.4 of RFC2822, but excluding the obsolete definitions as indicated by
   1.162 +      // the "obs-" prefix. Here we match only a `@` character.
   1.163 +      type = "email";
   1.164 +    } else {
   1.165 +      throw new WSP.CodeError("Address: invalid address");
   1.166 +    }
   1.167 +
   1.168 +    return {address: str, type: type};
   1.169 +  },
   1.170 +
   1.171 +  /**
   1.172 +   * @param data
   1.173 +   *        A wrapped object to store encoded raw data.
   1.174 +   * @param value
   1.175 +   *        An object of two string-typed attributes: address and type.
   1.176 +   */
   1.177 +  encode: function(data, value) {
   1.178 +    if (!value || !value.type || !value.address) {
   1.179 +      throw new WSP.CodeError("Address: invalid value");
   1.180 +    }
   1.181 +
   1.182 +    let str;
   1.183 +    switch (value.type) {
   1.184 +      case "email":
   1.185 +        if (value.address.indexOf("@") > 0) {
   1.186 +          str = value.address;
   1.187 +        }
   1.188 +        break;
   1.189 +      case "num":
   1.190 +        if (value.address.match(this.REGEXP_NUM)) {
   1.191 +          str = value.address;
   1.192 +        }
   1.193 +        break;
   1.194 +      case "alphanum":
   1.195 +        if (value.address.match(this.REGEXP_ALPHANUM)) {
   1.196 +          str = value.address;
   1.197 +        }
   1.198 +        break;
   1.199 +      case "IPv4":
   1.200 +        if (value.address.match(this.REGEXP_ENCODE_IPV4)) {
   1.201 +          str = value.address + "/TYPE=IPv4";
   1.202 +        }
   1.203 +        break;
   1.204 +      case "IPv6":
   1.205 +        if (value.address.match(this.REGEXP_ENCODE_IPV6)) {
   1.206 +          str = value.address + "/TYPE=IPv6";
   1.207 +        }
   1.208 +        break;
   1.209 +      case "PLMN":
   1.210 +        if (value.address.match(this.REGEXP_ENCODE_PLMN)) {
   1.211 +          str = value.address + "/TYPE=PLMN";
   1.212 +        }
   1.213 +        break;
   1.214 +      default:
   1.215 +        if (value.type.match(this.REGEXP_ENCODE_CUSTOM_TYPE)
   1.216 +            && value.address.match(this.REGEXP_ENCODE_CUSTOM_ADDR)) {
   1.217 +          str = value.address + "/TYPE=" + value.type;
   1.218 +	}
   1.219 +        break;
   1.220 +    }
   1.221 +
   1.222 +    if (!str) {
   1.223 +      throw new WSP.CodeError("Address: invalid value: " + JSON.stringify(value));
   1.224 +    }
   1.225 +
   1.226 +    EncodedStringValue.encode(data, str);
   1.227 +  },
   1.228 +
   1.229 +  /**
   1.230 +   * @param address
   1.231 +   *        Address string which want to find the type.
   1.232 +   *
   1.233 +   * @return Address type.
   1.234 +   */
   1.235 +  resolveType: function(address) {
   1.236 +    if (address.match(this.REGEXP_EMAIL)) {
   1.237 +      return "email";
   1.238 +    }
   1.239 +
   1.240 +    if (address.match(this.REGEXP_IPV4)) {
   1.241 +      return "IPv4";
   1.242 +    }
   1.243 +
   1.244 +    if (address.match(this.REGEXP_IPV6)) {
   1.245 +      return "IPv6";
   1.246 +    }
   1.247 +
   1.248 +    let normalizedAddress = PhoneNumberUtils.normalize(address, false);
   1.249 +    if (PhoneNumberUtils.isPlainPhoneNumber(normalizedAddress)) {
   1.250 +      return "PLMN";
   1.251 +    }
   1.252 +
   1.253 +    return "Others";
   1.254 +  },
   1.255 +};
   1.256 +
   1.257 +defineLazyRegExp(Address, "REGEXP_DECODE_PLMN",        "^(\\+?[\\d.-]+)\\/TYPE=(PLMN)$");
   1.258 +defineLazyRegExp(Address, "REGEXP_DECODE_IPV4",        "^(\\d{1,3}(?:\\.\\d{1,3}){3})\\/TYPE=(IPv4)$");
   1.259 +defineLazyRegExp(Address, "REGEXP_DECODE_IPV6",        "^([\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7})\\/TYPE=(IPv6)$");
   1.260 +defineLazyRegExp(Address, "REGEXP_DECODE_CUSTOM",      "^([\\w\\+\\-.%]+)\\/TYPE=(\\w+)$");
   1.261 +defineLazyRegExp(Address, "REGEXP_ENCODE_PLMN",        "^\\+?[\\d.-]+$");
   1.262 +defineLazyRegExp(Address, "REGEXP_ENCODE_IPV4",        "^\\d{1,3}(?:\\.\\d{1,3}){3}$");
   1.263 +defineLazyRegExp(Address, "REGEXP_ENCODE_IPV6",        "^[\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7}$");
   1.264 +defineLazyRegExp(Address, "REGEXP_ENCODE_CUSTOM_TYPE", "^\\w+$");
   1.265 +defineLazyRegExp(Address, "REGEXP_ENCODE_CUSTOM_ADDR", "^[\\w\\+\\-.%]+$");
   1.266 +defineLazyRegExp(Address, "REGEXP_NUM",                "^[\\+*#]\\d+$");
   1.267 +defineLazyRegExp(Address, "REGEXP_ALPHANUM",           "^\\w+$");
   1.268 +defineLazyRegExp(Address, "REGEXP_PLMN",               "^\\?[\\d.-]$");
   1.269 +defineLazyRegExp(Address, "REGEXP_IPV4",               "^\\d{1,3}(?:\\.\\d{1,3}){3}$");
   1.270 +defineLazyRegExp(Address, "REGEXP_IPV6",               "^[\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7}$");
   1.271 +defineLazyRegExp(Address, "REGEXP_EMAIL",              "@");
   1.272 +
   1.273 +/**
   1.274 + * Header-field = MMS-header | Application-header
   1.275 + *
   1.276 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.2
   1.277 + */
   1.278 +this.HeaderField = {
   1.279 +  /**
   1.280 +   * @param data
   1.281 +   *        A wrapped object containing raw PDU data.
   1.282 +   * @param options
   1.283 +   *        Extra context for decoding.
   1.284 +   *
   1.285 +   * @return A decoded object containing `name` and `value` properties or null
   1.286 +   *         in case of a failed parsing. The `name` property must be a string,
   1.287 +   *         but the `value` property can be many different types depending on
   1.288 +   *         `name`.
   1.289 +   */
   1.290 +  decode: function(data, options) {
   1.291 +    return WSP.decodeAlternatives(data, options,
   1.292 +                                  MmsHeader, WSP.ApplicationHeader);
   1.293 +  },
   1.294 +
   1.295 +  /**
   1.296 +   * @param data
   1.297 +   *        A wrapped object to store encoded raw data.
   1.298 +   * @param octet
   1.299 +   *        Octet value to be encoded.
   1.300 +   * @param options
   1.301 +   *        Extra context for encoding.
   1.302 +   */
   1.303 +  encode: function(data, value, options) {
   1.304 +    WSP.encodeAlternatives(data, value, options,
   1.305 +                           MmsHeader, WSP.ApplicationHeader);
   1.306 +  },
   1.307 +};
   1.308 +
   1.309 +/**
   1.310 + * MMS-header = MMS-field-name MMS-value
   1.311 + * MMS-field-name = Short-integer
   1.312 + *
   1.313 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.2
   1.314 + */
   1.315 +this.MmsHeader = {
   1.316 +  /**
   1.317 +   * @param data
   1.318 +   *        A wrapped object containing raw PDU data.
   1.319 +   * @param options
   1.320 +   *        Extra context for decoding.
   1.321 +   *
   1.322 +   * @return A decoded object containing `name` and `value` properties or null
   1.323 +   *         in case of a failed parsing. The `name` property must be a string,
   1.324 +   *         but the `value` property can be many different types depending on
   1.325 +   *         `name`.
   1.326 +   *
   1.327 +   * @throws NotWellKnownEncodingError if decoded well-known header field
   1.328 +   *         number is not registered or supported.
   1.329 +   */
   1.330 +  decode: function(data, options) {
   1.331 +    let index = WSP.ShortInteger.decode(data);
   1.332 +
   1.333 +    let entry = MMS_HEADER_FIELDS[index];
   1.334 +    if (!entry) {
   1.335 +      throw new WSP.NotWellKnownEncodingError(
   1.336 +        "MMS-header: not well known header " + index);
   1.337 +    }
   1.338 +
   1.339 +    let cur = data.offset, value;
   1.340 +    try {
   1.341 +      value = entry.coder.decode(data, options);
   1.342 +    } catch (e) {
   1.343 +      data.offset = cur;
   1.344 +
   1.345 +      value = WSP.skipValue(data);
   1.346 +      debug("Skip malformed well known header: "
   1.347 +            + JSON.stringify({name: entry.name, value: value}));
   1.348 +
   1.349 +      return null;
   1.350 +    }
   1.351 +
   1.352 +    return {
   1.353 +      name: entry.name,
   1.354 +      value: value,
   1.355 +    };
   1.356 +  },
   1.357 +
   1.358 +  /**
   1.359 +   * @param data
   1.360 +   *        A wrapped object to store encoded raw data.
   1.361 +   * @param header
   1.362 +   *        An object containing two attributes: a string-typed `name` and a
   1.363 +   *        `value` of arbitrary type.
   1.364 +   *
   1.365 +   * @throws CodeError if got an empty header name.
   1.366 +   * @throws NotWellKnownEncodingError if the well-known header field number is
   1.367 +   *         not registered or supported.
   1.368 +   */
   1.369 +  encode: function(data, header) {
   1.370 +    if (!header.name) {
   1.371 +      throw new WSP.CodeError("MMS-header: empty header name");
   1.372 +    }
   1.373 +
   1.374 +    let entry = MMS_HEADER_FIELDS[header.name.toLowerCase()];
   1.375 +    if (!entry) {
   1.376 +      throw new WSP.NotWellKnownEncodingError(
   1.377 +        "MMS-header: not well known header " + header.name);
   1.378 +    }
   1.379 +
   1.380 +    WSP.ShortInteger.encode(data, entry.number);
   1.381 +    entry.coder.encode(data, header.value);
   1.382 +  },
   1.383 +};
   1.384 +
   1.385 +/**
   1.386 + * Cancel-status-value = Cancel Request Successfully received |
   1.387 + *                       Cancel Request corrupted
   1.388 + *
   1.389 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.7
   1.390 + */
   1.391 +this.CancelStatusValue = new RangedValue("Cancel-status-value", 128, 129);
   1.392 +
   1.393 +/**
   1.394 + * Content-class-value = text | image-basic| image-rich | video-basic |
   1.395 + *                       video-rich | megapixel | content-basic | content-rich
   1.396 + *
   1.397 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.9
   1.398 + */
   1.399 +this.ContentClassValue = new RangedValue("Content-class-value", 128, 135);
   1.400 +
   1.401 +/**
   1.402 + * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
   1.403 + *
   1.404 + *   Content-location-value = Uri-value
   1.405 + *
   1.406 + * When used in the M-Mbox-Delete.conf and M-Delete.conf PDU:
   1.407 + *
   1.408 + *   Content-location-Del-value = Value-length Status-count-value Content-location-value
   1.409 + *   Status-count-value = Integer-value
   1.410 + *
   1.411 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.10
   1.412 + */
   1.413 +this.ContentLocationValue = {
   1.414 +  /**
   1.415 +   * @param data
   1.416 +   *        A wrapped object containing raw PDU data.
   1.417 +   * @param options
   1.418 +   *        Extra context for decoding.
   1.419 +   *
   1.420 +   * @return A decoded object containing `uri` and conditional `statusCount`
   1.421 +   *         properties.
   1.422 +   */
   1.423 +  decode: function(data, options) {
   1.424 +    let type = WSP.ensureHeader(options, "x-mms-message-type");
   1.425 +
   1.426 +    let result = {};
   1.427 +    if ((type == MMS_PDU_TYPE_MBOX_DELETE_CONF)
   1.428 +        || (type == MMS_PDU_TYPE_DELETE_CONF)) {
   1.429 +      let length = WSP.ValueLength.decode(data);
   1.430 +      let end = data.offset + length;
   1.431 +
   1.432 +      result.statusCount = WSP.IntegerValue.decode(data);
   1.433 +      result.uri = WSP.UriValue.decode(data);
   1.434 +
   1.435 +      if (data.offset != end) {
   1.436 +        data.offset = end;
   1.437 +      }
   1.438 +    } else {
   1.439 +      result.uri = WSP.UriValue.decode(data);
   1.440 +    }
   1.441 +
   1.442 +    return result;
   1.443 +  },
   1.444 +};
   1.445 +
   1.446 +/**
   1.447 + * Element-Descriptor-value = Value-length Content-Reference-value *(Parameter)
   1.448 + * Content-Reference-value = Text-string
   1.449 + *
   1.450 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.18
   1.451 + */
   1.452 +this.ElementDescriptorValue = {
   1.453 +  /**
   1.454 +   * @param data
   1.455 +   *        A wrapped object containing raw PDU data.
   1.456 +   *
   1.457 +   * @return A decoded object containing a string property `contentReference`
   1.458 +   *         and an optinal `params` name-value map.
   1.459 +   */
   1.460 +  decode: function(data) {
   1.461 +    let length = WSP.ValueLength.decode(data);
   1.462 +    let end = data.offset + length;
   1.463 +
   1.464 +    let result = {};
   1.465 +    result.contentReference = WSP.TextString.decode(data);
   1.466 +    if (data.offset < end) {
   1.467 +      result.params = Parameter.decodeMultiple(data, end);
   1.468 +    }
   1.469 +
   1.470 +    if (data.offset != end) {
   1.471 +      // Explicitly seek to end in case of skipped parameters.
   1.472 +      data.offset = end;
   1.473 +    }
   1.474 +
   1.475 +    return result;
   1.476 +  },
   1.477 +};
   1.478 +
   1.479 +/**
   1.480 + * OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.18:
   1.481 + * `For well-known parameter names binary tokens MUST be used as defined in
   1.482 + * Table 27.` So we can't reuse that of WSP.
   1.483 + *
   1.484 + *   Parameter = Parameter-name Parameter-value
   1.485 + *   Parameter-name = Short-integer | Text-string
   1.486 + *   Parameter-value = Constrained-encoding | Text-string
   1.487 + *
   1.488 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.18
   1.489 + */
   1.490 +this.Parameter = {
   1.491 +  /**
   1.492 +   * @param data
   1.493 +   *        A wrapped object containing raw PDU data.
   1.494 +   *
   1.495 +   * @return A decoded string.
   1.496 +   *
   1.497 +   * @throws NotWellKnownEncodingError if decoded well-known parameter number
   1.498 +   *         is not registered or supported.
   1.499 +   */
   1.500 +  decodeParameterName: function(data) {
   1.501 +    let begin = data.offset;
   1.502 +    let number;
   1.503 +    try {
   1.504 +      number = WSP.ShortInteger.decode(data);
   1.505 +    } catch (e) {
   1.506 +      data.offset = begin;
   1.507 +      return WSP.TextString.decode(data).toLowerCase();
   1.508 +    }
   1.509 +
   1.510 +    let entry = MMS_WELL_KNOWN_PARAMS[number];
   1.511 +    if (!entry) {
   1.512 +      throw new WSP.NotWellKnownEncodingError(
   1.513 +        "Parameter-name: not well known parameter " + number);
   1.514 +    }
   1.515 +
   1.516 +    return entry.name;
   1.517 +  },
   1.518 +
   1.519 +  /**
   1.520 +   * @param data
   1.521 +   *        A wrapped object containing raw PDU data.
   1.522 +   *
   1.523 +   * @return A decoded object containing `name` and `value` properties or null
   1.524 +   *         in case of a failed parsing. The `name` property must be a string,
   1.525 +   *         but the `value` property can be many different types depending on
   1.526 +   *         `name`.
   1.527 +   */
   1.528 +  decode: function(data) {
   1.529 +    let name = this.decodeParameterName(data);
   1.530 +    let value = WSP.decodeAlternatives(data, null,
   1.531 +                                       WSP.ConstrainedEncoding, WSP.TextString);
   1.532 +    return {
   1.533 +      name: name,
   1.534 +      value: value,
   1.535 +    };
   1.536 +  },
   1.537 +
   1.538 +  /**
   1.539 +   * @param data
   1.540 +   *        A wrapped object containing raw PDU data.
   1.541 +   * @param end
   1.542 +   *        Ending offset of following parameters.
   1.543 +   *
   1.544 +   * @return An array of decoded objects.
   1.545 +   */
   1.546 +  decodeMultiple: function(data, end) {
   1.547 +    let params, param;
   1.548 +
   1.549 +    while (data.offset < end) {
   1.550 +      try {
   1.551 +        param = this.decode(data);
   1.552 +      } catch (e) {
   1.553 +        break;
   1.554 +      }
   1.555 +      if (param) {
   1.556 +        if (!params) {
   1.557 +          params = {};
   1.558 +        }
   1.559 +        params[param.name] = param.value;
   1.560 +      }
   1.561 +    }
   1.562 +
   1.563 +    return params;
   1.564 +  },
   1.565 +
   1.566 +  /**
   1.567 +   * @param data
   1.568 +   *        A wrapped object to store encoded raw data.
   1.569 +   * @param param
   1.570 +   *        An object containing two attributes: `name` and `value`.
   1.571 +   * @param options
   1.572 +   *        Extra context for encoding.
   1.573 +   */
   1.574 +  encode: function(data, param, options) {
   1.575 +    if (!param || !param.name) {
   1.576 +      throw new WSP.CodeError("Parameter-name: empty param name");
   1.577 +    }
   1.578 +
   1.579 +    let entry = MMS_WELL_KNOWN_PARAMS[param.name.toLowerCase()];
   1.580 +    if (entry) {
   1.581 +      WSP.ShortInteger.encode(data, entry.number);
   1.582 +    } else {
   1.583 +      WSP.TextString.encode(data, param.name);
   1.584 +    }
   1.585 +
   1.586 +    WSP.encodeAlternatives(data, param.value, options,
   1.587 +                           WSP.ConstrainedEncoding, WSP.TextString);
   1.588 +  },
   1.589 +};
   1.590 +
   1.591 +/**
   1.592 + * The Char-set values are registered by IANA as MIBEnum value and SHALL be
   1.593 + * encoded as Integer-value.
   1.594 + *
   1.595 + *   Encoded-string-value = Text-string | Value-length Char-set Text-string
   1.596 + *
   1.597 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.19
   1.598 + * @see OMA-TS-MMS_CONF-V1_3-20110913-A clause 10.2.1
   1.599 + */
   1.600 +this.EncodedStringValue = {
   1.601 +  /**
   1.602 +   * @param data
   1.603 +   *        A wrapped object containing raw PDU data.
   1.604 +   *
   1.605 +   * @return Decoded string.
   1.606 +   *
   1.607 +   * @throws CodeError if the raw octets cannot be converted.
   1.608 +   * @throws NotWellKnownEncodingError if decoded well-known charset number is
   1.609 +   *         not registered or supported.
   1.610 +   */
   1.611 +  decodeCharsetEncodedString: function(data) {
   1.612 +    let length = WSP.ValueLength.decode(data);
   1.613 +    let end = data.offset + length;
   1.614 +
   1.615 +    let charset = WSP.IntegerValue.decode(data);
   1.616 +    let entry = WSP.WSP_WELL_KNOWN_CHARSETS[charset];
   1.617 +    if (!entry) {
   1.618 +      throw new WSP.NotWellKnownEncodingError(
   1.619 +        "Charset-encoded-string: not well known charset " + charset);
   1.620 +    }
   1.621 +
   1.622 +    let str;
   1.623 +    if (entry.converter) {
   1.624 +      // Read a possible string quote(<Octet 127>).
   1.625 +      let begin = data.offset;
   1.626 +      if (WSP.Octet.decode(data) != 127) {
   1.627 +        data.offset = begin;
   1.628 +      }
   1.629 +
   1.630 +      let raw = WSP.Octet.decodeMultiple(data, end - 1);
   1.631 +      // Read NUL character.
   1.632 +      WSP.Octet.decodeEqualTo(data, 0);
   1.633 +
   1.634 +      if (!raw) {
   1.635 +        str = "";
   1.636 +      } else {
   1.637 +        let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
   1.638 +                   .createInstance(Ci.nsIScriptableUnicodeConverter);
   1.639 +        conv.charset = entry.converter;
   1.640 +        try {
   1.641 +          str = conv.convertFromByteArray(raw, raw.length);
   1.642 +        } catch (e) {
   1.643 +          throw new WSP.CodeError("Charset-encoded-string: " + e.message);
   1.644 +        }
   1.645 +      }
   1.646 +    } else {
   1.647 +      str = WSP.TextString.decode(data);
   1.648 +    }
   1.649 +
   1.650 +    if (data.offset != end) {
   1.651 +      data.offset = end;
   1.652 +    }
   1.653 +
   1.654 +    return str;
   1.655 +  },
   1.656 +
   1.657 +  /**
   1.658 +   * @param data
   1.659 +   *        A wrapped object containing raw PDU data.
   1.660 +   *
   1.661 +   * @return Decoded string.
   1.662 +   */
   1.663 +  decode: function(data) {
   1.664 +    let begin = data.offset;
   1.665 +    try {
   1.666 +      return WSP.TextString.decode(data);
   1.667 +    } catch (e) {
   1.668 +      data.offset = begin;
   1.669 +      return this.decodeCharsetEncodedString(data);
   1.670 +    }
   1.671 +  },
   1.672 +
   1.673 +  /**
   1.674 +   * Always encode target string with UTF-8 encoding.
   1.675 +   *
   1.676 +   * @param data
   1.677 +   *        A wrapped object to store encoded raw data.
   1.678 +   * @param str
   1.679 +   *        A string.
   1.680 +   */
   1.681 +  encodeCharsetEncodedString: function(data, str) {
   1.682 +    let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
   1.683 +               .createInstance(Ci.nsIScriptableUnicodeConverter);
   1.684 +    // `When the text string cannot be represented as us-ascii, the character
   1.685 +    // set SHALL be encoded as utf-8(IANA MIBenum 106) which has unique byte
   1.686 +    // ordering.` ~ OMA-TS-MMS_CONF-V1_3-20110913-A clause 10.2.1
   1.687 +    conv.charset = "UTF-8";
   1.688 +
   1.689 +    let raw;
   1.690 +    try {
   1.691 +      raw = conv.convertToByteArray(str);
   1.692 +    } catch (e) {
   1.693 +      throw new WSP.CodeError("Charset-encoded-string: " + e.message);
   1.694 +    }
   1.695 +
   1.696 +    let length = raw.length + 2; // Charset number and NUL character
   1.697 +    // Prepend <Octet 127> if necessary.
   1.698 +    if (raw[0] >= 128) {
   1.699 +      ++length;
   1.700 +    }
   1.701 +
   1.702 +    WSP.ValueLength.encode(data, length);
   1.703 +
   1.704 +    let entry = WSP.WSP_WELL_KNOWN_CHARSETS["utf-8"];
   1.705 +    WSP.IntegerValue.encode(data, entry.number);
   1.706 +
   1.707 +    if (raw[0] >= 128) {
   1.708 +      WSP.Octet.encode(data, 127);
   1.709 +    }
   1.710 +    WSP.Octet.encodeMultiple(data, raw);
   1.711 +    WSP.Octet.encode(data, 0);
   1.712 +  },
   1.713 +
   1.714 +  /**
   1.715 +   * @param data
   1.716 +   *        A wrapped object to store encoded raw data.
   1.717 +   * @param str
   1.718 +   *        A string.
   1.719 +   */
   1.720 +  encode: function(data, str) {
   1.721 +    let begin = data.offset;
   1.722 +    try {
   1.723 +      // Quoted from OMA-TS-MMS-CONF-V1_3-20110913-A:
   1.724 +      // Some of the MMS headers have been defined as "Encoded-string-value".
   1.725 +      // The character set IANA MIBEnum value in these headers SHALL be
   1.726 +      // encoded as Integer-value ([WAPWSP] section 8.4.2.3). The character
   1.727 +      // set us-ascii (IANA MIBenum 3) SHALL always be accepted. If the
   1.728 +      // character set is not specified (simple Text-string encoding) the
   1.729 +      // character set SHALL be identified as us-ascii (lower half of ISO
   1.730 +      // 8859-1 [ISO8859-1]). When the text string cannot be represented as
   1.731 +      // us-ascii, the character set SHALL be encoded as utf-8 (IANA MIBenum
   1.732 +      // 106) which has unique byte ordering.
   1.733 +      WSP.TextString.encode(data, str, true);
   1.734 +    } catch (e) {
   1.735 +      data.offset = begin;
   1.736 +      this.encodeCharsetEncodedString(data, str);
   1.737 +    }
   1.738 +  },
   1.739 +};
   1.740 +
   1.741 +/**
   1.742 + * Expiry-value = Value-length (Absolute-token Date-value | Relative-token Delta-seconds-value)
   1.743 + * Absolute-token = <Octet 128>
   1.744 + * Relative-token = <Octet 129>
   1.745 + *
   1.746 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.20
   1.747 + */
   1.748 +this.ExpiryValue = {
   1.749 +  /**
   1.750 +   * @param data
   1.751 +   *        A wrapped object containing raw PDU data.
   1.752 +   *
   1.753 +   * @return A Date object for absolute expiry or an integer for relative one.
   1.754 +   *
   1.755 +   * @throws CodeError if decoded token equals to neither 128 nor 129.
   1.756 +   */
   1.757 +  decode: function(data) {
   1.758 +    let length = WSP.ValueLength.decode(data);
   1.759 +    let end = data.offset + length;
   1.760 +
   1.761 +    let token = WSP.Octet.decode(data);
   1.762 +    if ((token != 128) && (token != 129)) {
   1.763 +      throw new WSP.CodeError("Expiry-value: invalid token " + token);
   1.764 +    }
   1.765 +
   1.766 +    let result;
   1.767 +    if (token == 128) {
   1.768 +      result = WSP.DateValue.decode(data);
   1.769 +    } else {
   1.770 +      result = WSP.DeltaSecondsValue.decode(data);
   1.771 +    }
   1.772 +
   1.773 +    if (data.offset != end) {
   1.774 +      data.offset = end;
   1.775 +    }
   1.776 +
   1.777 +    return result;
   1.778 +  },
   1.779 +
   1.780 +  /**
   1.781 +   * @param data
   1.782 +   *        A wrapped object to store encoded raw data.
   1.783 +   * @param value
   1.784 +   *        A Date object for absolute expiry or an integer for relative one.
   1.785 +   */
   1.786 +  encode: function(data, value) {
   1.787 +    let isDate, begin = data.offset;
   1.788 +    if (value instanceof Date) {
   1.789 +      isDate = true;
   1.790 +      WSP.DateValue.encode(data, value);
   1.791 +    } else if (typeof value == "number") {
   1.792 +      isDate = false;
   1.793 +      WSP.DeltaSecondsValue.encode(data, value);
   1.794 +    } else {
   1.795 +      throw new CodeError("Expiry-value: invalid value type");
   1.796 +    }
   1.797 +
   1.798 +    // Calculate how much octets will be written and seek back.
   1.799 +    // TODO: use memmove, see bug 730873
   1.800 +    let len = data.offset - begin;
   1.801 +    data.offset = begin;
   1.802 +
   1.803 +    WSP.ValueLength.encode(data, len + 1);
   1.804 +    if (isDate) {
   1.805 +      WSP.Octet.encode(data, 128);
   1.806 +      WSP.DateValue.encode(data, value);
   1.807 +    } else {
   1.808 +      WSP.Octet.encode(data, 129);
   1.809 +      WSP.DeltaSecondsValue.encode(data, value);
   1.810 +    }
   1.811 +  },
   1.812 +};
   1.813 +
   1.814 +/**
   1.815 + * From-value = Value-length (Address-present-token Address | Insert-address-token)
   1.816 + * Address-present-token = <Octet 128>
   1.817 + * Insert-address-token = <Octet 129>
   1.818 + *
   1.819 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.21
   1.820 + */
   1.821 +this.FromValue = {
   1.822 +  /**
   1.823 +   * @param data
   1.824 +   *        A wrapped object containing raw PDU data.
   1.825 +   *
   1.826 +   * @return A decoded Address-value or null for MMS Proxy-Relay Insert-Address
   1.827 +   *         mode.
   1.828 +   *
   1.829 +   * @throws CodeError if decoded token equals to neither 128 nor 129.
   1.830 +   */
   1.831 +  decode: function(data) {
   1.832 +    let length = WSP.ValueLength.decode(data);
   1.833 +    let end = data.offset + length;
   1.834 +
   1.835 +    let token = WSP.Octet.decode(data);
   1.836 +    if ((token != 128) && (token != 129)) {
   1.837 +      throw new WSP.CodeError("From-value: invalid token " + token);
   1.838 +    }
   1.839 +
   1.840 +    let result = null;
   1.841 +    if (token == 128) {
   1.842 +      result = Address.decode(data);
   1.843 +    }
   1.844 +
   1.845 +    if (data.offset != end) {
   1.846 +      data.offset = end;
   1.847 +    }
   1.848 +
   1.849 +    return result;
   1.850 +  },
   1.851 +
   1.852 +  /**
   1.853 +   * @param data
   1.854 +   *        A wrapped object to store encoded raw data.
   1.855 +   * @param value
   1.856 +   *        A Address-value or null for MMS Proxy-Relay Insert-Address mode.
   1.857 +   */
   1.858 +  encode: function(data, value) {
   1.859 +    if (!value) {
   1.860 +      WSP.ValueLength.encode(data, 1);
   1.861 +      WSP.Octet.encode(data, 129);
   1.862 +      return;
   1.863 +    }
   1.864 +
   1.865 +    // Calculate how much octets will be written and seek back.
   1.866 +    // TODO: use memmove, see bug 730873
   1.867 +    let begin = data.offset;
   1.868 +    Address.encode(data, value);
   1.869 +    let len = data.offset - begin;
   1.870 +    data.offset = begin;
   1.871 +
   1.872 +    WSP.ValueLength.encode(data, len + 1);
   1.873 +    WSP.Octet.encode(data, 128);
   1.874 +    Address.encode(data, value);
   1.875 +  },
   1.876 +};
   1.877 +
   1.878 +/**
   1.879 + * Previously-sent-by-value = Value-length Forwarded-count-value Address
   1.880 + * Forwarded-count-value = Integer-value
   1.881 + *
   1.882 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.23
   1.883 + */
   1.884 +this.PreviouslySentByValue = {
   1.885 +  /**
   1.886 +   * @param data
   1.887 +   *        A wrapped object containing raw PDU data.
   1.888 +   *
   1.889 +   * @return Decoded object containing an integer `forwardedCount` and an
   1.890 +   *         string-typed `originator` attributes.
   1.891 +   */
   1.892 +  decode: function(data) {
   1.893 +    let length = WSP.ValueLength.decode(data);
   1.894 +    let end = data.offset + length;
   1.895 +
   1.896 +    let result = {};
   1.897 +    result.forwardedCount = WSP.IntegerValue.decode(data);
   1.898 +    result.originator = Address.decode(data);
   1.899 +
   1.900 +    if (data.offset != end) {
   1.901 +      data.offset = end;
   1.902 +    }
   1.903 +
   1.904 +    return result;
   1.905 +  },
   1.906 +};
   1.907 +
   1.908 +/**
   1.909 + * Previously-sent-date-value = Value-length Forwarded-count-value Date-value
   1.910 + *
   1.911 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.23
   1.912 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.24
   1.913 + */
   1.914 +this.PreviouslySentDateValue = {
   1.915 +  /**
   1.916 +   * @param data
   1.917 +   *        A wrapped object containing raw PDU data.
   1.918 +   *
   1.919 +   * @return Decoded object containing an integer `forwardedCount` and an
   1.920 +   *         Date-typed `timestamp` attributes.
   1.921 +   */
   1.922 +  decode: function(data) {
   1.923 +    let length = WSP.ValueLength.decode(data);
   1.924 +    let end = data.offset + length;
   1.925 +
   1.926 +    let result = {};
   1.927 +    result.forwardedCount = WSP.IntegerValue.decode(data);
   1.928 +    result.timestamp = WSP.DateValue.decode(data);
   1.929 +
   1.930 +    if (data.offset != end) {
   1.931 +      data.offset = end;
   1.932 +    }
   1.933 +
   1.934 +    return result;
   1.935 +  },
   1.936 +};
   1.937 +
   1.938 +/**
   1.939 + * Message-class-value = Class-identifier | Token-text
   1.940 + * Class-identifier = Personal | Advertisement | Informational | Auto
   1.941 + * Personal = <Octet 128>
   1.942 + * Advertisement = <Octet 129>
   1.943 + * Informational = <Octet 130>
   1.944 + * Auto = <Octet 131>
   1.945 + *
   1.946 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.27
   1.947 + */
   1.948 +this.MessageClassValue = {
   1.949 +  WELL_KNOWN_CLASSES: ["personal", "advertisement", "informational", "auto"],
   1.950 +
   1.951 +  /**
   1.952 +   * @param data
   1.953 +   *        A wrapped object containing raw PDU data.
   1.954 +   *
   1.955 +   * @return A decoded string.
   1.956 +   *
   1.957 +   * @throws CodeError if decoded value is not in the range 128..131.
   1.958 +   */
   1.959 +  decodeClassIdentifier: function(data) {
   1.960 +    let value = WSP.Octet.decode(data);
   1.961 +    if ((value >= 128) && (value < (128 + this.WELL_KNOWN_CLASSES.length))) {
   1.962 +      return this.WELL_KNOWN_CLASSES[value - 128];
   1.963 +    }
   1.964 +
   1.965 +    throw new WSP.CodeError("Class-identifier: invalid id " + value);
   1.966 +  },
   1.967 +
   1.968 +  /**
   1.969 +   * @param data
   1.970 +   *        A wrapped object containing raw PDU data.
   1.971 +   *
   1.972 +   * @return A decoded string.
   1.973 +   */
   1.974 +  decode: function(data) {
   1.975 +    let begin = data.offset;
   1.976 +    try {
   1.977 +      return this.decodeClassIdentifier(data);
   1.978 +    } catch (e) {
   1.979 +      data.offset = begin;
   1.980 +      return WSP.TokenText.decode(data);
   1.981 +    }
   1.982 +  },
   1.983 +
   1.984 +  /**
   1.985 +   * @param data
   1.986 +   *        A wrapped object to store encoded raw data.
   1.987 +   * @param klass
   1.988 +   */
   1.989 +  encode: function(data, klass) {
   1.990 +    let index = this.WELL_KNOWN_CLASSES.indexOf(klass.toLowerCase());
   1.991 +    if (index >= 0) {
   1.992 +      WSP.Octet.encode(data, index + 128);
   1.993 +    } else {
   1.994 +      WSP.TokenText.encode(data, klass);
   1.995 +    }
   1.996 +  },
   1.997 +};
   1.998 +
   1.999 + /**
  1.1000 + * Message-type-value = <Octet 128..151>
  1.1001 + *
  1.1002 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.30
  1.1003 + */
  1.1004 +this.MessageTypeValue = new RangedValue("Message-type-value", 128, 151);
  1.1005 +
  1.1006 +/**
  1.1007 + * MM-flags-value = Value-length ( Add-token | Remove-token | Filter-token ) Encoded-string-value
  1.1008 + * Add-token = <Octet 128>
  1.1009 + * Remove-token = <Octet 129>
  1.1010 + * Filter-token = <Octet 130>
  1.1011 + *
  1.1012 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.32
  1.1013 + */
  1.1014 +this.MmFlagsValue = {
  1.1015 +  /**
  1.1016 +   * @param data
  1.1017 +   *        A wrapped object containing raw PDU data.
  1.1018 +   *
  1.1019 +   * @return Decoded object containing an integer `type` and an string-typed
  1.1020 +   *         `text` attributes.
  1.1021 +   *
  1.1022 +   * @throws CodeError if decoded value is not in the range 128..130.
  1.1023 +   */
  1.1024 +  decode: function(data) {
  1.1025 +    let length = WSP.ValueLength.decode(data);
  1.1026 +    let end = data.offset + length;
  1.1027 +
  1.1028 +    let result = {};
  1.1029 +    result.type = WSP.Octet.decode(data);
  1.1030 +    if ((result.type < 128) || (result.type > 130)) {
  1.1031 +      throw new WSP.CodeError("MM-flags-value: invalid type " + result.type);
  1.1032 +    }
  1.1033 +    result.text = EncodedStringValue.decode(data);
  1.1034 +
  1.1035 +    if (data.offset != end) {
  1.1036 +      data.offset = end;
  1.1037 +    }
  1.1038 +
  1.1039 +    return result;
  1.1040 +  },
  1.1041 +
  1.1042 +  /**
  1.1043 +   * @param data
  1.1044 +   *        A wrapped object to store encoded raw data.
  1.1045 +   * @param value
  1.1046 +   *        An object containing an integer `type` and an string-typed
  1.1047 +   *        `text` attributes.
  1.1048 +   */
  1.1049 +  encode: function(data, value) {
  1.1050 +    if ((value.type < 128) || (value.type > 130)) {
  1.1051 +      throw new WSP.CodeError("MM-flags-value: invalid type " + value.type);
  1.1052 +    }
  1.1053 +
  1.1054 +    // Calculate how much octets will be written and seek back.
  1.1055 +    // TODO: use memmove, see bug 730873
  1.1056 +    let begin = data.offset;
  1.1057 +    EncodedStringValue.encode(data, value.text);
  1.1058 +    let len = data.offset - begin;
  1.1059 +    data.offset = begin;
  1.1060 +
  1.1061 +    WSP.ValueLength.encode(data, len + 1);
  1.1062 +    WSP.Octet.encode(data, value.type);
  1.1063 +    EncodedStringValue.encode(data, value.text);
  1.1064 +  },
  1.1065 +};
  1.1066 +
  1.1067 +/**
  1.1068 + * MM-state-value = Draft | Sent | New | Retrieved | Forwarded
  1.1069 + * Draft = <Octet 128>
  1.1070 + * Sent = <Octet 129>
  1.1071 + * New = <Octet 130>
  1.1072 + * Retrieved = <Octet 131>
  1.1073 + * Forwarded = <Octet 132>
  1.1074 + *
  1.1075 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.33
  1.1076 + */
  1.1077 +this.MmStateValue = new RangedValue("MM-state-value", 128, 132);
  1.1078 +
  1.1079 +/**
  1.1080 + * Priority-value = Low | Normal | High
  1.1081 + * Low = <Octet 128>
  1.1082 + * Normal = <Octet 129>
  1.1083 + * High = <Octet 130>
  1.1084 + *
  1.1085 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.35
  1.1086 + */
  1.1087 +this.PriorityValue = new RangedValue("Priority-value", 128, 130);
  1.1088 +
  1.1089 +/**
  1.1090 + * Read-status-value = Read | Deleted without being read
  1.1091 + *
  1.1092 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.38
  1.1093 + */
  1.1094 +this.ReadStatusValue = new RangedValue("Read-status-value", 128, 129);
  1.1095 +
  1.1096 +/**
  1.1097 + * Recommended-Retrieval-Mode-value = Manual
  1.1098 + * Manual = <Octet 128>
  1.1099 + *
  1.1100 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.39
  1.1101 + */
  1.1102 +this.RecommendedRetrievalModeValue = {
  1.1103 +  /**
  1.1104 +   * @param data
  1.1105 +   *        A wrapped object containing raw PDU data.
  1.1106 +   *
  1.1107 +   * @return A decoded integer.
  1.1108 +   */
  1.1109 +  decode: function(data) {
  1.1110 +    return WSP.Octet.decodeEqualTo(data, 128);
  1.1111 +  },
  1.1112 +};
  1.1113 +
  1.1114 +/**
  1.1115 + * Reply-charging-value = Requested | Requested text only | Accepted |
  1.1116 + *                        Accepted text only
  1.1117 + * Requested = <Octet 128>
  1.1118 + * Requested text only = <Octet 129>
  1.1119 + * Accepted = <Octet 130>
  1.1120 + * Accepted text only = <Octet 131>
  1.1121 + *
  1.1122 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.43
  1.1123 + */
  1.1124 +this.ReplyChargingValue = new RangedValue("Reply-charging-value", 128, 131);
  1.1125 +
  1.1126 +/**
  1.1127 + * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
  1.1128 + *
  1.1129 + *   Response-text-value = Encoded-string-value
  1.1130 + *
  1.1131 + * When used in the M-Mbox-Delete.conf and M-Delete.conf PDUs:
  1.1132 + *
  1.1133 + *   Response-text-Del-value = Value-length Status-count-value Response-text-value
  1.1134 + *
  1.1135 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.49
  1.1136 + */
  1.1137 +this.ResponseText = {
  1.1138 +  /**
  1.1139 +   * @param data
  1.1140 +   *        A wrapped object containing raw PDU data.
  1.1141 +   * @param options
  1.1142 +   *        Extra context for decoding.
  1.1143 +   *
  1.1144 +   * @return An object containing a string-typed `text` attribute and a
  1.1145 +   *         integer-typed `statusCount` one.
  1.1146 +   */
  1.1147 +  decode: function(data, options) {
  1.1148 +    let type = WSP.ensureHeader(options, "x-mms-message-type");
  1.1149 +
  1.1150 +    let result = {};
  1.1151 +    if ((type == MMS_PDU_TYPE_MBOX_DELETE_CONF)
  1.1152 +        || (type == MMS_PDU_TYPE_DELETE_CONF)) {
  1.1153 +      let length = WSP.ValueLength.decode(data);
  1.1154 +      let end = data.offset + length;
  1.1155 +
  1.1156 +      result.statusCount = WSP.IntegerValue.decode(data);
  1.1157 +      result.text = EncodedStringValue.decode(data);
  1.1158 +
  1.1159 +      if (data.offset != end) {
  1.1160 +        data.offset = end;
  1.1161 +      }
  1.1162 +    } else {
  1.1163 +      result.text = EncodedStringValue.decode(data);
  1.1164 +    }
  1.1165 +
  1.1166 +    return result;
  1.1167 +  },
  1.1168 +};
  1.1169 +
  1.1170 +/**
  1.1171 + * Retrieve-status-value = Ok | Error-transient-failure |
  1.1172 + *                         Error-transient-message-not-found |
  1.1173 + *                         Error-transient-network-problem |
  1.1174 + *                         Error-permanent-failure |
  1.1175 + *                         Error-permanent-service-denied |
  1.1176 + *                         Error-permanent-message-not-found |
  1.1177 + *                         Error-permanent-content-unsupported
  1.1178 + * Ok = <Octet 128>
  1.1179 + * Error-transient-failure = <Octet 192>
  1.1180 + * Error-transient-message-not-found = <Octet 193>
  1.1181 + * Error-transient-network-problem = <Octet 194>
  1.1182 + * Error-permanent-failure = <Octet 224>
  1.1183 + * Error-permanent-service-denied = <Octet 225>
  1.1184 + * Error-permanent-message-not-found = <Octet 226>
  1.1185 + * Error-permanent-content-unsupported = <Octet 227>
  1.1186 + *
  1.1187 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.50
  1.1188 + */
  1.1189 +this.RetrieveStatusValue = {
  1.1190 +  /**
  1.1191 +   * @param data
  1.1192 +   *        A wrapped object containing raw PDU data.
  1.1193 +   *
  1.1194 +   * @return A decoded integer.
  1.1195 +   */
  1.1196 +  decode: function(data) {
  1.1197 +    let value = WSP.Octet.decode(data);
  1.1198 +    if (value == MMS_PDU_ERROR_OK) {
  1.1199 +      return value;
  1.1200 +    }
  1.1201 +
  1.1202 +    if ((value >= MMS_PDU_ERROR_TRANSIENT_FAILURE) && (value < 256)) {
  1.1203 +      return value;
  1.1204 +    }
  1.1205 +
  1.1206 +    // Any other values SHALL NOT be used. They are reserved for future use.
  1.1207 +    // An MMS Client that receives such a reserved value MUST react the same
  1.1208 +    // as it does to the value 224 (Error-permanent-failure).
  1.1209 +    return MMS_PDU_ERROR_PERMANENT_FAILURE;
  1.1210 +  },
  1.1211 +};
  1.1212 +
  1.1213 +/**
  1.1214 + * Sender-visibility-value = Hide | Show
  1.1215 + *
  1.1216 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.52
  1.1217 + */
  1.1218 +this.SenderVisibilityValue = new RangedValue("Sender-visibility-value", 128, 129);
  1.1219 +
  1.1220 +/**
  1.1221 + * Status-value = Expired | Retrieved | Rejected | Deferred | Unrecognised |
  1.1222 + *                Indeterminate | Forwarded | Unreachable
  1.1223 + * Expired = <Octet 128>
  1.1224 + * Retrieved = <Octet 129>
  1.1225 + * Rejected = <Octet 130>
  1.1226 + * Deferred = <Octet 131>
  1.1227 + * Unrecognised = <Octet 132>
  1.1228 + * Indeterminate = <Octet 133>
  1.1229 + * Forwarded = <Octet 134>
  1.1230 + * Unreachable = <Octet 135>
  1.1231 + *
  1.1232 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.54
  1.1233 + */
  1.1234 +this.StatusValue = new RangedValue("Status-value", 128, 135);
  1.1235 +
  1.1236 +this.PduHelper = {
  1.1237 +  /**
  1.1238 +   * @param data
  1.1239 +   *        A wrapped object containing raw PDU data.
  1.1240 +   * @param headers
  1.1241 +   *        An optional object to store parsed header fields. Created
  1.1242 +   *        automatically if undefined.
  1.1243 +   *
  1.1244 +   * @return A boolean value indicating whether it's followed by message body.
  1.1245 +   */
  1.1246 +  parseHeaders: function(data, headers) {
  1.1247 +    if (!headers) {
  1.1248 +      headers = {};
  1.1249 +    }
  1.1250 +
  1.1251 +    let header;
  1.1252 +    while (data.offset < data.array.length) {
  1.1253 +      // There is no `header length` information in MMS PDU. If we just got
  1.1254 +      // something wrong in parsing header fields, we might not be able to
  1.1255 +      // determine the correct header-content boundary.
  1.1256 +      header = HeaderField.decode(data, headers);
  1.1257 +
  1.1258 +      if (header) {
  1.1259 +        let orig = headers[header.name];
  1.1260 +        if (Array.isArray(orig)) {
  1.1261 +          headers[header.name].push(header.value);
  1.1262 +        } else if (orig) {
  1.1263 +          headers[header.name] = [orig, header.value];
  1.1264 +        } else {
  1.1265 +          headers[header.name] = header.value;
  1.1266 +        }
  1.1267 +        if (header.name == "content-type") {
  1.1268 +          // `... if the PDU contains a message body the Content Type MUST be
  1.1269 +          // the last header field, followed by message body.` See
  1.1270 +          // OMA-TS-MMS_ENC-V1_3-20110913-A section 7.
  1.1271 +          break;
  1.1272 +        }
  1.1273 +      }
  1.1274 +    }
  1.1275 +
  1.1276 +    return headers;
  1.1277 +  },
  1.1278 +
  1.1279 +  /**
  1.1280 +   * @param data
  1.1281 +   *        A wrapped object containing raw PDU data.
  1.1282 +   * @param msg
  1.1283 +   *        A message object to store decoded multipart or octet array content.
  1.1284 +   */
  1.1285 +  parseContent: function(data, msg) {
  1.1286 +    let contentType = msg.headers["content-type"].media;
  1.1287 +    if ((contentType == "application/vnd.wap.multipart.related")
  1.1288 +        || (contentType == "application/vnd.wap.multipart.mixed")) {
  1.1289 +      msg.parts = WSP.PduHelper.parseMultiPart(data);
  1.1290 +      return;
  1.1291 +    }
  1.1292 +
  1.1293 +    if (data.offset >= data.array.length) {
  1.1294 +      return;
  1.1295 +    }
  1.1296 +
  1.1297 +    msg.content = WSP.Octet.decodeMultiple(data, data.array.length);
  1.1298 +    if (false) {
  1.1299 +      for (let begin = 0; begin < msg.content.length; begin += 20) {
  1.1300 +        debug("content: " + JSON.stringify(msg.content.subarray(begin, begin + 20)));
  1.1301 +      }
  1.1302 +    }
  1.1303 +  },
  1.1304 +
  1.1305 +  /**
  1.1306 +   * Check existences of all mandatory fields of a MMS message. Also sets `type`
  1.1307 +   * for convenient access.
  1.1308 +   *
  1.1309 +   * @param msg
  1.1310 +   *        A MMS message object.
  1.1311 +   *
  1.1312 +   * @return The corresponding entry in MMS_PDU_TYPES;
  1.1313 +   *
  1.1314 +   * @throws FatalCodeError if the PDU type is not supported yet.
  1.1315 +   */
  1.1316 +  checkMandatoryFields: function(msg) {
  1.1317 +    let type = WSP.ensureHeader(msg.headers, "x-mms-message-type");
  1.1318 +    let entry = MMS_PDU_TYPES[type];
  1.1319 +    if (!entry) {
  1.1320 +      throw new WSP.FatalCodeError(
  1.1321 +        "checkMandatoryFields: unsupported message type " + type);
  1.1322 +    }
  1.1323 +
  1.1324 +    entry.mandatoryFields.forEach(function(name) {
  1.1325 +      WSP.ensureHeader(msg.headers, name);
  1.1326 +    });
  1.1327 +
  1.1328 +    // Setup convenient alias that referenced frequently.
  1.1329 +    msg.type = type;
  1.1330 +
  1.1331 +    return entry;
  1.1332 +  },
  1.1333 +
  1.1334 +  /**
  1.1335 +   * @param data
  1.1336 +   *        A wrapped object containing raw PDU data.
  1.1337 +   * @param msg [optional]
  1.1338 +   *        Optional target object for decoding.
  1.1339 +   *
  1.1340 +   * @return A MMS message object or null in case of errors found.
  1.1341 +   */
  1.1342 +  parse: function(data, msg) {
  1.1343 +    if (!msg) {
  1.1344 +      msg = {};
  1.1345 +    }
  1.1346 +
  1.1347 +    try {
  1.1348 +      msg.headers = this.parseHeaders(data, msg.headers);
  1.1349 +
  1.1350 +      // Validity checks
  1.1351 +      let typeinfo = this.checkMandatoryFields(msg);
  1.1352 +      if (typeinfo.hasContent) {
  1.1353 +        this.parseContent(data, msg);
  1.1354 +      }
  1.1355 +    } catch (e) {
  1.1356 +      debug("Failed to parse MMS message, error message: " + e.message);
  1.1357 +      return null;
  1.1358 +    }
  1.1359 +
  1.1360 +    return msg;
  1.1361 +  },
  1.1362 +
  1.1363 +  /**
  1.1364 +   * @param data
  1.1365 +   *        A wrapped object to store encoded raw data.
  1.1366 +   * @param headers
  1.1367 +   *        A dictionary object containing multiple name/value mapping.
  1.1368 +   * @param name
  1.1369 +   *        Name of the header field to be encoded.
  1.1370 +   */
  1.1371 +  encodeHeader: function(data, headers, name) {
  1.1372 +    let value = headers[name];
  1.1373 +    if (Array.isArray(value)) {
  1.1374 +      for (let i = 0; i < value.length; i++) {
  1.1375 +        HeaderField.encode(data, {name: name, value: value[i]}, headers);
  1.1376 +      }
  1.1377 +    } else {
  1.1378 +      HeaderField.encode(data, {name: name, value: value}, headers);
  1.1379 +    }
  1.1380 +  },
  1.1381 +
  1.1382 +  /**
  1.1383 +   * @param data
  1.1384 +   *        A wrapped object to store encoded raw data.
  1.1385 +   * @param headers
  1.1386 +   *        A dictionary object containing multiple name/value mapping.
  1.1387 +   */
  1.1388 +  encodeHeaderIfExists: function(data, headers, name) {
  1.1389 +    // Header value could be zero or null.
  1.1390 +    if (headers[name] !== undefined) {
  1.1391 +      this.encodeHeader(data, headers, name);
  1.1392 +    }
  1.1393 +  },
  1.1394 +
  1.1395 +  /**
  1.1396 +   * @param data [optional]
  1.1397 +   *        A wrapped object to store encoded raw data. Created if undefined.
  1.1398 +   * @param headers
  1.1399 +   *        A dictionary object containing multiple name/value mapping.
  1.1400 +   *
  1.1401 +   * @return the passed data parameter or a created one.
  1.1402 +   */
  1.1403 +  encodeHeaders: function(data, headers) {
  1.1404 +    if (!data) {
  1.1405 +      data = {array: [], offset: 0};
  1.1406 +    }
  1.1407 +
  1.1408 +    // `In the encoding of the header fields, the order of the fields is not
  1.1409 +    // significant, except that X-Mms-Message-Type, X-Mms-Transaction-ID (when
  1.1410 +    // present) and X-Mms-MMS-Version MUST be at the beginning of the message
  1.1411 +    // headers, in that order, and if the PDU contains a message body the
  1.1412 +    // Content Type MUST be the last header field, followed by message body.`
  1.1413 +    // ~ OMA-TS-MMS_ENC-V1_3-20110913-A section 7
  1.1414 +    this.encodeHeader(data, headers, "x-mms-message-type");
  1.1415 +    this.encodeHeaderIfExists(data, headers, "x-mms-transaction-id");
  1.1416 +    this.encodeHeaderIfExists(data, headers, "x-mms-mms-version");
  1.1417 +
  1.1418 +    for (let key in headers) {
  1.1419 +      if ((key == "x-mms-message-type")
  1.1420 +          || (key == "x-mms-transaction-id")
  1.1421 +          || (key == "x-mms-mms-version")
  1.1422 +          || (key == "content-type")) {
  1.1423 +        continue;
  1.1424 +      }
  1.1425 +      this.encodeHeader(data, headers, key);
  1.1426 +    }
  1.1427 +
  1.1428 +    this.encodeHeaderIfExists(data, headers, "content-type");
  1.1429 +
  1.1430 +    return data;
  1.1431 +  },
  1.1432 +
  1.1433 +  /**
  1.1434 +   * @param multiStream
  1.1435 +   *        An exsiting nsIMultiplexInputStream.
  1.1436 +   * @param msg
  1.1437 +   *        A MMS message object.
  1.1438 +   *
  1.1439 +   * @return An instance of nsIMultiplexInputStream or null in case of errors.
  1.1440 +   */
  1.1441 +  compose: function(multiStream, msg) {
  1.1442 +    if (!multiStream) {
  1.1443 +      multiStream = Cc["@mozilla.org/io/multiplex-input-stream;1"]
  1.1444 +                    .createInstance(Ci.nsIMultiplexInputStream);
  1.1445 +    }
  1.1446 +
  1.1447 +    try {
  1.1448 +      // Validity checks
  1.1449 +      let typeinfo = this.checkMandatoryFields(msg);
  1.1450 +
  1.1451 +      let data = this.encodeHeaders(null, msg.headers);
  1.1452 +      debug("Composed PDU Header: " + JSON.stringify(data.array));
  1.1453 +      WSP.PduHelper.appendArrayToMultiStream(multiStream, data.array, data.offset);
  1.1454 +
  1.1455 +      if (msg.content) {
  1.1456 +        WSP.PduHelper.appendArrayToMultiStream(multiStream, msg.content, msg.content.length);
  1.1457 +      } else if (msg.parts) {
  1.1458 +        WSP.PduHelper.composeMultiPart(multiStream, msg.parts);
  1.1459 +      } else if (typeinfo.hasContent) {
  1.1460 +        throw new WSP.CodeError("Missing message content");
  1.1461 +      }
  1.1462 +
  1.1463 +      return multiStream;
  1.1464 +    } catch (e) {
  1.1465 +      debug("Failed to compose MMS message, error message: " + e.message);
  1.1466 +      return null;
  1.1467 +    }
  1.1468 +  },
  1.1469 +};
  1.1470 +
  1.1471 +const MMS_PDU_TYPES = (function() {
  1.1472 +  let pdus = {};
  1.1473 +  function add(number, hasContent, mandatoryFields) {
  1.1474 +    pdus[number] = {
  1.1475 +      number: number,
  1.1476 +      hasContent: hasContent,
  1.1477 +      mandatoryFields: mandatoryFields,
  1.1478 +    };
  1.1479 +  }
  1.1480 +
  1.1481 +  add(MMS_PDU_TYPE_SEND_REQ, true, ["x-mms-message-type",
  1.1482 +                                    "x-mms-transaction-id",
  1.1483 +                                    "x-mms-mms-version",
  1.1484 +                                    "from",
  1.1485 +                                    "content-type"]);
  1.1486 +  add(MMS_PDU_TYPE_SEND_CONF, false, ["x-mms-message-type",
  1.1487 +                                      "x-mms-transaction-id",
  1.1488 +                                      "x-mms-mms-version",
  1.1489 +                                      "x-mms-response-status"]);
  1.1490 +  add(MMS_PDU_TYPE_NOTIFICATION_IND, false, ["x-mms-message-type",
  1.1491 +                                             "x-mms-transaction-id",
  1.1492 +                                             "x-mms-mms-version",
  1.1493 +                                             "x-mms-message-class",
  1.1494 +                                             "x-mms-message-size",
  1.1495 +                                             "x-mms-expiry",
  1.1496 +                                             "x-mms-content-location"]);
  1.1497 +  add(MMS_PDU_TYPE_RETRIEVE_CONF, true, ["x-mms-message-type",
  1.1498 +                                         "x-mms-mms-version",
  1.1499 +                                         "date",
  1.1500 +                                         "content-type"]);
  1.1501 +  add(MMS_PDU_TYPE_NOTIFYRESP_IND, false, ["x-mms-message-type",
  1.1502 +                                           "x-mms-transaction-id",
  1.1503 +                                           "x-mms-mms-version",
  1.1504 +                                           "x-mms-status"]);
  1.1505 +  add(MMS_PDU_TYPE_DELIVERY_IND, false, ["x-mms-message-type",
  1.1506 +                                         "x-mms-mms-version",
  1.1507 +                                         "message-id",
  1.1508 +                                         "to",
  1.1509 +                                         "date",
  1.1510 +                                         "x-mms-status"]);
  1.1511 +  add(MMS_PDU_TYPE_ACKNOWLEDGE_IND, false, ["x-mms-message-type",
  1.1512 +                                            "x-mms-transaction-id",
  1.1513 +                                            "x-mms-mms-version"]);
  1.1514 +  add(MMS_PDU_TYPE_READ_REC_IND, false, ["x-mms-message-type",
  1.1515 +                                         "message-id",
  1.1516 +                                         "x-mms-mms-version",
  1.1517 +                                         "to",
  1.1518 +                                         "from",
  1.1519 +                                         "x-mms-read-status"]);
  1.1520 +  add(MMS_PDU_TYPE_READ_ORIG_IND, false, ["x-mms-message-type",
  1.1521 +                                          "x-mms-mms-version",
  1.1522 +                                          "message-id",
  1.1523 +                                          "to",
  1.1524 +                                          "from",
  1.1525 +                                          "date",
  1.1526 +                                          "x-mms-read-status"]);
  1.1527 +
  1.1528 +  return pdus;
  1.1529 +})();
  1.1530 +
  1.1531 +/**
  1.1532 + * Header field names and assigned numbers.
  1.1533 + *
  1.1534 + * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.4
  1.1535 + */
  1.1536 +const MMS_HEADER_FIELDS = (function() {
  1.1537 +  let names = {};
  1.1538 +  function add(name, number, coder) {
  1.1539 +    let entry = {
  1.1540 +      name: name,
  1.1541 +      number: number,
  1.1542 +      coder: coder,
  1.1543 +    };
  1.1544 +    names[name] = names[number] = entry;
  1.1545 +  }
  1.1546 +
  1.1547 +  add("bcc",                                     0x01, Address);
  1.1548 +  add("cc",                                      0x02, Address);
  1.1549 +  add("x-mms-content-location",                  0x03, ContentLocationValue);
  1.1550 +  add("content-type",                            0x04, WSP.ContentTypeValue);
  1.1551 +  add("date",                                    0x05, WSP.DateValue);
  1.1552 +  add("x-mms-delivery-report",                   0x06, BooleanValue);
  1.1553 +  add("x-mms-delivery-time",                     0x07, ExpiryValue);
  1.1554 +  add("x-mms-expiry",                            0x08, ExpiryValue);
  1.1555 +  add("from",                                    0x09, FromValue);
  1.1556 +  add("x-mms-message-class",                     0x0A, MessageClassValue);
  1.1557 +  add("message-id",                              0x0B, WSP.TextString);
  1.1558 +  add("x-mms-message-type",                      0x0C, MessageTypeValue);
  1.1559 +  add("x-mms-mms-version",                       0x0D, WSP.ShortInteger);
  1.1560 +  add("x-mms-message-size",                      0x0E, WSP.LongInteger);
  1.1561 +  add("x-mms-priority",                          0x0F, PriorityValue);
  1.1562 +  add("x-mms-read-report",                       0x10, BooleanValue);
  1.1563 +  add("x-mms-report-allowed",                    0x11, BooleanValue);
  1.1564 +  add("x-mms-response-status",                   0x12, RetrieveStatusValue);
  1.1565 +  add("x-mms-response-text",                     0x13, ResponseText);
  1.1566 +  add("x-mms-sender-visibility",                 0x14, SenderVisibilityValue);
  1.1567 +  add("x-mms-status",                            0x15, StatusValue);
  1.1568 +  add("subject",                                 0x16, EncodedStringValue);
  1.1569 +  add("to",                                      0x17, Address);
  1.1570 +  add("x-mms-transaction-id",                    0x18, WSP.TextString);
  1.1571 +  add("x-mms-retrieve-status",                   0x19, RetrieveStatusValue);
  1.1572 +  add("x-mms-retrieve-text",                     0x1A, EncodedStringValue);
  1.1573 +  add("x-mms-read-status",                       0x1B, ReadStatusValue);
  1.1574 +  add("x-mms-reply-charging",                    0x1C, ReplyChargingValue);
  1.1575 +  add("x-mms-reply-charging-deadline",           0x1D, ExpiryValue);
  1.1576 +  add("x-mms-reply-charging-id",                 0x1E, WSP.TextString);
  1.1577 +  add("x-mms-reply-charging-size",               0x1F, WSP.LongInteger);
  1.1578 +  add("x-mms-previously-sent-by",                0x20, PreviouslySentByValue);
  1.1579 +  add("x-mms-previously-sent-date",              0x21, PreviouslySentDateValue);
  1.1580 +  add("x-mms-store",                             0x22, BooleanValue);
  1.1581 +  add("x-mms-mm-state",                          0x23, MmStateValue);
  1.1582 +  add("x-mms-mm-flags",                          0x24, MmFlagsValue);
  1.1583 +  add("x-mms-store-status",                      0x25, RetrieveStatusValue);
  1.1584 +  add("x-mms-store-status-text",                 0x26, EncodedStringValue);
  1.1585 +  add("x-mms-stored",                            0x27, BooleanValue);
  1.1586 +  //add("x-mms-attributes", 0x28);
  1.1587 +  add("x-mms-totals",                            0x29, BooleanValue);
  1.1588 +  //add("x-mms-mbox-totals", 0x2A);
  1.1589 +  add("x-mms-quotas",                            0x2B, BooleanValue);
  1.1590 +  //add("x-mms-mbox-quotas", 0x2C);
  1.1591 +  add("x-mms-message-count",                     0x2D, WSP.IntegerValue);
  1.1592 +  //add("content", 0x2E);
  1.1593 +  add("x-mms-start",                             0x2F, WSP.IntegerValue);
  1.1594 +  //add("additional-headers", 0x30);
  1.1595 +  add("x-mms-distribution-indicator",            0x31, BooleanValue);
  1.1596 +  add("x-mms-element-descriptor",                0x32, ElementDescriptorValue);
  1.1597 +  add("x-mms-limit",                             0x33, WSP.IntegerValue);
  1.1598 +  add("x-mms-recommended-retrieval-mode",        0x34, RecommendedRetrievalModeValue);
  1.1599 +  add("x-mms-recommended-retrieval-mode-text",   0x35, EncodedStringValue);
  1.1600 +  //add("x-mms-status-text", 0x36);
  1.1601 +  add("x-mms-applic-id",                         0x37, WSP.TextString);
  1.1602 +  add("x-mms-reply-applic-id",                   0x38, WSP.TextString);
  1.1603 +  add("x-mms-aux-applic-id",                     0x39, WSP.TextString);
  1.1604 +  add("x-mms-content-class",                     0x3A, ContentClassValue);
  1.1605 +  add("x-mms-drm-content",                       0x3B, BooleanValue);
  1.1606 +  add("x-mms-adaptation-allowed",                0x3C, BooleanValue);
  1.1607 +  add("x-mms-replace-id",                        0x3D, WSP.TextString);
  1.1608 +  add("x-mms-cancel-id",                         0x3E, WSP.TextString);
  1.1609 +  add("x-mms-cancel-status",                     0x3F, CancelStatusValue);
  1.1610 +
  1.1611 +  return names;
  1.1612 +})();
  1.1613 +
  1.1614 +// @see OMA-TS-MMS_ENC-V1_3-20110913-A Table 27: Parameter Name Assignments
  1.1615 +const MMS_WELL_KNOWN_PARAMS = (function() {
  1.1616 +  let params = {};
  1.1617 +
  1.1618 +  function add(name, number, coder) {
  1.1619 +    let entry = {
  1.1620 +      name: name,
  1.1621 +      number: number,
  1.1622 +      coder: coder,
  1.1623 +    };
  1.1624 +    params[name] = params[number] = entry;
  1.1625 +  }
  1.1626 +
  1.1627 +  // Encoding Version: 1.2
  1.1628 +  add("type", 0x02, WSP.TypeValue);
  1.1629 +
  1.1630 +  return params;
  1.1631 +})();
  1.1632 +
  1.1633 +let debug;
  1.1634 +if (DEBUG) {
  1.1635 +  debug = function(s) {
  1.1636 +    dump("-$- MmsPduHelper: " + s + "\n");
  1.1637 +  };
  1.1638 +} else {
  1.1639 +  debug = function(s) {};
  1.1640 +}
  1.1641 +
  1.1642 +this.EXPORTED_SYMBOLS = ALL_CONST_SYMBOLS.concat([
  1.1643 +  // Constant values
  1.1644 +  "MMS_VERSION",
  1.1645 +
  1.1646 +  // Utility functions
  1.1647 +  "translatePduErrorToStatus",
  1.1648 +
  1.1649 +  // Decoders
  1.1650 +  "BooleanValue",
  1.1651 +  "Address",
  1.1652 +  "HeaderField",
  1.1653 +  "MmsHeader",
  1.1654 +  "CancelStatusValue",
  1.1655 +  "ContentClassValue",
  1.1656 +  "ContentLocationValue",
  1.1657 +  "ElementDescriptorValue",
  1.1658 +  "Parameter",
  1.1659 +  "EncodedStringValue",
  1.1660 +  "ExpiryValue",
  1.1661 +  "FromValue",
  1.1662 +  "PreviouslySentByValue",
  1.1663 +  "PreviouslySentDateValue",
  1.1664 +  "MessageClassValue",
  1.1665 +  "MessageTypeValue",
  1.1666 +  "MmFlagsValue",
  1.1667 +  "MmStateValue",
  1.1668 +  "PriorityValue",
  1.1669 +  "ReadStatusValue",
  1.1670 +  "RecommendedRetrievalModeValue",
  1.1671 +  "ReplyChargingValue",
  1.1672 +  "ResponseText",
  1.1673 +  "RetrieveStatusValue",
  1.1674 +  "SenderVisibilityValue",
  1.1675 +  "StatusValue",
  1.1676 +
  1.1677 +  // Parser
  1.1678 +  "PduHelper",
  1.1679 +]);
  1.1680 +

mercurial