-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathutil.js
182 lines (150 loc) · 5.1 KB
/
util.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
171
172
173
174
175
176
177
178
179
180
181
182
var text = require('./text'),
ReParse = require('reparse').ReParse;
exports.foldHeader = foldHeader;
exports.escapeHeader = escapeHeader;
exports.fill = fill;
exports.stuffDots = stuffDots;
exports.titleCaseHeader = titleCaseHeader;
exports.is7Bit = is7Bit;
exports.date = date;
exports.eachLine = eachLine;
exports.readLines = readLines;
exports.aEach = aEach;
exports.extend = extend;
// ## Debug ##
var debug;
if (process.env.NODE_DEBUG && /mail/.test(process.env.NODE_DEBUG)) {
debug = exports.debug = function(label) {
var args = Array.prototype.slice.call(arguments, 1);
console.error.apply(console, ['TLS: ' + label].concat(args));
};
} else {
debug = exports.debug = function() { };
}
/// --- Formatting
// [Long Header Fields](http://tools.ietf.org/html/rfc5322#section-2.2.3)
function foldHeader(name, value, safe) {
var line = name + ': ' + escapeHeader(value.replace(/^\s*|\s*$/, ''), safe),
// Wrap at the character before a space, forcing the space to
// the next line. This way, when the segments are glued
// together the CRLF goes before the space.
segments = text.wrap(line, 78, 998, ".(?=\\s)"),
result = segments[0];
// It's possible that the line was wrapped across a hard boundary.
// This means a segment might not start with a space character. One
// way to handle this is to throw an error. Since it's unlikely to
// happen, keep things simple by quietly adding a space.
for (var i = 1, l = segments.length; i < l; i++)
result += "\r\n" + segments[i].replace(/^(\S)/, ' $1');
return result;
}
// [Header Fields](http://tools.ietf.org/html/rfc6532#section-3.2)
function escapeHeader(value, safe) {
// Per RFC 5322, A header value is allowed to be a printable ASCII
// character, a tab, or a space. Anything else is elided into a
// safe character (space by default).
//return value.replace(/[^ \t\w!"#\$%&'\(\)\*\+,\-\.\/:;<=>\?@\[\\\]\^`\{\|\}\~\]]+/gm, safe || ' ');
// Updated to with RFC 6532:
// Per the RFC Header values are now allowed to be any legal UTF-8
// string. Javascript strings are unicode by default, so if
// something is a Javascript string, it should also be able to be
// legally encoded into UTF-8. There is nothing to escape.
return value;
}
// [Line Length Limits](http://tools.ietf.org/html/rfc5322#section-2.1.1)
function fill(body) {
function wrap(result, line) {
return result.concat(text.wrap(line, 78, 998));
}
return text.splitLines(body)
.reduce(wrap, [])
.join('\r\n');
}
// [Transparency](http://tools.ietf.org/html/rfc5321#section-4.5.2)
function stuffDots(body, newline) {
if (newline)
body = body.replace(/^\./, '..');
return body.replace(/\r\n\./gm, '\r\n..');
}
function titleCaseHeader(name) {
return name && name.split(/\-/).map(function(segment) {
return (segment[0].toUpperCase() + segment.substr(1).toLowerCase());
}).join('-');
}
function is7Bit(data) {
return /^[\x00-\x7f]*$/.test(data);
}
// [Date and Time](http://tools.ietf.org/html/rfc5322#section-3.3)
var DAY = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
MONTH = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
function date(when) {
when = when || new Date();
return (
DAY[when.getDay()]
+ ', ' + pad(when.getDate())
+ ' ' + MONTH[when.getMonth()]
+ ' ' + when.getFullYear()
+ ' ' + pad(when.getHours())
+ ':' + pad(when.getMinutes())
+ ':' + pad(when.getSeconds())
+ ' ' + tz(when.getTimezoneOffset())
);
}
function tz(n) {
// From <https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset>:
//
// The time-zone offset is the difference, in minutes, between UTC
// and local time. Note that this means that the offset is positive
// if the local timezone is behind UTC and negative if it is ahead.
// For example, if your time zone is UTC+10 (Australian Eastern
// Standard Time), -600 will be returned. Daylight savings time
// prevents this value from being a constant even for a given
// locale.
var sign = (n < 0) ? '+' : '-';
n = Math.abs(n);
return sign + pad(Math.floor(n / 60)) + pad(n % 60);
};
function pad(n) {
return (n < 10) ? ('0' + n) : n;
}
/// --- Aux
function eachLine(stream, handle) {
return stream.on('data', readLines(handle));
}
function readLines(handle) {
var buffer = '',
line;
return function(chunk) {
buffer += chunk.toString();
while((line = buffer.match(/^(.*)(?:\r\n|\r|\n)/mg)) !== null) {
buffer = buffer.substr(line[0].length);
handle(line[0]);
}
};
}
function aEach(list, fn, callback) {
var index = -1,
limit = list.length;
each();
function each(err) {
if (err instanceof Error)
callback(err);
else if (++index >= limit)
callback(null);
else
fn.call(this, list[index], index, next);
}
function next() {
process.nextTick(each);
}
}
function extend(target) {
var obj, key;
for (var i = 1, l = arguments.length; i < l; i++) {
if ((obj = arguments[i])) {
for (key in obj)
target[key] = obj[key];
}
}
return target;
}