-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathstructuredHeaders.js
170 lines (148 loc) · 5.75 KB
/
structuredHeaders.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* This file implements knowledge of how to encode or decode structured headers
* for several key headers. It is not meant to be used externally to jsmime.
*/
define(function (require) {
"use strict";
var structuredDecoders = new Map();
var structuredEncoders = new Map();
var preferredSpellings = new Map();
function addHeader(name, decoder, encoder) {
var lowerName = name.toLowerCase();
structuredDecoders.set(lowerName, decoder);
structuredEncoders.set(lowerName, encoder);
preferredSpellings.set(lowerName, name);
}
// Addressing headers: We assume that they can be specified in 1* form (this is
// false for From, but it's close enough to the truth that it shouldn't matter).
// There is no need to specialize the results for the header, so just pun it
// back to parseAddressingHeader.
function parseAddress(value) {
let results = [];
let headerparser = this;
return value.reduce(function (results, header) {
return results.concat(headerparser.parseAddressingHeader(header, true));
}, []);
}
function writeAddress(value) {
// Make sure the input is an array (accept a single entry)
if (!Array.isArray(value))
value = [value];
this.addAddresses(value);
}
// Addressing headers from RFC 5322:
addHeader("Bcc", parseAddress, writeAddress);
addHeader("Cc", parseAddress, writeAddress);
addHeader("From", parseAddress, writeAddress);
addHeader("Reply-To", parseAddress, writeAddress);
addHeader("Resent-Bcc", parseAddress, writeAddress);
addHeader("Resent-Cc", parseAddress, writeAddress);
addHeader("Resent-From", parseAddress, writeAddress);
addHeader("Resent-Reply-To", parseAddress, writeAddress);
addHeader("Resent-Sender", parseAddress, writeAddress);
addHeader("Resent-To", parseAddress, writeAddress);
addHeader("Sender", parseAddress, writeAddress);
addHeader("To", parseAddress, writeAddress);
// From RFC 5536:
addHeader("Approved", parseAddress, writeAddress);
// From RFC 3798:
addHeader("Disposition-Notification-To", parseAddress, writeAddress);
// Non-standard headers:
addHeader("Delivered-To", parseAddress, writeAddress);
addHeader("Return-Receipt-To", parseAddress, writeAddress);
// http://cr.yp.to/proto/replyto.html
addHeader("Mail-Reply-To", parseAddress, writeAddress);
addHeader("Mail-Followup-To", parseAddress, writeAddress);
// Parameter-based headers. Note that all parameters are slightly different, so
// we use slightly different variants here.
function parseParameterHeader(value, do2231, do2047) {
// Only use the first header for parameters; ignore subsequent redefinitions.
return this.parseParameterHeader(value[0], do2231, do2047);
}
// RFC 2045
function parseContentType(value) {
let params = parseParameterHeader.call(this, value, false, false);
let origtype = params.preSemi;
let parts = origtype.split('/');
if (parts.length != 2) {
// Malformed. Return to text/plain. Evil, ain't it?
params = new Map();
parts = ["text", "plain"];
}
let mediatype = parts[0].toLowerCase();
let subtype = parts[1].toLowerCase();
let type = mediatype + '/' + subtype;
let structure = new Map();
structure.mediatype = mediatype;
structure.subtype = subtype;
structure.type = type;
params.forEach(function (value, name) {
structure.set(name.toLowerCase(), value);
});
return structure;
}
structuredDecoders.set("Content-Type", parseContentType);
// Unstructured headers (just decode RFC 2047 for the first header value)
function parseUnstructured(values) {
return this.decodeRFC2047Words(values[0]);
}
function writeUnstructured(value) {
this.addUnstructured(value);
}
// Message-ID headers.
function parseMessageID(values) {
// TODO: Proper parsing support for these headers is currently unsupported).
return this.decodeRFC2047Words(values[0]);
}
function writeMessageID(value) {
// TODO: Proper parsing support for these headers is currently unsupported).
this.addUnstructured(value);
}
// RFC 5322
addHeader("Comments", parseUnstructured, writeUnstructured);
addHeader("Keywords", parseUnstructured, writeUnstructured);
addHeader("Subject", parseUnstructured, writeUnstructured);
// RFC 2045
addHeader("MIME-Version", parseUnstructured, writeUnstructured);
addHeader("Content-Description", parseUnstructured, writeUnstructured);
// RFC 7231
addHeader("User-Agent", parseUnstructured, writeUnstructured);
// Date headers
function parseDate(values) { return this.parseDateHeader(values[0]); }
function writeDate(value) { this.addDate(value); }
// RFC 5322
addHeader("Date", parseDate, writeDate);
addHeader("Resent-Date", parseDate, writeDate);
// RFC 5536
addHeader("Expires", parseDate, writeDate);
addHeader("Injection-Date", parseDate, writeDate);
addHeader("NNTP-Posting-Date", parseDate, writeDate);
// RFC 5322
addHeader("Message-ID", parseMessageID, writeMessageID);
addHeader("Resent-Message-ID", parseMessageID, writeMessageID);
// Miscellaneous headers (those that don't fall under the above schemes):
// RFC 2047
structuredDecoders.set("Content-Transfer-Encoding", function (values) {
return values[0].toLowerCase();
});
structuredEncoders.set("Content-Transfer-Encoding", writeUnstructured);
// Some clients like outlook.com send non-compliant References headers that
// separate values using commas. Also, some clients don't separate References
// with spaces, since these are optional accordint to RFC2822. So here we
// preprocess these headers (see bug 1154521 and bug 1197686).
function preprocessMessageIDs(values) {
let msgId = /<[^>]*>/g;
let match, ids = [];
while ((match = msgId.exec(values)) !== null) {
ids.push(match[0]);
}
return ids.join(' ');
}
structuredDecoders.set("References", preprocessMessageIDs);
structuredDecoders.set("In-Reply-To", preprocessMessageIDs);
return Object.freeze({
decoders: structuredDecoders,
encoders: structuredEncoders,
spellings: preferredSpellings,
});
});