dom/mobilemessage/src/gonk/WspPduHelper.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 "use strict";
michael@0 6
michael@0 7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
michael@0 8
michael@0 9 Cu.import("resource://gre/modules/wap_consts.js", this);
michael@0 10
michael@0 11 let DEBUG; // set to true to see debug messages
michael@0 12
michael@0 13 // Special ASCII characters
michael@0 14 const NUL = 0;
michael@0 15 const CR = 13;
michael@0 16 const LF = 10;
michael@0 17 const SP = 32;
michael@0 18 const HT = 9;
michael@0 19 const DQUOTE = 34;
michael@0 20 const DEL = 127;
michael@0 21
michael@0 22 // Special ASCII character ranges
michael@0 23 const CTLS = 32;
michael@0 24 const ASCIIS = 128;
michael@0 25
michael@0 26 /**
michael@0 27 * Error class for generic encoding/decoding failures.
michael@0 28 */
michael@0 29 this.CodeError = function CodeError(message) {
michael@0 30 this.name = "CodeError";
michael@0 31 this.message = message || "Invalid format";
michael@0 32 }
michael@0 33 CodeError.prototype = new Error();
michael@0 34 CodeError.prototype.constructor = CodeError;
michael@0 35
michael@0 36 /**
michael@0 37 * Error class for unexpected NUL char at decoding text elements.
michael@0 38 *
michael@0 39 * @param message [optional]
michael@0 40 * A short description for the error.
michael@0 41 */
michael@0 42 function NullCharError(message) {
michael@0 43 this.name = "NullCharError";
michael@0 44 this.message = message || "Null character found";
michael@0 45 }
michael@0 46 NullCharError.prototype = new CodeError();
michael@0 47 NullCharError.prototype.constructor = NullCharError;
michael@0 48
michael@0 49 /**
michael@0 50 * Error class for fatal encoding/decoding failures.
michael@0 51 *
michael@0 52 * This error is only raised when expected format isn't met and the parser
michael@0 53 * context can't do anything more to either skip it or hand over to other
michael@0 54 * alternative encoding/decoding steps.
michael@0 55 *
michael@0 56 * @param message [optional]
michael@0 57 * A short description for the error.
michael@0 58 */
michael@0 59 this.FatalCodeError = function FatalCodeError(message) {
michael@0 60 this.name = "FatalCodeError";
michael@0 61 this.message = message || "Decoding fails";
michael@0 62 }
michael@0 63 FatalCodeError.prototype = new Error();
michael@0 64 FatalCodeError.prototype.constructor = FatalCodeError;
michael@0 65
michael@0 66 /**
michael@0 67 * Error class for undefined well known encoding.
michael@0 68 *
michael@0 69 * When a encoded header field/parameter has unknown/unsupported value, we may
michael@0 70 * never know how to decode the next value. For example, a parameter of
michael@0 71 * undefined well known encoding may be followed by a Q-value, which is
michael@0 72 * basically a uintvar. However, there is no way you can distiguish an Q-value
michael@0 73 * 0.64, encoded as 0x41, from a string begins with 'A', which is also 0x41.
michael@0 74 * The `skipValue` will try the latter one, which is not expected.
michael@0 75 *
michael@0 76 * @param message [optional]
michael@0 77 * A short description for the error.
michael@0 78 */
michael@0 79 this.NotWellKnownEncodingError = function NotWellKnownEncodingError(message) {
michael@0 80 this.name = "NotWellKnownEncodingError";
michael@0 81 this.message = message || "Not well known encoding";
michael@0 82 }
michael@0 83 NotWellKnownEncodingError.prototype = new FatalCodeError();
michael@0 84 NotWellKnownEncodingError.prototype.constructor = NotWellKnownEncodingError;
michael@0 85
michael@0 86 /**
michael@0 87 * Internal helper function to retrieve the value of a property with its name
michael@0 88 * specified by `name` inside the object `headers`.
michael@0 89 *
michael@0 90 * @param headers
michael@0 91 * An object that contains parsed header fields.
michael@0 92 * @param name
michael@0 93 * Header name string to be checked.
michael@0 94 *
michael@0 95 * @return Value of specified header field.
michael@0 96 *
michael@0 97 * @throws FatalCodeError if headers[name] is undefined.
michael@0 98 */
michael@0 99 this.ensureHeader = function ensureHeader(headers, name) {
michael@0 100 let value = headers[name];
michael@0 101 // Header field might have a null value as NoValue
michael@0 102 if (value === undefined) {
michael@0 103 throw new FatalCodeError("ensureHeader: header " + name + " not defined");
michael@0 104 }
michael@0 105 return value;
michael@0 106 }
michael@0 107
michael@0 108 /**
michael@0 109 * Skip field value.
michael@0 110 *
michael@0 111 * The WSP field values are encoded so that the length of the field value can
michael@0 112 * always be determined, even if the detailed format of a specific field value
michael@0 113 * is not known. This makes it possible to skip over individual header fields
michael@0 114 * without interpreting their content. ... the first octet in all the field
michael@0 115 * values can be interpreted as follows:
michael@0 116 *
michael@0 117 * 0 - 30 | This octet is followed by the indicated number (0 - 30) of data
michael@0 118 * octets.
michael@0 119 * 31 | This octet is followed by a unitvar, which indicates the number
michael@0 120 * of data octets after it.
michael@0 121 * 32 - 127 | The value is a text string, terminated by a zero octet (NUL
michael@0 122 * character).
michael@0 123 * 128 - 255 | It is an encoded 7-bit value; this header has no more data.
michael@0 124 *
michael@0 125 * @param data
michael@0 126 * A wrapped object containing raw PDU data.
michael@0 127 *
michael@0 128 * @return Skipped value of several possible types like string, integer, or
michael@0 129 * an array of octets.
michael@0 130 *
michael@0 131 * @see WAP-230-WSP-20010705-a clause 8.4.1.2
michael@0 132 */
michael@0 133 this.skipValue = function skipValue(data) {
michael@0 134 let begin = data.offset;
michael@0 135 let value = Octet.decode(data);
michael@0 136 if (value <= 31) {
michael@0 137 if (value == 31) {
michael@0 138 value = UintVar.decode(data);
michael@0 139 }
michael@0 140
michael@0 141 if (value) {
michael@0 142 // `value` can be larger than 30, max length of a multi-octet integer
michael@0 143 // here. So we must decode it as an array instead.
michael@0 144 value = Octet.decodeMultiple(data, data.offset + value);
michael@0 145 } else {
michael@0 146 value = null;
michael@0 147 }
michael@0 148 } else if (value <= 127) {
michael@0 149 data.offset = begin;
michael@0 150 value = NullTerminatedTexts.decode(data);
michael@0 151 } else {
michael@0 152 value &= 0x7F;
michael@0 153 }
michael@0 154
michael@0 155 return value;
michael@0 156 }
michael@0 157
michael@0 158 /**
michael@0 159 * Helper function for decoding multiple alternative forms.
michael@0 160 *
michael@0 161 * @param data
michael@0 162 * A wrapped object containing raw PDU data.
michael@0 163 * @param options
michael@0 164 * Extra context for decoding.
michael@0 165 *
michael@0 166 * @return Decoded value.
michael@0 167 */
michael@0 168 this.decodeAlternatives = function decodeAlternatives(data, options) {
michael@0 169 let begin = data.offset;
michael@0 170 for (let i = 2; i < arguments.length; i++) {
michael@0 171 try {
michael@0 172 return arguments[i].decode(data, options);
michael@0 173 } catch (e) {
michael@0 174 // Throw the last exception we get
michael@0 175 if (i == (arguments.length - 1)) {
michael@0 176 throw e;
michael@0 177 }
michael@0 178
michael@0 179 data.offset = begin;
michael@0 180 }
michael@0 181 }
michael@0 182 }
michael@0 183
michael@0 184 /**
michael@0 185 * Helper function for encoding multiple alternative forms.
michael@0 186 *
michael@0 187 * @param data
michael@0 188 * A wrapped object to store encoded raw data.
michael@0 189 * @param value
michael@0 190 * Object value of arbitrary type to be encoded.
michael@0 191 * @param options
michael@0 192 * Extra context for encoding.
michael@0 193 */
michael@0 194 this.encodeAlternatives = function encodeAlternatives(data, value, options) {
michael@0 195 let begin = data.offset;
michael@0 196 for (let i = 3; i < arguments.length; i++) {
michael@0 197 try {
michael@0 198 arguments[i].encode(data, value, options);
michael@0 199 return;
michael@0 200 } catch (e) {
michael@0 201 // Throw the last exception we get
michael@0 202 if (i == (arguments.length - 1)) {
michael@0 203 throw e;
michael@0 204 }
michael@0 205
michael@0 206 data.offset = begin;
michael@0 207 }
michael@0 208 }
michael@0 209 }
michael@0 210
michael@0 211 this.Octet = {
michael@0 212 /**
michael@0 213 * @param data
michael@0 214 * A wrapped object containing raw PDU data.
michael@0 215 *
michael@0 216 * @throws RangeError if no more data is available.
michael@0 217 */
michael@0 218 decode: function(data) {
michael@0 219 if (data.offset >= data.array.length) {
michael@0 220 throw new RangeError();
michael@0 221 }
michael@0 222
michael@0 223 return data.array[data.offset++];
michael@0 224 },
michael@0 225
michael@0 226 /**
michael@0 227 * @param data
michael@0 228 * A wrapped object containing raw PDU data.
michael@0 229 * @param end
michael@0 230 * An ending offset indicating the end of octet array to read.
michael@0 231 *
michael@0 232 * @return A decoded array object.
michael@0 233 *
michael@0 234 * @throws RangeError if no enough data to read.
michael@0 235 * @throws TypeError if `data` has neither subarray() nor slice() method.
michael@0 236 */
michael@0 237 decodeMultiple: function(data, end) {
michael@0 238 if ((end < data.offset) || (end > data.array.length)) {
michael@0 239 throw new RangeError();
michael@0 240 }
michael@0 241 if (end == data.offset) {
michael@0 242 return [];
michael@0 243 }
michael@0 244
michael@0 245 let result;
michael@0 246 if (data.array.subarray) {
michael@0 247 result = data.array.subarray(data.offset, end);
michael@0 248 } else if (data.array.slice) {
michael@0 249 result = data.array.slice(data.offset, end);
michael@0 250 } else {
michael@0 251 throw new TypeError();
michael@0 252 }
michael@0 253
michael@0 254 data.offset = end;
michael@0 255 return result;
michael@0 256 },
michael@0 257
michael@0 258 /**
michael@0 259 * Internal octet decoding for specific value.
michael@0 260 *
michael@0 261 * @param data
michael@0 262 * A wrapped object containing raw PDU data.
michael@0 263 * @param expected
michael@0 264 * Expected octet value.
michael@0 265 *
michael@0 266 * @return Expected octet value.
michael@0 267 *
michael@0 268 * @throws CodeError if read octet is not equal to expected one.
michael@0 269 */
michael@0 270 decodeEqualTo: function(data, expected) {
michael@0 271 if (this.decode(data) != expected) {
michael@0 272 throw new CodeError("Octet - decodeEqualTo: doesn't match " + expected);
michael@0 273 }
michael@0 274
michael@0 275 return expected;
michael@0 276 },
michael@0 277
michael@0 278 /**
michael@0 279 * @param data
michael@0 280 * A wrapped object to store encoded raw data.
michael@0 281 * @param octet
michael@0 282 * Octet value to be encoded.
michael@0 283 */
michael@0 284 encode: function(data, octet) {
michael@0 285 if (data.offset >= data.array.length) {
michael@0 286 data.array.push(octet);
michael@0 287 data.offset++;
michael@0 288 } else {
michael@0 289 data.array[data.offset++] = octet;
michael@0 290 }
michael@0 291 },
michael@0 292
michael@0 293 /**
michael@0 294 * @param data
michael@0 295 * A wrapped object to store encoded raw data.
michael@0 296 * @param octet
michael@0 297 * An octet array object.
michael@0 298 */
michael@0 299 encodeMultiple: function(data, array) {
michael@0 300 for (let i = 0; i < array.length; i++) {
michael@0 301 this.encode(data, array[i]);
michael@0 302 }
michael@0 303 },
michael@0 304 };
michael@0 305
michael@0 306 /**
michael@0 307 * TEXT = <any OCTET except CTLs, but including LWS>
michael@0 308 * CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
michael@0 309 * LWS = [CRLF] 1*(SP|HT)
michael@0 310 * CRLF = CR LF
michael@0 311 * CR = <US-ASCII CR, carriage return (13)>
michael@0 312 * LF = <US-ASCII LF, linefeed (10)>
michael@0 313 * SP = <US-ASCII SP, space (32)>
michael@0 314 * HT = <US-ASCII HT, horizontal-tab(9)>
michael@0 315 *
michael@0 316 * @see RFC 2616 clause 2.2 Basic Rules
michael@0 317 */
michael@0 318 this.Text = {
michael@0 319 /**
michael@0 320 * @param data
michael@0 321 * A wrapped object containing raw PDU data.
michael@0 322 *
michael@0 323 * @return Decoded character.
michael@0 324 *
michael@0 325 * @throws NullCharError if a NUL character read.
michael@0 326 * @throws CodeError if a control character read.
michael@0 327 */
michael@0 328 decode: function(data) {
michael@0 329 let code = Octet.decode(data);
michael@0 330 if ((code >= CTLS) && (code != DEL)) {
michael@0 331 return String.fromCharCode(code);
michael@0 332 }
michael@0 333
michael@0 334 if (code == NUL) {
michael@0 335 throw new NullCharError();
michael@0 336 }
michael@0 337
michael@0 338 if (code != CR) {
michael@0 339 throw new CodeError("Text: invalid char code " + code);
michael@0 340 }
michael@0 341
michael@0 342 // "A CRLF is allowed in the definition of TEXT only as part of a header
michael@0 343 // field continuation. It is expected that the folding LWS will be
michael@0 344 // replaced with a single SP before interpretation of the TEXT value."
michael@0 345 // ~ RFC 2616 clause 2.2
michael@0 346
michael@0 347 let extra;
michael@0 348
michael@0 349 // Rethrow everything as CodeError. We had already a successful read above.
michael@0 350 try {
michael@0 351 extra = Octet.decode(data);
michael@0 352 if (extra != LF) {
michael@0 353 throw new CodeError("Text: doesn't match LWS sequence");
michael@0 354 }
michael@0 355
michael@0 356 extra = Octet.decode(data);
michael@0 357 if ((extra != SP) && (extra != HT)) {
michael@0 358 throw new CodeError("Text: doesn't match LWS sequence");
michael@0 359 }
michael@0 360 } catch (e if e instanceof CodeError) {
michael@0 361 throw e;
michael@0 362 } catch (e) {
michael@0 363 throw new CodeError("Text: doesn't match LWS sequence");
michael@0 364 }
michael@0 365
michael@0 366 // Let's eat as many SP|HT as possible.
michael@0 367 let begin;
michael@0 368
michael@0 369 // Do not throw anything here. We had already matched (SP | HT).
michael@0 370 try {
michael@0 371 do {
michael@0 372 begin = data.offset;
michael@0 373 extra = Octet.decode(data);
michael@0 374 } while ((extra == SP) || (extra == HT));
michael@0 375 } catch (e) {}
michael@0 376
michael@0 377 data.offset = begin;
michael@0 378 return " ";
michael@0 379 },
michael@0 380
michael@0 381 /**
michael@0 382 * @param data
michael@0 383 * A wrapped object to store encoded raw data.
michael@0 384 * @param text
michael@0 385 * String text of one character to be encoded.
michael@0 386 * @param asciiOnly
michael@0 387 * A boolean to decide if it's only allowed to encode ASCII (0 ~ 127).
michael@0 388 *
michael@0 389 * @throws CodeError if a control character got.
michael@0 390 */
michael@0 391 encode: function(data, text, asciiOnly) {
michael@0 392 if (!text) {
michael@0 393 throw new CodeError("Text: empty string");
michael@0 394 }
michael@0 395
michael@0 396 let code = text.charCodeAt(0);
michael@0 397 if ((code < CTLS) || (code == DEL) || (code > 255) ||
michael@0 398 (code >= 128 && asciiOnly)) {
michael@0 399 throw new CodeError("Text: invalid char code " + code);
michael@0 400 }
michael@0 401 Octet.encode(data, code);
michael@0 402 },
michael@0 403 };
michael@0 404
michael@0 405 this.NullTerminatedTexts = {
michael@0 406 /**
michael@0 407 * Decode internal referenced null terminated text string.
michael@0 408 *
michael@0 409 * @param data
michael@0 410 * A wrapped object containing raw PDU data.
michael@0 411 *
michael@0 412 * @return Decoded string.
michael@0 413 */
michael@0 414 decode: function(data) {
michael@0 415 let str = "";
michael@0 416 try {
michael@0 417 // A End-of-string is also a CTL, which should cause a error.
michael@0 418 while (true) {
michael@0 419 str += Text.decode(data);
michael@0 420 }
michael@0 421 } catch (e if e instanceof NullCharError) {
michael@0 422 return str;
michael@0 423 }
michael@0 424 },
michael@0 425
michael@0 426 /**
michael@0 427 * @param data
michael@0 428 * A wrapped object to store encoded raw data.
michael@0 429 * @param str
michael@0 430 * A String to be encoded.
michael@0 431 * @param asciiOnly
michael@0 432 * A boolean to decide if it's only allowed to encode ASCII (0 ~ 127).
michael@0 433 */
michael@0 434 encode: function(data, str, asciiOnly) {
michael@0 435 if (str) {
michael@0 436 for (let i = 0; i < str.length; i++) {
michael@0 437 Text.encode(data, str.charAt(i), asciiOnly);
michael@0 438 }
michael@0 439 }
michael@0 440 Octet.encode(data, 0);
michael@0 441 },
michael@0 442 };
michael@0 443
michael@0 444 /**
michael@0 445 * TOKEN = 1*<any CHAR except CTLs or separators>
michael@0 446 * CHAR = <any US-ASCII character (octets 0 - 127)>
michael@0 447 * SEPARATORS = ()<>@,;:\"/[]?={} SP HT
michael@0 448 *
michael@0 449 * @see RFC 2616 clause 2.2 Basic Rules
michael@0 450 */
michael@0 451 this.Token = {
michael@0 452 /**
michael@0 453 * @param data
michael@0 454 * A wrapped object containing raw PDU data.
michael@0 455 *
michael@0 456 * @return Decoded character.
michael@0 457 *
michael@0 458 * @throws NullCharError if a NUL character read.
michael@0 459 * @throws CodeError if an invalid character read.
michael@0 460 */
michael@0 461 decode: function(data) {
michael@0 462 let code = Octet.decode(data);
michael@0 463 if ((code < ASCIIS) && (code >= CTLS)) {
michael@0 464 if ((code == HT) || (code == SP)
michael@0 465 || (code == 34) || (code == 40) || (code == 41) // ASCII "()
michael@0 466 || (code == 44) || (code == 47) // ASCII ,/
michael@0 467 || ((code >= 58) && (code <= 64)) // ASCII :;<=>?@
michael@0 468 || ((code >= 91) && (code <= 93)) // ASCII [\]
michael@0 469 || (code == 123) || (code == 125)) { // ASCII {}
michael@0 470 throw new CodeError("Token: invalid char code " + code);
michael@0 471 }
michael@0 472
michael@0 473 return String.fromCharCode(code);
michael@0 474 }
michael@0 475
michael@0 476 if (code == NUL) {
michael@0 477 throw new NullCharError();
michael@0 478 }
michael@0 479
michael@0 480 throw new CodeError("Token: invalid char code " + code);
michael@0 481 },
michael@0 482
michael@0 483 /**
michael@0 484 * @param data
michael@0 485 * A wrapped object to store encoded raw data.
michael@0 486 * @param token
michael@0 487 * String text of one character to be encoded.
michael@0 488 *
michael@0 489 * @throws CodeError if an invalid character got.
michael@0 490 */
michael@0 491 encode: function(data, token) {
michael@0 492 if (!token) {
michael@0 493 throw new CodeError("Token: empty string");
michael@0 494 }
michael@0 495
michael@0 496 let code = token.charCodeAt(0);
michael@0 497 if ((code < ASCIIS) && (code >= CTLS)) {
michael@0 498 if ((code == HT) || (code == SP)
michael@0 499 || (code == 34) || (code == 40) || (code == 41) // ASCII "()
michael@0 500 || (code == 44) || (code == 47) // ASCII ,/
michael@0 501 || ((code >= 58) && (code <= 64)) // ASCII :;<=>?@
michael@0 502 || ((code >= 91) && (code <= 93)) // ASCII [\]
michael@0 503 || (code == 123) || (code == 125)) { // ASCII {}
michael@0 504 // Fallback to throw CodeError
michael@0 505 } else {
michael@0 506 Octet.encode(data, token.charCodeAt(0));
michael@0 507 return;
michael@0 508 }
michael@0 509 }
michael@0 510
michael@0 511 throw new CodeError("Token: invalid char code " + code);
michael@0 512 },
michael@0 513 };
michael@0 514
michael@0 515 /**
michael@0 516 * uric = reserved | unreserved | escaped
michael@0 517 * reserved = ;/?:@&=+$,
michael@0 518 * unreserved = alphanum | mark
michael@0 519 * mark = -_.!~*'()
michael@0 520 * escaped = % hex hex
michael@0 521 * excluded but used = #%
michael@0 522 *
michael@0 523 * Or, in decimal, they are: 33,35-59,61,63-90,95,97-122,126
michael@0 524 *
michael@0 525 * @see RFC 2396 Uniform Resource Indentifiers (URI)
michael@0 526 */
michael@0 527 this.URIC = {
michael@0 528 /**
michael@0 529 * @param data
michael@0 530 * A wrapped object containing raw PDU data.
michael@0 531 *
michael@0 532 * @return Decoded character.
michael@0 533 *
michael@0 534 * @throws NullCharError if a NUL character read.
michael@0 535 * @throws CodeError if an invalid character read.
michael@0 536 */
michael@0 537 decode: function(data) {
michael@0 538 let code = Octet.decode(data);
michael@0 539 if (code == NUL) {
michael@0 540 throw new NullCharError();
michael@0 541 }
michael@0 542
michael@0 543 if ((code <= CTLS) || (code >= ASCIIS) || (code == 34) || (code == 60)
michael@0 544 || (code == 62) || ((code >= 91) && (code <= 94)) || (code == 96)
michael@0 545 || ((code >= 123) && (code <= 125)) || (code == 127)) {
michael@0 546 throw new CodeError("URIC: invalid char code " + code);
michael@0 547 }
michael@0 548
michael@0 549 return String.fromCharCode(code);
michael@0 550 },
michael@0 551 };
michael@0 552
michael@0 553 /**
michael@0 554 * If the first character in the TEXT is in the range of 128-255, a Quote
michael@0 555 * character must precede it. Otherwise the Quote character must be omitted.
michael@0 556 * The Quote is not part of the contents.
michael@0 557 *
michael@0 558 * Text-string = [Quote] *TEXT End-of-string
michael@0 559 * Quote = <Octet 127>
michael@0 560 *
michael@0 561 * @see WAP-230-WSP-20010705-a clause 8.4.2.1
michael@0 562 */
michael@0 563 this.TextString = {
michael@0 564 /**
michael@0 565 * @param data
michael@0 566 * A wrapped object containing raw PDU data.
michael@0 567 *
michael@0 568 * @return Decoded string.
michael@0 569 */
michael@0 570 decode: function(data) {
michael@0 571 let begin = data.offset;
michael@0 572 let firstCode = Octet.decode(data);
michael@0 573 if (firstCode == 127) {
michael@0 574 // Quote found, check if first char code is larger-equal than 128.
michael@0 575 begin = data.offset;
michael@0 576 try {
michael@0 577 if (Octet.decode(data) < 128) {
michael@0 578 throw new CodeError("Text-string: illegal quote found.");
michael@0 579 }
michael@0 580 } catch (e if e instanceof CodeError) {
michael@0 581 throw e;
michael@0 582 } catch (e) {
michael@0 583 throw new CodeError("Text-string: unexpected error.");
michael@0 584 }
michael@0 585 } else if (firstCode >= 128) {
michael@0 586 throw new CodeError("Text-string: invalid char code " + firstCode);
michael@0 587 }
michael@0 588
michael@0 589 data.offset = begin;
michael@0 590 return NullTerminatedTexts.decode(data);
michael@0 591 },
michael@0 592
michael@0 593 /**
michael@0 594 * @param data
michael@0 595 * A wrapped object to store encoded raw data.
michael@0 596 * @param str
michael@0 597 * A String to be encoded.
michael@0 598 * @param asciiOnly
michael@0 599 * A boolean to decide if it's only allowed to encode ASCII (0 ~ 127).
michael@0 600 */
michael@0 601 encode: function(data, str, asciiOnly) {
michael@0 602 if (!str) {
michael@0 603 Octet.encode(data, 0);
michael@0 604 return;
michael@0 605 }
michael@0 606
michael@0 607 let firstCharCode = str.charCodeAt(0);
michael@0 608 if (firstCharCode >= 128) {
michael@0 609 if (asciiOnly) {
michael@0 610 throw new CodeError("Text: invalid char code " + code);
michael@0 611 }
michael@0 612
michael@0 613 Octet.encode(data, 127);
michael@0 614 }
michael@0 615
michael@0 616 NullTerminatedTexts.encode(data, str, asciiOnly);
michael@0 617 },
michael@0 618 };
michael@0 619
michael@0 620 /**
michael@0 621 * Token-text = Token End-of-string
michael@0 622 *
michael@0 623 * @see WAP-230-WSP-20010705-a clause 8.4.2.1
michael@0 624 */
michael@0 625 this.TokenText = {
michael@0 626 /**
michael@0 627 * @param data
michael@0 628 * A wrapped object containing raw PDU data.
michael@0 629 *
michael@0 630 * @return Decoded string.
michael@0 631 */
michael@0 632 decode: function(data) {
michael@0 633 let str = "";
michael@0 634 try {
michael@0 635 // A End-of-string is also a CTL, which should cause a error.
michael@0 636 while (true) {
michael@0 637 str += Token.decode(data);
michael@0 638 }
michael@0 639 } catch (e if e instanceof NullCharError) {
michael@0 640 return str;
michael@0 641 }
michael@0 642 },
michael@0 643
michael@0 644 /**
michael@0 645 * @param data
michael@0 646 * A wrapped object to store encoded raw data.
michael@0 647 * @param str
michael@0 648 * A String to be encoded.
michael@0 649 */
michael@0 650 encode: function(data, str) {
michael@0 651 if (str) {
michael@0 652 for (let i = 0; i < str.length; i++) {
michael@0 653 Token.encode(data, str.charAt(i));
michael@0 654 }
michael@0 655 }
michael@0 656 Octet.encode(data, 0);
michael@0 657 },
michael@0 658 };
michael@0 659
michael@0 660 /**
michael@0 661 * The TEXT encodes an RFC2616 Quoted-string with the enclosing
michael@0 662 * quotation-marks <"> removed.
michael@0 663 *
michael@0 664 * Quoted-string = <Octet 34> *TEXT End-of-string
michael@0 665 *
michael@0 666 * @see WAP-230-WSP-20010705-a clause 8.4.2.1
michael@0 667 */
michael@0 668 this.QuotedString = {
michael@0 669 /**
michael@0 670 * @param data
michael@0 671 * A wrapped object containing raw PDU data.
michael@0 672 *
michael@0 673 * @return Decoded string.
michael@0 674 *
michael@0 675 * @throws CodeError if first octet read is not 0x34.
michael@0 676 */
michael@0 677 decode: function(data) {
michael@0 678 let value = Octet.decode(data);
michael@0 679 if (value != 34) {
michael@0 680 throw new CodeError("Quoted-string: not quote " + value);
michael@0 681 }
michael@0 682
michael@0 683 return NullTerminatedTexts.decode(data);
michael@0 684 },
michael@0 685
michael@0 686 /**
michael@0 687 * @param data
michael@0 688 * A wrapped object to store encoded raw data.
michael@0 689 * @param str
michael@0 690 * A String to be encoded.
michael@0 691 */
michael@0 692 encode: function(data, str) {
michael@0 693 Octet.encode(data, 34);
michael@0 694 NullTerminatedTexts.encode(data, str);
michael@0 695 },
michael@0 696 };
michael@0 697
michael@0 698 /**
michael@0 699 * Integers in range 0-127 shall be encoded as a one octet value with the
michael@0 700 * most significant bit set to one (1xxx xxxx) and with the value in the
michael@0 701 * remaining least significant bits.
michael@0 702 *
michael@0 703 * Short-integer = OCTET
michael@0 704 *
michael@0 705 * @see WAP-230-WSP-20010705-a clause 8.4.2.1
michael@0 706 */
michael@0 707 this.ShortInteger = {
michael@0 708 /**
michael@0 709 * @param data
michael@0 710 * A wrapped object containing raw PDU data.
michael@0 711 *
michael@0 712 * @return Decoded integer value.
michael@0 713 *
michael@0 714 * @throws CodeError if the octet read is less than 0x80.
michael@0 715 */
michael@0 716 decode: function(data) {
michael@0 717 let value = Octet.decode(data);
michael@0 718 if (!(value & 0x80)) {
michael@0 719 throw new CodeError("Short-integer: invalid value " + value);
michael@0 720 }
michael@0 721
michael@0 722 return (value & 0x7F);
michael@0 723 },
michael@0 724
michael@0 725 /**
michael@0 726 * @param data
michael@0 727 * A wrapped object to store encoded raw data.
michael@0 728 * @param value
michael@0 729 * A numeric value to be encoded.
michael@0 730 *
michael@0 731 * @throws CodeError if the octet read is larger-equal than 0x80.
michael@0 732 */
michael@0 733 encode: function(data, value) {
michael@0 734 if (value >= 0x80) {
michael@0 735 throw new CodeError("Short-integer: invalid value " + value);
michael@0 736 }
michael@0 737
michael@0 738 Octet.encode(data, value | 0x80);
michael@0 739 },
michael@0 740 };
michael@0 741
michael@0 742 /**
michael@0 743 * The content octets shall be an unsigned integer value with the most
michael@0 744 * significant octet encoded first (big-endian representation). The minimum
michael@0 745 * number of octets must be used to encode the value.
michael@0 746 *
michael@0 747 * Long-integer = Short-length Multi-octet-integer
michael@0 748 * Short-length = <Any octet 0-30>
michael@0 749 * Multi-octet-integer = 1*30 OCTET
michael@0 750 *
michael@0 751 * @see WAP-230-WSP-20010705-a clause 8.4.2.1
michael@0 752 */
michael@0 753 this.LongInteger = {
michael@0 754 /**
michael@0 755 * @param data
michael@0 756 * A wrapped object containing raw PDU data.
michael@0 757 * @param length
michael@0 758 * Number of octets to read.
michael@0 759 *
michael@0 760 * @return A decoded integer value or an octets array of max 30 elements.
michael@0 761 */
michael@0 762 decodeMultiOctetInteger: function(data, length) {
michael@0 763 if (length < 7) {
michael@0 764 // Return a integer instead of an array as possible. For a multi-octet
michael@0 765 // integer, there are only maximum 53 bits for integer in javascript. We
michael@0 766 // will get an inaccurate one beyond that. We can't neither use bitwise
michael@0 767 // operation here, for it will be limited in 32 bits.
michael@0 768 let value = 0;
michael@0 769 while (length--) {
michael@0 770 value = value * 256 + Octet.decode(data);
michael@0 771 }
michael@0 772 return value;
michael@0 773 }
michael@0 774
michael@0 775 return Octet.decodeMultiple(data, data.offset + length);
michael@0 776 },
michael@0 777
michael@0 778 /**
michael@0 779 * @param data
michael@0 780 * A wrapped object containing raw PDU data.
michael@0 781 *
michael@0 782 * @return A decoded integer value or an octets array of max 30 elements.
michael@0 783 *
michael@0 784 * @throws CodeError if the length read is not in 1..30.
michael@0 785 */
michael@0 786 decode: function(data) {
michael@0 787 let length = Octet.decode(data);
michael@0 788 if ((length < 1) || (length > 30)) {
michael@0 789 throw new CodeError("Long-integer: invalid length " + length);
michael@0 790 }
michael@0 791
michael@0 792 return this.decodeMultiOctetInteger(data, length);
michael@0 793 },
michael@0 794
michael@0 795 /**
michael@0 796 * @param data
michael@0 797 * A wrapped object to store encoded raw data.
michael@0 798 * @param numOrArray
michael@0 799 * An octet array of less-equal than 30 elements or an integer
michael@0 800 * greater-equal than 128.
michael@0 801 */
michael@0 802 encode: function(data, numOrArray) {
michael@0 803 if (typeof numOrArray === "number") {
michael@0 804 let num = numOrArray;
michael@0 805 if (num >= 0x1000000000000) {
michael@0 806 throw new CodeError("Long-integer: number too large " + num);
michael@0 807 }
michael@0 808
michael@0 809 let stack = [];
michael@0 810 do {
michael@0 811 stack.push(Math.floor(num % 256));
michael@0 812 num = Math.floor(num / 256);
michael@0 813 } while (num);
michael@0 814
michael@0 815 Octet.encode(data, stack.length);
michael@0 816 while (stack.length) {
michael@0 817 Octet.encode(data, stack.pop());
michael@0 818 }
michael@0 819 return;
michael@0 820 }
michael@0 821
michael@0 822 let array = numOrArray;
michael@0 823 if ((array.length < 1) || (array.length > 30)) {
michael@0 824 throw new CodeError("Long-integer: invalid length " + array.length);
michael@0 825 }
michael@0 826
michael@0 827 Octet.encode(data, array.length);
michael@0 828 Octet.encodeMultiple(data, array);
michael@0 829 },
michael@0 830 };
michael@0 831
michael@0 832 /**
michael@0 833 * @see WAP-230-WSP-20010705-a clause 8.4.2.1
michael@0 834 */
michael@0 835 this.UintVar = {
michael@0 836 /**
michael@0 837 * @param data
michael@0 838 * A wrapped object containing raw PDU data.
michael@0 839 *
michael@0 840 * @return Decoded integer value.
michael@0 841 */
michael@0 842 decode: function(data) {
michael@0 843 let value = Octet.decode(data);
michael@0 844 let result = value & 0x7F;
michael@0 845 while (value & 0x80) {
michael@0 846 value = Octet.decode(data);
michael@0 847 result = result * 128 + (value & 0x7F);
michael@0 848 }
michael@0 849
michael@0 850 return result;
michael@0 851 },
michael@0 852
michael@0 853 /**
michael@0 854 * @param data
michael@0 855 * A wrapped object to store encoded raw data.
michael@0 856 * @param value
michael@0 857 * An integer value.
michael@0 858 */
michael@0 859 encode: function(data, value) {
michael@0 860 if (value < 0) {
michael@0 861 throw new CodeError("UintVar: invalid value " + value);
michael@0 862 }
michael@0 863
michael@0 864 let stack = [];
michael@0 865 while (value >= 128) {
michael@0 866 stack.push(Math.floor(value % 128));
michael@0 867 value = Math.floor(value / 128);
michael@0 868 }
michael@0 869
michael@0 870 while (stack.length) {
michael@0 871 Octet.encode(data, value | 0x80);
michael@0 872 value = stack.pop();
michael@0 873 }
michael@0 874 Octet.encode(data, value);
michael@0 875 },
michael@0 876 };
michael@0 877
michael@0 878 /**
michael@0 879 * This encoding is used for token values, which have no well-known binary
michael@0 880 * encoding, or when the assigned number of the well-known encoding is small
michael@0 881 * enough to fit into Short-Integer. We change Extension-Media from
michael@0 882 * NullTerminatedTexts to TextString because of Bug 823816.
michael@0 883 *
michael@0 884 * Constrained-encoding = Extension-Media | Short-integer
michael@0 885 * Extension-Media = TextString
michael@0 886 *
michael@0 887 * @see WAP-230-WSP-20010705-a clause 8.4.2.1
michael@0 888 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=823816
michael@0 889 */
michael@0 890 this.ConstrainedEncoding = {
michael@0 891 /**
michael@0 892 * @param data
michael@0 893 * A wrapped object containing raw PDU data.
michael@0 894 *
michael@0 895 * @return Decode integer value or string.
michael@0 896 */
michael@0 897 decode: function(data) {
michael@0 898 return decodeAlternatives(data, null, TextString, ShortInteger);
michael@0 899 },
michael@0 900
michael@0 901 /**
michael@0 902 * @param data
michael@0 903 * A wrapped object to store encoded raw data.
michael@0 904 * @param value
michael@0 905 * An integer or a string value.
michael@0 906 */
michael@0 907 encode: function(data, value) {
michael@0 908 if (typeof value == "number") {
michael@0 909 ShortInteger.encode(data, value);
michael@0 910 } else {
michael@0 911 TextString.encode(data, value);
michael@0 912 }
michael@0 913 },
michael@0 914 };
michael@0 915
michael@0 916 /**
michael@0 917 * Value-length = Short-length | (Length-quote Length)
michael@0 918 * Short-length = <Any octet 0-30>
michael@0 919 * Length-quote = <Octet 31>
michael@0 920 * Length = Uintvar-integer
michael@0 921 *
michael@0 922 * @see WAP-230-WSP-20010705-a clause 8.4.2.2
michael@0 923 */
michael@0 924 this.ValueLength = {
michael@0 925 /**
michael@0 926 * @param data
michael@0 927 * A wrapped object containing raw PDU data.
michael@0 928 *
michael@0 929 * @return Decoded integer value.
michael@0 930 *
michael@0 931 * @throws CodeError if the first octet read is larger than 31.
michael@0 932 */
michael@0 933 decode: function(data) {
michael@0 934 let value = Octet.decode(data);
michael@0 935 if (value <= 30) {
michael@0 936 return value;
michael@0 937 }
michael@0 938
michael@0 939 if (value == 31) {
michael@0 940 return UintVar.decode(data);
michael@0 941 }
michael@0 942
michael@0 943 throw new CodeError("Value-length: invalid value " + value);
michael@0 944 },
michael@0 945
michael@0 946 /**
michael@0 947 * @param data
michael@0 948 * A wrapped object to store encoded raw data.
michael@0 949 * @param value
michael@0 950 */
michael@0 951 encode: function(data, value) {
michael@0 952 if (value <= 30) {
michael@0 953 Octet.encode(data, value);
michael@0 954 } else {
michael@0 955 Octet.encode(data, 31);
michael@0 956 UintVar.encode(data, value);
michael@0 957 }
michael@0 958 },
michael@0 959 };
michael@0 960
michael@0 961 /**
michael@0 962 * No-value = <Octet 0>
michael@0 963 *
michael@0 964 * @see WAP-230-WSP-20010705-a clause 8.4.2.3
michael@0 965 */
michael@0 966 this.NoValue = {
michael@0 967 /**
michael@0 968 * @param data
michael@0 969 * A wrapped object containing raw PDU data.
michael@0 970 *
michael@0 971 * @return Always returns null.
michael@0 972 */
michael@0 973 decode: function(data) {
michael@0 974 Octet.decodeEqualTo(data, 0);
michael@0 975 return null;
michael@0 976 },
michael@0 977
michael@0 978 /**
michael@0 979 * @param data
michael@0 980 * A wrapped object to store encoded raw data.
michael@0 981 * @param value
michael@0 982 * A null or undefined value.
michael@0 983 */
michael@0 984 encode: function(data, value) {
michael@0 985 if (value != null) {
michael@0 986 throw new CodeError("No-value: invalid value " + value);
michael@0 987 }
michael@0 988 Octet.encode(data, 0);
michael@0 989 },
michael@0 990 };
michael@0 991
michael@0 992 /**
michael@0 993 * Text-value = No-value | Token-text | Quoted-string
michael@0 994 *
michael@0 995 * @see WAP-230-WSP-20010705-a clause 8.4.2.3
michael@0 996 */
michael@0 997 this.TextValue = {
michael@0 998 /**
michael@0 999 * @param data
michael@0 1000 * A wrapped object containing raw PDU data.
michael@0 1001 *
michael@0 1002 * @return Decoded string or null for No-value.
michael@0 1003 */
michael@0 1004 decode: function(data) {
michael@0 1005 return decodeAlternatives(data, null, NoValue, TokenText, QuotedString);
michael@0 1006 },
michael@0 1007
michael@0 1008 /**
michael@0 1009 * @param data
michael@0 1010 * A wrapped object to store encoded raw data.
michael@0 1011 * @param text
michael@0 1012 * A null or undefined or text string.
michael@0 1013 */
michael@0 1014 encode: function(data, text) {
michael@0 1015 encodeAlternatives(data, text, null, NoValue, TokenText, QuotedString);
michael@0 1016 },
michael@0 1017 };
michael@0 1018
michael@0 1019 /**
michael@0 1020 * Integer-Value = Short-integer | Long-integer
michael@0 1021 *
michael@0 1022 * @see WAP-230-WSP-20010705-a clause 8.4.2.3
michael@0 1023 */
michael@0 1024 this.IntegerValue = {
michael@0 1025 /**
michael@0 1026 * @param data
michael@0 1027 * A wrapped object containing raw PDU data.
michael@0 1028 *
michael@0 1029 * @return Decoded integer value or array of octets.
michael@0 1030 */
michael@0 1031 decode: function(data) {
michael@0 1032 return decodeAlternatives(data, null, ShortInteger, LongInteger);
michael@0 1033 },
michael@0 1034
michael@0 1035 /**
michael@0 1036 * @param data
michael@0 1037 * A wrapped object to store encoded raw data.
michael@0 1038 * @param value
michael@0 1039 * An integer value or an octet array of less-equal than 31 elements.
michael@0 1040 */
michael@0 1041 encode: function(data, value) {
michael@0 1042 if (typeof value === "number") {
michael@0 1043 encodeAlternatives(data, value, null, ShortInteger, LongInteger);
michael@0 1044 } else if (Array.isArray(value) || (value instanceof Uint8Array)) {
michael@0 1045 LongInteger.encode(data, value);
michael@0 1046 } else {
michael@0 1047 throw new CodeError("Integer-Value: invalid value type");
michael@0 1048 }
michael@0 1049 },
michael@0 1050 };
michael@0 1051
michael@0 1052 /**
michael@0 1053 * The encoding of dates shall be done in number of seconds from
michael@0 1054 * 1970-01-01, 00:00:00 GMT.
michael@0 1055 *
michael@0 1056 * Date-value = Long-integer
michael@0 1057 *
michael@0 1058 * @see WAP-230-WSP-20010705-a clause 8.4.2.3
michael@0 1059 */
michael@0 1060 this.DateValue = {
michael@0 1061 /**
michael@0 1062 * @param data
michael@0 1063 * A wrapped object containing raw PDU data.
michael@0 1064 *
michael@0 1065 * @return A Date object.
michael@0 1066 */
michael@0 1067 decode: function(data) {
michael@0 1068 let numOrArray = LongInteger.decode(data);
michael@0 1069 let seconds;
michael@0 1070 if (typeof numOrArray == "number") {
michael@0 1071 seconds = numOrArray;
michael@0 1072 } else {
michael@0 1073 seconds = 0;
michael@0 1074 for (let i = 0; i < numOrArray.length; i++) {
michael@0 1075 seconds = seconds * 256 + numOrArray[i];
michael@0 1076 }
michael@0 1077 }
michael@0 1078
michael@0 1079 return new Date(seconds * 1000);
michael@0 1080 },
michael@0 1081
michael@0 1082 /**
michael@0 1083 * @param data
michael@0 1084 * A wrapped object to store encoded raw data.
michael@0 1085 * @param date
michael@0 1086 * A Date object.
michael@0 1087 */
michael@0 1088 encode: function(data, date) {
michael@0 1089 let seconds = date.getTime() / 1000;
michael@0 1090 if (seconds < 0) {
michael@0 1091 throw new CodeError("Date-value: negative seconds " + seconds);
michael@0 1092 }
michael@0 1093
michael@0 1094 LongInteger.encode(data, seconds);
michael@0 1095 },
michael@0 1096 };
michael@0 1097
michael@0 1098 /**
michael@0 1099 * Delta-seconds-value = Integer-value
michael@0 1100 *
michael@0 1101 * @see WAP-230-WSP-20010705-a clause 8.4.2.3
michael@0 1102 */
michael@0 1103 this.DeltaSecondsValue = IntegerValue;
michael@0 1104
michael@0 1105 /**
michael@0 1106 * Quality factor 0 and quality factors with one or two decimal digits are
michael@0 1107 * encoded into 1-100; three digits ones into 101-1099.
michael@0 1108 *
michael@0 1109 * Q-value = 1*2 OCTET
michael@0 1110 *
michael@0 1111 * @see WAP-230-WSP-20010705-a clause 8.4.2.3
michael@0 1112 */
michael@0 1113 this.QValue = {
michael@0 1114 /**
michael@0 1115 * @param data
michael@0 1116 * A wrapped object containing raw PDU data.
michael@0 1117 *
michael@0 1118 * @return Decoded integer value of 1..1099.
michael@0 1119 *
michael@0 1120 * @throws CodeError if decoded UintVar is not in range 1..1099.
michael@0 1121 */
michael@0 1122 decode: function(data) {
michael@0 1123 let value = UintVar.decode(data);
michael@0 1124 if (value > 0) {
michael@0 1125 if (value <= 100) {
michael@0 1126 return (value - 1) / 100.0;
michael@0 1127 }
michael@0 1128 if (value <= 1099) {
michael@0 1129 return (value - 100) / 1000.0;
michael@0 1130 }
michael@0 1131 }
michael@0 1132
michael@0 1133 throw new CodeError("Q-value: invalid value " + value);
michael@0 1134 },
michael@0 1135
michael@0 1136 /**
michael@0 1137 * @param data
michael@0 1138 * A wrapped object to store encoded raw data.
michael@0 1139 * @param value
michael@0 1140 * An integer within the range 1..1099.
michael@0 1141 */
michael@0 1142 encode: function(data, value) {
michael@0 1143 if ((value < 0) || (value >= 1)) {
michael@0 1144 throw new CodeError("Q-value: invalid value " + value);
michael@0 1145 }
michael@0 1146
michael@0 1147 value *= 1000;
michael@0 1148 if ((value % 10) == 0) {
michael@0 1149 // Two digits only.
michael@0 1150 UintVar.encode(data, Math.floor(value / 10 + 1));
michael@0 1151 } else {
michael@0 1152 // Three digits.
michael@0 1153 UintVar.encode(data, Math.floor(value + 100));
michael@0 1154 }
michael@0 1155 },
michael@0 1156 };
michael@0 1157
michael@0 1158 /**
michael@0 1159 * The three most significant bits of the Short-integer value are interpreted
michael@0 1160 * to encode a major version number in the range 1-7, and the four least
michael@0 1161 * significant bits contain a minor version number in the range 0-14. If
michael@0 1162 * there is only a major version number, this is encoded by placing the value
michael@0 1163 * 15 in the four least significant bits.
michael@0 1164 *
michael@0 1165 * Version-value = Short-integer | Text-string
michael@0 1166 *
michael@0 1167 * @see WAP-230-WSP-20010705-a clause 8.4.2.3
michael@0 1168 */
michael@0 1169 this.VersionValue = {
michael@0 1170 /**
michael@0 1171 * @param data
michael@0 1172 * A wrapped object containing raw PDU data.
michael@0 1173 *
michael@0 1174 * @return Binary encoded version number.
michael@0 1175 */
michael@0 1176 decode: function(data) {
michael@0 1177 let begin = data.offset;
michael@0 1178 let value;
michael@0 1179 try {
michael@0 1180 value = ShortInteger.decode(data);
michael@0 1181 if ((value >= 0x10) && (value < 0x80)) {
michael@0 1182 return value;
michael@0 1183 }
michael@0 1184
michael@0 1185 throw new CodeError("Version-value: invalid value " + value);
michael@0 1186 } catch (e) {}
michael@0 1187
michael@0 1188 data.offset = begin;
michael@0 1189
michael@0 1190 let str = TextString.decode(data);
michael@0 1191 if (!str.match(/^[1-7](\.1?\d)?$/)) {
michael@0 1192 throw new CodeError("Version-value: invalid value " + str);
michael@0 1193 }
michael@0 1194
michael@0 1195 let major = str.charCodeAt(0) - 0x30;
michael@0 1196 let minor = 0x0F;
michael@0 1197 if (str.length > 1) {
michael@0 1198 minor = str.charCodeAt(2) - 0x30;
michael@0 1199 if (str.length > 3) {
michael@0 1200 minor = 10 + (str.charCodeAt(3) - 0x30);
michael@0 1201 if (minor > 14) {
michael@0 1202 throw new CodeError("Version-value: invalid minor " + minor);
michael@0 1203 }
michael@0 1204 }
michael@0 1205 }
michael@0 1206
michael@0 1207 return major << 4 | minor;
michael@0 1208 },
michael@0 1209
michael@0 1210 /**
michael@0 1211 * @param data
michael@0 1212 * A wrapped object to store encoded raw data.
michael@0 1213 * @param version
michael@0 1214 * A binary encoded version number.
michael@0 1215 */
michael@0 1216 encode: function(data, version) {
michael@0 1217 if ((version < 0x10) || (version >= 0x80)) {
michael@0 1218 throw new CodeError("Version-value: invalid version " + version);
michael@0 1219 }
michael@0 1220
michael@0 1221 ShortInteger.encode(data, version);
michael@0 1222 },
michael@0 1223 };
michael@0 1224
michael@0 1225 /**
michael@0 1226 * URI value should be encoded per [RFC2616], but service user may use a
michael@0 1227 * different format.
michael@0 1228 *
michael@0 1229 * Uri-value = Text-string
michael@0 1230 *
michael@0 1231 * @see WAP-230-WSP-20010705-a clause 8.4.2.3
michael@0 1232 * @see RFC 2616 clause 2.2 Basic Rules
michael@0 1233 */
michael@0 1234 this.UriValue = {
michael@0 1235 /**
michael@0 1236 * @param data
michael@0 1237 * A wrapped object containing raw PDU data.
michael@0 1238 *
michael@0 1239 * @return Decoded uri string.
michael@0 1240 */
michael@0 1241 decode: function(data) {
michael@0 1242 let str = "";
michael@0 1243 try {
michael@0 1244 // A End-of-string is also a CTL, which should cause a error.
michael@0 1245 while (true) {
michael@0 1246 str += URIC.decode(data);
michael@0 1247 }
michael@0 1248 } catch (e if e instanceof NullCharError) {
michael@0 1249 return str;
michael@0 1250 }
michael@0 1251 },
michael@0 1252 };
michael@0 1253
michael@0 1254 /**
michael@0 1255 * Internal coder for "type" parameter.
michael@0 1256 *
michael@0 1257 * Type-value = Constrained-encoding
michael@0 1258 *
michael@0 1259 * @see WAP-230-WSP-20010705-a table 38
michael@0 1260 */
michael@0 1261 this.TypeValue = {
michael@0 1262 /**
michael@0 1263 * @param data
michael@0 1264 * A wrapped object containing raw PDU data.
michael@0 1265 *
michael@0 1266 * @return Decoded content type string.
michael@0 1267 */
michael@0 1268 decode: function(data) {
michael@0 1269 let numOrStr = ConstrainedEncoding.decode(data);
michael@0 1270 if (typeof numOrStr == "string") {
michael@0 1271 return numOrStr.toLowerCase();
michael@0 1272 }
michael@0 1273
michael@0 1274 let number = numOrStr;
michael@0 1275 let entry = WSP_WELL_KNOWN_CONTENT_TYPES[number];
michael@0 1276 if (!entry) {
michael@0 1277 throw new NotWellKnownEncodingError(
michael@0 1278 "Constrained-media: not well known media " + number);
michael@0 1279 }
michael@0 1280
michael@0 1281 return entry.type;
michael@0 1282 },
michael@0 1283
michael@0 1284 /**
michael@0 1285 * @param data
michael@0 1286 * A wrapped object to store encoded raw data.
michael@0 1287 * @param type
michael@0 1288 * A content type string.
michael@0 1289 */
michael@0 1290 encode: function(data, type) {
michael@0 1291 let entry = WSP_WELL_KNOWN_CONTENT_TYPES[type.toLowerCase()];
michael@0 1292 if (entry) {
michael@0 1293 ConstrainedEncoding.encode(data, entry.number);
michael@0 1294 } else {
michael@0 1295 ConstrainedEncoding.encode(data, type);
michael@0 1296 }
michael@0 1297 },
michael@0 1298 };
michael@0 1299
michael@0 1300 /**
michael@0 1301 * Parameter = Typed-parameter | Untyped-parameter
michael@0 1302 *
michael@0 1303 * For Typed-parameters, the actual expected type of the value is implied by
michael@0 1304 * the well-known parameter. In addition to the expected type, there may be no
michael@0 1305 * value. If the value cannot be encoded using expected type, it shall be
michael@0 1306 * encoded as text.
michael@0 1307 *
michael@0 1308 * Typed-parameter = Well-known-parameter-token Typed-value
michael@0 1309 * Well-known-parameter-token = Integer-value
michael@0 1310 * Typed-value = Compact-value | Text-value
michael@0 1311 * Compact-value = Integer-value | Date-value | Delta-seconds-value | Q-value
michael@0 1312 * | Version-value | Uri-value
michael@0 1313 *
michael@0 1314 * For Untyped-parameters, the type of the value is unknown, but is shall be
michael@0 1315 * encoded as an integer, if that is possible.
michael@0 1316 *
michael@0 1317 * Untyped-parameter = Token-text Untyped-value
michael@0 1318 * Untyped-value = Integer-value | Text-value
michael@0 1319 *
michael@0 1320 * @see WAP-230-WSP-20010705-a clause 8.4.2.4
michael@0 1321 */
michael@0 1322 this.Parameter = {
michael@0 1323 /**
michael@0 1324 * @param data
michael@0 1325 * A wrapped object containing raw PDU data.
michael@0 1326 *
michael@0 1327 * @return A decoded object containing `name` and `value` properties or null
michael@0 1328 * if something wrong. The `name` property must be a string, but the
michael@0 1329 * `value` property can be many different types depending on `name`.
michael@0 1330 *
michael@0 1331 * @throws CodeError if decoded IntegerValue is an array.
michael@0 1332 * @throws NotWellKnownEncodingError if decoded well-known parameter number
michael@0 1333 * is not registered or supported.
michael@0 1334 */
michael@0 1335 decodeTypedParameter: function(data) {
michael@0 1336 let numOrArray = IntegerValue.decode(data);
michael@0 1337 // `decodeIntegerValue` can return a array, which doesn't apply here.
michael@0 1338 if (typeof numOrArray != "number") {
michael@0 1339 throw new CodeError("Typed-parameter: invalid integer type");
michael@0 1340 }
michael@0 1341
michael@0 1342 let number = numOrArray;
michael@0 1343 let param = WSP_WELL_KNOWN_PARAMS[number];
michael@0 1344 if (!param) {
michael@0 1345 throw new NotWellKnownEncodingError(
michael@0 1346 "Typed-parameter: not well known parameter " + number);
michael@0 1347 }
michael@0 1348
michael@0 1349 let begin = data.offset, value;
michael@0 1350 try {
michael@0 1351 // Althought Text-string is not included in BNF of Compact-value, but
michael@0 1352 // some service provider might still pass a less-strict text form and
michael@0 1353 // cause a unexpected CodeError raised. For example, the `start`
michael@0 1354 // parameter expects its value of Text-value, but service provider might
michael@0 1355 // gives "<smil>", which contains illegal characters "<" and ">".
michael@0 1356 value = decodeAlternatives(data, null,
michael@0 1357 param.coder, TextValue, TextString);
michael@0 1358 } catch (e) {
michael@0 1359 data.offset = begin;
michael@0 1360
michael@0 1361 // Skip current parameter.
michael@0 1362 value = skipValue(data);
michael@0 1363 debug("Skip malformed typed parameter: "
michael@0 1364 + JSON.stringify({name: param.name, value: value}));
michael@0 1365
michael@0 1366 return null;
michael@0 1367 }
michael@0 1368
michael@0 1369 return {
michael@0 1370 name: param.name,
michael@0 1371 value: value,
michael@0 1372 };
michael@0 1373 },
michael@0 1374
michael@0 1375 /**
michael@0 1376 * @param data
michael@0 1377 * A wrapped object containing raw PDU data.
michael@0 1378 *
michael@0 1379 * @return A decoded object containing `name` and `value` properties or null
michael@0 1380 * if something wrong. The `name` property must be a string, but the
michael@0 1381 * `value` property can be many different types depending on `name`.
michael@0 1382 */
michael@0 1383 decodeUntypedParameter: function(data) {
michael@0 1384 let name = TokenText.decode(data);
michael@0 1385
michael@0 1386 let begin = data.offset, value;
michael@0 1387 try {
michael@0 1388 value = decodeAlternatives(data, null, IntegerValue, TextValue);
michael@0 1389 } catch (e) {
michael@0 1390 data.offset = begin;
michael@0 1391
michael@0 1392 // Skip current parameter.
michael@0 1393 value = skipValue(data);
michael@0 1394 debug("Skip malformed untyped parameter: "
michael@0 1395 + JSON.stringify({name: name, value: value}));
michael@0 1396
michael@0 1397 return null;
michael@0 1398 }
michael@0 1399
michael@0 1400 return {
michael@0 1401 name: name.toLowerCase(),
michael@0 1402 value: value,
michael@0 1403 };
michael@0 1404 },
michael@0 1405
michael@0 1406 /**
michael@0 1407 * @param data
michael@0 1408 * A wrapped object containing raw PDU data.
michael@0 1409 *
michael@0 1410 * @return A decoded object containing `name` and `value` properties or null
michael@0 1411 * if something wrong. The `name` property must be a string, but the
michael@0 1412 * `value` property can be many different types depending on `name`.
michael@0 1413 */
michael@0 1414 decode: function(data) {
michael@0 1415 let begin = data.offset;
michael@0 1416 try {
michael@0 1417 return this.decodeTypedParameter(data);
michael@0 1418 } catch (e) {
michael@0 1419 data.offset = begin;
michael@0 1420 return this.decodeUntypedParameter(data);
michael@0 1421 }
michael@0 1422 },
michael@0 1423
michael@0 1424 /**
michael@0 1425 * @param data
michael@0 1426 * A wrapped object containing raw PDU data.
michael@0 1427 * @param end
michael@0 1428 * Ending offset of following parameters.
michael@0 1429 *
michael@0 1430 * @return An array of decoded objects.
michael@0 1431 */
michael@0 1432 decodeMultiple: function(data, end) {
michael@0 1433 let params = null, param;
michael@0 1434
michael@0 1435 while (data.offset < end) {
michael@0 1436 try {
michael@0 1437 param = this.decode(data);
michael@0 1438 } catch (e) {
michael@0 1439 break;
michael@0 1440 }
michael@0 1441 if (param) {
michael@0 1442 if (!params) {
michael@0 1443 params = {};
michael@0 1444 }
michael@0 1445 params[param.name] = param.value;
michael@0 1446 }
michael@0 1447 }
michael@0 1448
michael@0 1449 return params;
michael@0 1450 },
michael@0 1451
michael@0 1452 /**
michael@0 1453 * @param data
michael@0 1454 * A wrapped object to store encoded raw data.
michael@0 1455 * @param param
michael@0 1456 * An object containing `name` and `value` properties.
michael@0 1457 */
michael@0 1458 encodeTypedParameter: function(data, param) {
michael@0 1459 let entry = WSP_WELL_KNOWN_PARAMS[param.name.toLowerCase()];
michael@0 1460 if (!entry) {
michael@0 1461 throw new NotWellKnownEncodingError(
michael@0 1462 "Typed-parameter: not well known parameter " + param.name);
michael@0 1463 }
michael@0 1464
michael@0 1465 IntegerValue.encode(data, entry.number);
michael@0 1466 encodeAlternatives(data, param.value, null,
michael@0 1467 entry.coder, TextValue, TextString);
michael@0 1468 },
michael@0 1469
michael@0 1470 /**
michael@0 1471 * @param data
michael@0 1472 * A wrapped object to store encoded raw data.
michael@0 1473 * @param param
michael@0 1474 * An object containing `name` and `value` properties.
michael@0 1475 */
michael@0 1476 encodeUntypedParameter: function(data, param) {
michael@0 1477 TokenText.encode(data, param.name);
michael@0 1478 encodeAlternatives(data, param.value, null, IntegerValue, TextValue);
michael@0 1479 },
michael@0 1480
michael@0 1481 /**
michael@0 1482 * @param data
michael@0 1483 * A wrapped object to store encoded raw data.
michael@0 1484 * @param param
michael@0 1485 * An array of parameter objects.
michael@0 1486 */
michael@0 1487 encodeMultiple: function(data, params) {
michael@0 1488 for (let name in params) {
michael@0 1489 this.encode(data, {name: name, value: params[name]});
michael@0 1490 }
michael@0 1491 },
michael@0 1492
michael@0 1493 /**
michael@0 1494 * @param data
michael@0 1495 * A wrapped object to store encoded raw data.
michael@0 1496 * @param param
michael@0 1497 * An object containing `name` and `value` properties.
michael@0 1498 */
michael@0 1499 encode: function(data, param) {
michael@0 1500 let begin = data.offset;
michael@0 1501 try {
michael@0 1502 this.encodeTypedParameter(data, param);
michael@0 1503 } catch (e) {
michael@0 1504 data.offset = begin;
michael@0 1505 this.encodeUntypedParameter(data, param);
michael@0 1506 }
michael@0 1507 },
michael@0 1508 };
michael@0 1509
michael@0 1510 /**
michael@0 1511 * Header = Message-header | Shift-sequence
michael@0 1512 * Message-header = Well-known-header | Application-header
michael@0 1513 *
michael@0 1514 * @see WAP-230-WSP-20010705-a clause 8.4.2.6
michael@0 1515 */
michael@0 1516 this.Header = {
michael@0 1517 /**
michael@0 1518 * @param data
michael@0 1519 * A wrapped object containing raw PDU data.
michael@0 1520 *
michael@0 1521 * @return A decoded object containing `name` and `value` properties or null
michael@0 1522 * in case of a failed parsing. The `name` property must be a string,
michael@0 1523 * but the `value` property can be many different types depending on
michael@0 1524 * `name`.
michael@0 1525 */
michael@0 1526 decodeMessageHeader: function(data) {
michael@0 1527 return decodeAlternatives(data, null, WellKnownHeader, ApplicationHeader);
michael@0 1528 },
michael@0 1529
michael@0 1530 /**
michael@0 1531 * @param data
michael@0 1532 * A wrapped object containing raw PDU data.
michael@0 1533 *
michael@0 1534 * @return A decoded object containing `name` and `value` properties or null
michael@0 1535 * in case of a failed parsing. The `name` property must be a string,
michael@0 1536 * but the `value` property can be many different types depending on
michael@0 1537 * `name`.
michael@0 1538 */
michael@0 1539 decode: function(data) {
michael@0 1540 // TODO: support header code page shift-sequence
michael@0 1541 return this.decodeMessageHeader(data);
michael@0 1542 },
michael@0 1543
michael@0 1544 encodeMessageHeader: function(data, header) {
michael@0 1545 encodeAlternatives(data, header, null, WellKnownHeader, ApplicationHeader);
michael@0 1546 },
michael@0 1547
michael@0 1548 /**
michael@0 1549 * @param data
michael@0 1550 * A wrapped object to store encoded raw data.
michael@0 1551 * @param header
michael@0 1552 * An object containing two attributes: a string-typed `name` and a
michael@0 1553 * `value` of arbitrary type.
michael@0 1554 */
michael@0 1555 encode: function(data, header) {
michael@0 1556 // TODO: support header code page shift-sequence
michael@0 1557 this.encodeMessageHeader(data, header);
michael@0 1558 },
michael@0 1559 };
michael@0 1560
michael@0 1561 /**
michael@0 1562 * Well-known-header = Well-known-field-name Wap-value
michael@0 1563 * Well-known-field-name = Short-integer
michael@0 1564 *
michael@0 1565 * @see WAP-230-WSP-20010705-a clause 8.4.2.6
michael@0 1566 */
michael@0 1567 this.WellKnownHeader = {
michael@0 1568 /**
michael@0 1569 * @param data
michael@0 1570 * A wrapped object containing raw PDU data.
michael@0 1571 *
michael@0 1572 * @return A decoded object containing `name` and `value` properties or null
michael@0 1573 * in case of a failed parsing. The `name` property must be a string,
michael@0 1574 * but the `value` property can be many different types depending on
michael@0 1575 * `name`.
michael@0 1576 *
michael@0 1577 * @throws NotWellKnownEncodingError if decoded well-known header field
michael@0 1578 * number is not registered or supported.
michael@0 1579 */
michael@0 1580 decode: function(data) {
michael@0 1581 let index = ShortInteger.decode(data);
michael@0 1582
michael@0 1583 let entry = WSP_HEADER_FIELDS[index];
michael@0 1584 if (!entry) {
michael@0 1585 throw new NotWellKnownEncodingError(
michael@0 1586 "Well-known-header: not well known header " + index);
michael@0 1587 }
michael@0 1588
michael@0 1589 let begin = data.offset, value;
michael@0 1590 try {
michael@0 1591 value = decodeAlternatives(data, null, entry.coder, TextValue);
michael@0 1592 } catch (e) {
michael@0 1593 data.offset = begin;
michael@0 1594
michael@0 1595 value = skipValue(data);
michael@0 1596 debug("Skip malformed well known header(" + index + "): "
michael@0 1597 + JSON.stringify({name: entry.name, value: value}));
michael@0 1598
michael@0 1599 return null;
michael@0 1600 }
michael@0 1601
michael@0 1602 return {
michael@0 1603 name: entry.name,
michael@0 1604 value: value,
michael@0 1605 };
michael@0 1606 },
michael@0 1607
michael@0 1608 /**
michael@0 1609 * @param data
michael@0 1610 * A wrapped object to store encoded raw data.
michael@0 1611 * @param header
michael@0 1612 * An object containing two attributes: a string-typed `name` and a
michael@0 1613 * `value` of arbitrary type.
michael@0 1614 */
michael@0 1615 encode: function(data, header) {
michael@0 1616 let entry = WSP_HEADER_FIELDS[header.name.toLowerCase()];
michael@0 1617 if (!entry) {
michael@0 1618 throw new NotWellKnownEncodingError(
michael@0 1619 "Well-known-header: not well known header " + header.name);
michael@0 1620 }
michael@0 1621
michael@0 1622 ShortInteger.encode(data, entry.number);
michael@0 1623 encodeAlternatives(data, header.value, null, entry.coder, TextValue);
michael@0 1624 },
michael@0 1625 };
michael@0 1626
michael@0 1627 /**
michael@0 1628 * Application-header = Token-text Application-specific-value
michael@0 1629 * Application-specific-value = Text-string
michael@0 1630 *
michael@0 1631 * @see WAP-230-WSP-20010705-a clause 8.4.2.6
michael@0 1632 */
michael@0 1633 this.ApplicationHeader = {
michael@0 1634 /**
michael@0 1635 * @param data
michael@0 1636 * A wrapped object containing raw PDU data.
michael@0 1637 *
michael@0 1638 * @return A decoded object containing `name` and `value` properties or null
michael@0 1639 * in case of a failed parsing. The `name` property must be a string,
michael@0 1640 * but the `value` property can be many different types depending on
michael@0 1641 * `name`.
michael@0 1642 */
michael@0 1643 decode: function(data) {
michael@0 1644 let name = TokenText.decode(data);
michael@0 1645
michael@0 1646 let begin = data.offset, value;
michael@0 1647 try {
michael@0 1648 value = TextString.decode(data);
michael@0 1649 } catch (e) {
michael@0 1650 data.offset = begin;
michael@0 1651
michael@0 1652 value = skipValue(data);
michael@0 1653 debug("Skip malformed application header: "
michael@0 1654 + JSON.stringify({name: name, value: value}));
michael@0 1655
michael@0 1656 return null;
michael@0 1657 }
michael@0 1658
michael@0 1659 return {
michael@0 1660 name: name.toLowerCase(),
michael@0 1661 value: value,
michael@0 1662 };
michael@0 1663 },
michael@0 1664
michael@0 1665 /**
michael@0 1666 * @param data
michael@0 1667 * A wrapped object to store encoded raw data.
michael@0 1668 * @param header
michael@0 1669 * An object containing two attributes: a string-typed `name` and a
michael@0 1670 * `value` of arbitrary type.
michael@0 1671 *
michael@0 1672 * @throws CodeError if got an empty header name.
michael@0 1673 */
michael@0 1674 encode: function(data, header) {
michael@0 1675 if (!header.name) {
michael@0 1676 throw new CodeError("Application-header: empty header name");
michael@0 1677 }
michael@0 1678
michael@0 1679 TokenText.encode(data, header.name);
michael@0 1680 TextString.encode(data, header.value);
michael@0 1681 },
michael@0 1682 };
michael@0 1683
michael@0 1684 /**
michael@0 1685 * Field-name = Token-text | Well-known-field-name
michael@0 1686 * Well-known-field-name = Short-integer
michael@0 1687 *
michael@0 1688 * @see WAP-230-WSP-20010705-a clause 8.4.2.6
michael@0 1689 */
michael@0 1690 this.FieldName = {
michael@0 1691 /**
michael@0 1692 * @param data
michael@0 1693 * A wrapped object containing raw PDU data.
michael@0 1694 *
michael@0 1695 * @return A field name string.
michael@0 1696 *
michael@0 1697 * @throws NotWellKnownEncodingError if decoded well-known header field
michael@0 1698 * number is not registered or supported.
michael@0 1699 */
michael@0 1700 decode: function(data) {
michael@0 1701 let begin = data.offset;
michael@0 1702 try {
michael@0 1703 return TokenText.decode(data).toLowerCase();
michael@0 1704 } catch (e) {}
michael@0 1705
michael@0 1706 data.offset = begin;
michael@0 1707
michael@0 1708 let number = ShortInteger.decode(data);
michael@0 1709 let entry = WSP_HEADER_FIELDS[number];
michael@0 1710 if (!entry) {
michael@0 1711 throw new NotWellKnownEncodingError(
michael@0 1712 "Field-name: not well known encoding " + number);
michael@0 1713 }
michael@0 1714
michael@0 1715 return entry.name;
michael@0 1716 },
michael@0 1717
michael@0 1718 /**
michael@0 1719 * @param data
michael@0 1720 * A wrapped object to store encoded raw data.
michael@0 1721 * @param name
michael@0 1722 * A field name string.
michael@0 1723 */
michael@0 1724 encode: function(data, name) {
michael@0 1725 let entry = WSP_HEADER_FIELDS[name.toLowerCase()];
michael@0 1726 if (entry) {
michael@0 1727 ShortInteger.encode(data, entry.number);
michael@0 1728 } else {
michael@0 1729 TokenText.encode(data, name);
michael@0 1730 }
michael@0 1731 },
michael@0 1732 };
michael@0 1733
michael@0 1734 /**
michael@0 1735 * Accept-charset-value = Constrained-charset | Accept-charset-general-form
michael@0 1736 * Constrained-charset = Any-charset | Constrained-encoding
michael@0 1737 * Any-charset = <Octet 128>
michael@0 1738 * Accept-charset-general-form = Value-length (Well-known-charset | Token-text) [Q-value]
michael@0 1739 *
michael@0 1740 * @see WAP-230-WSP-20010705-a clause 8.4.2.8
michael@0 1741 */
michael@0 1742 this.AcceptCharsetValue = {
michael@0 1743 /**
michael@0 1744 * @param data
michael@0 1745 * A wrapped object containing raw PDU data.
michael@0 1746 *
michael@0 1747 * @return A object with a property `charset` of string "*".
michael@0 1748 */
michael@0 1749 decodeAnyCharset: function(data) {
michael@0 1750 Octet.decodeEqualTo(data, 128);
michael@0 1751 return {charset: "*"};
michael@0 1752 },
michael@0 1753
michael@0 1754 /**
michael@0 1755 * @param data
michael@0 1756 * A wrapped object containing raw PDU data.
michael@0 1757 *
michael@0 1758 * @return A object with a string property `charset` and a optional integer
michael@0 1759 * property `q`.
michael@0 1760 *
michael@0 1761 * @throws NotWellKnownEncodingError if decoded well-known charset number is
michael@0 1762 * not registered or supported.
michael@0 1763 */
michael@0 1764 decodeConstrainedCharset: function(data) {
michael@0 1765 let begin = data.offset;
michael@0 1766 try {
michael@0 1767 return this.decodeAnyCharset(data);
michael@0 1768 } catch (e) {}
michael@0 1769
michael@0 1770 data.offset = begin;
michael@0 1771
michael@0 1772 let numOrStr = ConstrainedEncoding.decode(data);
michael@0 1773 if (typeof numOrStr == "string") {
michael@0 1774 return {charset: numOrStr};
michael@0 1775 }
michael@0 1776
michael@0 1777 let charset = numOrStr;
michael@0 1778 let entry = WSP_WELL_KNOWN_CHARSETS[charset];
michael@0 1779 if (!entry) {
michael@0 1780 throw new NotWellKnownEncodingError(
michael@0 1781 "Constrained-charset: not well known charset: " + charset);
michael@0 1782 }
michael@0 1783
michael@0 1784 return {charset: entry.name};
michael@0 1785 },
michael@0 1786
michael@0 1787 /**
michael@0 1788 * @param data
michael@0 1789 * A wrapped object containing raw PDU data.
michael@0 1790 *
michael@0 1791 * @return A object with a string property `charset` and a optional integer
michael@0 1792 * property `q`.
michael@0 1793 */
michael@0 1794 decodeAcceptCharsetGeneralForm: function(data) {
michael@0 1795 let length = ValueLength.decode(data);
michael@0 1796
michael@0 1797 let begin = data.offset;
michael@0 1798 let end = begin + length;
michael@0 1799
michael@0 1800 let result;
michael@0 1801 try {
michael@0 1802 result = WellKnownCharset.decode(data);
michael@0 1803 } catch (e) {
michael@0 1804 data.offset = begin;
michael@0 1805
michael@0 1806 result = {charset: TokenText.decode(data)};
michael@0 1807 if (data.offset < end) {
michael@0 1808 result.q = QValue.decode(data);
michael@0 1809 }
michael@0 1810 }
michael@0 1811
michael@0 1812 if (data.offset != end) {
michael@0 1813 data.offset = end;
michael@0 1814 }
michael@0 1815
michael@0 1816 return result;
michael@0 1817 },
michael@0 1818
michael@0 1819 /**
michael@0 1820 * @param data
michael@0 1821 * A wrapped object containing raw PDU data.
michael@0 1822 *
michael@0 1823 * @return A object with a string property `charset` and a optional integer
michael@0 1824 * property `q`.
michael@0 1825 */
michael@0 1826 decode: function(data) {
michael@0 1827 let begin = data.offset;
michael@0 1828 try {
michael@0 1829 return this.decodeConstrainedCharset(data);
michael@0 1830 } catch (e) {
michael@0 1831 data.offset = begin;
michael@0 1832 return this.decodeAcceptCharsetGeneralForm(data);
michael@0 1833 }
michael@0 1834 },
michael@0 1835
michael@0 1836 /**
michael@0 1837 * @param data
michael@0 1838 * A wrapped object to store encoded raw data.
michael@0 1839 * @param value
michael@0 1840 * An object with a string property `charset`.
michael@0 1841 */
michael@0 1842 encodeAnyCharset: function(data, value) {
michael@0 1843 if (!value || !value.charset || (value.charset === "*")) {
michael@0 1844 Octet.encode(data, 128);
michael@0 1845 return;
michael@0 1846 }
michael@0 1847
michael@0 1848 throw new CodeError("Any-charset: invalid value " + value);
michael@0 1849 },
michael@0 1850 };
michael@0 1851
michael@0 1852 /**
michael@0 1853 * Well-known-charset = Any-charset | Integer-value
michael@0 1854 *
michael@0 1855 * @see WAP-230-WSP-20010705-a clause 8.4.2.8
michael@0 1856 */
michael@0 1857 this.WellKnownCharset = {
michael@0 1858 /**
michael@0 1859 * @param data
michael@0 1860 * A wrapped object containing raw PDU data.
michael@0 1861 *
michael@0 1862 * @return A object with a string property `charset`.
michael@0 1863 *
michael@0 1864 * @throws CodeError if decoded charset number is an array.
michael@0 1865 * @throws NotWellKnownEncodingError if decoded well-known charset number
michael@0 1866 * is not registered or supported.
michael@0 1867 */
michael@0 1868 decode: function(data) {
michael@0 1869 let begin = data.offset;
michael@0 1870
michael@0 1871 try {
michael@0 1872 return AcceptCharsetValue.decodeAnyCharset(data);
michael@0 1873 } catch (e) {}
michael@0 1874
michael@0 1875 data.offset = begin;
michael@0 1876
michael@0 1877 // `IntegerValue.decode` can return a array, which doesn't apply here.
michael@0 1878 let numOrArray = IntegerValue.decode(data);
michael@0 1879 if (typeof numOrArray != "number") {
michael@0 1880 throw new CodeError("Well-known-charset: invalid integer type");
michael@0 1881 }
michael@0 1882
michael@0 1883 let charset = numOrArray;
michael@0 1884 let entry = WSP_WELL_KNOWN_CHARSETS[charset];
michael@0 1885 if (!entry) {
michael@0 1886 throw new NotWellKnownEncodingError(
michael@0 1887 "Well-known-charset: not well known charset " + charset);
michael@0 1888 }
michael@0 1889
michael@0 1890 return {charset: entry.name};
michael@0 1891 },
michael@0 1892
michael@0 1893 /**
michael@0 1894 * @param data
michael@0 1895 * A wrapped object to store encoded raw data.
michael@0 1896 * @param value
michael@0 1897 */
michael@0 1898 encode: function(data, value) {
michael@0 1899 let begin = data.offset;
michael@0 1900 try {
michael@0 1901 AcceptCharsetValue.encodeAnyCharset(data, value);
michael@0 1902 return;
michael@0 1903 } catch (e) {}
michael@0 1904
michael@0 1905 data.offset = begin;
michael@0 1906 let entry = WSP_WELL_KNOWN_CHARSETS[value.charset.toLowerCase()];
michael@0 1907 if (!entry) {
michael@0 1908 throw new NotWellKnownEncodingError(
michael@0 1909 "Well-known-charset: not well known charset " + value.charset);
michael@0 1910 }
michael@0 1911
michael@0 1912 IntegerValue.encode(data, entry.number);
michael@0 1913 },
michael@0 1914 };
michael@0 1915
michael@0 1916 /**
michael@0 1917 * The short form of the Content-type-value MUST only be used when the
michael@0 1918 * well-known media is in the range of 0-127 or a text string. In all other
michael@0 1919 * cases the general form MUST be used.
michael@0 1920 *
michael@0 1921 * Content-type-value = Constrained-media | Content-general-form
michael@0 1922 * Constrained-media = Constrained-encoding
michael@0 1923 * Content-general-form = Value-length Media-type
michael@0 1924 * Media-type = Media *(Parameter)
michael@0 1925 * Media = Well-known-media | Extension-Media
michael@0 1926 * Well-known-media = Integer-value
michael@0 1927 * Extension-Media = *TEXT End-of-string
michael@0 1928 *
michael@0 1929 * @see WAP-230-WSP-20010705-a clause 8.4.2.24
michael@0 1930 */
michael@0 1931 this.ContentTypeValue = {
michael@0 1932 /**
michael@0 1933 * @param data
michael@0 1934 * A wrapped object containing raw PDU data.
michael@0 1935 *
michael@0 1936 * @return A decoded object containing `media` and `params` properties or
michael@0 1937 * null in case of a failed parsing. The `media` property must be a
michael@0 1938 * string, and the `params` property is always null.
michael@0 1939 *
michael@0 1940 * @throws NotWellKnownEncodingError if decoded well-known content type number
michael@0 1941 * is not registered or supported.
michael@0 1942 */
michael@0 1943 decodeConstrainedMedia: function(data) {
michael@0 1944 return {
michael@0 1945 media: TypeValue.decode(data),
michael@0 1946 params: null,
michael@0 1947 };
michael@0 1948 },
michael@0 1949
michael@0 1950 /**
michael@0 1951 * @param data
michael@0 1952 * A wrapped object containing raw PDU data.
michael@0 1953 *
michael@0 1954 * @return Decode string.
michael@0 1955 *
michael@0 1956 * @throws CodeError if decoded content type number is an array.
michael@0 1957 * @throws NotWellKnownEncodingError if decoded well-known content type
michael@0 1958 * number is not registered or supported.
michael@0 1959 */
michael@0 1960 decodeMedia: function(data) {
michael@0 1961 let begin = data.offset, number;
michael@0 1962 try {
michael@0 1963 number = IntegerValue.decode(data);
michael@0 1964 } catch (e) {
michael@0 1965 data.offset = begin;
michael@0 1966 return NullTerminatedTexts.decode(data).toLowerCase();
michael@0 1967 }
michael@0 1968
michael@0 1969 // `decodeIntegerValue` can return a array, which doesn't apply here.
michael@0 1970 if (typeof number != "number") {
michael@0 1971 throw new CodeError("Media: invalid integer type");
michael@0 1972 }
michael@0 1973
michael@0 1974 let entry = WSP_WELL_KNOWN_CONTENT_TYPES[number];
michael@0 1975 if (!entry) {
michael@0 1976 throw new NotWellKnownEncodingError("Media: not well known media " + number);
michael@0 1977 }
michael@0 1978
michael@0 1979 return entry.type;
michael@0 1980 },
michael@0 1981
michael@0 1982 /**
michael@0 1983 * @param data
michael@0 1984 * A wrapped object containing raw PDU data.
michael@0 1985 * @param end
michael@0 1986 * Ending offset of the Media-type value.
michael@0 1987 *
michael@0 1988 * @return A decoded object containing `media` and `params` properties or
michael@0 1989 * null in case of a failed parsing. The `media` property must be a
michael@0 1990 * string, and the `params` property is a hash map from a string to
michael@0 1991 * an value of unspecified type.
michael@0 1992 */
michael@0 1993 decodeMediaType: function(data, end) {
michael@0 1994 let media = this.decodeMedia(data);
michael@0 1995 let params = Parameter.decodeMultiple(data, end);
michael@0 1996
michael@0 1997 return {
michael@0 1998 media: media,
michael@0 1999 params: params,
michael@0 2000 };
michael@0 2001 },
michael@0 2002
michael@0 2003 /**
michael@0 2004 * @param data
michael@0 2005 * A wrapped object containing raw PDU data.
michael@0 2006 *
michael@0 2007 * @return A decoded object containing `media` and `params` properties or
michael@0 2008 * null in case of a failed parsing. The `media` property must be a
michael@0 2009 * string, and the `params` property is null or a hash map from a
michael@0 2010 * string to an value of unspecified type.
michael@0 2011 */
michael@0 2012 decodeContentGeneralForm: function(data) {
michael@0 2013 let length = ValueLength.decode(data);
michael@0 2014 let end = data.offset + length;
michael@0 2015
michael@0 2016 let value = this.decodeMediaType(data, end);
michael@0 2017
michael@0 2018 if (data.offset != end) {
michael@0 2019 data.offset = end;
michael@0 2020 }
michael@0 2021
michael@0 2022 return value;
michael@0 2023 },
michael@0 2024
michael@0 2025 /**
michael@0 2026 * @param data
michael@0 2027 * A wrapped object containing raw PDU data.
michael@0 2028 *
michael@0 2029 * @return A decoded object containing `media` and `params` properties or
michael@0 2030 * null in case of a failed parsing. The `media` property must be a
michael@0 2031 * string, and the `params` property is null or a hash map from a
michael@0 2032 * string to an value of unspecified type.
michael@0 2033 */
michael@0 2034 decode: function(data) {
michael@0 2035 let begin = data.offset;
michael@0 2036
michael@0 2037 try {
michael@0 2038 return this.decodeConstrainedMedia(data);
michael@0 2039 } catch (e) {
michael@0 2040 data.offset = begin;
michael@0 2041 return this.decodeContentGeneralForm(data);
michael@0 2042 }
michael@0 2043 },
michael@0 2044
michael@0 2045 /**
michael@0 2046 * @param data
michael@0 2047 * A wrapped object to store encoded raw data.
michael@0 2048 * @param value
michael@0 2049 * An object containing `media` and `params` properties.
michael@0 2050 */
michael@0 2051 encodeConstrainedMedia: function(data, value) {
michael@0 2052 if (value.params) {
michael@0 2053 throw new CodeError("Constrained-media: should use general form instead");
michael@0 2054 }
michael@0 2055
michael@0 2056 TypeValue.encode(data, value.media);
michael@0 2057 },
michael@0 2058
michael@0 2059 /**
michael@0 2060 * @param data
michael@0 2061 * A wrapped object to store encoded raw data.
michael@0 2062 * @param value
michael@0 2063 * An object containing `media` and `params` properties.
michael@0 2064 */
michael@0 2065 encodeMediaType: function(data, value) {
michael@0 2066 let entry = WSP_WELL_KNOWN_CONTENT_TYPES[value.media.toLowerCase()];
michael@0 2067 if (entry) {
michael@0 2068 IntegerValue.encode(data, entry.number);
michael@0 2069 } else {
michael@0 2070 NullTerminatedTexts.encode(data, value.media);
michael@0 2071 }
michael@0 2072
michael@0 2073 Parameter.encodeMultiple(data, value.params);
michael@0 2074 },
michael@0 2075
michael@0 2076 /**
michael@0 2077 * @param data
michael@0 2078 * A wrapped object to store encoded raw data.
michael@0 2079 * @param value
michael@0 2080 * An object containing `media` and `params` properties.
michael@0 2081 */
michael@0 2082 encodeContentGeneralForm: function(data, value) {
michael@0 2083 let begin = data.offset;
michael@0 2084 this.encodeMediaType(data, value);
michael@0 2085
michael@0 2086 // Calculate how much octets will be written and seek back.
michael@0 2087 // TODO: use memmove, see bug 730873
michael@0 2088 let len = data.offset - begin;
michael@0 2089 data.offset = begin;
michael@0 2090
michael@0 2091 ValueLength.encode(data, len);
michael@0 2092 this.encodeMediaType(data, value);
michael@0 2093 },
michael@0 2094
michael@0 2095 /**
michael@0 2096 * @param data
michael@0 2097 * A wrapped object to store encoded raw data.
michael@0 2098 * @param value
michael@0 2099 * An object containing `media` and `params` properties.
michael@0 2100 */
michael@0 2101 encode: function(data, value) {
michael@0 2102 let begin = data.offset;
michael@0 2103
michael@0 2104 try {
michael@0 2105 this.encodeConstrainedMedia(data, value);
michael@0 2106 } catch (e) {
michael@0 2107 data.offset = begin;
michael@0 2108 this.encodeContentGeneralForm(data, value);
michael@0 2109 }
michael@0 2110 },
michael@0 2111 };
michael@0 2112
michael@0 2113 /**
michael@0 2114 * Application-id-value = Uri-value | App-assigned-code
michael@0 2115 * App-assigned-code = Integer-value
michael@0 2116 *
michael@0 2117 * @see WAP-230-WSP-20010705-a clause 8.4.2.54
michael@0 2118 */
michael@0 2119 this.ApplicationIdValue = {
michael@0 2120 /**
michael@0 2121 * @param data
michael@0 2122 * A wrapped object containing raw PDU data.
michael@0 2123 *
michael@0 2124 * @return Decoded string value.
michael@0 2125 *
michael@0 2126 * @throws CodeError if decoded application id number is an array.
michael@0 2127 * @throws NotWellKnownEncodingError if decoded well-known application id
michael@0 2128 * number is not registered or supported.
michael@0 2129 */
michael@0 2130 decode: function(data) {
michael@0 2131 let begin = data.offset;
michael@0 2132 try {
michael@0 2133 return UriValue.decode(data);
michael@0 2134 } catch (e) {}
michael@0 2135
michael@0 2136 data.offset = begin;
michael@0 2137
michael@0 2138 // `decodeIntegerValue` can return a array, which doesn't apply here.
michael@0 2139 let numOrArray = IntegerValue.decode(data);
michael@0 2140 if (typeof numOrArray != "number") {
michael@0 2141 throw new CodeError("Application-id-value: invalid integer type");
michael@0 2142 }
michael@0 2143
michael@0 2144 let id = numOrArray;
michael@0 2145 let entry = OMNA_PUSH_APPLICATION_IDS[id];
michael@0 2146 if (!entry) {
michael@0 2147 throw new NotWellKnownEncodingError(
michael@0 2148 "Application-id-value: not well known id: " + id);
michael@0 2149 }
michael@0 2150
michael@0 2151 return entry.urn;
michael@0 2152 },
michael@0 2153 };
michael@0 2154
michael@0 2155 this.PduHelper = {
michael@0 2156 /**
michael@0 2157 * @param data
michael@0 2158 * A UInt8Array of data for decode.
michael@0 2159 * @param charset
michael@0 2160 * charset for decode
michael@0 2161 *
michael@0 2162 * @return Decoded string.
michael@0 2163 */
michael@0 2164 decodeStringContent: function(data, charset) {
michael@0 2165 let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
michael@0 2166 .createInstance(Ci.nsIScriptableUnicodeConverter);
michael@0 2167
michael@0 2168 let entry;
michael@0 2169 if (charset) {
michael@0 2170 entry = WSP_WELL_KNOWN_CHARSETS[charset];
michael@0 2171 }
michael@0 2172 // Set converter to default one if (entry && entry.converter) is null.
michael@0 2173 // @see OMA-TS-MMS-CONF-V1_3-20050526-D 7.1.9
michael@0 2174 conv.charset = (entry && entry.converter) || "UTF-8";
michael@0 2175 try {
michael@0 2176 return conv.convertFromByteArray(data, data.length);
michael@0 2177 } catch (e) {
michael@0 2178 }
michael@0 2179 return null;
michael@0 2180 },
michael@0 2181
michael@0 2182 /**
michael@0 2183 * @param strContent
michael@0 2184 * Decoded string content.
michael@0 2185 * @param charset
michael@0 2186 * Charset for encode.
michael@0 2187 *
michael@0 2188 * @return An encoded UInt8Array of string content.
michael@0 2189 */
michael@0 2190 encodeStringContent: function(strContent, charset) {
michael@0 2191 let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
michael@0 2192 .createInstance(Ci.nsIScriptableUnicodeConverter);
michael@0 2193
michael@0 2194 let entry;
michael@0 2195 if (charset) {
michael@0 2196 entry = WSP_WELL_KNOWN_CHARSETS[charset];
michael@0 2197 }
michael@0 2198 // Set converter to default one if (entry && entry.converter) is null.
michael@0 2199 // @see OMA-TS-MMS-CONF-V1_3-20050526-D 7.1.9
michael@0 2200 conv.charset = (entry && entry.converter) || "UTF-8";
michael@0 2201 try {
michael@0 2202 return conv.convertToByteArray(strContent);
michael@0 2203 } catch (e) {
michael@0 2204 }
michael@0 2205 return null;
michael@0 2206 },
michael@0 2207
michael@0 2208 /**
michael@0 2209 * Parse multiple header fields with end mark.
michael@0 2210 *
michael@0 2211 * @param data
michael@0 2212 * A wrapped object containing raw PDU data.
michael@0 2213 * @param end
michael@0 2214 * An ending offset indicating the end of headers.
michael@0 2215 * @param headers [optional]
michael@0 2216 * An optional object to store parsed header fields. Created
michael@0 2217 * automatically if undefined.
michael@0 2218 *
michael@0 2219 * @return A object containing decoded header fields as its attributes.
michael@0 2220 */
michael@0 2221 parseHeaders: function(data, end, headers) {
michael@0 2222 if (!headers) {
michael@0 2223 headers = {};
michael@0 2224 }
michael@0 2225
michael@0 2226 let header;
michael@0 2227 while (data.offset < end) {
michael@0 2228 try {
michael@0 2229 header = Header.decode(data);
michael@0 2230 } catch (e) {
michael@0 2231 break;
michael@0 2232 }
michael@0 2233 if (header) {
michael@0 2234 headers[header.name] = header.value;
michael@0 2235 }
michael@0 2236 }
michael@0 2237
michael@0 2238 if (data.offset != end) {
michael@0 2239 debug("Parser expects ending in " + end + ", but in " + data.offset);
michael@0 2240 // Explicitly seek to end in case of skipped header fields.
michael@0 2241 data.offset = end;
michael@0 2242 }
michael@0 2243
michael@0 2244 return headers;
michael@0 2245 },
michael@0 2246
michael@0 2247 /**
michael@0 2248 * @param data
michael@0 2249 * A wrapped object containing raw PDU data.
michael@0 2250 * @param msg
michael@0 2251 * Message object to be populated with decoded header fields.
michael@0 2252 *
michael@0 2253 * @see WAP-230-WSP-20010705-a clause 8.2.4
michael@0 2254 */
michael@0 2255 parsePushHeaders: function(data, msg) {
michael@0 2256 if (!msg.headers) {
michael@0 2257 msg.headers = {};
michael@0 2258 }
michael@0 2259
michael@0 2260 let headersLen = UintVar.decode(data);
michael@0 2261 let headersEnd = data.offset + headersLen;
michael@0 2262
michael@0 2263 let contentType = ContentTypeValue.decode(data);
michael@0 2264 msg.headers["content-type"] = contentType;
michael@0 2265
michael@0 2266 msg.headers = this.parseHeaders(data, headersEnd, msg.headers);
michael@0 2267 },
michael@0 2268
michael@0 2269 /**
michael@0 2270 * @param data
michael@0 2271 * A wrapped object containing raw PDU data.
michael@0 2272 *
michael@0 2273 * @return An array of objects representing multipart entries or null in case
michael@0 2274 * of errors found.
michael@0 2275 *
michael@0 2276 * @see WAP-230-WSP-20010705-a section 8.5
michael@0 2277 */
michael@0 2278 parseMultiPart: function(data) {
michael@0 2279 let nEntries = UintVar.decode(data);
michael@0 2280 if (!nEntries) {
michael@0 2281 return null;
michael@0 2282 }
michael@0 2283
michael@0 2284 let parts = new Array(nEntries);
michael@0 2285 for (let i = 0; i < nEntries; i++) {
michael@0 2286 // Length of the ContentType and Headers fields combined.
michael@0 2287 let headersLen = UintVar.decode(data);
michael@0 2288 // Length of the Data field
michael@0 2289 let contentLen = UintVar.decode(data);
michael@0 2290
michael@0 2291 let headersEnd = data.offset + headersLen;
michael@0 2292 let contentEnd = headersEnd + contentLen;
michael@0 2293
michael@0 2294 try {
michael@0 2295 let headers = {};
michael@0 2296
michael@0 2297 let contentType = ContentTypeValue.decode(data);
michael@0 2298 headers["content-type"] = contentType;
michael@0 2299 headers["content-length"] = contentLen;
michael@0 2300
michael@0 2301 headers = this.parseHeaders(data, headersEnd, headers);
michael@0 2302
michael@0 2303 let octetArray = Octet.decodeMultiple(data, contentEnd);
michael@0 2304 let content = null;
michael@0 2305 let charset = headers["content-type"].params &&
michael@0 2306 headers["content-type"].params.charset
michael@0 2307 ? headers["content-type"].params.charset.charset
michael@0 2308 : null;
michael@0 2309
michael@0 2310 let mimeType = headers["content-type"].media;
michael@0 2311
michael@0 2312 if (mimeType) {
michael@0 2313 if (mimeType == "application/smil") {
michael@0 2314 // If the content is a SMIL type, convert it to a string.
michael@0 2315 // We hope to save and expose the SMIL content in a string way.
michael@0 2316 content = this.decodeStringContent(octetArray, charset);
michael@0 2317 } else if (mimeType.indexOf("text/") == 0 && charset != "utf-8") {
michael@0 2318 // If the content is a "text/plain" type, we have to make sure
michael@0 2319 // the encoding of the blob content should always be "utf-8".
michael@0 2320 let tmpStr = this.decodeStringContent(octetArray, charset);
michael@0 2321 let encoder = new TextEncoder("UTF-8");
michael@0 2322 content = new Blob([encoder.encode(tmpStr)], {type : mimeType});
michael@0 2323
michael@0 2324 // Make up the missing encoding info.
michael@0 2325 if (!headers["content-type"].params) {
michael@0 2326 headers["content-type"].params = {};
michael@0 2327 }
michael@0 2328 if (!headers["content-type"].params.charset) {
michael@0 2329 headers["content-type"].params.charset = {};
michael@0 2330 }
michael@0 2331 headers["content-type"].params.charset.charset = "utf-8";
michael@0 2332 }
michael@0 2333 }
michael@0 2334
michael@0 2335 if (!content) {
michael@0 2336 content = new Blob([octetArray], {type : mimeType});
michael@0 2337 }
michael@0 2338
michael@0 2339 parts[i] = {
michael@0 2340 index: i,
michael@0 2341 headers: headers,
michael@0 2342 content: content,
michael@0 2343 };
michael@0 2344 } catch (e) {
michael@0 2345 debug("Failed to parse multipart entry, message: " + e.message);
michael@0 2346 // Placeholder to keep original index of following entries.
michael@0 2347 parts[i] = null;
michael@0 2348 }
michael@0 2349
michael@0 2350 if (data.offset != contentEnd) {
michael@0 2351 // Seek to entry boundary for next entry.
michael@0 2352 data.offset = contentEnd;
michael@0 2353 }
michael@0 2354 }
michael@0 2355
michael@0 2356 return parts;
michael@0 2357 },
michael@0 2358
michael@0 2359 /**
michael@0 2360 * @param data
michael@0 2361 * A wrapped object containing raw PDU data.
michael@0 2362 * @param isSessionless
michael@0 2363 * Whether or not the PDU contains a session less WSP PDU.
michael@0 2364 * @param msg [optional]
michael@0 2365 * Optional pre-defined PDU object.
michael@0 2366 *
michael@0 2367 * @return Parsed WSP PDU object or null in case of errors found.
michael@0 2368 */
michael@0 2369 parse: function(data, isSessionless, msg) {
michael@0 2370 if (!msg) {
michael@0 2371 msg = {
michael@0 2372 type: null,
michael@0 2373 };
michael@0 2374 }
michael@0 2375
michael@0 2376 try {
michael@0 2377 if (isSessionless) {
michael@0 2378 // "The `transactionId` is used to associate requests with replies in
michael@0 2379 // the connectionless session service." ~ WAP-230-WSP-20010705-a 8.2.1
michael@0 2380 msg.transactionId = Octet.decode(data);
michael@0 2381 }
michael@0 2382
michael@0 2383 msg.type = Octet.decode(data);
michael@0 2384 switch (msg.type) {
michael@0 2385 case WSP_PDU_TYPE_PUSH:
michael@0 2386 this.parsePushHeaders(data, msg);
michael@0 2387 break;
michael@0 2388 }
michael@0 2389 } catch (e) {
michael@0 2390 debug("Parse error. Message: " + e.message);
michael@0 2391 msg = null;
michael@0 2392 }
michael@0 2393
michael@0 2394 return msg;
michael@0 2395 },
michael@0 2396
michael@0 2397 /**
michael@0 2398 * @param multiStream
michael@0 2399 * An exsiting nsIMultiplexInputStream.
michael@0 2400 * @param array
michael@0 2401 * An octet array.
michael@0 2402 * @param length
michael@0 2403 * Max number of octets to be coverted into an input stream.
michael@0 2404 */
michael@0 2405 appendArrayToMultiStream: function(multiStream, array, length) {
michael@0 2406 let storageStream = Cc["@mozilla.org/storagestream;1"]
michael@0 2407 .createInstance(Ci.nsIStorageStream);
michael@0 2408 storageStream.init(4096, length, null);
michael@0 2409
michael@0 2410 let boStream = Cc["@mozilla.org/binaryoutputstream;1"]
michael@0 2411 .createInstance(Ci.nsIBinaryOutputStream);
michael@0 2412 boStream.setOutputStream(storageStream.getOutputStream(0));
michael@0 2413 boStream.writeByteArray(array, length);
michael@0 2414 boStream.close();
michael@0 2415
michael@0 2416 multiStream.appendStream(storageStream.newInputStream(0));
michael@0 2417 },
michael@0 2418
michael@0 2419 /**
michael@0 2420 * @param multiStream
michael@0 2421 * An exsiting nsIMultiplexInputStream.
michael@0 2422 * @param parts
michael@0 2423 * An array of objects representing multipart entries.
michael@0 2424 *
michael@0 2425 * @see WAP-230-WSP-20010705-a section 8.5
michael@0 2426 */
michael@0 2427 composeMultiPart: function(multiStream, parts) {
michael@0 2428 // Encode multipart header
michael@0 2429 {
michael@0 2430 let data = {array: [], offset: 0};
michael@0 2431 UintVar.encode(data, parts.length);
michael@0 2432 debug("Encoded multipart header: " + JSON.stringify(data.array));
michael@0 2433 this.appendArrayToMultiStream(multiStream, data.array, data.offset);
michael@0 2434 }
michael@0 2435
michael@0 2436 // Encode each part
michael@0 2437 for (let i = 0; i < parts.length; i++) {
michael@0 2438 let part = parts[i];
michael@0 2439 let data = {array: [], offset: 0};
michael@0 2440
michael@0 2441 // Encode Content-Type
michael@0 2442 let contentType = part.headers["content-type"];
michael@0 2443 ContentTypeValue.encode(data, contentType);
michael@0 2444
michael@0 2445 // Encode other headers
michael@0 2446 if (Object.keys(part).length > 1) {
michael@0 2447 // Remove Content-Type temporarily
michael@0 2448 delete part.headers["content-type"];
michael@0 2449
michael@0 2450 for (let name in part.headers) {
michael@0 2451 Header.encode(data, {name: name, value: part.headers[name]});
michael@0 2452 }
michael@0 2453
michael@0 2454 // Restore Content-Type back
michael@0 2455 part.headers["content-type"] = contentType;
michael@0 2456 }
michael@0 2457
michael@0 2458 // Encode headersLen, DataLen
michael@0 2459 let headersLen = data.offset;
michael@0 2460 let content = part.content;
michael@0 2461 UintVar.encode(data, headersLen);
michael@0 2462 if (typeof content === "string") {
michael@0 2463 let charset;
michael@0 2464 if (contentType && contentType.params && contentType.params.charset &&
michael@0 2465 contentType.params.charset.charset) {
michael@0 2466 charset = contentType.params.charset.charset;
michael@0 2467 }
michael@0 2468 content = this.encodeStringContent(content, charset);
michael@0 2469 UintVar.encode(data, content.length);
michael@0 2470 } else if (part.content instanceof Uint8Array) {
michael@0 2471 UintVar.encode(data, content.length);
michael@0 2472 } else {
michael@0 2473 throw new TypeError();
michael@0 2474 }
michael@0 2475
michael@0 2476 // Move them to the beginning of encoded octet array.
michael@0 2477 let slice1 = data.array.slice(headersLen);
michael@0 2478 let slice2 = data.array.slice(0, headersLen);
michael@0 2479 data.array = slice1.concat(slice2);
michael@0 2480 debug("Encoded per-part header: " + JSON.stringify(data.array));
michael@0 2481
michael@0 2482 // Append per-part header
michael@0 2483 this.appendArrayToMultiStream(multiStream, data.array, data.offset);
michael@0 2484 // Append part content
michael@0 2485 this.appendArrayToMultiStream(multiStream, content, content.length);
michael@0 2486 }
michael@0 2487 },
michael@0 2488 };
michael@0 2489
michael@0 2490 // WSP Header Field Name Assignments
michael@0 2491 // Note: Items commented out are either deprecated or not implemented.
michael@0 2492 // Deprecated items should only be supported for backward compatibility
michael@0 2493 // purpose.
michael@0 2494 // @see WAP-230-WSP-20010705-a Appendix A. Assigned Numbers.
michael@0 2495 this.WSP_HEADER_FIELDS = (function() {
michael@0 2496 let names = {};
michael@0 2497 function add(name, number, coder) {
michael@0 2498 let entry = {
michael@0 2499 name: name,
michael@0 2500 number: number,
michael@0 2501 coder: coder,
michael@0 2502 };
michael@0 2503 names[name] = names[number] = entry;
michael@0 2504 }
michael@0 2505
michael@0 2506 // Encoding Version: 1.1
michael@0 2507 //add("accept", 0x00);
michael@0 2508 //add("accept-charset", 0x01); Deprecated
michael@0 2509 //add("accept-encoding", 0x02); Deprecated
michael@0 2510 //add("accept-language", 0x03);
michael@0 2511 //add("accept-ranges", 0x04);
michael@0 2512 add("age", 0x05, DeltaSecondsValue);
michael@0 2513 //add("allow", 0x06);
michael@0 2514 //add("authorization", 0x07);
michael@0 2515 //add("cache-control", 0x08); Deprecated
michael@0 2516 //add("connection", 0x09);
michael@0 2517 //add("content-base", 0x0A); Deprecated
michael@0 2518 //add("content-encoding", 0x0B);
michael@0 2519 //add("content-language", 0x0C);
michael@0 2520 add("content-length", 0x0D, IntegerValue);
michael@0 2521 add("content-location", 0x0E, UriValue);
michael@0 2522 //add("content-md5", 0x0F);
michael@0 2523 //add("content-range", 0x10); Deprecated
michael@0 2524 add("content-type", 0x11, ContentTypeValue);
michael@0 2525 add("date", 0x12, DateValue);
michael@0 2526 add("etag", 0x13, TextString);
michael@0 2527 add("expires", 0x14, DateValue);
michael@0 2528 add("from", 0x15, TextString);
michael@0 2529 add("host", 0x16, TextString);
michael@0 2530 add("if-modified-since", 0x17, DateValue);
michael@0 2531 add("if-match", 0x18, TextString);
michael@0 2532 add("if-none-match", 0x19, TextString);
michael@0 2533 //add("if-range", 0x1A);
michael@0 2534 add("if-unmodified-since", 0x1B, DateValue);
michael@0 2535 add("location", 0x1C, UriValue);
michael@0 2536 add("last-modified", 0x1D, DateValue);
michael@0 2537 add("max-forwards", 0x1E, IntegerValue);
michael@0 2538 //add("pragma", 0x1F);
michael@0 2539 //add("proxy-authenticate", 0x20);
michael@0 2540 //add("proxy-authentication", 0x21);
michael@0 2541 //add("public", 0x22);
michael@0 2542 //add("range", 0x23);
michael@0 2543 add("referer", 0x24, UriValue);
michael@0 2544 //add("retry-after", 0x25);
michael@0 2545 add("server", 0x26, TextString);
michael@0 2546 //add("transfer-encoding", 0x27);
michael@0 2547 add("upgrade", 0x28, TextString);
michael@0 2548 add("user-agent", 0x29, TextString);
michael@0 2549 //add("vary", 0x2A);
michael@0 2550 add("via", 0x2B, TextString);
michael@0 2551 //add("warning", 0x2C);
michael@0 2552 //add("www-authenticate", 0x2D);
michael@0 2553 //add("content-disposition", 0x2E); Deprecated
michael@0 2554
michael@0 2555 // Encoding Version: 1.2
michael@0 2556 add("x-wap-application-id", 0x2F, ApplicationIdValue);
michael@0 2557 add("x-wap-content-uri", 0x30, UriValue);
michael@0 2558 add("x-wap-initiator-uri", 0x31, UriValue);
michael@0 2559 //add("accept-application", 0x32);
michael@0 2560 add("bearer-indication", 0x33, IntegerValue);
michael@0 2561 add("push-flag", 0x34, ShortInteger);
michael@0 2562 add("profile", 0x35, UriValue);
michael@0 2563 //add("profile-diff", 0x36);
michael@0 2564 //add("profile-warning", 0x37); Deprecated
michael@0 2565
michael@0 2566 // Encoding Version: 1.3
michael@0 2567 //add("expect", 0x38);
michael@0 2568 //add("te", 0x39);
michael@0 2569 //add("trailer", 0x3A);
michael@0 2570 add("accept-charset", 0x3B, AcceptCharsetValue);
michael@0 2571 //add("accept-encoding", 0x3C);
michael@0 2572 //add("cache-control", 0x3D); Deprecated
michael@0 2573 //add("content-range", 0x3E);
michael@0 2574 add("x-wap-tod", 0x3F, DateValue);
michael@0 2575 add("content-id", 0x40, QuotedString);
michael@0 2576 //add("set-cookie", 0x41);
michael@0 2577 //add("cookie", 0x42);
michael@0 2578 //add("encoding-version", 0x43);
michael@0 2579
michael@0 2580 // Encoding Version: 1.4
michael@0 2581 //add("profile-warning", 0x44);
michael@0 2582 //add("content-disposition", 0x45);
michael@0 2583 //add("x-wap-security", 0x46);
michael@0 2584 //add("cache-control", 0x47);
michael@0 2585
michael@0 2586 return names;
michael@0 2587 })();
michael@0 2588
michael@0 2589 // WSP Content Type Assignments
michael@0 2590 // @see http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx
michael@0 2591 this.WSP_WELL_KNOWN_CONTENT_TYPES = (function() {
michael@0 2592 let types = {};
michael@0 2593
michael@0 2594 function add(type, number) {
michael@0 2595 let entry = {
michael@0 2596 type: type,
michael@0 2597 number: number,
michael@0 2598 };
michael@0 2599 // For case like "text/x-vCalendar", we need toLoweCase() for generating
michael@0 2600 // the same index.
michael@0 2601 types[type.toLowerCase()] = types[number] = entry;
michael@0 2602 }
michael@0 2603
michael@0 2604 // Well Known Values
michael@0 2605 // Encoding Version: 1.1
michael@0 2606 add("*/*", 0x00);
michael@0 2607 add("text/*", 0x01);
michael@0 2608 add("text/html", 0x02);
michael@0 2609 add("text/plain", 0x03);
michael@0 2610 add("text/x-hdml", 0x04);
michael@0 2611 add("text/x-ttml", 0x05);
michael@0 2612 add("text/x-vCalendar", 0x06);
michael@0 2613 add("text/x-vCard", 0x07);
michael@0 2614 add("text/vnd.wap.wml", 0x08);
michael@0 2615 add("text/vnd.wap.wmlscript", 0x09);
michael@0 2616 add("text/vnd.wap.wta-event", 0x0A);
michael@0 2617 add("multipart/*", 0x0B);
michael@0 2618 add("multipart/mixed", 0x0C);
michael@0 2619 add("multipart/form-data", 0x0D);
michael@0 2620 add("multipart/byterantes", 0x0E);
michael@0 2621 add("multipart/alternative", 0x0F);
michael@0 2622 add("application/*", 0x10);
michael@0 2623 add("application/java-vm", 0x11);
michael@0 2624 add("application/x-www-form-urlencoded", 0x12);
michael@0 2625 add("application/x-hdmlc", 0x13);
michael@0 2626 add("application/vnd.wap.wmlc", 0x14);
michael@0 2627 add("application/vnd.wap.wmlscriptc", 0x15);
michael@0 2628 add("application/vnd.wap.wta-eventc", 0x16);
michael@0 2629 add("application/vnd.wap.uaprof", 0x17);
michael@0 2630 add("application/vnd.wap.wtls-ca-certificate", 0x18);
michael@0 2631 add("application/vnd.wap.wtls-user-certificate", 0x19);
michael@0 2632 add("application/x-x509-ca-cert", 0x1A);
michael@0 2633 add("application/x-x509-user-cert", 0x1B);
michael@0 2634 add("image/*", 0x1C);
michael@0 2635 add("image/gif", 0x1D);
michael@0 2636 add("image/jpeg", 0x1E);
michael@0 2637 add("image/tiff", 0x1F);
michael@0 2638 add("image/png", 0x20);
michael@0 2639 add("image/vnd.wap.wbmp", 0x21);
michael@0 2640 add("application/vnd.wap.multipart.*", 0x22);
michael@0 2641 add("application/vnd.wap.multipart.mixed", 0x23);
michael@0 2642 add("application/vnd.wap.multipart.form-data", 0x24);
michael@0 2643 add("application/vnd.wap.multipart.byteranges", 0x25);
michael@0 2644 add("application/vnd.wap.multipart.alternative", 0x26);
michael@0 2645 add("application/xml", 0x27);
michael@0 2646 add("text/xml", 0x28);
michael@0 2647 add("application/vnd.wap.wbxml", 0x29);
michael@0 2648 add("application/x-x968-cross-cert", 0x2A);
michael@0 2649 add("application/x-x968-ca-cert", 0x2B);
michael@0 2650 add("application/x-x968-user-cert", 0x2C);
michael@0 2651 add("text/vnd.wap.si", 0x2D);
michael@0 2652
michael@0 2653 // Encoding Version: 1.2
michael@0 2654 add("application/vnd.wap.sic", 0x2E);
michael@0 2655 add("text/vnd.wap.sl", 0x2F);
michael@0 2656 add("application/vnd.wap.slc", 0x30);
michael@0 2657 add("text/vnd.wap.co", 0x31);
michael@0 2658 add("application/vnd.wap.coc", 0x32);
michael@0 2659 add("application/vnd.wap.multipart.related", 0x33);
michael@0 2660 add("application/vnd.wap.sia", 0x34);
michael@0 2661
michael@0 2662 // Encoding Version: 1.3
michael@0 2663 add("text/vnd.wap.connectivity-xml", 0x35);
michael@0 2664 add("application/vnd.wap.connectivity-wbxml", 0x36);
michael@0 2665
michael@0 2666 // Encoding Version: 1.4
michael@0 2667 add("application/pkcs7-mime", 0x37);
michael@0 2668 add("application/vnd.wap.hashed-certificate", 0x38);
michael@0 2669 add("application/vnd.wap.signed-certificate", 0x39);
michael@0 2670 add("application/vnd.wap.cert-response", 0x3A);
michael@0 2671 add("application/xhtml+xml", 0x3B);
michael@0 2672 add("application/wml+xml", 0x3C);
michael@0 2673 add("text/css", 0x3D);
michael@0 2674 add("application/vnd.wap.mms-message", 0x3E);
michael@0 2675 add("application/vnd.wap.rollover-certificate", 0x3F);
michael@0 2676
michael@0 2677 // Encoding Version: 1.5
michael@0 2678 add("application/vnd.wap.locc+wbxml", 0x40);
michael@0 2679 add("application/vnd.wap.loc+xml", 0x41);
michael@0 2680 add("application/vnd.syncml.dm+wbxml", 0x42);
michael@0 2681 add("application/vnd.syncml.dm+xml", 0x43);
michael@0 2682 add("application/vnd.syncml.notification", 0x44);
michael@0 2683 add("application/vnd.wap.xhtml+xml", 0x45);
michael@0 2684 add("application/vnd.wv.csp.cir", 0x46);
michael@0 2685 add("application/vnd.oma.dd+xml", 0x47);
michael@0 2686 add("application/vnd.oma.drm.message", 0x48);
michael@0 2687 add("application/vnd.oma.drm.content", 0x49);
michael@0 2688 add("application/vnd.oma.drm.rights+xml", 0x4A);
michael@0 2689 add("application/vnd.oma.drm.rights+wbxml", 0x4B);
michael@0 2690 add("application/vnd.wv.csp+xml", 0x4C);
michael@0 2691 add("application/vnd.wv.csp+wbxml", 0x4D);
michael@0 2692 add("application/vnd.syncml.ds.notification", 0x4E);
michael@0 2693
michael@0 2694 // Encoding Version: 1.6
michael@0 2695 add("audio/*", 0x4F);
michael@0 2696 add("video/*", 0x50);
michael@0 2697
michael@0 2698 // Encoding Version: TBD
michael@0 2699 add("application/vnd.oma.dd2+xml", 0x51);
michael@0 2700 add("application/mikey", 0x52);
michael@0 2701 add("application/vnd.oma.dcd", 0x53);
michael@0 2702 add("application/vnd.oma.dcdc", 0x54);
michael@0 2703 add("text/x-vMessage", 0x55);
michael@0 2704 add("application/vnd.omads-email+wbxml", 0x56);
michael@0 2705 add("text/x-vBookmark", 0x57);
michael@0 2706 add("application/vnd.syncml.dm.notification", 0x58);
michael@0 2707 add("application/octet-stream", 0x5A);
michael@0 2708
michael@0 2709 return types;
michael@0 2710 })();
michael@0 2711
michael@0 2712 // WSP Well-Known Parameter Assignments
michael@0 2713 // Note: Items commented out are either deprecated or not implemented.
michael@0 2714 // Deprecated items should not be used.
michael@0 2715 // @see WAP-230-WSP-20010705-a Appendix A. Assigned Numbers.
michael@0 2716 this.WSP_WELL_KNOWN_PARAMS = (function() {
michael@0 2717 let params = {};
michael@0 2718
michael@0 2719 function add(name, number, coder) {
michael@0 2720 let entry = {
michael@0 2721 name: name,
michael@0 2722 number: number,
michael@0 2723 coder: coder,
michael@0 2724 };
michael@0 2725 params[name] = params[number] = entry;
michael@0 2726 }
michael@0 2727
michael@0 2728 // Encoding Version: 1.1
michael@0 2729 add("q", 0x00, QValue);
michael@0 2730 add("charset", 0x01, WellKnownCharset);
michael@0 2731 add("level", 0x02, VersionValue);
michael@0 2732 add("type", 0x03, IntegerValue);
michael@0 2733 add("name", 0x05, TextValue); // Deprecated, but used in some carriers, eg. Hinet.
michael@0 2734 //add("filename", 0x06); Deprecated
michael@0 2735 add("differences", 0x07, FieldName);
michael@0 2736 add("padding", 0x08, ShortInteger);
michael@0 2737
michael@0 2738 // Encoding Version: 1.2
michael@0 2739 add("type", 0x09, TypeValue);
michael@0 2740 add("start", 0x0A, TextValue); // Deprecated, but used in some carriers, eg. T-Mobile.
michael@0 2741 //add("start-info", 0x0B); Deprecated
michael@0 2742
michael@0 2743 // Encoding Version: 1.3
michael@0 2744 //add("comment", 0x0C); Deprecated
michael@0 2745 //add("domain", 0x0D); Deprecated
michael@0 2746 add("max-age", 0x0E, DeltaSecondsValue);
michael@0 2747 //add("path", 0x0F); Deprecated
michael@0 2748 add("secure", 0x10, NoValue);
michael@0 2749
michael@0 2750 // Encoding Version: 1.4
michael@0 2751 add("sec", 0x11, ShortInteger);
michael@0 2752 add("mac", 0x12, TextValue);
michael@0 2753 add("creation-date", 0x13, DateValue);
michael@0 2754 add("modification-date", 0x14, DateValue);
michael@0 2755 add("read-date", 0x15, DateValue);
michael@0 2756 add("size", 0x16, IntegerValue);
michael@0 2757 //add("name", 0x17, TextValue); // Not supported in some carriers, eg. Hinet.
michael@0 2758 add("filename", 0x18, TextValue);
michael@0 2759 //add("start", 0x19, TextValue); // Not supported in some carriers, eg. Hinet.
michael@0 2760 add("start-info", 0x1A, TextValue);
michael@0 2761 add("comment", 0x1B, TextValue);
michael@0 2762 add("domain", 0x1C, TextValue);
michael@0 2763 add("path", 0x1D, TextValue);
michael@0 2764
michael@0 2765 return params;
michael@0 2766 })();
michael@0 2767
michael@0 2768 // WSP Character Set Assignments
michael@0 2769 // @see WAP-230-WSP-20010705-a Appendix A. Assigned Numbers.
michael@0 2770 // @see http://www.iana.org/assignments/character-sets
michael@0 2771 this.WSP_WELL_KNOWN_CHARSETS = (function() {
michael@0 2772 let charsets = {};
michael@0 2773
michael@0 2774 function add(name, number, converter) {
michael@0 2775 let entry = {
michael@0 2776 name: name,
michael@0 2777 number: number,
michael@0 2778 converter: converter,
michael@0 2779 };
michael@0 2780
michael@0 2781 charsets[name] = charsets[number] = entry;
michael@0 2782 }
michael@0 2783
michael@0 2784 add("us-ascii", 3, null);
michael@0 2785 add("iso-8859-1", 4, "ISO-8859-1");
michael@0 2786 add("iso-8859-2", 5, "ISO-8859-2");
michael@0 2787 add("iso-8859-3", 6, "ISO-8859-3");
michael@0 2788 add("iso-8859-4", 7, "ISO-8859-4");
michael@0 2789 add("iso-8859-5", 8, "ISO-8859-5");
michael@0 2790 add("iso-8859-6", 9, "ISO-8859-6");
michael@0 2791 add("iso-8859-7", 10, "ISO-8859-7");
michael@0 2792 add("iso-8859-8", 11, "ISO-8859-8");
michael@0 2793 add("iso-8859-9", 12, "ISO-8859-9");
michael@0 2794 add("iso-8859-10", 13, "ISO-8859-10");
michael@0 2795 add("shift_jis", 17, "Shift_JIS");
michael@0 2796 add("euc-jp", 18, "EUC-JP");
michael@0 2797 add("iso-2022-kr", 37, "ISO-2022-KR");
michael@0 2798 add("euc-kr", 38, "EUC-KR");
michael@0 2799 add("iso-2022-jp", 39, "ISO-2022-JP");
michael@0 2800 add("iso-2022-jp-2", 40, "iso-2022-jp-2");
michael@0 2801 add("iso-8859-6-e", 81, "ISO-8859-6-E");
michael@0 2802 add("iso-8859-6-i", 82, "ISO-8859-6-I");
michael@0 2803 add("iso-8859-8-e", 84, "ISO-8859-8-E");
michael@0 2804 add("iso-8859-8-i", 85, "ISO-8859-8-I");
michael@0 2805 add("utf-8", 106, "UTF-8");
michael@0 2806 add("iso-10646-ucs-2", 1000, "iso-10646-ucs-2");
michael@0 2807 add("utf-16", 1015, "UTF-16");
michael@0 2808 add("gb2312", 2025, "GB2312");
michael@0 2809 add("big5", 2026, "Big5");
michael@0 2810 add("koi8-r", 2084, "KOI8-R");
michael@0 2811 add("windows-1252", 2252, "windows-1252");
michael@0 2812
michael@0 2813 return charsets;
michael@0 2814 })();
michael@0 2815
michael@0 2816 // OMNA PUSH Application ID
michael@0 2817 // @see http://www.openmobilealliance.org/tech/omna/omna-push-app-id.aspx
michael@0 2818 this.OMNA_PUSH_APPLICATION_IDS = (function() {
michael@0 2819 let ids = {};
michael@0 2820
michael@0 2821 function add(urn, number) {
michael@0 2822 let entry = {
michael@0 2823 urn: urn,
michael@0 2824 number: number,
michael@0 2825 };
michael@0 2826
michael@0 2827 ids[urn] = ids[number] = entry;
michael@0 2828 }
michael@0 2829
michael@0 2830 add("x-wap-application:wml.ua", 0x02);
michael@0 2831 add("x-wap-application:mms.ua", 0x04);
michael@0 2832
michael@0 2833 return ids;
michael@0 2834 })();
michael@0 2835
michael@0 2836 let debug;
michael@0 2837 if (DEBUG) {
michael@0 2838 debug = function(s) {
michael@0 2839 dump("-@- WspPduHelper: " + s + "\n");
michael@0 2840 };
michael@0 2841 } else {
michael@0 2842 debug = function(s) {};
michael@0 2843 }
michael@0 2844
michael@0 2845 this.EXPORTED_SYMBOLS = ALL_CONST_SYMBOLS.concat([
michael@0 2846 // Constant values
michael@0 2847 "WSP_HEADER_FIELDS",
michael@0 2848 "WSP_WELL_KNOWN_CONTENT_TYPES",
michael@0 2849 "WSP_WELL_KNOWN_PARAMS",
michael@0 2850 "WSP_WELL_KNOWN_CHARSETS",
michael@0 2851 "OMNA_PUSH_APPLICATION_IDS",
michael@0 2852
michael@0 2853 // Error classes
michael@0 2854 "CodeError",
michael@0 2855 "FatalCodeError",
michael@0 2856 "NotWellKnownEncodingError",
michael@0 2857
michael@0 2858 // Utility functions
michael@0 2859 "ensureHeader",
michael@0 2860 "skipValue",
michael@0 2861 "decodeAlternatives",
michael@0 2862 "encodeAlternatives",
michael@0 2863
michael@0 2864 // Decoders
michael@0 2865 "Octet",
michael@0 2866 "Text",
michael@0 2867 "NullTerminatedTexts",
michael@0 2868 "Token",
michael@0 2869 "URIC",
michael@0 2870 "TextString",
michael@0 2871 "TokenText",
michael@0 2872 "QuotedString",
michael@0 2873 "ShortInteger",
michael@0 2874 "LongInteger",
michael@0 2875 "UintVar",
michael@0 2876 "ConstrainedEncoding",
michael@0 2877 "ValueLength",
michael@0 2878 "NoValue",
michael@0 2879 "TextValue",
michael@0 2880 "IntegerValue",
michael@0 2881 "DateValue",
michael@0 2882 "DeltaSecondsValue",
michael@0 2883 "QValue",
michael@0 2884 "VersionValue",
michael@0 2885 "UriValue",
michael@0 2886 "TypeValue",
michael@0 2887 "Parameter",
michael@0 2888 "Header",
michael@0 2889 "WellKnownHeader",
michael@0 2890 "ApplicationHeader",
michael@0 2891 "FieldName",
michael@0 2892 "AcceptCharsetValue",
michael@0 2893 "WellKnownCharset",
michael@0 2894 "ContentTypeValue",
michael@0 2895 "ApplicationIdValue",
michael@0 2896
michael@0 2897 // Parser
michael@0 2898 "PduHelper",
michael@0 2899 ]);

mercurial