dom/mobilemessage/src/gonk/MmsPduHelper.jsm

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:f07233993096
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/. */
4
5 "use strict";
6
7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
8
9 let WSP = {};
10 Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP);
11
12 Cu.import("resource://gre/modules/mms_consts.js");
13
14 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
15
16 let DEBUG; // set to true to see debug messages
17
18 this.MMS_VERSION = (function() {
19 Cu.import("resource://gre/modules/Services.jsm");
20
21 try {
22 return Services.prefs.getIntPref("dom.mms.version");
23 } catch(ex) {}
24
25 return MMS_VERSION_1_3;
26 })();
27
28 this.translatePduErrorToStatus = function translatePduErrorToStatus(error) {
29 if (error == MMS_PDU_ERROR_OK) {
30 return MMS_PDU_STATUS_RETRIEVED;
31 }
32
33 if ((error >= MMS_PDU_ERROR_TRANSIENT_FAILURE)
34 && (error < MMS_PDU_ERROR_PERMANENT_FAILURE)) {
35 return MMS_PDU_STATUS_DEFERRED;
36 }
37
38 return MMS_PDU_STATUS_UNRECOGNISED;
39 }
40
41 function defineLazyRegExp(obj, name, pattern) {
42 obj.__defineGetter__(name, function() {
43 delete obj[name];
44 return obj[name] = new RegExp(pattern);
45 });
46 }
47
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,
57
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 }
71
72 throw new WSP.CodeError(this.name + ": invalid value " + value);
73 },
74
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 }
85
86 WSP.Octet.encode(data, value);
87 },
88 };
89
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 }
111
112 return value == 128;
113 },
114
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 };
125
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);
142
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 }
150
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 }
164
165 return {address: str, type: type};
166 },
167
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 }
178
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 }
218
219 if (!str) {
220 throw new WSP.CodeError("Address: invalid value: " + JSON.stringify(value));
221 }
222
223 EncodedStringValue.encode(data, str);
224 },
225
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 }
236
237 if (address.match(this.REGEXP_IPV4)) {
238 return "IPv4";
239 }
240
241 if (address.match(this.REGEXP_IPV6)) {
242 return "IPv6";
243 }
244
245 let normalizedAddress = PhoneNumberUtils.normalize(address, false);
246 if (PhoneNumberUtils.isPlainPhoneNumber(normalizedAddress)) {
247 return "PLMN";
248 }
249
250 return "Others";
251 },
252 };
253
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", "@");
269
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 },
291
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 };
305
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);
329
330 let entry = MMS_HEADER_FIELDS[index];
331 if (!entry) {
332 throw new WSP.NotWellKnownEncodingError(
333 "MMS-header: not well known header " + index);
334 }
335
336 let cur = data.offset, value;
337 try {
338 value = entry.coder.decode(data, options);
339 } catch (e) {
340 data.offset = cur;
341
342 value = WSP.skipValue(data);
343 debug("Skip malformed well known header: "
344 + JSON.stringify({name: entry.name, value: value}));
345
346 return null;
347 }
348
349 return {
350 name: entry.name,
351 value: value,
352 };
353 },
354
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 }
370
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 }
376
377 WSP.ShortInteger.encode(data, entry.number);
378 entry.coder.encode(data, header.value);
379 },
380 };
381
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);
389
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);
397
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");
422
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;
428
429 result.statusCount = WSP.IntegerValue.decode(data);
430 result.uri = WSP.UriValue.decode(data);
431
432 if (data.offset != end) {
433 data.offset = end;
434 }
435 } else {
436 result.uri = WSP.UriValue.decode(data);
437 }
438
439 return result;
440 },
441 };
442
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;
460
461 let result = {};
462 result.contentReference = WSP.TextString.decode(data);
463 if (data.offset < end) {
464 result.params = Parameter.decodeMultiple(data, end);
465 }
466
467 if (data.offset != end) {
468 // Explicitly seek to end in case of skipped parameters.
469 data.offset = end;
470 }
471
472 return result;
473 },
474 };
475
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 }
506
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 }
512
513 return entry.name;
514 },
515
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 },
534
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;
545
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 }
559
560 return params;
561 },
562
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 }
575
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 }
582
583 WSP.encodeAlternatives(data, param.value, options,
584 WSP.ConstrainedEncoding, WSP.TextString);
585 },
586 };
587
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;
611
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 }
618
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 }
626
627 let raw = WSP.Octet.decodeMultiple(data, end - 1);
628 // Read NUL character.
629 WSP.Octet.decodeEqualTo(data, 0);
630
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 }
646
647 if (data.offset != end) {
648 data.offset = end;
649 }
650
651 return str;
652 },
653
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 },
669
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";
685
686 let raw;
687 try {
688 raw = conv.convertToByteArray(str);
689 } catch (e) {
690 throw new WSP.CodeError("Charset-encoded-string: " + e.message);
691 }
692
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 }
698
699 WSP.ValueLength.encode(data, length);
700
701 let entry = WSP.WSP_WELL_KNOWN_CHARSETS["utf-8"];
702 WSP.IntegerValue.encode(data, entry.number);
703
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 },
710
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 };
737
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;
757
758 let token = WSP.Octet.decode(data);
759 if ((token != 128) && (token != 129)) {
760 throw new WSP.CodeError("Expiry-value: invalid token " + token);
761 }
762
763 let result;
764 if (token == 128) {
765 result = WSP.DateValue.decode(data);
766 } else {
767 result = WSP.DeltaSecondsValue.decode(data);
768 }
769
770 if (data.offset != end) {
771 data.offset = end;
772 }
773
774 return result;
775 },
776
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 }
794
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;
799
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 };
810
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;
831
832 let token = WSP.Octet.decode(data);
833 if ((token != 128) && (token != 129)) {
834 throw new WSP.CodeError("From-value: invalid token " + token);
835 }
836
837 let result = null;
838 if (token == 128) {
839 result = Address.decode(data);
840 }
841
842 if (data.offset != end) {
843 data.offset = end;
844 }
845
846 return result;
847 },
848
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 }
861
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;
868
869 WSP.ValueLength.encode(data, len + 1);
870 WSP.Octet.encode(data, 128);
871 Address.encode(data, value);
872 },
873 };
874
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;
892
893 let result = {};
894 result.forwardedCount = WSP.IntegerValue.decode(data);
895 result.originator = Address.decode(data);
896
897 if (data.offset != end) {
898 data.offset = end;
899 }
900
901 return result;
902 },
903 };
904
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;
922
923 let result = {};
924 result.forwardedCount = WSP.IntegerValue.decode(data);
925 result.timestamp = WSP.DateValue.decode(data);
926
927 if (data.offset != end) {
928 data.offset = end;
929 }
930
931 return result;
932 },
933 };
934
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"],
947
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 }
961
962 throw new WSP.CodeError("Class-identifier: invalid id " + value);
963 },
964
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 },
980
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 };
995
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);
1002
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;
1024
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);
1031
1032 if (data.offset != end) {
1033 data.offset = end;
1034 }
1035
1036 return result;
1037 },
1038
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 }
1050
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;
1057
1058 WSP.ValueLength.encode(data, len + 1);
1059 WSP.Octet.encode(data, value.type);
1060 EncodedStringValue.encode(data, value.text);
1061 },
1062 };
1063
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);
1075
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);
1085
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);
1092
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 };
1110
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);
1122
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");
1146
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;
1152
1153 result.statusCount = WSP.IntegerValue.decode(data);
1154 result.text = EncodedStringValue.decode(data);
1155
1156 if (data.offset != end) {
1157 data.offset = end;
1158 }
1159 } else {
1160 result.text = EncodedStringValue.decode(data);
1161 }
1162
1163 return result;
1164 },
1165 };
1166
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 }
1198
1199 if ((value >= MMS_PDU_ERROR_TRANSIENT_FAILURE) && (value < 256)) {
1200 return value;
1201 }
1202
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 };
1209
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);
1216
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);
1232
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 }
1247
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);
1254
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 }
1272
1273 return headers;
1274 },
1275
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 }
1289
1290 if (data.offset >= data.array.length) {
1291 return;
1292 }
1293
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 },
1301
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 }
1320
1321 entry.mandatoryFields.forEach(function(name) {
1322 WSP.ensureHeader(msg.headers, name);
1323 });
1324
1325 // Setup convenient alias that referenced frequently.
1326 msg.type = type;
1327
1328 return entry;
1329 },
1330
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 }
1343
1344 try {
1345 msg.headers = this.parseHeaders(data, msg.headers);
1346
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 }
1356
1357 return msg;
1358 },
1359
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 },
1378
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 },
1391
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 }
1404
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");
1414
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 }
1424
1425 this.encodeHeaderIfExists(data, headers, "content-type");
1426
1427 return data;
1428 },
1429
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 }
1443
1444 try {
1445 // Validity checks
1446 let typeinfo = this.checkMandatoryFields(msg);
1447
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);
1451
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 }
1459
1460 return multiStream;
1461 } catch (e) {
1462 debug("Failed to compose MMS message, error message: " + e.message);
1463 return null;
1464 }
1465 },
1466 };
1467
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 }
1477
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"]);
1524
1525 return pdus;
1526 })();
1527
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 }
1543
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);
1607
1608 return names;
1609 })();
1610
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 = {};
1614
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 }
1623
1624 // Encoding Version: 1.2
1625 add("type", 0x02, WSP.TypeValue);
1626
1627 return params;
1628 })();
1629
1630 let debug;
1631 if (DEBUG) {
1632 debug = function(s) {
1633 dump("-$- MmsPduHelper: " + s + "\n");
1634 };
1635 } else {
1636 debug = function(s) {};
1637 }
1638
1639 this.EXPORTED_SYMBOLS = ALL_CONST_SYMBOLS.concat([
1640 // Constant values
1641 "MMS_VERSION",
1642
1643 // Utility functions
1644 "translatePduErrorToStatus",
1645
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",
1673
1674 // Parser
1675 "PduHelper",
1676 ]);
1677

mercurial