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.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 "use strict";
7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
9 let WSP = {};
10 Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP);
12 Cu.import("resource://gre/modules/mms_consts.js");
14 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
16 let DEBUG; // set to true to see debug messages
18 this.MMS_VERSION = (function() {
19 Cu.import("resource://gre/modules/Services.jsm");
21 try {
22 return Services.prefs.getIntPref("dom.mms.version");
23 } catch(ex) {}
25 return MMS_VERSION_1_3;
26 })();
28 this.translatePduErrorToStatus = function translatePduErrorToStatus(error) {
29 if (error == MMS_PDU_ERROR_OK) {
30 return MMS_PDU_STATUS_RETRIEVED;
31 }
33 if ((error >= MMS_PDU_ERROR_TRANSIENT_FAILURE)
34 && (error < MMS_PDU_ERROR_PERMANENT_FAILURE)) {
35 return MMS_PDU_STATUS_DEFERRED;
36 }
38 return MMS_PDU_STATUS_UNRECOGNISED;
39 }
41 function defineLazyRegExp(obj, name, pattern) {
42 obj.__defineGetter__(name, function() {
43 delete obj[name];
44 return obj[name] = new RegExp(pattern);
45 });
46 }
48 function RangedValue(name, min, max) {
49 this.name = name;
50 this.min = min;
51 this.max = max;
52 }
53 RangedValue.prototype = {
54 name: null,
55 min: null,
56 max: null,
58 /**
59 * @param data
60 * A wrapped object containing raw PDU data.
61 *
62 * @return A decoded integer.
63 *
64 * @throws CodeError if decoded value is not in the range [this.min, this.max].
65 */
66 decode: function(data) {
67 let value = WSP.Octet.decode(data);
68 if ((value >= this.min) && (value <= this.max)) {
69 return value;
70 }
72 throw new WSP.CodeError(this.name + ": invalid value " + value);
73 },
75 /**
76 * @param data
77 * A wrapped object to store encoded raw data.
78 * @param value
79 * An integer value within thr range [this.min, this.max].
80 */
81 encode: function(data, value) {
82 if ((value < this.min) || (value > this.max)) {
83 throw new WSP.CodeError(this.name + ": invalid value " + value);
84 }
86 WSP.Octet.encode(data, value);
87 },
88 };
90 /**
91 * Internal decoding function for boolean values.
92 *
93 * Boolean-value = Yes | No
94 * Yes = <Octet 128>
95 * No = <Octet 129>
96 */
97 this.BooleanValue = {
98 /**
99 * @param data
100 * A wrapped object containing raw PDU data.
101 *
102 * @return Boolean true or false.
103 *
104 * @throws CodeError if read octet equals to neither 128 nor 129.
105 */
106 decode: function(data) {
107 let value = WSP.Octet.decode(data);
108 if ((value != 128) && (value != 129)) {
109 throw new WSP.CodeError("Boolean-value: invalid value " + value);
110 }
112 return value == 128;
113 },
115 /**
116 * @param data
117 * A wrapped object to store encoded raw data.
118 * @param value
119 * A boolean value to be encoded.
120 */
121 encode: function(data, value) {
122 WSP.Octet.encode(data, value ? 128 : 129);
123 },
124 };
126 /**
127 * MMS Address
128 *
129 * address = email | device-address | alphanum-shortcode | num-shortcode
130 *
131 * @see OMA-TS-MMS_ENC-V1_3-20110913-A section 8
132 */
133 this.Address = {
134 /**
135 * @param data
136 * A wrapped object to store encoded raw data.
137 *
138 * @return An object of two string-typed attributes: address and type.
139 */
140 decode: function(data) {
141 let str = EncodedStringValue.decode(data);
143 let result;
144 if (((result = str.match(this.REGEXP_DECODE_PLMN)) != null)
145 || ((result = str.match(this.REGEXP_DECODE_IPV4)) != null)
146 || ((result = str.match(this.REGEXP_DECODE_IPV6)) != null)
147 || ((result = str.match(this.REGEXP_DECODE_CUSTOM)) != null)) {
148 return {address: result[1], type: result[2]};
149 }
151 let type;
152 if (str.match(this.REGEXP_NUM)) {
153 type = "num";
154 } else if (str.match(this.REGEXP_ALPHANUM)) {
155 type = "alphanum";
156 } else if (str.indexOf("@") > 0) {
157 // E-mail should match the definition of `mailbox` as described in section
158 // 3.4 of RFC2822, but excluding the obsolete definitions as indicated by
159 // the "obs-" prefix. Here we match only a `@` character.
160 type = "email";
161 } else {
162 throw new WSP.CodeError("Address: invalid address");
163 }
165 return {address: str, type: type};
166 },
168 /**
169 * @param data
170 * A wrapped object to store encoded raw data.
171 * @param value
172 * An object of two string-typed attributes: address and type.
173 */
174 encode: function(data, value) {
175 if (!value || !value.type || !value.address) {
176 throw new WSP.CodeError("Address: invalid value");
177 }
179 let str;
180 switch (value.type) {
181 case "email":
182 if (value.address.indexOf("@") > 0) {
183 str = value.address;
184 }
185 break;
186 case "num":
187 if (value.address.match(this.REGEXP_NUM)) {
188 str = value.address;
189 }
190 break;
191 case "alphanum":
192 if (value.address.match(this.REGEXP_ALPHANUM)) {
193 str = value.address;
194 }
195 break;
196 case "IPv4":
197 if (value.address.match(this.REGEXP_ENCODE_IPV4)) {
198 str = value.address + "/TYPE=IPv4";
199 }
200 break;
201 case "IPv6":
202 if (value.address.match(this.REGEXP_ENCODE_IPV6)) {
203 str = value.address + "/TYPE=IPv6";
204 }
205 break;
206 case "PLMN":
207 if (value.address.match(this.REGEXP_ENCODE_PLMN)) {
208 str = value.address + "/TYPE=PLMN";
209 }
210 break;
211 default:
212 if (value.type.match(this.REGEXP_ENCODE_CUSTOM_TYPE)
213 && value.address.match(this.REGEXP_ENCODE_CUSTOM_ADDR)) {
214 str = value.address + "/TYPE=" + value.type;
215 }
216 break;
217 }
219 if (!str) {
220 throw new WSP.CodeError("Address: invalid value: " + JSON.stringify(value));
221 }
223 EncodedStringValue.encode(data, str);
224 },
226 /**
227 * @param address
228 * Address string which want to find the type.
229 *
230 * @return Address type.
231 */
232 resolveType: function(address) {
233 if (address.match(this.REGEXP_EMAIL)) {
234 return "email";
235 }
237 if (address.match(this.REGEXP_IPV4)) {
238 return "IPv4";
239 }
241 if (address.match(this.REGEXP_IPV6)) {
242 return "IPv6";
243 }
245 let normalizedAddress = PhoneNumberUtils.normalize(address, false);
246 if (PhoneNumberUtils.isPlainPhoneNumber(normalizedAddress)) {
247 return "PLMN";
248 }
250 return "Others";
251 },
252 };
254 defineLazyRegExp(Address, "REGEXP_DECODE_PLMN", "^(\\+?[\\d.-]+)\\/TYPE=(PLMN)$");
255 defineLazyRegExp(Address, "REGEXP_DECODE_IPV4", "^(\\d{1,3}(?:\\.\\d{1,3}){3})\\/TYPE=(IPv4)$");
256 defineLazyRegExp(Address, "REGEXP_DECODE_IPV6", "^([\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7})\\/TYPE=(IPv6)$");
257 defineLazyRegExp(Address, "REGEXP_DECODE_CUSTOM", "^([\\w\\+\\-.%]+)\\/TYPE=(\\w+)$");
258 defineLazyRegExp(Address, "REGEXP_ENCODE_PLMN", "^\\+?[\\d.-]+$");
259 defineLazyRegExp(Address, "REGEXP_ENCODE_IPV4", "^\\d{1,3}(?:\\.\\d{1,3}){3}$");
260 defineLazyRegExp(Address, "REGEXP_ENCODE_IPV6", "^[\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7}$");
261 defineLazyRegExp(Address, "REGEXP_ENCODE_CUSTOM_TYPE", "^\\w+$");
262 defineLazyRegExp(Address, "REGEXP_ENCODE_CUSTOM_ADDR", "^[\\w\\+\\-.%]+$");
263 defineLazyRegExp(Address, "REGEXP_NUM", "^[\\+*#]\\d+$");
264 defineLazyRegExp(Address, "REGEXP_ALPHANUM", "^\\w+$");
265 defineLazyRegExp(Address, "REGEXP_PLMN", "^\\?[\\d.-]$");
266 defineLazyRegExp(Address, "REGEXP_IPV4", "^\\d{1,3}(?:\\.\\d{1,3}){3}$");
267 defineLazyRegExp(Address, "REGEXP_IPV6", "^[\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7}$");
268 defineLazyRegExp(Address, "REGEXP_EMAIL", "@");
270 /**
271 * Header-field = MMS-header | Application-header
272 *
273 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.2
274 */
275 this.HeaderField = {
276 /**
277 * @param data
278 * A wrapped object containing raw PDU data.
279 * @param options
280 * Extra context for decoding.
281 *
282 * @return A decoded object containing `name` and `value` properties or null
283 * in case of a failed parsing. The `name` property must be a string,
284 * but the `value` property can be many different types depending on
285 * `name`.
286 */
287 decode: function(data, options) {
288 return WSP.decodeAlternatives(data, options,
289 MmsHeader, WSP.ApplicationHeader);
290 },
292 /**
293 * @param data
294 * A wrapped object to store encoded raw data.
295 * @param octet
296 * Octet value to be encoded.
297 * @param options
298 * Extra context for encoding.
299 */
300 encode: function(data, value, options) {
301 WSP.encodeAlternatives(data, value, options,
302 MmsHeader, WSP.ApplicationHeader);
303 },
304 };
306 /**
307 * MMS-header = MMS-field-name MMS-value
308 * MMS-field-name = Short-integer
309 *
310 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.2
311 */
312 this.MmsHeader = {
313 /**
314 * @param data
315 * A wrapped object containing raw PDU data.
316 * @param options
317 * Extra context for decoding.
318 *
319 * @return A decoded object containing `name` and `value` properties or null
320 * in case of a failed parsing. The `name` property must be a string,
321 * but the `value` property can be many different types depending on
322 * `name`.
323 *
324 * @throws NotWellKnownEncodingError if decoded well-known header field
325 * number is not registered or supported.
326 */
327 decode: function(data, options) {
328 let index = WSP.ShortInteger.decode(data);
330 let entry = MMS_HEADER_FIELDS[index];
331 if (!entry) {
332 throw new WSP.NotWellKnownEncodingError(
333 "MMS-header: not well known header " + index);
334 }
336 let cur = data.offset, value;
337 try {
338 value = entry.coder.decode(data, options);
339 } catch (e) {
340 data.offset = cur;
342 value = WSP.skipValue(data);
343 debug("Skip malformed well known header: "
344 + JSON.stringify({name: entry.name, value: value}));
346 return null;
347 }
349 return {
350 name: entry.name,
351 value: value,
352 };
353 },
355 /**
356 * @param data
357 * A wrapped object to store encoded raw data.
358 * @param header
359 * An object containing two attributes: a string-typed `name` and a
360 * `value` of arbitrary type.
361 *
362 * @throws CodeError if got an empty header name.
363 * @throws NotWellKnownEncodingError if the well-known header field number is
364 * not registered or supported.
365 */
366 encode: function(data, header) {
367 if (!header.name) {
368 throw new WSP.CodeError("MMS-header: empty header name");
369 }
371 let entry = MMS_HEADER_FIELDS[header.name.toLowerCase()];
372 if (!entry) {
373 throw new WSP.NotWellKnownEncodingError(
374 "MMS-header: not well known header " + header.name);
375 }
377 WSP.ShortInteger.encode(data, entry.number);
378 entry.coder.encode(data, header.value);
379 },
380 };
382 /**
383 * Cancel-status-value = Cancel Request Successfully received |
384 * Cancel Request corrupted
385 *
386 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.7
387 */
388 this.CancelStatusValue = new RangedValue("Cancel-status-value", 128, 129);
390 /**
391 * Content-class-value = text | image-basic| image-rich | video-basic |
392 * video-rich | megapixel | content-basic | content-rich
393 *
394 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.9
395 */
396 this.ContentClassValue = new RangedValue("Content-class-value", 128, 135);
398 /**
399 * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
400 *
401 * Content-location-value = Uri-value
402 *
403 * When used in the M-Mbox-Delete.conf and M-Delete.conf PDU:
404 *
405 * Content-location-Del-value = Value-length Status-count-value Content-location-value
406 * Status-count-value = Integer-value
407 *
408 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.10
409 */
410 this.ContentLocationValue = {
411 /**
412 * @param data
413 * A wrapped object containing raw PDU data.
414 * @param options
415 * Extra context for decoding.
416 *
417 * @return A decoded object containing `uri` and conditional `statusCount`
418 * properties.
419 */
420 decode: function(data, options) {
421 let type = WSP.ensureHeader(options, "x-mms-message-type");
423 let result = {};
424 if ((type == MMS_PDU_TYPE_MBOX_DELETE_CONF)
425 || (type == MMS_PDU_TYPE_DELETE_CONF)) {
426 let length = WSP.ValueLength.decode(data);
427 let end = data.offset + length;
429 result.statusCount = WSP.IntegerValue.decode(data);
430 result.uri = WSP.UriValue.decode(data);
432 if (data.offset != end) {
433 data.offset = end;
434 }
435 } else {
436 result.uri = WSP.UriValue.decode(data);
437 }
439 return result;
440 },
441 };
443 /**
444 * Element-Descriptor-value = Value-length Content-Reference-value *(Parameter)
445 * Content-Reference-value = Text-string
446 *
447 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.18
448 */
449 this.ElementDescriptorValue = {
450 /**
451 * @param data
452 * A wrapped object containing raw PDU data.
453 *
454 * @return A decoded object containing a string property `contentReference`
455 * and an optinal `params` name-value map.
456 */
457 decode: function(data) {
458 let length = WSP.ValueLength.decode(data);
459 let end = data.offset + length;
461 let result = {};
462 result.contentReference = WSP.TextString.decode(data);
463 if (data.offset < end) {
464 result.params = Parameter.decodeMultiple(data, end);
465 }
467 if (data.offset != end) {
468 // Explicitly seek to end in case of skipped parameters.
469 data.offset = end;
470 }
472 return result;
473 },
474 };
476 /**
477 * OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.18:
478 * `For well-known parameter names binary tokens MUST be used as defined in
479 * Table 27.` So we can't reuse that of WSP.
480 *
481 * Parameter = Parameter-name Parameter-value
482 * Parameter-name = Short-integer | Text-string
483 * Parameter-value = Constrained-encoding | Text-string
484 *
485 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.18
486 */
487 this.Parameter = {
488 /**
489 * @param data
490 * A wrapped object containing raw PDU data.
491 *
492 * @return A decoded string.
493 *
494 * @throws NotWellKnownEncodingError if decoded well-known parameter number
495 * is not registered or supported.
496 */
497 decodeParameterName: function(data) {
498 let begin = data.offset;
499 let number;
500 try {
501 number = WSP.ShortInteger.decode(data);
502 } catch (e) {
503 data.offset = begin;
504 return WSP.TextString.decode(data).toLowerCase();
505 }
507 let entry = MMS_WELL_KNOWN_PARAMS[number];
508 if (!entry) {
509 throw new WSP.NotWellKnownEncodingError(
510 "Parameter-name: not well known parameter " + number);
511 }
513 return entry.name;
514 },
516 /**
517 * @param data
518 * A wrapped object containing raw PDU data.
519 *
520 * @return A decoded object containing `name` and `value` properties or null
521 * in case of a failed parsing. The `name` property must be a string,
522 * but the `value` property can be many different types depending on
523 * `name`.
524 */
525 decode: function(data) {
526 let name = this.decodeParameterName(data);
527 let value = WSP.decodeAlternatives(data, null,
528 WSP.ConstrainedEncoding, WSP.TextString);
529 return {
530 name: name,
531 value: value,
532 };
533 },
535 /**
536 * @param data
537 * A wrapped object containing raw PDU data.
538 * @param end
539 * Ending offset of following parameters.
540 *
541 * @return An array of decoded objects.
542 */
543 decodeMultiple: function(data, end) {
544 let params, param;
546 while (data.offset < end) {
547 try {
548 param = this.decode(data);
549 } catch (e) {
550 break;
551 }
552 if (param) {
553 if (!params) {
554 params = {};
555 }
556 params[param.name] = param.value;
557 }
558 }
560 return params;
561 },
563 /**
564 * @param data
565 * A wrapped object to store encoded raw data.
566 * @param param
567 * An object containing two attributes: `name` and `value`.
568 * @param options
569 * Extra context for encoding.
570 */
571 encode: function(data, param, options) {
572 if (!param || !param.name) {
573 throw new WSP.CodeError("Parameter-name: empty param name");
574 }
576 let entry = MMS_WELL_KNOWN_PARAMS[param.name.toLowerCase()];
577 if (entry) {
578 WSP.ShortInteger.encode(data, entry.number);
579 } else {
580 WSP.TextString.encode(data, param.name);
581 }
583 WSP.encodeAlternatives(data, param.value, options,
584 WSP.ConstrainedEncoding, WSP.TextString);
585 },
586 };
588 /**
589 * The Char-set values are registered by IANA as MIBEnum value and SHALL be
590 * encoded as Integer-value.
591 *
592 * Encoded-string-value = Text-string | Value-length Char-set Text-string
593 *
594 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.19
595 * @see OMA-TS-MMS_CONF-V1_3-20110913-A clause 10.2.1
596 */
597 this.EncodedStringValue = {
598 /**
599 * @param data
600 * A wrapped object containing raw PDU data.
601 *
602 * @return Decoded string.
603 *
604 * @throws CodeError if the raw octets cannot be converted.
605 * @throws NotWellKnownEncodingError if decoded well-known charset number is
606 * not registered or supported.
607 */
608 decodeCharsetEncodedString: function(data) {
609 let length = WSP.ValueLength.decode(data);
610 let end = data.offset + length;
612 let charset = WSP.IntegerValue.decode(data);
613 let entry = WSP.WSP_WELL_KNOWN_CHARSETS[charset];
614 if (!entry) {
615 throw new WSP.NotWellKnownEncodingError(
616 "Charset-encoded-string: not well known charset " + charset);
617 }
619 let str;
620 if (entry.converter) {
621 // Read a possible string quote(<Octet 127>).
622 let begin = data.offset;
623 if (WSP.Octet.decode(data) != 127) {
624 data.offset = begin;
625 }
627 let raw = WSP.Octet.decodeMultiple(data, end - 1);
628 // Read NUL character.
629 WSP.Octet.decodeEqualTo(data, 0);
631 if (!raw) {
632 str = "";
633 } else {
634 let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
635 .createInstance(Ci.nsIScriptableUnicodeConverter);
636 conv.charset = entry.converter;
637 try {
638 str = conv.convertFromByteArray(raw, raw.length);
639 } catch (e) {
640 throw new WSP.CodeError("Charset-encoded-string: " + e.message);
641 }
642 }
643 } else {
644 str = WSP.TextString.decode(data);
645 }
647 if (data.offset != end) {
648 data.offset = end;
649 }
651 return str;
652 },
654 /**
655 * @param data
656 * A wrapped object containing raw PDU data.
657 *
658 * @return Decoded string.
659 */
660 decode: function(data) {
661 let begin = data.offset;
662 try {
663 return WSP.TextString.decode(data);
664 } catch (e) {
665 data.offset = begin;
666 return this.decodeCharsetEncodedString(data);
667 }
668 },
670 /**
671 * Always encode target string with UTF-8 encoding.
672 *
673 * @param data
674 * A wrapped object to store encoded raw data.
675 * @param str
676 * A string.
677 */
678 encodeCharsetEncodedString: function(data, str) {
679 let conv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
680 .createInstance(Ci.nsIScriptableUnicodeConverter);
681 // `When the text string cannot be represented as us-ascii, the character
682 // set SHALL be encoded as utf-8(IANA MIBenum 106) which has unique byte
683 // ordering.` ~ OMA-TS-MMS_CONF-V1_3-20110913-A clause 10.2.1
684 conv.charset = "UTF-8";
686 let raw;
687 try {
688 raw = conv.convertToByteArray(str);
689 } catch (e) {
690 throw new WSP.CodeError("Charset-encoded-string: " + e.message);
691 }
693 let length = raw.length + 2; // Charset number and NUL character
694 // Prepend <Octet 127> if necessary.
695 if (raw[0] >= 128) {
696 ++length;
697 }
699 WSP.ValueLength.encode(data, length);
701 let entry = WSP.WSP_WELL_KNOWN_CHARSETS["utf-8"];
702 WSP.IntegerValue.encode(data, entry.number);
704 if (raw[0] >= 128) {
705 WSP.Octet.encode(data, 127);
706 }
707 WSP.Octet.encodeMultiple(data, raw);
708 WSP.Octet.encode(data, 0);
709 },
711 /**
712 * @param data
713 * A wrapped object to store encoded raw data.
714 * @param str
715 * A string.
716 */
717 encode: function(data, str) {
718 let begin = data.offset;
719 try {
720 // Quoted from OMA-TS-MMS-CONF-V1_3-20110913-A:
721 // Some of the MMS headers have been defined as "Encoded-string-value".
722 // The character set IANA MIBEnum value in these headers SHALL be
723 // encoded as Integer-value ([WAPWSP] section 8.4.2.3). The character
724 // set us-ascii (IANA MIBenum 3) SHALL always be accepted. If the
725 // character set is not specified (simple Text-string encoding) the
726 // character set SHALL be identified as us-ascii (lower half of ISO
727 // 8859-1 [ISO8859-1]). When the text string cannot be represented as
728 // us-ascii, the character set SHALL be encoded as utf-8 (IANA MIBenum
729 // 106) which has unique byte ordering.
730 WSP.TextString.encode(data, str, true);
731 } catch (e) {
732 data.offset = begin;
733 this.encodeCharsetEncodedString(data, str);
734 }
735 },
736 };
738 /**
739 * Expiry-value = Value-length (Absolute-token Date-value | Relative-token Delta-seconds-value)
740 * Absolute-token = <Octet 128>
741 * Relative-token = <Octet 129>
742 *
743 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.20
744 */
745 this.ExpiryValue = {
746 /**
747 * @param data
748 * A wrapped object containing raw PDU data.
749 *
750 * @return A Date object for absolute expiry or an integer for relative one.
751 *
752 * @throws CodeError if decoded token equals to neither 128 nor 129.
753 */
754 decode: function(data) {
755 let length = WSP.ValueLength.decode(data);
756 let end = data.offset + length;
758 let token = WSP.Octet.decode(data);
759 if ((token != 128) && (token != 129)) {
760 throw new WSP.CodeError("Expiry-value: invalid token " + token);
761 }
763 let result;
764 if (token == 128) {
765 result = WSP.DateValue.decode(data);
766 } else {
767 result = WSP.DeltaSecondsValue.decode(data);
768 }
770 if (data.offset != end) {
771 data.offset = end;
772 }
774 return result;
775 },
777 /**
778 * @param data
779 * A wrapped object to store encoded raw data.
780 * @param value
781 * A Date object for absolute expiry or an integer for relative one.
782 */
783 encode: function(data, value) {
784 let isDate, begin = data.offset;
785 if (value instanceof Date) {
786 isDate = true;
787 WSP.DateValue.encode(data, value);
788 } else if (typeof value == "number") {
789 isDate = false;
790 WSP.DeltaSecondsValue.encode(data, value);
791 } else {
792 throw new CodeError("Expiry-value: invalid value type");
793 }
795 // Calculate how much octets will be written and seek back.
796 // TODO: use memmove, see bug 730873
797 let len = data.offset - begin;
798 data.offset = begin;
800 WSP.ValueLength.encode(data, len + 1);
801 if (isDate) {
802 WSP.Octet.encode(data, 128);
803 WSP.DateValue.encode(data, value);
804 } else {
805 WSP.Octet.encode(data, 129);
806 WSP.DeltaSecondsValue.encode(data, value);
807 }
808 },
809 };
811 /**
812 * From-value = Value-length (Address-present-token Address | Insert-address-token)
813 * Address-present-token = <Octet 128>
814 * Insert-address-token = <Octet 129>
815 *
816 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.21
817 */
818 this.FromValue = {
819 /**
820 * @param data
821 * A wrapped object containing raw PDU data.
822 *
823 * @return A decoded Address-value or null for MMS Proxy-Relay Insert-Address
824 * mode.
825 *
826 * @throws CodeError if decoded token equals to neither 128 nor 129.
827 */
828 decode: function(data) {
829 let length = WSP.ValueLength.decode(data);
830 let end = data.offset + length;
832 let token = WSP.Octet.decode(data);
833 if ((token != 128) && (token != 129)) {
834 throw new WSP.CodeError("From-value: invalid token " + token);
835 }
837 let result = null;
838 if (token == 128) {
839 result = Address.decode(data);
840 }
842 if (data.offset != end) {
843 data.offset = end;
844 }
846 return result;
847 },
849 /**
850 * @param data
851 * A wrapped object to store encoded raw data.
852 * @param value
853 * A Address-value or null for MMS Proxy-Relay Insert-Address mode.
854 */
855 encode: function(data, value) {
856 if (!value) {
857 WSP.ValueLength.encode(data, 1);
858 WSP.Octet.encode(data, 129);
859 return;
860 }
862 // Calculate how much octets will be written and seek back.
863 // TODO: use memmove, see bug 730873
864 let begin = data.offset;
865 Address.encode(data, value);
866 let len = data.offset - begin;
867 data.offset = begin;
869 WSP.ValueLength.encode(data, len + 1);
870 WSP.Octet.encode(data, 128);
871 Address.encode(data, value);
872 },
873 };
875 /**
876 * Previously-sent-by-value = Value-length Forwarded-count-value Address
877 * Forwarded-count-value = Integer-value
878 *
879 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.23
880 */
881 this.PreviouslySentByValue = {
882 /**
883 * @param data
884 * A wrapped object containing raw PDU data.
885 *
886 * @return Decoded object containing an integer `forwardedCount` and an
887 * string-typed `originator` attributes.
888 */
889 decode: function(data) {
890 let length = WSP.ValueLength.decode(data);
891 let end = data.offset + length;
893 let result = {};
894 result.forwardedCount = WSP.IntegerValue.decode(data);
895 result.originator = Address.decode(data);
897 if (data.offset != end) {
898 data.offset = end;
899 }
901 return result;
902 },
903 };
905 /**
906 * Previously-sent-date-value = Value-length Forwarded-count-value Date-value
907 *
908 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.23
909 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.24
910 */
911 this.PreviouslySentDateValue = {
912 /**
913 * @param data
914 * A wrapped object containing raw PDU data.
915 *
916 * @return Decoded object containing an integer `forwardedCount` and an
917 * Date-typed `timestamp` attributes.
918 */
919 decode: function(data) {
920 let length = WSP.ValueLength.decode(data);
921 let end = data.offset + length;
923 let result = {};
924 result.forwardedCount = WSP.IntegerValue.decode(data);
925 result.timestamp = WSP.DateValue.decode(data);
927 if (data.offset != end) {
928 data.offset = end;
929 }
931 return result;
932 },
933 };
935 /**
936 * Message-class-value = Class-identifier | Token-text
937 * Class-identifier = Personal | Advertisement | Informational | Auto
938 * Personal = <Octet 128>
939 * Advertisement = <Octet 129>
940 * Informational = <Octet 130>
941 * Auto = <Octet 131>
942 *
943 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.27
944 */
945 this.MessageClassValue = {
946 WELL_KNOWN_CLASSES: ["personal", "advertisement", "informational", "auto"],
948 /**
949 * @param data
950 * A wrapped object containing raw PDU data.
951 *
952 * @return A decoded string.
953 *
954 * @throws CodeError if decoded value is not in the range 128..131.
955 */
956 decodeClassIdentifier: function(data) {
957 let value = WSP.Octet.decode(data);
958 if ((value >= 128) && (value < (128 + this.WELL_KNOWN_CLASSES.length))) {
959 return this.WELL_KNOWN_CLASSES[value - 128];
960 }
962 throw new WSP.CodeError("Class-identifier: invalid id " + value);
963 },
965 /**
966 * @param data
967 * A wrapped object containing raw PDU data.
968 *
969 * @return A decoded string.
970 */
971 decode: function(data) {
972 let begin = data.offset;
973 try {
974 return this.decodeClassIdentifier(data);
975 } catch (e) {
976 data.offset = begin;
977 return WSP.TokenText.decode(data);
978 }
979 },
981 /**
982 * @param data
983 * A wrapped object to store encoded raw data.
984 * @param klass
985 */
986 encode: function(data, klass) {
987 let index = this.WELL_KNOWN_CLASSES.indexOf(klass.toLowerCase());
988 if (index >= 0) {
989 WSP.Octet.encode(data, index + 128);
990 } else {
991 WSP.TokenText.encode(data, klass);
992 }
993 },
994 };
996 /**
997 * Message-type-value = <Octet 128..151>
998 *
999 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.30
1000 */
1001 this.MessageTypeValue = new RangedValue("Message-type-value", 128, 151);
1003 /**
1004 * MM-flags-value = Value-length ( Add-token | Remove-token | Filter-token ) Encoded-string-value
1005 * Add-token = <Octet 128>
1006 * Remove-token = <Octet 129>
1007 * Filter-token = <Octet 130>
1008 *
1009 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.32
1010 */
1011 this.MmFlagsValue = {
1012 /**
1013 * @param data
1014 * A wrapped object containing raw PDU data.
1015 *
1016 * @return Decoded object containing an integer `type` and an string-typed
1017 * `text` attributes.
1018 *
1019 * @throws CodeError if decoded value is not in the range 128..130.
1020 */
1021 decode: function(data) {
1022 let length = WSP.ValueLength.decode(data);
1023 let end = data.offset + length;
1025 let result = {};
1026 result.type = WSP.Octet.decode(data);
1027 if ((result.type < 128) || (result.type > 130)) {
1028 throw new WSP.CodeError("MM-flags-value: invalid type " + result.type);
1029 }
1030 result.text = EncodedStringValue.decode(data);
1032 if (data.offset != end) {
1033 data.offset = end;
1034 }
1036 return result;
1037 },
1039 /**
1040 * @param data
1041 * A wrapped object to store encoded raw data.
1042 * @param value
1043 * An object containing an integer `type` and an string-typed
1044 * `text` attributes.
1045 */
1046 encode: function(data, value) {
1047 if ((value.type < 128) || (value.type > 130)) {
1048 throw new WSP.CodeError("MM-flags-value: invalid type " + value.type);
1049 }
1051 // Calculate how much octets will be written and seek back.
1052 // TODO: use memmove, see bug 730873
1053 let begin = data.offset;
1054 EncodedStringValue.encode(data, value.text);
1055 let len = data.offset - begin;
1056 data.offset = begin;
1058 WSP.ValueLength.encode(data, len + 1);
1059 WSP.Octet.encode(data, value.type);
1060 EncodedStringValue.encode(data, value.text);
1061 },
1062 };
1064 /**
1065 * MM-state-value = Draft | Sent | New | Retrieved | Forwarded
1066 * Draft = <Octet 128>
1067 * Sent = <Octet 129>
1068 * New = <Octet 130>
1069 * Retrieved = <Octet 131>
1070 * Forwarded = <Octet 132>
1071 *
1072 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.33
1073 */
1074 this.MmStateValue = new RangedValue("MM-state-value", 128, 132);
1076 /**
1077 * Priority-value = Low | Normal | High
1078 * Low = <Octet 128>
1079 * Normal = <Octet 129>
1080 * High = <Octet 130>
1081 *
1082 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.35
1083 */
1084 this.PriorityValue = new RangedValue("Priority-value", 128, 130);
1086 /**
1087 * Read-status-value = Read | Deleted without being read
1088 *
1089 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.38
1090 */
1091 this.ReadStatusValue = new RangedValue("Read-status-value", 128, 129);
1093 /**
1094 * Recommended-Retrieval-Mode-value = Manual
1095 * Manual = <Octet 128>
1096 *
1097 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.39
1098 */
1099 this.RecommendedRetrievalModeValue = {
1100 /**
1101 * @param data
1102 * A wrapped object containing raw PDU data.
1103 *
1104 * @return A decoded integer.
1105 */
1106 decode: function(data) {
1107 return WSP.Octet.decodeEqualTo(data, 128);
1108 },
1109 };
1111 /**
1112 * Reply-charging-value = Requested | Requested text only | Accepted |
1113 * Accepted text only
1114 * Requested = <Octet 128>
1115 * Requested text only = <Octet 129>
1116 * Accepted = <Octet 130>
1117 * Accepted text only = <Octet 131>
1118 *
1119 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.43
1120 */
1121 this.ReplyChargingValue = new RangedValue("Reply-charging-value", 128, 131);
1123 /**
1124 * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
1125 *
1126 * Response-text-value = Encoded-string-value
1127 *
1128 * When used in the M-Mbox-Delete.conf and M-Delete.conf PDUs:
1129 *
1130 * Response-text-Del-value = Value-length Status-count-value Response-text-value
1131 *
1132 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.49
1133 */
1134 this.ResponseText = {
1135 /**
1136 * @param data
1137 * A wrapped object containing raw PDU data.
1138 * @param options
1139 * Extra context for decoding.
1140 *
1141 * @return An object containing a string-typed `text` attribute and a
1142 * integer-typed `statusCount` one.
1143 */
1144 decode: function(data, options) {
1145 let type = WSP.ensureHeader(options, "x-mms-message-type");
1147 let result = {};
1148 if ((type == MMS_PDU_TYPE_MBOX_DELETE_CONF)
1149 || (type == MMS_PDU_TYPE_DELETE_CONF)) {
1150 let length = WSP.ValueLength.decode(data);
1151 let end = data.offset + length;
1153 result.statusCount = WSP.IntegerValue.decode(data);
1154 result.text = EncodedStringValue.decode(data);
1156 if (data.offset != end) {
1157 data.offset = end;
1158 }
1159 } else {
1160 result.text = EncodedStringValue.decode(data);
1161 }
1163 return result;
1164 },
1165 };
1167 /**
1168 * Retrieve-status-value = Ok | Error-transient-failure |
1169 * Error-transient-message-not-found |
1170 * Error-transient-network-problem |
1171 * Error-permanent-failure |
1172 * Error-permanent-service-denied |
1173 * Error-permanent-message-not-found |
1174 * Error-permanent-content-unsupported
1175 * Ok = <Octet 128>
1176 * Error-transient-failure = <Octet 192>
1177 * Error-transient-message-not-found = <Octet 193>
1178 * Error-transient-network-problem = <Octet 194>
1179 * Error-permanent-failure = <Octet 224>
1180 * Error-permanent-service-denied = <Octet 225>
1181 * Error-permanent-message-not-found = <Octet 226>
1182 * Error-permanent-content-unsupported = <Octet 227>
1183 *
1184 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.50
1185 */
1186 this.RetrieveStatusValue = {
1187 /**
1188 * @param data
1189 * A wrapped object containing raw PDU data.
1190 *
1191 * @return A decoded integer.
1192 */
1193 decode: function(data) {
1194 let value = WSP.Octet.decode(data);
1195 if (value == MMS_PDU_ERROR_OK) {
1196 return value;
1197 }
1199 if ((value >= MMS_PDU_ERROR_TRANSIENT_FAILURE) && (value < 256)) {
1200 return value;
1201 }
1203 // Any other values SHALL NOT be used. They are reserved for future use.
1204 // An MMS Client that receives such a reserved value MUST react the same
1205 // as it does to the value 224 (Error-permanent-failure).
1206 return MMS_PDU_ERROR_PERMANENT_FAILURE;
1207 },
1208 };
1210 /**
1211 * Sender-visibility-value = Hide | Show
1212 *
1213 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.52
1214 */
1215 this.SenderVisibilityValue = new RangedValue("Sender-visibility-value", 128, 129);
1217 /**
1218 * Status-value = Expired | Retrieved | Rejected | Deferred | Unrecognised |
1219 * Indeterminate | Forwarded | Unreachable
1220 * Expired = <Octet 128>
1221 * Retrieved = <Octet 129>
1222 * Rejected = <Octet 130>
1223 * Deferred = <Octet 131>
1224 * Unrecognised = <Octet 132>
1225 * Indeterminate = <Octet 133>
1226 * Forwarded = <Octet 134>
1227 * Unreachable = <Octet 135>
1228 *
1229 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.54
1230 */
1231 this.StatusValue = new RangedValue("Status-value", 128, 135);
1233 this.PduHelper = {
1234 /**
1235 * @param data
1236 * A wrapped object containing raw PDU data.
1237 * @param headers
1238 * An optional object to store parsed header fields. Created
1239 * automatically if undefined.
1240 *
1241 * @return A boolean value indicating whether it's followed by message body.
1242 */
1243 parseHeaders: function(data, headers) {
1244 if (!headers) {
1245 headers = {};
1246 }
1248 let header;
1249 while (data.offset < data.array.length) {
1250 // There is no `header length` information in MMS PDU. If we just got
1251 // something wrong in parsing header fields, we might not be able to
1252 // determine the correct header-content boundary.
1253 header = HeaderField.decode(data, headers);
1255 if (header) {
1256 let orig = headers[header.name];
1257 if (Array.isArray(orig)) {
1258 headers[header.name].push(header.value);
1259 } else if (orig) {
1260 headers[header.name] = [orig, header.value];
1261 } else {
1262 headers[header.name] = header.value;
1263 }
1264 if (header.name == "content-type") {
1265 // `... if the PDU contains a message body the Content Type MUST be
1266 // the last header field, followed by message body.` See
1267 // OMA-TS-MMS_ENC-V1_3-20110913-A section 7.
1268 break;
1269 }
1270 }
1271 }
1273 return headers;
1274 },
1276 /**
1277 * @param data
1278 * A wrapped object containing raw PDU data.
1279 * @param msg
1280 * A message object to store decoded multipart or octet array content.
1281 */
1282 parseContent: function(data, msg) {
1283 let contentType = msg.headers["content-type"].media;
1284 if ((contentType == "application/vnd.wap.multipart.related")
1285 || (contentType == "application/vnd.wap.multipart.mixed")) {
1286 msg.parts = WSP.PduHelper.parseMultiPart(data);
1287 return;
1288 }
1290 if (data.offset >= data.array.length) {
1291 return;
1292 }
1294 msg.content = WSP.Octet.decodeMultiple(data, data.array.length);
1295 if (false) {
1296 for (let begin = 0; begin < msg.content.length; begin += 20) {
1297 debug("content: " + JSON.stringify(msg.content.subarray(begin, begin + 20)));
1298 }
1299 }
1300 },
1302 /**
1303 * Check existences of all mandatory fields of a MMS message. Also sets `type`
1304 * for convenient access.
1305 *
1306 * @param msg
1307 * A MMS message object.
1308 *
1309 * @return The corresponding entry in MMS_PDU_TYPES;
1310 *
1311 * @throws FatalCodeError if the PDU type is not supported yet.
1312 */
1313 checkMandatoryFields: function(msg) {
1314 let type = WSP.ensureHeader(msg.headers, "x-mms-message-type");
1315 let entry = MMS_PDU_TYPES[type];
1316 if (!entry) {
1317 throw new WSP.FatalCodeError(
1318 "checkMandatoryFields: unsupported message type " + type);
1319 }
1321 entry.mandatoryFields.forEach(function(name) {
1322 WSP.ensureHeader(msg.headers, name);
1323 });
1325 // Setup convenient alias that referenced frequently.
1326 msg.type = type;
1328 return entry;
1329 },
1331 /**
1332 * @param data
1333 * A wrapped object containing raw PDU data.
1334 * @param msg [optional]
1335 * Optional target object for decoding.
1336 *
1337 * @return A MMS message object or null in case of errors found.
1338 */
1339 parse: function(data, msg) {
1340 if (!msg) {
1341 msg = {};
1342 }
1344 try {
1345 msg.headers = this.parseHeaders(data, msg.headers);
1347 // Validity checks
1348 let typeinfo = this.checkMandatoryFields(msg);
1349 if (typeinfo.hasContent) {
1350 this.parseContent(data, msg);
1351 }
1352 } catch (e) {
1353 debug("Failed to parse MMS message, error message: " + e.message);
1354 return null;
1355 }
1357 return msg;
1358 },
1360 /**
1361 * @param data
1362 * A wrapped object to store encoded raw data.
1363 * @param headers
1364 * A dictionary object containing multiple name/value mapping.
1365 * @param name
1366 * Name of the header field to be encoded.
1367 */
1368 encodeHeader: function(data, headers, name) {
1369 let value = headers[name];
1370 if (Array.isArray(value)) {
1371 for (let i = 0; i < value.length; i++) {
1372 HeaderField.encode(data, {name: name, value: value[i]}, headers);
1373 }
1374 } else {
1375 HeaderField.encode(data, {name: name, value: value}, headers);
1376 }
1377 },
1379 /**
1380 * @param data
1381 * A wrapped object to store encoded raw data.
1382 * @param headers
1383 * A dictionary object containing multiple name/value mapping.
1384 */
1385 encodeHeaderIfExists: function(data, headers, name) {
1386 // Header value could be zero or null.
1387 if (headers[name] !== undefined) {
1388 this.encodeHeader(data, headers, name);
1389 }
1390 },
1392 /**
1393 * @param data [optional]
1394 * A wrapped object to store encoded raw data. Created if undefined.
1395 * @param headers
1396 * A dictionary object containing multiple name/value mapping.
1397 *
1398 * @return the passed data parameter or a created one.
1399 */
1400 encodeHeaders: function(data, headers) {
1401 if (!data) {
1402 data = {array: [], offset: 0};
1403 }
1405 // `In the encoding of the header fields, the order of the fields is not
1406 // significant, except that X-Mms-Message-Type, X-Mms-Transaction-ID (when
1407 // present) and X-Mms-MMS-Version MUST be at the beginning of the message
1408 // headers, in that order, and if the PDU contains a message body the
1409 // Content Type MUST be the last header field, followed by message body.`
1410 // ~ OMA-TS-MMS_ENC-V1_3-20110913-A section 7
1411 this.encodeHeader(data, headers, "x-mms-message-type");
1412 this.encodeHeaderIfExists(data, headers, "x-mms-transaction-id");
1413 this.encodeHeaderIfExists(data, headers, "x-mms-mms-version");
1415 for (let key in headers) {
1416 if ((key == "x-mms-message-type")
1417 || (key == "x-mms-transaction-id")
1418 || (key == "x-mms-mms-version")
1419 || (key == "content-type")) {
1420 continue;
1421 }
1422 this.encodeHeader(data, headers, key);
1423 }
1425 this.encodeHeaderIfExists(data, headers, "content-type");
1427 return data;
1428 },
1430 /**
1431 * @param multiStream
1432 * An exsiting nsIMultiplexInputStream.
1433 * @param msg
1434 * A MMS message object.
1435 *
1436 * @return An instance of nsIMultiplexInputStream or null in case of errors.
1437 */
1438 compose: function(multiStream, msg) {
1439 if (!multiStream) {
1440 multiStream = Cc["@mozilla.org/io/multiplex-input-stream;1"]
1441 .createInstance(Ci.nsIMultiplexInputStream);
1442 }
1444 try {
1445 // Validity checks
1446 let typeinfo = this.checkMandatoryFields(msg);
1448 let data = this.encodeHeaders(null, msg.headers);
1449 debug("Composed PDU Header: " + JSON.stringify(data.array));
1450 WSP.PduHelper.appendArrayToMultiStream(multiStream, data.array, data.offset);
1452 if (msg.content) {
1453 WSP.PduHelper.appendArrayToMultiStream(multiStream, msg.content, msg.content.length);
1454 } else if (msg.parts) {
1455 WSP.PduHelper.composeMultiPart(multiStream, msg.parts);
1456 } else if (typeinfo.hasContent) {
1457 throw new WSP.CodeError("Missing message content");
1458 }
1460 return multiStream;
1461 } catch (e) {
1462 debug("Failed to compose MMS message, error message: " + e.message);
1463 return null;
1464 }
1465 },
1466 };
1468 const MMS_PDU_TYPES = (function() {
1469 let pdus = {};
1470 function add(number, hasContent, mandatoryFields) {
1471 pdus[number] = {
1472 number: number,
1473 hasContent: hasContent,
1474 mandatoryFields: mandatoryFields,
1475 };
1476 }
1478 add(MMS_PDU_TYPE_SEND_REQ, true, ["x-mms-message-type",
1479 "x-mms-transaction-id",
1480 "x-mms-mms-version",
1481 "from",
1482 "content-type"]);
1483 add(MMS_PDU_TYPE_SEND_CONF, false, ["x-mms-message-type",
1484 "x-mms-transaction-id",
1485 "x-mms-mms-version",
1486 "x-mms-response-status"]);
1487 add(MMS_PDU_TYPE_NOTIFICATION_IND, false, ["x-mms-message-type",
1488 "x-mms-transaction-id",
1489 "x-mms-mms-version",
1490 "x-mms-message-class",
1491 "x-mms-message-size",
1492 "x-mms-expiry",
1493 "x-mms-content-location"]);
1494 add(MMS_PDU_TYPE_RETRIEVE_CONF, true, ["x-mms-message-type",
1495 "x-mms-mms-version",
1496 "date",
1497 "content-type"]);
1498 add(MMS_PDU_TYPE_NOTIFYRESP_IND, false, ["x-mms-message-type",
1499 "x-mms-transaction-id",
1500 "x-mms-mms-version",
1501 "x-mms-status"]);
1502 add(MMS_PDU_TYPE_DELIVERY_IND, false, ["x-mms-message-type",
1503 "x-mms-mms-version",
1504 "message-id",
1505 "to",
1506 "date",
1507 "x-mms-status"]);
1508 add(MMS_PDU_TYPE_ACKNOWLEDGE_IND, false, ["x-mms-message-type",
1509 "x-mms-transaction-id",
1510 "x-mms-mms-version"]);
1511 add(MMS_PDU_TYPE_READ_REC_IND, false, ["x-mms-message-type",
1512 "message-id",
1513 "x-mms-mms-version",
1514 "to",
1515 "from",
1516 "x-mms-read-status"]);
1517 add(MMS_PDU_TYPE_READ_ORIG_IND, false, ["x-mms-message-type",
1518 "x-mms-mms-version",
1519 "message-id",
1520 "to",
1521 "from",
1522 "date",
1523 "x-mms-read-status"]);
1525 return pdus;
1526 })();
1528 /**
1529 * Header field names and assigned numbers.
1530 *
1531 * @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.4
1532 */
1533 const MMS_HEADER_FIELDS = (function() {
1534 let names = {};
1535 function add(name, number, coder) {
1536 let entry = {
1537 name: name,
1538 number: number,
1539 coder: coder,
1540 };
1541 names[name] = names[number] = entry;
1542 }
1544 add("bcc", 0x01, Address);
1545 add("cc", 0x02, Address);
1546 add("x-mms-content-location", 0x03, ContentLocationValue);
1547 add("content-type", 0x04, WSP.ContentTypeValue);
1548 add("date", 0x05, WSP.DateValue);
1549 add("x-mms-delivery-report", 0x06, BooleanValue);
1550 add("x-mms-delivery-time", 0x07, ExpiryValue);
1551 add("x-mms-expiry", 0x08, ExpiryValue);
1552 add("from", 0x09, FromValue);
1553 add("x-mms-message-class", 0x0A, MessageClassValue);
1554 add("message-id", 0x0B, WSP.TextString);
1555 add("x-mms-message-type", 0x0C, MessageTypeValue);
1556 add("x-mms-mms-version", 0x0D, WSP.ShortInteger);
1557 add("x-mms-message-size", 0x0E, WSP.LongInteger);
1558 add("x-mms-priority", 0x0F, PriorityValue);
1559 add("x-mms-read-report", 0x10, BooleanValue);
1560 add("x-mms-report-allowed", 0x11, BooleanValue);
1561 add("x-mms-response-status", 0x12, RetrieveStatusValue);
1562 add("x-mms-response-text", 0x13, ResponseText);
1563 add("x-mms-sender-visibility", 0x14, SenderVisibilityValue);
1564 add("x-mms-status", 0x15, StatusValue);
1565 add("subject", 0x16, EncodedStringValue);
1566 add("to", 0x17, Address);
1567 add("x-mms-transaction-id", 0x18, WSP.TextString);
1568 add("x-mms-retrieve-status", 0x19, RetrieveStatusValue);
1569 add("x-mms-retrieve-text", 0x1A, EncodedStringValue);
1570 add("x-mms-read-status", 0x1B, ReadStatusValue);
1571 add("x-mms-reply-charging", 0x1C, ReplyChargingValue);
1572 add("x-mms-reply-charging-deadline", 0x1D, ExpiryValue);
1573 add("x-mms-reply-charging-id", 0x1E, WSP.TextString);
1574 add("x-mms-reply-charging-size", 0x1F, WSP.LongInteger);
1575 add("x-mms-previously-sent-by", 0x20, PreviouslySentByValue);
1576 add("x-mms-previously-sent-date", 0x21, PreviouslySentDateValue);
1577 add("x-mms-store", 0x22, BooleanValue);
1578 add("x-mms-mm-state", 0x23, MmStateValue);
1579 add("x-mms-mm-flags", 0x24, MmFlagsValue);
1580 add("x-mms-store-status", 0x25, RetrieveStatusValue);
1581 add("x-mms-store-status-text", 0x26, EncodedStringValue);
1582 add("x-mms-stored", 0x27, BooleanValue);
1583 //add("x-mms-attributes", 0x28);
1584 add("x-mms-totals", 0x29, BooleanValue);
1585 //add("x-mms-mbox-totals", 0x2A);
1586 add("x-mms-quotas", 0x2B, BooleanValue);
1587 //add("x-mms-mbox-quotas", 0x2C);
1588 add("x-mms-message-count", 0x2D, WSP.IntegerValue);
1589 //add("content", 0x2E);
1590 add("x-mms-start", 0x2F, WSP.IntegerValue);
1591 //add("additional-headers", 0x30);
1592 add("x-mms-distribution-indicator", 0x31, BooleanValue);
1593 add("x-mms-element-descriptor", 0x32, ElementDescriptorValue);
1594 add("x-mms-limit", 0x33, WSP.IntegerValue);
1595 add("x-mms-recommended-retrieval-mode", 0x34, RecommendedRetrievalModeValue);
1596 add("x-mms-recommended-retrieval-mode-text", 0x35, EncodedStringValue);
1597 //add("x-mms-status-text", 0x36);
1598 add("x-mms-applic-id", 0x37, WSP.TextString);
1599 add("x-mms-reply-applic-id", 0x38, WSP.TextString);
1600 add("x-mms-aux-applic-id", 0x39, WSP.TextString);
1601 add("x-mms-content-class", 0x3A, ContentClassValue);
1602 add("x-mms-drm-content", 0x3B, BooleanValue);
1603 add("x-mms-adaptation-allowed", 0x3C, BooleanValue);
1604 add("x-mms-replace-id", 0x3D, WSP.TextString);
1605 add("x-mms-cancel-id", 0x3E, WSP.TextString);
1606 add("x-mms-cancel-status", 0x3F, CancelStatusValue);
1608 return names;
1609 })();
1611 // @see OMA-TS-MMS_ENC-V1_3-20110913-A Table 27: Parameter Name Assignments
1612 const MMS_WELL_KNOWN_PARAMS = (function() {
1613 let params = {};
1615 function add(name, number, coder) {
1616 let entry = {
1617 name: name,
1618 number: number,
1619 coder: coder,
1620 };
1621 params[name] = params[number] = entry;
1622 }
1624 // Encoding Version: 1.2
1625 add("type", 0x02, WSP.TypeValue);
1627 return params;
1628 })();
1630 let debug;
1631 if (DEBUG) {
1632 debug = function(s) {
1633 dump("-$- MmsPduHelper: " + s + "\n");
1634 };
1635 } else {
1636 debug = function(s) {};
1637 }
1639 this.EXPORTED_SYMBOLS = ALL_CONST_SYMBOLS.concat([
1640 // Constant values
1641 "MMS_VERSION",
1643 // Utility functions
1644 "translatePduErrorToStatus",
1646 // Decoders
1647 "BooleanValue",
1648 "Address",
1649 "HeaderField",
1650 "MmsHeader",
1651 "CancelStatusValue",
1652 "ContentClassValue",
1653 "ContentLocationValue",
1654 "ElementDescriptorValue",
1655 "Parameter",
1656 "EncodedStringValue",
1657 "ExpiryValue",
1658 "FromValue",
1659 "PreviouslySentByValue",
1660 "PreviouslySentDateValue",
1661 "MessageClassValue",
1662 "MessageTypeValue",
1663 "MmFlagsValue",
1664 "MmStateValue",
1665 "PriorityValue",
1666 "ReadStatusValue",
1667 "RecommendedRetrievalModeValue",
1668 "ReplyChargingValue",
1669 "ResponseText",
1670 "RetrieveStatusValue",
1671 "SenderVisibilityValue",
1672 "StatusValue",
1674 // Parser
1675 "PduHelper",
1676 ]);