dom/mobilemessage/src/gonk/WspPduHelper.jsm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/mobilemessage/src/gonk/WspPduHelper.jsm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2899 @@
     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 +Cu.import("resource://gre/modules/wap_consts.js", this);
    1.13 +
    1.14 +let DEBUG; // set to true to see debug messages
    1.15 +
    1.16 +// Special ASCII characters
    1.17 +const NUL = 0;
    1.18 +const CR = 13;
    1.19 +const LF = 10;
    1.20 +const SP = 32;
    1.21 +const HT = 9;
    1.22 +const DQUOTE = 34;
    1.23 +const DEL = 127;
    1.24 +
    1.25 +// Special ASCII character ranges
    1.26 +const CTLS = 32;
    1.27 +const ASCIIS = 128;
    1.28 +
    1.29 +/**
    1.30 + * Error class for generic encoding/decoding failures.
    1.31 + */
    1.32 +this.CodeError = function CodeError(message) {
    1.33 +  this.name = "CodeError";
    1.34 +  this.message = message || "Invalid format";
    1.35 +}
    1.36 +CodeError.prototype = new Error();
    1.37 +CodeError.prototype.constructor = CodeError;
    1.38 +
    1.39 +/**
    1.40 + * Error class for unexpected NUL char at decoding text elements.
    1.41 + *
    1.42 + * @param message [optional]
    1.43 + *        A short description for the error.
    1.44 + */
    1.45 +function NullCharError(message) {
    1.46 +  this.name = "NullCharError";
    1.47 +  this.message = message || "Null character found";
    1.48 +}
    1.49 +NullCharError.prototype = new CodeError();
    1.50 +NullCharError.prototype.constructor = NullCharError;
    1.51 +
    1.52 +/**
    1.53 + * Error class for fatal encoding/decoding failures.
    1.54 + *
    1.55 + * This error is only raised when expected format isn't met and the parser
    1.56 + * context can't do anything more to either skip it or hand over to other
    1.57 + * alternative encoding/decoding steps.
    1.58 + *
    1.59 + * @param message [optional]
    1.60 + *        A short description for the error.
    1.61 + */
    1.62 +this.FatalCodeError = function FatalCodeError(message) {
    1.63 +  this.name = "FatalCodeError";
    1.64 +  this.message = message || "Decoding fails";
    1.65 +}
    1.66 +FatalCodeError.prototype = new Error();
    1.67 +FatalCodeError.prototype.constructor = FatalCodeError;
    1.68 +
    1.69 +/**
    1.70 + * Error class for undefined well known encoding.
    1.71 + *
    1.72 + * When a encoded header field/parameter has unknown/unsupported value, we may
    1.73 + * never know how to decode the next value. For example, a parameter of
    1.74 + * undefined well known encoding may be followed by a Q-value, which is
    1.75 + * basically a uintvar. However, there is no way you can distiguish an Q-value
    1.76 + * 0.64, encoded as 0x41, from a string begins with 'A', which is also 0x41.
    1.77 + * The `skipValue` will try the latter one, which is not expected.
    1.78 + *
    1.79 + * @param message [optional]
    1.80 + *        A short description for the error.
    1.81 + */
    1.82 +this.NotWellKnownEncodingError = function NotWellKnownEncodingError(message) {
    1.83 +  this.name = "NotWellKnownEncodingError";
    1.84 +  this.message = message || "Not well known encoding";
    1.85 +}
    1.86 +NotWellKnownEncodingError.prototype = new FatalCodeError();
    1.87 +NotWellKnownEncodingError.prototype.constructor = NotWellKnownEncodingError;
    1.88 +
    1.89 +/**
    1.90 + * Internal helper function to retrieve the value of a property with its name
    1.91 + * specified by `name` inside the object `headers`.
    1.92 + *
    1.93 + * @param headers
    1.94 + *        An object that contains parsed header fields.
    1.95 + * @param name
    1.96 + *        Header name string to be checked.
    1.97 + *
    1.98 + * @return Value of specified header field.
    1.99 + *
   1.100 + * @throws FatalCodeError if headers[name] is undefined.
   1.101 + */
   1.102 +this.ensureHeader = function ensureHeader(headers, name) {
   1.103 +  let value = headers[name];
   1.104 +  // Header field might have a null value as NoValue
   1.105 +  if (value === undefined) {
   1.106 +    throw new FatalCodeError("ensureHeader: header " + name + " not defined");
   1.107 +  }
   1.108 +  return value;
   1.109 +}
   1.110 +
   1.111 +/**
   1.112 + * Skip field value.
   1.113 + *
   1.114 + * The WSP field values are encoded so that the length of the field value can
   1.115 + * always be determined, even if the detailed format of a specific field value
   1.116 + * is not known. This makes it possible to skip over individual header fields
   1.117 + * without interpreting their content. ... the first octet in all the field
   1.118 + * values can be interpreted as follows:
   1.119 + *
   1.120 + *   0 -  30 | This octet is followed by the indicated number (0 - 30) of data
   1.121 + *             octets.
   1.122 + *        31 | This octet is followed by a unitvar, which indicates the number
   1.123 + *             of data octets after it.
   1.124 + *  32 - 127 | The value is a text string, terminated by a zero octet (NUL
   1.125 + *             character).
   1.126 + * 128 - 255 | It is an encoded 7-bit value; this header has no more data.
   1.127 + *
   1.128 + * @param data
   1.129 + *        A wrapped object containing raw PDU data.
   1.130 + *
   1.131 + * @return Skipped value of several possible types like string, integer, or
   1.132 + *         an array of octets.
   1.133 + *
   1.134 + * @see WAP-230-WSP-20010705-a clause 8.4.1.2
   1.135 + */
   1.136 +this.skipValue = function skipValue(data) {
   1.137 +  let begin = data.offset;
   1.138 +  let value = Octet.decode(data);
   1.139 +  if (value <= 31) {
   1.140 +    if (value == 31) {
   1.141 +      value = UintVar.decode(data);
   1.142 +    }
   1.143 +
   1.144 +    if (value) {
   1.145 +      // `value` can be larger than 30, max length of a multi-octet integer
   1.146 +      // here. So we must decode it as an array instead.
   1.147 +      value = Octet.decodeMultiple(data, data.offset + value);
   1.148 +    } else {
   1.149 +      value = null;
   1.150 +    }
   1.151 +  } else if (value <= 127) {
   1.152 +    data.offset = begin;
   1.153 +    value = NullTerminatedTexts.decode(data);
   1.154 +  } else {
   1.155 +    value &= 0x7F;
   1.156 +  }
   1.157 +
   1.158 +  return value;
   1.159 +}
   1.160 +
   1.161 +/**
   1.162 + * Helper function for decoding multiple alternative forms.
   1.163 + *
   1.164 + * @param data
   1.165 + *        A wrapped object containing raw PDU data.
   1.166 + * @param options
   1.167 + *        Extra context for decoding.
   1.168 + *
   1.169 + * @return Decoded value.
   1.170 + */
   1.171 +this.decodeAlternatives = function decodeAlternatives(data, options) {
   1.172 +  let begin = data.offset;
   1.173 +  for (let i = 2; i < arguments.length; i++) {
   1.174 +    try {
   1.175 +      return arguments[i].decode(data, options);
   1.176 +    } catch (e) {
   1.177 +      // Throw the last exception we get
   1.178 +      if (i == (arguments.length - 1)) {
   1.179 +        throw e;
   1.180 +      }
   1.181 +
   1.182 +      data.offset = begin;
   1.183 +    }
   1.184 +  }
   1.185 +}
   1.186 +
   1.187 +/**
   1.188 + * Helper function for encoding multiple alternative forms.
   1.189 + *
   1.190 + * @param data
   1.191 + *        A wrapped object to store encoded raw data.
   1.192 + * @param value
   1.193 + *        Object value of arbitrary type to be encoded.
   1.194 + * @param options
   1.195 + *        Extra context for encoding.
   1.196 + */
   1.197 +this.encodeAlternatives = function encodeAlternatives(data, value, options) {
   1.198 +  let begin = data.offset;
   1.199 +  for (let i = 3; i < arguments.length; i++) {
   1.200 +    try {
   1.201 +      arguments[i].encode(data, value, options);
   1.202 +      return;
   1.203 +    } catch (e) {
   1.204 +      // Throw the last exception we get
   1.205 +      if (i == (arguments.length - 1)) {
   1.206 +        throw e;
   1.207 +      }
   1.208 +
   1.209 +      data.offset = begin;
   1.210 +    }
   1.211 +  }
   1.212 +}
   1.213 +
   1.214 +this.Octet = {
   1.215 +  /**
   1.216 +   * @param data
   1.217 +   *        A wrapped object containing raw PDU data.
   1.218 +   *
   1.219 +   * @throws RangeError if no more data is available.
   1.220 +   */
   1.221 +  decode: function(data) {
   1.222 +    if (data.offset >= data.array.length) {
   1.223 +      throw new RangeError();
   1.224 +    }
   1.225 +
   1.226 +    return data.array[data.offset++];
   1.227 +  },
   1.228 +
   1.229 +  /**
   1.230 +   * @param data
   1.231 +   *        A wrapped object containing raw PDU data.
   1.232 +   * @param end
   1.233 +   *        An ending offset indicating the end of octet array to read.
   1.234 +   *
   1.235 +   * @return A decoded array object.
   1.236 +   *
   1.237 +   * @throws RangeError if no enough data to read.
   1.238 +   * @throws TypeError if `data` has neither subarray() nor slice() method.
   1.239 +   */
   1.240 +  decodeMultiple: function(data, end) {
   1.241 +    if ((end < data.offset) || (end > data.array.length)) {
   1.242 +      throw new RangeError();
   1.243 +    }
   1.244 +    if (end == data.offset) {
   1.245 +      return [];
   1.246 +    }
   1.247 +
   1.248 +    let result;
   1.249 +    if (data.array.subarray) {
   1.250 +      result = data.array.subarray(data.offset, end);
   1.251 +    } else if (data.array.slice) {
   1.252 +      result = data.array.slice(data.offset, end);
   1.253 +    } else {
   1.254 +      throw new TypeError();
   1.255 +    }
   1.256 +
   1.257 +    data.offset = end;
   1.258 +    return result;
   1.259 +  },
   1.260 +
   1.261 +  /**
   1.262 +   * Internal octet decoding for specific value.
   1.263 +   *
   1.264 +   * @param data
   1.265 +   *        A wrapped object containing raw PDU data.
   1.266 +   * @param expected
   1.267 +   *        Expected octet value.
   1.268 +   *
   1.269 +   * @return Expected octet value.
   1.270 +   *
   1.271 +   * @throws CodeError if read octet is not equal to expected one.
   1.272 +   */
   1.273 +  decodeEqualTo: function(data, expected) {
   1.274 +    if (this.decode(data) != expected) {
   1.275 +      throw new CodeError("Octet - decodeEqualTo: doesn't match " + expected);
   1.276 +    }
   1.277 +
   1.278 +    return expected;
   1.279 +  },
   1.280 +
   1.281 +  /**
   1.282 +   * @param data
   1.283 +   *        A wrapped object to store encoded raw data.
   1.284 +   * @param octet
   1.285 +   *        Octet value to be encoded.
   1.286 +   */
   1.287 +  encode: function(data, octet) {
   1.288 +    if (data.offset >= data.array.length) {
   1.289 +      data.array.push(octet);
   1.290 +      data.offset++;
   1.291 +    } else {
   1.292 +      data.array[data.offset++] = octet;
   1.293 +    }
   1.294 +  },
   1.295 +
   1.296 +  /**
   1.297 +   * @param data
   1.298 +   *        A wrapped object to store encoded raw data.
   1.299 +   * @param octet
   1.300 +   *        An octet array object.
   1.301 +   */
   1.302 +  encodeMultiple: function(data, array) {
   1.303 +    for (let i = 0; i < array.length; i++) {
   1.304 +      this.encode(data, array[i]);
   1.305 +    }
   1.306 +  },
   1.307 +};
   1.308 +
   1.309 +/**
   1.310 + * TEXT = <any OCTET except CTLs, but including LWS>
   1.311 + * CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
   1.312 + * LWS = [CRLF] 1*(SP|HT)
   1.313 + * CRLF = CR LF
   1.314 + * CR = <US-ASCII CR, carriage return (13)>
   1.315 + * LF = <US-ASCII LF, linefeed (10)>
   1.316 + * SP = <US-ASCII SP, space (32)>
   1.317 + * HT = <US-ASCII HT, horizontal-tab(9)>
   1.318 + *
   1.319 + * @see RFC 2616 clause 2.2 Basic Rules
   1.320 + */
   1.321 +this.Text = {
   1.322 +  /**
   1.323 +   * @param data
   1.324 +   *        A wrapped object containing raw PDU data.
   1.325 +   *
   1.326 +   * @return Decoded character.
   1.327 +   *
   1.328 +   * @throws NullCharError if a NUL character read.
   1.329 +   * @throws CodeError if a control character read.
   1.330 +   */
   1.331 +  decode: function(data) {
   1.332 +    let code = Octet.decode(data);
   1.333 +    if ((code >= CTLS) && (code != DEL)) {
   1.334 +      return String.fromCharCode(code);
   1.335 +    }
   1.336 +
   1.337 +    if (code == NUL) {
   1.338 +      throw new NullCharError();
   1.339 +    }
   1.340 +
   1.341 +    if (code != CR) {
   1.342 +      throw new CodeError("Text: invalid char code " + code);
   1.343 +    }
   1.344 +
   1.345 +    // "A CRLF is allowed in the definition of TEXT only as part of a header
   1.346 +    // field continuation. It is expected that the folding LWS will be
   1.347 +    // replaced with a single SP before interpretation of the TEXT value."
   1.348 +    // ~ RFC 2616 clause 2.2
   1.349 +
   1.350 +    let extra;
   1.351 +
   1.352 +    // Rethrow everything as CodeError. We had already a successful read above.
   1.353 +    try {
   1.354 +      extra = Octet.decode(data);
   1.355 +      if (extra != LF) {
   1.356 +        throw new CodeError("Text: doesn't match LWS sequence");
   1.357 +      }
   1.358 +
   1.359 +      extra = Octet.decode(data);
   1.360 +      if ((extra != SP) && (extra != HT)) {
   1.361 +        throw new CodeError("Text: doesn't match LWS sequence");
   1.362 +      }
   1.363 +    } catch (e if e instanceof CodeError) {
   1.364 +      throw e;
   1.365 +    } catch (e) {
   1.366 +      throw new CodeError("Text: doesn't match LWS sequence");
   1.367 +    }
   1.368 +
   1.369 +    // Let's eat as many SP|HT as possible.
   1.370 +    let begin;
   1.371 +
   1.372 +    // Do not throw anything here. We had already matched (SP | HT).
   1.373 +    try {
   1.374 +      do {
   1.375 +        begin = data.offset;
   1.376 +        extra = Octet.decode(data);
   1.377 +      } while ((extra == SP) || (extra == HT));
   1.378 +    } catch (e) {}
   1.379 +
   1.380 +    data.offset = begin;
   1.381 +    return " ";
   1.382 +  },
   1.383 +
   1.384 +  /**
   1.385 +   * @param data
   1.386 +   *        A wrapped object to store encoded raw data.
   1.387 +   * @param text
   1.388 +   *        String text of one character to be encoded.
   1.389 +   * @param asciiOnly
   1.390 +   *        A boolean to decide if it's only allowed to encode ASCII (0 ~ 127).
   1.391 +   *
   1.392 +   * @throws CodeError if a control character got.
   1.393 +   */
   1.394 +  encode: function(data, text, asciiOnly) {
   1.395 +    if (!text) {
   1.396 +      throw new CodeError("Text: empty string");
   1.397 +    }
   1.398 +
   1.399 +    let code = text.charCodeAt(0);
   1.400 +    if ((code < CTLS) || (code == DEL) || (code > 255) ||
   1.401 +        (code >= 128 && asciiOnly)) {
   1.402 +      throw new CodeError("Text: invalid char code " + code);
   1.403 +    }
   1.404 +    Octet.encode(data, code);
   1.405 +  },
   1.406 +};
   1.407 +
   1.408 +this.NullTerminatedTexts = {
   1.409 +  /**
   1.410 +   * Decode internal referenced null terminated text string.
   1.411 +   *
   1.412 +   * @param data
   1.413 +   *        A wrapped object containing raw PDU data.
   1.414 +   *
   1.415 +   * @return Decoded string.
   1.416 +   */
   1.417 +  decode: function(data) {
   1.418 +    let str = "";
   1.419 +    try {
   1.420 +      // A End-of-string is also a CTL, which should cause a error.
   1.421 +      while (true) {
   1.422 +        str += Text.decode(data);
   1.423 +      }
   1.424 +    } catch (e if e instanceof NullCharError) {
   1.425 +      return str;
   1.426 +    }
   1.427 +  },
   1.428 +
   1.429 +  /**
   1.430 +   * @param data
   1.431 +   *        A wrapped object to store encoded raw data.
   1.432 +   * @param str
   1.433 +   *        A String to be encoded.
   1.434 +   * @param asciiOnly
   1.435 +   *        A boolean to decide if it's only allowed to encode ASCII (0 ~ 127).
   1.436 +   */
   1.437 +  encode: function(data, str, asciiOnly) {
   1.438 +    if (str) {
   1.439 +      for (let i = 0; i < str.length; i++) {
   1.440 +        Text.encode(data, str.charAt(i), asciiOnly);
   1.441 +      }
   1.442 +    }
   1.443 +    Octet.encode(data, 0);
   1.444 +  },
   1.445 +};
   1.446 +
   1.447 +/**
   1.448 + * TOKEN = 1*<any CHAR except CTLs or separators>
   1.449 + * CHAR = <any US-ASCII character (octets 0 - 127)>
   1.450 + * SEPARATORS = ()<>@,;:\"/[]?={} SP HT
   1.451 + *
   1.452 + * @see RFC 2616 clause 2.2 Basic Rules
   1.453 + */
   1.454 +this.Token = {
   1.455 +  /**
   1.456 +   * @param data
   1.457 +   *        A wrapped object containing raw PDU data.
   1.458 +   *
   1.459 +   * @return Decoded character.
   1.460 +   *
   1.461 +   * @throws NullCharError if a NUL character read.
   1.462 +   * @throws CodeError if an invalid character read.
   1.463 +   */
   1.464 +  decode: function(data) {
   1.465 +    let code = Octet.decode(data);
   1.466 +    if ((code < ASCIIS) && (code >= CTLS)) {
   1.467 +      if ((code == HT) || (code == SP)
   1.468 +          || (code == 34) || (code == 40) || (code == 41) // ASCII "()
   1.469 +          || (code == 44) || (code == 47)                 // ASCII ,/
   1.470 +          || ((code >= 58) && (code <= 64))               // ASCII :;<=>?@
   1.471 +          || ((code >= 91) && (code <= 93))               // ASCII [\]
   1.472 +          || (code == 123) || (code == 125)) {            // ASCII {}
   1.473 +        throw new CodeError("Token: invalid char code " + code);
   1.474 +      }
   1.475 +
   1.476 +      return String.fromCharCode(code);
   1.477 +    }
   1.478 +
   1.479 +    if (code == NUL) {
   1.480 +      throw new NullCharError();
   1.481 +    }
   1.482 +
   1.483 +    throw new CodeError("Token: invalid char code " + code);
   1.484 +  },
   1.485 +
   1.486 +  /**
   1.487 +   * @param data
   1.488 +   *        A wrapped object to store encoded raw data.
   1.489 +   * @param token
   1.490 +   *        String text of one character to be encoded.
   1.491 +   *
   1.492 +   * @throws CodeError if an invalid character got.
   1.493 +   */
   1.494 +  encode: function(data, token) {
   1.495 +    if (!token) {
   1.496 +      throw new CodeError("Token: empty string");
   1.497 +    }
   1.498 +
   1.499 +    let code = token.charCodeAt(0);
   1.500 +    if ((code < ASCIIS) && (code >= CTLS)) {
   1.501 +      if ((code == HT) || (code == SP)
   1.502 +          || (code == 34) || (code == 40) || (code == 41) // ASCII "()
   1.503 +          || (code == 44) || (code == 47)                 // ASCII ,/
   1.504 +          || ((code >= 58) && (code <= 64))               // ASCII :;<=>?@
   1.505 +          || ((code >= 91) && (code <= 93))               // ASCII [\]
   1.506 +          || (code == 123) || (code == 125)) {            // ASCII {}
   1.507 +        // Fallback to throw CodeError
   1.508 +      } else {
   1.509 +        Octet.encode(data, token.charCodeAt(0));
   1.510 +        return;
   1.511 +      }
   1.512 +    }
   1.513 +
   1.514 +    throw new CodeError("Token: invalid char code " + code);
   1.515 +  },
   1.516 +};
   1.517 +
   1.518 +/**
   1.519 + * uric       = reserved | unreserved | escaped
   1.520 + * reserved   = ;/?:@&=+$,
   1.521 + * unreserved = alphanum | mark
   1.522 + * mark       = -_.!~*'()
   1.523 + * escaped    = % hex hex
   1.524 + * excluded but used = #%
   1.525 + *
   1.526 + * Or, in decimal, they are: 33,35-59,61,63-90,95,97-122,126
   1.527 + *
   1.528 + * @see RFC 2396 Uniform Resource Indentifiers (URI)
   1.529 + */
   1.530 +this.URIC = {
   1.531 +  /**
   1.532 +   * @param data
   1.533 +   *        A wrapped object containing raw PDU data.
   1.534 +   *
   1.535 +   * @return Decoded character.
   1.536 +   *
   1.537 +   * @throws NullCharError if a NUL character read.
   1.538 +   * @throws CodeError if an invalid character read.
   1.539 +   */
   1.540 +  decode: function(data) {
   1.541 +    let code = Octet.decode(data);
   1.542 +    if (code == NUL) {
   1.543 +      throw new NullCharError();
   1.544 +    }
   1.545 +
   1.546 +    if ((code <= CTLS) || (code >= ASCIIS) || (code == 34) || (code == 60)
   1.547 +        || (code == 62) || ((code >= 91) && (code <= 94)) || (code == 96)
   1.548 +        || ((code >= 123) && (code <= 125)) || (code == 127)) {
   1.549 +      throw new CodeError("URIC: invalid char code " + code);
   1.550 +    }
   1.551 +
   1.552 +    return String.fromCharCode(code);
   1.553 +  },
   1.554 +};
   1.555 +
   1.556 +/**
   1.557 + * If the first character in the TEXT is in the range of 128-255, a Quote
   1.558 + * character must precede it. Otherwise the Quote character must be omitted.
   1.559 + * The Quote is not part of the contents.
   1.560 + *
   1.561 + *   Text-string = [Quote] *TEXT End-of-string
   1.562 + *   Quote = <Octet 127>
   1.563 + *
   1.564 + * @see WAP-230-WSP-20010705-a clause 8.4.2.1
   1.565 + */
   1.566 +this.TextString = {
   1.567 +  /**
   1.568 +   * @param data
   1.569 +   *        A wrapped object containing raw PDU data.
   1.570 +   *
   1.571 +   * @return Decoded string.
   1.572 +   */
   1.573 +  decode: function(data) {
   1.574 +    let begin = data.offset;
   1.575 +    let firstCode = Octet.decode(data);
   1.576 +    if (firstCode == 127) {
   1.577 +      // Quote found, check if first char code is larger-equal than 128.
   1.578 +      begin = data.offset;
   1.579 +      try {
   1.580 +        if (Octet.decode(data) < 128) {
   1.581 +          throw new CodeError("Text-string: illegal quote found.");
   1.582 +        }
   1.583 +      } catch (e if e instanceof CodeError) {
   1.584 +        throw e;
   1.585 +      } catch (e) {
   1.586 +        throw new CodeError("Text-string: unexpected error.");
   1.587 +      }
   1.588 +    } else if (firstCode >= 128) {
   1.589 +      throw new CodeError("Text-string: invalid char code " + firstCode);
   1.590 +    }
   1.591 +
   1.592 +    data.offset = begin;
   1.593 +    return NullTerminatedTexts.decode(data);
   1.594 +  },
   1.595 +
   1.596 +  /**
   1.597 +   * @param data
   1.598 +   *        A wrapped object to store encoded raw data.
   1.599 +   * @param str
   1.600 +   *        A String to be encoded.
   1.601 +   * @param asciiOnly
   1.602 +   *        A boolean to decide if it's only allowed to encode ASCII (0 ~ 127).
   1.603 +   */
   1.604 +  encode: function(data, str, asciiOnly) {
   1.605 +    if (!str) {
   1.606 +      Octet.encode(data, 0);
   1.607 +      return;
   1.608 +    }
   1.609 +
   1.610 +    let firstCharCode = str.charCodeAt(0);
   1.611 +    if (firstCharCode >= 128) {
   1.612 +      if (asciiOnly) {
   1.613 +        throw new CodeError("Text: invalid char code " + code);
   1.614 +      }
   1.615 +
   1.616 +      Octet.encode(data, 127);
   1.617 +    }
   1.618 +
   1.619 +    NullTerminatedTexts.encode(data, str, asciiOnly);
   1.620 +  },
   1.621 +};
   1.622 +
   1.623 +/**
   1.624 + * Token-text = Token End-of-string
   1.625 + *
   1.626 + * @see WAP-230-WSP-20010705-a clause 8.4.2.1
   1.627 + */
   1.628 +this.TokenText = {
   1.629 +  /**
   1.630 +   * @param data
   1.631 +   *        A wrapped object containing raw PDU data.
   1.632 +   *
   1.633 +   * @return Decoded string.
   1.634 +   */
   1.635 +  decode: function(data) {
   1.636 +    let str = "";
   1.637 +    try {
   1.638 +      // A End-of-string is also a CTL, which should cause a error.
   1.639 +      while (true) {
   1.640 +        str += Token.decode(data);
   1.641 +      }
   1.642 +    } catch (e if e instanceof NullCharError) {
   1.643 +      return str;
   1.644 +    }
   1.645 +  },
   1.646 +
   1.647 +  /**
   1.648 +   * @param data
   1.649 +   *        A wrapped object to store encoded raw data.
   1.650 +   * @param str
   1.651 +   *        A String to be encoded.
   1.652 +   */
   1.653 +  encode: function(data, str) {
   1.654 +    if (str) {
   1.655 +      for (let i = 0; i < str.length; i++) {
   1.656 +        Token.encode(data, str.charAt(i));
   1.657 +      }
   1.658 +    }
   1.659 +    Octet.encode(data, 0);
   1.660 +  },
   1.661 +};
   1.662 +
   1.663 +/**
   1.664 + * The TEXT encodes an RFC2616 Quoted-string with the enclosing
   1.665 + * quotation-marks <"> removed.
   1.666 + *
   1.667 + *   Quoted-string = <Octet 34> *TEXT End-of-string
   1.668 + *
   1.669 + * @see WAP-230-WSP-20010705-a clause 8.4.2.1
   1.670 + */
   1.671 +this.QuotedString = {
   1.672 +  /**
   1.673 +   * @param data
   1.674 +   *        A wrapped object containing raw PDU data.
   1.675 +   *
   1.676 +   * @return Decoded string.
   1.677 +   *
   1.678 +   * @throws CodeError if first octet read is not 0x34.
   1.679 +   */
   1.680 +  decode: function(data) {
   1.681 +    let value = Octet.decode(data);
   1.682 +    if (value != 34) {
   1.683 +      throw new CodeError("Quoted-string: not quote " + value);
   1.684 +    }
   1.685 +
   1.686 +    return NullTerminatedTexts.decode(data);
   1.687 +  },
   1.688 +
   1.689 +  /**
   1.690 +   * @param data
   1.691 +   *        A wrapped object to store encoded raw data.
   1.692 +   * @param str
   1.693 +   *        A String to be encoded.
   1.694 +   */
   1.695 +  encode: function(data, str) {
   1.696 +    Octet.encode(data, 34);
   1.697 +    NullTerminatedTexts.encode(data, str);
   1.698 +  },
   1.699 +};
   1.700 +
   1.701 +/**
   1.702 + * Integers in range 0-127 shall be encoded as a one octet value with the
   1.703 + * most significant bit set to one (1xxx xxxx) and with the value in the
   1.704 + * remaining least significant bits.
   1.705 + *
   1.706 + *   Short-integer = OCTET
   1.707 + *
   1.708 + * @see WAP-230-WSP-20010705-a clause 8.4.2.1
   1.709 + */
   1.710 +this.ShortInteger = {
   1.711 +  /**
   1.712 +   * @param data
   1.713 +   *        A wrapped object containing raw PDU data.
   1.714 +   *
   1.715 +   * @return Decoded integer value.
   1.716 +   *
   1.717 +   * @throws CodeError if the octet read is less than 0x80.
   1.718 +   */
   1.719 +  decode: function(data) {
   1.720 +    let value = Octet.decode(data);
   1.721 +    if (!(value & 0x80)) {
   1.722 +      throw new CodeError("Short-integer: invalid value " + value);
   1.723 +    }
   1.724 +
   1.725 +    return (value & 0x7F);
   1.726 +  },
   1.727 +
   1.728 +  /**
   1.729 +   * @param data
   1.730 +   *        A wrapped object to store encoded raw data.
   1.731 +   * @param value
   1.732 +   *        A numeric value to be encoded.
   1.733 +   *
   1.734 +   * @throws CodeError if the octet read is larger-equal than 0x80.
   1.735 +   */
   1.736 +  encode: function(data, value) {
   1.737 +    if (value >= 0x80) {
   1.738 +      throw new CodeError("Short-integer: invalid value " + value);
   1.739 +    }
   1.740 +
   1.741 +    Octet.encode(data, value | 0x80);
   1.742 +  },
   1.743 +};
   1.744 +
   1.745 +/**
   1.746 + * The content octets shall be an unsigned integer value with the most
   1.747 + * significant octet encoded first (big-endian representation). The minimum
   1.748 + * number of octets must be used to encode the value.
   1.749 + *
   1.750 + *   Long-integer = Short-length Multi-octet-integer
   1.751 + *   Short-length = <Any octet 0-30>
   1.752 + *   Multi-octet-integer = 1*30 OCTET
   1.753 + *
   1.754 + * @see WAP-230-WSP-20010705-a clause 8.4.2.1
   1.755 + */
   1.756 +this.LongInteger = {
   1.757 +  /**
   1.758 +   * @param data
   1.759 +   *        A wrapped object containing raw PDU data.
   1.760 +   * @param length
   1.761 +   *        Number of octets to read.
   1.762 +   *
   1.763 +   * @return A decoded integer value or an octets array of max 30 elements.
   1.764 +   */
   1.765 +  decodeMultiOctetInteger: function(data, length) {
   1.766 +    if (length < 7) {
   1.767 +      // Return a integer instead of an array as possible. For a multi-octet
   1.768 +      // integer, there are only maximum 53 bits for integer in javascript. We
   1.769 +      // will get an inaccurate one beyond that. We can't neither use bitwise
   1.770 +      // operation here, for it will be limited in 32 bits.
   1.771 +      let value = 0;
   1.772 +      while (length--) {
   1.773 +        value = value * 256 + Octet.decode(data);
   1.774 +      }
   1.775 +      return value;
   1.776 +    }
   1.777 +
   1.778 +    return Octet.decodeMultiple(data, data.offset + length);
   1.779 +  },
   1.780 +
   1.781 +  /**
   1.782 +   * @param data
   1.783 +   *        A wrapped object containing raw PDU data.
   1.784 +   *
   1.785 +   * @return A decoded integer value or an octets array of max 30 elements.
   1.786 +   *
   1.787 +   * @throws CodeError if the length read is not in 1..30.
   1.788 +   */
   1.789 +  decode: function(data) {
   1.790 +    let length = Octet.decode(data);
   1.791 +    if ((length < 1) || (length > 30)) {
   1.792 +      throw new CodeError("Long-integer: invalid length " + length);
   1.793 +    }
   1.794 +
   1.795 +    return this.decodeMultiOctetInteger(data, length);
   1.796 +  },
   1.797 +
   1.798 +  /**
   1.799 +   * @param data
   1.800 +   *        A wrapped object to store encoded raw data.
   1.801 +   * @param numOrArray
   1.802 +   *        An octet array of less-equal than 30 elements or an integer
   1.803 +   *        greater-equal than 128.
   1.804 +   */
   1.805 +  encode: function(data, numOrArray) {
   1.806 +    if (typeof numOrArray === "number") {
   1.807 +      let num = numOrArray;
   1.808 +      if (num >= 0x1000000000000) {
   1.809 +        throw new CodeError("Long-integer: number too large " + num);
   1.810 +      }
   1.811 +
   1.812 +      let stack = [];
   1.813 +      do {
   1.814 +        stack.push(Math.floor(num % 256));
   1.815 +        num = Math.floor(num / 256);
   1.816 +      } while (num);
   1.817 +
   1.818 +      Octet.encode(data, stack.length);
   1.819 +      while (stack.length) {
   1.820 +        Octet.encode(data, stack.pop());
   1.821 +      }
   1.822 +      return;
   1.823 +    }
   1.824 +
   1.825 +    let array = numOrArray;
   1.826 +    if ((array.length < 1) || (array.length > 30)) {
   1.827 +      throw new CodeError("Long-integer: invalid length " + array.length);
   1.828 +    }
   1.829 +
   1.830 +    Octet.encode(data, array.length);
   1.831 +    Octet.encodeMultiple(data, array);
   1.832 +  },
   1.833 +};
   1.834 +
   1.835 +/**
   1.836 + * @see WAP-230-WSP-20010705-a clause 8.4.2.1
   1.837 + */
   1.838 +this.UintVar = {
   1.839 +  /**
   1.840 +   * @param data
   1.841 +   *        A wrapped object containing raw PDU data.
   1.842 +   *
   1.843 +   * @return Decoded integer value.
   1.844 +   */
   1.845 +  decode: function(data) {
   1.846 +    let value = Octet.decode(data);
   1.847 +    let result = value & 0x7F;
   1.848 +    while (value & 0x80) {
   1.849 +      value = Octet.decode(data);
   1.850 +      result = result * 128 + (value & 0x7F);
   1.851 +    }
   1.852 +
   1.853 +    return result;
   1.854 +  },
   1.855 +
   1.856 +  /**
   1.857 +   * @param data
   1.858 +   *        A wrapped object to store encoded raw data.
   1.859 +   * @param value
   1.860 +   *        An integer value.
   1.861 +   */
   1.862 +  encode: function(data, value) {
   1.863 +    if (value < 0) {
   1.864 +      throw new CodeError("UintVar: invalid value " + value);
   1.865 +    }
   1.866 +
   1.867 +    let stack = [];
   1.868 +    while (value >= 128) {
   1.869 +      stack.push(Math.floor(value % 128));
   1.870 +      value = Math.floor(value / 128);
   1.871 +    }
   1.872 +
   1.873 +    while (stack.length) {
   1.874 +      Octet.encode(data, value | 0x80);
   1.875 +      value = stack.pop();
   1.876 +    }
   1.877 +    Octet.encode(data, value);
   1.878 +  },
   1.879 +};
   1.880 +
   1.881 +/**
   1.882 + * This encoding is used for token values, which have no well-known binary
   1.883 + * encoding, or when the assigned number of the well-known encoding is small
   1.884 + * enough to fit into Short-Integer. We change Extension-Media from
   1.885 + * NullTerminatedTexts to TextString because of Bug 823816.
   1.886 + *
   1.887 + *   Constrained-encoding = Extension-Media | Short-integer
   1.888 + *   Extension-Media = TextString
   1.889 + *
   1.890 + * @see WAP-230-WSP-20010705-a clause 8.4.2.1
   1.891 + * @see https://bugzilla.mozilla.org/show_bug.cgi?id=823816
   1.892 + */
   1.893 +this.ConstrainedEncoding = {
   1.894 +  /**
   1.895 +   * @param data
   1.896 +   *        A wrapped object containing raw PDU data.
   1.897 +   *
   1.898 +   * @return Decode integer value or string.
   1.899 +   */
   1.900 +  decode: function(data) {
   1.901 +    return decodeAlternatives(data, null, TextString, ShortInteger);
   1.902 +  },
   1.903 +
   1.904 +  /**
   1.905 +   * @param data
   1.906 +   *        A wrapped object to store encoded raw data.
   1.907 +   * @param value
   1.908 +   *        An integer or a string value.
   1.909 +   */
   1.910 +  encode: function(data, value) {
   1.911 +    if (typeof value == "number") {
   1.912 +      ShortInteger.encode(data, value);
   1.913 +    } else {
   1.914 +      TextString.encode(data, value);
   1.915 +    }
   1.916 +  },
   1.917 +};
   1.918 +
   1.919 +/**
   1.920 + * Value-length = Short-length | (Length-quote Length)
   1.921 + * Short-length = <Any octet 0-30>
   1.922 + * Length-quote = <Octet 31>
   1.923 + * Length = Uintvar-integer
   1.924 + *
   1.925 + * @see WAP-230-WSP-20010705-a clause 8.4.2.2
   1.926 + */
   1.927 +this.ValueLength = {
   1.928 +  /**
   1.929 +   * @param data
   1.930 +   *        A wrapped object containing raw PDU data.
   1.931 +   *
   1.932 +   * @return Decoded integer value.
   1.933 +   *
   1.934 +   * @throws CodeError if the first octet read is larger than 31.
   1.935 +   */
   1.936 +  decode: function(data) {
   1.937 +    let value = Octet.decode(data);
   1.938 +    if (value <= 30) {
   1.939 +      return value;
   1.940 +    }
   1.941 +
   1.942 +    if (value == 31) {
   1.943 +      return UintVar.decode(data);
   1.944 +    }
   1.945 +
   1.946 +    throw new CodeError("Value-length: invalid value " + value);
   1.947 +  },
   1.948 +
   1.949 +  /**
   1.950 +   * @param data
   1.951 +   *        A wrapped object to store encoded raw data.
   1.952 +   * @param value
   1.953 +   */
   1.954 +  encode: function(data, value) {
   1.955 +    if (value <= 30) {
   1.956 +      Octet.encode(data, value);
   1.957 +    } else {
   1.958 +      Octet.encode(data, 31);
   1.959 +      UintVar.encode(data, value);
   1.960 +    }
   1.961 +  },
   1.962 +};
   1.963 +
   1.964 +/**
   1.965 + * No-value = <Octet 0>
   1.966 + *
   1.967 + * @see WAP-230-WSP-20010705-a clause 8.4.2.3
   1.968 + */
   1.969 +this.NoValue = {
   1.970 +  /**
   1.971 +   * @param data
   1.972 +   *        A wrapped object containing raw PDU data.
   1.973 +   *
   1.974 +   * @return Always returns null.
   1.975 +   */
   1.976 +  decode: function(data) {
   1.977 +    Octet.decodeEqualTo(data, 0);
   1.978 +    return null;
   1.979 +  },
   1.980 +
   1.981 +  /**
   1.982 +   * @param data
   1.983 +   *        A wrapped object to store encoded raw data.
   1.984 +   * @param value
   1.985 +   *        A null or undefined value.
   1.986 +   */
   1.987 +  encode: function(data, value) {
   1.988 +    if (value != null) {
   1.989 +      throw new CodeError("No-value: invalid value " + value);
   1.990 +    }
   1.991 +    Octet.encode(data, 0);
   1.992 +  },
   1.993 +};
   1.994 +
   1.995 +/**
   1.996 + * Text-value = No-value | Token-text | Quoted-string
   1.997 + *
   1.998 + * @see WAP-230-WSP-20010705-a clause 8.4.2.3
   1.999 + */
  1.1000 +this.TextValue = {
  1.1001 +  /**
  1.1002 +   * @param data
  1.1003 +   *        A wrapped object containing raw PDU data.
  1.1004 +   *
  1.1005 +   * @return Decoded string or null for No-value.
  1.1006 +   */
  1.1007 +  decode: function(data) {
  1.1008 +    return decodeAlternatives(data, null, NoValue, TokenText, QuotedString);
  1.1009 +  },
  1.1010 +
  1.1011 +  /**
  1.1012 +   * @param data
  1.1013 +   *        A wrapped object to store encoded raw data.
  1.1014 +   * @param text
  1.1015 +   *        A null or undefined or text string.
  1.1016 +   */
  1.1017 +  encode: function(data, text) {
  1.1018 +    encodeAlternatives(data, text, null, NoValue, TokenText, QuotedString);
  1.1019 +  },
  1.1020 +};
  1.1021 +
  1.1022 +/**
  1.1023 + * Integer-Value = Short-integer | Long-integer
  1.1024 + *
  1.1025 + * @see WAP-230-WSP-20010705-a clause 8.4.2.3
  1.1026 + */
  1.1027 +this.IntegerValue = {
  1.1028 +  /**
  1.1029 +   * @param data
  1.1030 +   *        A wrapped object containing raw PDU data.
  1.1031 +   *
  1.1032 +   * @return Decoded integer value or array of octets.
  1.1033 +   */
  1.1034 +  decode: function(data) {
  1.1035 +    return decodeAlternatives(data, null, ShortInteger, LongInteger);
  1.1036 +  },
  1.1037 +
  1.1038 +  /**
  1.1039 +   * @param data
  1.1040 +   *        A wrapped object to store encoded raw data.
  1.1041 +   * @param value
  1.1042 +   *        An integer value or an octet array of less-equal than 31 elements.
  1.1043 +   */
  1.1044 +  encode: function(data, value) {
  1.1045 +    if (typeof value === "number") {
  1.1046 +      encodeAlternatives(data, value, null, ShortInteger, LongInteger);
  1.1047 +    } else if (Array.isArray(value) || (value instanceof Uint8Array)) {
  1.1048 +      LongInteger.encode(data, value);
  1.1049 +    } else {
  1.1050 +      throw new CodeError("Integer-Value: invalid value type");
  1.1051 +    }
  1.1052 +  },
  1.1053 +};
  1.1054 +
  1.1055 +/**
  1.1056 + * The encoding of dates shall be done in number of seconds from
  1.1057 + * 1970-01-01, 00:00:00 GMT.
  1.1058 + *
  1.1059 + *   Date-value = Long-integer
  1.1060 + *
  1.1061 + * @see WAP-230-WSP-20010705-a clause 8.4.2.3
  1.1062 + */
  1.1063 +this.DateValue = {
  1.1064 +  /**
  1.1065 +   * @param data
  1.1066 +   *        A wrapped object containing raw PDU data.
  1.1067 +   *
  1.1068 +   * @return A Date object.
  1.1069 +   */
  1.1070 +  decode: function(data) {
  1.1071 +    let numOrArray = LongInteger.decode(data);
  1.1072 +    let seconds;
  1.1073 +    if (typeof numOrArray == "number") {
  1.1074 +      seconds = numOrArray;
  1.1075 +    } else {
  1.1076 +      seconds = 0;
  1.1077 +      for (let i = 0; i < numOrArray.length; i++) {
  1.1078 +        seconds = seconds * 256 + numOrArray[i];
  1.1079 +      }
  1.1080 +    }
  1.1081 +
  1.1082 +    return new Date(seconds * 1000);
  1.1083 +  },
  1.1084 +
  1.1085 +  /**
  1.1086 +   * @param data
  1.1087 +   *        A wrapped object to store encoded raw data.
  1.1088 +   * @param date
  1.1089 +   *        A Date object.
  1.1090 +   */
  1.1091 +  encode: function(data, date) {
  1.1092 +    let seconds = date.getTime() / 1000;
  1.1093 +    if (seconds < 0) {
  1.1094 +      throw new CodeError("Date-value: negative seconds " + seconds);
  1.1095 +    }
  1.1096 +
  1.1097 +    LongInteger.encode(data, seconds);
  1.1098 +  },
  1.1099 +};
  1.1100 +
  1.1101 +/**
  1.1102 + * Delta-seconds-value = Integer-value
  1.1103 + *
  1.1104 + * @see WAP-230-WSP-20010705-a clause 8.4.2.3
  1.1105 + */
  1.1106 +this.DeltaSecondsValue = IntegerValue;
  1.1107 +
  1.1108 +/**
  1.1109 + * Quality factor 0 and quality factors with one or two decimal digits are
  1.1110 + * encoded into 1-100; three digits ones into 101-1099.
  1.1111 + *
  1.1112 + *   Q-value = 1*2 OCTET
  1.1113 + *
  1.1114 + * @see WAP-230-WSP-20010705-a clause 8.4.2.3
  1.1115 + */
  1.1116 +this.QValue = {
  1.1117 +  /**
  1.1118 +   * @param data
  1.1119 +   *        A wrapped object containing raw PDU data.
  1.1120 +   *
  1.1121 +   * @return Decoded integer value of 1..1099.
  1.1122 +   *
  1.1123 +   * @throws CodeError if decoded UintVar is not in range 1..1099.
  1.1124 +   */
  1.1125 +  decode: function(data) {
  1.1126 +    let value = UintVar.decode(data);
  1.1127 +    if (value > 0) {
  1.1128 +      if (value <= 100) {
  1.1129 +        return (value - 1) / 100.0;
  1.1130 +      }
  1.1131 +      if (value <= 1099) {
  1.1132 +        return (value - 100) / 1000.0;
  1.1133 +      }
  1.1134 +    }
  1.1135 +
  1.1136 +    throw new CodeError("Q-value: invalid value " + value);
  1.1137 +  },
  1.1138 +
  1.1139 +  /**
  1.1140 +   * @param data
  1.1141 +   *        A wrapped object to store encoded raw data.
  1.1142 +   * @param value
  1.1143 +   *        An integer within the range 1..1099.
  1.1144 +   */
  1.1145 +  encode: function(data, value) {
  1.1146 +    if ((value < 0) || (value >= 1)) {
  1.1147 +      throw new CodeError("Q-value: invalid value " + value);
  1.1148 +    }
  1.1149 +
  1.1150 +    value *= 1000;
  1.1151 +    if ((value % 10) == 0) {
  1.1152 +      // Two digits only.
  1.1153 +      UintVar.encode(data, Math.floor(value / 10 + 1));
  1.1154 +    } else {
  1.1155 +      // Three digits.
  1.1156 +      UintVar.encode(data, Math.floor(value + 100));
  1.1157 +    }
  1.1158 +  },
  1.1159 +};
  1.1160 +
  1.1161 +/**
  1.1162 + * The three most significant bits of the Short-integer value are interpreted
  1.1163 + * to encode a major version number in the range 1-7, and the four least
  1.1164 + * significant bits contain a minor version number in the range 0-14. If
  1.1165 + * there is only a major version number, this is encoded by placing the value
  1.1166 + * 15 in the four least significant bits.
  1.1167 + *
  1.1168 + *   Version-value = Short-integer | Text-string
  1.1169 + *
  1.1170 + * @see WAP-230-WSP-20010705-a clause 8.4.2.3
  1.1171 + */
  1.1172 +this.VersionValue = {
  1.1173 +  /**
  1.1174 +   * @param data
  1.1175 +   *        A wrapped object containing raw PDU data.
  1.1176 +   *
  1.1177 +   * @return Binary encoded version number.
  1.1178 +   */
  1.1179 +  decode: function(data) {
  1.1180 +    let begin = data.offset;
  1.1181 +    let value;
  1.1182 +    try {
  1.1183 +      value = ShortInteger.decode(data);
  1.1184 +      if ((value >= 0x10) && (value < 0x80)) {
  1.1185 +        return value;
  1.1186 +      }
  1.1187 +
  1.1188 +      throw new CodeError("Version-value: invalid value " + value);
  1.1189 +    } catch (e) {}
  1.1190 +
  1.1191 +    data.offset = begin;
  1.1192 +
  1.1193 +    let str = TextString.decode(data);
  1.1194 +    if (!str.match(/^[1-7](\.1?\d)?$/)) {
  1.1195 +      throw new CodeError("Version-value: invalid value " + str);
  1.1196 +    }
  1.1197 +
  1.1198 +    let major = str.charCodeAt(0) - 0x30;
  1.1199 +    let minor = 0x0F;
  1.1200 +    if (str.length > 1) {
  1.1201 +      minor = str.charCodeAt(2) - 0x30;
  1.1202 +      if (str.length > 3) {
  1.1203 +        minor = 10 + (str.charCodeAt(3) - 0x30);
  1.1204 +        if (minor > 14) {
  1.1205 +          throw new CodeError("Version-value: invalid minor " + minor);
  1.1206 +        }
  1.1207 +      }
  1.1208 +    }
  1.1209 +
  1.1210 +    return major << 4 | minor;
  1.1211 +  },
  1.1212 +
  1.1213 +  /**
  1.1214 +   * @param data
  1.1215 +   *        A wrapped object to store encoded raw data.
  1.1216 +   * @param version
  1.1217 +   *        A binary encoded version number.
  1.1218 +   */
  1.1219 +  encode: function(data, version) {
  1.1220 +    if ((version < 0x10) || (version >= 0x80)) {
  1.1221 +      throw new CodeError("Version-value: invalid version " + version);
  1.1222 +    }
  1.1223 +
  1.1224 +    ShortInteger.encode(data, version);
  1.1225 +  },
  1.1226 +};
  1.1227 +
  1.1228 +/**
  1.1229 + * URI value should be encoded per [RFC2616], but service user may use a
  1.1230 + * different format.
  1.1231 + *
  1.1232 + *   Uri-value = Text-string
  1.1233 + *
  1.1234 + * @see WAP-230-WSP-20010705-a clause 8.4.2.3
  1.1235 + * @see RFC 2616 clause 2.2 Basic Rules
  1.1236 + */
  1.1237 +this.UriValue = {
  1.1238 +  /**
  1.1239 +   * @param data
  1.1240 +   *        A wrapped object containing raw PDU data.
  1.1241 +   *
  1.1242 +   * @return Decoded uri string.
  1.1243 +   */
  1.1244 +  decode: function(data) {
  1.1245 +    let str = "";
  1.1246 +    try {
  1.1247 +      // A End-of-string is also a CTL, which should cause a error.
  1.1248 +      while (true) {
  1.1249 +        str += URIC.decode(data);
  1.1250 +      }
  1.1251 +    } catch (e if e instanceof NullCharError) {
  1.1252 +      return str;
  1.1253 +    }
  1.1254 +  },
  1.1255 +};
  1.1256 +
  1.1257 +/**
  1.1258 + * Internal coder for "type" parameter.
  1.1259 + *
  1.1260 + *   Type-value = Constrained-encoding
  1.1261 + *
  1.1262 + * @see WAP-230-WSP-20010705-a table 38
  1.1263 + */
  1.1264 +this.TypeValue = {
  1.1265 +  /**
  1.1266 +   * @param data
  1.1267 +   *        A wrapped object containing raw PDU data.
  1.1268 +   *
  1.1269 +   * @return Decoded content type string.
  1.1270 +   */
  1.1271 +  decode: function(data) {
  1.1272 +    let numOrStr = ConstrainedEncoding.decode(data);
  1.1273 +    if (typeof numOrStr == "string") {
  1.1274 +      return numOrStr.toLowerCase();
  1.1275 +    }
  1.1276 +
  1.1277 +    let number = numOrStr;
  1.1278 +    let entry = WSP_WELL_KNOWN_CONTENT_TYPES[number];
  1.1279 +    if (!entry) {
  1.1280 +      throw new NotWellKnownEncodingError(
  1.1281 +        "Constrained-media: not well known media " + number);
  1.1282 +    }
  1.1283 +
  1.1284 +    return entry.type;
  1.1285 +  },
  1.1286 +
  1.1287 +  /**
  1.1288 +   * @param data
  1.1289 +   *        A wrapped object to store encoded raw data.
  1.1290 +   * @param type
  1.1291 +   *        A content type string.
  1.1292 +   */
  1.1293 +  encode: function(data, type) {
  1.1294 +    let entry = WSP_WELL_KNOWN_CONTENT_TYPES[type.toLowerCase()];
  1.1295 +    if (entry) {
  1.1296 +      ConstrainedEncoding.encode(data, entry.number);
  1.1297 +    } else {
  1.1298 +      ConstrainedEncoding.encode(data, type);
  1.1299 +    }
  1.1300 +  },
  1.1301 +};
  1.1302 +
  1.1303 +/**
  1.1304 + * Parameter = Typed-parameter | Untyped-parameter
  1.1305 + *
  1.1306 + * For Typed-parameters, the actual expected type of the value is implied by
  1.1307 + * the well-known parameter. In addition to the expected type, there may be no
  1.1308 + * value. If the value cannot be encoded using expected type, it shall be
  1.1309 + * encoded as text.
  1.1310 + *
  1.1311 + *   Typed-parameter = Well-known-parameter-token Typed-value
  1.1312 + *   Well-known-parameter-token = Integer-value
  1.1313 + *   Typed-value = Compact-value | Text-value
  1.1314 + *   Compact-value = Integer-value | Date-value | Delta-seconds-value | Q-value
  1.1315 + *                   | Version-value | Uri-value
  1.1316 + *
  1.1317 + * For Untyped-parameters, the type of the value is unknown, but is shall be
  1.1318 + * encoded as an integer, if that is possible.
  1.1319 + *
  1.1320 + *   Untyped-parameter = Token-text Untyped-value
  1.1321 + *   Untyped-value = Integer-value | Text-value
  1.1322 + *
  1.1323 + * @see WAP-230-WSP-20010705-a clause 8.4.2.4
  1.1324 + */
  1.1325 +this.Parameter = {
  1.1326 +  /**
  1.1327 +   * @param data
  1.1328 +   *        A wrapped object containing raw PDU data.
  1.1329 +   *
  1.1330 +   * @return A decoded object containing `name` and `value` properties or null
  1.1331 +   *         if something wrong. The `name` property must be a string, but the
  1.1332 +   *         `value` property can be many different types depending on `name`.
  1.1333 +   *
  1.1334 +   * @throws CodeError if decoded IntegerValue is an array.
  1.1335 +   * @throws NotWellKnownEncodingError if decoded well-known parameter number
  1.1336 +   *         is not registered or supported.
  1.1337 +   */
  1.1338 +  decodeTypedParameter: function(data) {
  1.1339 +    let numOrArray = IntegerValue.decode(data);
  1.1340 +    // `decodeIntegerValue` can return a array, which doesn't apply here.
  1.1341 +    if (typeof numOrArray != "number") {
  1.1342 +      throw new CodeError("Typed-parameter: invalid integer type");
  1.1343 +    }
  1.1344 +
  1.1345 +    let number = numOrArray;
  1.1346 +    let param = WSP_WELL_KNOWN_PARAMS[number];
  1.1347 +    if (!param) {
  1.1348 +      throw new NotWellKnownEncodingError(
  1.1349 +        "Typed-parameter: not well known parameter " + number);
  1.1350 +    }
  1.1351 +
  1.1352 +    let begin = data.offset, value;
  1.1353 +    try {
  1.1354 +      // Althought Text-string is not included in BNF of Compact-value, but
  1.1355 +      // some service provider might still pass a less-strict text form and
  1.1356 +      // cause a unexpected CodeError raised. For example, the `start`
  1.1357 +      // parameter expects its value of Text-value, but service provider might
  1.1358 +      // gives "<smil>", which contains illegal characters "<" and ">".
  1.1359 +      value = decodeAlternatives(data, null,
  1.1360 +                                 param.coder, TextValue, TextString);
  1.1361 +    } catch (e) {
  1.1362 +      data.offset = begin;
  1.1363 +
  1.1364 +      // Skip current parameter.
  1.1365 +      value = skipValue(data);
  1.1366 +      debug("Skip malformed typed parameter: "
  1.1367 +            + JSON.stringify({name: param.name, value: value}));
  1.1368 +
  1.1369 +      return null;
  1.1370 +    }
  1.1371 +
  1.1372 +    return {
  1.1373 +      name: param.name,
  1.1374 +      value: value,
  1.1375 +    };
  1.1376 +  },
  1.1377 +
  1.1378 +  /**
  1.1379 +   * @param data
  1.1380 +   *        A wrapped object containing raw PDU data.
  1.1381 +   *
  1.1382 +   * @return A decoded object containing `name` and `value` properties or null
  1.1383 +   *         if something wrong. The `name` property must be a string, but the
  1.1384 +   *         `value` property can be many different types depending on `name`.
  1.1385 +   */
  1.1386 +  decodeUntypedParameter: function(data) {
  1.1387 +    let name = TokenText.decode(data);
  1.1388 +
  1.1389 +    let begin = data.offset, value;
  1.1390 +    try {
  1.1391 +      value = decodeAlternatives(data, null, IntegerValue, TextValue);
  1.1392 +    } catch (e) {
  1.1393 +      data.offset = begin;
  1.1394 +
  1.1395 +      // Skip current parameter.
  1.1396 +      value = skipValue(data);
  1.1397 +      debug("Skip malformed untyped parameter: "
  1.1398 +            + JSON.stringify({name: name, value: value}));
  1.1399 +
  1.1400 +      return null;
  1.1401 +    }
  1.1402 +
  1.1403 +    return {
  1.1404 +      name: name.toLowerCase(),
  1.1405 +      value: value,
  1.1406 +    };
  1.1407 +  },
  1.1408 +
  1.1409 +  /**
  1.1410 +   * @param data
  1.1411 +   *        A wrapped object containing raw PDU data.
  1.1412 +   *
  1.1413 +   * @return A decoded object containing `name` and `value` properties or null
  1.1414 +   *         if something wrong. The `name` property must be a string, but the
  1.1415 +   *         `value` property can be many different types depending on `name`.
  1.1416 +   */
  1.1417 +  decode: function(data) {
  1.1418 +    let begin = data.offset;
  1.1419 +    try {
  1.1420 +      return this.decodeTypedParameter(data);
  1.1421 +    } catch (e) {
  1.1422 +      data.offset = begin;
  1.1423 +      return this.decodeUntypedParameter(data);
  1.1424 +    }
  1.1425 +  },
  1.1426 +
  1.1427 +  /**
  1.1428 +   * @param data
  1.1429 +   *        A wrapped object containing raw PDU data.
  1.1430 +   * @param end
  1.1431 +   *        Ending offset of following parameters.
  1.1432 +   *
  1.1433 +   * @return An array of decoded objects.
  1.1434 +   */
  1.1435 +  decodeMultiple: function(data, end) {
  1.1436 +    let params = null, param;
  1.1437 +
  1.1438 +    while (data.offset < end) {
  1.1439 +      try {
  1.1440 +        param = this.decode(data);
  1.1441 +      } catch (e) {
  1.1442 +        break;
  1.1443 +      }
  1.1444 +      if (param) {
  1.1445 +        if (!params) {
  1.1446 +          params = {};
  1.1447 +        }
  1.1448 +        params[param.name] = param.value;
  1.1449 +      }
  1.1450 +    }
  1.1451 +
  1.1452 +    return params;
  1.1453 +  },
  1.1454 +
  1.1455 +  /**
  1.1456 +   * @param data
  1.1457 +   *        A wrapped object to store encoded raw data.
  1.1458 +   * @param param
  1.1459 +   *        An object containing `name` and `value` properties.
  1.1460 +   */
  1.1461 +  encodeTypedParameter: function(data, param) {
  1.1462 +    let entry = WSP_WELL_KNOWN_PARAMS[param.name.toLowerCase()];
  1.1463 +    if (!entry) {
  1.1464 +      throw new NotWellKnownEncodingError(
  1.1465 +        "Typed-parameter: not well known parameter " + param.name);
  1.1466 +    }
  1.1467 +
  1.1468 +    IntegerValue.encode(data, entry.number);
  1.1469 +    encodeAlternatives(data, param.value, null,
  1.1470 +                       entry.coder, TextValue, TextString);
  1.1471 +  },
  1.1472 +
  1.1473 +  /**
  1.1474 +   * @param data
  1.1475 +   *        A wrapped object to store encoded raw data.
  1.1476 +   * @param param
  1.1477 +   *        An object containing `name` and `value` properties.
  1.1478 +   */
  1.1479 +  encodeUntypedParameter: function(data, param) {
  1.1480 +    TokenText.encode(data, param.name);
  1.1481 +    encodeAlternatives(data, param.value, null, IntegerValue, TextValue);
  1.1482 +  },
  1.1483 +
  1.1484 +  /**
  1.1485 +   * @param data
  1.1486 +   *        A wrapped object to store encoded raw data.
  1.1487 +   * @param param
  1.1488 +   *        An array of parameter objects.
  1.1489 +   */
  1.1490 +  encodeMultiple: function(data, params) {
  1.1491 +    for (let name in params) {
  1.1492 +      this.encode(data, {name: name, value: params[name]});
  1.1493 +    }
  1.1494 +  },
  1.1495 +
  1.1496 +  /**
  1.1497 +   * @param data
  1.1498 +   *        A wrapped object to store encoded raw data.
  1.1499 +   * @param param
  1.1500 +   *        An object containing `name` and `value` properties.
  1.1501 +   */
  1.1502 +  encode: function(data, param) {
  1.1503 +    let begin = data.offset;
  1.1504 +    try {
  1.1505 +      this.encodeTypedParameter(data, param);
  1.1506 +    } catch (e) {
  1.1507 +      data.offset = begin;
  1.1508 +      this.encodeUntypedParameter(data, param);
  1.1509 +    }
  1.1510 +  },
  1.1511 +};
  1.1512 +
  1.1513 +/**
  1.1514 + * Header = Message-header | Shift-sequence
  1.1515 + * Message-header = Well-known-header | Application-header
  1.1516 + *
  1.1517 + * @see WAP-230-WSP-20010705-a clause 8.4.2.6
  1.1518 + */
  1.1519 +this.Header = {
  1.1520 +  /**
  1.1521 +   * @param data
  1.1522 +   *        A wrapped object containing raw PDU data.
  1.1523 +   *
  1.1524 +   * @return A decoded object containing `name` and `value` properties or null
  1.1525 +   *         in case of a failed parsing. The `name` property must be a string,
  1.1526 +   *         but the `value` property can be many different types depending on
  1.1527 +   *         `name`.
  1.1528 +   */
  1.1529 +  decodeMessageHeader: function(data) {
  1.1530 +    return decodeAlternatives(data, null, WellKnownHeader, ApplicationHeader);
  1.1531 +  },
  1.1532 +
  1.1533 +  /**
  1.1534 +   * @param data
  1.1535 +   *        A wrapped object containing raw PDU data.
  1.1536 +   *
  1.1537 +   * @return A decoded object containing `name` and `value` properties or null
  1.1538 +   *         in case of a failed parsing. The `name` property must be a string,
  1.1539 +   *         but the `value` property can be many different types depending on
  1.1540 +   *         `name`.
  1.1541 +   */
  1.1542 +  decode: function(data) {
  1.1543 +    // TODO: support header code page shift-sequence
  1.1544 +    return this.decodeMessageHeader(data);
  1.1545 +  },
  1.1546 +
  1.1547 +  encodeMessageHeader: function(data, header) {
  1.1548 +    encodeAlternatives(data, header, null, WellKnownHeader, ApplicationHeader);
  1.1549 +  },
  1.1550 +
  1.1551 +  /**
  1.1552 +   * @param data
  1.1553 +   *        A wrapped object to store encoded raw data.
  1.1554 +   * @param header
  1.1555 +   *        An object containing two attributes: a string-typed `name` and a
  1.1556 +   *        `value` of arbitrary type.
  1.1557 +   */
  1.1558 +  encode: function(data, header) {
  1.1559 +    // TODO: support header code page shift-sequence
  1.1560 +    this.encodeMessageHeader(data, header);
  1.1561 +  },
  1.1562 +};
  1.1563 +
  1.1564 +/**
  1.1565 + * Well-known-header = Well-known-field-name Wap-value
  1.1566 + * Well-known-field-name = Short-integer
  1.1567 + *
  1.1568 + * @see WAP-230-WSP-20010705-a clause 8.4.2.6
  1.1569 + */
  1.1570 +this.WellKnownHeader = {
  1.1571 +  /**
  1.1572 +   * @param data
  1.1573 +   *        A wrapped object containing raw PDU data.
  1.1574 +   *
  1.1575 +   * @return A decoded object containing `name` and `value` properties or null
  1.1576 +   *         in case of a failed parsing. The `name` property must be a string,
  1.1577 +   *         but the `value` property can be many different types depending on
  1.1578 +   *         `name`.
  1.1579 +   *
  1.1580 +   * @throws NotWellKnownEncodingError if decoded well-known header field
  1.1581 +   *         number is not registered or supported.
  1.1582 +   */
  1.1583 +  decode: function(data) {
  1.1584 +    let index = ShortInteger.decode(data);
  1.1585 +
  1.1586 +    let entry = WSP_HEADER_FIELDS[index];
  1.1587 +    if (!entry) {
  1.1588 +      throw new NotWellKnownEncodingError(
  1.1589 +        "Well-known-header: not well known header " + index);
  1.1590 +    }
  1.1591 +
  1.1592 +    let begin = data.offset, value;
  1.1593 +    try {
  1.1594 +      value = decodeAlternatives(data, null, entry.coder, TextValue);
  1.1595 +    } catch (e) {
  1.1596 +      data.offset = begin;
  1.1597 +
  1.1598 +      value = skipValue(data);
  1.1599 +      debug("Skip malformed well known header(" + index + "): "
  1.1600 +            + JSON.stringify({name: entry.name, value: value}));
  1.1601 +
  1.1602 +      return null;
  1.1603 +    }
  1.1604 +
  1.1605 +    return {
  1.1606 +      name: entry.name,
  1.1607 +      value: value,
  1.1608 +    };
  1.1609 +  },
  1.1610 +
  1.1611 +  /**
  1.1612 +   * @param data
  1.1613 +   *        A wrapped object to store encoded raw data.
  1.1614 +   * @param header
  1.1615 +   *        An object containing two attributes: a string-typed `name` and a
  1.1616 +   *        `value` of arbitrary type.
  1.1617 +   */
  1.1618 +  encode: function(data, header) {
  1.1619 +    let entry = WSP_HEADER_FIELDS[header.name.toLowerCase()];
  1.1620 +    if (!entry) {
  1.1621 +      throw new NotWellKnownEncodingError(
  1.1622 +        "Well-known-header: not well known header " + header.name);
  1.1623 +    }
  1.1624 +
  1.1625 +    ShortInteger.encode(data, entry.number);
  1.1626 +    encodeAlternatives(data, header.value, null, entry.coder, TextValue);
  1.1627 +  },
  1.1628 +};
  1.1629 +
  1.1630 +/**
  1.1631 + * Application-header = Token-text Application-specific-value
  1.1632 + * Application-specific-value = Text-string
  1.1633 + *
  1.1634 + * @see WAP-230-WSP-20010705-a clause 8.4.2.6
  1.1635 + */
  1.1636 +this.ApplicationHeader = {
  1.1637 +  /**
  1.1638 +   * @param data
  1.1639 +   *        A wrapped object containing raw PDU data.
  1.1640 +   *
  1.1641 +   * @return A decoded object containing `name` and `value` properties or null
  1.1642 +   *         in case of a failed parsing. The `name` property must be a string,
  1.1643 +   *         but the `value` property can be many different types depending on
  1.1644 +   *         `name`.
  1.1645 +   */
  1.1646 +  decode: function(data) {
  1.1647 +    let name = TokenText.decode(data);
  1.1648 +
  1.1649 +    let begin = data.offset, value;
  1.1650 +    try {
  1.1651 +      value = TextString.decode(data);
  1.1652 +    } catch (e) {
  1.1653 +      data.offset = begin;
  1.1654 +
  1.1655 +      value = skipValue(data);
  1.1656 +      debug("Skip malformed application header: "
  1.1657 +            + JSON.stringify({name: name, value: value}));
  1.1658 +
  1.1659 +      return null;
  1.1660 +    }
  1.1661 +
  1.1662 +    return {
  1.1663 +      name: name.toLowerCase(),
  1.1664 +      value: value,
  1.1665 +    };
  1.1666 +  },
  1.1667 +
  1.1668 +  /**
  1.1669 +   * @param data
  1.1670 +   *        A wrapped object to store encoded raw data.
  1.1671 +   * @param header
  1.1672 +   *        An object containing two attributes: a string-typed `name` and a
  1.1673 +   *        `value` of arbitrary type.
  1.1674 +   *
  1.1675 +   * @throws CodeError if got an empty header name.
  1.1676 +   */
  1.1677 +  encode: function(data, header) {
  1.1678 +    if (!header.name) {
  1.1679 +      throw new CodeError("Application-header: empty header name");
  1.1680 +    }
  1.1681 +
  1.1682 +    TokenText.encode(data, header.name);
  1.1683 +    TextString.encode(data, header.value);
  1.1684 +  },
  1.1685 +};
  1.1686 +
  1.1687 +/**
  1.1688 + * Field-name = Token-text | Well-known-field-name
  1.1689 + * Well-known-field-name = Short-integer
  1.1690 + *
  1.1691 + * @see WAP-230-WSP-20010705-a clause 8.4.2.6
  1.1692 + */
  1.1693 +this.FieldName = {
  1.1694 +  /**
  1.1695 +   * @param data
  1.1696 +   *        A wrapped object containing raw PDU data.
  1.1697 +   *
  1.1698 +   * @return A field name string.
  1.1699 +   *
  1.1700 +   * @throws NotWellKnownEncodingError if decoded well-known header field
  1.1701 +   *         number is not registered or supported.
  1.1702 +   */
  1.1703 +  decode: function(data) {
  1.1704 +    let begin = data.offset;
  1.1705 +    try {
  1.1706 +      return TokenText.decode(data).toLowerCase();
  1.1707 +    } catch (e) {}
  1.1708 +
  1.1709 +    data.offset = begin;
  1.1710 +
  1.1711 +    let number = ShortInteger.decode(data);
  1.1712 +    let entry = WSP_HEADER_FIELDS[number];
  1.1713 +    if (!entry) {
  1.1714 +      throw new NotWellKnownEncodingError(
  1.1715 +        "Field-name: not well known encoding " + number);
  1.1716 +    }
  1.1717 +
  1.1718 +    return entry.name;
  1.1719 +  },
  1.1720 +
  1.1721 +  /**
  1.1722 +   * @param data
  1.1723 +   *        A wrapped object to store encoded raw data.
  1.1724 +   * @param name
  1.1725 +   *        A field name string.
  1.1726 +   */
  1.1727 +  encode: function(data, name) {
  1.1728 +    let entry = WSP_HEADER_FIELDS[name.toLowerCase()];
  1.1729 +    if (entry) {
  1.1730 +      ShortInteger.encode(data, entry.number);
  1.1731 +    } else {
  1.1732 +      TokenText.encode(data, name);
  1.1733 +    }
  1.1734 +  },
  1.1735 +};
  1.1736 +
  1.1737 +/**
  1.1738 + * Accept-charset-value = Constrained-charset | Accept-charset-general-form
  1.1739 + * Constrained-charset = Any-charset | Constrained-encoding
  1.1740 + * Any-charset = <Octet 128>
  1.1741 + * Accept-charset-general-form = Value-length (Well-known-charset | Token-text) [Q-value]
  1.1742 + *
  1.1743 + * @see WAP-230-WSP-20010705-a clause 8.4.2.8
  1.1744 + */
  1.1745 +this.AcceptCharsetValue = {
  1.1746 +  /**
  1.1747 +   * @param data
  1.1748 +   *        A wrapped object containing raw PDU data.
  1.1749 +   *
  1.1750 +   * @return A object with a property `charset` of string "*".
  1.1751 +   */
  1.1752 +  decodeAnyCharset: function(data) {
  1.1753 +    Octet.decodeEqualTo(data, 128);
  1.1754 +    return {charset: "*"};
  1.1755 +  },
  1.1756 +
  1.1757 +  /**
  1.1758 +   * @param data
  1.1759 +   *        A wrapped object containing raw PDU data.
  1.1760 +   *
  1.1761 +   * @return A object with a string property `charset` and a optional integer
  1.1762 +   *         property `q`.
  1.1763 +   *
  1.1764 +   * @throws NotWellKnownEncodingError if decoded well-known charset number is
  1.1765 +   *         not registered or supported.
  1.1766 +   */
  1.1767 +  decodeConstrainedCharset: function(data) {
  1.1768 +    let begin = data.offset;
  1.1769 +    try {
  1.1770 +      return this.decodeAnyCharset(data);
  1.1771 +    } catch (e) {}
  1.1772 +
  1.1773 +    data.offset = begin;
  1.1774 +
  1.1775 +    let numOrStr = ConstrainedEncoding.decode(data);
  1.1776 +    if (typeof numOrStr == "string") {
  1.1777 +      return {charset: numOrStr};
  1.1778 +    }
  1.1779 +
  1.1780 +    let charset = numOrStr;
  1.1781 +    let entry = WSP_WELL_KNOWN_CHARSETS[charset];
  1.1782 +    if (!entry) {
  1.1783 +      throw new NotWellKnownEncodingError(
  1.1784 +        "Constrained-charset: not well known charset: " + charset);
  1.1785 +    }
  1.1786 +
  1.1787 +    return {charset: entry.name};
  1.1788 +  },
  1.1789 +
  1.1790 +  /**
  1.1791 +   * @param data
  1.1792 +   *        A wrapped object containing raw PDU data.
  1.1793 +   *
  1.1794 +   * @return A object with a string property `charset` and a optional integer
  1.1795 +   *         property `q`.
  1.1796 +   */
  1.1797 +  decodeAcceptCharsetGeneralForm: function(data) {
  1.1798 +    let length = ValueLength.decode(data);
  1.1799 +
  1.1800 +    let begin = data.offset;
  1.1801 +    let end = begin + length;
  1.1802 +
  1.1803 +    let result;
  1.1804 +    try {
  1.1805 +      result = WellKnownCharset.decode(data);
  1.1806 +    } catch (e) {
  1.1807 +      data.offset = begin;
  1.1808 +
  1.1809 +      result = {charset: TokenText.decode(data)};
  1.1810 +      if (data.offset < end) {
  1.1811 +        result.q = QValue.decode(data);
  1.1812 +      }
  1.1813 +    }
  1.1814 +
  1.1815 +    if (data.offset != end) {
  1.1816 +      data.offset = end;
  1.1817 +    }
  1.1818 +
  1.1819 +    return result;
  1.1820 +  },
  1.1821 +
  1.1822 +  /**
  1.1823 +   * @param data
  1.1824 +   *        A wrapped object containing raw PDU data.
  1.1825 +   *
  1.1826 +   * @return A object with a string property `charset` and a optional integer
  1.1827 +   *         property `q`.
  1.1828 +   */
  1.1829 +  decode: function(data) {
  1.1830 +    let begin = data.offset;
  1.1831 +    try {
  1.1832 +      return this.decodeConstrainedCharset(data);
  1.1833 +    } catch (e) {
  1.1834 +      data.offset = begin;
  1.1835 +      return this.decodeAcceptCharsetGeneralForm(data);
  1.1836 +    }
  1.1837 +  },
  1.1838 +
  1.1839 +  /**
  1.1840 +   * @param data
  1.1841 +   *        A wrapped object to store encoded raw data.
  1.1842 +   * @param value
  1.1843 +   *        An object with a string property `charset`.
  1.1844 +   */
  1.1845 +  encodeAnyCharset: function(data, value) {
  1.1846 +    if (!value || !value.charset || (value.charset === "*")) {
  1.1847 +      Octet.encode(data, 128);
  1.1848 +      return;
  1.1849 +    }
  1.1850 +
  1.1851 +    throw new CodeError("Any-charset: invalid value " + value);
  1.1852 +  },
  1.1853 +};
  1.1854 +
  1.1855 +/**
  1.1856 + * Well-known-charset = Any-charset | Integer-value
  1.1857 + *
  1.1858 + * @see WAP-230-WSP-20010705-a clause 8.4.2.8
  1.1859 + */
  1.1860 +this.WellKnownCharset = {
  1.1861 +  /**
  1.1862 +   * @param data
  1.1863 +   *        A wrapped object containing raw PDU data.
  1.1864 +   *
  1.1865 +   * @return A object with a string property `charset`.
  1.1866 +   *
  1.1867 +   * @throws CodeError if decoded charset number is an array.
  1.1868 +   * @throws NotWellKnownEncodingError if decoded well-known charset number
  1.1869 +   *         is not registered or supported.
  1.1870 +   */
  1.1871 +  decode: function(data) {
  1.1872 +    let begin = data.offset;
  1.1873 +
  1.1874 +    try {
  1.1875 +      return AcceptCharsetValue.decodeAnyCharset(data);
  1.1876 +    } catch (e) {}
  1.1877 +
  1.1878 +    data.offset = begin;
  1.1879 +
  1.1880 +    // `IntegerValue.decode` can return a array, which doesn't apply here.
  1.1881 +    let numOrArray = IntegerValue.decode(data);
  1.1882 +    if (typeof numOrArray != "number") {
  1.1883 +      throw new CodeError("Well-known-charset: invalid integer type");
  1.1884 +    }
  1.1885 +
  1.1886 +    let charset = numOrArray;
  1.1887 +    let entry = WSP_WELL_KNOWN_CHARSETS[charset];
  1.1888 +    if (!entry) {
  1.1889 +      throw new NotWellKnownEncodingError(
  1.1890 +        "Well-known-charset: not well known charset " + charset);
  1.1891 +    }
  1.1892 +
  1.1893 +    return {charset: entry.name};
  1.1894 +  },
  1.1895 +
  1.1896 +  /**
  1.1897 +   * @param data
  1.1898 +   *        A wrapped object to store encoded raw data.
  1.1899 +   * @param value
  1.1900 +   */
  1.1901 +  encode: function(data, value) {
  1.1902 +    let begin = data.offset;
  1.1903 +    try {
  1.1904 +      AcceptCharsetValue.encodeAnyCharset(data, value);
  1.1905 +      return;
  1.1906 +    } catch (e) {}
  1.1907 +
  1.1908 +    data.offset = begin;
  1.1909 +    let entry = WSP_WELL_KNOWN_CHARSETS[value.charset.toLowerCase()];
  1.1910 +    if (!entry) {
  1.1911 +      throw new NotWellKnownEncodingError(
  1.1912 +        "Well-known-charset: not well known charset " + value.charset);
  1.1913 +    }
  1.1914 +
  1.1915 +    IntegerValue.encode(data, entry.number);
  1.1916 +  },
  1.1917 +};
  1.1918 +
  1.1919 +/**
  1.1920 + * The short form of the Content-type-value MUST only be used when the
  1.1921 + * well-known media is in the range of 0-127 or a text string. In all other
  1.1922 + * cases the general form MUST be used.
  1.1923 + *
  1.1924 + *   Content-type-value = Constrained-media | Content-general-form
  1.1925 + *   Constrained-media = Constrained-encoding
  1.1926 + *   Content-general-form = Value-length Media-type
  1.1927 + *   Media-type = Media *(Parameter)
  1.1928 + *   Media = Well-known-media | Extension-Media
  1.1929 + *   Well-known-media = Integer-value
  1.1930 + *   Extension-Media = *TEXT End-of-string
  1.1931 + *
  1.1932 + * @see WAP-230-WSP-20010705-a clause 8.4.2.24
  1.1933 + */
  1.1934 +this.ContentTypeValue = {
  1.1935 +  /**
  1.1936 +   * @param data
  1.1937 +   *        A wrapped object containing raw PDU data.
  1.1938 +   *
  1.1939 +   * @return A decoded object containing `media` and `params` properties or
  1.1940 +   *         null in case of a failed parsing. The `media` property must be a
  1.1941 +   *         string, and the `params` property is always null.
  1.1942 +   *
  1.1943 +   * @throws NotWellKnownEncodingError if decoded well-known content type number
  1.1944 +   *         is not registered or supported.
  1.1945 +   */
  1.1946 +  decodeConstrainedMedia: function(data) {
  1.1947 +    return {
  1.1948 +      media: TypeValue.decode(data),
  1.1949 +      params: null,
  1.1950 +    };
  1.1951 +  },
  1.1952 +
  1.1953 +  /**
  1.1954 +   * @param data
  1.1955 +   *        A wrapped object containing raw PDU data.
  1.1956 +   *
  1.1957 +   * @return Decode string.
  1.1958 +   *
  1.1959 +   * @throws CodeError if decoded content type number is an array.
  1.1960 +   * @throws NotWellKnownEncodingError if decoded well-known content type
  1.1961 +   *         number is not registered or supported.
  1.1962 +   */
  1.1963 +  decodeMedia: function(data) {
  1.1964 +    let begin = data.offset, number;
  1.1965 +    try {
  1.1966 +      number = IntegerValue.decode(data);
  1.1967 +    } catch (e) {
  1.1968 +      data.offset = begin;
  1.1969 +      return NullTerminatedTexts.decode(data).toLowerCase();
  1.1970 +    }
  1.1971 +
  1.1972 +    // `decodeIntegerValue` can return a array, which doesn't apply here.
  1.1973 +    if (typeof number != "number") {
  1.1974 +      throw new CodeError("Media: invalid integer type");
  1.1975 +    }
  1.1976 +
  1.1977 +    let entry = WSP_WELL_KNOWN_CONTENT_TYPES[number];
  1.1978 +    if (!entry) {
  1.1979 +      throw new NotWellKnownEncodingError("Media: not well known media " + number);
  1.1980 +    }
  1.1981 +
  1.1982 +    return entry.type;
  1.1983 +  },
  1.1984 +
  1.1985 +  /**
  1.1986 +   * @param data
  1.1987 +   *        A wrapped object containing raw PDU data.
  1.1988 +   * @param end
  1.1989 +   *        Ending offset of the Media-type value.
  1.1990 +   *
  1.1991 +   * @return A decoded object containing `media` and `params` properties or
  1.1992 +   *         null in case of a failed parsing. The `media` property must be a
  1.1993 +   *         string, and the `params` property is a hash map from a string to
  1.1994 +   *         an value of unspecified type.
  1.1995 +   */
  1.1996 +  decodeMediaType: function(data, end) {
  1.1997 +    let media = this.decodeMedia(data);
  1.1998 +    let params = Parameter.decodeMultiple(data, end);
  1.1999 +
  1.2000 +    return {
  1.2001 +      media: media,
  1.2002 +      params: params,
  1.2003 +    };
  1.2004 +  },
  1.2005 +
  1.2006 +  /**
  1.2007 +   * @param data
  1.2008 +   *        A wrapped object containing raw PDU data.
  1.2009 +   *
  1.2010 +   * @return A decoded object containing `media` and `params` properties or
  1.2011 +   *         null in case of a failed parsing. The `media` property must be a
  1.2012 +   *         string, and the `params` property is null or a hash map from a
  1.2013 +   *         string to an value of unspecified type.
  1.2014 +   */
  1.2015 +  decodeContentGeneralForm: function(data) {
  1.2016 +    let length = ValueLength.decode(data);
  1.2017 +    let end = data.offset + length;
  1.2018 +
  1.2019 +    let value = this.decodeMediaType(data, end);
  1.2020 +
  1.2021 +    if (data.offset != end) {
  1.2022 +      data.offset = end;
  1.2023 +    }
  1.2024 +
  1.2025 +    return value;
  1.2026 +  },
  1.2027 +
  1.2028 +  /**
  1.2029 +   * @param data
  1.2030 +   *        A wrapped object containing raw PDU data.
  1.2031 +   *
  1.2032 +   * @return A decoded object containing `media` and `params` properties or
  1.2033 +   *         null in case of a failed parsing. The `media` property must be a
  1.2034 +   *         string, and the `params` property is null or a hash map from a
  1.2035 +   *         string to an value of unspecified type.
  1.2036 +   */
  1.2037 +  decode: function(data) {
  1.2038 +    let begin = data.offset;
  1.2039 +
  1.2040 +    try {
  1.2041 +      return this.decodeConstrainedMedia(data);
  1.2042 +    } catch (e) {
  1.2043 +      data.offset = begin;
  1.2044 +      return this.decodeContentGeneralForm(data);
  1.2045 +    }
  1.2046 +  },
  1.2047 +
  1.2048 +  /**
  1.2049 +   * @param data
  1.2050 +   *        A wrapped object to store encoded raw data.
  1.2051 +   * @param value
  1.2052 +   *        An object containing `media` and `params` properties.
  1.2053 +   */
  1.2054 +  encodeConstrainedMedia: function(data, value) {
  1.2055 +    if (value.params) {
  1.2056 +      throw new CodeError("Constrained-media: should use general form instead");
  1.2057 +    }
  1.2058 +
  1.2059 +    TypeValue.encode(data, value.media);
  1.2060 +  },
  1.2061 +
  1.2062 +  /**
  1.2063 +   * @param data
  1.2064 +   *        A wrapped object to store encoded raw data.
  1.2065 +   * @param value
  1.2066 +   *        An object containing `media` and `params` properties.
  1.2067 +   */
  1.2068 +  encodeMediaType: function(data, value) {
  1.2069 +    let entry = WSP_WELL_KNOWN_CONTENT_TYPES[value.media.toLowerCase()];
  1.2070 +    if (entry) {
  1.2071 +      IntegerValue.encode(data, entry.number);
  1.2072 +    } else {
  1.2073 +      NullTerminatedTexts.encode(data, value.media);
  1.2074 +    }
  1.2075 +
  1.2076 +    Parameter.encodeMultiple(data, value.params);
  1.2077 +  },
  1.2078 +
  1.2079 +  /**
  1.2080 +   * @param data
  1.2081 +   *        A wrapped object to store encoded raw data.
  1.2082 +   * @param value
  1.2083 +   *        An object containing `media` and `params` properties.
  1.2084 +   */
  1.2085 +  encodeContentGeneralForm: function(data, value) {
  1.2086 +    let begin = data.offset;
  1.2087 +    this.encodeMediaType(data, value);
  1.2088 +
  1.2089 +    // Calculate how much octets will be written and seek back.
  1.2090 +    // TODO: use memmove, see bug 730873
  1.2091 +    let len = data.offset - begin;
  1.2092 +    data.offset = begin;
  1.2093 +
  1.2094 +    ValueLength.encode(data, len);
  1.2095 +    this.encodeMediaType(data, value);
  1.2096 +  },
  1.2097 +
  1.2098 +  /**
  1.2099 +   * @param data
  1.2100 +   *        A wrapped object to store encoded raw data.
  1.2101 +   * @param value
  1.2102 +   *        An object containing `media` and `params` properties.
  1.2103 +   */
  1.2104 +  encode: function(data, value) {
  1.2105 +    let begin = data.offset;
  1.2106 +
  1.2107 +    try {
  1.2108 +      this.encodeConstrainedMedia(data, value);
  1.2109 +    } catch (e) {
  1.2110 +      data.offset = begin;
  1.2111 +      this.encodeContentGeneralForm(data, value);
  1.2112 +    }
  1.2113 +  },
  1.2114 +};
  1.2115 +
  1.2116 +/**
  1.2117 + * Application-id-value = Uri-value | App-assigned-code
  1.2118 + * App-assigned-code = Integer-value
  1.2119 + *
  1.2120 + * @see WAP-230-WSP-20010705-a clause 8.4.2.54
  1.2121 + */
  1.2122 +this.ApplicationIdValue = {
  1.2123 +  /**
  1.2124 +   * @param data
  1.2125 +   *        A wrapped object containing raw PDU data.
  1.2126 +   *
  1.2127 +   * @return Decoded string value.
  1.2128 +   *
  1.2129 +   * @throws CodeError if decoded application id number is an array.
  1.2130 +   * @throws NotWellKnownEncodingError if decoded well-known application id
  1.2131 +   *         number is not registered or supported.
  1.2132 +   */
  1.2133 +  decode: function(data) {
  1.2134 +    let begin = data.offset;
  1.2135 +    try {
  1.2136 +      return UriValue.decode(data);
  1.2137 +    } catch (e) {}
  1.2138 +
  1.2139 +    data.offset = begin;
  1.2140 +
  1.2141 +    // `decodeIntegerValue` can return a array, which doesn't apply here.
  1.2142 +    let numOrArray = IntegerValue.decode(data);
  1.2143 +    if (typeof numOrArray != "number") {
  1.2144 +      throw new CodeError("Application-id-value: invalid integer type");
  1.2145 +    }
  1.2146 +
  1.2147 +    let id = numOrArray;
  1.2148 +    let entry = OMNA_PUSH_APPLICATION_IDS[id];
  1.2149 +    if (!entry) {
  1.2150 +      throw new NotWellKnownEncodingError(
  1.2151 +        "Application-id-value: not well known id: " + id);
  1.2152 +    }
  1.2153 +
  1.2154 +    return entry.urn;
  1.2155 +  },
  1.2156 +};
  1.2157 +
  1.2158 +this.PduHelper = {
  1.2159 +  /**
  1.2160 +   * @param data
  1.2161 +   *        A UInt8Array of data for decode.
  1.2162 +   * @param charset
  1.2163 +   *        charset for decode
  1.2164 +   *
  1.2165 +   * @return Decoded string.
  1.2166 +   */
  1.2167 +  decodeStringContent: function(data, charset) {
  1.2168 +      let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
  1.2169 +                 .createInstance(Ci.nsIScriptableUnicodeConverter);
  1.2170 +
  1.2171 +      let entry;
  1.2172 +      if (charset) {
  1.2173 +        entry = WSP_WELL_KNOWN_CHARSETS[charset];
  1.2174 +      }
  1.2175 +      // Set converter to default one if (entry && entry.converter) is null.
  1.2176 +      // @see OMA-TS-MMS-CONF-V1_3-20050526-D 7.1.9
  1.2177 +      conv.charset = (entry && entry.converter) || "UTF-8";
  1.2178 +      try {
  1.2179 +        return conv.convertFromByteArray(data, data.length);
  1.2180 +      } catch (e) {
  1.2181 +      }
  1.2182 +      return null;
  1.2183 +  },
  1.2184 +
  1.2185 +  /**
  1.2186 +  * @param strContent
  1.2187 +  *        Decoded string content.
  1.2188 +  * @param charset
  1.2189 +  *        Charset for encode.
  1.2190 +  *
  1.2191 +  * @return An encoded UInt8Array of string content.
  1.2192 +  */
  1.2193 +  encodeStringContent: function(strContent, charset) {
  1.2194 +    let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
  1.2195 +               .createInstance(Ci.nsIScriptableUnicodeConverter);
  1.2196 +
  1.2197 +    let entry;
  1.2198 +    if (charset) {
  1.2199 +      entry = WSP_WELL_KNOWN_CHARSETS[charset];
  1.2200 +    }
  1.2201 +    // Set converter to default one if (entry && entry.converter) is null.
  1.2202 +    // @see OMA-TS-MMS-CONF-V1_3-20050526-D 7.1.9
  1.2203 +    conv.charset = (entry && entry.converter) || "UTF-8";
  1.2204 +    try {
  1.2205 +      return conv.convertToByteArray(strContent);
  1.2206 +    } catch (e) {
  1.2207 +    }
  1.2208 +    return null;
  1.2209 +  },
  1.2210 +
  1.2211 +  /**
  1.2212 +   * Parse multiple header fields with end mark.
  1.2213 +   *
  1.2214 +   * @param data
  1.2215 +   *        A wrapped object containing raw PDU data.
  1.2216 +   * @param end
  1.2217 +   *        An ending offset indicating the end of headers.
  1.2218 +   * @param headers [optional]
  1.2219 +   *        An optional object to store parsed header fields. Created
  1.2220 +   *        automatically if undefined.
  1.2221 +   *
  1.2222 +   * @return A object containing decoded header fields as its attributes.
  1.2223 +   */
  1.2224 +  parseHeaders: function(data, end, headers) {
  1.2225 +    if (!headers) {
  1.2226 +      headers = {};
  1.2227 +    }
  1.2228 +
  1.2229 +    let header;
  1.2230 +    while (data.offset < end) {
  1.2231 +      try {
  1.2232 +        header = Header.decode(data);
  1.2233 +      } catch (e) {
  1.2234 +        break;
  1.2235 +      }
  1.2236 +      if (header) {
  1.2237 +        headers[header.name] = header.value;
  1.2238 +      }
  1.2239 +    }
  1.2240 +
  1.2241 +    if (data.offset != end) {
  1.2242 +      debug("Parser expects ending in " + end + ", but in " + data.offset);
  1.2243 +      // Explicitly seek to end in case of skipped header fields.
  1.2244 +      data.offset = end;
  1.2245 +    }
  1.2246 +
  1.2247 +    return headers;
  1.2248 +  },
  1.2249 +
  1.2250 +  /**
  1.2251 +   * @param data
  1.2252 +   *        A wrapped object containing raw PDU data.
  1.2253 +   * @param msg
  1.2254 +   *        Message object to be populated with decoded header fields.
  1.2255 +   *
  1.2256 +   * @see WAP-230-WSP-20010705-a clause 8.2.4
  1.2257 +   */
  1.2258 +  parsePushHeaders: function(data, msg) {
  1.2259 +    if (!msg.headers) {
  1.2260 +      msg.headers = {};
  1.2261 +    }
  1.2262 +
  1.2263 +    let headersLen = UintVar.decode(data);
  1.2264 +    let headersEnd = data.offset + headersLen;
  1.2265 +
  1.2266 +    let contentType = ContentTypeValue.decode(data);
  1.2267 +    msg.headers["content-type"] = contentType;
  1.2268 +
  1.2269 +    msg.headers = this.parseHeaders(data, headersEnd, msg.headers);
  1.2270 +  },
  1.2271 +
  1.2272 +  /**
  1.2273 +   * @param data
  1.2274 +   *        A wrapped object containing raw PDU data.
  1.2275 +   *
  1.2276 +   * @return An array of objects representing multipart entries or null in case
  1.2277 +   *         of errors found.
  1.2278 +   *
  1.2279 +   * @see WAP-230-WSP-20010705-a section 8.5
  1.2280 +   */
  1.2281 +  parseMultiPart: function(data) {
  1.2282 +    let nEntries = UintVar.decode(data);
  1.2283 +    if (!nEntries) {
  1.2284 +      return null;
  1.2285 +    }
  1.2286 +
  1.2287 +    let parts = new Array(nEntries);
  1.2288 +    for (let i = 0; i < nEntries; i++) {
  1.2289 +      // Length of the ContentType and Headers fields combined.
  1.2290 +      let headersLen = UintVar.decode(data);
  1.2291 +      // Length of the Data field
  1.2292 +      let contentLen = UintVar.decode(data);
  1.2293 +
  1.2294 +      let headersEnd = data.offset + headersLen;
  1.2295 +      let contentEnd = headersEnd + contentLen;
  1.2296 +
  1.2297 +      try {
  1.2298 +        let headers = {};
  1.2299 +
  1.2300 +        let contentType = ContentTypeValue.decode(data);
  1.2301 +        headers["content-type"] = contentType;
  1.2302 +        headers["content-length"] = contentLen;
  1.2303 +
  1.2304 +        headers = this.parseHeaders(data, headersEnd, headers);
  1.2305 +
  1.2306 +        let octetArray = Octet.decodeMultiple(data, contentEnd);
  1.2307 +        let content = null;
  1.2308 +        let charset = headers["content-type"].params &&
  1.2309 +                      headers["content-type"].params.charset
  1.2310 +                    ? headers["content-type"].params.charset.charset
  1.2311 +                    : null;
  1.2312 +
  1.2313 +        let mimeType = headers["content-type"].media;
  1.2314 +
  1.2315 +        if (mimeType) {
  1.2316 +          if (mimeType == "application/smil") {
  1.2317 +            // If the content is a SMIL type, convert it to a string.
  1.2318 +            // We hope to save and expose the SMIL content in a string way.
  1.2319 +            content = this.decodeStringContent(octetArray, charset);
  1.2320 +          } else if (mimeType.indexOf("text/") == 0 && charset != "utf-8") {
  1.2321 +            // If the content is a "text/plain" type, we have to make sure
  1.2322 +            // the encoding of the blob content should always be "utf-8".
  1.2323 +            let tmpStr = this.decodeStringContent(octetArray, charset);
  1.2324 +            let encoder = new TextEncoder("UTF-8");
  1.2325 +            content = new Blob([encoder.encode(tmpStr)], {type : mimeType});
  1.2326 +
  1.2327 +            // Make up the missing encoding info.
  1.2328 +            if (!headers["content-type"].params) {
  1.2329 +              headers["content-type"].params = {};
  1.2330 +            }
  1.2331 +            if (!headers["content-type"].params.charset) {
  1.2332 +              headers["content-type"].params.charset = {};
  1.2333 +            }
  1.2334 +            headers["content-type"].params.charset.charset = "utf-8";
  1.2335 +          }
  1.2336 +        }
  1.2337 +
  1.2338 +        if (!content) {
  1.2339 +          content = new Blob([octetArray], {type : mimeType});
  1.2340 +        }
  1.2341 +
  1.2342 +        parts[i] = {
  1.2343 +          index: i,
  1.2344 +          headers: headers,
  1.2345 +          content: content,
  1.2346 +        };
  1.2347 +      } catch (e) {
  1.2348 +        debug("Failed to parse multipart entry, message: " + e.message);
  1.2349 +        // Placeholder to keep original index of following entries.
  1.2350 +        parts[i] = null;
  1.2351 +      }
  1.2352 +
  1.2353 +      if (data.offset != contentEnd) {
  1.2354 +        // Seek to entry boundary for next entry.
  1.2355 +        data.offset = contentEnd;
  1.2356 +      }
  1.2357 +    }
  1.2358 +
  1.2359 +    return parts;
  1.2360 +  },
  1.2361 +
  1.2362 +  /**
  1.2363 +   * @param data
  1.2364 +   *        A wrapped object containing raw PDU data.
  1.2365 +   * @param isSessionless
  1.2366 +   *        Whether or not the PDU contains a session less WSP PDU.
  1.2367 +   * @param msg [optional]
  1.2368 +   *        Optional pre-defined PDU object.
  1.2369 +   *
  1.2370 +   * @return Parsed WSP PDU object or null in case of errors found.
  1.2371 +   */
  1.2372 +  parse: function(data, isSessionless, msg) {
  1.2373 +    if (!msg) {
  1.2374 +      msg = {
  1.2375 +        type: null,
  1.2376 +      };
  1.2377 +    }
  1.2378 +
  1.2379 +    try {
  1.2380 +      if (isSessionless) {
  1.2381 +        // "The `transactionId` is used to associate requests with replies in
  1.2382 +        // the connectionless session service." ~ WAP-230-WSP-20010705-a 8.2.1
  1.2383 +        msg.transactionId = Octet.decode(data);
  1.2384 +      }
  1.2385 +
  1.2386 +      msg.type = Octet.decode(data);
  1.2387 +      switch (msg.type) {
  1.2388 +        case WSP_PDU_TYPE_PUSH:
  1.2389 +          this.parsePushHeaders(data, msg);
  1.2390 +          break;
  1.2391 +      }
  1.2392 +    } catch (e) {
  1.2393 +      debug("Parse error. Message: " + e.message);
  1.2394 +      msg = null;
  1.2395 +    }
  1.2396 +
  1.2397 +    return msg;
  1.2398 +  },
  1.2399 +
  1.2400 +  /**
  1.2401 +   * @param multiStream
  1.2402 +   *        An exsiting nsIMultiplexInputStream.
  1.2403 +   * @param array
  1.2404 +   *        An octet array.
  1.2405 +   * @param length
  1.2406 +   *        Max number of octets to be coverted into an input stream.
  1.2407 +   */
  1.2408 +  appendArrayToMultiStream: function(multiStream, array, length) {
  1.2409 +    let storageStream = Cc["@mozilla.org/storagestream;1"]
  1.2410 +                        .createInstance(Ci.nsIStorageStream);
  1.2411 +    storageStream.init(4096, length, null);
  1.2412 +
  1.2413 +    let boStream = Cc["@mozilla.org/binaryoutputstream;1"]
  1.2414 +                   .createInstance(Ci.nsIBinaryOutputStream);
  1.2415 +    boStream.setOutputStream(storageStream.getOutputStream(0));
  1.2416 +    boStream.writeByteArray(array, length);
  1.2417 +    boStream.close();
  1.2418 +
  1.2419 +    multiStream.appendStream(storageStream.newInputStream(0));
  1.2420 +  },
  1.2421 +
  1.2422 +  /**
  1.2423 +   * @param multiStream
  1.2424 +   *        An exsiting nsIMultiplexInputStream.
  1.2425 +   * @param parts
  1.2426 +   *        An array of objects representing multipart entries.
  1.2427 +   *
  1.2428 +   * @see WAP-230-WSP-20010705-a section 8.5
  1.2429 +   */
  1.2430 +  composeMultiPart: function(multiStream, parts) {
  1.2431 +    // Encode multipart header
  1.2432 +    {
  1.2433 +      let data = {array: [], offset: 0};
  1.2434 +      UintVar.encode(data, parts.length);
  1.2435 +      debug("Encoded multipart header: " + JSON.stringify(data.array));
  1.2436 +      this.appendArrayToMultiStream(multiStream, data.array, data.offset);
  1.2437 +    }
  1.2438 +
  1.2439 +    // Encode each part
  1.2440 +    for (let i = 0; i < parts.length; i++) {
  1.2441 +      let part = parts[i];
  1.2442 +      let data = {array: [], offset: 0};
  1.2443 +
  1.2444 +      // Encode Content-Type
  1.2445 +      let contentType = part.headers["content-type"];
  1.2446 +      ContentTypeValue.encode(data, contentType);
  1.2447 +
  1.2448 +      // Encode other headers
  1.2449 +      if (Object.keys(part).length > 1) {
  1.2450 +        // Remove Content-Type temporarily
  1.2451 +        delete part.headers["content-type"];
  1.2452 +
  1.2453 +        for (let name in part.headers) {
  1.2454 +          Header.encode(data, {name: name, value: part.headers[name]});
  1.2455 +        }
  1.2456 +
  1.2457 +        // Restore Content-Type back
  1.2458 +        part.headers["content-type"] = contentType;
  1.2459 +      }
  1.2460 +
  1.2461 +      // Encode headersLen, DataLen
  1.2462 +      let headersLen = data.offset;
  1.2463 +      let content = part.content;
  1.2464 +      UintVar.encode(data, headersLen);
  1.2465 +      if (typeof content === "string") {
  1.2466 +        let charset;
  1.2467 +        if (contentType && contentType.params && contentType.params.charset &&
  1.2468 +          contentType.params.charset.charset) {
  1.2469 +          charset = contentType.params.charset.charset;
  1.2470 +        }
  1.2471 +        content = this.encodeStringContent(content, charset);
  1.2472 +        UintVar.encode(data, content.length);
  1.2473 +      } else if (part.content instanceof Uint8Array) {
  1.2474 +        UintVar.encode(data, content.length);
  1.2475 +      } else {
  1.2476 +        throw new TypeError();
  1.2477 +      }
  1.2478 +
  1.2479 +      // Move them to the beginning of encoded octet array.
  1.2480 +      let slice1 = data.array.slice(headersLen);
  1.2481 +      let slice2 = data.array.slice(0, headersLen);
  1.2482 +      data.array = slice1.concat(slice2);
  1.2483 +      debug("Encoded per-part header: " + JSON.stringify(data.array));
  1.2484 +
  1.2485 +      // Append per-part header
  1.2486 +      this.appendArrayToMultiStream(multiStream, data.array, data.offset);
  1.2487 +      // Append part content
  1.2488 +      this.appendArrayToMultiStream(multiStream, content, content.length);
  1.2489 +    }
  1.2490 +  },
  1.2491 +};
  1.2492 +
  1.2493 +// WSP Header Field Name Assignments
  1.2494 +// Note: Items commented out are either deprecated or not implemented.
  1.2495 +//       Deprecated items should only be supported for backward compatibility
  1.2496 +//       purpose.
  1.2497 +// @see WAP-230-WSP-20010705-a Appendix A. Assigned Numbers.
  1.2498 +this.WSP_HEADER_FIELDS = (function() {
  1.2499 +  let names = {};
  1.2500 +  function add(name, number, coder) {
  1.2501 +    let entry = {
  1.2502 +      name: name,
  1.2503 +      number: number,
  1.2504 +      coder: coder,
  1.2505 +    };
  1.2506 +    names[name] = names[number] = entry;
  1.2507 +  }
  1.2508 +
  1.2509 +  // Encoding Version: 1.1
  1.2510 +  //add("accept",               0x00);
  1.2511 +  //add("accept-charset",       0x01); Deprecated
  1.2512 +  //add("accept-encoding",      0x02); Deprecated
  1.2513 +  //add("accept-language",      0x03);
  1.2514 +  //add("accept-ranges",        0x04);
  1.2515 +  add("age",                    0x05, DeltaSecondsValue);
  1.2516 +  //add("allow",                0x06);
  1.2517 +  //add("authorization",        0x07);
  1.2518 +  //add("cache-control",        0x08); Deprecated
  1.2519 +  //add("connection",           0x09);
  1.2520 +  //add("content-base",         0x0A); Deprecated
  1.2521 +  //add("content-encoding",     0x0B);
  1.2522 +  //add("content-language",     0x0C);
  1.2523 +  add("content-length",         0x0D, IntegerValue);
  1.2524 +  add("content-location",       0x0E, UriValue);
  1.2525 +  //add("content-md5",          0x0F);
  1.2526 +  //add("content-range",        0x10); Deprecated
  1.2527 +  add("content-type",           0x11, ContentTypeValue);
  1.2528 +  add("date",                   0x12, DateValue);
  1.2529 +  add("etag",                   0x13, TextString);
  1.2530 +  add("expires",                0x14, DateValue);
  1.2531 +  add("from",                   0x15, TextString);
  1.2532 +  add("host",                   0x16, TextString);
  1.2533 +  add("if-modified-since",      0x17, DateValue);
  1.2534 +  add("if-match",               0x18, TextString);
  1.2535 +  add("if-none-match",          0x19, TextString);
  1.2536 +  //add("if-range",             0x1A);
  1.2537 +  add("if-unmodified-since",    0x1B, DateValue);
  1.2538 +  add("location",               0x1C, UriValue);
  1.2539 +  add("last-modified",          0x1D, DateValue);
  1.2540 +  add("max-forwards",           0x1E, IntegerValue);
  1.2541 +  //add("pragma",               0x1F);
  1.2542 +  //add("proxy-authenticate",   0x20);
  1.2543 +  //add("proxy-authentication", 0x21);
  1.2544 +  //add("public",               0x22);
  1.2545 +  //add("range",                0x23);
  1.2546 +  add("referer",                0x24, UriValue);
  1.2547 +  //add("retry-after",          0x25);
  1.2548 +  add("server",                 0x26, TextString);
  1.2549 +  //add("transfer-encoding",    0x27);
  1.2550 +  add("upgrade",                0x28, TextString);
  1.2551 +  add("user-agent",             0x29, TextString);
  1.2552 +  //add("vary",                 0x2A);
  1.2553 +  add("via",                    0x2B, TextString);
  1.2554 +  //add("warning",              0x2C);
  1.2555 +  //add("www-authenticate",     0x2D);
  1.2556 +  //add("content-disposition",  0x2E); Deprecated
  1.2557 +
  1.2558 +  // Encoding Version: 1.2
  1.2559 +  add("x-wap-application-id",   0x2F, ApplicationIdValue);
  1.2560 +  add("x-wap-content-uri",      0x30, UriValue);
  1.2561 +  add("x-wap-initiator-uri",    0x31, UriValue);
  1.2562 +  //add("accept-application",   0x32);
  1.2563 +  add("bearer-indication",      0x33, IntegerValue);
  1.2564 +  add("push-flag",              0x34, ShortInteger);
  1.2565 +  add("profile",                0x35, UriValue);
  1.2566 +  //add("profile-diff",         0x36);
  1.2567 +  //add("profile-warning",      0x37); Deprecated
  1.2568 +
  1.2569 +  // Encoding Version: 1.3
  1.2570 +  //add("expect",               0x38);
  1.2571 +  //add("te",                   0x39);
  1.2572 +  //add("trailer",              0x3A);
  1.2573 +  add("accept-charset",         0x3B, AcceptCharsetValue);
  1.2574 +  //add("accept-encoding",      0x3C);
  1.2575 +  //add("cache-control",        0x3D); Deprecated
  1.2576 +  //add("content-range",        0x3E);
  1.2577 +  add("x-wap-tod",              0x3F, DateValue);
  1.2578 +  add("content-id",             0x40, QuotedString);
  1.2579 +  //add("set-cookie",           0x41);
  1.2580 +  //add("cookie",               0x42);
  1.2581 +  //add("encoding-version",     0x43);
  1.2582 +
  1.2583 +  // Encoding Version: 1.4
  1.2584 +  //add("profile-warning",      0x44);
  1.2585 +  //add("content-disposition",  0x45);
  1.2586 +  //add("x-wap-security",       0x46);
  1.2587 +  //add("cache-control",        0x47);
  1.2588 +
  1.2589 +  return names;
  1.2590 +})();
  1.2591 +
  1.2592 +// WSP Content Type Assignments
  1.2593 +// @see http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
  1.2594 +this.WSP_WELL_KNOWN_CONTENT_TYPES = (function() {
  1.2595 +  let types = {};
  1.2596 +
  1.2597 +  function add(type, number) {
  1.2598 +    let entry = {
  1.2599 +      type: type,
  1.2600 +      number: number,
  1.2601 +    };
  1.2602 +    // For case like "text/x-vCalendar", we need toLoweCase() for generating
  1.2603 +    // the same index.
  1.2604 +    types[type.toLowerCase()] = types[number] = entry;
  1.2605 +  }
  1.2606 +
  1.2607 +  // Well Known Values
  1.2608 +  // Encoding Version: 1.1
  1.2609 +  add("*/*", 0x00);
  1.2610 +  add("text/*", 0x01);
  1.2611 +  add("text/html", 0x02);
  1.2612 +  add("text/plain", 0x03);
  1.2613 +  add("text/x-hdml", 0x04);
  1.2614 +  add("text/x-ttml", 0x05);
  1.2615 +  add("text/x-vCalendar", 0x06);
  1.2616 +  add("text/x-vCard", 0x07);
  1.2617 +  add("text/vnd.wap.wml", 0x08);
  1.2618 +  add("text/vnd.wap.wmlscript", 0x09);
  1.2619 +  add("text/vnd.wap.wta-event", 0x0A);
  1.2620 +  add("multipart/*", 0x0B);
  1.2621 +  add("multipart/mixed", 0x0C);
  1.2622 +  add("multipart/form-data", 0x0D);
  1.2623 +  add("multipart/byterantes", 0x0E);
  1.2624 +  add("multipart/alternative", 0x0F);
  1.2625 +  add("application/*", 0x10);
  1.2626 +  add("application/java-vm", 0x11);
  1.2627 +  add("application/x-www-form-urlencoded", 0x12);
  1.2628 +  add("application/x-hdmlc", 0x13);
  1.2629 +  add("application/vnd.wap.wmlc", 0x14);
  1.2630 +  add("application/vnd.wap.wmlscriptc", 0x15);
  1.2631 +  add("application/vnd.wap.wta-eventc", 0x16);
  1.2632 +  add("application/vnd.wap.uaprof", 0x17);
  1.2633 +  add("application/vnd.wap.wtls-ca-certificate", 0x18);
  1.2634 +  add("application/vnd.wap.wtls-user-certificate", 0x19);
  1.2635 +  add("application/x-x509-ca-cert", 0x1A);
  1.2636 +  add("application/x-x509-user-cert", 0x1B);
  1.2637 +  add("image/*", 0x1C);
  1.2638 +  add("image/gif", 0x1D);
  1.2639 +  add("image/jpeg", 0x1E);
  1.2640 +  add("image/tiff", 0x1F);
  1.2641 +  add("image/png", 0x20);
  1.2642 +  add("image/vnd.wap.wbmp", 0x21);
  1.2643 +  add("application/vnd.wap.multipart.*", 0x22);
  1.2644 +  add("application/vnd.wap.multipart.mixed", 0x23);
  1.2645 +  add("application/vnd.wap.multipart.form-data", 0x24);
  1.2646 +  add("application/vnd.wap.multipart.byteranges", 0x25);
  1.2647 +  add("application/vnd.wap.multipart.alternative", 0x26);
  1.2648 +  add("application/xml", 0x27);
  1.2649 +  add("text/xml", 0x28);
  1.2650 +  add("application/vnd.wap.wbxml", 0x29);
  1.2651 +  add("application/x-x968-cross-cert", 0x2A);
  1.2652 +  add("application/x-x968-ca-cert", 0x2B);
  1.2653 +  add("application/x-x968-user-cert", 0x2C);
  1.2654 +  add("text/vnd.wap.si", 0x2D);
  1.2655 +
  1.2656 +  // Encoding Version: 1.2
  1.2657 +  add("application/vnd.wap.sic", 0x2E);
  1.2658 +  add("text/vnd.wap.sl", 0x2F);
  1.2659 +  add("application/vnd.wap.slc", 0x30);
  1.2660 +  add("text/vnd.wap.co", 0x31);
  1.2661 +  add("application/vnd.wap.coc", 0x32);
  1.2662 +  add("application/vnd.wap.multipart.related", 0x33);
  1.2663 +  add("application/vnd.wap.sia", 0x34);
  1.2664 +
  1.2665 +  // Encoding Version: 1.3
  1.2666 +  add("text/vnd.wap.connectivity-xml", 0x35);
  1.2667 +  add("application/vnd.wap.connectivity-wbxml", 0x36);
  1.2668 +
  1.2669 +  // Encoding Version: 1.4
  1.2670 +  add("application/pkcs7-mime", 0x37);
  1.2671 +  add("application/vnd.wap.hashed-certificate", 0x38);
  1.2672 +  add("application/vnd.wap.signed-certificate", 0x39);
  1.2673 +  add("application/vnd.wap.cert-response", 0x3A);
  1.2674 +  add("application/xhtml+xml", 0x3B);
  1.2675 +  add("application/wml+xml", 0x3C);
  1.2676 +  add("text/css", 0x3D);
  1.2677 +  add("application/vnd.wap.mms-message", 0x3E);
  1.2678 +  add("application/vnd.wap.rollover-certificate", 0x3F);
  1.2679 +
  1.2680 +  // Encoding Version: 1.5
  1.2681 +  add("application/vnd.wap.locc+wbxml", 0x40);
  1.2682 +  add("application/vnd.wap.loc+xml", 0x41);
  1.2683 +  add("application/vnd.syncml.dm+wbxml", 0x42);
  1.2684 +  add("application/vnd.syncml.dm+xml", 0x43);
  1.2685 +  add("application/vnd.syncml.notification", 0x44);
  1.2686 +  add("application/vnd.wap.xhtml+xml", 0x45);
  1.2687 +  add("application/vnd.wv.csp.cir", 0x46);
  1.2688 +  add("application/vnd.oma.dd+xml", 0x47);
  1.2689 +  add("application/vnd.oma.drm.message", 0x48);
  1.2690 +  add("application/vnd.oma.drm.content", 0x49);
  1.2691 +  add("application/vnd.oma.drm.rights+xml", 0x4A);
  1.2692 +  add("application/vnd.oma.drm.rights+wbxml", 0x4B);
  1.2693 +  add("application/vnd.wv.csp+xml", 0x4C);
  1.2694 +  add("application/vnd.wv.csp+wbxml", 0x4D);
  1.2695 +  add("application/vnd.syncml.ds.notification", 0x4E);
  1.2696 +
  1.2697 +  // Encoding Version: 1.6
  1.2698 +  add("audio/*", 0x4F);
  1.2699 +  add("video/*", 0x50);
  1.2700 +
  1.2701 +  // Encoding Version: TBD
  1.2702 +  add("application/vnd.oma.dd2+xml", 0x51);
  1.2703 +  add("application/mikey", 0x52);
  1.2704 +  add("application/vnd.oma.dcd", 0x53);
  1.2705 +  add("application/vnd.oma.dcdc", 0x54);
  1.2706 +  add("text/x-vMessage", 0x55);
  1.2707 +  add("application/vnd.omads-email+wbxml", 0x56);
  1.2708 +  add("text/x-vBookmark", 0x57);
  1.2709 +  add("application/vnd.syncml.dm.notification", 0x58);
  1.2710 +  add("application/octet-stream", 0x5A);
  1.2711 +
  1.2712 +  return types;
  1.2713 +})();
  1.2714 +
  1.2715 +// WSP Well-Known Parameter Assignments
  1.2716 +// Note: Items commented out are either deprecated or not implemented.
  1.2717 +//       Deprecated items should not be used.
  1.2718 +// @see WAP-230-WSP-20010705-a Appendix A. Assigned Numbers.
  1.2719 +this.WSP_WELL_KNOWN_PARAMS = (function() {
  1.2720 +  let params = {};
  1.2721 +
  1.2722 +  function add(name, number, coder) {
  1.2723 +    let entry = {
  1.2724 +      name: name,
  1.2725 +      number: number,
  1.2726 +      coder: coder,
  1.2727 +    };
  1.2728 +    params[name] = params[number] = entry;
  1.2729 +  }
  1.2730 +
  1.2731 +  // Encoding Version: 1.1
  1.2732 +  add("q",                 0x00, QValue);
  1.2733 +  add("charset",           0x01, WellKnownCharset);
  1.2734 +  add("level",             0x02, VersionValue);
  1.2735 +  add("type",              0x03, IntegerValue);
  1.2736 +  add("name",              0x05, TextValue); // Deprecated, but used in some carriers, eg. Hinet.
  1.2737 +  //add("filename",        0x06); Deprecated
  1.2738 +  add("differences",       0x07, FieldName);
  1.2739 +  add("padding",           0x08, ShortInteger);
  1.2740 +
  1.2741 +  // Encoding Version: 1.2
  1.2742 +  add("type",              0x09, TypeValue);
  1.2743 +  add("start",             0x0A, TextValue); // Deprecated, but used in some carriers, eg. T-Mobile.
  1.2744 +  //add("start-info",      0x0B); Deprecated
  1.2745 +
  1.2746 +  // Encoding Version: 1.3
  1.2747 +  //add("comment",         0x0C); Deprecated
  1.2748 +  //add("domain",          0x0D); Deprecated
  1.2749 +  add("max-age",           0x0E, DeltaSecondsValue);
  1.2750 +  //add("path",            0x0F); Deprecated
  1.2751 +  add("secure",            0x10, NoValue);
  1.2752 +
  1.2753 +  // Encoding Version: 1.4
  1.2754 +  add("sec",               0x11, ShortInteger);
  1.2755 +  add("mac",               0x12, TextValue);
  1.2756 +  add("creation-date",     0x13, DateValue);
  1.2757 +  add("modification-date", 0x14, DateValue);
  1.2758 +  add("read-date",         0x15, DateValue);
  1.2759 +  add("size",              0x16, IntegerValue);
  1.2760 +  //add("name",            0x17, TextValue); // Not supported in some carriers, eg. Hinet.
  1.2761 +  add("filename",          0x18, TextValue);
  1.2762 +  //add("start",           0x19, TextValue); // Not supported in some carriers, eg. Hinet.
  1.2763 +  add("start-info",        0x1A, TextValue);
  1.2764 +  add("comment",           0x1B, TextValue);
  1.2765 +  add("domain",            0x1C, TextValue);
  1.2766 +  add("path",              0x1D, TextValue);
  1.2767 +
  1.2768 +  return params;
  1.2769 +})();
  1.2770 +
  1.2771 +// WSP Character Set Assignments
  1.2772 +// @see WAP-230-WSP-20010705-a Appendix A. Assigned Numbers.
  1.2773 +// @see http://www.iana.org/assignments/character-sets
  1.2774 +this.WSP_WELL_KNOWN_CHARSETS = (function() {
  1.2775 +  let charsets = {};
  1.2776 +
  1.2777 +  function add(name, number, converter) {
  1.2778 +    let entry = {
  1.2779 +      name: name,
  1.2780 +      number: number,
  1.2781 +      converter: converter,
  1.2782 +    };
  1.2783 +
  1.2784 +    charsets[name] = charsets[number] = entry;
  1.2785 +  }
  1.2786 +
  1.2787 +  add("us-ascii",           3, null);
  1.2788 +  add("iso-8859-1",         4, "ISO-8859-1");
  1.2789 +  add("iso-8859-2",         5, "ISO-8859-2");
  1.2790 +  add("iso-8859-3",         6, "ISO-8859-3");
  1.2791 +  add("iso-8859-4",         7, "ISO-8859-4");
  1.2792 +  add("iso-8859-5",         8, "ISO-8859-5");
  1.2793 +  add("iso-8859-6",         9, "ISO-8859-6");
  1.2794 +  add("iso-8859-7",        10, "ISO-8859-7");
  1.2795 +  add("iso-8859-8",        11, "ISO-8859-8");
  1.2796 +  add("iso-8859-9",        12, "ISO-8859-9");
  1.2797 +  add("iso-8859-10",       13, "ISO-8859-10");
  1.2798 +  add("shift_jis",         17, "Shift_JIS");
  1.2799 +  add("euc-jp",            18, "EUC-JP");
  1.2800 +  add("iso-2022-kr",       37, "ISO-2022-KR");
  1.2801 +  add("euc-kr",            38, "EUC-KR");
  1.2802 +  add("iso-2022-jp",       39, "ISO-2022-JP");
  1.2803 +  add("iso-2022-jp-2",     40, "iso-2022-jp-2");
  1.2804 +  add("iso-8859-6-e",      81, "ISO-8859-6-E");
  1.2805 +  add("iso-8859-6-i",      82, "ISO-8859-6-I");
  1.2806 +  add("iso-8859-8-e",      84, "ISO-8859-8-E");
  1.2807 +  add("iso-8859-8-i",      85, "ISO-8859-8-I");
  1.2808 +  add("utf-8",            106, "UTF-8");
  1.2809 +  add("iso-10646-ucs-2", 1000, "iso-10646-ucs-2");
  1.2810 +  add("utf-16",          1015, "UTF-16");
  1.2811 +  add("gb2312",          2025, "GB2312");
  1.2812 +  add("big5",            2026, "Big5");
  1.2813 +  add("koi8-r",          2084, "KOI8-R");
  1.2814 +  add("windows-1252",    2252, "windows-1252");
  1.2815 +
  1.2816 +  return charsets;
  1.2817 +})();
  1.2818 +
  1.2819 +// OMNA PUSH Application ID
  1.2820 +// @see http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
  1.2821 +this.OMNA_PUSH_APPLICATION_IDS = (function() {
  1.2822 +  let ids = {};
  1.2823 +
  1.2824 +  function add(urn, number) {
  1.2825 +    let entry = {
  1.2826 +      urn: urn,
  1.2827 +      number: number,
  1.2828 +    };
  1.2829 +
  1.2830 +    ids[urn] = ids[number] = entry;
  1.2831 +  }
  1.2832 +
  1.2833 +  add("x-wap-application:wml.ua", 0x02);
  1.2834 +  add("x-wap-application:mms.ua", 0x04);
  1.2835 +
  1.2836 +  return ids;
  1.2837 +})();
  1.2838 +
  1.2839 +let debug;
  1.2840 +if (DEBUG) {
  1.2841 +  debug = function(s) {
  1.2842 +    dump("-@- WspPduHelper: " + s + "\n");
  1.2843 +  };
  1.2844 +} else {
  1.2845 +  debug = function(s) {};
  1.2846 +}
  1.2847 +
  1.2848 +this.EXPORTED_SYMBOLS = ALL_CONST_SYMBOLS.concat([
  1.2849 +  // Constant values
  1.2850 +  "WSP_HEADER_FIELDS",
  1.2851 +  "WSP_WELL_KNOWN_CONTENT_TYPES",
  1.2852 +  "WSP_WELL_KNOWN_PARAMS",
  1.2853 +  "WSP_WELL_KNOWN_CHARSETS",
  1.2854 +  "OMNA_PUSH_APPLICATION_IDS",
  1.2855 +
  1.2856 +  // Error classes
  1.2857 +  "CodeError",
  1.2858 +  "FatalCodeError",
  1.2859 +  "NotWellKnownEncodingError",
  1.2860 +
  1.2861 +  // Utility functions
  1.2862 +  "ensureHeader",
  1.2863 +  "skipValue",
  1.2864 +  "decodeAlternatives",
  1.2865 +  "encodeAlternatives",
  1.2866 +
  1.2867 +  // Decoders
  1.2868 +  "Octet",
  1.2869 +  "Text",
  1.2870 +  "NullTerminatedTexts",
  1.2871 +  "Token",
  1.2872 +  "URIC",
  1.2873 +  "TextString",
  1.2874 +  "TokenText",
  1.2875 +  "QuotedString",
  1.2876 +  "ShortInteger",
  1.2877 +  "LongInteger",
  1.2878 +  "UintVar",
  1.2879 +  "ConstrainedEncoding",
  1.2880 +  "ValueLength",
  1.2881 +  "NoValue",
  1.2882 +  "TextValue",
  1.2883 +  "IntegerValue",
  1.2884 +  "DateValue",
  1.2885 +  "DeltaSecondsValue",
  1.2886 +  "QValue",
  1.2887 +  "VersionValue",
  1.2888 +  "UriValue",
  1.2889 +  "TypeValue",
  1.2890 +  "Parameter",
  1.2891 +  "Header",
  1.2892 +  "WellKnownHeader",
  1.2893 +  "ApplicationHeader",
  1.2894 +  "FieldName",
  1.2895 +  "AcceptCharsetValue",
  1.2896 +  "WellKnownCharset",
  1.2897 +  "ContentTypeValue",
  1.2898 +  "ApplicationIdValue",
  1.2899 +
  1.2900 +  // Parser
  1.2901 +  "PduHelper",
  1.2902 +]);

mercurial