Wed, 31 Dec 2014 06:09:35 +0100
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 | ]); |